From 525f4100462b7c61a98f1a43eebec5135d3d9618 Mon Sep 17 00:00:00 2001 From: Workbench IA Date: Mon, 5 Oct 2026 08:11:16 +0000 Subject: [PATCH 1/2] =?UTF-8?q?Ajouter=20le=20mod=C3=A8le=20iPhone=203GS?= =?UTF-8?q?=20ios6-n88=20et=20le=20simulateur=20iOS=206?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - hw/arm/ios6_n88.c : machine ios6-n88 (Cortex-A8, 256 Mio, PL192, UART, affichage logiciel) qui démarre iOS 6.1.6 (10B500) jusqu'à SpringBoard ; compilée sous CONFIG_IPOD_TOUCH_2G. - ios6/ : outils de développement natif (bin/ios6, agent invité root, relais PPP/libslirp netd, app SimulatorLab, smoke_test.py), préparation du firmware et du disque, preuves de validation dans ios6/results/. - tools/build.sh compile ce dépôt dans ios6/build sans toucher au build/ de l'émulateur iPod ; procédure macOS dans ios6/README.md. Co-Authored-By: Claude Opus 5.5 --- README.md | 5 + hw/arm/ios6_n88.c | 471 +++++++++ hw/arm/meson.build | 2 + ios6/.gitignore | 7 + ios6/AGENTS.md | 39 + ios6/README.md | 377 +++++++ ios6/agent/entitlements.plist | 6 + ios6/agent/ios6-agent.c | 904 ++++++++++++++++ ios6/agent/netd.c | 657 ++++++++++++ ios6/apps/SimulatorLab/main.m | 42 + ios6/bin/ios6 | 4 + ios6/requirements.txt | 3 + .../20261003-first-boot/boot-layout.json | 9 + ios6/results/20261003-first-boot/command.json | 24 + .../firmware-manifest.json | 22 + .../20261003-first-boot/provenance.json | 9 + ios6/results/20261003-first-boot/result.json | 23 + ios6/results/20261003-first-boot/serial.log | 955 +++++++++++++++++ ios6/results/20261003-first-boot/summary.json | 28 + .../20261003-first-boot/verification.txt | 1 + ios6/results/20261003-native-apps/app.log | 31 + .../development-validation.json | 22 + .../20261003-native-apps/native-app.png | Bin 0 -> 17435 bytes .../20261003-native-apps/validation.json | 14 + .../checkpoint-command.json | 24 + .../checkpoint-result.json | 22 + .../checkpoint-serial.log | 615 +++++++++++ .../components-sha256.json | 12 + .../20261003-springboard/fresh-command.json | 24 + .../20261003-springboard/fresh-result.json | 22 + .../20261003-springboard/fresh-serial.log | 998 ++++++++++++++++++ .../20261003-springboard/springboard.png | Bin 0 -> 216423 bytes .../validation-display.txt | 1 + .../20261003-springboard/validation.txt | 2 + .../20261003-springboard/verification.json | 19 + ios6/src/keybag_init.c | 126 +++ ios6/src/session_input.h | 46 + ios6/tools/build.sh | 25 + ios6/tools/build_agent.sh | 8 + ios6/tools/build_app.py | 15 + ios6/tools/build_netd.sh | 8 + ios6/tools/cache_address.py | 73 ++ ios6/tools/gdb_remote.py | 67 ++ ios6/tools/inspect_firmware.py | 83 ++ ios6/tools/kernel_address.py | 51 + ios6/tools/kext_info.py | 43 + ios6/tools/macho_address.py | 99 ++ ios6/tools/prepare_boot.py | 149 +++ ios6/tools/prepare_dev.py | 23 + ios6/tools/prepare_firmware.py | 114 ++ ios6/tools/prepare_rootfs.py | 165 +++ ios6/tools/prepare_sdk.py | 41 + ios6/tools/probe.py | 173 +++ ios6/tools/recover_hfs.py | 128 +++ ios6/tools/simulator.py | 212 ++++ ios6/tools/smoke_test.py | 86 ++ ios6/tools/verify.py | 93 ++ ios6/tools/verify_display.py | 120 +++ 58 files changed, 7342 insertions(+) create mode 100644 hw/arm/ios6_n88.c create mode 100644 ios6/.gitignore create mode 100644 ios6/AGENTS.md create mode 100644 ios6/README.md create mode 100644 ios6/agent/entitlements.plist create mode 100644 ios6/agent/ios6-agent.c create mode 100644 ios6/agent/netd.c create mode 100644 ios6/apps/SimulatorLab/main.m create mode 100755 ios6/bin/ios6 create mode 100644 ios6/requirements.txt create mode 100644 ios6/results/20261003-first-boot/boot-layout.json create mode 100644 ios6/results/20261003-first-boot/command.json create mode 100644 ios6/results/20261003-first-boot/firmware-manifest.json create mode 100644 ios6/results/20261003-first-boot/provenance.json create mode 100644 ios6/results/20261003-first-boot/result.json create mode 100644 ios6/results/20261003-first-boot/serial.log create mode 100644 ios6/results/20261003-first-boot/summary.json create mode 100644 ios6/results/20261003-first-boot/verification.txt create mode 100644 ios6/results/20261003-native-apps/app.log create mode 100644 ios6/results/20261003-native-apps/development-validation.json create mode 100644 ios6/results/20261003-native-apps/native-app.png create mode 100644 ios6/results/20261003-native-apps/validation.json create mode 100644 ios6/results/20261003-springboard/checkpoint-command.json create mode 100644 ios6/results/20261003-springboard/checkpoint-result.json create mode 100644 ios6/results/20261003-springboard/checkpoint-serial.log create mode 100644 ios6/results/20261003-springboard/components-sha256.json create mode 100644 ios6/results/20261003-springboard/fresh-command.json create mode 100644 ios6/results/20261003-springboard/fresh-result.json create mode 100644 ios6/results/20261003-springboard/fresh-serial.log create mode 100644 ios6/results/20261003-springboard/springboard.png create mode 100644 ios6/results/20261003-springboard/validation-display.txt create mode 100644 ios6/results/20261003-springboard/validation.txt create mode 100644 ios6/results/20261003-springboard/verification.json create mode 100644 ios6/src/keybag_init.c create mode 100644 ios6/src/session_input.h create mode 100755 ios6/tools/build.sh create mode 100755 ios6/tools/build_agent.sh create mode 100644 ios6/tools/build_app.py create mode 100755 ios6/tools/build_netd.sh create mode 100644 ios6/tools/cache_address.py create mode 100644 ios6/tools/gdb_remote.py create mode 100755 ios6/tools/inspect_firmware.py create mode 100755 ios6/tools/kernel_address.py create mode 100644 ios6/tools/kext_info.py create mode 100644 ios6/tools/macho_address.py create mode 100755 ios6/tools/prepare_boot.py create mode 100644 ios6/tools/prepare_dev.py create mode 100755 ios6/tools/prepare_firmware.py create mode 100644 ios6/tools/prepare_rootfs.py create mode 100644 ios6/tools/prepare_sdk.py create mode 100755 ios6/tools/probe.py create mode 100644 ios6/tools/recover_hfs.py create mode 100644 ios6/tools/simulator.py create mode 100644 ios6/tools/smoke_test.py create mode 100644 ios6/tools/verify.py create mode 100644 ios6/tools/verify_display.py diff --git a/README.md b/README.md index fa51cc0b48..92c059107a 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,8 @@ A technical blog post with more information about the peripherals and reverse en ### Running the iPod Touch 2G Instructions on how to run the iPod Touch 2G emulator can be found [here](https://github.com/devos50/qemu-ios/blob/ipod_touch_2g/RUNNING.md). + +### iPhone 3GS sous iOS 6.1.6 (expérimental) + +Modèle `ios6-n88` (`hw/arm/ios6_n88.c`) et outils de développement natif : +voir [`ios6/README.md`](ios6/README.md), y compris la procédure macOS. diff --git a/hw/arm/ios6_n88.c b/hw/arm/ios6_n88.c new file mode 100644 index 0000000000..5bfdde3d51 --- /dev/null +++ b/hw/arm/ios6_n88.c @@ -0,0 +1,471 @@ +/* + * Prototype de démarrage direct du noyau iOS 6 sur N88AP (S5L8920). + * GPL-2.0-or-later. Les périphériques absents restent explicitement tracés. + */ +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "target/arm/cpu.h" +#include "hw/arm/ipod_touch_uart.h" +#include "hw/intc/pl192.h" +#include "hw/irq.h" +#include "exec/address-spaces.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "sysemu/reset.h" +#include "sysemu/sysemu.h" +#include "ui/console.h" + +#define TYPE_IOS6_N88 MACHINE_TYPE_NAME("ios6-n88") +OBJECT_DECLARE_SIMPLE_TYPE(IOS6N88State, IOS6_N88) + +struct IOS6N88State { + MachineState parent; + ARMCPU *cpu; + MemoryRegion ram, vectors, io, rootdisk; + bool rootfs; + uint32_t ram_base; + uint32_t entry; + uint32_t clcd[0x3000 / 4], dsim[0x1000 / 4]; + uint32_t dart[2][0x1000 / 4]; + uint32_t dart_tables[2][16]; + unsigned dart_trace; + unsigned display_trace; + QemuConsole *console; + struct { + uint32_t regs[16]; + uint8_t fifo[256], devices[128][256], cursor[128]; + unsigned count, readpos, trace; + qemu_irq irq; + } i2c[2]; + unsigned unknown_reads, unknown_writes; + PL192State *vic[3]; + QEMUTimer *timer; + QEMUTimer *vblank_timer; + qemu_irq timer_irq; + qemu_irq display_irq; + uint32_t timer_count, timer_control; +}; + +static void n88_display_irq(IOS6N88State *s) +{ + qemu_set_irq(s->display_irq, !!(s->clcd[8 / 4] & s->clcd[0xc / 4])); +} + +static void n88_vblank(void *opaque) +{ + IOS6N88State *s = opaque; + if (s->clcd[0x1b10 / 4] & 1) { + s->clcd[0xc / 4] |= 1; + n88_display_irq(s); + } + timer_mod(s->vblank_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 16666667); +} + +static void n88_timer_expired(void *opaque) +{ + IOS6N88State *s = opaque; + qemu_set_irq(s->timer_irq, 1); +} + +static void n88_timer_schedule(IOS6N88State *s) +{ + timer_del(s->timer); + if (s->timer_control & 1) { + uint64_t ns = ((uint64_t)s->timer_count * 1000000000 + 23999999) / 24000000; + timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + MAX(ns, 1)); + } +} + +static uint64_t n88_io_read(void *opaque, hwaddr addr, unsigned size) +{ + IOS6N88State *s = opaque; + if (addr >= 0x05400000 && addr < 0x05403000) { + uint32_t value = s->clcd[(addr - 0x05400000) / 4]; + if (addr != 0x05400410 && s->display_trace++ < 3000) { + qemu_log_mask(LOG_UNIMP, "n88 clcd R %04x = %08x\n", (unsigned)(addr - 0x05400000), value); + } + return value; + } + if (addr >= 0x09000000 && addr < 0x09001000) { + unsigned offset = addr - 0x09000000; + uint32_t value = s->dsim[offset / 4]; + if (offset == 0) { + value = 0x80100103; + if (s->dsim[0x14 / 4] & 2) { value |= 0x230; } + if (s->dsim[0x14 / 4] & 1) { value &= ~0x230; } + if (s->dsim[8 / 4] & 0x80000000) { value |= 0x400; } + } + if (offset == 0x44) { value |= 0x500000; } + if (s->display_trace++ < 3000) { + qemu_log_mask(LOG_UNIMP, "n88 dsim R %04x = %08x\n", offset, value); + } + return value; + } + int bus = (addr & ~0xfffULL) == 0x03200000 ? 0 : + (addr & ~0xfffULL) == 0x03400000 ? 1 : -1; + if (bus >= 0 && (addr & 0xfff) < 0x40) { + unsigned reg = (addr & 0xfff) / 4; + uint32_t value = s->i2c[bus].regs[reg]; + if (reg == 8) { value = s->i2c[bus].fifo[s->i2c[bus].readpos++ % 256]; } + if (s->i2c[bus].trace++ < 1000) { + qemu_log_mask(LOG_UNIMP, "n88 i2c%d R %02x = %08x\n", bus, reg * 4, value); + } + return value; + } + /* Compteur libre 24 MHz, confirmé par le noyau et openiBoot S5L8920. */ + if (addr == 0x3f100200 || addr == 0x3f100204) { + uint64_t ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint64_t ticks = (ns / 1000) * 24 + ((ns % 1000) * 24) / 1000; + return addr == 0x3f100200 ? (uint32_t)ticks : ticks >> 32; + } + if (addr == 0x3f100208) { return s->timer_count; } + if (addr == 0x3f100220) { return s->timer_control; } + if (s->unknown_reads++ < 200) { + qemu_log_mask(LOG_UNIMP, "n88: lecture MMIO non implémentée @0x%08" HWADDR_PRIx " taille %u\n", + addr + 0x80000000, size); + } + return 0; +} + +static void n88_io_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) +{ + IOS6N88State *s = opaque; + if ((addr & ~0xfffULL) == 0x3fe00000 || (addr & ~0xfffULL) == 0x3ff00000) { + unsigned index = (addr >> 20) & 1; + unsigned offset = addr & 0xfff; + s->dart[index][offset / 4] = value; + if (offset == 8 && (value & 1)) { + s->dart_tables[index][(value >> 8) & 15] = value & 0x0ffff000; + } + if (s->dart_trace++ < 300) { + qemu_log_mask(LOG_UNIMP, "n88 dart%u W %04x = %08" PRIx64 "\n", index, offset, value); + } + return; + } + if (addr >= 0x05400000 && addr < 0x05403000) { + unsigned offset = addr - 0x05400000; + uint32_t stored = value; + if (offset == 0) { stored = (stored & ~0x100) | 2; } + if (offset == 0xc || offset == 0x1b2c) { stored = s->clcd[offset / 4] & ~value; } + if (offset == 0x1b10) { stored |= 2; } + s->clcd[offset / 4] = stored; + if (offset == 8 || offset == 0xc) { n88_display_irq(s); } + if (offset != 0x408 && offset != 0x40c && s->display_trace++ < 3000) { + qemu_log_mask(LOG_UNIMP, "n88 clcd W %04x = %08" PRIx64 "\n", offset, value); + } + return; + } + if (addr >= 0x09000000 && addr < 0x09001000) { + unsigned offset = addr - 0x09000000; + if (offset == 0x2c) { s->dsim[offset / 4] &= ~value; } + else { s->dsim[offset / 4] = value; } + if (offset == 0x34) { s->dsim[0x2c / 4] |= 0x10000000; } + if (s->display_trace++ < 3000) { + qemu_log_mask(LOG_UNIMP, "n88 dsim W %04x = %08" PRIx64 "\n", offset, value); + } + return; + } + int bus = (addr & ~0xfffULL) == 0x03200000 ? 0 : + (addr & ~0xfffULL) == 0x03400000 ? 1 : -1; + if (bus >= 0 && (addr & 0xfff) < 0x40) { + unsigned reg = (addr & 0xfff) / 4; + if (s->i2c[bus].trace++ < 1000) { + qemu_log_mask(LOG_UNIMP, "n88 i2c%d W %02x = %08" PRIx64 "\n", bus, reg * 4, value); + } + if (reg == 3) { + s->i2c[bus].regs[3] &= ~value; + qemu_set_irq(s->i2c[bus].irq, !!s->i2c[bus].regs[3]); + } else if (reg == 8) { + s->i2c[bus].fifo[s->i2c[bus].count++ % 256] = value; + } else { + s->i2c[bus].regs[reg] = value; + } + if (reg == 9 && (value == 4 || value == 5)) { + unsigned slave = s->i2c[bus].regs[0] & 127; + unsigned count = MIN(s->i2c[bus].regs[6], 256); + uint8_t *cursor = &s->i2c[bus].cursor[slave]; + *cursor = s->i2c[bus].regs[4]; + /* LIS302DL: bit 7 selects register auto-increment, not a bank. */ + if (bus == 0 && slave == 0x1d) { + *cursor &= 0x7f; + } + if (value == 5 && s->i2c[bus].count) { + for (unsigned i = 0; i < MIN(s->i2c[bus].count, 256); i++) { + s->i2c[bus].devices[slave][(*cursor)++] = s->i2c[bus].fifo[i]; + } + if (bus == 0 && slave == 0x1d) { + /* CTRL_REG2 BOOT self-clears after loading calibration. */ + s->i2c[bus].devices[slave][0x21] &= ~0x40; + } + } else if (value == 4) { + for (unsigned i = 0; i < count; i++) { + s->i2c[bus].fifo[i] = s->i2c[bus].devices[slave][(*cursor)++]; + } + } + s->i2c[bus].count = s->i2c[bus].readpos = 0; + s->i2c[bus].regs[3] = 0x10; + qemu_set_irq(s->i2c[bus].irq, 1); + } + return; + } + if (addr == 0x3f100208) { + s->timer_count = value; + n88_timer_schedule(s); + return; + } + if (addr == 0x3f100220) { + s->timer_control = value; + if (value & 2) { qemu_set_irq(s->timer_irq, 0); } + n88_timer_schedule(s); + return; + } + if (s->unknown_writes++ < 200) { + qemu_log_mask(LOG_UNIMP, "n88: écriture MMIO non implémentée @0x%08" HWADDR_PRIx " = 0x%" PRIx64 "\n", + addr + 0x80000000, value); + } +} + +static const MemoryRegionOps n88_io_ops = { + .read = n88_io_read, + .write = n88_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { .min_access_size = 1, .max_access_size = 4 }, +}; + +/* Lecture DMA du LCD : RAM directe à l'amorçage, puis fenêtre legacy-DART. */ +static bool n88_display_read(IOS6N88State *s, uint32_t source, uint8_t *output, unsigned remaining) +{ + uint8_t *ram = memory_region_get_ram_ptr(&s->ram); + while (remaining) { + uint32_t physical = source; + unsigned count = MIN(remaining, 0x1000 - (source & 0xfff)); + if (source >= 0x3c000000 && source < 0x40000000) { + /* 16 tables de 1024 PTE, pages de 4 Kio, adresses RAM 28 bits. */ + unsigned page = (source - 0x3c000000) >> 12; + uint32_t table = s->dart_tables[0][page >> 10]; + if (!table || table > 0x10000000 - 0x1000) { return false; } + uint32_t pte = ldl_le_p(ram + table + (page & 1023) * 4); + if (!(pte & 1)) { return false; } + physical = s->ram_base + (pte & 0x0ffff000) + (source & 0xfff); + } + if (physical < s->ram_base || + (uint64_t)physical + count > s->ram_base + 0x10000000ULL) { return false; } + memcpy(output, ram + physical - s->ram_base, count); + source += count; + output += count; + remaining -= count; + } + return true; +} + +static void n88_display_update(void *opaque) +{ + IOS6N88State *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->console); + for (unsigned row = 0; row < 480; row++) { + memset(surface_data(surface) + row * surface_stride(surface), 0, 320 * 4); + } + /* Les deux fenêtres RGB utilisées par le pilote AppleM2CLCD : fond et UI. */ + for (unsigned plane = 0; plane < 2; plane++) { + unsigned base = (0x20 + plane * 0x20) / 4; + if (!(s->clcd[1] & (0x10 << plane))) { continue; } + uint32_t control = s->clcd[base]; + unsigned format = (control >> 8) & 7; + unsigned pixel_size = format == 7 ? 4 : format == 4 ? 2 : 0; + if (!pixel_size) { continue; } + unsigned x = s->clcd[base + 5] >> 16, y = s->clcd[base + 5] & 0xffff; + if (x >= 320 || y >= 480) { continue; } + unsigned width = MIN(s->clcd[base + 4] >> 16, 320 - x); + unsigned height = MIN(s->clcd[base + 4] & 0xffff, 480 - y); + unsigned pitch = s->clcd[base + 2]; + if (pitch < width || pitch > 4096) { continue; } + for (unsigned row = 0; row < height; row++) { + uint8_t line[320 * 4]; + uint64_t address = (uint64_t)s->clcd[base + 1] + row * pitch * pixel_size; + if (address > UINT32_MAX || !n88_display_read(s, address, line, width * pixel_size)) { break; } + uint32_t *output = (uint32_t *)(surface_data(surface) + (row + y) * surface_stride(surface)) + x; + for (unsigned col = 0; col < width; col++) { + uint32_t color; + if (pixel_size == 2) { + uint16_t pixel = lduw_le_p(line + col * 2); + color = (((pixel >> 11) * 255 / 31) << 16) | + ((((pixel >> 5) & 63) * 255 / 63) << 8) | ((pixel & 31) * 255 / 31); + } else { + color = ldl_le_p(line + col * 4); + } + if (pixel_size == 4 && (control & 0x10000)) { + unsigned inverse_alpha = 255 - (color >> 24); + uint32_t blended = 0; + for (unsigned shift = 0; shift < 24; shift += 8) { + unsigned component = ((color >> shift) & 255) + + (((output[col] >> shift) & 255) * inverse_alpha + 127) / 255; + blended |= MIN(component, 255) << shift; + } + output[col] = blended; + } else { + output[col] = color & 0xffffff; + } + } + } + } + dpy_gfx_update(s->console, 0, 0, 320, 480); +} + +static const GraphicHwOps n88_display_ops = { + .gfx_update = n88_display_update, +}; + +static void n88_reset(void *opaque) +{ + IOS6N88State *s = opaque; + cpu_reset(CPU(s->cpu)); + s->cpu->env.regs[0] = s->ram_base + 0x01000000; + s->cpu->env.regs[1] = 0; + cpu_set_pc(CPU(s->cpu), s->entry); + s->unknown_reads = s->unknown_writes = s->display_trace = 0; + for (unsigned i = 0; i < 2; i++) { + memset(s->i2c[i].regs, 0, sizeof(s->i2c[i].regs)); + memset(s->i2c[i].devices, 0, sizeof(s->i2c[i].devices)); + memset(s->i2c[i].fifo, 0, sizeof(s->i2c[i].fifo)); + memset(s->i2c[i].cursor, 0, sizeof(s->i2c[i].cursor)); + s->i2c[i].count = s->i2c[i].readpos = s->i2c[i].trace = 0; + qemu_set_irq(s->i2c[i].irq, 0); + } + s->i2c[0].devices[0x1d][0x0f] = 0x3b; /* LIS302DL WHO_AM_I */ + s->i2c[0].devices[0x1d][0x20] = 0x07; /* axes enabled, power-down */ + s->i2c[0].devices[0x1d][0x27] = 0x0f; /* stationary sample available */ + s->i2c[0].devices[0x1d][0x2d] = 56; /* approximately +1 g on Z */ + memset(s->clcd, 0, sizeof(s->clcd)); + memset(s->dsim, 0, sizeof(s->dsim)); + memset(s->dart, 0, sizeof(s->dart)); + memset(s->dart_tables, 0, sizeof(s->dart_tables)); + s->dart_trace = 0; + s->clcd[0] = 0x20087; + s->clcd[1] = 0x10; + s->clcd[0x20 / 4] = 0x200700; + s->clcd[0x24 / 4] = s->ram_base + 0x0f000000; + s->clcd[0x28 / 4] = 320; + s->clcd[0x30 / 4] = (320 << 16) | 480; + s->clcd[0x1b10 / 4] = 3; + s->clcd[0x1b24 / 4] = (319 << 16) | 479; + timer_del(s->timer); + s->timer_count = s->timer_control = 0; + qemu_set_irq(s->timer_irq, 0); + qemu_set_irq(s->display_irq, 0); + timer_mod(s->vblank_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 16666667); +} + +static void n88_init(MachineState *machine) +{ + IOS6N88State *s = IOS6_N88(machine); + MemoryRegion *sysmem = get_system_memory(); + Object *cpuobj = object_new(machine->cpu_type); + s->ram_base = s->rootfs ? 0x70000000 : 0x40000000; + s->cpu = ARM_CPU(cpuobj); + object_property_set_bool(cpuobj, "has_el3", false, &error_fatal); + object_property_set_link(cpuobj, "memory", OBJECT(sysmem), &error_fatal); + qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); + memory_region_init_ram(&s->ram, NULL, "n88.ram", 0x10000000, &error_fatal); + memory_region_add_subregion(sysmem, s->ram_base, &s->ram); + /* Le bootstrap 10B500 installe ses vecteurs via un mapping de la page 0. + * Modèle minimal de cette page, sans prétendre émuler la BootROM. */ + memory_region_init_ram(&s->vectors, NULL, "n88.vector-page", 0x1000, &error_fatal); + memory_region_add_subregion(sysmem, 0, &s->vectors); + memory_region_init_io(&s->io, OBJECT(machine), &n88_io_ops, s, "n88.unimplemented-io", 0x40000000); + memory_region_add_subregion_overlap(sysmem, 0x80000000, &s->io, -10); + for (unsigned i = 0; i < 3; i++) { + char name[16]; + snprintf(name, sizeof(name), "n88.vic%u", i); + DeviceState *dev; + if (i == 0) { + dev = pl192_manual_init(name, + qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ), + qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ), NULL); + } else { + dev = pl192_manual_init(name, NULL); + } + s->vic[i] = PL192(dev); + if (i) { s->vic[i]->daisy = s->vic[i - 1]; } + memory_region_add_subregion_overlap(sysmem, 0xbf200000 + 0x10000 * i, + &s->vic[i]->iomem, 0); + } + s->i2c[0].irq = qdev_get_gpio_in(DEVICE(s->vic[0]), 19); + s->i2c[1].irq = qdev_get_gpio_in(DEVICE(s->vic[0]), 17); + s->timer_irq = qdev_get_gpio_in(DEVICE(s->vic[0]), 6); + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, n88_timer_expired, s); + s->display_irq = qdev_get_gpio_in(DEVICE(s->vic[1]), 5); /* DeviceTree : IRQ 37. */ + s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, n88_vblank, s); + /* Le DeviceTree original désigne uart0 (0x82500000) comme boot-console. */ + ipod_touch_uart_create(0x82500000, serial_hd(0), + qdev_get_gpio_in(DEVICE(s->vic[0]), 24)); + ipod_touch_uart_create(0x82600000, serial_hd(1), + qdev_get_gpio_in(DEVICE(s->vic[0]), 23)); + if (!machine->kernel_filename || !machine->dtb) { + error_report("ios6-n88 attend -kernel kernel.bin et -dtb boot-data.bin (tools/prepare_boot.py)"); + exit(1); + } + if (load_image_targphys(machine->kernel_filename, s->ram_base + 0x1000, 0x00fff000) < 0 || + load_image_targphys(machine->dtb, s->ram_base + 0x01000000, 0x01000000) < 0) { + error_report("Chargement du noyau ou des paramètres impossible"); + exit(1); + } + if (s->rootfs) { + int64_t size = machine->initrd_filename ? get_image_size(machine->initrd_filename) : -1; + if (size <= 0 || size > 0x6ff00000 || size % 4096) { + error_report("rootfs attend une image HFS alignée à 4096 octets, < 1791 Mio"); + exit(1); + } + /* MAP_PRIVATE : les écritures invitées ne modifient jamais l'image. */ + memory_region_init_ram_from_file(&s->rootdisk, NULL, "n88.rootdisk", + size, 0, RAM_READONLY_FD, machine->initrd_filename, 0, &error_fatal); + memory_region_add_subregion(sysmem, 0x00100000, &s->rootdisk); + } else if (machine->initrd_filename && + load_image_targphys(machine->initrd_filename, 0x42000000, 0x02000000) < 0) { + error_report("Chargement du disque de restauration impossible"); + exit(1); + } + /* Entrée LC_UNIXTHREAD vérifiée par prepare_boot.py pour 10B500/n88. */ + s->entry = s->ram_base + 0x86084; + s->console = graphic_console_init(NULL, 0, &n88_display_ops, s); + qemu_console_resize(s->console, 320, 480); + qemu_register_reset(n88_reset, s); +} + +static bool n88_get_rootfs(Object *obj, Error **errp) +{ + return IOS6_N88(obj)->rootfs; +} + +static void n88_set_rootfs(Object *obj, bool value, Error **errp) +{ + IOS6_N88(obj)->rootfs = value; +} + +static void n88_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "iPhone 3GS iOS 6: prototype de démarrage du noyau"; + mc->init = n88_init; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a8"); + mc->default_ram_size = 0x10000000; + mc->max_cpus = 1; + object_class_property_add_bool(oc, "rootfs", n88_get_rootfs, n88_set_rootfs); +} + +static const TypeInfo n88_type = { + .name = TYPE_IOS6_N88, + .parent = TYPE_MACHINE, + .instance_size = sizeof(IOS6N88State), + .class_init = n88_class_init, +}; + +static void n88_register(void) +{ + type_register_static(&n88_type); +} +type_init(n88_register) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 8fcab09e39..d72991e3c8 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -73,3 +73,5 @@ system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) system_ss.add(when: 'CONFIG_TOSA', if_true: files('tosa.c')) hw_arch += {'arm': arm_ss} + +arm_ss.add(when: 'CONFIG_IPOD_TOUCH_2G', if_true: files('ios6_n88.c')) diff --git a/ios6/.gitignore b/ios6/.gitignore new file mode 100644 index 0000000000..6286b212f8 --- /dev/null +++ b/ios6/.gitignore @@ -0,0 +1,7 @@ +.venv/ +build/ +firmware/ +references/ +evidence/ +__pycache__/ +run/ diff --git a/ios6/AGENTS.md b/ios6/AGENTS.md new file mode 100644 index 0000000000..dbddc9f4d0 --- /dev/null +++ b/ios6/AGENTS.md @@ -0,0 +1,39 @@ +# Émulateur iOS 6 pour le développement natif + +Lire `README.md` pour la procédure complète et les limites. Le projet exécute +iOS 6.1.6 (10B500) pour iPhone 3GS dans QEMU. Il sert aux agents à compiler, +installer et tester des apps natives ARM32, via les commandes locales. +L’utilisateur ne souhaite pas de panneau web de pilotage. + +## Utilisation + +- Travailler depuis `/home/dev/ios6-emu`. Vérifier `bin/ios6 status` et + `bin/ios6 front` avant de modifier une session existante. +- Démarrer avec `bin/ios6 start`, puis attendre avec `bin/ios6 wait`. +- Compiler l’exemple : `.venv/bin/python tools/build_app.py`. +- Installer : `bin/ios6 install build/apps/SimulatorLab.app`. + L’installation sauvegarde le disque et redémarre la VM. +- Lancer : `bin/ios6 launch org.ios6emu.SimulatorLab`. +- Piloter avec `tap`, `swipe`, `home` ; observer avec `screenshot`, `logs`, + `front`, `get` et `exec`. Consulter `bin/ios6 --help` pour les arguments. +- Tester l’exemple installé : `.venv/bin/python tools/smoke_test.py`. + Les rapports et captures sont conservés dans `run/reports/`. + +## État partagé et conservation des données + +La VM et son disque sont partagés par les sessions de cet environnement. +Éviter les tests, installations et gestes concurrents : ils modifient le même +état invité. Une session déjà démarrée peut être utilisée par une autre tâche. +Pour arrêter en conservant les changements, utiliser `bin/ios6 stop`. +Tuer QEMU ou utiliser `stop --discard` perd les changements non sauvegardés. +Ne jamais modifier le disque hors ligne pendant que la VM tourne. +Ne pas réinitialiser un disque existant dans le cadre d’un simple test d’app. +L’émulateur iPhone OS 2 dans `/srv/homes/iphoneos2/ipod-emu` est indépendant. + +## Portée des résultats + +La chaîne a été validée avec l’app UIKit `SimulatorLab` : compilation, +installation, lancement, gestes, fichiers, logs, HTTP vers l’hôte et persistance +après redémarrage. GPU, audio, caméra, modem, GPS et stockage sécurisé matériel +ne sont pas reproduits. La compatibilité de chaque nouvelle app doit être +vérifiée ; ne pas présenter ces tests comme une validation sur iPhone physique. diff --git a/ios6/README.md b/ios6/README.md new file mode 100644 index 0000000000..de10c8dd4e --- /dev/null +++ b/ios6/README.md @@ -0,0 +1,377 @@ +# Simulateur expérimental iOS 6 — développement natif + +Le vrai **iOS 6.1.6 (10B500, iPhone 3GS)** tourne dans QEMU, avec SpringBoard, +UIKit et un écran 320 × 480. On peut compiler une app Objective-C ARM32, +l’installer, la lancer, envoyer des gestes et vérifier ses fichiers et ses logs. +Cet outil est destiné au pilotage direct par l’agent de développement : +commandes locales, captures du framebuffer réel et tests automatisés. + +Il vit dans le dossier `ios6/` du fork `Mahtan/qemu-ios` ; le modèle de machine +est `hw/arm/ios6_n88.c`, compilé avec le reste du dépôt. Sur le workbench, la +copie de travail est le worktree `/home/dev/qemu-ios-ios6` (branche +`agent/ios6-n88`), et `/home/dev/ios6-emu` pointe vers son dossier `ios6/`. + +## Utiliser la session locale + +```bash +cd /home/dev/ios6-emu +bin/ios6 start +bin/ios6 wait +``` + +L’agent utilise `bin/ios6` pour installer et lancer les apps, envoyer des gestes, +inspecter leur état et récupérer les preuves de test. Les fonctions de +`tools/simulator.py` sont aussi accessibles depuis les scripts Python. + +```bash +# Construire et installer l’exemple natif +.venv/bin/python tools/build_app.py +bin/ios6 install build/apps/SimulatorLab.app +bin/ios6 launch org.ios6emu.SimulatorLab + +# Piloter et observer +bin/ios6 tap 160 296 +bin/ios6 swipe 160 400 160 140 --ms 600 +bin/ios6 front +bin/ios6 screenshot run/capture.png +bin/ios6 logs SimulatorLab +bin/ios6 get /var/mobile/Library/Caches/SimulatorLab-result.txt run/result.txt +bin/ios6 home + +# Test automatisé de l’exemple installé +.venv/bin/python tools/smoke_test.py + +# Conserver les données et arrêter +bin/ios6 stop +``` + +`install` copie le bundle sous `/Applications/.app`, sauvegarde le +disque et redémarre iOS pour enregistrer l’app. Prévoir quelques minutes. +Les bundles acceptés contiennent un exécutable Mach-O ARM32 signé ad hoc ; +les identifiants Apple et les liens symboliques sont refusés. +Une mise à jour remplace les fichiers fournis ; les anciens fichiers supplémentaires +restent présents. Les apps sont installées comme applications système de développement, +pas dans un conteneur App Store : les tests d’isolation ne sont pas représentatifs. + +**Persistance : utiliser `bin/ios6 stop`.** Le disque invité est en RAM ; cette +commande synchronise, suspend la VM, extrait le volume et rejoue son journal HFS +dans une nouvelle copie avant de remplacer `firmware/rootfs-dev.hfs`. +Tuer QEMU perd les changements depuis la dernière sauvegarde. +`stop --discard` abandonne explicitement ces changements. +Ne jamais modifier hors ligne le disque d’une VM active. + +Le test `smoke_test.py` contrôle SpringBoard, le lancement de l’app, +la remise à zéro et trois incréments UIKit, le transfert binaire, +un aller-retour HTTP vers l’hôte et les traces NSLog. Il conserve capture, +logs et rapport dans `run/reports/`. Les preuves de validation sont dans +[`results/20261003-native-apps`](results/20261003-native-apps). + +## Sur macOS (non encore essayé) + +Le disque et le noyau préparés ne sont pas dans Git : ce sont des fichiers Apple +(1,8 Gio). Une archive prête est sur le NAS, dans le gestionnaire de fichiers UGOS : +`data/workbench/home/ios6-emu-mac/ios6-emu-firmware.tar.zst` (930 Mio, SHA-256 +dans le fichier `.sha256` voisin). Elle contient `firmware/rootfs-dev.hfs`, +`firmware/kernel-rootfs.bin` et `firmware/boot-display-rootfs.bin`, soit le +nécessaire pour `bin/ios6 start`. + +```bash +brew install ninja pkgconf glib pixman libpng jpeg-turbo openssl@3 libslirp zstd +git clone https://git.mahtan-melwasul.com/Mahtan/qemu-ios.git && cd qemu-ios/ios6 +shasum -a 256 -c ~/Downloads/ios6-emu-firmware.tar.zst.sha256 # depuis ~/Downloads +zstd -dc ~/Downloads/ios6-emu-firmware.tar.zst | tar -xf - +export PKG_CONFIG_PATH="$(brew --prefix openssl@3)/lib/pkgconfig:$PKG_CONFIG_PATH" +bash tools/build.sh # compile ce dépôt QEMU dans ios6/build, puis netd +bin/ios6 start && bin/ios6 wait +.venv/bin/python tools/smoke_test.py +``` + +L’affichage est accessible en VNC sur +`127.0.0.1:5906` (Partage d’écran : `vnc://127.0.0.1:5906`). +`bin/ios6 stop` sauvegarde le disque dans `firmware/rootfs-dev.hfs` du Mac ; +il reste indépendant de celui du workbench. + +Restent propres au workbench Linux : la préparation du disque +(`prepare_rootfs.py`, `prepare_dev.py`, qui utilisent `ipod-emu`), la compilation de +l’agent invité (`build_agent.sh`) et celle des apps (`build_app.py`), qui demandent +`/opt/cctools` et les SDK locaux. Compiler les apps sur le workbench puis les copier +sur le Mac pour `bin/ios6 install`. + +## Préparer un autre projet natif + +L’exemple est dans [`apps/SimulatorLab/main.m`](apps/SimulatorLab/main.m). +Le petit script de compilation assemble les fichiers `.m` à la racine d’un projet, +crée son Info.plist et signe son exécutable avec `ldid` : + +```bash +.venv/bin/python tools/build_app.py apps/MonApp --bundle-id org.exemple.MonApp +bin/ios6 install build/apps/MonApp.app +bin/ios6 launch org.exemple.MonApp +``` + +Le SDK iOS 6.0 est local sous `references/sdk/iPhoneOS6.0.sdk`. Le script +`tools/prepare_sdk.py` récupère les trois archives du dépôt +[Sn0wCooder/theos-sdks](https://github.com/Sn0wCooder/theos-sdks/tree/3111c86f53b021dabee781aec3fb2d9fb8f3f862/iPhoneOS6.0.sdk), +à une révision fixe, et vérifie le SHA-256 de l’archive assemblée. +Un SDK existant peut être choisi avec `--sdk` ou `IOS6_SDK`. +La compilation utilise le cross-compilateur `/opt/cctools/bin/arm-apple-darwin9-gcc` +et `ldid` déjà présents dans cet environnement. Ce script est un point de départ +pour les projets Objective-C simples ; il ne gère pas les projets Xcode, +les storyboards ou la copie des ressources. Pas de prise en charge Swift. + +Pour préparer un disque de développement après la préparation du firmware +et du profil complet décrite plus bas : + +```bash +python3 tools/prepare_boot.py --profile display --rootfs --software-rendering +bash tools/build.sh +python3 tools/prepare_dev.py +python3 tools/prepare_sdk.py +``` + +`prepare_dev.py` conserve le disque de démonstration et ajoute l’agent au nouveau +`rootfs-dev.hfs`. Il exige `--reset` pour remplacer un disque de développement +existant et refuse d’opérer sur une VM active. L’agent est compilé avec le SDK +2.2.1 local pour ses API C communes ; les apps utilisent le SDK 6.0. +Le relais hôte nécessite les en-têtes et la bibliothèque `libslirp` via pkg-config. + +## Réseau et automatisation + +UART1 (`0x82600000`, IRQ 23, `/dev/tty.debug`) transporte PPP en mode PIO. +L’agent publie la configuration réseau dans configd. L’invité utilise +`10.0.2.15`, la passerelle hôte `10.0.2.2` et le relais DNS `10.0.2.3`. +Un serveur HTTP local peut donc être testé depuis une app via `http://10.0.2.2:PORT`. +Les anciens protocoles TLS d’iOS 6 peuvent être incompatibles avec les services modernes. + +L’agent root est accessible sur **127.0.0.1:7796** +et l’affichage VNC sur **127.0.0.1:5906**. Le tactile passe par GraphicsServices, +pas par VNC. Ces interfaces de développement restent liées à la boucle locale. +`bin/ios6 exec /chemin/programme arguments` lance un processus invité et transmet +son code de sortie ; `put fichier-local /chemin/invité` et `get` transfèrent les fichiers. +Les journaux de session et les PID sont sous `run/`. +L’autorisation `com.apple.springboard.launchapplications` de l’agent permet +le lancement avec `SBSLaunchApplicationWithIdentifier`. + +## Portée et limites + +La chaîne a été validée avec l’app UIKit fournie. La compatibilité de chaque +nouvelle app reste à tester. Le GPU, l’audio, la caméra, le modem, le GPS et +le stockage sécurisé matériel ne sont pas reproduits. L’accéléromètre est fixe. +Il n’y a pas encore de saisie directe au clavier hôte, de multitouch, +de débogueur source intégré, ni d’installation IPA/App Store. +Ce prototype permet des tests fonctionnels ; ses performances et son matériel +ne représentent pas fidèlement un iPhone physique. + +![App native de test](results/20261003-native-apps/native-app.png) + +## Historique du démarrage système + +Le premier écran d’accueil a été reproduit depuis le firmware original en +approximativement 220 secondes, puis vers 115 secondes depuis un volume initialisé. +Les captures proviennent du framebuffer invité. + +![SpringBoard iOS 6](results/20261003-springboard/springboard.png) + +Deux profils permettent de distinguer les résultats : + +- Le profil initial exécute le noyau sans patch, avec le disque de restauration. + Il reste conservé comme référence et bloque sur le coprocesseur ARM7M. +- Le profil expérimental `display --rootfs` charge le système complet, omet + les dépendances audio/IOP et la boussole, et adapte le memdev au stockage physique. Il emploie + quatre modifications d'opcodes documentées dans `prepare_boot.py` (deux pour + le disque, deux pour borner l'attente de l'horloge), avec vérification des + octets originaux. Le DeviceTree désactive le protocole NAND `secure-root-prefix` + selon le chemin prévu par AppleARMPE et renseigne le panneau Pinot. + +L'horloge, le PMU et le panneau LCD sont reconnus grâce aux modèles expérimentaux +I²C/CLCD/DSIM. La sortie graphique traduit les tables legacy-DART remplies +par AppleH2PDART et émet l'interruption LCD 37 à 60 Hz. +Ces modèles sont partiels : ils ne constituent pas une émulation +fidèle du matériel, ni du GPU. Un trousseau temporaire en RAM est accepté par +AppleKeyStore ; le stockage sécurisé matériel reste absent. + +iBoot et la BootROM ne sont pas exécutés : le noyau est lancé directement. +Le texte « iBoot version » vient d'une propriété du DeviceTree. + +### Essai du système complet + +```bash +python3 tools/prepare_rootfs.py # recrée uniquement la copie rootfs-vm.hfs +python3 tools/prepare_boot.py --profile display --rootfs --software-rendering +bash tools/build.sh +python3 tools/probe.py --seconds 300 --boot-data boot-display-rootfs.bin \ + --rootfs --capture-interval 10 --dump-disk +``` + +Le disque original reste dans `firmware/rootfs.hfs`. Sa copie expérimentale +`rootfs-vm.hfs` est agrandie à 1776 Mio et reçoit un `fstab` pour `/dev/md0`, +la journalisation ASL. `--software-rendering` omet le GPU SGX du DeviceTree. +Les variables `CA_NO_ACCEL=1`, `CA_ENABLE_OGL=0` sont également transmises +aux processus graphiques. Le paramètre `launchctl_enforce_codesign=0`, lu +par le `launchctl` original, permet le chargement des plists locales au lieu +des configurations précompilées du cache xpcd. Leur prise en compte a été +vérifiée dans l'environnement des processus invités. +Le programme expérimental `src/keybag_init.c`, installé comme `ios6-session`, +initialise un trousseau de session sans clé matérielle, via les fonctions Apple +du cache 10B500. Le démon `keybagd` original est conservé et son job est écarté. +Le service de session n'enregistre pas son port IPC : les clients n'attendent +donc pas une réponse d'un serveur absent. Le profil ajoute +`amfi_allow_any_signature=1` pour exécuter cet outil local. +Le service vérifie l'absence de code de verrouillage avant d'envoyer le geste +initial. Il réveille ensuite régulièrement l'écran pendant six minutes pour +la démonstration, puis cesse toute entrée automatique. +Le bundle `CommCenterMigrator.migrator` est conservé dans +`/System/Library/EmulatorDisabledMigrators` : la migration cellulaire attendait +indéfiniment le modem absent. Les autres migrations Apple restent exécutées. +Les capacités cellulaires de `N88AP.plist` sont désactivées pour éviter +les fonctions liées au modem. Cela ne suffit pas à supprimer les appels +CoreTelephony de SpringBoard : les jobs `CommCenter*` sont aussi conservés +hors de `LaunchDaemons`, dans `EmulatorDisabledDaemons`. +Le job `mediaserverd` est également écarté, l'audio n'étant pas émulé. +Les tables de polices Apple U/UR remplacent les tables H/HR qui désignent +des fichiers absents de ce firmware. Le modèle LIS302DL acquitte son reset +et fournit une accélération verticale fixe. Le bundle `Setup.app` est conservé +dans `EmulatorDisabledApplications`, hors du répertoire des applications : +l'assistant exige des services d'activation absents de cette VM. +L'état d'activation n'est pas modifié. +La sonde `probe.py` utilise un mapping privé : les écritures invitées disparaissent à l'arrêt, +sauf si `--dump-disk` conserve un instantané `guest-disk.hfs` dans les preuves. + +`--diagnostic-panic` provoque volontairement une panique à la fin d'un essai +pour relever les processus et piles. Le journal précédant cette intervention +est sauvegardé séparément ; ce diagnostic ne compte pas comme une panique +spontanée. La sonde conserve aussi `screen.ppm`, sans modifier ses pixels. +`--capture-interval 30` ajoute des captures pendant le démarrage ; `--dump-ram` +conserve les 256 Mio de RAM. Un essai est borné à 300 secondes au maximum. +`python3 tools/verify.py --rootfs` vérifie les artefacts et le plan mémoire ; +`python3 tools/verify_display.py` teste séparément le périphérique graphique +avec des pixels synthétiques, sans démarrer iOS. + +Un premier démarrage a terminé les migrations Apple sur le volume agrandi. +Son instantané local est conservé dans `firmware/rootfs-initialized.hfs` ; +son journal HFS est encore actif. Les modifications hors ligne de cet +instantané ont produit des erreurs de lecture au démarrage suivant. +`tools/recover_hfs.py source.hfs nouvelle-copie.hfs` vérifie et rejoue ses +transactions dans une nouvelle copie avant toute modification hors ligne. +Il contrôle les sommes de contrôle du journal et des blocs ; il ne remplace +pas une vérification complète du système de fichiers. Le premier essai de +cette récupération a permis de reprendre l'état initialisé et de modifier +les configurations sans reproduire l'erreur de lecture du profil. +`prepare_rootfs.py` repart toujours du firmware original et réinitialise cette +copie de travail. Le fichier `evidence/initialized-rootfs.json` trace l'instantané. + +## Reproduire dans cet environnement + +Les commandes se lancent depuis `ios6/` (`/home/dev/ios6-emu` sur le workbench). +L'émulateur iPhone OS 2 et sa session sous `/srv/homes/iphoneos2/ipod-emu` +ne sont pas modifiés. + +```bash +cd /home/dev/ios6-emu +python3 tools/verify.py +python3 tools/probe.py --seconds 10 --trace \ + --expect-panic 'ARM7M not stopped for some reason' +``` + +La sonde crée un nouveau dossier `evidence/probe-/` par essai, conserve +`serial.log`, `qemu.log`, `stderr.log`, la commande et les registres dans +`result.json`, puis arrête uniquement la VM qu'elle a lancée. Elle utilise un +socket QMP privé, sans ouvrir de port TCP. La durée est bornée à 300 secondes et +la trace à environ 32 Mio (contrôle toutes les 50 ms). + +Sans `--expect-panic`, une panique invitée produit le code de sortie **2**. +Avec cette option, le code **0** signifie que le blocage attendu a été retrouvé, +pas que le système a fini de démarrer. `guest_status` et `milestones` décrivent +séparément les progrès observés. + +Une trace ciblée est possible : + +```bash +python3 tools/probe.py --seconds 5 \ + --trace-range 0x8065f600..0x8065f800 \ + --expect-panic 'ARM7M not stopped for some reason' +.venv/bin/python tools/kernel_address.py 0x8065f7c0 +``` + +## Préparation et compilation + +Les fichiers Apple, les téléchargements de référence, l'environnement Python +et les sorties de compilation restent locaux et sont exclus de Git. + +```bash +cd /home/dev/ios6-emu +python3 tools/prepare_firmware.py +python3 tools/prepare_boot.py +bash tools/build.sh +python3 tools/verify.py +``` + +`prepare_firmware.py` récupère l'IPSW directement chez Apple, vérifie son SHA-256, +extrait les conteneurs IMG3 avec OpenSSL et vérifie la taille et l'Adler32 du +noyau LZSS. Le dernier bloc AES doit être lu avec le padding du tag IMG3 : +l'ancien `xpwntool` disponible ici tronquait 45 octets à la décompression. + +`tools/build.sh` configure le dépôt parent dans `ios6/build` (cible +`arm-softmmu` seule) : il ne touche pas au `build/` de l'émulateur 2.2.1. +Le modèle **`hw/arm/ios6_n88.c`** est déclaré dans `hw/arm/meson.build` +sous `CONFIG_IPOD_TOUCH_2G` ; le binaire contient donc aussi la machine iPod. + +Dépendances de compilation : GCC, Git, Ninja, pkg-config, Python avec venv, +en-têtes GLib/Pixman/OpenSSL et dépendances habituelles de ce fork QEMU. +Les paquets Python sont isolés dans `.venv`. L'image du workbench ne fournit +pas encore Ninja, pkgconf, diffutils ni les bibliothèques d'exécution +(pixman, libpng, libjpeg-turbo, alsa-lib, libslirp) : les installer avec +`sudo pacman -S` après chaque redémarrage du conteneur. + +## Matériel et chargement réalisés + +| Élément | Modèle actuel | +|---|---| +| CPU | Cortex-A8 ARMv7 de QEMU, un cœur | +| RAM | 256 Mio physiques à `0x40000000`, dont 240 Mio annoncés utilisables | +| Vecteurs | Page de RAM expérimentale à l'adresse physique zéro | +| Console | UART Samsung à `0x82500000`, réutilisée du fork iPod | +| Interruptions | Trois PL192 à `0xbf200000`, `0xbf210000`, `0xbf220000` | +| Temps | Compteur 24 MHz à `0xbf100200/204` ; timer one-shot `208/220`, ligne VIC 6 | +| Noyau | Segments Mach-O chargés à partir de `0x40001000`, entrée `0x40086084` | +| Paramètres | `boot_args` à `0x41000000`, format Version **5** requis par 10B500 | +| DeviceTree | À `0x41004000`, issu du firmware et complété comme par un chargeur | +| RAMDisk | Disque de restauration Apple à `0x42000000` | +| Autres MMIO | Réponses nulles et traces bornées ; périphériques **non émulés** | + +Le profil `rootfs` relocalise la RAM à `0x70000000`, les paramètres à +`0x71000000` et la VRAM à `0x7f000000`. Son disque de 1776 Mio commence à +`0x00100000` et ne chevauche pas la RAM ni les MMIO. + +Le compteur et le timer sont des modèles minimaux validés pour ce démarrage, +pas une implémentation exhaustive du contrôleur S5L8920. La page de vecteurs +est également un aménagement de démarrage direct, à préciser avant de viser +une émulation fidèle de la chaîne de boot. + +Le DeviceTree reçoit les fréquences CPU, la mémoire, la console et les régions +réservées `pram`/`vram`, ainsi qu'une partition NVRAM `common` vide et valide. +Les placeholders NVRAM nuls du firmware entraînaient une boucle infinie dans +IODTNVRAM. La graine aléatoire fixe est réservée à ces essais reproductibles. +Dans le profil initial, le code du noyau n'est jamais réécrit ; `verify.py` compare chaque segment et +vérifie la mise à zéro du BSS ainsi que les limites des allocations. + +## Preuves du premier démarrage + +Le dossier [`results/20261003-springboard`](results/20261003-springboard) conserve +la capture de l'écran d'accueil. Le dossier +[`results/20261003-first-boot`](results/20261003-first-boot) conserve +le rapport de validation, les empreintes des composants et le journal série +(aux retours de ligne normalisés ; original brut dans `evidence/`). +Deux essais avec la NVRAM corrigée ont reproduit le blocage ARM7M. La trace +du dernier essai contient des FIQ et IRQ réellement prises par le processeur. + +Ces preuves concernent le premier jalon, antérieur au démarrage de `launchd`. +Les essais plus récents et leurs instantanés se trouvent dans `evidence/`. +Les adresses et les symboles se contrôlent avec `kernel_address.py`, +`kext_info.py` et `cache_address.py` (cache dyld avec glissement explicite). + +Références : [QEMU-iOS](https://github.com/devos50/qemu-ios), +[openiBoot S5L8920](https://github.com/iDroid-Project/openiBoot/tree/master/plat-s5l8920), +[structure boot_args Apple](https://github.com/apple-oss-distributions/xnu/blob/main/pexpert/pexpert/arm/boot.h), +[IODTNVRAM Apple](https://github.com/apple-oss-distributions/xnu/blob/xnu-2050.48.11/iokit/Kernel/IONVRAM.cpp). +Les adresses sont recoupées avec le DeviceTree et le noyau exacts du firmware. diff --git a/ios6/agent/entitlements.plist b/ios6/agent/entitlements.plist new file mode 100644 index 0000000000..406afabe3c --- /dev/null +++ b/ios6/agent/entitlements.plist @@ -0,0 +1,6 @@ + + + +com.apple.springboard.launchapplications +com.apple.backboardd.launchapplications + diff --git a/ios6/agent/ios6-agent.c b/ios6/agent/ios6-agent.c new file mode 100644 index 0000000000..238d53acdf --- /dev/null +++ b/ios6/agent/ios6-agent.c @@ -0,0 +1,904 @@ +/* + * Agent de développement iOS 6, lancé par launchd en root. + * Adapté de l'agent local de l'émulateur iPhone OS 2. + * + * 1. Réseau : lance pppd sur l'UART1 (/dev/tty.debug, reliée à ios6-netd côté hôte) et publie le lien dans le + * SCDynamicStore de configd (IPv4, routeur, DNS), pour qu'iOS le considère comme le réseau actif. + * 2. Pilotage : serveur TCP sur le port 7788 (redirigé vers 127.0.0.1:7796 sur l'hôte). + * + * Protocole : une requête = une ligne, champs séparés par des tabulations. Réponse : "OK [info]\n" ou + * "ERR \n", suivi de n octets. Pour "put", n octets de contenu suivent la ligne de requête. + */ +#include +/* SCDynamicStore est dans SystemConfiguration.framework mais marqué indisponible dans les en-têtes iPhone */ +typedef const struct __SCDynamicStore *SCDynamicStoreRef; +extern SCDynamicStoreRef SCDynamicStoreCreate(CFAllocatorRef, CFStringRef, void *, void *); +extern Boolean SCDynamicStoreSetValue(SCDynamicStoreRef, CFStringRef, CFPropertyListRef); +extern CFPropertyListRef SCDynamicStoreCopyValue(SCDynamicStoreRef, CFStringRef); +extern CFArrayRef SCDynamicStoreCopyKeyList(SCDynamicStoreRef, CFStringRef); +#include +#include +#include +#include +#include +#include +#include +/* net/route.h absent du SDK iPhone : structures du socket de routage de XNU 1228 (Darwin 9) */ +struct rt_metrics { + u_int32_t rmx_locks, rmx_mtu, rmx_hopcount; + int32_t rmx_expire; + u_int32_t rmx_recvpipe, rmx_sendpipe, rmx_ssthresh, rmx_rtt, rmx_rttvar, rmx_pksent; + u_int32_t rmx_filler[4]; +}; +struct rt_msghdr { + u_short rtm_msglen; + u_char rtm_version, rtm_type; + u_short rtm_index; + int rtm_flags, rtm_addrs; + pid_t rtm_pid; + int rtm_seq, rtm_errno, rtm_use; + u_int32_t rtm_inits; + struct rt_metrics rtm_rmx; +}; +#define RTM_VERSION 5 +#define RTM_ADD 0x1 +#define RTM_CHANGE 0x3 +#define NET_RT_DUMP 1 +#define RTF_UP 0x1 +#define RTF_GATEWAY 0x2 +#define RTF_STATIC 0x800 +#define RTA_DST 0x1 +#define RTA_GATEWAY 0x2 +#define RTA_NETMASK 0x4 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "ios6-1" +#define PORT 7788 +#define PPP_TTY "/dev/tty.debug" +#define GUEST_IP "10.0.2.15" +#define GW_IP "10.0.2.2" +#define DNS_IP "10.0.2.3" +#define SERVICE_ID "emuppp" /* pppd publie son état sous State:/Network/Service/ */ + +extern char **environ; +static FILE *con; +static pid_t pppd_pid; +static SCDynamicStoreRef store; +static int net_published; + +static void clog_(const char *fmt, ...) +{ + va_list ap; + if (!con) return; + fputs("emuagent: ", con); + va_start(ap, fmt); + vfprintf(con, fmt, ap); + va_end(ap); + fputc('\n', con); +} + +#include "../src/session_input.h" + +/* ---------- réseau ---------- */ + +static void start_pppd(void) +{ + char *argv[] = { "/usr/sbin/pppd", PPP_TTY, "115200", "local", "nocrtscts", "noauth", "nodetach", + "persist", "maxfail", "0", "holdoff", "2", "defaultroute", "noipdefault", + "ipcp-accept-local", "ipcp-accept-remote", "usepeerdns", "novj", "noccp", + "lcp-echo-interval", "0", "mtu", "1500", "mru", "1500", "serviceid", SERVICE_ID, NULL }; + posix_spawn_file_actions_t fa; + posix_spawn_file_actions_init(&fa); + posix_spawn_file_actions_addopen(&fa, 0, "/dev/null", O_RDONLY, 0); + posix_spawn_file_actions_addopen(&fa, 1, "/dev/console", O_WRONLY, 0); + posix_spawn_file_actions_addopen(&fa, 2, "/dev/console", O_WRONLY, 0); + if (posix_spawn(&pppd_pid, argv[0], &fa, NULL, argv, environ) != 0) { + clog_("pppd : échec du lancement (%s)", strerror(errno)); + pppd_pid = 0; + } else { + clog_("pppd lancé (pid %d) sur %s", pppd_pid, PPP_TTY); + } + posix_spawn_file_actions_destroy(&fa); +} + +static int ppp_up(char *ifname, size_t len) +{ + struct ifaddrs *ifa, *i; + int up = 0; + if (getifaddrs(&ifa)) return 0; + for (i = ifa; i; i = i->ifa_next) { + if (!strncmp(i->ifa_name, "ppp", 3) && i->ifa_addr && i->ifa_addr->sa_family == AF_INET && (i->ifa_flags & IFF_UP)) { + strncpy(ifname, i->ifa_name, len); + up = 1; + } + } + freeifaddrs(ifa); + return up; +} + +static CFArrayRef str_array(const char *s) +{ + CFStringRef v = CFStringCreateWithCString(NULL, s, kCFStringEncodingUTF8); + CFArrayRef a = CFArrayCreate(NULL, (const void **)&v, 1, &kCFTypeArrayCallBacks); + CFRelease(v); + return a; +} + +static void set_key(CFStringRef key, CFDictionaryRef val) +{ + if (!SCDynamicStoreSetValue(store, key, val)) clog_("SCDynamicStoreSetValue a échoué"); +} + +/* Route par défaut via la passerelle PPP (RTM_ADD sur le socket de routage, comme route add default) */ +static int route_msg(int type) +{ + struct { + struct rt_msghdr h; + struct sockaddr_in dst, gw, mask; + } m; + memset(&m, 0, sizeof m); + m.h.rtm_msglen = sizeof m; + m.h.rtm_version = RTM_VERSION; + m.h.rtm_type = type; + m.h.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; + m.h.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + m.h.rtm_pid = getpid(); + m.h.rtm_seq = 1; + m.dst.sin_len = m.gw.sin_len = m.mask.sin_len = sizeof(struct sockaddr_in); + m.dst.sin_family = m.gw.sin_family = m.mask.sin_family = AF_INET; + inet_aton(GW_IP, &m.gw.sin_addr); + int s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) return -1; + int r = write(s, &m, sizeof m) == sizeof m ? 0 : -1; + int e = errno; + close(s); + errno = e; + return r; +} + +static int add_default_route(void) +{ + if (route_msg(RTM_ADD) == 0) return 0; + if (errno == EEXIST && route_msg(RTM_CHANGE) == 0) return 0; /* une route par défaut existait déjà */ + clog_("route par défaut : %s", strerror(errno)); + return -1; +} + +static CFMutableDictionaryRef dict(void) +{ + return CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); +} + +/* + * Déclare le service PPP côté Setup: (ce que contiendrait preferences.plist pour un service configuré) : sans cela, + * IPMonitor ignore l'état publié par pppd et n'installe ni la route par défaut ni le DNS global. + */ +static void publish_setup(void) +{ + if (!store) store = SCDynamicStoreCreate(NULL, CFSTR("emuagent"), NULL, NULL); + if (!store) { clog_("SCDynamicStoreCreate a échoué"); return; } + + CFMutableDictionaryRef svc = dict(), itf = dict(), ipv4 = dict(), glob = dict(); + CFDictionarySetValue(svc, CFSTR("UserDefinedName"), CFSTR("Émulateur (PPP)")); + set_key(CFSTR("Setup:/Network/Service/" SERVICE_ID), svc); + CFDictionarySetValue(itf, CFSTR("Type"), CFSTR("PPP")); + CFDictionarySetValue(itf, CFSTR("SubType"), CFSTR("PPPSerial")); + CFDictionarySetValue(itf, CFSTR("DeviceName"), CFSTR("tty.bluetooth")); + set_key(CFSTR("Setup:/Network/Service/" SERVICE_ID "/Interface"), itf); + CFDictionarySetValue(ipv4, CFSTR("ConfigMethod"), CFSTR("PPP")); + set_key(CFSTR("Setup:/Network/Service/" SERVICE_ID "/IPv4"), ipv4); + CFArrayRef order = str_array(SERVICE_ID); + CFDictionarySetValue(glob, CFSTR("ServiceOrder"), order); + set_key(CFSTR("Setup:/Network/Global/IPv4"), glob); + CFRelease(order); CFRelease(svc); CFRelease(itf); CFRelease(ipv4); CFRelease(glob); +} + +/* Lien monté : vérifie qu'IPMonitor a bien installé la route par défaut, sinon la pose nous-mêmes */ +static void publish_network(const char *ifname) +{ + net_published = 1; + sleep(1); + if (add_default_route() == 0) clog_("lien %s prêt (%s via %s, DNS %s)", ifname, GUEST_IP, GW_IP, DNS_IP); +} + +static void net_tick(void) +{ + int status; + char ifname[IFNAMSIZ]; + if (pppd_pid && waitpid(pppd_pid, &status, WNOHANG) == pppd_pid) { + clog_("pppd terminé (statut %d), relance", status); + pppd_pid = 0; + net_published = 0; + } + if (!pppd_pid) { publish_setup(); start_pppd(); } + int up = ppp_up(ifname, sizeof ifname); + if (up && !net_published) publish_network(ifname); + if (!up) net_published = 0; +} + +/* ---------- réponses ---------- */ + +static int send_all(int fd, const void *b, size_t n) +{ + const char *p = b; + while (n) { + ssize_t w = write(fd, p, n); + if (w <= 0) { if (w < 0 && errno == EINTR) continue; if (!w) errno = EIO; return -1; } + p += w; n -= w; + } + return 0; +} + +static void reply(int fd, int ok, const char *info, const void *data, size_t len) +{ + char hdr[128]; + snprintf(hdr, sizeof hdr, "%s %zu%s%s\n", ok ? "OK" : "ERR", len, info ? " " : "", info ? info : ""); + send_all(fd, hdr, strlen(hdr)); + if (len) send_all(fd, data, len); +} + +static void reply_str(int fd, int ok, const char *fmt, ...) +{ + char buf[4096]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + reply(fd, ok, NULL, buf, strlen(buf)); +} + +/* tampon dynamique */ +struct buf { char *p; size_t n, cap; }; +static void bput(struct buf *b, const void *d, size_t n) +{ + if (b->n + n + 1 > b->cap) { + b->cap = (b->n + n + 1) * 2; + b->p = realloc(b->p, b->cap); + } + memcpy(b->p + b->n, d, n); + b->n += n; + b->p[b->n] = 0; +} +static void bprintf(struct buf *b, const char *fmt, ...) +{ + char tmp[2048]; + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(tmp, sizeof tmp, fmt, ap); + va_end(ap); + bput(b, tmp, n < (int)sizeof tmp ? n : (int)sizeof tmp - 1); +} + +/* ---------- commandes ---------- */ + +static void cmd_exec(int fd, char **args, int nargs) +{ + int pfd[2]; + pid_t pid; + if (nargs < 1) { reply_str(fd, 0, "usage: execprogrammeargs..."); return; } + pipe(pfd); + posix_spawn_file_actions_t fa; + posix_spawn_file_actions_init(&fa); + posix_spawn_file_actions_addopen(&fa, 0, "/dev/null", O_RDONLY, 0); + posix_spawn_file_actions_adddup2(&fa, pfd[1], 1); + posix_spawn_file_actions_adddup2(&fa, pfd[1], 2); + posix_spawn_file_actions_addclose(&fa, pfd[0]); + char *argv[64]; + int i; + for (i = 0; i < nargs && i < 63; i++) argv[i] = args[i]; + argv[i] = NULL; + int err = posix_spawn(&pid, argv[0], &fa, NULL, argv, environ); + posix_spawn_file_actions_destroy(&fa); + close(pfd[1]); + if (err) { close(pfd[0]); reply_str(fd, 0, "posix_spawn %s : %s", argv[0], strerror(err)); return; } + + struct buf out = { 0 }; + char tmp[4096]; + time_t start = time(NULL); + fcntl(pfd[0], F_SETFL, O_NONBLOCK); + for (;;) { + fd_set rs; + struct timeval tv = { 1, 0 }; + FD_ZERO(&rs); FD_SET(pfd[0], &rs); + if (select(pfd[0] + 1, &rs, NULL, NULL, &tv) > 0) { + ssize_t n = read(pfd[0], tmp, sizeof tmp); + if (n > 0) { bput(&out, tmp, n); continue; } + if (n == 0) break; + } + if (time(NULL) - start > 120) { kill(pid, SIGKILL); bprintf(&out, "\n[emuagent: délai dépassé, processus tué]\n"); break; } + } + close(pfd[0]); + int status = 0; + waitpid(pid, &status, 0); + char info[32]; + snprintf(info, sizeof info, "%d", WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status)); + reply(fd, 1, info, out.p, out.n); + free(out.p); +} + +static int (*p_SBSLaunchApplication)(CFStringRef, unsigned char); +static CFStringRef (*p_SBSApplicationLaunchingErrorString)(int); +static CFStringRef (*p_SBSCopyFrontmostApplicationDisplayIdentifier)(void); +static unsigned (*p_SBSSpringBoardServerPort)(void); +static int (*p_SBGetScreenLockStatus)(unsigned, unsigned char *, unsigned char *); +static int (*p_SBDimScreen)(unsigned, unsigned char); + +static void load_sbs(void) +{ + static void *h; + if (h) return; + h = dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_NOW); + if (!h) return; + p_SBSSpringBoardServerPort = dlsym(h, "SBSSpringBoardServerPort"); + p_SBGetScreenLockStatus = dlsym(h, "SBGetScreenLockStatus"); + p_SBDimScreen = dlsym(h, "SBDimScreen"); + p_SBSLaunchApplication = dlsym(h, "SBSLaunchApplicationWithIdentifier"); + p_SBSApplicationLaunchingErrorString = dlsym(h, "SBSApplicationLaunchingErrorString"); + p_SBSCopyFrontmostApplicationDisplayIdentifier = dlsym(h, "SBSCopyFrontmostApplicationDisplayIdentifier"); +} + +static void cfstr(CFStringRef s, char *out, size_t len) +{ + out[0] = 0; + if (s) CFStringGetCString(s, out, len, kCFStringEncodingUTF8); +} + +/* API MIG du firmware 2.2.1 : les deux états sont des octets, retour kern_return_t. */ +static void cmd_screen(int fd, int wake) +{ + load_sbs(); + if (!p_SBSSpringBoardServerPort || !p_SBGetScreenLockStatus || !p_SBDimScreen) { + reply_str(fd, 0, "API d'écran indisponible"); return; + } + unsigned port = p_SBSSpringBoardServerPort(); + unsigned char locked = 0, passcode = 0; + int rc = p_SBGetScreenLockStatus(port, &locked, &passcode); + if (rc) { reply_str(fd, 0, "SBGetScreenLockStatus : %d", rc); return; } + if (wake) { + /* SBApplicationRequestedDeviceUnlock affiche le clavier de code même + sans code activé. Réveiller seulement l'écran, puis glisser côté hôte. */ + rc = p_SBDimScreen(port, 0); + if (rc) { reply_str(fd, 0, "SBDimScreen : %d", rc); return; } + } + reply_str(fd, 1, "locked=%u passcode=%u", locked, passcode); +} + +static void cmd_launch(int fd, char **args, int nargs) +{ + load_sbs(); + if (!p_SBSLaunchApplication) { reply_str(fd, 0, "SpringBoardServices indisponible"); return; } + if (nargs != 1) { reply_str(fd, 0, "usage: launchbundle-id"); return; } + CFStringRef id = CFStringCreateWithCString(NULL, args[0], kCFStringEncodingUTF8); + int r = p_SBSLaunchApplication(id, 0); + CFRelease(id); + if (r == 0) { reply_str(fd, 1, "lancé"); return; } + char err[256] = "?"; + if (p_SBSApplicationLaunchingErrorString) cfstr(p_SBSApplicationLaunchingErrorString(r), err, sizeof err); + reply_str(fd, 0, "SBSLaunchApplication : %d (%s)", r, err); +} + +/* fetchhttp://hôte[:port]/chemin : résolution DNS + GET HTTP depuis l'iPod (diagnostic réseau) */ +static void cmd_fetch(int fd, char **args, int nargs) +{ + char host[256], path[1024] = "/", port[8] = "80"; + if (nargs < 1 || strncmp(args[0], "http://", 7)) { reply_str(fd, 0, "usage: fetchhttp://hôte[:port]/chemin"); return; } + const char *h = args[0] + 7, *slash = strchr(h, '/'); + size_t hl = slash ? (size_t)(slash - h) : strlen(h); + if (hl >= sizeof host) hl = sizeof host - 1; + memcpy(host, h, hl); host[hl] = 0; + if (slash) strncpy(path, slash, sizeof path - 1); + char *colon = strchr(host, ':'); + if (colon) { *colon = 0; strncpy(port, colon + 1, sizeof port - 1); } + + struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM }, *res; + int e = getaddrinfo(host, port, &hints, &res); + if (e) { reply_str(fd, 0, "getaddrinfo %s : %s", host, gai_strerror(e)); return; } + char ip[32]; + inet_ntop(AF_INET, &((struct sockaddr_in *)res->ai_addr)->sin_addr, ip, sizeof ip); + int s = socket(AF_INET, SOCK_STREAM, 0); + struct timeval to = { 15, 0 }; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof to); + setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &to, sizeof to); + if (connect(s, res->ai_addr, res->ai_addrlen)) { reply_str(fd, 0, "connect %s (%s) : %s", host, ip, strerror(errno)); close(s); freeaddrinfo(res); return; } + freeaddrinfo(res); + char req[1400]; + snprintf(req, sizeof req, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: emuagent\r\nConnection: close\r\n\r\n", path, host); + send_all(s, req, strlen(req)); + struct buf b = { 0 }; + bprintf(&b, "[%s -> %s]\n", host, ip); + char tmp[4096]; + ssize_t n; + while (b.n < 256 * 1024 && (n = read(s, tmp, sizeof tmp)) > 0) bput(&b, tmp, n); + close(s); + reply(fd, 1, NULL, b.p, b.n); + free(b.p); +} + +/* sc[clé] : liste des clés du SCDynamicStore, ou valeur d'une clé (plist XML) */ +static void cmd_sc(int fd, char **args, int nargs) +{ + if (!store) store = SCDynamicStoreCreate(NULL, CFSTR("emuagent"), NULL, NULL); + CFStringRef k = CFStringCreateWithCString(NULL, nargs ? args[0] : ".*", kCFStringEncodingUTF8); + CFPropertyListRef v = nargs && !strchr(args[0], '*') ? SCDynamicStoreCopyValue(store, k) : SCDynamicStoreCopyKeyList(store, k); + CFRelease(k); + if (!v) { reply_str(fd, 0, "clé absente"); return; } + CFDataRef x = CFPropertyListCreateXMLData(NULL, v); + reply(fd, 1, NULL, CFDataGetBytePtr(x), CFDataGetLength(x)); + CFRelease(x); + CFRelease(v); +} + +/* routes : table de routage IPv4 (sysctl NET_RT_DUMP) */ +static void cmd_routes(int fd) +{ + int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0 }; + size_t len = 0; + if (sysctl(mib, 6, NULL, &len, NULL, 0)) { reply_str(fd, 0, "sysctl : %s", strerror(errno)); return; } + char *b = malloc(len), *p; + sysctl(mib, 6, b, &len, NULL, 0); + struct buf out = { 0 }; + for (p = b; p < b + len;) { + struct rt_msghdr *h = (struct rt_msghdr *)p; + struct sockaddr *sa = (struct sockaddr *)(h + 1); + char txt[3][32] = { "-", "-", "-" }; + for (int i = 0; i < 3; i++) { + if (!(h->rtm_addrs & (1 << i))) continue; + if (sa->sa_family == AF_INET) inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, txt[i], 32); + else if (sa->sa_family == AF_LINK) strcpy(txt[i], "link"); + else if (sa->sa_len == 0) strcpy(txt[i], "0"); + sa = (struct sockaddr *)((char *)sa + (sa->sa_len ? ((sa->sa_len + 3) & ~3) : 4)); + } + char ifn[IFNAMSIZ] = "?"; + if_indextoname(h->rtm_index, ifn); + bprintf(&out, "%-16s gw %-16s mask %-16s %s flags 0x%x\n", txt[0], txt[1], txt[2], ifn, h->rtm_flags); + p += h->rtm_msglen; + } + free(b); + reply(fd, 1, NULL, out.p ? out.p : "", out.n); + free(out.p); +} + +static void cmd_front(int fd) +{ + load_sbs(); + char s[256] = ""; + if (p_SBSCopyFrontmostApplicationDisplayIdentifier) { + CFStringRef f = p_SBSCopyFrontmostApplicationDisplayIdentifier(); + cfstr(f, s, sizeof s); + if (f) CFRelease(f); + } + reply_str(fd, 1, "%s", s[0] ? s : "com.apple.springboard"); +} + +static void list_apps_in(struct buf *b, const char *dir, int depth) +{ + DIR *d = opendir(dir); + struct dirent *e; + if (!d) return; + while ((e = readdir(d))) { + char path[1024]; + if (e->d_name[0] == '.') continue; + snprintf(path, sizeof path, "%s/%s", dir, e->d_name); + size_t l = strlen(e->d_name); + if (l > 4 && !strcmp(e->d_name + l - 4, ".app")) { + CFURLRef u = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8 *)path, strlen(path), true); + CFBundleRef bundle = CFBundleCreate(NULL, u); + char id[256] = "", name[256] = ""; + if (bundle) { + cfstr(CFBundleGetIdentifier(bundle), id, sizeof id); + CFTypeRef n = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleDisplayName")); + if (!n) n = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey); + if (n && CFGetTypeID(n) == CFStringGetTypeID()) cfstr(n, name, sizeof name); + CFRelease(bundle); + } + CFRelease(u); + bprintf(b, "%s\t%s\t%s\n", id, name, path); + } else if (depth > 0) { + list_apps_in(b, path, depth - 1); + } + } + closedir(d); +} + +static void cmd_apps(int fd) +{ + struct buf b = { 0 }; + list_apps_in(&b, "/Applications", 0); + list_apps_in(&b, "/private/var/mobile/Applications", 1); + reply(fd, 1, NULL, b.p, b.n); + free(b.p); +} + +static int mkdirs(const char *path) +{ + char tmp[1024]; + strncpy(tmp, path, sizeof tmp - 1); + tmp[sizeof tmp - 1] = 0; + for (char *p = tmp + 1; *p; p++) { + if (*p == '/') { *p = 0; mkdir(tmp, 0755); *p = '/'; } + } + return mkdir(tmp, 0755) == 0 || errno == EEXIST ? 0 : -1; +} + +static int read_full(int fd, void *b, size_t n) +{ + char *p = b; + while (n) { + ssize_t r = read(fd, p, n); + if (r <= 0) { if (r < 0 && errno == EINTR) continue; if (!r) errno = EIO; return -1; } + p += r; n -= r; + } + return 0; +} + +/* Transferts par blocs : ne pas allouer la taille du fichier sur l'iPod. */ +static void cmd_put(int fd, char **args, int nargs) +{ + if (nargs < 3) { reply_str(fd, 0, "usage: putcheminmode octaltaille + données"); return; } + char *end; + errno = 0; + unsigned long size = strtoul(args[2], &end, 10); + if (errno || !args[2][0] || args[2][0] == '-' || *end) { + reply_str(fd, 0, "taille invalide"); return; + } + char dir[1024]; + if (strlen(args[0]) >= sizeof dir) { reply_str(fd, 0, "chemin trop long"); return; } + strcpy(dir, args[0]); + char *slash = strrchr(dir, '/'); + if (slash && slash != dir) { *slash = 0; mkdirs(dir); } + char tmp[1100]; + snprintf(tmp, sizeof tmp, "%s.emuagent-XXXXXX", args[0]); + int f = mkstemp(tmp); + if (f < 0) { reply_str(fd, 0, "création : %s", strerror(errno)); return; } + char data[65536]; + size_t remaining = size; + int failed = 0; + while (remaining) { + size_t chunk = remaining < sizeof data ? remaining : sizeof data; + if (read_full(fd, data, chunk) || send_all(f, data, chunk)) { failed = 1; break; } + remaining -= chunk; + } + if (!failed && (fchmod(f, strtoul(args[1], NULL, 8)) || fsync(f))) failed = 1; + int saved = errno; + if (close(f) && !failed) { failed = 1; saved = errno; } + if (!failed && rename(tmp, args[0])) { failed = 1; saved = errno; } + if (failed) { + unlink(tmp); + reply_str(fd, 0, "écriture de %s : %s", args[0], strerror(saved)); + return; + } + reply_str(fd, 1, "%lu octets écrits", size); +} + +static void cmd_get(int fd, char **args, int nargs) +{ + if (nargs < 1) { reply_str(fd, 0, "usage: getchemin"); return; } + int f = open(args[0], O_RDONLY | O_NONBLOCK); + struct stat st; + if (f < 0 || fstat(f, &st) || !S_ISREG(st.st_mode)) { + reply_str(fd, 0, "lecture de %s impossible", args[0]); + if (f >= 0) close(f); + return; + } + char hdr[128], data[65536]; + snprintf(hdr, sizeof hdr, "OK %llu\n", (unsigned long long)st.st_size); + if (send_all(fd, hdr, strlen(hdr))) { close(f); return; } + off_t remaining = st.st_size; + while (remaining) { + size_t chunk = remaining < (off_t)sizeof data ? (size_t)remaining : sizeof data; + /* Après l'en-tête, une erreur impose de fermer la connexion : + le client détecte la réponse incomplète et conserve son ancien fichier. */ + if (read_full(f, data, chunk) || send_all(fd, data, chunk)) { + close(f); shutdown(fd, SHUT_RDWR); return; + } + remaining -= chunk; + } + close(f); +} + +static void cmd_ls(int fd, char **args, int nargs) +{ + const char *path = nargs ? args[0] : "/"; + DIR *d = opendir(path); + if (!d) { reply_str(fd, 0, "%s : %s", path, strerror(errno)); return; } + struct buf b = { 0 }; + struct dirent *e; + while ((e = readdir(d))) { + char p[1024]; + struct stat st; + if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue; + snprintf(p, sizeof p, "%s/%s", path, e->d_name); + if (lstat(p, &st)) continue; + bprintf(&b, "%c%04o %5d %5d %10lld %s\n", S_ISDIR(st.st_mode) ? 'd' : S_ISLNK(st.st_mode) ? 'l' : '-', + st.st_mode & 07777, st.st_uid, st.st_gid, (long long)st.st_size, e->d_name); + } + closedir(d); + reply(fd, 1, NULL, b.p ? b.p : "", b.n); + free(b.p); +} + +static int rm_rf(const char *path) +{ + struct stat st; + if (lstat(path, &st)) return -1; + if (S_ISDIR(st.st_mode)) { + DIR *d = opendir(path); + struct dirent *e; + while (d && (e = readdir(d))) { + char p[1024]; + if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue; + snprintf(p, sizeof p, "%s/%s", path, e->d_name); + rm_rf(p); + } + if (d) closedir(d); + return rmdir(path); + } + return unlink(path); +} + +static void cmd_ps(int fd) +{ + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; + size_t len = 0; + sysctl(mib, 3, NULL, &len, NULL, 0); + struct kinfo_proc *procs = malloc(len); + if (sysctl(mib, 3, procs, &len, NULL, 0)) { reply_str(fd, 0, "sysctl : %s", strerror(errno)); free(procs); return; } + struct buf b = { 0 }; + bprintf(&b, " PID PPID UID COMMAND\n"); + for (size_t i = 0; i < len / sizeof *procs; i++) { + struct kinfo_proc *p = &procs[i]; + bprintf(&b, "%5d %5d %5d %s\n", p->kp_proc.p_pid, p->kp_eproc.e_ppid, p->kp_eproc.e_ucred.cr_uid, p->kp_proc.p_comm); + } + reply(fd, 1, NULL, b.p, b.n); + free(b.p); + free(procs); +} + +static int kill_by_name(const char *name, int sig) +{ + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }, n = 0; + size_t len = 0; + sysctl(mib, 3, NULL, &len, NULL, 0); + struct kinfo_proc *procs = malloc(len); + if (!sysctl(mib, 3, procs, &len, NULL, 0)) { + for (size_t i = 0; i < len / sizeof *procs; i++) { + if (!strcmp(procs[i].kp_proc.p_comm, name) && !kill(procs[i].kp_proc.p_pid, sig)) n++; + } + } + free(procs); + return n; +} + +static void cmd_kill(int fd, char **args, int nargs) +{ + if (nargs < 1) { reply_str(fd, 0, "usage: killpid|nom[signal]"); return; } + int sig = nargs > 1 ? atoi(args[1]) : SIGTERM; + char *end; + long pid = strtol(args[0], &end, 10); + if (!*end) { + if (kill(pid, sig)) reply_str(fd, 0, "kill %ld : %s", pid, strerror(errno)); + else reply_str(fd, 1, "1"); + } else { + int n = kill_by_name(args[0], sig); + reply_str(fd, n > 0, "%d", n); + } +} + +static void cmd_net(int fd) +{ + struct ifaddrs *ifa, *i; + struct buf b = { 0 }; + if (!getifaddrs(&ifa)) { + for (i = ifa; i; i = i->ifa_next) { + if (i->ifa_addr && i->ifa_addr->sa_family == AF_INET) { + char a[32], dst[32] = ""; + inet_ntop(AF_INET, &((struct sockaddr_in *)i->ifa_addr)->sin_addr, a, sizeof a); + if (i->ifa_dstaddr && (i->ifa_flags & IFF_POINTOPOINT)) + inet_ntop(AF_INET, &((struct sockaddr_in *)i->ifa_dstaddr)->sin_addr, dst, sizeof dst); + bprintf(&b, "%s %s%s%s %s\n", i->ifa_name, a, dst[0] ? " -> " : "", dst, (i->ifa_flags & IFF_UP) ? "up" : "down"); + } + } + freeifaddrs(ifa); + } + bprintf(&b, "pppd: %s, configd: %s\n", pppd_pid ? "actif" : "arrêté", net_published ? "publié" : "non publié"); + reply(fd, 1, NULL, b.p, b.n); + free(b.p); +} + +static void cmd_touch(int fd, char **a, int n) +{ + if (n != 5) { reply_str(fd, 0, "touch x1 y1 x2 y2 duration_ms"); return; } + float v[4]; + for (int i=0;i<4;i++) { char *end; v[i]=strtof(a[i],&end); if (*end || !(v[i]>=0 && v[i]<(i%2?480:320))) { reply_str(fd,0,"coordonnees invalides"); return; } } + unsigned ms=atoi(a[4]); if(ms<32 || ms>5000) { reply_str(fd,0,"duree invalide"); return; } + load_sbs(); char bundle[256]=""; + CFStringRef front=p_SBSCopyFrontmostApplicationDisplayIdentifier ? p_SBSCopyFrontmostApplicationDisplayIdentifier() : NULL; + if(front) { cfstr(front,bundle,sizeof bundle); CFRelease(front); } + int rc=send_touch_gesture(v[0],v[1],v[2],v[3],ms,bundle[0] && strcmp(bundle,"com.apple.springboard") ? bundle : NULL); + reply_str(fd,rc==0,"touch %d %s",rc,bundle); +} + +static void handle(int fd, char *line) +{ + char *args[64]; + int n = 0; + for (char *t = strtok(line, "\t"); t && n < 64; t = strtok(NULL, "\t")) args[n++] = t; + if (!n) return; + char *cmd = args[0]; + char **a = args + 1; + int na = n - 1; + if (!strcmp(cmd, "ping")) reply_str(fd, 1, "emuagent %s", VERSION); + else if (!strcmp(cmd, "touch")) cmd_touch(fd,a,na); + else if (!strcmp(cmd, "home")) { + void *h=dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices",RTLD_NOW); + int (*fn)(void)=h?dlsym(h,"SBSSuspendFrontmostApplication"):NULL; + reply_str(fd,fn && fn(),"home"); + } + else if (!strcmp(cmd, "exec")) cmd_exec(fd, a, na); + else if (!strcmp(cmd, "launch")) cmd_launch(fd, a, na); + else if (!strcmp(cmd, "front")) cmd_front(fd); + else if (!strcmp(cmd, "screen")) cmd_screen(fd, 0); + else if (!strcmp(cmd, "wake")) cmd_screen(fd, 1); + else if (!strcmp(cmd, "fetch")) cmd_fetch(fd, a, na); + else if (!strcmp(cmd, "sc")) cmd_sc(fd, a, na); + else if (!strcmp(cmd, "routes")) cmd_routes(fd); + else if (!strcmp(cmd, "settime")) { + /* Réglage explicite de l'heure pour les tests (secondes Unix). */ + struct timeval tv = { na ? strtol(a[0], NULL, 10) : 0, 0 }; + if (na && !settimeofday(&tv, NULL)) { + notify_post("com.apple.system.clock_set"); /* SpringBoard et les apps mettent leur horloge à jour */ + reply_str(fd, 1, "%ld", (long)tv.tv_sec); + } + else reply_str(fd, 0, "settimeofday : %s", strerror(errno)); + } + else if (!strcmp(cmd, "apps")) cmd_apps(fd); + else if (!strcmp(cmd, "put")) cmd_put(fd, a, na); + else if (!strcmp(cmd, "get")) cmd_get(fd, a, na); + else if (!strcmp(cmd, "ls")) cmd_ls(fd, a, na); + else if (!strcmp(cmd, "rm")) { if (na && !rm_rf(a[0])) reply_str(fd, 1, "supprimé"); else reply_str(fd, 0, "rm : %s", strerror(errno)); } + else if (!strcmp(cmd, "mkdir")) { if (na && !mkdirs(a[0])) reply_str(fd, 1, "ok"); else reply_str(fd, 0, "mkdir : %s", strerror(errno)); } + else if (!strcmp(cmd, "ps")) cmd_ps(fd); + else if (!strcmp(cmd, "kill")) cmd_kill(fd, a, na); + else if (!strcmp(cmd, "net")) cmd_net(fd); + else if (!strcmp(cmd, "respring")) reply_str(fd, kill_by_name("SpringBoard", SIGTERM) > 0, "SpringBoard relancé"); + else if (!strcmp(cmd, "sync")) { sync(); reply_str(fd, 1, "ok"); } + else reply_str(fd, 0, "commande inconnue : %s (ping exec launch fetch sc routes settime screen wake front apps put get ls rm mkdir ps kill net respring sync)", cmd); +} + +static void serve_client(int fd) +{ + char line[8192]; + size_t len = 0; + for (;;) { + char c; + ssize_t r = read(fd, &c, 1); + if (r <= 0) break; + if (c == '\n') { + line[len] = 0; + handle(fd, line); + len = 0; + } else if (len < sizeof line - 1) { + line[len++] = c; + } + } + close(fd); +} + +/* diagnostic : si /private/var/root/emuagent-ttytest existe, lit la ligne série brute pendant 20 s avant pppd */ +static void tty_selftest(void) +{ + struct stat st; + if (stat("/private/var/root/emuagent-ttytest", &st)) return; + int fd = open(PPP_TTY, O_RDWR | O_NOCTTY | O_NONBLOCK); + clog_("ttytest : open %s -> %d (%s)", PPP_TTY, fd, fd < 0 ? strerror(errno) : "ok"); + if (fd < 0) return; + struct termios t; + tcgetattr(fd, &t); cfmakeraw(&t); cfsetspeed(&t, B115200); t.c_cflag |= CLOCAL | CREAD; t.c_cflag &= ~CRTSCTS; + tcsetattr(fd, TCSANOW, &t); + write(fd, "EMUAGENT-TTYTEST\n", 17); + time_t start = time(NULL); + size_t total = 0; + while (time(NULL) - start < 20) { + char b[256]; + ssize_t n = read(fd, b, sizeof b); + if (n > 0) { + total += n; + char hex[64] = ""; + for (int i = 0; i < n && i < 16; i++) sprintf(hex + 3 * i, "%02x ", (unsigned char)b[i]); + clog_("ttytest : %zd octets : %s", n, hex); + } else { + usleep(100000); + } + } + clog_("ttytest : %zu octets reçus au total", total); + close(fd); +} + +/* iPhone OS 2.x does not implement the public idle-sleep assertions. + * Its root power domain does support SleepDisabled, which is required + * because this emulated SoC cannot resume from deep sleep. */ +static void prevent_idle_sleep(void) +{ + void *h = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW); + CFMutableDictionaryRef (*matching)(const char *) = h ? dlsym(h, "IOServiceMatching") : NULL; + unsigned (*get_service)(unsigned, CFDictionaryRef) = h ? dlsym(h, "IOServiceGetMatchingService") : NULL; + int (*set_property)(unsigned, CFStringRef, CFTypeRef) = h ? dlsym(h, "IORegistryEntrySetCFProperty") : NULL; + int (*release)(unsigned) = h ? dlsym(h, "IOObjectRelease") : NULL; + if (!matching || !get_service || !set_property || !release) { + clog_("controle de veille IOKit indisponible"); + return; + } + unsigned root = get_service(0, matching("IOPMrootDomain")); + if (!root) { + clog_("IOPMrootDomain indisponible"); + return; + } + int rc = set_property(root, CFSTR("SleepDisabled"), kCFBooleanTrue); + release(root); + clog_("SleepDisabled: 0x%x", rc); +} + +int main(void) +{ + con = fopen("/dev/console", "w"); + if (con) setvbuf(con, NULL, _IONBF, 0); + signal(SIGPIPE, SIG_IGN); + clog_("version %s, port %d", VERSION, PORT); + prevent_idle_sleep(); + tty_selftest(); + + /* agent relancé (mise à jour, crash) : le pppd de l'instance précédente tient encore la ligne série */ + if (kill_by_name("pppd", SIGTERM) > 0) { clog_("ancien pppd arrêté"); sleep(2); } + + int s = socket(AF_INET, SOCK_STREAM, 0), one = 1; + fcntl(s, F_SETFD, FD_CLOEXEC); /* sinon pppd hérite du port d'écoute */ + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); + struct sockaddr_in a = { .sin_family = AF_INET, .sin_port = htons(PORT), .sin_addr.s_addr = htonl(INADDR_ANY) }; + while (bind(s, (struct sockaddr *)&a, sizeof a)) { clog_("bind : %s, nouvel essai", strerror(errno)); sleep(2); } + listen(s, 4); + + for (;;) { + pid_t z; + int zs; + while ((z = waitpid(-1, &zs, WNOHANG)) > 0) { + if (z == pppd_pid) { clog_("pppd terminé (statut %d), relance", zs); pppd_pid = 0; net_published = 0; } + } + net_tick(); + fd_set rs; + struct timeval tv = { 2, 0 }; + FD_ZERO(&rs); + FD_SET(s, &rs); + if (select(s + 1, &rs, NULL, NULL, &tv) > 0) { + int c = accept(s, NULL, NULL); + if (c >= 0) { + fcntl(c, F_SETFD, FD_CLOEXEC); + /* un processus par client : une commande longue ou bloquée ne gêne ni les autres ni le suivi réseau */ + pid_t p = fork(); + if (p == 0) { + close(s); + struct timeval to = { 300, 0 }; + setsockopt(c, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof to); + serve_client(c); + _exit(0); + } + close(c); + } + } + } +} diff --git a/ios6/agent/netd.c b/ios6/agent/netd.c new file mode 100644 index 0000000000..ae34d99243 --- /dev/null +++ b/ios6/agent/netd.c @@ -0,0 +1,657 @@ +#define SLIRP_DEPRECATED_OK 1 +/* + * ipod-netd : réseau pour l'émulateur iPod Touch 2G, sans privilèges. + * + * Termine le lien PPP que pppd (côté iPhone OS) ouvre sur l'UART1 émulée, et passe les paquets IP à libslirp : + * NAT vers Internet, DNS (10.0.2.3), redirections de ports hôte -> iPod. + * + * ipod-netd [-s uart1.sock] [-L [addr:]hostport:guestport]... [-v] + * + * Adresses : iPod 10.0.2.15, passerelle 10.0.2.2, DNS 10.0.2.3. Défaut : -L 127.0.0.1:7788:7788 (emuagent). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GUEST_IP "10.0.2.15" +#define GW_IP "10.0.2.2" +#define DNS_IP "10.0.2.3" + +static const uint8_t guest_mac[6] = { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 }; +static const uint8_t slirp_mac[6] = { 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02 }; + +static int verbose; +static int uart = -1; +static const char *sock_path; +static Slirp *slirp; + +static void logm(const char *fmt, ...) +{ + va_list ap; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + fprintf(stderr, "[%ld.%03ld] ", (long)ts.tv_sec % 100000, ts.tv_nsec / 1000000); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +/* ---------- PPP : trames HDLC (RFC 1662) ---------- */ + +static uint16_t fcstab[256]; + +static void fcs_init(void) +{ + for (int b = 0; b < 256; b++) { + uint16_t v = b; + for (int i = 0; i < 8; i++) { + v = (v & 1) ? (v >> 1) ^ 0x8408 : v >> 1; + } + fcstab[b] = v; + } +} + +static uint16_t fcs16(uint16_t fcs, const uint8_t *p, size_t len) +{ + while (len--) { + fcs = (fcs >> 8) ^ fcstab[(fcs ^ *p++) & 0xff]; + } + return fcs; +} + +static void ppp_send(uint16_t proto, const uint8_t *data, size_t len) +{ + static uint8_t raw[4 + 2048], out[2 * (sizeof(raw) + 2) + 2]; + if (uart < 0 || len > 2048) { + return; + } + raw[0] = 0xff; raw[1] = 0x03; raw[2] = proto >> 8; raw[3] = proto & 0xff; + memcpy(raw + 4, data, len); + size_t n = 4 + len; + uint16_t fcs = fcs16(0xffff, raw, n) ^ 0xffff; + raw[n++] = fcs & 0xff; + raw[n++] = fcs >> 8; + + size_t o = 0; + out[o++] = 0x7e; + for (size_t i = 0; i < n; i++) { + uint8_t c = raw[i]; + if (c < 0x20 || c == 0x7e || c == 0x7d) { + out[o++] = 0x7d; + c ^= 0x20; + } + out[o++] = c; + } + out[o++] = 0x7e; + for (size_t w = 0; w < o;) { + ssize_t r = write(uart, out + w, o - w); + if (r < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + return; + } + w += r; + } +} + +/* ---------- LCP / IPCP ---------- */ + +enum { CONF_REQ = 1, CONF_ACK, CONF_NAK, CONF_REJ, TERM_REQ, TERM_ACK, CODE_REJ, PROTO_REJ, ECHO_REQ, ECHO_REP, DISC_REQ }; + +#define PROTO_IP 0x0021 +#define PROTO_LCP 0xc021 +#define PROTO_IPCP 0x8021 + +struct ncp { + const char *name; + uint16_t proto; + int acked_peer; /* on a acquitté la requête du pair */ + int acked_ours; /* le pair a acquitté la nôtre */ + uint8_t id; + time_t last_req; +}; + +static struct ncp lcp = { "LCP", PROTO_LCP }, ipcp = { "IPCP", PROTO_IPCP }; +static uint32_t lcp_magic = 0x49504f44; /* "IPOD" */ + +static int is_open(struct ncp *n) +{ + return n->acked_peer && n->acked_ours; +} + +static void send_ctl(uint16_t proto, uint8_t code, uint8_t id, const uint8_t *opts, size_t len) +{ + uint8_t pkt[1500]; + pkt[0] = code; pkt[1] = id; pkt[2] = (len + 4) >> 8; pkt[3] = (len + 4) & 0xff; + memcpy(pkt + 4, opts, len); + ppp_send(proto, pkt, len + 4); +} + +static void send_conf_req(struct ncp *n) +{ + uint8_t opts[16]; + size_t len = 0; + if (n == &ipcp) { + struct in_addr a; + inet_aton(GW_IP, &a); + opts[len++] = 3; opts[len++] = 6; + memcpy(opts + len, &a, 4); len += 4; + } else { + opts[len++] = 5; opts[len++] = 6; + uint32_t m = htonl(lcp_magic); + memcpy(opts + len, &m, 4); len += 4; + } + send_ctl(n->proto, CONF_REQ, ++n->id, opts, len); + n->last_req = time(NULL); +} + +static void guest_announce(void); + +static void ncp_opened(struct ncp *n) +{ + logm("%s ouvert", n->name); + if (n == &lcp) { + send_conf_req(&ipcp); + } else { + logm("lien IP prêt : iPod %s, passerelle %s, DNS %s", GUEST_IP, GW_IP, DNS_IP); + guest_announce(); + } +} + +/* Traite une Configure-Request du pair : Ack / Nak / Rej, option par option (RFC 1661 §5.1) */ +static void handle_conf_req(struct ncp *n, uint8_t id, const uint8_t *opts, size_t len) +{ + uint8_t ack[1500], nak[1500], rej[1500]; + size_t na = 0, nn = 0, nr = 0; + struct in_addr want_ip, want_dns; + inet_aton(GUEST_IP, &want_ip); + inet_aton(DNS_IP, &want_dns); + + for (size_t i = 0; i + 2 <= len;) { + uint8_t type = opts[i], olen = opts[i + 1]; + if (olen < 2 || i + olen > len) { + break; + } + const uint8_t *o = opts + i; + int reject = 0, naked = 0; + if (n == &lcp) { + switch (type) { + case 1: /* MRU */ + case 2: /* ACCM : on échappe de toute façon tous les caractères de contrôle */ + case 5: /* magic */ + case 7: /* PFC */ + case 8: /* ACFC */ + break; + default: /* authentification, callback... */ + reject = 1; + } + } else { + if ((type == 3 || type == 129 || type == 131) && olen == 6) { + const struct in_addr *want = type == 3 ? &want_ip : &want_dns; + if (memcmp(o + 2, want, 4)) { + memcpy(nak + nn, o, 2); + memcpy(nak + nn + 2, want, 4); + nn += 6; + naked = 1; + } + } else { + reject = 1; /* compression VJ, NBNS... */ + } + } + if (reject) { + memcpy(rej + nr, o, olen); nr += olen; + } else if (!naked) { + memcpy(ack + na, o, olen); na += olen; + } + i += olen; + } + if (nr) { + send_ctl(n->proto, CONF_REJ, id, rej, nr); + } else if (nn) { + send_ctl(n->proto, CONF_NAK, id, nak, nn); + } else { + int was_open = is_open(n); + send_ctl(n->proto, CONF_ACK, id, ack, na); + n->acked_peer = 1; + if (!n->acked_ours) { + send_conf_req(n); + } + if (!was_open && is_open(n)) { + ncp_opened(n); + } + } +} + +static void handle_ctl(struct ncp *n, const uint8_t *p, size_t len) +{ + if (len < 4) { + return; + } + uint8_t code = p[0], id = p[1]; + size_t plen = (p[2] << 8) | p[3]; + if (plen < 4 || plen > len) { + return; + } + const uint8_t *data = p + 4; + size_t dlen = plen - 4; + if (verbose) { + logm("%s <- code %d id %d len %zu", n->name, code, id, dlen); + } + + switch (code) { + case CONF_REQ: + if (n == &lcp && is_open(&lcp)) { /* renégociation : tout repart de zéro */ + lcp.acked_ours = 0; + ipcp.acked_peer = ipcp.acked_ours = 0; + } + if (n == &ipcp && !is_open(&lcp)) { + return; + } + handle_conf_req(n, id, data, dlen); + break; + case CONF_ACK: + if (id == n->id && !n->acked_ours) { + n->acked_ours = 1; + if (is_open(n)) { + ncp_opened(n); + } + } + break; + case CONF_NAK: + case CONF_REJ: + /* nos options sont minimales : on les renvoie sans l'option refusée */ + send_ctl(n->proto, CONF_REQ, ++n->id, NULL, 0); + break; + case TERM_REQ: + send_ctl(n->proto, TERM_ACK, id, NULL, 0); + logm("%s fermé par l'iPod", n->name); + n->acked_peer = n->acked_ours = 0; + if (n == &lcp) { + ipcp.acked_peer = ipcp.acked_ours = 0; + } + break; + case ECHO_REQ: + if (n == &lcp && dlen >= 4) { + uint8_t rep[1500]; + uint32_t m = htonl(lcp_magic); + memcpy(rep, &m, 4); + memcpy(rep + 4, data + 4, dlen - 4); + send_ctl(PROTO_LCP, ECHO_REP, id, rep, dlen); + } + break; + default: + break; + } +} + +/* ---------- IP <-> slirp (trames Ethernet fictives) ---------- */ + +static void log_ip(const char *dir, const uint8_t *ip, size_t len) +{ + if (!verbose || len < 20) return; + char a[16], b[16]; + snprintf(a, sizeof a, "%d.%d.%d.%d", ip[12], ip[13], ip[14], ip[15]); + snprintf(b, sizeof b, "%d.%d.%d.%d", ip[16], ip[17], ip[18], ip[19]); + int ihl = (ip[0] & 0xf) * 4, proto = ip[9]; + if (proto == 1 && len > (size_t)ihl + 1) logm("%s ICMP %s -> %s type %d code %d", dir, a, b, ip[ihl], ip[ihl + 1]); + else if ((proto == 6 || proto == 17) && len >= (size_t)ihl + 4) + logm("%s %s %s:%d -> %s:%d len %zu", dir, proto == 6 ? "TCP" : "UDP", a, (ip[ihl] << 8) | ip[ihl + 1], b, (ip[ihl + 2] << 8) | ip[ihl + 3], len); + else logm("%s proto %d %s -> %s", dir, proto, a, b); +} + +static void slirp_ip_input(const uint8_t *ip, size_t len) +{ + log_ip("iPod ->", ip, len); + uint8_t frame[14 + 2048]; + if (len > 2048) { + return; + } + memcpy(frame, slirp_mac, 6); + memcpy(frame + 6, guest_mac, 6); + frame[12] = 0x08; frame[13] = 0x00; + memcpy(frame + 14, ip, len); + slirp_input(slirp, frame, len + 14); +} + +static void arp_packet(uint16_t op, const uint8_t *dst_mac, const uint8_t *target_ip) +{ + uint8_t f[42]; + struct in_addr gip; + inet_aton(GUEST_IP, &gip); + memcpy(f, dst_mac, 6); + memcpy(f + 6, guest_mac, 6); + f[12] = 0x08; f[13] = 0x06; + f[14] = 0; f[15] = 1; f[16] = 8; f[17] = 0; f[18] = 6; f[19] = 4; + f[20] = 0; f[21] = op; + memcpy(f + 22, guest_mac, 6); + memcpy(f + 28, &gip, 4); + memcpy(f + 32, op == 2 ? dst_mac : (const uint8_t *)"\0\0\0\0\0\0", 6); + memcpy(f + 38, target_ip, 4); + slirp_input(slirp, f, sizeof f); +} + +/* ARP gratuit : slirp apprend l'adresse MAC de l'iPod */ +static void guest_announce(void) +{ + static const uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + struct in_addr gw; + inet_aton(GW_IP, &gw); + arp_packet(1, bcast, (const uint8_t *)&gw); +} + +static slirp_ssize_t slirp_send(const void *buf, size_t len, void *opaque) +{ + const uint8_t *f = buf; + if (len < 14) { + return len; + } + uint16_t type = (f[12] << 8) | f[13]; + if (type == 0x0806 && len >= 42 && f[21] == 1) { + struct in_addr gip; + inet_aton(GUEST_IP, &gip); + if (!memcmp(f + 38, &gip, 4)) { + arp_packet(2, f + 22, f + 28); + } + } else if (type == 0x0800 && is_open(&ipcp)) { + log_ip("-> iPod", f + 14, len - 14); + ppp_send(PROTO_IP, f + 14, len - 14); + } + return len; +} + +/* ---------- réception PPP ---------- */ + +static void ppp_frame(uint8_t *p, size_t len) +{ + if (len < 4 || fcs16(0xffff, p, len) != 0xf0b8) { + if (verbose && len) { + logm("trame invalide (%zu octets)", len); + } + return; + } + len -= 2; + if (len >= 2 && p[0] == 0xff && p[1] == 0x03) { + p += 2; len -= 2; + } + if (!len) { + return; + } + uint16_t proto; + if (p[0] & 1) { + proto = p[0]; p += 1; len -= 1; + } else { + if (len < 2) { + return; + } + proto = (p[0] << 8) | p[1]; p += 2; len -= 2; + } + switch (proto) { + case PROTO_LCP: + handle_ctl(&lcp, p, len); + break; + case PROTO_IPCP: + if (is_open(&lcp)) { + handle_ctl(&ipcp, p, len); + } + break; + case PROTO_IP: + if (is_open(&ipcp)) { + slirp_ip_input(p, len); + } + break; + default: + if (is_open(&lcp)) { /* IPV6CP, CCP... : Protocol-Reject */ + uint8_t rej[1500]; + size_t n = len > 1400 ? 1400 : len; + rej[0] = proto >> 8; rej[1] = proto & 0xff; + memcpy(rej + 2, p, n); + send_ctl(PROTO_LCP, PROTO_REJ, ++lcp.id, rej, n + 2); + } + } +} + +static void uart_input(const uint8_t *buf, size_t n) +{ + static uint8_t frame[4096]; + static size_t flen; + static int esc; + for (size_t i = 0; i < n; i++) { + uint8_t c = buf[i]; + if (c == 0x7e) { + ppp_frame(frame, flen); + flen = 0; esc = 0; + } else if (c == 0x7d) { + esc = 1; + } else { + if (esc) { + c ^= 0x20; esc = 0; + } + if (flen < sizeof frame) { + frame[flen++] = c; + } + } + } +} + +/* ---------- boucle principale, timers slirp ---------- */ + +struct timer { SlirpTimerCb cb; void *cb_opaque; SlirpTimerId id; int by_id; int64_t expire; int used; }; +static struct timer timers[16]; + +static int64_t now_ns(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; +} + +static int64_t cb_clock(void *o) { return now_ns(); } +static void cb_guest_error(const char *msg, void *o) { logm("slirp : %s", msg); } +static void cb_notify(void *o) {} +static void cb_register_poll_fd(int fd, void *o) {} /* on repasse tous les sockets à poll() à chaque tour */ +static void cb_unregister_poll_fd(int fd, void *o) {} + +static struct timer *timer_alloc(void) +{ + for (int i = 0; i < 16; i++) { + if (!timers[i].used) { + memset(&timers[i], 0, sizeof timers[i]); + timers[i].used = 1; + timers[i].expire = -1; + return &timers[i]; + } + } + abort(); +} + +static void *cb_timer_new(SlirpTimerCb cb, void *cb_opaque, void *o) +{ + struct timer *t = timer_alloc(); + t->cb = cb; t->cb_opaque = cb_opaque; + return t; +} + +static void *cb_timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *o) +{ + struct timer *t = timer_alloc(); + t->id = id; t->by_id = 1; t->cb_opaque = cb_opaque; + return t; +} + +static void cb_timer_free(void *t, void *o) { ((struct timer *)t)->used = 0; } +static void cb_timer_mod(void *t, int64_t expire_ms, void *o) { ((struct timer *)t)->expire = expire_ms; } + +static struct pollfd pfds[1024]; +static int npfds; + +static int add_poll(slirp_os_socket fd, int events, void *o) +{ + short ev = 0; + if (events & SLIRP_POLL_IN) ev |= POLLIN; + if (events & SLIRP_POLL_OUT) ev |= POLLOUT; + if (events & SLIRP_POLL_PRI) ev |= POLLPRI; + pfds[npfds].fd = fd; + pfds[npfds].events = ev; + pfds[npfds].revents = 0; + return npfds++; +} + +static int get_revents(int idx, void *o) +{ + short r = pfds[idx].revents; + int ev = 0; + if (r & POLLIN) ev |= SLIRP_POLL_IN; + if (r & POLLOUT) ev |= SLIRP_POLL_OUT; + if (r & POLLPRI) ev |= SLIRP_POLL_PRI; + if (r & POLLERR) ev |= SLIRP_POLL_ERR; + if (r & POLLHUP) ev |= SLIRP_POLL_HUP; + return ev; +} + +static int uart_connect(void) +{ + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + struct sockaddr_un a = { .sun_family = AF_UNIX }; + strncpy(a.sun_path, sock_path, sizeof a.sun_path - 1); + if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { + close(fd); + return -1; + } + return fd; +} + +static void add_fwd(const char *spec) +{ + char buf[128], *parts[3]; + int n = 0; + strncpy(buf, spec, sizeof buf - 1); + buf[sizeof buf - 1] = 0; + for (char *t = strtok(buf, ":"); t && n < 3; t = strtok(NULL, ":")) { + parts[n++] = t; + } + struct in_addr host, guest; + inet_aton("127.0.0.1", &host); + inet_aton(GUEST_IP, &guest); + int hp, gp; + if (n == 3) { + inet_aton(parts[0], &host); + hp = atoi(parts[1]); gp = atoi(parts[2]); + } else if (n == 2) { + hp = atoi(parts[0]); gp = atoi(parts[1]); + } else { + fprintf(stderr, "redirection invalide : %s\n", spec); + exit(1); + } + if (slirp_add_hostfwd(slirp, 0, host, hp, guest, gp) < 0) { + logm("redirection %s impossible (port occupé ?)", spec); + /* Ne pas laisser le client contacter le relais d'une autre session. */ + exit(1); + } else { + logm("redirection tcp %s:%d -> iPod:%d", inet_ntoa(host), hp, gp); + } +} + +int main(int argc, char **argv) +{ + const char *fwds[32]; + int nfwd = 0; + static char default_sock[512]; + snprintf(default_sock, sizeof default_sock, "%s/ipod-emu/run/uart1.sock", getenv("HOME")); + sock_path = default_sock; + + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-s") && i + 1 < argc) sock_path = argv[++i]; + else if (!strcmp(argv[i], "-L") && i + 1 < argc && nfwd < 32) fwds[nfwd++] = argv[++i]; + else if (!strcmp(argv[i], "-v")) verbose = 1; + else { fprintf(stderr, "usage: %s [-s uart1.sock] [-L [addr:]hostport:guestport]... [-v]\n", argv[0]); return 1; } + } + if (!nfwd) fwds[nfwd++] = "127.0.0.1:7788:7788"; + signal(SIGPIPE, SIG_IGN); + fcs_init(); + + SlirpConfig cfg = { 0 }; + cfg.version = 4; + cfg.in_enabled = true; + inet_aton("10.0.2.0", &cfg.vnetwork); + inet_aton("255.255.255.0", &cfg.vnetmask); + inet_aton(GW_IP, &cfg.vhost); + inet_aton(DNS_IP, &cfg.vnameserver); + inet_aton(GUEST_IP, &cfg.vdhcp_start); + cfg.vhostname = "ipod-emu-host"; + cfg.if_mtu = 1500; + cfg.if_mru = 1500; + static const SlirpCb cb = { + .send_packet = slirp_send, .guest_error = cb_guest_error, .clock_get_ns = cb_clock, + .timer_new = cb_timer_new, .timer_free = cb_timer_free, .timer_mod = cb_timer_mod, + .notify = cb_notify, .timer_new_opaque = cb_timer_new_opaque, + .register_poll_fd = cb_register_poll_fd, .unregister_poll_fd = cb_unregister_poll_fd, + }; + slirp = slirp_new(&cfg, &cb, NULL); + for (int i = 0; i < nfwd; i++) add_fwd(fwds[i]); + + for (;;) { + if (uart < 0) { + uart = uart_connect(); + if (uart < 0) { + sleep(1); + continue; + } + logm("connecté à %s, en attente de pppd", sock_path); + lcp.acked_peer = lcp.acked_ours = ipcp.acked_peer = ipcp.acked_ours = 0; + } + + uint32_t timeout = 1000; + npfds = 0; + int uidx = add_poll(uart, SLIRP_POLL_IN, NULL); + slirp_pollfds_fill_socket(slirp, &timeout, add_poll, NULL); + int64_t now_ms = now_ns() / 1000000; + for (int i = 0; i < 16; i++) { + if (timers[i].used && timers[i].expire >= 0) { + int64_t d = timers[i].expire - now_ms; + if (d < 0) d = 0; + if (d < timeout) timeout = d; + } + } + int r = poll(pfds, npfds, timeout); + slirp_pollfds_poll(slirp, r < 0, get_revents, NULL); + + now_ms = now_ns() / 1000000; + for (int i = 0; i < 16; i++) { + struct timer *t = &timers[i]; + if (t->used && t->expire >= 0 && t->expire <= now_ms) { + t->expire = -1; + if (t->by_id) slirp_handle_timer(slirp, t->id, t->cb_opaque); + else t->cb(t->cb_opaque); + } + } + + if (r > 0 && (pfds[uidx].revents & (POLLIN | POLLHUP | POLLERR))) { + uint8_t buf[4096]; + ssize_t n = read(uart, buf, sizeof buf); + if (n <= 0) { + logm("UART déconnectée (émulateur arrêté ?)"); + close(uart); + uart = -1; + continue; + } + uart_input(buf, n); + } + + /* relance des Configure-Request non acquittées */ + time_t t = time(NULL); + if (is_open(&lcp) && !ipcp.acked_ours && t - ipcp.last_req >= 2) send_conf_req(&ipcp); + if (lcp.acked_peer && !lcp.acked_ours && t - lcp.last_req >= 2) send_conf_req(&lcp); + } +} diff --git a/ios6/apps/SimulatorLab/main.m b/ios6/apps/SimulatorLab/main.m new file mode 100644 index 0000000000..7fb978761e --- /dev/null +++ b/ios6/apps/SimulatorLab/main.m @@ -0,0 +1,42 @@ +#import + +@interface LabController : UIViewController { + UILabel *counterLabel; + NSInteger count; +} +- (void)increment; +- (void)resetCount; +@end +@implementation LabController +- (void)save { + counterLabel.text=[NSString stringWithFormat:@"%ld",(long)count]; + NSString *state=[NSString stringWithFormat:@"count=%ld\n",(long)count]; + [state writeToFile:@"/var/mobile/Library/Caches/SimulatorLab-result.txt" atomically:YES encoding:NSUTF8StringEncoding error:NULL]; + NSLog(@"SimulatorLab count=%ld",(long)count); +} +- (void)increment { count++; [self save]; } +- (void)resetCount { count=0; [self save]; } +- (void)loadView { + UIView *v=[[UIView alloc] initWithFrame:CGRectMake(0,0,320,460)]; + v.backgroundColor=[UIColor colorWithRed:0.92 green:0.94 blue:0.97 alpha:1]; self.view=v; [v release]; + UILabel *title=[[[UILabel alloc] initWithFrame:CGRectMake(20,30,280,45)] autorelease]; + title.text=@"Simulator Lab";title.font=[UIFont boldSystemFontOfSize:28];title.textAlignment=NSTextAlignmentCenter;title.backgroundColor=[UIColor clearColor];[self.view addSubview:title]; + UILabel *subtitle=[[[UILabel alloc] initWithFrame:CGRectMake(20,80,280,30)] autorelease];subtitle.text=@"Application native · iOS 6";subtitle.textAlignment=NSTextAlignmentCenter;subtitle.backgroundColor=[UIColor clearColor];[self.view addSubview:subtitle]; + counterLabel=[[UILabel alloc] initWithFrame:CGRectMake(20,130,280,100)];counterLabel.font=[UIFont boldSystemFontOfSize:64];counterLabel.textAlignment=NSTextAlignmentCenter;counterLabel.backgroundColor=[UIColor clearColor];[self.view addSubview:counterLabel]; + UIButton *plus=[UIButton buttonWithType:UIButtonTypeRoundedRect];plus.frame=CGRectMake(40,250,240,55);[plus setTitle:@"Ajouter +1" forState:UIControlStateNormal];[plus addTarget:self action:@selector(increment) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:plus]; + UIButton *reset=[UIButton buttonWithType:UIButtonTypeRoundedRect];reset.frame=CGRectMake(40,325,240,45);[reset setTitle:@"Remettre à zéro" forState:UIControlStateNormal];[reset addTarget:self action:@selector(resetCount) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:reset]; + [self save]; +} +- (void)dealloc { [counterLabel release];[super dealloc]; } +@end +@interface LabApp : NSObject { UIWindow *window; } +@end +@implementation LabApp +- (void)applicationDidFinishLaunching:(UIApplication *)app { + window=[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + LabController *vc=[[LabController alloc] init]; + window.rootViewController=vc;[vc release];[window makeKeyAndVisible]; + NSLog(@"SimulatorLab ready"); +} +@end +int main(int argc,char **argv) { NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init]; int r=UIApplicationMain(argc,argv,nil,@"LabApp");[pool release];return r; } diff --git a/ios6/bin/ios6 b/ios6/bin/ios6 new file mode 100755 index 0000000000..3179a8814d --- /dev/null +++ b/ios6/bin/ios6 @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail +sim_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) +exec "$sim_root/.venv/bin/python" "$sim_root/tools/simulator.py" "$@" diff --git a/ios6/requirements.txt b/ios6/requirements.txt new file mode 100644 index 0000000000..5f1d0c5e46 --- /dev/null +++ b/ios6/requirements.txt @@ -0,0 +1,3 @@ +meson>=1.0,<2 +distlib>=0.3,<1 +capstone>=5,<6 diff --git a/ios6/results/20261003-first-boot/boot-layout.json b/ios6/results/20261003-first-boot/boot-layout.json new file mode 100644 index 0000000000..79dee9ff6b --- /dev/null +++ b/ios6/results/20261003-first-boot/boot-layout.json @@ -0,0 +1,9 @@ +{ + "kernel_entry": "0x80086084", + "physical_entry": "0x40086084", + "kernel_bytes": 12083200, + "boot_args_bytes": 320, + "devicetree_bytes": 57764, + "kernel_patches": [], + "command_line": "-v debug=0x14e serial=3 rd=md0 keepsyms=1" +} diff --git a/ios6/results/20261003-first-boot/command.json b/ios6/results/20261003-first-boot/command.json new file mode 100644 index 0000000000..c36aa18289 --- /dev/null +++ b/ios6/results/20261003-first-boot/command.json @@ -0,0 +1,24 @@ +[ + "/home/dev/ios6-emu/build/qemu-system-arm", + "-M", + "ios6-n88", + "-kernel", + "/home/dev/ios6-emu/firmware/kernel.bin", + "-dtb", + "/home/dev/ios6-emu/firmware/boot-data.bin", + "-initrd", + "/home/dev/ios6-emu/firmware/RestoreRamdisk.dec", + "-display", + "none", + "-serial", + "file:/home/dev/ios6-emu/evidence/probe-20261003T101443-261760Z/serial.log", + "-monitor", + "none", + "-qmp", + "unix:/home/dev/ios6-emu/evidence/probe-20261003T101443-261760Z/qmp,server=on,wait=off", + "-d", + "unimp,guest_errors,in_asm,int", + "-D", + "/home/dev/ios6-emu/evidence/probe-20261003T101443-261760Z/qemu.log", + "-no-reboot" +] diff --git a/ios6/results/20261003-first-boot/firmware-manifest.json b/ios6/results/20261003-first-boot/firmware-manifest.json new file mode 100644 index 0000000000..12176c01f7 --- /dev/null +++ b/ios6/results/20261003-first-boot/firmware-manifest.json @@ -0,0 +1,22 @@ +{ + "ipsw_url": "https://secure-appldnld.apple.com/iOS6.1/091-3457.20140221.Btt3e/iPhone2,1_6.1.6_10B500_Restore.ipsw", + "ipsw_sha256": "37a4b3bb0cbc0cc4c94f4ec2987f23c9207644c2669084084c4a78e8325e6b91", + "keys_url": "https://raw.githubusercontent.com/LukeZGD/Legacy-iOS-Kit-Keys/af6bf5934dc61ed557a967a3f42ab7fb8ed8c45e/iPhone2,1/10B500/index.html", + "components": { + "Kernelcache": { + "member": "kernelcache.release.n88", + "bytes": 11821056, + "sha256": "ec4787ac012567f9c10ba2d5ab611058545bce7e17cd95ef310eba94bfca9b78" + }, + "DeviceTree": { + "member": "Firmware/all_flash/all_flash.n88ap.production/DeviceTree.n88ap.img3", + "bytes": 57856, + "sha256": "6608eba10d4a1086a4d3f5d299b8b9582bcb27262206152ea4029ca1a25718ad" + }, + "RestoreRamdisk": { + "member": "048-2889-001.dmg", + "bytes": 13459456, + "sha256": "e5c7eff45a0e36b7f6fdda9246bc8c0ff0561d4cedd615035f3ebe23c3d235e8" + } + } +} diff --git a/ios6/results/20261003-first-boot/provenance.json b/ios6/results/20261003-first-boot/provenance.json new file mode 100644 index 0000000000..a57c701fcf --- /dev/null +++ b/ios6/results/20261003-first-boot/provenance.json @@ -0,0 +1,9 @@ +{ + "qemu_base": "6cdda3bc1326f0ae54f40e2b852372dcc4921565", + "qemu_local": "/srv/homes/iphoneos2/qemu-ios", + "qemu_upstream": "https://github.com/devos50/qemu-ios", + "hardware_reference": "https://github.com/iDroid-Project/openiBoot/tree/master/plat-s5l8920", + "boot_args_reference": "https://github.com/apple-oss-distributions/xnu/blob/main/pexpert/pexpert/arm/boot.h", + "firmware_metadata": "https://api.ipsw.me/v4/device/iPhone2,1?type=ipsw", + "note": "Les adresses MMIO du prototype sont recoup\u00e9es avec le DeviceTree Apple 10B500/n88." +} diff --git a/ios6/results/20261003-first-boot/result.json b/ios6/results/20261003-first-boot/result.json new file mode 100644 index 0000000000..f433717499 --- /dev/null +++ b/ios6/results/20261003-first-boot/result.json @@ -0,0 +1,23 @@ +{ + "run": "/home/dev/ios6-emu/evidence/probe-20261003T101443-261760Z", + "seconds": 10.0, + "status": { + "status": "paused", + "singlestep": false, + "running": false + }, + "registers": "\r\nCPU#0\r\nR00=cae2b9a8 R01=00000000 R02=800895a4 R03=802dc000\r\nR04=cae2b9a8 R05=802ffd88 R06=cae2b9a8 R07=cae2b998\r\nR08=802ffcc0 R09=3b9ac9ff R10=00000001 R11=cae2baa0\r\nR12=c00d9200 R13=cae2b994 R14=8001732f R15=800895a8\r\nPSR=a00000d3 N-C- A svc32\r\ns00=00000000 s01=00000000 d00=0000000000000000\r\ns02=00000000 s03=00000000 d01=0000000000000000\r\ns04=00000000 s05=00000000 d02=0000000000000000\r\ns06=00000000 s07=00000000 d03=0000000000000000\r\ns08=00000000 s09=00000000 d04=0000000000000000\r\ns10=00000000 s11=00000000 d05=0000000000000000\r\ns12=00000000 s13=00000000 d06=0000000000000000\r\ns14=00000000 s15=00000000 d07=0000000000000000\r\ns16=00000000 s17=00000000 d08=0000000000000000\r\ns18=00000000 s19=00000000 d09=0000000000000000\r\ns20=00000000 s21=00000000 d10=0000000000000000\r\ns22=00000000 s23=00000000 d11=0000000000000000\r\ns24=00000000 s25=00000000 d12=0000000000000000\r\ns26=00000000 s27=00000000 d13=0000000000000000\r\ns28=00000000 s29=00000000 d14=0000000000000000\r\ns30=00000000 s31=00000000 d15=0000000000000000\r\ns32=00000000 s33=00000000 d16=0000000000000000\r\ns34=00000000 s35=00000000 d17=0000000000000000\r\ns36=00000000 s37=00000000 d18=0000000000000000\r\ns38=00000000 s39=00000000 d19=0000000000000000\r\ns40=00000000 s41=00000000 d20=0000000000000000\r\ns42=00000000 s43=00000000 d21=0000000000000000\r\ns44=00000000 s45=00000000 d22=0000000000000000\r\ns46=00000000 s47=00000000 d23=0000000000000000\r\ns48=00000000 s49=00000000 d24=0000000000000000\r\ns50=00000000 s51=00000000 d25=0000000000000000\r\ns52=00000000 s53=00000000 d26=0000000000000000\r\ns54=00000000 s55=00000000 d27=0000000000000000\r\ns56=00000000 s57=00000000 d28=0000000000000000\r\ns58=00000000 s59=00000000 d29=0000000000000000\r\ns60=00000000 s61=00000000 d30=0000000000000000\r\ns62=00000000 s63=00000000 d31=0000000000000000\r\nFPSCR: 03000000\r\n", + "instructions": "0xffffffff800895a8: Asm output not supported on this arch\r\n", + "stack": "cae2b994: 0x8030037c 0xcae2b9c0 0x8000d071 0x0e58154a\r\ncae2b9a4: 0x00000000 0x0e57d1c6 0x00000000 0xcae2baec\r\ncae2b9b4: 0x802ffcc0 0x00000000 0x802ffcb0 0xcae2ba48\r\ncae2b9c4: 0x8000c6a3 0x802d4580 0x00000000 0x802ffd88\r\ncae2b9d4: 0x00000001 0x00000000 0x00000000 0x00000001\r\ncae2b9e4: 0x834baab8 0x8334ae80 0x80327c80 0x833375d0\r\ncae2b9f4: 0x80326708 0x834baa80 0xcae2ba30 0xcae2ba08\r\ncae2ba04: 0x8027c470 0xcae2ba18 0x8027c42c 0x802ffca0\r\n", + "returncode": 0, + "guest_status": "kernel-panic", + "panic": "panic(cpu 0 caller 0x8065f7ed): \"ARM7M not stopped for some reason\"", + "milestones": { + "serial_console": true, + "crypto_self_tests": true, + "platform_driver": true, + "interrupt_controller_driver": true, + "userspace_confirmed": false + }, + "expected_panic_matched": true +} diff --git a/ios6/results/20261003-first-boot/serial.log b/ios6/results/20261003-first-boot/serial.log new file mode 100644 index 0000000000..d69dff1348 --- /dev/null +++ b/ios6/results/20261003-first-boot/serial.log @@ -0,0 +1,955 @@ +iBoot version: ios6-emu direct-kernel prototype +corecrypto kext started! +Running kernel space in FIPS MODE +Plist hmac value is 9fd77803e256b61c91d83182dfb9aee94e57b18702a272b6a206fb074ca8e541 +Computed hmac value is 9fd77803e256b61c91d83182dfb9aee94e57b18702a272b6a206fb074ca8e541 +corecrypto.kext FIPS integrity POST test passed! +corecrypto.kext FIPS AES CBC POST test passed! +corecrypto.kext FIPS TDES CBC POST test passed! +corecrypto.kext FIPS SHA POST test passed! +corecrypto.kext FIPS HMAC POST test passed! +corecrypto.kext FIPS ECDSA POST test passed! +corecrypto.kext FIPS DRBG POST test passed! +corecrypto.kext FIPS POST passed! +AppleS5L8920XIO::start: chip-revision: A0 +AppleARMPL192VIC::start: _vicBaseAddress = 0xcc44d000 +AppleS5L8920XGPIOIC::start: gpioicBaseAddress: 0xc4dc6000 +AppleARMPerformanceController::traceBufferEnable: Trace is enabled +AppleS5L8920XPerformanceController::start: _pcBaseAddress: 0xc4dcd000 _pcMCUBaseAddress: 0xc4dd5000 +AppleARMPerformanceController configured with 1 Performance Domains + Performance Domain 0 : pcVoltageStateCount: 3 pcPerformanceStateCount: 4 + Voltage State 0 : .vsPerformanceLimit: /2 .vsDeviceConstraint: 1ff8ffef ffff8fff + Voltage State 1 : .vsPerformanceLimit: /2 .vsDeviceConstraint: 1ffeffff ffff8fff + Voltage State 2 : .vsPerformanceLimit: /1 .vsDeviceConstraint: 1fffffff ffff8fff + Performance State 0 : .psPerformanceDivider: /1 .psVoltageRequired: 2 .psDeviceConstraint: 1fffffff ffff8fff + Performance State 1 : .psPerformanceDivider: /2 .psVoltageRequired: 0 .psDeviceConstraint: 1ffeb5ff ffe78fff + Performance State 2 : .psPerformanceDivider: /4 .psVoltageRequired: 0 .psDeviceConstraint: 1ffe24ff ffe78fff + Performance State 3 : .psPerformanceDivider: /8 .psVoltageRequired: 0 .psDeviceConstraint: 1ffe20df ffe78fff +AppleARMPerformanceController: Dynamic Performance State Management Enabled on Performance Domain 0 +AppleARMPerformanceController: Dynamic Voltage Control Enabled on Performance Domain 0 with 10000us activity timeout and 100us grace period +IOSDIOController::init(): IOSDIOFamily-34 Feb 13 2013 21:35:31 @ 0x833db700 +AppleIOPSDIO::start(): AppleIOPSDIO-12 Feb 13 2013 22:17:01 +AppleBaseband::start(0x83407dc0): baseband +AppleARMBacklight::start: Backlight calibration data 0 +Couldn't alloc class "AppleRawIOAddressSpace" ++ AppleM2ScalerCSCDriver[0x83505f00]::start(provider 0x8339e480) +AppleH2CamIn::start +AppleH2CamIn::start, fSensorExtClkFunction = 0x83371CC0 +AppleH2CamIn::start, fSensorShutdownFunction = 0x83383BA0 +AppleH2CamIn::start, fISPResetFunction = 0x83357180 ++ H264EncDriver: start (provider 0x8339e300) ++ H264EncDriver: compiled on Feb 13 2013 22:32:00 +AppleVXD375 - start (provider 0x8339e280) +AppleVXD375 - compiled on Feb 13 2013 22:32:12 +AppleARMIICController::start buffer=0x8345a300 entries=8 +AppleS5L8920XI2CController::start: i2c0 _i2cBaseAddress: 0xcae0d000 +AppleARMIICController::start buffer=0x8345a280 entries=8 +AppleS5L8920XI2CController::start: i2c2 _i2cBaseAddress: 0xcae0e000 +AppleS5L8920XPWM::start: _pwmBaseAddress: 0xcae15000 +AppleSamsungLP65USBPhy::start: hsic disabled +AppleSamsungMIPIDSIController::start: _dsimBaseAddress = 0xcae16000 +AppleSamsungSWI::start: _swiBaseAddress: 0x00000000cae1d000 +AppleSamsungSWI::start: _swiBaseAddress: _swiWorstCaseIntraVBUCK=0 +AppleS5L8920XAudioComplex::start: audio-complex audioAHBMemoryMap: 0x83592840 audioAPBMemoryMap: 0x835927e8 ncoRef: 0 +AppleS5L8920XI2SController::start: i2s0 i2sBaseAddress: 0xcae25000 +AppleS5L8920XI2SController::start: i2s1 i2sBaseAddress: 0xcae26000 +AppleS5L8920XI2SController::start: i2s2 i2sBaseAddress: 0xcae2d000 +AppleSamsungSerial: Identified Serial Port on ARM Device=uart0 at 0x82500000(0xcae3d000) +AppleSamsungSerial: Identified Serial Port on ARM Device=uart4 at 0x82900000(0xcae45000) +ARM7M::start: mapped I/O registers at 0xcaded000/0x86300000, 0xcadee000/0xbf300000 +IOP::start: starting IOP (DEBUG) @ 0x835f7d80 +IOP::_prepareFirmwareImage: EmbeddedIOP firmware s5l8920x-RELEASE iBoot-1537.9.55 +panic(cpu 0 caller 0x8065f7ed): "ARM7M not stopped for some reason" +Debugger message: panic +OS version: Not set yet +Kernel version: Darwin Kernel Version 13.0.0: Wed Feb 13 21:35:42 PST 2013; root:xnu-2107.7.55.2.2~1/RELEASE_ARM_S5L8920X +iBoot version: ios6-emu direct-kernel prototype +secure boot?: NO +Paniclog version: 1 +Epoch Time: sec usec + Boot : 0x00000000 0x00000000 + Sleep : 0x00000000 0x00000000 + Wake : 0x00000000 0x00000000 + Calendar: 0x00000002 0x00016c10 + +Task 0xc048ec20: 1578 pages, 79 threads: pid 0: kernel_task + thread 0xc04ab2a0 + kernel backtrace: cae2bd30 + lr: 0x80089f41 fp: 0xcae2bd60 + lr: 0x8008a7dd fp: 0xcae2bd8c + lr: 0x80017ca7 fp: 0xcae2bda4 + lr: 0x8065f7ed fp: 0xcae2bdcc + lr: 0x809391f1 fp: 0xcae2bde8 + lr: 0x80939a7d fp: 0xcae2be04 + lr: 0x8025914f fp: 0xcae2be2c + lr: 0x80939a4f fp: 0xcae2be48 + lr: 0x8065ab17 fp: 0xcae2be54 + lr: 0x80898e1f fp: 0xcae2be60 + lr: 0x80898c03 fp: 0xcae2be94 + lr: 0x808989d5 fp: 0xcae2bea8 + lr: 0x80896673 fp: 0xcae2bed8 + lr: 0x80249b47 fp: 0xcae2bf10 + lr: 0x802497c3 fp: 0xcae2bf60 + lr: 0x8024a299 fp: 0xcae2bf88 + lr: 0x8024a06b fp: 0xcae2bfa8 + lr: 0x8008805c fp: 0x00000000 + +Debugger message: panic +OS version: Not set yet +Kernel version: Darwin Kernel Version 13.0.0: Wed Feb 13 21:35:42 PST 2013; root:xnu-2107.7.55.2.2~1/RELEASE_ARM_S5L8920X +iBoot version: ios6-emu direct-kernel prototype +secure boot?: NO +Paniclog version: 1 +Epoch Time: sec usec + Boot : 0x00000000 0x00000000 + Sleep : 0x00000000 0x00000000 + Wake : 0x00000000 0x00000000 + Calendar: 0x00000002 0x0001a537 + + +********* Dumping thread state and stacks ********* + +Task 0xc048ec20: 1578 pages, 79 threads: pid 0: kernel_task + thread 0x80302670 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x0199268f r5: 0xc04a48f0 r6: 0x00000000 r7: 0xc4dbbda4 + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0x80302670 + r12: 0x800880a0 sp: 0xc4dbbd78 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: c4dbbda4 + lr: 0x800244d9 fp: 0xc4dbbdd0 + lr: 0x8001eddd fp: 0xc4dbbdf0 + lr: 0x80240693 fp: 0xc4dbbe10 + lr: 0x8024b055 fp: 0xc4dbbe40 + lr: 0x80249ea3 fp: 0xc4dbbe50 + lr: 0x80240b0f fp: 0xc4dbbe60 + lr: 0x802bbce7 fp: 0xc4dbbf9c + lr: 0x800271e9 fp: 0xc4dbbfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04ad990 + kernel continuation: 0x800256c9 + + thread 0xc04ad400 + kernel continuation: 0x80022059 + + thread 0xc04ace70 + kernel continuation: 0x8002cf39 + + thread 0xc04ac8e0 + kernel continuation: 0x8002d135 + + thread 0xc04ac350 + kernel continuation: 0x8002fc65 + + thread 0xc04abdc0 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x01be7e71 r5: 0xc04a9c60 r6: 0x00000000 r7: 0xc4debf40 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04abdc0 + r12: 0x800880a0 sp: 0xc4debf14 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: c4debf40 + lr: 0x800244d9 fp: 0xc4debf6c + lr: 0x80033173 fp: 0xc4debfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04ab830 + kernel continuation: 0x802582fd + + thread 0xc04aa780 + kernel continuation: 0x802582fd + + thread 0xc04a7b00 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x01ea275f r5: 0xc058d990 r6: 0x00000000 r7: 0xcae4bd94 + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc04a7b00 + r12: 0x800880a0 sp: 0xcae4bd68 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae4bd94 + lr: 0x800244d9 fp: 0xcae4bdc0 + lr: 0x8001ed6d fp: 0xcae4bdd8 + lr: 0x80240655 fp: 0xcae4bdf0 + lr: 0x8024b01d fp: 0xcae4be20 + lr: 0x80249ea3 fp: 0xcae4be30 + lr: 0x804ac80d fp: 0xcae4be64 + lr: 0x804ac789 fp: 0xcae4be74 + lr: 0x80988693 fp: 0xcae4bebc + lr: 0x8076a53f fp: 0xcae4bed8 + lr: 0x80249b47 fp: 0xcae4bf10 + lr: 0x802497c3 fp: 0xcae4bf60 + lr: 0x8024a299 fp: 0xcae4bf88 + lr: 0x8024a06b fp: 0xcae4bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a7570 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02a97213 r5: 0xc04a2200 r6: 0x00000000 r7: 0xcae53dd8 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04a7570 + r12: 0x800880a0 sp: 0xcae53dac lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae53dd8 + lr: 0x800244d9 fp: 0xcae53e04 + lr: 0x8001ed6d fp: 0xcae53e1c + lr: 0x802642b3 fp: 0xcae53e30 + lr: 0x8024bf13 fp: 0xcae53e4c + lr: 0x8024bff7 fp: 0xcae53e64 + lr: 0x8024c097 fp: 0xcae53e80 + lr: 0x80258a9f fp: 0xcae53ea0 + lr: 0x80258b7b fp: 0xcae53ec4 + lr: 0x8079a32f fp: 0xcae53ed8 + lr: 0x80249b47 fp: 0xcae53f10 + lr: 0x802497c3 fp: 0xcae53f60 + lr: 0x8024a299 fp: 0xcae53f88 + lr: 0x8024a06b fp: 0xcae53fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a6a50 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x01f30645 r5: 0xc04a64c0 r6: 0x00000000 r7: 0xcae63d4c + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04a6a50 + r12: 0x800880a0 sp: 0xcae63d20 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae63d4c + lr: 0x800244d9 fp: 0xcae63d78 + lr: 0x8001ed6d fp: 0xcae63d90 + lr: 0x80240655 fp: 0xcae63da8 + lr: 0x8024b01d fp: 0xcae63dd8 + lr: 0x80249ea3 fp: 0xcae63de8 + lr: 0x804ac80d fp: 0xcae63e1c + lr: 0x804ac789 fp: 0xcae63e2c + lr: 0x80a548c5 fp: 0xcae63e7c + lr: 0x80a55f99 fp: 0xcae63ec4 + lr: 0x80a565ad fp: 0xcae63ed8 + lr: 0x80249b47 fp: 0xcae63f10 + lr: 0x802497c3 fp: 0xcae63f60 + lr: 0x8024a299 fp: 0xcae63f88 + lr: 0x8024a06b fp: 0xcae63fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a64c0 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x01f49395 r5: 0xc058ce70 r6: 0x00000000 r7: 0xcae6bd8c + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc04a64c0 + r12: 0x800880a0 sp: 0xcae6bd60 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae6bd8c + lr: 0x800244d9 fp: 0xcae6bdb8 + lr: 0x8001ed6d fp: 0xcae6bdd0 + lr: 0x80240655 fp: 0xcae6bde8 + lr: 0x8024b01d fp: 0xcae6be1c + lr: 0x80249ea3 fp: 0xcae6be2c + lr: 0x804ac80d fp: 0xcae6be60 + lr: 0x804ac789 fp: 0xcae6be70 + lr: 0x804bbde3 fp: 0xcae6bed8 + lr: 0x80249b47 fp: 0xcae6bf10 + lr: 0x802497c3 fp: 0xcae6bf60 + lr: 0x8024a299 fp: 0xcae6bf88 + lr: 0x8024a06b fp: 0xcae6bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a4e80 + kernel continuation: 0x8002fde1 + + thread 0xc04a48f0 + kernel continuation: 0x801e34a5 + +>>>>>>>>thread 0xc04ab2a0 +>>>>>>>> kernel backtrace: cae2bd60 +>>>>>>>> lr: 0x8008a7e5 fp: 0xcae2bd8c +>>>>>>>> lr: 0x80017ca7 fp: 0xcae2bda4 +>>>>>>>> lr: 0x8065f7ed fp: 0xcae2bdcc +>>>>>>>> lr: 0x809391f1 fp: 0xcae2bde8 +>>>>>>>> lr: 0x80939a7d fp: 0xcae2be04 +>>>>>>>> lr: 0x8025914f fp: 0xcae2be2c +>>>>>>>> lr: 0x80939a4f fp: 0xcae2be48 +>>>>>>>> lr: 0x8065ab17 fp: 0xcae2be54 +>>>>>>>> lr: 0x80898e1f fp: 0xcae2be60 +>>>>>>>> lr: 0x80898c03 fp: 0xcae2be94 +>>>>>>>> lr: 0x808989d5 fp: 0xcae2bea8 +>>>>>>>> lr: 0x80896673 fp: 0xcae2bed8 +>>>>>>>> lr: 0x80249b47 fp: 0xcae2bf10 +>>>>>>>> lr: 0x802497c3 fp: 0xcae2bf60 +>>>>>>>> lr: 0x8024a299 fp: 0xcae2bf88 +>>>>>>>> lr: 0x8024a06b fp: 0xcae2bfa8 +>>>>>>>> lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a3840 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02239bed r5: 0xc0588bb0 r6: 0x00000000 r7: 0xcae0be14 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04a3840 + r12: 0x800880a0 sp: 0xcae0bde8 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae0be14 + lr: 0x800244d9 fp: 0xcae0be40 + lr: 0x8001ed6d fp: 0xcae0be58 + lr: 0x80240655 fp: 0xcae0be70 + lr: 0x8024b01d fp: 0xcae0bea0 + lr: 0x809e37d5 fp: 0xcae0bed8 + lr: 0x80249b47 fp: 0xcae0bf10 + lr: 0x802497c3 fp: 0xcae0bf60 + lr: 0x8024a299 fp: 0xcae0bf88 + lr: 0x8024a06b fp: 0xcae0bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a32b0 + kernel state: + r0: 0x79656b3c r1: 0x5064693e r2: 0x75646f72 r3: 0x2f3c7463 + r4: 0x022cc054 r5: 0xc04a2d20 r6: 0x00000000 r7: 0xcc363dcc + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04a32b0 + r12: 0x800880a0 sp: 0xcc363da0 lr: 0x80024cab pc: 0x3c3e7265 + cpsr: 0x00000093 fsr: 0x6b3c3e74 far: 0x413e7965 + kernel backtrace: cc363dcc + lr: 0x800244d9 fp: 0xcc363df8 + lr: 0x8001ed6d fp: 0xcc363e10 + lr: 0x80240655 fp: 0xcc363e28 + lr: 0x8024b01d fp: 0xcc363e58 + lr: 0x80249ea3 fp: 0xcc363e68 + lr: 0x80706d67 fp: 0xcc363e98 + lr: 0x809b5e75 fp: 0xcc363ed8 + lr: 0x80249b47 fp: 0xcc363f10 + lr: 0x802497c3 fp: 0xcc363f60 + lr: 0x8024a299 fp: 0xcc363f88 + lr: 0x8024a06b fp: 0xcc363fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a2d20 + kernel state: + r0: 0x6b3c3e67 r1: 0x4e3e7965 r2: 0x6d754853 r3: 0x65526e61 + r4: 0x022e9b7b r5: 0xc05896d0 r6: 0x00000000 r7: 0xcc36be04 + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc04a2d20 + r12: 0x800880a0 sp: 0xcc36bdd8 lr: 0x80024cab pc: 0x322d3630 + cpsr: 0x00000093 fsr: 0x6c707041 far: 0x6e492065 + kernel backtrace: cc36be04 + lr: 0x800244d9 fp: 0xcc36be30 + lr: 0x8001ed6d fp: 0xcc36be48 + lr: 0x80240655 fp: 0xcc36be60 + lr: 0x8024b01d fp: 0xcc36be90 + lr: 0x80249ea3 fp: 0xcc36bea0 + lr: 0x806ed0c3 fp: 0xcc36bed8 + lr: 0x80249b47 fp: 0xcc36bf10 + lr: 0x802497c3 fp: 0xcc36bf60 + lr: 0x8024a299 fp: 0xcc36bf88 + lr: 0x8024a06b fp: 0xcc36bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a2790 + kernel state: + r0: 0x67616944 r1: 0x74736f6e r2: 0x61446369 r3: 0x63416174 + r4: 0x0230ec4b r5: 0xc04a2200 r6: 0x00000000 r7: 0xcc373dfc + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04a2790 + r12: 0x800880a0 sp: 0xcc373dd0 lr: 0x80024cab pc: 0x656b2f3c + cpsr: 0x00000093 fsr: 0x3e746369 far: 0x79656b3c + kernel backtrace: cc373dfc + lr: 0x800244d9 fp: 0xcc373e28 + lr: 0x8001ed6d fp: 0xcc373e40 + lr: 0x80240655 fp: 0xcc373e58 + lr: 0x8024b01d fp: 0xcc373e8c + lr: 0x80249ea3 fp: 0xcc373e9c + lr: 0x806d1d2b fp: 0xcc373ed8 + lr: 0x80249b47 fp: 0xcc373f10 + lr: 0x802497c3 fp: 0xcc373f60 + lr: 0x8024a299 fp: 0xcc373f88 + lr: 0x8024a06b fp: 0xcc373fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a2200 + kernel state: + r0: 0x75415641 r1: 0x446f6964 r2: 0x65766972 r3: 0x6b2f3c72 + r4: 0x02a9d2fd r5: 0xc04a1c70 r6: 0x00000000 r7: 0xcc37be0c + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04a2200 + r12: 0x800880a0 sp: 0xcc37bde0 lr: 0x80024cab pc: 0x76697244 + cpsr: 0x00000093 fsr: 0x69727473 far: 0x3c3e676e + kernel backtrace: cc37be0c + lr: 0x800244d9 fp: 0xcc37be38 + lr: 0x8001ed6d fp: 0xcc37be50 + lr: 0x80240655 fp: 0xcc37be68 + lr: 0x8024b01d fp: 0xcc37be98 + lr: 0x80249ea3 fp: 0xcc37bea8 + lr: 0x80b0cc4b fp: 0xcc37bed8 + lr: 0x80249b47 fp: 0xcc37bf10 + lr: 0x802497c3 fp: 0xcc37bf60 + lr: 0x8024a299 fp: 0xcc37bf88 + lr: 0x8024a06b fp: 0xcc37bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a1c70 + kernel state: + r0: 0x3c737361 r1: 0x79656b2f r2: 0x74733c3e r3: 0x676e6972 + r4: 0x02aa226e r5: 0xc04a16e0 r6: 0x00000000 r7: 0xcc383dfc + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04a1c70 + r12: 0x800880a0 sp: 0xcc383dd0 lr: 0x80024cab pc: 0x6c707041 + cpsr: 0x00000093 fsr: 0x3c6c6562 far: 0x7274732f + kernel backtrace: cc383dfc + lr: 0x800244d9 fp: 0xcc383e28 + lr: 0x8001ed6d fp: 0xcc383e40 + lr: 0x80240655 fp: 0xcc383e58 + lr: 0x8024b01d fp: 0xcc383e8c + lr: 0x80249ea3 fp: 0xcc383e9c + lr: 0x80a8bf79 fp: 0xcc383ed8 + lr: 0x80249b47 fp: 0xcc383f10 + lr: 0x802497c3 fp: 0xcc383f60 + lr: 0x8024a299 fp: 0xcc383f88 + lr: 0x8024a06b fp: 0xcc383fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a16e0 + kernel state: + r0: 0x2f3c7469 r1: 0x3e79656b r2: 0x7274733c r3: 0x20676e69 + r4: 0x02aac1ff r5: 0xc049b2c0 r6: 0x00000000 r7: 0xcc38be24 + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc04a16e0 + r12: 0x800880a0 sp: 0xcc38bdf8 lr: 0x80024cab pc: 0x74726f70 + cpsr: 0x00000093 fsr: 0x3e79656b far: 0x7274733c + kernel backtrace: cc38be24 + lr: 0x800244d9 fp: 0xcc38be50 + lr: 0x8001ed6d fp: 0xcc38be68 + lr: 0x80240655 fp: 0xcc38be80 + lr: 0x8024b01d fp: 0xcc38beb0 + lr: 0x80249ea3 fp: 0xcc38bec0 + lr: 0x804e562f fp: 0xcc38bed8 + lr: 0x80249b47 fp: 0xcc38bf10 + lr: 0x802497c3 fp: 0xcc38bf60 + lr: 0x8024a299 fp: 0xcc38bf88 + lr: 0x8024a06b fp: 0xcc38bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04a0630 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02491bfa r5: 0xc058bdc0 r6: 0x00000000 r7: 0xcc3a3d7c + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc04a0630 + r12: 0x800880a0 sp: 0xcc3a3d50 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc3a3d7c + lr: 0x800244d9 fp: 0xcc3a3da8 + lr: 0x8001ed6d fp: 0xcc3a3dc0 + lr: 0x80240655 fp: 0xcc3a3dd8 + lr: 0x8024b01d fp: 0xcc3a3e0c + lr: 0x80249ea3 fp: 0xcc3a3e1c + lr: 0x8026935d fp: 0xcc3a3e5c + lr: 0x80259ecd fp: 0xcc3a3e80 + lr: 0x80259f85 fp: 0xcc3a3ea8 + lr: 0x809502e5 fp: 0xcc3a3ec0 + lr: 0x80952279 fp: 0xcc3a3ecc + lr: 0x80952631 fp: 0xcc3a3ed8 + lr: 0x80249b47 fp: 0xcc3a3f10 + lr: 0x802497c3 fp: 0xcc3a3f60 + lr: 0x8024a299 fp: 0xcc3a3f88 + lr: 0x8024a06b fp: 0xcc3a3fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc049fb10 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x024d9ad5 r5: 0xc04aad10 r6: 0x00000000 r7: 0xcc3b3d3c + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc049fb10 + r12: 0x800880a0 sp: 0xcc3b3d10 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc3b3d3c + lr: 0x800244d9 fp: 0xcc3b3d68 + lr: 0x8001ed6d fp: 0xcc3b3d80 + lr: 0x80240655 fp: 0xcc3b3d98 + lr: 0x8024b01d fp: 0xcc3b3dc8 + lr: 0x80249ea3 fp: 0xcc3b3dd8 + lr: 0x8026935d fp: 0xcc3b3e18 + lr: 0x80259ecd fp: 0xcc3b3e3c + lr: 0x80259f85 fp: 0xcc3b3e64 + lr: 0x8083bf8f fp: 0xcc3b3ed8 + lr: 0x80249b47 fp: 0xcc3b3f10 + lr: 0x802497c3 fp: 0xcc3b3f60 + lr: 0x8024a299 fp: 0xcc3b3f88 + lr: 0x8024a06b fp: 0xcc3b3fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc049f580 + kernel state: + r0: 0x36223d65 r1: 0x303e2234 r2: 0x33303878 r3: 0x30303963 + r4: 0x024eb061 r5: 0xc058c8e0 r6: 0x00000000 r7: 0xcc3bbd3c + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc049f580 + r12: 0x800880a0 sp: 0xcc3bbd10 lr: 0x80024cab pc: 0x73207265 + cpsr: 0x00000093 fsr: 0x22343622 far: 0x3878303e + kernel backtrace: cc3bbd3c + lr: 0x800244d9 fp: 0xcc3bbd68 + lr: 0x8001ed6d fp: 0xcc3bbd80 + lr: 0x80240655 fp: 0xcc3bbd98 + lr: 0x8024b01d fp: 0xcc3bbdc8 + lr: 0x80249ea3 fp: 0xcc3bbdd8 + lr: 0x8026935d fp: 0xcc3bbe18 + lr: 0x80259ecd fp: 0xcc3bbe3c + lr: 0x80259f85 fp: 0xcc3bbe64 + lr: 0x8083bf8f fp: 0xcc3bbed8 + lr: 0x80249b47 fp: 0xcc3bbf10 + lr: 0x802497c3 fp: 0xcc3bbf60 + lr: 0x8024a299 fp: 0xcc3bbf88 + lr: 0x8024a06b fp: 0xcc3bbfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc049eff0 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x025023bd r5: 0xc04a5410 r6: 0x00000000 r7: 0xcc3c3d3c + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc049eff0 + r12: 0x800880a0 sp: 0xcc3c3d10 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc3c3d3c + lr: 0x800244d9 fp: 0xcc3c3d68 + lr: 0x8001ed6d fp: 0xcc3c3d80 + lr: 0x80240655 fp: 0xcc3c3d98 + lr: 0x8024b01d fp: 0xcc3c3dc8 + lr: 0x80249ea3 fp: 0xcc3c3dd8 + lr: 0x8026935d fp: 0xcc3c3e18 + lr: 0x80259ecd fp: 0xcc3c3e3c + lr: 0x80259f85 fp: 0xcc3c3e64 + lr: 0x8083bf8f fp: 0xcc3c3ed8 + lr: 0x80249b47 fp: 0xcc3c3f10 + lr: 0x802497c3 fp: 0xcc3c3f60 + lr: 0x8024a299 fp: 0xcc3c3f88 + lr: 0x8024a06b fp: 0xcc3c3fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc049ce90 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02a07426 r5: 0xc0586fe0 r6: 0x00000000 r7: 0xcc3f3de4 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc049ce90 + r12: 0x800880a0 sp: 0xcc3f3db8 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc3f3de4 + lr: 0x800244d9 fp: 0xcc3f3e10 + lr: 0x8001ed6d fp: 0xcc3f3e28 + lr: 0x80240655 fp: 0xcc3f3e40 + lr: 0x8024b01d fp: 0xcc3f3e70 + lr: 0x80249ea3 fp: 0xcc3f3e80 + lr: 0x804ac80d fp: 0xcc3f3eb4 + lr: 0x804ac789 fp: 0xcc3f3ec4 + lr: 0x80a6d51d fp: 0xcc3f3ed8 + lr: 0x80249b47 fp: 0xcc3f3f10 + lr: 0x802497c3 fp: 0xcc3f3f60 + lr: 0x8024a299 fp: 0xcc3f3f88 + lr: 0x8024a06b fp: 0xcc3f3fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc049bde0 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02654ead r5: 0xc049b850 r6: 0x00000000 r7: 0xcc40bdfc + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc049bde0 + r12: 0x800880a0 sp: 0xcc40bdd0 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc40bdfc + lr: 0x800244d9 fp: 0xcc40be28 + lr: 0x8001ed6d fp: 0xcc40be40 + lr: 0x80240655 fp: 0xcc40be58 + lr: 0x8024b01d fp: 0xcc40be8c + lr: 0x80249ea3 fp: 0xcc40be9c + lr: 0x806d1d2b fp: 0xcc40bed8 + lr: 0x80249b47 fp: 0xcc40bf10 + lr: 0x802497c3 fp: 0xcc40bf60 + lr: 0x8024a299 fp: 0xcc40bf88 + lr: 0x8024a06b fp: 0xcc40bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04996f0 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02f76a91 r5: 0xc04ace70 r6: 0x00000000 r7: 0xcc443d64 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04996f0 + r12: 0x800880a0 sp: 0xcc443d38 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc443d64 + lr: 0x800244d9 fp: 0xcc443d90 + lr: 0x8001ed6d fp: 0xcc443da8 + lr: 0x80240655 fp: 0xcc443dc0 + lr: 0x8024b01d fp: 0xcc443df0 + lr: 0x80249ea3 fp: 0xcc443e00 + lr: 0x8026935d fp: 0xcc443e40 + lr: 0x80259ecd fp: 0xcc443e64 + lr: 0x80259f85 fp: 0xcc443e8c + lr: 0x808c9fe5 fp: 0xcc443eb8 + lr: 0x808c9e73 fp: 0xcc443ed8 + lr: 0x80249b47 fp: 0xcc443f10 + lr: 0x802497c3 fp: 0xcc443f60 + lr: 0x8024a299 fp: 0xcc443f88 + lr: 0x8024a06b fp: 0xcc443fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc0499160 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x029df5a9 r5: 0xc0586a50 r6: 0x00000000 r7: 0xcc44bdfc + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc0499160 + r12: 0x800880a0 sp: 0xcc44bdd0 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc44bdfc + lr: 0x800244d9 fp: 0xcc44be28 + lr: 0x8001ed6d fp: 0xcc44be40 + lr: 0x80240655 fp: 0xcc44be58 + lr: 0x8024b01d fp: 0xcc44be8c + lr: 0x80249ea3 fp: 0xcc44be9c + lr: 0x80ac2227 fp: 0xcc44bed8 + lr: 0x80249b47 fp: 0xcc44bf10 + lr: 0x802497c3 fp: 0xcc44bf60 + lr: 0x8024a299 fp: 0xcc44bf88 + lr: 0x8024a06b fp: 0xcc44bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc0498bd0 + kernel continuation: 0x802582fd + + thread 0xc0498640 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02a96d0f r5: 0xc04a7570 r6: 0x00000000 r7: 0xcc4cbdf0 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc0498640 + r12: 0x800880a0 sp: 0xcc4cbdc4 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc4cbdf0 + lr: 0x800244d9 fp: 0xcc4cbe1c + lr: 0x804b04f7 fp: 0xcc4cbe48 + lr: 0x8024715b fp: 0xcc4cbe70 + lr: 0x80668893 fp: 0xcc4cbebc + lr: 0x808a284f fp: 0xcc4cbed8 + lr: 0x80249b47 fp: 0xcc4cbf10 + lr: 0x802497c3 fp: 0xcc4cbf60 + lr: 0x8024a299 fp: 0xcc4cbf88 + lr: 0x8024a06b fp: 0xcc4cbfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc04980b0 + kernel continuation: 0x802582fd + + thread 0xc0497b20 + kernel continuation: 0x802582fd + + thread 0xc0497590 + kernel continuation: 0x802582fd + + thread 0xc0497000 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x01e5bdb3 r5: 0xc04a7b00 r6: 0x00000000 r7: 0xcae5bf64 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc0497000 + r12: 0x800880a0 sp: 0xcae5bf38 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae5bf64 + lr: 0x800244d9 fp: 0xcae5bf90 + lr: 0x80258339 fp: 0xcae5bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc058d990 + kernel continuation: 0x802582fd + + thread 0xc058d400 + kernel continuation: 0x802582fd + + thread 0xc058ce70 + kernel continuation: 0x802582fd + + thread 0xc058ad10 + kernel continuation: 0x802582fd + + thread 0xc0588bb0 + kernel continuation: 0x802582fd + + thread 0xc0589140 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x0223a2ca r5: 0xc04a32b0 r6: 0x00000000 r7: 0xcc4dbf64 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc0589140 + r12: 0x800880a0 sp: 0xcc4dbf38 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc4dbf64 + lr: 0x800244d9 fp: 0xcc4dbf90 + lr: 0x80258339 fp: 0xcc4dbfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc05896d0 + kernel continuation: 0x802582fd + + thread 0xc0589c60 + kernel continuation: 0x802582fd + + thread 0xc058a1f0 + kernel continuation: 0x802582fd + + thread 0xc058a780 + kernel continuation: 0x802582fd + + thread 0xc058bdc0 + kernel continuation: 0x802582fd + + thread 0xc058c350 + kernel continuation: 0x802582fd + + thread 0xc04aad10 + kernel continuation: 0x802582fd + + thread 0xc058c8e0 + kernel continuation: 0x802582fd + + thread 0xc04a5410 + kernel continuation: 0x802582fd + + thread 0xc04a9140 + kernel continuation: 0x802582fd + + thread 0xc04a3dd0 + kernel continuation: 0x802582fd + + thread 0xc04a5f30 + kernel continuation: 0x802582fd + + thread 0xc04a59a0 + kernel continuation: 0x802582fd + + thread 0xc0588090 + kernel continuation: 0x802582fd + + thread 0xc0587570 + kernel continuation: 0x802582fd + + thread 0xc0586fe0 + kernel continuation: 0x802582fd + + thread 0xc0588620 + kernel continuation: 0x802582fd + + thread 0xc049d420 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x026b871e r5: 0xc04a4360 r6: 0x00000000 r7: 0xcae43ddc + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc049d420 + r12: 0x800880a0 sp: 0xcae43db0 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae43ddc + lr: 0x800244d9 fp: 0xcae43e08 + lr: 0x8001ed6d fp: 0xcae43e20 + lr: 0x80240655 fp: 0xcae43e38 + lr: 0x8024b01d fp: 0xcae43e68 + lr: 0x80249ea3 fp: 0xcae43e78 + lr: 0x804ac80d fp: 0xcae43eac + lr: 0x804ac789 fp: 0xcae43ebc + lr: 0x8093f86f fp: 0xcae43ed8 + lr: 0x80249b47 fp: 0xcae43f10 + lr: 0x802497c3 fp: 0xcae43f60 + lr: 0x8024a299 fp: 0xcae43f88 + lr: 0x8024a06b fp: 0xcae43fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc058b830 + kernel continuation: 0x802582fd + + thread 0xc04a0bc0 + kernel continuation: 0x802582fd + + thread 0xc058b2a0 + kernel continuation: 0x802582fd + + thread 0xc04a8090 + kernel continuation: 0x802582fd + + thread 0xc0586a50 + kernel continuation: 0x802582fd + + thread 0xc04a6fe0 + kernel continuation: 0x8002fde1 + + thread 0xc049b2c0 + kernel continuation: 0x802582fd + + thread 0xc04a4360 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02aef154 r5: 0xc04aa780 r6: 0x00000000 r7: 0xcae73e24 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc04a4360 + r12: 0x800880a0 sp: 0xcae73df8 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae73e24 + lr: 0x800244d9 fp: 0xcae73e50 + lr: 0x8001ed6d fp: 0xcae73e68 + lr: 0x80240655 fp: 0xcae73e80 + lr: 0x8024b01d fp: 0xcae73eb0 + lr: 0x80249d95 fp: 0xcae73ec4 + lr: 0x80249f35 fp: 0xcae73ed8 + lr: 0x80249b23 fp: 0xcae73f10 + lr: 0x802497c3 fp: 0xcae73f60 + lr: 0x8024a299 fp: 0xcae73f88 + lr: 0x8024a06b fp: 0xcae73fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc049b850 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02b2ce4b r5: 0xc04aa780 r6: 0x00000000 r7: 0xcc3abe24 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc049b850 + r12: 0x800880a0 sp: 0xcc3abdf8 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc3abe24 + lr: 0x800244d9 fp: 0xcc3abe50 + lr: 0x8001ed6d fp: 0xcc3abe68 + lr: 0x80240655 fp: 0xcc3abe80 + lr: 0x8024b01d fp: 0xcc3abeb0 + lr: 0x80249d95 fp: 0xcc3abec4 + lr: 0x80249f35 fp: 0xcc3abed8 + lr: 0x80249b23 fp: 0xcc3abf10 + lr: 0x802497c3 fp: 0xcc3abf60 + lr: 0x8024a299 fp: 0xcc3abf88 + lr: 0x8024a06b fp: 0xcc3abfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc0587b00 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02b840ac r5: 0xc049c900 r6: 0x00000000 r7: 0xcc3cbde4 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc0587b00 + r12: 0x800880a0 sp: 0xcc3cbdb8 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc3cbde4 + lr: 0x800244d9 fp: 0xcc3cbe10 + lr: 0x8001ed6d fp: 0xcc3cbe28 + lr: 0x80240655 fp: 0xcc3cbe40 + lr: 0x80258781 fp: 0xcc3cbe48 + lr: 0x8078bfdd fp: 0xcc3cbe64 + lr: 0x804ad255 fp: 0xcc3cbe74 + lr: 0x804ad167 fp: 0xcc3cbe88 + lr: 0x8025914f fp: 0xcc3cbeb0 + lr: 0x804ad8e5 fp: 0xcc3cbee8 + lr: 0x80ad0949 fp: 0xcc3cbefc + lr: 0x80acfa7f fp: 0xcc3cbf10 + lr: 0x80249657 fp: 0xcc3cbf60 + lr: 0x8024a299 fp: 0xcc3cbf88 + lr: 0x8024a06b fp: 0xcc3cbfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc049c900 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02bacb7e r5: 0xc05864c0 r6: 0x00000000 r7: 0xcae33e08 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc049c900 + r12: 0x800880a0 sp: 0xcae33ddc lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae33e08 + lr: 0x800244d9 fp: 0xcae33e34 + lr: 0x8001ed6d fp: 0xcae33e4c + lr: 0x80240655 fp: 0xcae33e64 + lr: 0x80258781 fp: 0xcae33e6c + lr: 0x804ad14f fp: 0xcae33e80 + lr: 0x8025914f fp: 0xcae33ea8 + lr: 0x804ad8e5 fp: 0xcae33ee0 + lr: 0x808113b5 fp: 0xcae33ef4 + lr: 0x80811681 fp: 0xcae33f10 + lr: 0x80249657 fp: 0xcae33f60 + lr: 0x8024a299 fp: 0xcae33f88 + lr: 0x8024a06b fp: 0xcae33fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc05864c0 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02bb5c89 r5: 0xc0585f30 r6: 0x00000000 r7: 0xc4dd3e08 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc05864c0 + r12: 0x800880a0 sp: 0xc4dd3ddc lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: c4dd3e08 + lr: 0x800244d9 fp: 0xc4dd3e34 + lr: 0x8001ed6d fp: 0xc4dd3e4c + lr: 0x80240655 fp: 0xc4dd3e64 + lr: 0x80258781 fp: 0xc4dd3e6c + lr: 0x804ad14f fp: 0xc4dd3e80 + lr: 0x8025914f fp: 0xc4dd3ea8 + lr: 0x804ad8e5 fp: 0xc4dd3ee0 + lr: 0x808113b5 fp: 0xc4dd3ef4 + lr: 0x80811681 fp: 0xc4dd3f10 + lr: 0x80249657 fp: 0xc4dd3f60 + lr: 0x8024a299 fp: 0xc4dd3f88 + lr: 0x8024a06b fp: 0xc4dd3fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc0585f30 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02c7b225 r5: 0xc05859a0 r6: 0x00000000 r7: 0xcae3be1c + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc0585f30 + r12: 0x800880a0 sp: 0xcae3bdf0 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cae3be1c + lr: 0x800244d9 fp: 0xcae3be48 + lr: 0x8001ed6d fp: 0xcae3be60 + lr: 0x80240655 fp: 0xcae3be78 + lr: 0x8024b01d fp: 0xcae3bea8 + lr: 0x80249ea3 fp: 0xcae3beb8 + lr: 0x807c2379 fp: 0xcae3bed8 + lr: 0x80249b47 fp: 0xcae3bf10 + lr: 0x802497c3 fp: 0xcae3bf60 + lr: 0x8024a299 fp: 0xcae3bf88 + lr: 0x8024a06b fp: 0xcae3bfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc05859a0 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02caa3ab r5: 0xc0585410 r6: 0x00000000 r7: 0xcadfbd9c + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc05859a0 + r12: 0x800880a0 sp: 0xcadfbd70 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cadfbd9c + lr: 0x800244d9 fp: 0xcadfbdc8 + lr: 0x8001ed6d fp: 0xcadfbde0 + lr: 0x80240655 fp: 0xcadfbdf8 + lr: 0x80258781 fp: 0xcadfbe00 + lr: 0x804ad14f fp: 0xcadfbe14 + lr: 0x8025914f fp: 0xcadfbe3c + lr: 0x804ad8e5 fp: 0xcadfbe74 + lr: 0x80ae6a21 fp: 0xcadfbea0 + lr: 0x80ae63c7 fp: 0xcadfbed8 + lr: 0x80249b47 fp: 0xcadfbf10 + lr: 0x802497c3 fp: 0xcadfbf60 + lr: 0x8024a299 fp: 0xcadfbf88 + lr: 0x8024a06b fp: 0xcadfbfa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc0585410 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02cd1181 r5: 0xc0584e80 r6: 0x00000000 r7: 0xcc423dec + r8: 0x00000000 r9: 0x0000c000 r10: 0x80329838 r11: 0xc0585410 + r12: 0x800880a0 sp: 0xcc423dc0 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc423dec + lr: 0x800244d9 fp: 0xcc423e18 + lr: 0x8001ed6d fp: 0xcc423e30 + lr: 0x802642b3 fp: 0xcc423e44 + lr: 0x8024bf13 fp: 0xcc423e60 + lr: 0x8024bff7 fp: 0xcc423e78 + lr: 0x8024c097 fp: 0xcc423e94 + lr: 0x807e2707 fp: 0xcc423ebc + lr: 0x8076a53f fp: 0xcc423ed8 + lr: 0x80249b47 fp: 0xcc423f10 + lr: 0x802497c3 fp: 0xcc423f60 + lr: 0x8024a299 fp: 0xcc423f88 + lr: 0x8024a06b fp: 0xcc423fa8 + lr: 0x8008805c fp: 0x00000000 + + thread 0xc0584360 + kernel continuation: 0x8002cd11 + + thread 0xc0583dd0 + kernel continuation: 0x802582fd + + thread 0xc05832b0 + kernel state: + r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00000000 + r4: 0x02e0715b r5: 0xc0582d20 r6: 0x00000000 r7: 0xcc403f64 + r8: 0x00000000 r9: 0x00000000 r10: 0x80329838 r11: 0xc05832b0 + r12: 0x800880a0 sp: 0xcc403f38 lr: 0x80024cab pc: 0x00000000 + cpsr: 0x00000093 fsr: 0x00000000 far: 0x00000000 + kernel backtrace: cc403f64 + lr: 0x800244d9 fp: 0xcc403f90 + lr: 0x80258339 fp: 0xcc403fa8 + lr: 0x8008805c fp: 0x00000000 diff --git a/ios6/results/20261003-first-boot/summary.json b/ios6/results/20261003-first-boot/summary.json new file mode 100644 index 0000000000..bac646288b --- /dev/null +++ b/ios6/results/20261003-first-boot/summary.json @@ -0,0 +1,28 @@ +{ + "run": "/home/dev/ios6-emu/evidence/probe-20261003T101443-261760Z", + "seconds": 10.0, + "status": { + "status": "paused", + "singlestep": false, + "running": false + }, + "returncode": 0, + "guest_status": "kernel-panic", + "panic": "panic(cpu 0 caller 0x8065f7ed): \"ARM7M not stopped for some reason\"", + "milestones": { + "serial_console": true, + "crypto_self_tests": true, + "platform_driver": true, + "interrupt_controller_driver": true, + "userspace_confirmed": false + }, + "expected_panic_matched": true, + "exceptions": { + "Taking exception 6 [FIQ] on CPU 0": 341, + "Taking exception 1 [Undefined Instruction] on CPU 0": 16, + "Taking exception 5 [IRQ] on CPU 0": 1 + }, + "kernel_modified": false, + "machine": "ios6-n88", + "firmware": "iPhone2,1 / 6.1.6 / 10B500" +} diff --git a/ios6/results/20261003-first-boot/verification.txt b/ios6/results/20261003-first-boot/verification.txt new file mode 100644 index 0000000000..681127c56a --- /dev/null +++ b/ios6/results/20261003-first-boot/verification.txt @@ -0,0 +1 @@ +OK : empreintes, DeviceTree, noyau inchangé, BSS, mémoire réservée, RAMDisk, NVRAM et source QEMU. diff --git a/ios6/results/20261003-native-apps/app.log b/ios6/results/20261003-native-apps/app.log new file mode 100644 index 0000000000..9b7b79927b --- /dev/null +++ b/ios6/results/20261003-native-apps/app.log @@ -0,0 +1,31 @@ +Dec 31 16:00:33 iPhone installd[32]: 0x3d7dfb88 verify_signer_identity: MISValidateSignatureAndCopyInfo failed for /Applications/org.ios6emu.SimulatorLab.app/SimulatorLab: 0xe800801c +Dec 31 16:00:33 iPhone installd[32]: 0x3d7dfb88 load_application_info: WARNING: failed to validate binary at path /Applications/org.ios6emu.SimulatorLab.app/SimulatorLab: 0xe800801c. This binary may not work properly. +Dec 31 16:00:33 iPhone installd[32]: 0x3d7dfb88 __reconcileApps_block_invoke_0550: org.ios6emu.SimulatorLab got added +Oct 3 06:28:39 iPhone amfid[137]: /Applications/org.ios6emu.SimulatorLab.app/SimulatorLab not valid: 0xe800801c: No code signature found. +Oct 3 06:28:42 iPhone SimulatorLab[140]: SimulatorLab count=0 +Oct 3 06:28:42 iPhone SimulatorLab[140]: SimulatorLab ready +Oct 3 06:29:01 iPhone SimulatorLab[140]: 5 transactions +Oct 3 06:29:12 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:29:13 iPhone SimulatorLab[140]: SimulatorLab count=1 +Oct 3 06:29:13 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:30:00 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:31:00 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:32:00 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:33:00 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:34:01 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:34:02 iPhone SimulatorLab[140]: 6 transactions +Oct 3 06:34:03 iPhone SimulatorLab[140]: 9 transactions +Oct 3 06:35:00 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:35:29 iPhone SimulatorLab[140]: 1 transactions +Oct 3 06:35:38 iPhone SimulatorLab[140]: 1 transactions +Dec 31 16:00:41 iPhone installd[31]: 0x3ba54b88 verify_signer_identity: MISValidateSignatureAndCopyInfo failed for /Applications/org.ios6emu.SimulatorLab.app/SimulatorLab: 0xe800801c +Dec 31 16:00:41 iPhone installd[31]: 0x3ba54b88 load_application_info: WARNING: failed to validate binary at path /Applications/org.ios6emu.SimulatorLab.app/SimulatorLab: 0xe800801c. This binary may not work properly. +Oct 3 06:28:34 iPhone amfid[151]: /Applications/org.ios6emu.SimulatorLab.app/SimulatorLab not valid: 0xe800801c: No code signature found. +Oct 3 06:28:39 iPhone SimulatorLab[150]: SimulatorLab count=0 +Oct 3 06:28:40 iPhone SimulatorLab[150]: SimulatorLab ready +Oct 3 06:28:42 iPhone SimulatorLab[150]: 5 transactions +Oct 3 06:28:42 iPhone SimulatorLab[150]: SimulatorLab count=1 +Oct 3 06:28:43 iPhone SimulatorLab[150]: 2 transactions +Oct 3 06:28:44 iPhone SimulatorLab[150]: SimulatorLab count=2 +Oct 3 06:28:45 iPhone SimulatorLab[150]: 2 transactions +Oct 3 06:28:45 iPhone SimulatorLab[150]: SimulatorLab count=3 diff --git a/ios6/results/20261003-native-apps/development-validation.json b/ios6/results/20261003-native-apps/development-validation.json new file mode 100644 index 0000000000..e6861197f4 --- /dev/null +++ b/ios6/results/20261003-native-apps/development-validation.json @@ -0,0 +1,22 @@ +{ + "firmware": "iOS 6.1.6 (10B500), iPhone2,1", + "app_architecture": "ARMv7 Mach-O", + "sdk": "iPhoneOS6.0.sdk", + "sdk_archive_sha256": "68a163f56d7ff9abcad18586a15c76f572de99e13c122e119118e228cdecb652", + "checks": [ + "native app rebuilt and reinstalled", + "data file survived installation and cold reboot", + "system bundle IDs rejected", + "non Mach-O executables rejected", + "symlink bundles rejected", + "concurrent lifecycle lock rejected", + "firmware and memory layout verification passed" + ], + "sha256": { + "build/apps/SimulatorLab.app/SimulatorLab": "06e91095ac3a7bcd0e5015c93cbcdebeb6e7fbc2e5b4ff590296ece9cab8c104", + "build/ios6-agent": "c503acea3a901770af193386e8e5bf8ca1728fa1eaf6fb28bea35553e369a737", + "build/qemu-system-arm": "c59f1330b7ee5bb59b63be009b6442e68f9d862398ebb447f362bbe968864ac4", + "firmware/kernel-rootfs.bin": "56fa914337772f16a8c6155f958e5f5551852820988b9d6d910236a8936084f1", + "firmware/boot-display-rootfs.bin": "1c773d638522c173e0c3183558a2f19ae34d931572902fc54e55c0977cfc513b" + } +} diff --git a/ios6/results/20261003-native-apps/native-app.png b/ios6/results/20261003-native-apps/native-app.png new file mode 100644 index 0000000000000000000000000000000000000000..c758387f053550e50fada1ab7f5d4b2d0bc3508b GIT binary patch literal 17435 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 aWti9-yT1v1)!>hQKr#}F;-#X-AO92ZDbX$f literal 0 HcmV?d00001 diff --git a/ios6/results/20261003-native-apps/validation.json b/ios6/results/20261003-native-apps/validation.json new file mode 100644 index 0000000000..cd5c2e3b23 --- /dev/null +++ b/ios6/results/20261003-native-apps/validation.json @@ -0,0 +1,14 @@ +{ + "date": "20261003T134812Z", + "bundle": "org.ios6emu.SimulatorLab", + "checks": [ + "retour à SpringBoard", + "lancement natif par SpringBoardServices", + "bouton de remise à zéro", + "trois appuis UIKit : count=3", + "transfert binaire aller-retour", + "HTTP invité vers serveur local via PPP", + "coordonnées tactiles invalides refusées", + "récupération des traces NSLog" + ] +} diff --git a/ios6/results/20261003-springboard/checkpoint-command.json b/ios6/results/20261003-springboard/checkpoint-command.json new file mode 100644 index 0000000000..853513eba2 --- /dev/null +++ b/ios6/results/20261003-springboard/checkpoint-command.json @@ -0,0 +1,24 @@ +[ + "/home/dev/ios6-emu/build/qemu-system-arm", + "-M", + "ios6-n88,rootfs=on", + "-kernel", + "/home/dev/ios6-emu/firmware/kernel-rootfs.bin", + "-dtb", + "/home/dev/ios6-emu/firmware/boot-display-rootfs.bin", + "-initrd", + "/home/dev/ios6-emu/firmware/rootfs-vm.hfs", + "-display", + "none", + "-serial", + "file:/home/dev/ios6-emu/evidence/probe-20261003T124815-824562Z/serial.log", + "-monitor", + "none", + "-qmp", + "unix:/home/dev/ios6-emu/evidence/probe-20261003T124815-824562Z/qmp,server=on,wait=off", + "-d", + "unimp,guest_errors", + "-D", + "/home/dev/ios6-emu/evidence/probe-20261003T124815-824562Z/qemu.log", + "-no-reboot" +] diff --git a/ios6/results/20261003-springboard/checkpoint-result.json b/ios6/results/20261003-springboard/checkpoint-result.json new file mode 100644 index 0000000000..8666afbb16 --- /dev/null +++ b/ios6/results/20261003-springboard/checkpoint-result.json @@ -0,0 +1,22 @@ +{ + "run": "/home/dev/ios6-emu/evidence/probe-20261003T124815-824562Z", + "seconds": 150.0, + "status": { + "status": "paused", + "singlestep": false, + "running": false + }, + "registers": "\r\nCPU#0\r\nR00=00000000 R01=00000000 R02=800895a4 R03=802dc000\r\nR04=00000000 R05=80329838 R06=00000000 R07=ccc2bf9c\r\nR08=800256c9 R09=c0430990 R10=80076add R11=80324f78\r\nR12=c00d9200 R13=ccc2bf74 R14=800254a1 R15=800895ac\r\nPSR=a0000093 N-C- A svc32\r\ns00=00000000 s01=00000000 d00=0000000000000000\r\ns02=000f3917 s03=00000000 d01=00000000000f3917\r\ns04=00000000 s05=00000000 d02=0000000000000000\r\ns06=00000000 s07=00000000 d03=0000000000000000\r\ns08=6c707061 s09=6f692e65 d04=6f692e656c707061\r\ns10=2e74696b s11=6374616d d05=6374616d2e74696b\r\ns12=676e6968 s13=00000000 d06=00000000676e6968\r\ns14=6e657665 s15=706f2d74 d07=706f2d746e657665\r\ns16=0ed6dd56 s17=422dd863 d08=422dd8630ed6dd56\r\ns18=00000000 s19=00000000 d09=0000000000000000\r\ns20=00000000 s21=00000000 d10=0000000000000000\r\ns22=00000000 s23=00000000 d11=0000000000000000\r\ns24=00000000 s25=00000000 d12=0000000000000000\r\ns26=00000000 s27=00000000 d13=0000000000000000\r\ns28=00000000 s29=00000000 d14=0000000000000000\r\ns30=00000000 s31=00000000 d15=0000000000000000\r\ns32=f4833efe s33=c1cd27e3 d16=c1cd27e3f4833efe\r\ns34=38b3168a s35=3fef4376 d17=3fef437638b3168a\r\ns36=a0b5ed8d s37=3eb0c6f7 d18=3eb0c6f7a0b5ed8d\r\ns38=40000000 s39=c1cd27e4 d19=c1cd27e440000000\r\ns40=00000000 s41=00000000 d20=0000000000000000\r\ns42=00000000 s43=00000000 d21=0000000000000000\r\ns44=00000000 s45=00000000 d22=0000000000000000\r\ns46=00000000 s47=00000000 d23=0000000000000000\r\ns48=00000000 s49=00000000 d24=0000000000000000\r\ns50=00000000 s51=00000000 d25=0000000000000000\r\ns52=00000000 s53=00000000 d26=0000000000000000\r\ns54=00000000 s55=00000000 d27=0000000000000000\r\ns56=00000000 s57=00000000 d28=0000000000000000\r\ns58=00000000 s59=00000000 d29=0000000000000000\r\ns60=00000000 s61=00000000 d30=0000000000000000\r\ns62=00000000 s63=00000000 d31=0000000000000000\r\nFPSCR: 23000010\r\n", + "instructions": "0xffffffff800895ac: Asm output not supported on this arch\r\n", + "stack": "ccc2bf74: 0x00000001 0x80076add 0x800104f1 0x8033d318\r\nccc2bf84: 0x800256c9 0x80076add 0xc050af30 0x80329838\r\nccc2bf94: 0xc0430990 0x800256c9 0xccc2bfa8 0x800256db\r\nccc2bfa4: 0x00000000 0x00000000 0x8008805c 0x00000000\r\nccc2bfb4: 0x00000000 0x00000000 0x00000000 0x00000000\r\nccc2bfc4: 0x80329838 0x80329d0c 0xccc2bf9c 0x80329cf8\r\nccc2bfd4: 0xc0430990 0x80325460 0x80324f78 0x80025505\r\nccc2bfe4: 0xccc2bf74 0x80088bb8 0x00000000 0x00000093\r\n", + "returncode": 0, + "guest_status": "partial-boot", + "panic": null, + "milestones": { + "serial_console": true, + "crypto_self_tests": true, + "platform_driver": true, + "interrupt_controller_driver": true, + "userspace_confirmed": true + } +} diff --git a/ios6/results/20261003-springboard/checkpoint-serial.log b/ios6/results/20261003-springboard/checkpoint-serial.log new file mode 100644 index 0000000000..da2c5c441e --- /dev/null +++ b/ios6/results/20261003-springboard/checkpoint-serial.log @@ -0,0 +1,615 @@ +iBoot version: ios6-emu direct-kernel prototype + + +corecrypto kext started! + + +Running kernel space in FIPS MODE + + +Plist hmac value is 9fd77803e256b61c91d83182dfb9aee94e57b18702a272b6a206fb074ca8e541 + + +Computed hmac value is 9fd77803e256b61c91d83182dfb9aee94e57b18702a272b6a206fb074ca8e541 + + +corecrypto.kext FIPS integrity POST test passed! + + +corecrypto.kext FIPS AES CBC POST test passed! + + +corecrypto.kext FIPS TDES CBC POST test passed! + + +corecrypto.kext FIPS SHA POST test passed! + + +corecrypto.kext FIPS HMAC POST test passed! + + +corecrypto.kext FIPS ECDSA POST test passed! + + +corecrypto.kext FIPS DRBG POST test passed! + + +corecrypto.kext FIPS POST passed! + + +kern_return_t _initializeAppleMobileFileIntegrity(): signature enforcement disabled by boot-arg + + +AppleS5L8920XIO::start: chip-revision: A0 + +AppleARMBacklight::start: Backlight calibration data 0 + + +Couldn't alloc class "AppleRawIOAddressSpace" + + +AppleARMPL192VIC::start: _vicBaseAddress = 0xcc3c5000 + +AppleS5L8920XGPIOIC::start: gpioicBaseAddress: 0xc4d3a000 + +AppleARMPerformanceController::traceBufferEnable: Trace is enabled + +AppleS5L8920XPerformanceController::start: _pcBaseAddress: 0xc4d4d000 _pcMCUBaseAddress: 0xc4d3e000 + +AppleARMPerformanceController configured with 1 Performance Domains + + Performance Domain 0 : pcVoltageStateCount: 3 pcPerformanceStateCount: 4 + + Voltage State 0 : .vsPerformanceLimit: /2 .vsDeviceConstraint: 1ff8ffef ffff8fff + + Voltage State 1 : .vsPerformanceLimit: /2 .vsDeviceConstraint: 1ffeffff ffff8fff + + Voltage State 2 : .vsPerformanceLimit: /1 .vsDeviceConstraint: 1fffffff ffff8fff + + Performance State 0 : .psPerformanceDivider: /1 .psVoltageRequired: 2 .psDeviceConstraint: 1fffffff ffff8fff + + Performance State 1 : .psPerformanceDivider: /2 .psVoltageRequired: 0 .psDeviceConstraint: 1ffeb5ff ffe78fff + + Performance State 2 : .psPerformanceDivider: /4 .psVoltageRequired: 0 .psDeviceConstraint: 1ffe24ff ffe78fff + + Performance State 3 : .psPerformanceDivider: /8 .psVoltageRequired: 0 .psDeviceConstraint: 1ffe20df ffe78fff + +AppleARMPerformanceController: Dynamic Performance State Management Enabled on Performance Domain 0 + + +AppleARMPerformanceController: Dynamic Voltage Control Enabled on Performance Domain 0 with 10000us activity timeout and 100us grace period + + +IOSDIOController::init(): IOSDIOFamily-34 Feb 13 2013 21:35:31 @ 0x833dc500 + + +AppleBaseband::start(0x83407dc0): baseband + +AppleIOPSDIO::start(): AppleIOPSDIO-12 Feb 13 2013 22:17:01 + + ++ AppleM2ScalerCSCDriver[0x834e3f00]::start(provider 0x8339f480) + + +AppleH2CamIn::start + +AppleH2CamIn::start, fSensorExtClkFunction = 0x83372F90 + +AppleH2CamIn::start, fSensorShutdownFunction = 0x83372F60 + +AppleH2CamIn::start, fISPResetFunction = 0x8336AA80 + ++ H264EncDriver: start (provider 0x8339f300) + ++ H264EncDriver: compiled on Feb 13 2013 22:32:00 + +AppleVXD375 - start (provider 0x8339f280) + +AppleVXD375 - compiled on Feb 13 2013 22:32:12 + +AppleSamsungSerial: Identified Serial Port on ARM Device=uart0 at 0x82500000(0xcad86000) + + +AppleSamsungSerial: Identified Serial Port on ARM Device=uart4 at 0x82900000(0xcad8e000) + + +AppleARMIICController::start buffer=0x8355dd80 entries=8 + +AppleS5L8920XI2CController::start: i2c0 _i2cBaseAddress: 0xcad96000 + + +AppleD1755PMU::start: pmu _pmuIICNub: 0x83582d90 built 21:35:40 Feb 13 2013 + +AppleD1755PMU::start: reading DOWN converter voltages + +AppleD1755PMU::start: DOWN0: 725mV + +AppleD1755PMU::start: DOWN1: 725mV + +AppleD1755PMU::start: DOWN2: 725mV + +AppleD1755PMU::start: DOWN3: 725mV + +AppleD1755PMU::start: set VBUCK1_PRE1 to 0 + +AppleD1755PMU::start: set VBUCK1_PRE2 to 0 + +AppleD1755PMU::start: set VBUCK1_PRE3 to 0 + +AppleD1755PMU::start: using SWI for CPU BUCK changes + +AppleARMIICController::start buffer=0x8355d600 entries=8 + +AppleS5L8920XI2CController::start: i2c2 _i2cBaseAddress: 0xcad9d000 + + +AppleS5L8920XPWM::start: _pwmBaseAddress: 0xcad9e000 + +AppleSamsungLP65USBPhy::start: hsic disabled + + +AppleSamsungMIPIDSIController::start: _dsimBaseAddress = 0xcada5000 + +AppleSamsungSWI::start: _swiBaseAddress: 0x00000000cada6000 + +AppleSamsungSWI::start: _swiBaseAddress: _swiWorstCaseIntraVBUCK=0 + +BSD root: md0, major 3, minor 0 + + +com.apple.AppleFSCompressionTypeZlib kmod start + + +AppleARMBacklight::start: _minDACLevel=56 _maxDACLevel=245 + + +000001.770986 AppleBasebandN88::loadDebugArguments: Loaded debug arguments 0x00000001 + + +ApplePinotLCD: _lcdPanelID: 0x00e50486 _lcdPanelClass: 1 + + +AppleSamsungLP65USBPhy::start : registers at virtual: 0xc4d46000, physical: 0x86000000 + +com.apple.AppleFSCompressionTypeZlib load succeeded + + +virtual bool AppleMobileFileIntegrity::start(IOService *): built Feb 11 2014 20:55:23 + + +IOSurfaceRoot::installMemoryRegions() + +L2TP domain init + + +L2TP domain init complete + + +PPTP domain init + + +virtual bool AppleM2ScalerCSCHal::init(uint32_t, IOService *, IOService *, IOWorkLoop *): scalerNumber=0, IOKitProvider=0x8339f480, powerProvider=0x8339f480 + + +bool AppleM2ScalerCSCHal::mapRegisterMemory(IOService *): Scaler 0 RegisterMemoryMap[0x835d3108]: base=0xc4d55000 physical address = 0x85500000 length = 4096 + + +Scaler 0 enableDeviceClock(true,HALF_CLOCK_INDEX) = 0x0 + + +Scaler 0 enableDeviceClock(true,FULL_CLOCK_INDEX) = 0xe00002c7 + + +Scaler 0 enableDevicePower(true,0,HALF_CLOCK_INDEX) = 0xe00002c7 + + +Scaler HW version at start is 0x0 + + +void AppleM2ScalerCSCHal::setScalerPropertiesAndExtents() WARNING: Failed to find properties and extents data for SOC 0x0, Scaler HW Version 0x0 + + +Scaler 0 enableDeviceClock(false,FULL_CLOCK_INDEX) = 0xe00002c7 + + +AppleM2TVOut, fMapper: 0 + + +H264Driver: power_off_hardware + +H264Driver: power_on_hardware + +H264Driver: power_off_hardware + +AppleVXD375: deviceType = H2A/H2P + +AppleH2CamIn::getDARTInstance + +AppleH2CamIn::power_off_hardware + +AppleH2CamIn::setPowerStateGated: 1 + +AppleH2CamIn::setPowerStateGated, ISPCPU firmware not yet loaded + +AppleH2CamIn::setPowerStateGated: 0 + +AppleM2CLCD, fMapper: 0 + + +AppleM2CLCD (0x834e0000) + + +virtual bool AppleM2ScalerCSCDriver::start(IOService *) Object=0x834e3f00: Provider=0x8339f480: Register Service + + +virtual bool AppleM2ScalerCSCDriver::start(IOService *): this=0x834e3f00, Added Scaler Number = 0 + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +IOReturn AppleJPEGHal::reset_jpeg_block() : SW Reset failed (2) + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +AppleM2ScalerCSCDriver: Added framebuffer device: AppleM2CLCD id: 834e0000 + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +AppleD1755PMU::_readADCGated(3) ADC read bad state: 1 + + +AppleD1755PMU::_readADCGated(3) failed with status=e00002d6 + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +AppleM2ScalerCSCDriver: Added framebuffer device: AppleM2TVOut id: 832cf400 + + +com.apple.launchd 1 com.apple.launchd 1 *** launchd[1] has started up. *** + + + +com.apple.launchd 1 com.apple.launchd 1 *** Verbose boot, will log to /dev/console. *** + + + +AppleD1755PMU::_readADCGated(3) ADC read bad state: 1 + + +AppleD1755PMU::_readADCGated(3) failed with status=e00002d6 + + +GetMasterBlock: Error 16 opening /dev/md0 + + + +/dev/md0 on / (hfs, local, journaled, noatime) + + + +FIPS User Space Integrity Check Success + + + +AppleD1755PMU::_readADCGated(4) ADC read bad state: 1 + + +AppleD1755PMU::_readADCGated(4) failed with status=e00002d6 + + +AppleD1755PMUPowerSource: AppleUSBCableDetect 0 + + +AppleD1755PMUPowerSource: AppleUSBCableType Detached + + +AppleSynopsysOTGDevice::init : Logging Buffer Length = 4K + + +AppleSynopsysOTGDevice::start : buffer-options 0x00000400 + + +AppleSynopsysOTGDevice::start : registers at 0xccbaf000, 0x86100000 physical + +AppleSynopsysOTGDevice::start : object is 0xc0581600, registers at 0xccbaf000, 0x86100000 physical + + +AppleSynopsysOTGDevice::findMaxEndpoints: in EPs: 7, out EPs: 7, max_endpoint: 8, num_endpoints: 14 + + +IOAccessoryPortSerial::start: iap: could not set up serial port: device is offline + + +AppleD1755PMU::_readADCGated(3) ADC read bad state: 1 + + +AppleD1755PMU::_readADCGated(3) failed with status=e00002d6 + + +FIPS USER Space POST Success! + + + +AppleIOPSDIOEndpoint::init(): Failed to get IOP after 10 sec. + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +virtual void AppleM2CLCD::do_set_debug_flags(uint32_t, uint32_t), ******************** DITHER STATE IS SET TO 0 ********************** + + +launchd[18] Builtin profile: ptpd (sandbox) + + +launchd[41] Builtin profile: iapd (sandbox) + + +launchd[56] Builtin profile: BTServer (sandbox) + + +AMFI: Invalid signature but permitting execution + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +launchd[66] Builtin profile: apsd (sandbox) + + +launchd[67] Builtin profile: mobileassetd (sandbox) + + +com.apple.launchd 1 com.apple.SpringBoard 61 (com.apple.SpringBoard) Stopping job... + + + +ios6-emu: initialisation du trousseau systC(me + + + +booted from secure root: give device keybag access to everyone + + +Will try to use UIDPlus to derive key + + +AppleKeyStore:cp_key_store_action(1) + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: trousseau RAM create=0 handle=1 + + + +AppleKeyStore:cp_key_store_action(1) + + +ios6-emu: trousseau RAM set_system=0 + + + +ios6-emu: preferences de session synchronisees=1 + + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: environnement graphique pid=12 /usr/libexec/backboardd + + + +ios6-emu: CA_ENABLE_OGL=0 + + + +ios6-emu: CA_PRINT_PERF=1 + + + +ios6-emu: CA_NO_ACCEL=1 + + + +ios6-emu: environnement graphique pid=69 /System/Library/CoreServices/SpringBoard.app/SpringBoard + + + +ios6-emu: CA_ENABLE_OGL=0 + + + +ios6-emu: CA_PRINT_PERF=1 + + + +ios6-emu: CA_NO_ACCEL=1 + + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +launchd[76] Builtin profile: geod (sandbox) + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: ecran status=0 verrouille=1 code=0 + + + +launchd[83] Builtin profile: MobileMail (sandbox) + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: geste de deverrouillage transmis a GraphicsServices + + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +launchd[90] Builtin profile: syncdefaultsd (sandbox) + + +AppleSynopsysOTGDevice - Configuration: PTP + + +AppleSynopsysOTGDevice Interface: PTP + + +AppleSynopsysOTGDevice - Configuration: iPod USB Interface + + +AppleSynopsysOTGDevice Interface: USBAudioControl + + +AppleSynopsysOTGDevice Interface: USBAudioStreaming + + +AppleSynopsysOTGDevice Interface: IapOverUsbHid + + +AppleSynopsysOTGDevice - Configuration: PTP + Apple Mobile Device + + +AppleSynopsysOTGDevice Interface: PTP + + +AppleSynopsysOTGDevice Interface: AppleUSBMux + + +AppleSynopsysOTGDevice - Configuration: PTP + Apple Mobile Device + Apple USB Ethernet + + +AppleSynopsysOTGDevice Interface: PTP + + +AppleSynopsysOTGDevice Interface: AppleUSBMux + + +AppleSynopsysOTGDevice Interface: AppleUSBEthernet + + +AppleSynopsysOTGDevice::gated_registerFunction Register function USBAudioControl + + +AppleSynopsysOTGDevice::gated_registerFunction Register function USBAudioStreaming + + +IOAccessoryPortUSB::start + + +AppleSynopsysOTGDevice::gated_registerFunction Register function IapOverUsbHid + + +AppleSynopsysOTGDevice::gated_registerFunction Register function AppleUSBEthernet + + +virtual bool AppleUSBDeviceMux::start(IOService *) build: Feb 13 2013 22:02:59 + + +init_waste + + +AppleSynopsysOTGDevice::gated_registerFunction Register function AppleUSBMux + + +AppleUSBEthernetDevice: Ethernet address 02:00:00:00:00:00 + + +AppleSynopsysOTGDevice::gated_registerFunction Register function PTP + + +AppleSynopsysOTGDevice::startUSBStack Starting usb stack + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: ecran status=0 verrouille=0 code=0 + + + +AppleIOPSDIOEndpoint::init(): Failed to get IOP after 130 sec. + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: ecran status=0 verrouille=0 code=0 + + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + diff --git a/ios6/results/20261003-springboard/components-sha256.json b/ios6/results/20261003-springboard/components-sha256.json new file mode 100644 index 0000000000..aed492b121 --- /dev/null +++ b/ios6/results/20261003-springboard/components-sha256.json @@ -0,0 +1,12 @@ +{ + "src/ios6_n88.c": "62181b482dbf5ae0361c7646a307c59e407938f8e7825f250301f8608ca2c0c4", + "src/keybag_init.c": "136c2393f637eef473e57294a44d48ce8b48242554738eba7e748b1ab22064ec", + "src/session_input.h": "00a66abb65d195cba0604383103d85c2e1bf22e7dd24d2b4b45329817ad58039", + "tools/prepare_boot.py": "7e85bee06a3fcd3c98fb7ad4c88cdf1a7af1c4d1c477e6ab6434f2274cdb7371", + "tools/prepare_rootfs.py": "2b5e3bd75cdb8a5fc297aede2396608162f3e36faf70056806e0b29510cf960f", + "tools/probe.py": "4755c5aee24cdb6f78aac661e7578188fc5a4125d379899e09b7171265d49ad3", + "build/qemu-system-arm": "1b00b4c610ec94e3402b3cc06fcee20b23101b923ea33b5469c71f8c76fe85f3", + "build/ios6-keybag-init": "f24a67328c4b4be6afd0fcbd507c63bd421aeb0caf616491de622289ddbbd9fc", + "firmware/kernel-rootfs.bin": "56fa914337772f16a8c6155f958e5f5551852820988b9d6d910236a8936084f1", + "firmware/boot-display-rootfs.bin": "117530bb9feddd2c39892830ee0a73942a52eb0980b8956c892dbd42dc30bfca" +} diff --git a/ios6/results/20261003-springboard/fresh-command.json b/ios6/results/20261003-springboard/fresh-command.json new file mode 100644 index 0000000000..03d39236f1 --- /dev/null +++ b/ios6/results/20261003-springboard/fresh-command.json @@ -0,0 +1,24 @@ +[ + "/home/dev/ios6-emu/build/qemu-system-arm", + "-M", + "ios6-n88,rootfs=on", + "-kernel", + "/home/dev/ios6-emu/firmware/kernel-rootfs.bin", + "-dtb", + "/home/dev/ios6-emu/firmware/boot-display-rootfs.bin", + "-initrd", + "/home/dev/ios6-emu/firmware/rootfs-vm.hfs", + "-display", + "none", + "-serial", + "file:/home/dev/ios6-emu/evidence/probe-20261003T125116-664879Z/serial.log", + "-monitor", + "none", + "-qmp", + "unix:/home/dev/ios6-emu/evidence/probe-20261003T125116-664879Z/qmp,server=on,wait=off", + "-d", + "unimp,guest_errors", + "-D", + "/home/dev/ios6-emu/evidence/probe-20261003T125116-664879Z/qemu.log", + "-no-reboot" +] diff --git a/ios6/results/20261003-springboard/fresh-result.json b/ios6/results/20261003-springboard/fresh-result.json new file mode 100644 index 0000000000..009ef2c59a --- /dev/null +++ b/ios6/results/20261003-springboard/fresh-result.json @@ -0,0 +1,22 @@ +{ + "run": "/home/dev/ios6-emu/evidence/probe-20261003T125116-664879Z", + "seconds": 300.0, + "status": { + "status": "paused", + "singlestep": false, + "running": false + }, + "registers": "\r\nCPU#0\r\nR00=0000000a R01=00000001 R02=800895dc R03=802dc000\r\nR04=00000001 R05=acf331f0 R06=0000000a R07=c0463f08\r\nR08=802dc000 R09=8008958c R10=c00d9200 R11=00000003\r\nR12=00000000 R13=802d9fb0 R14=800895ec R15=80089590\r\nPSR=a00001d1 N-C- A fiq32\r\ns00=00000000 s01=00000000 d00=0000000000000000\r\ns02=00000000 s03=00000000 d01=0000000000000000\r\ns04=00000000 s05=00000000 d02=0000000000000000\r\ns06=00000000 s07=00000000 d03=0000000000000000\r\ns08=20313932 s09=32337830 d04=3233783020313932\r\ns10=66333363 s11=30203130 d05=3020313066333363\r\ns12=00000000 s13=bf97fd80 d06=bf97fd8000000000\r\ns14=aa000000 s15=c1cd27e3 d07=c1cd27e3aa000000\r\ns16=00000000 s17=3ff00000 d08=3ff0000000000000\r\ns18=00000000 s19=00000000 d09=0000000000000000\r\ns20=00000000 s21=00000000 d10=0000000000000000\r\ns22=00000000 s23=00000000 d11=0000000000000000\r\ns24=00000000 s25=00000000 d12=0000000000000000\r\ns26=00000000 s27=00000000 d13=0000000000000000\r\ns28=00000000 s29=00000000 d14=0000000000000000\r\ns30=00000000 s31=00000000 d15=0000000000000000\r\ns32=00000000 s33=00000000 d16=0000000000000000\r\ns34=00000000 s35=00000000 d17=0000000000000000\r\ns36=a0b5ed8d s37=3eb0c6f7 d18=3eb0c6f7a0b5ed8d\r\ns38=40000000 s39=c1cd27e4 d19=c1cd27e440000000\r\ns40=00000000 s41=00000000 d20=0000000000000000\r\ns42=00000000 s43=40740000 d21=4074000000000000\r\ns44=00000000 s45=40640000 d22=4064000000000000\r\ns46=00000000 s47=00000000 d23=0000000000000000\r\ns48=00000000 s49=00000000 d24=0000000000000000\r\ns50=00000000 s51=00000000 d25=0000000000000000\r\ns52=00000000 s53=00000000 d26=0000000000000000\r\ns54=ffffffff s55=ffffffff d27=ffffffffffffffff\r\ns56=ffffffff s57=ffffffff d28=ffffffffffffffff\r\ns58=ffffffff s59=ffffffff d29=ffffffffffffffff\r\ns60=fcfcffff s61=30300b0b d30=30300b0bfcfcffff\r\ns62=fcfcffff s63=30300b0b d31=30300b0bfcfcffff\r\nFPSCR: 8b000095\r\n", + "instructions": "0xffffffff80089590: Asm output not supported on this arch\r\n", + "stack": "802d9fb0: 0x0000000a 0x00000001 0x800895dc 0x802dc000\r\n802d9fc0: 0x00000001 0xacf331f0 0x0000000a 0xc0463f08\r\n802d9fd0: 0x802dc000 0x8008958c 0xc00d9200 0x00000003\r\n802d9fe0: 0x00000000 0x00000000 0x00000000 0x800895e8\r\n802d9ff0: 0xa0000093 0x00000000 0x00000000 0x00000000\r\n802da000: 0x00000000 0x00000000 0x00000000 0x00000000\r\n802da010: 0x00000000 0x00000000 0x00000000 0x00000000\r\n802da020: 0x00000000 0x00000000 0x00000000 0x00000000\r\n", + "returncode": 0, + "guest_status": "partial-boot", + "panic": null, + "milestones": { + "serial_console": true, + "crypto_self_tests": true, + "platform_driver": true, + "interrupt_controller_driver": true, + "userspace_confirmed": true + } +} diff --git a/ios6/results/20261003-springboard/fresh-serial.log b/ios6/results/20261003-springboard/fresh-serial.log new file mode 100644 index 0000000000..ce8e8647ae --- /dev/null +++ b/ios6/results/20261003-springboard/fresh-serial.log @@ -0,0 +1,998 @@ +iBoot version: ios6-emu direct-kernel prototype + + +corecrypto kext started! + + +Running kernel space in FIPS MODE + + +Plist hmac value is 9fd77803e256b61c91d83182dfb9aee94e57b18702a272b6a206fb074ca8e541 + + +Computed hmac value is 9fd77803e256b61c91d83182dfb9aee94e57b18702a272b6a206fb074ca8e541 + + +corecrypto.kext FIPS integrity POST test passed! + + +corecrypto.kext FIPS AES CBC POST test passed! + + +corecrypto.kext FIPS TDES CBC POST test passed! + + +corecrypto.kext FIPS SHA POST test passed! + + +corecrypto.kext FIPS HMAC POST test passed! + + +corecrypto.kext FIPS ECDSA POST test passed! + + +corecrypto.kext FIPS DRBG POST test passed! + + +corecrypto.kext FIPS POST passed! + + +kern_return_t _initializeAppleMobileFileIntegrity(): signature enforcement disabled by boot-arg + + +AppleS5L8920XIO::start: chip-revision: A0 + +AppleARMBacklight::start: Backlight calibration data 0 + + +Couldn't alloc class "AppleRawIOAddressSpace" + + +AppleARMPL192VIC::start: _vicBaseAddress = 0xcc38e000 + +AppleS5L8920XGPIOIC::start: gpioicBaseAddress: 0xc4df2000 + +AppleARMPerformanceController::traceBufferEnable: Trace is enabled + +AppleS5L8920XPerformanceController::start: _pcBaseAddress: 0xc4e05000 _pcMCUBaseAddress: 0xc4df6000 + +AppleARMPerformanceController configured with 1 Performance Domains + + Performance Domain 0 : pcVoltageStateCount: 3 pcPerformanceStateCount: 4 + + Voltage State 0 : .vsPerformanceLimit: /2 .vsDeviceConstraint: 1ff8ffef ffff8fff + + Voltage State 1 : .vsPerformanceLimit: /2 .vsDeviceConstraint: 1ffeffff ffff8fff + + Voltage State 2 : .vsPerformanceLimit: /1 .vsDeviceConstraint: 1fffffff ffff8fff + + Performance State 0 : .psPerformanceDivider: /1 .psVoltageRequired: 2 .psDeviceConstraint: 1fffffff ffff8fff + + Performance State 1 : .psPerformanceDivider: /2 .psVoltageRequired: 0 .psDeviceConstraint: 1ffeb5ff ffe78fff + + Performance State 2 : .psPerformanceDivider: /4 .psVoltageRequired: 0 .psDeviceConstraint: 1ffe24ff ffe78fff + + Performance State 3 : .psPerformanceDivider: /8 .psVoltageRequired: 0 .psDeviceConstraint: 1ffe20df ffe78fff + +AppleARMPerformanceController: Dynamic Performance State Management Enabled on Performance Domain 0 + + +AppleARMPerformanceController: Dynamic Voltage Control Enabled on Performance Domain 0 with 10000us activity timeout and 100us grace period + + +IOSDIOController::init(): IOSDIOFamily-34 Feb 13 2013 21:35:31 @ 0x833dc300 + + +AppleIOPSDIO::start(): AppleIOPSDIO-12 Feb 13 2013 22:17:01 + + +AppleBaseband::start(0x833f9e18): baseband + ++ AppleM2ScalerCSCDriver[0x834eaf00]::start(provider 0x8339f380) + + +AppleH2CamIn::start + +AppleH2CamIn::start, fSensorExtClkFunction = 0x83372F60 + +AppleH2CamIn::start, fSensorShutdownFunction = 0x83372EA0 + +AppleH2CamIn::start, fISPResetFunction = 0x8334A000 + ++ H264EncDriver: start (provider 0x8339f200) + ++ H264EncDriver: compiled on Feb 13 2013 22:32:00 + +AppleVXD375 - start (provider 0x8339f180) + +AppleVXD375 - compiled on Feb 13 2013 22:32:12 + +AppleSamsungSerial: Identified Serial Port on ARM Device=uart0 at 0x82500000(0xcae3e000) + + +AppleSamsungSerial: Identified Serial Port on ARM Device=uart4 at 0x82900000(0xcae46000) + + +AppleARMIICController::start buffer=0x83584d80 entries=8 + +AppleS5L8920XI2CController::start: i2c0 _i2cBaseAddress: 0xcae4e000 + + +AppleD1755PMU::start: pmu _pmuIICNub: 0x83589d90 built 21:35:40 Feb 13 2013 + +AppleD1755PMU::start: reading DOWN converter voltages + +AppleD1755PMU::start: DOWN0: 725mV + +AppleD1755PMU::start: DOWN1: 725mV + +AppleD1755PMU::start: DOWN2: 725mV + +AppleD1755PMU::start: DOWN3: 725mV + +AppleD1755PMU::start: set VBUCK1_PRE1 to 0 + +AppleD1755PMU::start: set VBUCK1_PRE2 to 0 + +AppleD1755PMU::start: set VBUCK1_PRE3 to 0 + +AppleD1755PMU::start: using SWI for CPU BUCK changes + +BSD root: md0, major 3, minor 0 + + +com.apple.AppleFSCompressionTypeZlib kmod start + + +com.apple.AppleFSCompressionTypeZlib load succeeded + + +virtual bool AppleMobileFileIntegrity::start(IOService *): built Feb 11 2014 20:55:23 + + +IOSurfaceRoot::installMemoryRegions() + +000001.603790 AppleBasebandN88::loadDebugArguments: Loaded debug arguments 0x00000001 + + +AppleARMIICController::start buffer=0x83677080 entries=8 + +AppleS5L8920XI2CController::start: i2c2 _i2cBaseAddress: 0xcae56000 + + +AppleS5L8920XPWM::start: _pwmBaseAddress: 0xcae5d000 + +L2TP domain init + + +L2TP domain init complete + + +PPTP domain init + + +AppleSamsungLP65USBPhy::start: hsic disabled + + +AppleM2CLCD, fMapper: 0 + + +AppleM2CLCD (0x834c9000) + + +AppleSamsungMIPIDSIController::start: _dsimBaseAddress = 0xc4dfd000 + +ApplePinotLCD: _lcdPanelID: 0x00e50486 _lcdPanelClass: 1 + + +AppleSamsungSWI::start: _swiBaseAddress: 0x00000000c4dfe000 + +AppleSamsungSWI::start: _swiBaseAddress: _swiWorstCaseIntraVBUCK=0 + +virtual bool AppleM2ScalerCSCHal::init(uint32_t, IOService *, IOService *, IOWorkLoop *): scalerNumber=0, IOKitProvider=0x8339f380, powerProvider=0x8339f380 + + +bool AppleM2ScalerCSCHal::mapRegisterMemory(IOService *): Scaler 0 RegisterMemoryMap[0x836b0738]: base=0xc4e0e000 physical address = 0x85500000 length = 4096 + + +AppleM2TVOut, fMapper: 0 + + +H264Driver: power_off_hardware + +H264Driver: power_on_hardware + +AppleVXD375: deviceType = H2A/H2P + +AppleH2CamIn::getDARTInstance + +AppleH2CamIn::power_off_hardware + +AppleH2CamIn::setPowerStateGated: 1 + +AppleH2CamIn::setPowerStateGated, ISPCPU firmware not yet loaded + +AppleH2CamIn::setPowerStateGated: 0 + +AppleSamsungLP65USBPhy::start : registers at virtual: 0xcae75000, physical: 0x86000000 + +AppleARMBacklight::start: _minDACLevel=56 _maxDACLevel=245 + + +IOReturn AppleJPEGHal::reset_jpeg_block() : SW Reset failed (2) + +H264Driver: power_off_hardware + +Scaler 0 enableDeviceClock(true,HALF_CLOCK_INDEX) = 0x0 + + +Scaler 0 enableDeviceClock(true,FULL_CLOCK_INDEX) = 0xe00002c7 + + +Scaler 0 enableDevicePower(true,0,HALF_CLOCK_INDEX) = 0xe00002c7 + + +Scaler HW version at start is 0x0 + + +void AppleM2ScalerCSCHal::setScalerPropertiesAndExtents() WARNING: Failed to find properties and extents data for SOC 0x0, Scaler HW Version 0x0 + + +Scaler 0 enableDeviceClock(false,FULL_CLOCK_INDEX) = 0xe00002c7 + + +AppleM2ScalerCSCDriver: Added framebuffer device: AppleM2CLCD id: 834c9000 + + +virtual bool AppleM2ScalerCSCDriver::start(IOService *) Object=0x834eaf00: Provider=0x8339f380: Register Service + + +virtual bool AppleM2ScalerCSCDriver::start(IOService *): this=0x834eaf00, Added Scaler Number = 0 + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +AppleD1755PMU::_readADCGated(3) ADC read bad state: 1 + + +AppleD1755PMU::_readADCGated(3) failed with status=e00002d6 + + +IOAccessoryPortSerial::start: iap: could not set up serial port: device is offline + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +AppleM2ScalerCSCDriver: Added framebuffer device: AppleM2TVOut id: 832cf400 + + +com.apple.launchd 1 com.apple.launchd 1 *** launchd[1] has started up. *** + + + +com.apple.launchd 1 com.apple.launchd 1 *** Verbose boot, will log to /dev/console. *** + + + +AppleD1755PMU::_readADCGated(3) ADC read bad state: 1 + + +AppleD1755PMU::_readADCGated(3) failed with status=e00002d6 + + +GetMasterBlock: Error 16 opening /dev/md0 + + + +/dev/md0 on / (hfs, local, journaled, noatime) + + + +FIPS User Space Integrity Check Success + + + +AppleD1755PMU::_readADCGated(4) ADC read bad state: 1 + + +AppleD1755PMU::_readADCGated(4) failed with status=e00002d6 + + +AppleD1755PMUPowerSource: AppleUSBCableDetect 0 + + +AppleD1755PMUPowerSource: AppleUSBCableType Detached + + +AppleSynopsysOTGDevice::init : Logging Buffer Length = 4K + + +AppleSynopsysOTGDevice::start : buffer-options 0x00000400 + + +AppleSynopsysOTGDevice::start : registers at 0xccc7d000, 0x86100000 physical + +AppleSynopsysOTGDevice::start : object is 0xc0626600, registers at 0xccc7d000, 0x86100000 physical + + +AppleSynopsysOTGDevice::findMaxEndpoints: in EPs: 7, out EPs: 7, max_endpoint: 8, num_endpoints: 14 + + +AppleD1755PMU::_readADCGated(3) ADC read bad state: 1 + + +AppleD1755PMU::_readADCGated(3) failed with status=e00002d6 + + +FIPS USER Space POST Success! + + + +AppleIOPSDIOEndpoint::init(): Failed to get IOP after 10 sec. + + +TVOUT SHUT DOWN PROBLEM: !(enable.reg.clkgating_rdy) + + +TVOUT SHUT DOWN PROBLEM: !(mix_ctrl.reg.reg_mixer_ready_clk_down) + + +TVOUT SHUT DOWN PROBLEM: !(readSDOReg(SDO_CLKCON) & 0x2) + + +virtual void AppleM2CLCD::do_set_debug_flags(uint32_t, uint32_t), ******************** DITHER STATE IS SET TO 0 ********************** + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +launchd[19] Builtin profile: ptpd (sandbox) + + +launchd[42] Builtin profile: iapd (sandbox) + + +launchd[57] Builtin profile: BTServer (sandbox) + + +AMFI: Invalid signature but permitting execution + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +launchd[64] Builtin profile: apsd (sandbox) + + +ios6-emu: initialisation du trousseau systC(me + + + +booted from secure root: give device keybag access to everyone + + +Will try to use UIDPlus to derive key + + +launchd[66] Builtin profile: mobileassetd (sandbox) + + +AppleKeyStore:cp_key_store_action(1) + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: trousseau RAM create=0 handle=1 + + + +AppleKeyStore:cp_key_store_action(1) + + +ios6-emu: trousseau RAM set_system=0 + + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: preferences de session synchronisees=1 + + + +ios6-emu: environnement graphique pid=12 /usr/libexec/backboardd + + + +ios6-emu: CA_ENABLE_OGL=0 + + + +ios6-emu: CA_PRINT_PERF=1 + + + +ios6-emu: CA_NO_ACCEL=1 + + + +ios6-emu: environnement graphique pid=60 /System/Library/CoreServices/SpringBoard.app/SpringBoard + + + +ios6-emu: CA_ENABLE_OGL=0 + + + +ios6-emu: CA_PRINT_PERF=1 + + + +ios6-emu: CA_NO_ACCEL=1 + + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +AppleIOPSDIOEndpoint::init(): Failed to get IOP after 130 sec. + + +AppleSynopsysOTGDevice - Configuration: PTP + + +AppleSynopsysOTGDevice Interface: PTP + + +AppleSynopsysOTGDevice - Configuration: iPod USB Interface + + +AppleSynopsysOTGDevice Interface: USBAudioControl + + +AppleSynopsysOTGDevice Interface: USBAudioStreaming + + +AppleSynopsysOTGDevice Interface: IapOverUsbHid + + +AppleSynopsysOTGDevice - Configuration: PTP + Apple Mobile Device + + +AppleSynopsysOTGDevice Interface: PTP + + +AppleSynopsysOTGDevice Interface: AppleUSBMux + + +AppleSynopsysOTGDevice - Configuration: PTP + Apple Mobile Device + Apple USB Ethernet + + +AppleSynopsysOTGDevice Interface: PTP + + +AppleSynopsysOTGDevice Interface: AppleUSBMux + + +AppleSynopsysOTGDevice Interface: AppleUSBEthernet + + +AppleSynopsysOTGDevice::gated_registerFunction Register function USBAudioControl + + +AppleSynopsysOTGDevice::gated_registerFunction Register function USBAudioStreaming + + +IOAccessoryPortUSB::start + + +virtual bool AppleUSBDeviceMux::start(IOService *) build: Feb 13 2013 22:02:59 + + +AppleSynopsysOTGDevice::gated_registerFunction Register function IapOverUsbHid + + +AppleSynopsysOTGDevice::gated_registerFunction Register function AppleUSBEthernet + + +init_waste + + +AppleSynopsysOTGDevice::gated_registerFunction Register function AppleUSBMux + + +AppleUSBEthernetDevice: Ethernet address 02:00:00:00:00:00 + + +AppleSynopsysOTGDevice::gated_registerFunction Register function PTP + + +AppleSynopsysOTGDevice::startUSBStack Starting usb stack + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +launchd[87] Builtin profile: geod (sandbox) + + +ios6-emu: ecran status=268435459 verrouille=0 code=1 + + + +booted from secure root: give device keybag access to everyone + + +booted from secure root: give device keybag access to everyone + + +launchd[95] Builtin profile: MobileMail (sandbox) + + +ios6-emu: ecran status=0 verrouille=1 code=0 + + + +booted from secure root: give device keybag access to everyone + + +void AppleM2CLCD::disable_backlight(), Where is my backlight? + + +ApplePinotLCD::_lcdEnable: enable: 0 + +booted from secure root: give device keybag access to everyone + + +ios6-emu: geste de deverrouillage transmis a GraphicsServices + + + +launchd[100] Builtin profile: syncdefaultsd (sandbox) + + +ios6-emu: ecran status=0 verrouille=1 code=0 + + + +ApplePinotLCD::_lcdEnable: enable: 1 + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +booted from secure root: give device keybag access to everyone + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +Dec 31 16:03:34 iPhone SpringBoard[60] : too many attempts to unregister from camera preview well changes, ignoring + + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +ios6-emu: geste de deverrouillage transmis a GraphicsServices + + + +booted from secure root: give device keybag access to everyone + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +booted from secure root: give device keybag access to everyone + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +ios6-emu: ecran status=0 verrouille=0 code=0 + + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +void AppleM2CLCD::safe_enable_backlight(), Where is my backlight? + + +ios6-emu: ecran status=0 verrouille=0 code=0 + + + +AppleIOPSDIOEndpoint::init(): Failed to get IOP after 250 sec. + + +ios6-emu: ecran status=0 verrouille=0 code=0 + + + +ios6-emu: ecran status=0 verrouille=0 code=0 + + + +ios6-emu: ecran status=0 verrouille=0 code=0 + + + diff --git a/ios6/results/20261003-springboard/springboard.png b/ios6/results/20261003-springboard/springboard.png new file mode 100644 index 0000000000000000000000000000000000000000..3b20bc7156c97c5064eef94dc24316fb10406610 GIT binary patch literal 216423 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�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 +#include +#include +#include +#include +#include +#include +#include +#include "session_input.h" + +static void configure_session(void) +{ + CFStringRef user = CFSTR("mobile"), host = kCFPreferencesAnyHost; + CFStringRef buddy = CFSTR("com.apple.purplebuddy"); + CFStringRef keys[] = { CFSTR("ForceNoBuddy"), CFSTR("SetupDone"), + CFSTR("SetupFinishedAllSteps") }; + for (unsigned i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) { + CFPreferencesSetValue(keys[i], kCFBooleanTrue, buddy, user, host); + } + int version = 3; + CFNumberRef number = CFNumberCreate(NULL, kCFNumberIntType, &version); + CFPreferencesSetValue(CFSTR("SetupVersion"), number, buddy, user, host); + CFRelease(number); + printf("ios6-emu: preferences de session synchronisees=%d\n", + CFPreferencesSynchronize(buddy, user, host)); + chown("/var/mobile/Library/Preferences/com.apple.purplebuddy.plist", 501, 501); + chown("/var/mobile/Library/Preferences/com.apple.purplebuddy.notbackedup.plist", 501, 501); + double seconds = 3600; + number = CFNumberCreate(NULL, kCFNumberDoubleType, &seconds); + CFPreferencesSetValue(CFSTR("SBAutoDimTime"), number, CFSTR("com.apple.springboard"), user, host); + CFRelease(number); + CFPreferencesSynchronize(CFSTR("com.apple.springboard"), user, host); + chown("/var/mobile/Library/Preferences/com.apple.springboard.plist", 501, 501); +} + +static void diagnose_graphics_environment(void) +{ + static unsigned char seen[160]; + static char buffer[65536]; + for (int pid = 2; pid < 160; pid++) { + if (seen[pid]) continue; + int mib[] = { CTL_KERN, KERN_PROCARGS2, pid }; + size_t size = sizeof(buffer) - 1; + if (sysctl(mib, 3, buffer, &size, NULL, 0) || size <= sizeof(int)) continue; + buffer[size] = 0; + const char *path = buffer + sizeof(int); + if (!strstr(path, "/backboardd") && !strstr(path, "/SpringBoard")) continue; + seen[pid] = 1; + printf("ios6-emu: environnement graphique pid=%d %s\n", pid, path); + for (size_t offset = sizeof(int); offset < size;) { + const char *entry = buffer + offset; + if (!strncmp(entry, "CA_", 3)) printf("ios6-emu: %s\n", entry); + offset += strlen(entry) + 1; + } + } +} + +/* Déverrouillage de la session de démonstration vierge, sans code configuré. + * Utilise le serveur SpringBoard, sans modifier son exécutable. */ +static void show_home_screen(void) +{ + static void *library; + if (!library) library = dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_NOW); + if (!library) return; + unsigned (*server)(void) = dlsym(library, "SBSSpringBoardServerPort"); + int (*status)(unsigned, unsigned char *, unsigned char *) = dlsym(library, "SBGetScreenLockStatus"); + int (*dim)(unsigned, unsigned char) = dlsym(library, "SBDimScreen"); + if (!server || !status || !dim) return; + unsigned port = server(); + unsigned char locked = 0, passcode = 1; + int result = status(port, &locked, &passcode); + printf("ios6-emu: ecran status=%d verrouille=%u code=%u\n", result, locked, passcode); + if (result || passcode) return; + dim(port, 0); + if (locked) { + sleep(1); + send_unlock_swipe(); + } +} + +int main(void) +{ + freopen("/dev/console", "w", stdout); + freopen("/dev/console", "w", stderr); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + puts("ios6-emu: initialisation du trousseau système"); + void *library = dlopen("/System/Library/PrivateFrameworks/MobileKeyBag.framework/MobileKeyBag", RTLD_NOW); + if (!library) { + fprintf(stderr, "MobileKeyBag: %s\n", dlerror()); + return 1; + } + int (*create_system)(int, const char *) = dlsym(library, "MKBKeyBagCreateSystemUnwrapped"); + if (!create_system) { + fprintf(stderr, "MKBKeyBagCreateSystemUnwrapped absent\n"); + return 1; + } + /* Fonctions internes vérifiées du cache 10B500 : création puis adoption + * du trousseau de session. Aucun fichier chiffré sans clé matérielle. */ + uintptr_t slide = (uintptr_t)create_system - 0x354a40bd; + int (*create_bag)(void *, int, int *) = (void *)(slide + 0x354a75b1); + int (*set_system)(int) = (void *)(slide + 0x354a7371); + if (memcmp((void *)(slide + 0x354a75b0), "\xf0\xb5\x03\xaf\x8d\xb0\x6c\x46", 8) || + memcmp((void *)(slide + 0x354a7370), "\x90\xb5\x01\xaf\x89\xb0\x6c\x46", 8)) { + fputs("ios6-emu: MobileKeyBag incompatible avec 10B500\n", stderr); + return 1; + } + int handle = -1; + int result = create_bag(NULL, (int)0x80000000, &handle); + printf("ios6-emu: trousseau RAM create=%d handle=%d\n", result, handle); + if (result) return 2; + result = set_system(handle); + printf("ios6-emu: trousseau RAM set_system=%d\n", result); + if (result) return 3; + configure_session(); + /* Inclut le premier démarrage et ses migrations ; maintient la démo + * visible pendant six minutes, puis cesse toute entrée automatique. */ + for (int iteration = 0; iteration < 24; iteration++) { + sleep(15); + diagnose_graphics_environment(); + show_home_screen(); + sync(); + } + for (;;) pause(); +} diff --git a/ios6/src/session_input.h b/ios6/src/session_input.h new file mode 100644 index 0000000000..098377808e --- /dev/null +++ b/ios6/src/session_input.h @@ -0,0 +1,46 @@ +/* Geste de démonstration via GraphicsServices, ABI ARM32 iOS 6. + * Structure recoupée avec GSEvent.h et l'implémentation iOS 6 de hid-support : + * https://github.com/mringwal/hid-support/tree/master/hidspringboard + * Aucun pixel n'est dessiné par cet outil. + */ +static int send_touch_gesture(float x1, float y1, float x2, float y2, unsigned duration_ms, const char *bundle) +{ + void *gs = dlopen("/System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices", RTLD_NOW); + if (!gs) return -1; + void (*send)(const void *) = dlsym(gs, "GSSendSystemEvent"); + void (*send_port)(const void *, unsigned) = dlsym(gs, "GSSendEvent"); + unsigned (*copy_port)(const char *) = dlsym(gs, "GSCopyPurpleNamedPort"); + uint64_t (*timestamp)(void) = dlsym(gs, "GSCurrentEventTimestamp"); + if (!send || !timestamp) { dlclose(gs); return -1; } + unsigned port = bundle && copy_port ? copy_port(bundle) : 0; + if (bundle && (!port || !send_port)) { dlclose(gs); return -2; } + /* GSEventRecord: 52 octets ; GSHandInfo: 36 ; GSPathInfo: 24. */ + for (int step = 0; step <= 31; step++) { + uint32_t packet[28] = {0}; + unsigned char *bytes = (void *)packet; + int down = step < 31; + float fraction = (step < 30 ? step : 30) / 30.0f; + float point[2] = {x1 + (x2-x1)*fraction, y1 + (y2-y1)*fraction}; + uint64_t time = timestamp(); + packet[0] = 3001; + memcpy(bytes + 8, point, sizeof(point)); + memcpy(bytes + 16, point, sizeof(point)); + memcpy(bytes + 28, &time, sizeof(time)); + packet[12] = 60; + packet[13] = step == 0 ? 1 : down ? 2 : 6; + bytes[86] = 1; /* path count, iOS 5+ */ + bytes[88] = 1; /* path index */ + bytes[89] = 2; /* path identity */ + bytes[90] = down ? 3 : 0; /* range + touch */ + memcpy(bytes + 100, point, sizeof(point)); + if (port) send_port(packet, port); else send(packet); + usleep(duration_ms * 1000 / 32); + } + dlclose(gs); + return 0; +} + +static void send_unlock_swipe(void) +{ + send_touch_gesture(50, 433, 245, 433, 800, NULL); +} diff --git a/ios6/tools/build.sh b/ios6/tools/build.sh new file mode 100755 index 0000000000..d37afdbf22 --- /dev/null +++ b/ios6/tools/build.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Compile le QEMU de ce dépôt (modèle ios6-n88 inclus) et le relais netd +# dans ios6/build, sans toucher au build/ de l'émulateur iPhone OS 2. +set -euo pipefail +sim_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +qemu_root="$(dirname -- "$sim_root")" +cd "$sim_root" +for program in cc ninja pkg-config git python3; do + command -v "$program" >/dev/null || { echo "Dépendance absente : $program" >&2; exit 1; } +done +mkdir -p evidence build +if [[ ! -d .venv ]]; then + python3 -m venv .venv +fi +.venv/bin/python -m pip install -r requirements.txt > evidence/python-deps.log 2>&1 +cd build +"$qemu_root/configure" --python="$sim_root/.venv/bin/python" \ + --target-list=arm-softmmu --disable-docs --disable-werror \ + --disable-gtk --disable-sdl --disable-slirp --disable-capstone \ + --disable-tools --disable-guest-agent --disable-debug-info \ + --extra-ldflags="$(pkg-config --libs-only-L openssl 2>/dev/null) -lcrypto" > ../evidence/configure.log 2>&1 +ninja -j"${IOS6_BUILD_JOBS:-4}" qemu-system-arm > ../evidence/build.log 2>&1 +cd .. +bash tools/build_netd.sh +echo 'Compilation du prototype iOS 6 terminée.' diff --git a/ios6/tools/build_agent.sh b/ios6/tools/build_agent.sh new file mode 100755 index 0000000000..60724cd9a5 --- /dev/null +++ b/ios6/tools/build_agent.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +sim_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) +cd "$sim_root" +mkdir -p build +/opt/cctools/bin/arm-apple-darwin9-gcc -isysroot /opt/theos/sdks/iPhoneOS2.2.1.sdk -miphoneos-version-min=2.2.1 -std=gnu99 -O2 agent/ios6-agent.c -framework CoreFoundation -framework SystemConfiguration -o build/ios6-agent +/opt/cctools/bin/ldid -Sagent/entitlements.plist build/ios6-agent +bash tools/build_netd.sh diff --git a/ios6/tools/build_app.py b/ios6/tools/build_app.py new file mode 100644 index 0000000000..9d45ae931d --- /dev/null +++ b/ios6/tools/build_app.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +"""Compile un projet Objective-C simple (.m) en bundle ARM pour la VM iOS 6.""" +import argparse,os,pathlib,plistlib,subprocess +ROOT=pathlib.Path(__file__).resolve().parents[1] +def main(): + p=argparse.ArgumentParser(description=__doc__);p.add_argument('project',type=pathlib.Path,nargs='?',default=ROOT/'apps/SimulatorLab');p.add_argument('--bundle-id');p.add_argument('--sdk',default=os.environ.get('IOS6_SDK',str(ROOT/'references/sdk/iPhoneOS6.0.sdk')));a=p.parse_args() + if not pathlib.Path(a.sdk,'SDKSettings.plist').is_file(): p.error('SDK iOS absent : exécuter tools/prepare_sdk.py ou fournir --sdk') + name=a.project.resolve().name;out=ROOT/'build/apps'/(name+'.app');out.mkdir(parents=True,exist_ok=True) + a.bundle_id=a.bundle_id or 'org.ios6emu.'+name + sources=sorted(a.project.glob('*.m')) + if not sources: p.error('Aucune source Objective-C') + subprocess.run(['/opt/cctools/bin/arm-apple-darwin9-gcc','-isysroot',a.sdk,'-miphoneos-version-min=6.0','-arch','armv7','-fblocks','-std=gnu99','-O2','-Wall','-Wextra',*map(str,sources),'-framework','UIKit','-framework','Foundation','-framework','CoreGraphics','-o',str(out/name)],check=True) + (out/'Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier':a.bundle_id,'CFBundleExecutable':name,'CFBundleName':name,'CFBundleDisplayName':name,'CFBundlePackageType':'APPL','CFBundleVersion':'1','MinimumOSVersion':'6.0','LSRequiresIPhoneOS':True,'UIDeviceFamily':[1]})) + subprocess.run(['/opt/cctools/bin/ldid','-S',str(out/name)],check=True);print(out) +if __name__=='__main__': main() diff --git a/ios6/tools/build_netd.sh b/ios6/tools/build_netd.sh new file mode 100755 index 0000000000..969494806a --- /dev/null +++ b/ios6/tools/build_netd.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Relais réseau de l'hôte (PPP sur UART1 vers libslirp) ; Linux et macOS. +set -euo pipefail +sim_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) +cd "$sim_root" +mkdir -p build +cc -O2 agent/netd.c -o build/ios6-netd.new $(pkg-config --cflags --libs slirp) +mv build/ios6-netd.new build/ios6-netd diff --git a/ios6/tools/cache_address.py b/ios6/tools/cache_address.py new file mode 100644 index 0000000000..c5ab35f348 --- /dev/null +++ b/ios6/tools/cache_address.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""Résout une adresse ARM32 dans le cache dyld Apple, avec glissement explicite.""" +import argparse +import pathlib +import struct + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +class Cache: + def __init__(self): + self.data = (ROOT / 'firmware/dyld_shared_cache_armv7').read_bytes() + b = self.data + assert b[:16] == b'dyld_v1 armv7\0' + mo, mc, io, ic = struct.unpack_from('<4I', b, 16) + self.maps = [struct.unpack_from(' {address:#x} : {name}') + for label, symbol in [('symbole précédent', previous), ('export précédent', named)]: + if symbol: + print(f' {label}: {symbol[1]}+{address-symbol[0]:#x}') + if args.bytes: + from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB + offset = cache.offset(address) + for i in Cs(CS_ARCH_ARM, CS_MODE_THUMB).disasm(cache.data[offset:offset + args.bytes], address): + print(f' {i.address:08x}: {i.mnemonic:10s} {i.op_str}') + + +if __name__ == '__main__': + main() diff --git a/ios6/tools/gdb_remote.py b/ios6/tools/gdb_remote.py new file mode 100644 index 0000000000..b7b0d58f54 --- /dev/null +++ b/ios6/tools/gdb_remote.py @@ -0,0 +1,67 @@ +"""Client RSP minimal pour examiner notre VM QEMU locale.""" +import socket + + +class Remote: + def __init__(self, path): + self.socket = socket.socket(socket.AF_UNIX) + self.socket.settimeout(10) + self.socket.connect(str(path)) + + def command(self, command): + payload = command.encode() + self.socket.sendall(b'$' + payload + b'#' + f'{sum(payload) & 255:02x}'.encode()) + return self.receive() + + def receive(self): + while True: + marker = self.socket.recv(1) + if not marker: + raise EOFError('Débogueur déconnecté') + if marker == b'$': + break + reply = bytearray() + while True: + c = self.socket.recv(1) + if c == b'#': + break + if not c: + raise EOFError('Débogueur déconnecté') + reply += c + checksum = self.socket.recv(2) + assert int(checksum, 16) == sum(reply) & 255 + self.socket.sendall(b'+') + # Décodage RSP : échappement et répétition. + result = bytearray() + i = 0 + while i < len(reply): + if reply[i] == ord('}'): + i += 1 + result.append(reply[i] ^ 0x20) + elif reply[i] == ord('*'): + i += 1 + result.extend([result[-1]] * (reply[i] - 29)) + else: + result.append(reply[i]) + i += 1 + return result.decode() + + def monitor(self, command): + result = self.command('qRcmd,' + command.encode().hex()) + output = bytearray() + while result != 'OK': + if not result.startswith('O'): + raise RuntimeError(result) + output += bytes.fromhex(result[1:]) + result = self.receive() + return output.decode(errors='replace') + + def write_memory(self, address, data): + assert self.command(f'M{address:x},{len(data):x}:{data.hex()}') == 'OK' + + def write_register(self, number, value): + assert self.command(f'P{number:x}:{value.to_bytes(4, "little").hex()}') == 'OK' + + def close(self): + self.command('D') + self.socket.close() diff --git a/ios6/tools/inspect_firmware.py b/ios6/tools/inspect_firmware.py new file mode 100755 index 0000000000..5f7106df4a --- /dev/null +++ b/ios6/tools/inspect_firmware.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Lecture des formats Apple DeviceTree et Mach-O ARM32, sans modification.""" +import json +import pathlib +import struct + + +def devicetree(data, offset=0): + nprops, nchildren = struct.unpack_from('= 8 and offset + size <= len(data) + if cmd == 1: + name, va, vsize, fo, fs, maxp, initp, ns, sf = struct.unpack_from('<16s8I', data, offset + 8) + segments.append(dict(name=name.rstrip(b'\0').decode(), vmaddr=va, + vmsize=vsize, fileoff=fo, filesize=fs)) + elif cmd == 5: + flavor, count = struct.unpack_from(' 64 else '') + print(' ', key, formatted) diff --git a/ios6/tools/kernel_address.py b/ios6/tools/kernel_address.py new file mode 100755 index 0000000000..62cf781cf1 --- /dev/null +++ b/ios6/tools/kernel_address.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""Résout et désassemble une adresse du noyau ARM32 de référence.""" +import argparse +import pathlib +import struct +from inspect_firmware import macho + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +def symbols(data): + offset = 28 + result = [] + for _ in range(struct.unpack_from('= 0: + result.append((value, data[stroff + name:end].decode(errors='replace'))) + offset += size + return sorted(result) + + +def main(): + from capstone import Cs, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_THUMB + parser = argparse.ArgumentParser() + parser.add_argument('address', type=lambda s: int(s, 0)) + parser.add_argument('--arm', action='store_true') + parser.add_argument('--bytes', type=int, default=128) + args = parser.parse_args() + address = args.address & ~1 + if 0x40000000 <= address < 0x50000000: + address += 0x40000000 + data = (ROOT / 'firmware/Kernelcache.dec').read_bytes() + previous = [s for s in symbols(data) if s[0] <= address] + if previous: + value, name = previous[-1] + print(f'Symbole exporté précédent : {name}+0x{address-value:x} (peut être une autre fonction)') + info = macho(data) + segment = next(s for s in info['segments'] if s['vmaddr'] <= address < s['vmaddr'] + s['filesize']) + offset = address - segment['vmaddr'] + segment['fileoff'] + mode = CS_MODE_ARM if args.arm else CS_MODE_THUMB + for i in Cs(CS_ARCH_ARM, mode).disasm(data[offset:offset + args.bytes], address): + print(f'{i.address:08x}: {i.mnemonic:10s} {i.op_str}') + + +if __name__ == '__main__': + main() diff --git a/ios6/tools/kext_info.py b/ios6/tools/kext_info.py new file mode 100644 index 0000000000..a017106262 --- /dev/null +++ b/ios6/tools/kext_info.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""Lit OSSerializeXML (ID/IDREF) et décrit les extensions du kernelcache.""" +import base64 +import json +import pathlib +import xml.etree.ElementTree as ET +from inspect_firmware import macho + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +def deserialize(data): + references = {} + def parse(node): + if 'IDREF' in node.attrib: + return references[node.attrib['IDREF']] + if node.tag == 'dict': + children = list(node) + value = {children[i].text: parse(children[i+1]) for i in range(0, len(children), 2)} + elif node.tag == 'array': + value = [parse(child) for child in node] + elif node.tag == 'integer': + value = int(node.text or '0', 0) + elif node.tag in ('true', 'false'): + value = node.tag == 'true' + elif node.tag == 'data': + value = base64.b64decode(node.text or '') + else: + value = node.text or '' + if 'ID' in node.attrib: + references[node.attrib['ID']] = value + return value + return parse(ET.fromstring(data)) + + +def kexts(): + b = (ROOT / 'firmware/Kernelcache.dec').read_bytes() + s = next(s for s in macho(b)['segments'] if s['name'] == '__PRELINK_INFO') + return deserialize(b[s['fileoff']:s['fileoff'] + s['filesize']].split(b'\0')[0])['_PrelinkInfoDictionary'] + + +if __name__ == '__main__': + print(json.dumps(kexts(), indent=2, default=lambda v: v.hex())) diff --git a/ios6/tools/macho_address.py b/ios6/tools/macho_address.py new file mode 100644 index 0000000000..670cab744a --- /dev/null +++ b/ios6/tools/macho_address.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +"""Résout les méthodes Objective-C et les imports d'un Mach-O ARM32 local.""" +import argparse +import pathlib +import struct + + +class MachO: + def __init__(self, path): + self.data = pathlib.Path(path).read_bytes() + b = self.data + assert b[:4] == bytes.fromhex('cefaedfe'), 'Mach-O ARM32 simple requis' + assert struct.unpack_from(' {address:#x}') + if previous is not None: + print(f' Méthode/import précédent : {macho.names[previous]}+{address-previous:#x}') + if args.bytes: + from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB + offset = macho.offset(address) + for ins in Cs(CS_ARCH_ARM, CS_MODE_THUMB).disasm(macho.data[offset:offset+args.bytes], address): + annotation = '' + if ins.mnemonic.startswith('bl') and ins.op_str.startswith('#0x'): + target = int(ins.op_str[1:], 16) + if target in macho.names: + annotation = ' ; ' + macho.names[target] + print(f' {ins.address:08x}: {ins.mnemonic:10s} {ins.op_str}{annotation}') + + +if __name__ == '__main__': + main() diff --git a/ios6/tools/prepare_boot.py b/ios6/tools/prepare_boot.py new file mode 100755 index 0000000000..4620c51f24 --- /dev/null +++ b/ios6/tools/prepare_boot.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Prépare un démarrage direct 10B500/n88 en conservant le firmware original.""" +import json +import argparse +import pathlib +import struct +from inspect_firmware import devicetree, encode_tree, macho, walk + +ROOT = pathlib.Path(__file__).resolve().parents[1] +FW = ROOT / 'firmware' +u32 = lambda *values: struct.pack('<' + 'I' * len(values), *values) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--profile', choices=['original', 'display', 'core'], default='original') + parser.add_argument('--rootfs', action='store_true', help='Disque iOS complet, RAM relocalisée et memdev physique') + parser.add_argument('--software-rendering', action='store_true', help='Essai sans publication du GPU SGX dans le DeviceTree') + options = parser.parse_args() + pa = 0x70000000 if options.rootfs else 0x40000000 + kernel = (FW / 'Kernelcache.dec').read_bytes() + info = macho(kernel) + assert info['entry'] == 0x80086084, 'Ce prototype attend le noyau 10B500/n88' + base = 0x80001000 + end = max(s['vmaddr'] + s['vmsize'] for s in info['segments']) + assert end < 0x81000000 + flat = bytearray(end - base) + for s in info['segments']: + if s['filesize']: + assert s['fileoff'] + s['filesize'] <= len(kernel) + offset = s['vmaddr'] - base + flat[offset:offset + s['filesize']] = kernel[s['fileoff']:s['fileoff'] + s['filesize']] + patches = [] + if options.rootfs: + # Adaptations du seul noyau expérimental ; chaque opcode source est vérifié. + for address, before, after, reason in [ + (0x80270822, '17f69dff', '00bf00bf', 'RAMDisk : conserver une adresse physique'), + (0x80270828, '0021', '0121', 'mdevadd : activer les transferts physiques'), + (0x80240af8, '1e21', '0121', 'IORTC absent : limiter son attente à une seconde'), + (0x804b0674, '1e21', '0121', 'Horloge absente : limiter son attente à une seconde'), + ]: + offset = address - base + original, replacement = bytes.fromhex(before), bytes.fromhex(after) + assert flat[offset:offset + len(original)] == original, hex(address) + flat[offset:offset + len(original)] = replacement + patches.append(dict(address=hex(address), original=before, replacement=after, reason=reason)) + kernel_name = 'kernel-rootfs.bin' if options.rootfs else 'kernel.bin' + (FW / kernel_name).write_bytes(flat) + tree, _ = devicetree((FW / 'DeviceTree.dec').read_bytes()) + nodes = dict(walk(tree)) + removed = [] + if options.profile in ('display', 'core'): + # Le système graphique est conservé ; le coprocesseur audio et ses + # consommateurs sont exclus jusqu'à leur émulation effective. + excluded = {'iop', 'audio-complex', 'i2s0', 'i2s1', 'i2s2', 'compass', 'compass1'} + if options.software_rendering: + excluded.add('sgx') + def prune(node): + props, children = node + for child in list(children): + name = child[0].get('name', b'').rstrip(b'\0').decode() + if name in excluded: + removed.append(name) + child[0]['compatible'] = b'ios6-emu,disabled\0' + else: + prune(child) + prune(tree) + if options.profile == 'core': + arm_io = next(n for n in tree[1] if n[0].get('name') == b'arm-io\0') + for child in list(arm_io[1]): + name = child[0]['name'].rstrip(b'\0').decode() + if name not in {'vic', 'uart0'}: + removed.append(name) + child[0]['compatible'] = b'ios6-emu,disabled\0' + if options.profile == 'display': + # Identifiant non nul requis par ApplePinotLCD ; panneau expérimental. + nodes['/device-tree/arm-io/mipi-dsim/lcd']['lcd-panel-id'] = u32(0xE50486) + root = nodes['/device-tree'] + if options.rootfs: + # Mode sans vérification NAND prévu par AppleARMPE ; le disque est local. + root.pop('secure-root-prefix', None) + # Le profil expérimental ne publie pas de classe de modem physique. + nodes['/device-tree/baseband']['class'] = u32(0) + # UART debug dédié au lien de développement PPP, sans contrôleur DMA. + for key in ('dma-channels', 'dma-parent', 'dma-types'): + nodes['/device-tree/arm-io/uart1'].pop(key, None) + root['clock-frequency'] = u32(600000000) + root['platform-name'] = b's5l8920x\0' + chosen = nodes['/device-tree/chosen'] + chosen.update({'chip-id': u32(0x8920), 'board-id': u32(0), + 'debug-enabled': u32(1), 'secure-boot': u32(0), + 'firmware-version': b'ios6-emu direct-kernel prototype\0', + 'random-seed': bytes(range(64))}) + # IODTNVRAM parcourt des partitions CHRP de longueur non nulle en unités + # de 16 octets. Le placeholder IPSW (8192 zéros) provoque une boucle infinie. + nvram = bytearray(8192) + nvram[:16] = struct.pack('> 8) + chosen['nvram-proxy-data'] = bytes(nvram) + cpu = nodes['/device-tree/cpus/cpu0'] + for key, value in {'clock-frequency': 600000000, 'timebase-frequency': 24000000, + 'bus-frequency': 100000000, 'memory-frequency': 200000000, + 'peripheral-frequency': 100000000, 'fixed-frequency': 24000000, + 'cpu-version': 0x410fc080, 'reg': 0}.items(): + cpu[key] = u32(value) + nodes['/device-tree/memory']['reg'] = u32(pa, 0x10000000) + # Régions réservées par le chargeur : journal de panique et framebuffer. + # Le firmware contient des placeholders nuls qu'iBoot remplit normalement. + nodes['/device-tree/pram']['reg'] = u32(pa + 0x0ffe0000, 0x10000) + nodes['/device-tree/vram']['reg'] = u32(pa + 0x0f000000, 0x100000) + nodes['/device-tree/arm-io']['chip-revision'] = u32(0) + nodes['/device-tree/arm-io']['usbphy-frequency'] = u32(24000000) + nodes['/device-tree/arm-io/vic']['vic-offset'] = u32(0) + memory = nodes['/device-tree/chosen/memory-map'] + memory['RAMDisk'] = u32(0x00100000 if options.rootfs else 0x42000000, + (FW / ('rootfs-vm.hfs' if options.rootfs else 'RestoreRamdisk.dec')).stat().st_size) + memory['Kernel-__TEXT'] = u32(pa + 0x1000, info['segments'][0]['vmsize']) + dt = encode_tree(tree) + dt_pa = pa + 0x01004000 + # boot_args ARM32: revision/version, bases, mémoire, sommet des allocations, + # Boot_Video (6 mots), machineType, DeviceTree virtuel, longueur, cmdline. + # 10B500 exige Version == 5 (contrôle à 0x8027acea). + args = struct.pack('III', data, 8) + assert 0 < expected <= 64 * 1024 * 1024 and 384 + compressed <= len(data) + source = data[384:384 + compressed] + output = bytearray() + ring = bytearray(b' ' * 4096) + pos, cursor = 4096 - 18, 0 + while cursor < len(source): + flags = source[cursor] + cursor += 1 + for bit in range(8): + if cursor == len(source): + break + if flags & (1 << bit): + values = [source[cursor]] + cursor += 1 + idx, count = 0, 1 + else: + assert cursor + 2 <= len(source) + a, b = source[cursor:cursor + 2] + cursor += 2 + idx, count = a | ((b & 0xf0) << 4), (b & 15) + 3 + values = None + assert len(output) + count <= expected + for j in range(count): + c = values[0] if values else ring[(idx + j) & 4095] + output.append(c) + ring[pos] = c + pos = (pos + 1) & 4095 + assert len(output) == expected, 'Taille LZSS incorrecte' + assert zlib.adler32(output) == checksum, 'Somme Adler32 incorrecte' + return bytes(output) + + +def decrypt_img3(data, key): + assert data[:4] == b'3gmI' + cursor = 20 + while cursor + 12 <= len(data): + tag, size, length = struct.unpack_from('<4sII', data, cursor) + assert size >= 12 and cursor + size <= len(data) + if tag == b'ATAD': + # DATA déclare parfois une taille non alignée ; le dernier bloc AES + # inclut le padding du tag (nécessaire pour ce kernelcache). + encrypted_size = (length + 15) & ~15 + assert encrypted_size <= size - 12 + encrypted = data[cursor + 12:cursor + 12 + encrypted_size] + decoded = subprocess.run( + ['openssl', 'enc', '-d', '-aes-256-cbc', '-K', key['key'], + '-iv', key['iv'], '-nopad'], input=encrypted, + capture_output=True, check=True).stdout[:length] + return lzss(decoded) if decoded.startswith(b'complzss') else decoded + cursor += size + raise ValueError('Tag DATA absent') + + +def main(): + FW.mkdir(exist_ok=True) + (ROOT / 'evidence').mkdir(exist_ok=True) + ipsw = FW / URL.rsplit('/', 1)[-1] + download(URL, ipsw) + with ipsw.open('rb') as source: + assert hashlib.file_digest(source, 'sha256').hexdigest() == SHA256, 'IPSW corrompu' + download(KEYS_URL, FW / 'keys.json') + keys = json.loads((FW / 'keys.json').read_text()) + assert keys['identifier'] == 'iPhone2,1' and keys['buildid'] == '10B500' + manifest = dict(ipsw_url=URL, ipsw_sha256=SHA256, keys_url=KEYS_URL, components={}) + with zipfile.ZipFile(ipsw) as archive: + for image in ('Kernelcache', 'DeviceTree', 'RestoreRamdisk'): + key = next(k for k in keys['keys'] if k['image'] == image) + names = [n for n in archive.namelist() if n.rsplit('/', 1)[-1] == key['filename']] + assert len(names) == 1 + decoded = decrypt_img3(archive.read(names[0]), key) + if image == 'Kernelcache': + assert decoded[:4] == b'\xce\xfa\xed\xfe' + temporary = FW / (image + '.dec.tmp') + temporary.write_bytes(decoded) + temporary.replace(FW / (image + '.dec')) + manifest['components'][image] = dict(member=names[0], bytes=len(decoded), + sha256=hashlib.sha256(decoded).hexdigest()) + print(image, len(decoded), 'octets vérifiés', flush=True) + (ROOT / 'evidence/firmware-manifest.json').write_text(json.dumps(manifest, indent=2) + '\n') + + +if __name__ == '__main__': + main() diff --git a/ios6/tools/prepare_rootfs.py b/ios6/tools/prepare_rootfs.py new file mode 100644 index 0000000000..06a009295b --- /dev/null +++ b/ios6/tools/prepare_rootfs.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +"""Prépare une copie HFS indépendante du système iOS pour le disque virtuel.""" +import hashlib +import json +import pathlib +import plistlib +import shutil +import struct +import subprocess +import zipfile + +from prepare_firmware import FW, ROOT, SHA256 + +LEGACY = pathlib.Path('/srv/homes/iphoneos2/ipod-emu') +HFS = LEGACY / 'xpwn/b/hfs/hfsplus' +SIZE = 0x6f000000 + + +def run(*args): + subprocess.run([str(a) for a in args], check=True, timeout=120) + + +def main(): + original = FW / 'rootfs.hfs' + if not original.exists(): + ipsw = FW / 'iPhone2,1_6.1.6_10B500_Restore.ipsw' + with ipsw.open('rb') as f: + assert hashlib.file_digest(f, 'sha256').hexdigest() == SHA256 + key = next(k for k in json.loads((FW / 'keys.json').read_text())['keys'] if k['image'] == 'RootFS') + encrypted = FW / key['filename'] + with zipfile.ZipFile(ipsw) as archive, archive.open(key['filename']) as source, encrypted.open('wb') as output: + shutil.copyfileobj(source, output) + run(LEGACY / 'dmg2img/vfdecrypt', '-i', encrypted, '-k', key['key'], '-o', FW / 'rootfs.dmg') + run(LEGACY / 'dmg2img/dmg2img', FW / 'rootfs.dmg', FW / 'rootfs.img') + with (FW / 'rootfs.img').open('rb') as source: + header = source.read(512) + assert header[:2] == b'ER' + blocksize = struct.unpack_from('>H', header, 2)[0] + source.seek(blocksize) + entry = source.read(512) + count = struct.unpack_from('>I', entry, 4)[0] + for index in range(1, count + 1): + source.seek(index * blocksize) + entry = source.read(512) + assert entry[:2] == b'PM' + if entry[48:80].split(b'\0')[0] == b'Apple_HFSX': + start, blocks = struct.unpack_from('>II', entry, 8) + source.seek(start * blocksize) + with original.open('wb') as output: + remaining = blocks * blocksize + while remaining: + data = source.read(min(remaining, 1024 * 1024)) + assert data + output.write(data) + remaining -= len(data) + break + else: + raise ValueError('Partition Apple_HFSX absente') + image = FW / 'rootfs-vm.hfs' + # Une reconstruction explicite réinitialise uniquement la copie expérimentale. + run('cp', '--reflink=auto', '--sparse=always', original, image) + run(HFS, image, 'grow', SIZE) + fstab = ROOT / 'evidence/vm-fstab' + fstab.write_text('/dev/md0 / hfs rw 0 0\n') + run(HFS, image, 'add', fstab, '/etc/fstab') + # Trousseau de session en RAM, indépendant du serveur keybagd d'Apple. + helper = ROOT / 'build/ios6-keybag-init' + run('/opt/cctools/bin/arm-apple-darwin9-gcc', '-isysroot', + '/opt/theos/sdks/iPhoneOS2.2.1.sdk', '-miphoneos-version-min=2.2.1', + '-std=gnu99', '-O2', '-Wall', '-Wextra', ROOT / 'src/keybag_init.c', + '-framework', 'CoreFoundation', '-o', helper) + run('/opt/cctools/bin/ldid', '-S', helper) + run(HFS, image, 'add', helper, '/usr/libexec/ios6-session') + run(HFS, image, 'chmod', '755', '/usr/libexec/ios6-session') + # Le migrateur du modem attend indéfiniment CommCenter sans baseband. + # Conserver le bundle Apple hors du répertoire parcouru par DataMigrator. + run(HFS, image, 'mkdir', '/System/Library/EmulatorDisabledMigrators') + run(HFS, image, 'mv', '/System/Library/DataClassMigrators/CommCenterMigrator.migrator', + '/System/Library/EmulatorDisabledMigrators/CommCenterMigrator.migrator') + # L'assistant de configuration exige des services d'activation absents de + # cette VM. Son bundle reste conservé hors des applications enregistrées. + run(HFS, image, 'mkdir', '/System/Library/EmulatorDisabledApplications') + run(HFS, image, 'mv', '/Applications/Setup.app', + '/System/Library/EmulatorDisabledApplications/Setup.app') + run(HFS, image, 'mkdir', '/System/Library/EmulatorDisabledDaemons') + for name in ('CommCenter', 'CommCenterClassic', 'CommCenterLite', + 'CommCenterMobileHelper', 'CommCenterRootHelper', 'mediaserverd', + 'mobile.keybagd'): + filename = f'com.apple.{name}.plist' + run(HFS, image, 'mv', f'/System/Library/LaunchDaemons/{filename}', + f'/System/Library/EmulatorDisabledDaemons/{filename}') + session = ROOT / 'evidence/ios6-session.plist' + session.write_bytes(plistlib.dumps({ + 'Label': 'org.ios6-emu.session', 'RunAtLoad': True, + 'ProgramArguments': ['/usr/libexec/ios6-session'], + 'StandardOutPath': '/dev/console', 'StandardErrorPath': '/dev/console', + })) + run(HFS, image, 'add', session, '/System/Library/LaunchDaemons/org.ios6-emu.session.plist') + # SpringBoard ne doit pas attendre les réponses d'un modem inexistant. + capability_path = '/System/Library/CoreServices/SpringBoard.app/N88AP.plist' + capability_file = ROOT / 'evidence/N88AP-vm.plist' + run(HFS, original, 'extract', capability_path, capability_file) + capabilities = plistlib.loads(capability_file.read_bytes()) + for key in ('telephony', 'any-telephony', 'sms', 'mms', 'personal-hotspot'): + capabilities['capabilities'][key] = False + capabilities['capabilities']['telephony-maximum-generation'] = 0.0 + capability_file.write_bytes(plistlib.dumps(capabilities)) + run(HFS, image, 'rm', capability_path) + run(HFS, image, 'add', capability_file, capability_path) + # Le firmware n88 fournit les fontes _UH ; le profil graphique sans SGX + # sélectionne sinon les tables _H dont les fichiers sont absents. + for target, source in [('CGFontCache', 'CGFontCacheU'), ('CGFontCacheR', 'CGFontCacheUR')]: + local = ROOT / f'evidence/{target}-vm.plist' + run(HFS, original, 'extract', f'/System/Library/Fonts/{source}.plist', local) + run(HFS, image, 'add', local, f'/System/Library/Fonts/{target}.plist') + setup = ROOT / 'evidence/vm-setup.plist' + setup.write_bytes(plistlib.dumps({ + 'SetupDone': True, 'SetupFinishedAllSteps': True, + 'SetupVersion': 3, 'ForceNoBuddy': True, + })) + for domain in ('com.apple.purplebuddy', 'com.apple.purplebuddy.notbackedup'): + run(HFS, image, 'add', setup, f'/var/mobile/Library/Preferences/{domain}.plist') + for name in ('backboardd', 'SpringBoard'): + guest_path = f'/System/Library/LaunchDaemons/com.apple.{name}.plist' + local_path = ROOT / f'evidence/{name}-console.plist' + run(HFS, original, 'extract', guest_path, local_path) + config = plistlib.loads(local_path.read_bytes()) + config.update(StandardOutPath='/dev/console', StandardErrorPath='/dev/console') + config['EnvironmentVariables'] = { + 'CA_NO_ACCEL': '1', 'CA_ENABLE_OGL': '0', 'CA_PRINT_PERF': '1', + } + local_path.write_bytes(plistlib.dumps(config)) + run(HFS, image, 'rm', guest_path) + run(HFS, image, 'add', local_path, guest_path) + asl = ROOT / 'evidence/vm-asl.conf' + asl.write_text('? [>= Level 0] file /var/log/ios6-emu.log format=bsd mode=0644\n' + '? [= Sender backboardd] file /dev/console format=bsd\n' + '? [= Sender SpringBoard] file /dev/console format=bsd\n' + '? [= Sender DataMigrator] file /dev/console format=bsd\n' + '? [<= PID 1] store\n') + run(HFS, image, 'add', asl, '/etc/asl.conf') + with image.open('rb') as f: + f.seek(1024) + header = f.read(512) + assert header[:2] == b'HX' + blocksize, blocks, free = struct.unpack_from('>III', header, 40) + assert blocks * blocksize == SIZE and free > 0 + report = {'image': str(image), 'bytes': SIZE, 'free_bytes': free * blocksize, + 'changes': ['fstab: volume unique md0, lecture-écriture, sans fsck automatique', + 'ios6-session: trousseau RAM et geste tactile de démonstration ; keybagd Apple conservé, job écarté', + 'CommCenterMigrator: conservé hors du répertoire des migrations (modem absent)', + 'CommCenter et mediaserverd: jobs conservés hors de LaunchDaemons (modem/audio absents)', + 'Polices: tables Apple U/UR pour les fichiers _UH présents', + 'Assistant initial: SetupDone, SetupFinishedAllSteps, SetupVersion=3, ForceNoBuddy', + 'Setup.app: conservée hors du répertoire des applications', + 'N88AP: capacités de téléphonie désactivées (modem absent)', + 'backboardd et SpringBoard: console, CA_NO_ACCEL=1, CA_ENABLE_OGL=0', + 'SpringBoard: lancement par backboardd (configuration Apple)', + 'journal ASL: /var/log/ios6-emu.log']} + (ROOT / 'evidence/rootfs-layout.json').write_text(json.dumps(report, indent=2) + '\n') + print(json.dumps(report, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/ios6/tools/prepare_sdk.py b/ios6/tools/prepare_sdk.py new file mode 100644 index 0000000000..83351d73f3 --- /dev/null +++ b/ios6/tools/prepare_sdk.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +"""Prépare le SDK iOS 6.0 de référence, épinglé et vérifié.""" +import hashlib +from pathlib import Path +import subprocess +import urllib.request + +ROOT = Path(__file__).resolve().parents[1] +COMMIT = '3111c86f53b021dabee781aec3fb2d9fb8f3f862' +SHA256 = '68a163f56d7ff9abcad18586a15c76f572de99e13c122e119118e228cdecb652' +BASE = f'https://raw.githubusercontent.com/Sn0wCooder/theos-sdks/{COMMIT}/iPhoneOS6.0.sdk/' + +def main(): + refs = ROOT / 'references' + refs.mkdir(exist_ok=True) + archive = refs / 'ios6-sdk.zip' + if not archive.exists(): + temporary = archive.with_suffix('.download') + with temporary.open('wb') as out: + for number in range(1, 4): + name = f'iPhoneOS6.0.sdk.zip.{number:03}' + print('Téléchargement :', name, flush=True) + with urllib.request.urlopen(BASE + name, timeout=120) as response: + while chunk := response.read(1024 * 1024): + out.write(chunk) + temporary.replace(archive) + digest = hashlib.sha256() + with archive.open('rb') as stream: + while chunk := stream.read(1024 * 1024): digest.update(chunk) + if digest.hexdigest() != SHA256: + raise SystemExit('SHA-256 du SDK incorrect ; archive conservée pour diagnostic') + destination = refs / 'sdk' + sdk = destination / 'iPhoneOS6.0.sdk' + if not sdk.exists(): + destination.mkdir(exist_ok=True) + subprocess.run(['unzip', '-q', str(archive), '-d', str(destination)], check=True) + if not (sdk / 'SDKSettings.plist').is_file(): + raise SystemExit('SDK incomplet') + print(sdk) + +if __name__ == '__main__': main() diff --git a/ios6/tools/probe.py b/ios6/tools/probe.py new file mode 100755 index 0000000000..0a5ffc8361 --- /dev/null +++ b/ios6/tools/probe.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""Essai borné : console série, MMIO, registres et arrêt de cette seule VM.""" +import argparse +import datetime +import json +import pathlib +import re +import socket +import subprocess +import time +from gdb_remote import Remote + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--seconds', type=float, default=5) + parser.add_argument('--trace', action='store_true', help='Trace assembleur (plafond 32 Mio)') + parser.add_argument('--trace-range', help='Plage QEMU -dfilter pour les registres de chaque bloc') + parser.add_argument('--expect-panic', help='Valide un blocage connu au lieu de le signaler comme échec') + parser.add_argument('--boot-data', default='boot-data.bin') + parser.add_argument('--diagnostic-panic', action='store_true', help='Déclenche volontairement une panique pour inspecter les threads bloqués') + parser.add_argument('--rootfs', action='store_true') + parser.add_argument('--dump-disk', action='store_true', help='Sauvegarde le disque privé avant arrêt pour lire les journaux invités') + parser.add_argument('--dump-ram', action='store_true', help='Sauvegarde la RAM pour examiner les tables DMA et les tampons écran') + parser.add_argument('--capture-interval', type=float, default=0, help='Capture périodique en secondes ; zéro désactive') + options = parser.parse_args() + assert 0 < options.seconds <= 300 + assert options.capture_interval >= 0 + run = ROOT / 'evidence' / datetime.datetime.now(datetime.timezone.utc).strftime('probe-%Y%m%dT%H%M%S-%fZ') + run.mkdir(parents=True) + # Socket court pour respecter la limite Unix de 108 caractères. + sockpath = run / 'qmp' + fw = ROOT / 'firmware' + command = [str(ROOT / 'build/qemu-system-arm'), '-M', 'ios6-n88' + (',rootfs=on' if options.rootfs else ''), + '-kernel', str(fw / ('kernel-rootfs.bin' if options.rootfs else 'kernel.bin')), '-dtb', str(fw / options.boot_data), + '-initrd', str(fw / ('rootfs-vm.hfs' if options.rootfs else 'RestoreRamdisk.dec')), '-display', 'none', + '-serial', 'file:' + str(run / 'serial.log'), '-monitor', 'none', + '-qmp', 'unix:' + str(sockpath) + ',server=on,wait=off', + '-d', 'unimp,guest_errors' + (',in_asm,int' if options.trace else ''), + '-D', str(run / 'qemu.log'), '-no-reboot'] + if options.diagnostic_panic: + command += ['-gdb', 'unix:' + str(run / 'gdb') + ',server=on,wait=off'] + if options.trace_range: + command += ['-d', 'unimp,guest_errors,in_asm,int,cpu', '-dfilter', options.trace_range] + (run / 'command.json').write_text(json.dumps(command, indent=2) + '\n') + result = {'run': str(run), 'seconds': options.seconds} + with (run / 'stderr.log').open('wb') as err: + process = subprocess.Popen(command, stdout=err, stderr=err) + try: + deadline = time.monotonic() + 10 + while not sockpath.exists() and process.poll() is None and time.monotonic() < deadline: + time.sleep(.05) + with socket.socket(socket.AF_UNIX) as client: + client.settimeout(5) + client.connect(str(sockpath)) + stream = client.makefile('rwb') + json.loads(stream.readline()) + + def qmp(name, **arguments): + stream.write(json.dumps({'execute': name, 'arguments': arguments}).encode() + b'\n') + stream.flush() + while True: + reply = json.loads(stream.readline()) + if 'return' in reply: + return reply['return'] + if 'error' in reply: + raise RuntimeError(reply['error']) + + qmp('qmp_capabilities') + deadline = time.monotonic() + options.seconds + next_capture = time.monotonic() + options.capture_interval + capture_number = 0 + while process.poll() is None and time.monotonic() < deadline: + if (run / 'qemu.log').stat().st_size > 32 * 1024 * 1024: + result['trace_limit_reached'] = True + break + if options.rootfs and options.capture_interval and time.monotonic() >= next_capture: + capture_number += 1 + qmp('screendump', filename=str(run / f'screen-{capture_number:03d}.png'), format='png') + next_capture = time.monotonic() + options.capture_interval + time.sleep(.05) + qmp('stop') + result['status'] = qmp('query-status') + result['registers'] = qmp('human-monitor-command', **{'command-line': 'info registers'}) + registers = dict(re.findall(r'R(\d\d)=([0-9a-fA-F]+)', result['registers'])) + for field, reg, fmt in [('instructions', '15', '16i'), ('stack', '13', '32wx')]: + result[field] = qmp('human-monitor-command', **{'command-line': f'x /{fmt} 0x{registers[reg]}'}) + if options.rootfs: + qmp('screendump', filename=str(run / 'screen.ppm')) + qmp('screendump', filename=str(run / 'screen.png'), format='png') + if options.dump_ram: + client.settimeout(60) + qmp('pmemsave', val=0x70000000 if options.rootfs else 0x40000000, + size=0x10000000, filename=str(run / 'ram.bin')) + client.settimeout(5) + if options.dump_disk: + assert options.rootfs + client.settimeout(60) + qmp('pmemsave', val=0x100000, size=(fw / 'rootfs-vm.hfs').stat().st_size, filename=str(run / 'guest-disk.hfs')) + assert (run / 'guest-disk.hfs').stat().st_size == (fw / 'rootfs-vm.hfs').stat().st_size + client.settimeout(5) + # Un arrêt spontané peut passer par Debugger() sans la chaîne + # « panic(cpu » ; conserver sa pile plutôt que la remplacer. + serial_so_far = (run / 'serial.log').read_text(errors='replace') + natural_debugger = re.search(r'panic\(cpu[^\n]+|Debugger message:[^\n]+', serial_so_far) + if options.diagnostic_panic and not natural_debugger: + for _ in range(200): + psr = int(re.search(r'PSR=([0-9a-fA-F]+)', result['registers']).group(1), 16) + # Éviter les trampolines ARM de retour de syscall : + # leur SP/R7 n'est pas un cadre C de noyau ordinaire. + if psr & 0x3f == 0x33: + break + qmp('cont') + time.sleep(.01) + qmp('stop') + result['registers'] = qmp('human-monitor-command', **{'command-line': 'info registers'}) + assert psr & 0x3f == 0x33, 'Diagnostic : contexte SVC Thumb introuvable' + result['diagnostic_panic_requested'] = True + (run / 'serial-before-diagnostic.log').write_bytes((run / 'serial.log').read_bytes()) + remote = Remote(run / 'gdb') + remote.command('qSupported') + remote.command('Hg0') + # Chaîne constante du noyau : aucun écrasement de mémoire invitée. + remote.write_register(0, 0x80288d9c) + psr = int(re.search(r'PSR=([0-9a-fA-F]+)', result['registers']).group(1), 16) + remote.write_register(25, psr | 0x20) + remote.write_register(15, 0x80017c11) + remote.close() + qmp('cont') + time.sleep(3) + qmp('stop') + qmp('quit') + process.wait(timeout=5) + except Exception as exc: + result['error'] = f'{type(exc).__name__}: {exc}' + finally: + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=5) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + result['returncode'] = process.returncode + serial = (run / 'serial.log').read_text(errors='replace') if (run / 'serial.log').exists() else '' + natural_serial = (run / 'serial-before-diagnostic.log').read_text(errors='replace') if options.diagnostic_panic and (run / 'serial-before-diagnostic.log').exists() else serial + panic = re.search(r'panic\(cpu[^\n]+|Debugger message:[^\n]+', natural_serial) + result['guest_status'] = 'kernel-panic' if panic else 'partial-boot' + result['panic'] = panic.group() if panic else None + result['milestones'] = { + 'serial_console': 'iBoot version:' in serial, + 'crypto_self_tests': 'corecrypto.kext FIPS POST passed!' in serial, + 'platform_driver': 'AppleS5L8920XIO::start' in serial, + 'interrupt_controller_driver': 'AppleARMPL192VIC::start' in serial, + 'userspace_confirmed': '*** launchd[1] has started up. ***' in serial, + } + # Ces jalons n'impliquent ni montage du disque ni démarrage de launchd. + if options.expect_panic: + result['expected_panic_matched'] = bool(panic and options.expect_panic in panic.group()) + (run / 'result.json').write_text(json.dumps(result, indent=2) + '\n') + print(json.dumps({k: v for k, v in result.items() if k not in ('registers', 'instructions', 'stack')}, indent=2)) + if 'error' in result or result['returncode'] != 0: + raise SystemExit(1) + if options.expect_panic and not result['expected_panic_matched']: + raise SystemExit(1) + if panic and not options.expect_panic: + raise SystemExit(2) + + +if __name__ == '__main__': + main() diff --git a/ios6/tools/recover_hfs.py b/ios6/tools/recover_hfs.py new file mode 100644 index 0000000000..78d64abdbf --- /dev/null +++ b/ios6/tools/recover_hfs.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +"""Rejoue le journal d'un instantané HFSX dans une nouvelle copie hors ligne. + +Format : Apple TN1150 et xnu-2050.18.24/bsd/vfs/vfs_journal.{c,h}. +Ce lecteur strict traite les transactions validées jusqu'à jhdr.end ; il ne +remplace pas fsck et n'efface pas le marqueur de volume monté. +https://developer.apple.com/library/archive/technotes/tn/tn1150.html +https://github.com/apple-oss-distributions/xnu/tree/xnu-2050.18.24/bsd/vfs +""" +import argparse +import json +import os +import pathlib +import struct +import subprocess + + +def checksum(data): + value = 0 + for byte in data: + value = ((value << 8) ^ (value + byte)) & 0xffffffff + return value ^ 0xffffffff + + +def verified_header(data, checksum_offset, endian): + data = bytearray(data) + expected = struct.unpack_from(endian + 'I', data, checksum_offset)[0] + data[checksum_offset:checksum_offset+4] = bytes(4) + assert checksum(data) == expected, 'Somme de contrôle du journal incorrecte' + + +def recover(source, destination): + assert source.resolve() != destination.resolve() + assert not destination.exists(), 'La destination doit être nouvelle' + image_size = source.stat().st_size + with source.open('rb') as image: + image.seek(1024) + vh = image.read(512) + assert vh[:4] == b'HX\0\5' + attributes, jib_block = struct.unpack_from('>I', vh, 4)[0], struct.unpack_from('>I', vh, 12)[0] + block_size, blocks = struct.unpack_from('>II', vh, 40) + assert blocks * block_size == image_size and attributes & 0x2000 + image.seek(jib_block * block_size) + jib = image.read(64) + assert struct.unpack_from('>I', jib)[0] == 1, 'Journal interne initialisé requis' + journal_offset, journal_size = struct.unpack_from('>QQ', jib, 36) + assert journal_offset + journal_size <= image_size + image.seek(journal_offset) + journal = image.read(journal_size) + assert len(journal) == journal_size + endian = '<' if journal[:4] == b'xLNJ' else '>' + magic, marker, start, end, size, bh_size, _, sector = struct.unpack_from(endian + 'IIQQQIII', journal) + assert magic == 0x4a4e4c78 and marker == 0x12345678 and size == journal_size + assert sector == 512 and bh_size >= 512 + verified_header(journal[:44], 36, endian) + start = sector if start == size else start + end = sector if end == size else end + assert sector <= start < size and sector <= end < size + + def advance(offset, length): + return sector + (offset - sector + length) % (size - sector) + + def read(offset, length): + assert sector <= offset < size and 0 <= length < size - sector + first = min(length, size - offset) + return journal[offset:offset+first] + journal[sector:sector+length-first] + + writes, headers, total, previous_sequence = [], 0, 0, None + position = start + while position != end: + header = read(position, bh_size) + maximum, count, used, _, flags = struct.unpack_from(endian + 'HHIII', header) + assert 1 <= count <= maximum and 16 + 16*count <= bh_size + assert bh_size <= used < size - sector and not flags & ~3 + verified_header(header[:32], 8, endian) + sequence = struct.unpack_from(endian + 'I', header, 28)[0] + if previous_sequence is not None: + assert sequence in (previous_sequence, (previous_sequence + 1) & 0xffffffff) + previous_sequence = sequence + offset, consumed = advance(position, bh_size), bh_size + for index in range(1, count): + number, length, expected = struct.unpack_from(endian + 'QII', header, 16 + 16*index) + assert length and length % sector == 0 + data = read(offset, length) + if number != 0xffffffffffffffff: + if flags & 1 and expected: + assert checksum(data) == expected, f'Bloc corrompu : {number:#x}' + target = number * sector + assert target + length <= image_size + assert target + length <= journal_offset or target >= journal_offset + journal_size + writes.append((target, data)) + consumed += length + offset = advance(offset, length) + assert consumed == used + total += used + assert total <= size - sector, 'Boucle dans le journal' + position = advance(position, used) + headers += 1 + # Toutes les validations précèdent la création de la copie. + subprocess.run(['cp', '--reflink=auto', '--sparse=always', str(source), str(destination)], check=True) + with destination.open('r+b') as image: + for target, data in writes: + image.seek(target) + image.write(data) + image.flush() + os.fsync(image.fileno()) + header = bytearray(journal[:sector]) + struct.pack_into(endian + 'Q', header, 8, end) + struct.pack_into(endian + 'I', header, 36, 0) + struct.pack_into(endian + 'I', header, 36, checksum(header[:44])) + image.seek(journal_offset) + image.write(header) + image.flush() + os.fsync(image.fileno()) + return {'source': str(source), 'destination': str(destination), + 'block_lists': headers, 'block_writes': len(writes), 'journal_bytes': total} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('source', type=pathlib.Path) + parser.add_argument('destination', type=pathlib.Path) + args = parser.parse_args() + print(json.dumps(recover(args.source, args.destination), indent=2)) + + +if __name__ == '__main__': + main() diff --git a/ios6/tools/simulator.py b/ios6/tools/simulator.py new file mode 100644 index 0000000000..a73833ce43 --- /dev/null +++ b/ios6/tools/simulator.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +"""Outils de développement pour la VM iOS 6 locale.""" +import argparse +import contextlib +import fcntl +import json +import os +from pathlib import Path +import plistlib +import signal +import socket +import subprocess +import sys +import time + +ROOT = Path(__file__).resolve().parents[1] +RUN = ROOT / 'run' +DISK = ROOT / 'firmware/rootfs-dev.hfs' +PORT = 7796 + +class Agent: + def __init__(self, timeout=60): + self.sock = socket.create_connection(('127.0.0.1', PORT), timeout=timeout) + self.stream = self.sock.makefile('rb') + def __enter__(self): return self + def __exit__(self, *args): self.stream.close(); self.sock.close() + def call(self, *fields, data=None): + if any('\n' in str(x) or '\t' in str(x) for x in fields): + raise ValueError('Champs de protocole invalides') + self.sock.sendall(('\t'.join(map(str, fields))+'\n').encode()) + if data is not None: self.sock.sendall(data) + header = self.stream.readline(4096).decode().strip().split(' ', 2) + if len(header)<2 or header[0] not in ('OK','ERR'): raise RuntimeError('Réponse agent invalide') + size=int(header[1]); body=bytearray() + if not 0<=size<=64*1024*1024: raise RuntimeError('Réponse trop grande') + while len(body)2 else '' + +def command(*args, timeout=60): + with Agent(timeout) as a: return a.call(*args)[0].decode(errors='replace') + +class Monitor: + def __enter__(self): + self.sock=socket.socket(socket.AF_UNIX); self.sock.settimeout(60) + self.sock.connect(str(RUN/'qmp.sock')); self.stream=self.sock.makefile('rwb') + json.loads(self.stream.readline()); self.call('qmp_capabilities'); return self + def call(self, name, **args): + self.stream.write(json.dumps({'execute':name,'arguments':args}).encode()+b'\n'); self.stream.flush() + while True: + r=json.loads(self.stream.readline()) + if 'error' in r: raise RuntimeError(r['error']) + if 'return' in r: return r['return'] + def __exit__(self,*args): self.stream.close(); self.sock.close() + +def pid(kind): + try: + n=int((RUN/(kind+'.pid')).read_text()) + # macOS n'a pas /proc : ps donne la même ligne de commande. + if Path('/proc/self').exists(): args=Path(f'/proc/{n}/cmdline').read_bytes() + else: args=subprocess.run(['ps','-o','command=','-p',str(n)],capture_output=True).stdout + expected=b'ios6-n88' if kind=='qemu' else str(ROOT/'build/ios6-netd').encode() + return n if expected in args else None + except (OSError,ValueError): return None + +@contextlib.contextmanager +def lifecycle_lock(): + RUN.mkdir(exist_ok=True) + with (RUN/'lifecycle.lock').open('a') as f: + fcntl.flock(f,fcntl.LOCK_EX|fcntl.LOCK_NB); yield + +def start(): + with lifecycle_lock(): + if pid('qemu'): return + if not DISK.exists(): raise RuntimeError('Préparer le disque avec tools/prepare_dev.py') + if pid('netd'): os.kill(pid('netd'),signal.SIGTERM); time.sleep(.2) + for name in ('qmp.sock','uart1.sock'): (RUN/name).unlink(missing_ok=True) + args=[str(ROOT/'build/qemu-system-arm'),'-L',str(ROOT.parent/'pc-bios'),'-M','ios6-n88,rootfs=on', + '-kernel',str(ROOT/'firmware/kernel-rootfs.bin'),'-dtb',str(ROOT/'firmware/boot-display-rootfs.bin'), + '-initrd',str(DISK),'-display','none','-vnc','127.0.0.1:6','-serial','file:'+str(RUN/'serial.log'), + '-serial','unix:'+str(RUN/'uart1.sock')+',server=on,wait=off', + '-qmp','unix:'+str(RUN/'qmp.sock')+',server=on,wait=off','-monitor','none','-no-reboot'] + with (RUN/'qemu.log').open('wb') as log: p=subprocess.Popen(args,cwd=ROOT,stdout=log,stderr=log,start_new_session=True) + (RUN/'qemu.pid').write_text(str(p.pid)) + for _ in range(100): + if p.poll() is not None: raise RuntimeError((RUN/'qemu.log').read_text()) + if (RUN/'qmp.sock').exists(): break + time.sleep(.1) + with (RUN/'netd.log').open('wb') as log: + n=subprocess.Popen([str(ROOT/'build/ios6-netd'),'-s',str(RUN/'uart1.sock'),'-L',f'127.0.0.1:{PORT}:7788'],stdout=log,stderr=log,start_new_session=True) + (RUN/'netd.pid').write_text(str(n.pid)) + time.sleep(.5) + if n.poll() is not None: + p.terminate(); raise RuntimeError((RUN/'netd.log').read_text()) + +def wait_ready(timeout=360): + end=time.monotonic()+timeout; last='agent absent' + while time.monotonic()= 16 and offset + size <= len(nvram) + header = bytearray(nvram[offset:offset + 16]) + checksum, header[1] = header[1], 0 + total = 0 + for byte in header: + total += byte + total = (total & 255) + (total >> 8) + assert checksum == total + offset += size + assert offset == 8192 + assert "files('ios6_n88.c')" in (ROOT.parent / 'hw/arm/meson.build').read_text() + print('OK : empreintes, DeviceTree, noyau inchangé, BSS, mémoire réservée, RAMDisk, NVRAM et source QEMU.') + if options.rootfs: + expected = bytearray(image) + for address, replacement in [(0x80270822, '00bf00bf'), (0x80270828, '0121'), + (0x80240af8, '0121'), (0x804b0674, '0121')]: + offset = address - 0x80001000 + value = bytes.fromhex(replacement) + expected[offset:offset + len(value)] = value + assert (FW / 'kernel-rootfs.bin').read_bytes() == expected, 'Modifications supplémentaires du noyau' + boot = (FW / 'boot-display-rootfs.bin').read_bytes() + assert struct.unpack_from('<4I', boot, 4) == (0x80000000, 0x70000000, 0x0f000000, 0x73000000) + assert struct.unpack_from('<6I', boot, 20) == (0x7f000000, 0, 1280, 320, 480, 32) + tree, end = devicetree(boot, 0x4000) + nodes = dict(walk(tree)) + assert end - 0x4000 == struct.unpack_from('III', header, 40) + assert header[:2] == b'HX' and blocksize * blocks == size and free > 0 + print('OK : quatre adaptations noyau, RAM relocalisée, disque HFSX indépendant, panneau LCD et framebuffer.') + + +if __name__ == '__main__': + main() diff --git a/ios6/tools/verify_display.py b/ios6/tools/verify_display.py new file mode 100644 index 0000000000..4eff00b402 --- /dev/null +++ b/ios6/tools/verify_display.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""Teste le scanout QEMU avec des pixels synthétiques, sans démarrer iOS. + +Ce contrôle matériel ne constitue jamais une preuve d'affichage de SpringBoard. +""" +import json +import pathlib +import socket +import struct +import subprocess +import tempfile +import time + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +def main(): + with tempfile.TemporaryDirectory(prefix='ios6-display-') as directory: + tmp = pathlib.Path(directory) + with (tmp / 'stderr').open('wb') as log: + process = subprocess.Popen([ + str(ROOT / 'build/qemu-system-arm'), '-M', 'ios6-n88,rootfs=on', + '-accel', 'qtest', '-display', 'none', '-serial', 'none', '-monitor', 'none', + '-kernel', str(ROOT / 'firmware/kernel-rootfs.bin'), + '-dtb', str(ROOT / 'firmware/boot-display-rootfs.bin'), + '-initrd', str(ROOT / 'firmware/rootfs-vm.hfs'), + '-qmp', f'unix:{tmp}/qmp,server=on,wait=off', + '-qtest', f'unix:{tmp}/qtest,server=on,wait=off', + ], stdout=log, stderr=log) + sockets = [] + try: + def connect(name): + deadline = time.monotonic() + 10 + while time.monotonic() < deadline: + client = socket.socket(socket.AF_UNIX) + client.settimeout(5) + try: + client.connect(str(tmp / name)) + sockets.append(client) + return client.makefile('rwb') + except (FileNotFoundError, ConnectionRefusedError): + client.close() + assert process.poll() is None, (tmp / 'stderr').read_text() + time.sleep(.02) + raise TimeoutError(name) + + monitor = connect('qmp') + json.loads(monitor.readline()) + + def qmp(command, **args): + monitor.write(json.dumps({'execute': command, 'arguments': args}).encode() + b'\n') + monitor.flush() + while True: + response = json.loads(monitor.readline()) + assert 'error' not in response, response + if 'return' in response: + return response['return'] + + qmp('qmp_capabilities') + test = connect('qtest') + + def command(value): + test.write(value.encode() + b'\n') + test.flush() + result = test.readline().decode().strip() + assert result.startswith('OK'), result + return result.split()[1:] + + def write32(address, value): + command(f'writel {address:#x} {value:#x}') + + def write(address, value): + command(f'write {address:#x} {len(value):#x} 0x{value.hex()}') + + # Deux pages physiques disjointes dans une même image DMA. + write(0x7e000000, struct.pack(' Date: Mon, 5 Oct 2026 11:49:27 +0200 Subject: [PATCH 2/2] Faire tourner le simulateur iOS 6 sur macOS, avec souris et clavier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Compilation sur macOS : en-têtes OpenSSL et libslirp de Homebrew, apps et agent compilés avec le clang de Xcode (cible 5.1, code ARM : le ld récent ne pose plus le bit Thumb sur LC_MAIN ni sur les IMP Objective-C). - Sauvegarde du disque : copie par clone APFS (cp -c) au lieu des options GNU. - Agent : commandes text et key (événements GS clavier matériel construits à la main, appui vide puis relâchement portant le caractère). - Fenêtre macOS native (macos/, tools/build_mac_app.sh) : affichage via le VNC local, souris convertie en gestes, clavier transmis à l'agent. - tools/update_agent.py remplace l'agent à chaud ; tools/vnc_input.py ajoute la souris et le clavier à un client VNC classique. --- ios6/agent/ios6-agent.c | 40 ++++- ios6/macos/iOS6Simulator.swift | 291 +++++++++++++++++++++++++++++++++ ios6/src/session_input.h | 47 ++++++ ios6/tools/build.sh | 1 + ios6/tools/build_agent.sh | 14 +- ios6/tools/build_app.py | 22 ++- ios6/tools/build_mac_app.sh | 22 +++ ios6/tools/build_netd.sh | 3 +- ios6/tools/recover_hfs.py | 9 +- ios6/tools/update_agent.py | 28 ++++ ios6/tools/vnc_input.py | 121 ++++++++++++++ 11 files changed, 589 insertions(+), 9 deletions(-) create mode 100644 ios6/macos/iOS6Simulator.swift create mode 100644 ios6/tools/build_mac_app.sh create mode 100644 ios6/tools/update_agent.py create mode 100644 ios6/tools/vnc_input.py diff --git a/ios6/agent/ios6-agent.c b/ios6/agent/ios6-agent.c index 238d53acdf..1f38a7942f 100644 --- a/ios6/agent/ios6-agent.c +++ b/ios6/agent/ios6-agent.c @@ -734,6 +734,42 @@ static void cmd_touch(int fd, char **a, int n) reply_str(fd,rc==0,"touch %d %s",rc,bundle); } +static const char *front_target(char *bundle, size_t len) +{ + load_sbs(); bundle[0]=0; + CFStringRef front=p_SBSCopyFrontmostApplicationDisplayIdentifier ? p_SBSCopyFrontmostApplicationDisplayIdentifier() : NULL; + if(front) { cfstr(front,bundle,len); CFRelease(front); } + return bundle[0] && strcmp(bundle,"com.apple.springboard") ? bundle : NULL; +} + +/* text : une touche par unité UTF-16, vers l'app au premier plan. */ +static void cmd_text(int fd, char **a, int n) +{ + if (n != 1) { reply_str(fd, 0, "text chaine"); return; } + CFStringRef s = CFStringCreateWithCString(NULL, a[0], kCFStringEncodingUTF8); + if (!s) { reply_str(fd, 0, "UTF-8 invalide"); return; } + char bundle[256]; const char *target = front_target(bundle, sizeof bundle); + int rc = 0; + for (CFIndex i = 0; i < CFStringGetLength(s) && !rc; i++) rc = send_key_event(CFStringGetCharacterAtIndex(s, i), target); + CFRelease(s); + reply_str(fd, rc == 0, "text %d %s", rc, bundle); +} + +/* key return|backspace|tab|0xNNNN */ +static void cmd_key(int fd, char **a, int n) +{ + static const struct { const char *name; UniChar code; } keys[] = { {"return", '\n'}, {"backspace", 0x08}, {"tab", '\t'} }; + if (n < 1 || n > 3) { reply_str(fd, 0, "key return|backspace|tab|0xNNNN [sequence DdUu [keycode]]"); return; } + long code = -1; + for (size_t i = 0; i < sizeof keys / sizeof *keys; i++) if (!strcmp(a[0], keys[i].name)) code = keys[i].code; + if (code < 0 && !strncmp(a[0], "0x", 2)) { char *end; code = strtol(a[0] + 2, &end, 16); if (*end || code <= 0 || code > 0xffff) code = -1; } + if (code < 0) { reply_str(fd, 0, "touche inconnue : %s", a[0]); return; } + char bundle[256]; const char *target = front_target(bundle, sizeof bundle); + int rc = n > 1 ? send_key_sequence((UniChar)code, target, a[1], n > 2 ? (uint16_t)strtol(a[2], NULL, 0) : 0) + : send_key_event((UniChar)code, target); + reply_str(fd, rc == 0, "key %d %s", rc, bundle); +} + static void handle(int fd, char *line) { char *args[64]; @@ -745,6 +781,8 @@ static void handle(int fd, char *line) int na = n - 1; if (!strcmp(cmd, "ping")) reply_str(fd, 1, "emuagent %s", VERSION); else if (!strcmp(cmd, "touch")) cmd_touch(fd,a,na); + else if (!strcmp(cmd, "text")) cmd_text(fd,a,na); + else if (!strcmp(cmd, "key")) cmd_key(fd,a,na); else if (!strcmp(cmd, "home")) { void *h=dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices",RTLD_NOW); int (*fn)(void)=h?dlsym(h,"SBSSuspendFrontmostApplication"):NULL; @@ -778,7 +816,7 @@ static void handle(int fd, char *line) else if (!strcmp(cmd, "net")) cmd_net(fd); else if (!strcmp(cmd, "respring")) reply_str(fd, kill_by_name("SpringBoard", SIGTERM) > 0, "SpringBoard relancé"); else if (!strcmp(cmd, "sync")) { sync(); reply_str(fd, 1, "ok"); } - else reply_str(fd, 0, "commande inconnue : %s (ping exec launch fetch sc routes settime screen wake front apps put get ls rm mkdir ps kill net respring sync)", cmd); + else reply_str(fd, 0, "commande inconnue : %s (ping touch text key exec launch fetch sc routes settime screen wake front apps put get ls rm mkdir ps kill net respring sync)", cmd); } static void serve_client(int fd) diff --git a/ios6/macos/iOS6Simulator.swift b/ios6/macos/iOS6Simulator.swift new file mode 100644 index 0000000000..175c12c8c5 --- /dev/null +++ b/ios6/macos/iOS6Simulator.swift @@ -0,0 +1,291 @@ +// Fenêtre macOS pour la VM iOS 6 : affiche l'écran via le VNC local de QEMU +// et transmet la souris et le clavier à l'agent invité (127.0.0.1:7796). +import AppKit + +let vncPort: UInt16 = 5906 +let agentPort: UInt16 = 7796 + +// MARK: - Sockets bloquantes + +final class Socket { + let fd: Int32 + init?(port: UInt16) { + fd = socket(AF_INET, SOCK_STREAM, 0) + if fd < 0 { return nil } + var one: Int32 = 1 + setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, socklen_t(MemoryLayout.size)) + var addr = sockaddr_in() + addr.sin_family = sa_family_t(AF_INET) + addr.sin_port = port.bigEndian + addr.sin_addr.s_addr = inet_addr("127.0.0.1") + let ok = withUnsafePointer(to: &addr) { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { + connect(fd, $0, socklen_t(MemoryLayout.size)) == 0 + } + } + if !ok { close(fd); return nil } + } + deinit { close(fd) } + func read(_ n: Int) throws -> [UInt8] { + var out = [UInt8](repeating: 0, count: n) + var got = 0 + while got < n { + let r = out.withUnsafeMutableBytes { recv(fd, $0.baseAddress! + got, n - got, 0) } + if r <= 0 { throw POSIXError(.ECONNRESET) } + got += r + } + return out + } + func readInto(_ buffer: UnsafeMutableRawPointer, _ n: Int) throws { + var got = 0 + while got < n { + let r = recv(fd, buffer + got, n - got, 0) + if r <= 0 { throw POSIXError(.ECONNRESET) } + got += r + } + } + func write(_ bytes: [UInt8]) throws { + var sent = 0 + while sent < bytes.count { + let r = bytes.withUnsafeBytes { send(fd, $0.baseAddress! + sent, bytes.count - sent, 0) } + if r <= 0 { throw POSIXError(.EPIPE) } + sent += r + } + } + func readLine() throws -> String { + var line = [UInt8]() + while true { + let c = try read(1)[0] + if c == 10 { return String(decoding: line, as: UTF8.self) } + line.append(c) + } + } +} + +func u16(_ b: [UInt8], _ i: Int) -> Int { Int(b[i]) << 8 | Int(b[i + 1]) } +func u32(_ b: [UInt8], _ i: Int) -> Int { u16(b, i) << 16 | u16(b, i + 2) } +func be16(_ v: Int) -> [UInt8] { [UInt8(v >> 8 & 0xff), UInt8(v & 0xff)] } + +// MARK: - Agent invité + +final class Agent { + private let queue = DispatchQueue(label: "agent") + private var warnedKeyboard = false + /// Envoie une commande de l'agent ; les gestes sont exécutés dans l'ordre. + func send(_ fields: [String]) { + queue.async { + guard let s = Socket(port: agentPort) else { return } + do { + try s.write(Array((fields.joined(separator: "\t") + "\n").utf8)) + let header = try s.readLine().split(separator: " ", maxSplits: 2).map(String.init) + let size = header.count > 1 ? Int(header[1]) ?? 0 : 0 + let body = String(decoding: try s.read(size), as: UTF8.self) + if header.first == "ERR" { + if body.contains("commande inconnue") { + if !self.warnedKeyboard { self.warnedKeyboard = true; NSLog("Clavier indisponible : l'agent n'a pas encore text/key") } + } else { NSLog("Agent : %@", body) } + } + } catch {} + } + } +} + +// MARK: - Client VNC (encodage brut) + +final class VNCClient { + var onFrame: ((CGImage?) -> Void)? + private var width = 0, height = 0 + private var pixels = [UInt8]() + + func run() { + Thread.detachNewThread { [weak self] in + while let self { + try? self.session() + DispatchQueue.main.async { self.onFrame?(nil) } + Thread.sleep(forTimeInterval: 1) + } + } + } + + private func session() throws { + guard let s = Socket(port: vncPort) else { return } + _ = try s.read(12) + try s.write(Array("RFB 003.008\n".utf8)) + let count = Int(try s.read(1)[0]) + guard count > 0, try s.read(count).contains(1) else { return } + try s.write([1]) + guard try s.read(4) == [0, 0, 0, 0] else { return } + try s.write([1]) // connexion partagée + let initMsg = try s.read(24) + resize(u16(initMsg, 0), u16(initMsg, 2)) + _ = try s.read(u32(initMsg, 20)) + // 32 bpp, profondeur 24, little-endian, rouge << 16 : octets BGRX. + try s.write([0, 0, 0, 0, 32, 24, 0, 1, 0, 255, 0, 255, 0, 255, 16, 8, 0, 0, 0, 0]) + try s.write([2, 0] + be16(2) + [0, 0, 0, 0] + [0xff, 0xff, 0xff, 0x21]) // brut, DesktopSize + try request(s, incremental: false) + while true { + let type = try s.read(1)[0] + switch type { + case 0: + let head = try s.read(3) + for _ in 0.. Bool { true } + + func show(_ image: CGImage?) { + wantsLayer = true + layer?.backgroundColor = NSColor.black.cgColor + layer?.contentsGravity = .resize + layer?.magnificationFilter = .nearest + layer?.contents = image + if let image { screenSize = CGSize(width: image.width, height: image.height) } + } + + private func guestPoint(_ event: NSEvent) -> CGPoint { + let p = convert(event.locationInWindow, from: nil) + let x = min(max(p.x / bounds.width * screenSize.width, 0), screenSize.width - 1) + let y = min(max(p.y / bounds.height * screenSize.height, 0), screenSize.height - 1) + return CGPoint(x: x.rounded(), y: y.rounded()) + } + + override func mouseDown(with event: NSEvent) { press = (guestPoint(event), Date()) } + + override func mouseUp(with event: NSEvent) { + guard let (start, time) = press else { return } + press = nil + var end = guestPoint(event) + var ms = Int(Date().timeIntervalSince(time) * 1000) + if abs(end.x - start.x) < 6 && abs(end.y - start.y) < 6 { end = start } + ms = min(max(ms, 96), 5000) + agent.send(["touch", "\(Int(start.x))", "\(Int(start.y))", "\(Int(end.x))", "\(Int(end.y))", "\(ms)"]) + } + + override func rightMouseDown(with event: NSEvent) { agent.send(["home"]) } + + override func keyDown(with event: NSEvent) { + switch event.keyCode { + case 53: agent.send(["home"]) // Échap + case 36, 76: agent.send(["key", "return"]) + case 51: agent.send(["key", "backspace"]) + case 48: agent.send(["key", "tab"]) + default: + guard let text = event.characters, !text.isEmpty, + !event.modifierFlags.contains(.command), + text.unicodeScalars.allSatisfy({ $0.value >= 0x20 && $0.value != 0x7f && $0.value < 0xf700 }) + else { return super.keyDown(with: event) } + agent.send(["text", text]) + } + } +} + +// MARK: - Application + +final class AppDelegate: NSObject, NSApplicationDelegate { + var window: NSWindow! + let vnc = VNCClient() + + func applicationDidFinishLaunching(_ note: Notification) { + let view = ScreenView(frame: NSRect(x: 0, y: 0, width: 320, height: 480)) + view.show(nil) + window = NSWindow(contentRect: view.frame, styleMask: [.titled, .closable, .miniaturizable, .resizable], + backing: .buffered, defer: false) + window.title = "iOS 6" + window.contentView = view + window.contentAspectRatio = NSSize(width: 320, height: 480) + window.setContentSize(NSSize(width: 320, height: 480)) + window.center() + window.makeKeyAndOrderFront(nil) + window.makeFirstResponder(view) + buildMenu(view) + vnc.onFrame = { [weak view] image in view?.show(image) } + vnc.run() + NSApp.activate(ignoringOtherApps: true) + } + + func applicationShouldTerminateAfterLastWindowClosed(_ app: NSApplication) -> Bool { true } + + private func buildMenu(_ view: ScreenView) { + let main = NSMenu() + let appItem = NSMenuItem(); main.addItem(appItem) + let appMenu = NSMenu() + appMenu.addItem(withTitle: "Quitter", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q") + appItem.submenu = appMenu + let deviceItem = NSMenuItem(); main.addItem(deviceItem) + let device = NSMenu(title: "Appareil") + for (title, key, action) in [("Home", "h", #selector(home)), ("Taille réelle", "1", #selector(size1)), ("Taille ×2", "2", #selector(size2))] { + let item = device.addItem(withTitle: title, action: action, keyEquivalent: key) + item.keyEquivalentModifierMask = [.command, .shift] + item.target = self + } + deviceItem.submenu = device + NSApp.mainMenu = main + } + + @objc func home() { (window.contentView as? ScreenView)?.agent.send(["home"]) } + @objc func size1() { window.setContentSize(NSSize(width: 320, height: 480)) } + @objc func size2() { window.setContentSize(NSSize(width: 640, height: 960)) } +} + +let app = NSApplication.shared +let delegate = AppDelegate() +app.delegate = delegate +app.setActivationPolicy(.regular) +app.run() diff --git a/ios6/src/session_input.h b/ios6/src/session_input.h index 098377808e..3123448a8c 100644 --- a/ios6/src/session_input.h +++ b/ios6/src/session_input.h @@ -40,6 +40,53 @@ static int send_touch_gesture(float x1, float y1, float x2, float y2, unsigned d return 0; } +/* Touche clavier matériel (GSEventKeyDown 10 / KeyUp 11). GSEventCreateKeyEvent + * exige le clavier d'un processus UIKit : l'enregistrement est donc construit + * à la main. GSHardwareKeyInfo (144 octets) suit l'en-tête de 52 octets : + * keyCode, charIgnoringModifier, charCode, charSet, puis longueur et UTF-16 + * des caractères (32 max), même chose pour les caractères non modifiés. */ +static int send_key_sequence(UniChar unicode, const char *bundle, const char *seq, uint16_t keycode); +static int send_key_event(UniChar unicode, const char *bundle) +{ + /* Vérifié sous iOS 6.1.6 : UIKit insère le caractère porté par chaque + * événement, et seul un relâchement avec caractère arrête la répétition. + * L'appui est donc vide et le relâchement porte le caractère. */ + return send_key_sequence(unicode, bundle, "dU", 0); +} + +/* seq : D/d appui avec/sans caractère, U/u relâchement avec/sans caractère. */ +static int send_key_sequence(UniChar unicode, const char *bundle, const char *seq, uint16_t keycode) +{ + void *gs = dlopen("/System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices", RTLD_NOW); + if (!gs) return -1; + void (*send)(const void *) = dlsym(gs, "GSSendSystemEvent"); + void (*send_port)(const void *, unsigned) = dlsym(gs, "GSSendEvent"); + unsigned (*copy_port)(const char *) = dlsym(gs, "GSCopyPurpleNamedPort"); + uint64_t (*timestamp)(void) = dlsym(gs, "GSCurrentEventTimestamp"); + if (!send || !timestamp) { dlclose(gs); return -1; } + unsigned port = bundle && copy_port ? copy_port(bundle) : 0; + if (bundle && (!port || !send_port)) { dlclose(gs); return -2; } + for (const char *step = seq; *step; step++) { + uint32_t packet[49] = {0}; /* 52 + 144 octets */ + unsigned char *bytes = (void *)packet; + uint64_t time = timestamp(); + UniChar info[4] = {keycode, unicode, unicode, 0}; + uint16_t one = *step == 'D' || *step == 'U'; + packet[0] = *step == 'D' || *step == 'd' ? 10 : 11; + memcpy(bytes + 28, &time, sizeof(time)); + packet[12] = 144; /* infoSize */ + memcpy(bytes + 52, info, sizeof(info)); + memcpy(bytes + 60, &one, 2); /* characters_length */ + memcpy(bytes + 62, &unicode, 2); /* characters[0] */ + memcpy(bytes + 126, &one, 2); /* unmodified_characters_length */ + memcpy(bytes + 128, &unicode, 2); + if (port) send_port(packet, port); else send(packet); + usleep(50000); /* 20 ms : caractères perdus près des espaces */ + } + dlclose(gs); + return 0; +} + static void send_unlock_swipe(void) { send_touch_gesture(50, 433, 245, 433, 800, NULL); diff --git a/ios6/tools/build.sh b/ios6/tools/build.sh index d37afdbf22..0d4e1bb4a0 100755 --- a/ios6/tools/build.sh +++ b/ios6/tools/build.sh @@ -18,6 +18,7 @@ cd build --target-list=arm-softmmu --disable-docs --disable-werror \ --disable-gtk --disable-sdl --disable-slirp --disable-capstone \ --disable-tools --disable-guest-agent --disable-debug-info \ + --extra-cflags="$(pkg-config --cflags openssl 2>/dev/null)" \ --extra-ldflags="$(pkg-config --libs-only-L openssl 2>/dev/null) -lcrypto" > ../evidence/configure.log 2>&1 ninja -j"${IOS6_BUILD_JOBS:-4}" qemu-system-arm > ../evidence/build.log 2>&1 cd .. diff --git a/ios6/tools/build_agent.sh b/ios6/tools/build_agent.sh index 60724cd9a5..3c60b930cc 100755 --- a/ios6/tools/build_agent.sh +++ b/ios6/tools/build_agent.sh @@ -3,6 +3,16 @@ set -euo pipefail sim_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) cd "$sim_root" mkdir -p build -/opt/cctools/bin/arm-apple-darwin9-gcc -isysroot /opt/theos/sdks/iPhoneOS2.2.1.sdk -miphoneos-version-min=2.2.1 -std=gnu99 -O2 agent/ios6-agent.c -framework CoreFoundation -framework SystemConfiguration -o build/ios6-agent -/opt/cctools/bin/ldid -Sagent/entitlements.plist build/ios6-agent +if [[ -x /opt/cctools/bin/arm-apple-darwin9-gcc ]]; then + /opt/cctools/bin/arm-apple-darwin9-gcc -isysroot /opt/theos/sdks/iPhoneOS2.2.1.sdk -miphoneos-version-min=2.2.1 -std=gnu99 -O2 agent/ios6-agent.c -framework CoreFoundation -framework SystemConfiguration -o build/ios6-agent + /opt/cctools/bin/ldid -Sagent/entitlements.plist build/ios6-agent +else + # macOS : clang de Xcode, mêmes contraintes que tools/build_app.py + # (cible 5.1 pour LC_UNIXTHREAD, code ARM seulement). + sdk=${IOS6_SDK:-$sim_root/references/sdk/iPhoneOS6.0.sdk} + [[ -f $sdk/SDKSettings.plist ]] || sdk=${THEOS:-$HOME/theos}/sdks/iPhoneOS6.1.sdk + xcrun clang -target armv7-apple-ios5.1 -marm -isysroot "$sdk" -std=gnu99 -O2 -Wno-incompatible-pointer-types-discards-qualifiers \ + agent/ios6-agent.c -framework CoreFoundation -framework SystemConfiguration -o build/ios6-agent + ldid -Sagent/entitlements.plist build/ios6-agent +fi bash tools/build_netd.sh diff --git a/ios6/tools/build_app.py b/ios6/tools/build_app.py index 9d45ae931d..1f24027625 100644 --- a/ios6/tools/build_app.py +++ b/ios6/tools/build_app.py @@ -1,15 +1,29 @@ #!/usr/bin/env python3 """Compile un projet Objective-C simple (.m) en bundle ARM pour la VM iOS 6.""" -import argparse,os,pathlib,plistlib,subprocess +import argparse,os,pathlib,plistlib,shutil,subprocess ROOT=pathlib.Path(__file__).resolve().parents[1] +CCTOOLS=pathlib.Path('/opt/cctools/bin') +def default_sdk(): + if os.environ.get('IOS6_SDK'): return os.environ['IOS6_SDK'] + for sdk in (ROOT/'references/sdk/iPhoneOS6.0.sdk',pathlib.Path(os.environ.get('THEOS',pathlib.Path.home()/'theos'))/'sdks/iPhoneOS6.1.sdk'): + if (sdk/'SDKSettings.plist').is_file(): return str(sdk) + return str(ROOT/'references/sdk/iPhoneOS6.0.sdk') +def compiler(): + """GCC cctools sous Linux, clang de Xcode sous macOS.""" + if (CCTOOLS/'arm-apple-darwin9-gcc').exists(): return [str(CCTOOLS/'arm-apple-darwin9-gcc'),'-arch','armv7','-miphoneos-version-min=6.0'],str(CCTOOLS/'ldid') + # Le ld récent ne pose plus le bit Thumb sur le point d'entrée LC_MAIN ni sur + # les IMP Objective-C : sous iOS 6, le saut part en mode ARM (SIGBUS + # EXC_ARM_DA_ALIGN). Cible 5.1 (crt1.o, LC_UNIXTHREAD) et code ARM seulement. + return ['xcrun','clang','-target','armv7-apple-ios5.1','-marm'],shutil.which('ldid') or 'ldid' def main(): - p=argparse.ArgumentParser(description=__doc__);p.add_argument('project',type=pathlib.Path,nargs='?',default=ROOT/'apps/SimulatorLab');p.add_argument('--bundle-id');p.add_argument('--sdk',default=os.environ.get('IOS6_SDK',str(ROOT/'references/sdk/iPhoneOS6.0.sdk')));a=p.parse_args() + p=argparse.ArgumentParser(description=__doc__);p.add_argument('project',type=pathlib.Path,nargs='?',default=ROOT/'apps/SimulatorLab');p.add_argument('--bundle-id');p.add_argument('--sdk',default=default_sdk());a=p.parse_args() if not pathlib.Path(a.sdk,'SDKSettings.plist').is_file(): p.error('SDK iOS absent : exécuter tools/prepare_sdk.py ou fournir --sdk') name=a.project.resolve().name;out=ROOT/'build/apps'/(name+'.app');out.mkdir(parents=True,exist_ok=True) a.bundle_id=a.bundle_id or 'org.ios6emu.'+name sources=sorted(a.project.glob('*.m')) if not sources: p.error('Aucune source Objective-C') - subprocess.run(['/opt/cctools/bin/arm-apple-darwin9-gcc','-isysroot',a.sdk,'-miphoneos-version-min=6.0','-arch','armv7','-fblocks','-std=gnu99','-O2','-Wall','-Wextra',*map(str,sources),'-framework','UIKit','-framework','Foundation','-framework','CoreGraphics','-o',str(out/name)],check=True) + cc,ldid=compiler() + subprocess.run([*cc,'-isysroot',a.sdk,'-fblocks','-std=gnu99','-O2','-Wall','-Wextra',*map(str,sources),'-framework','UIKit','-framework','Foundation','-framework','CoreGraphics','-o',str(out/name)],check=True) (out/'Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier':a.bundle_id,'CFBundleExecutable':name,'CFBundleName':name,'CFBundleDisplayName':name,'CFBundlePackageType':'APPL','CFBundleVersion':'1','MinimumOSVersion':'6.0','LSRequiresIPhoneOS':True,'UIDeviceFamily':[1]})) - subprocess.run(['/opt/cctools/bin/ldid','-S',str(out/name)],check=True);print(out) + subprocess.run([ldid,'-S',str(out/name)],check=True);print(out) if __name__=='__main__': main() diff --git a/ios6/tools/build_mac_app.sh b/ios6/tools/build_mac_app.sh new file mode 100644 index 0000000000..daecc7403a --- /dev/null +++ b/ios6/tools/build_mac_app.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Compile la fenêtre macOS de la VM iOS 6 dans build/iOS6 Simulator.app. +set -euo pipefail +sim_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) +app="$sim_root/build/iOS6 Simulator.app" +mkdir -p "$app/Contents/MacOS" +xcrun swiftc -O -o "$app/Contents/MacOS/iOS6Simulator" "$sim_root/macos/iOS6Simulator.swift" +cat > "$app/Contents/Info.plist" <<'PLIST' + + + +CFBundleIdentifierorg.ios6emu.mac +CFBundleNameiOS6 Simulator +CFBundleExecutableiOS6Simulator +CFBundlePackageTypeAPPL +CFBundleShortVersionString1.0 +LSMinimumSystemVersion13.0 +NSHighResolutionCapable + +PLIST +codesign -s - --force "$app" >/dev/null +echo "$app" diff --git a/ios6/tools/build_netd.sh b/ios6/tools/build_netd.sh index 969494806a..8a59c3656c 100755 --- a/ios6/tools/build_netd.sh +++ b/ios6/tools/build_netd.sh @@ -4,5 +4,6 @@ set -euo pipefail sim_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) cd "$sim_root" mkdir -p build -cc -O2 agent/netd.c -o build/ios6-netd.new $(pkg-config --cflags --libs slirp) +cc -O2 agent/netd.c -o build/ios6-netd.new \ + -I"$(pkg-config --variable=includedir slirp)" $(pkg-config --cflags --libs slirp) mv build/ios6-netd.new build/ios6-netd diff --git a/ios6/tools/recover_hfs.py b/ios6/tools/recover_hfs.py index 78d64abdbf..fc18b30846 100644 --- a/ios6/tools/recover_hfs.py +++ b/ios6/tools/recover_hfs.py @@ -11,8 +11,10 @@ import argparse import json import os import pathlib +import shutil import struct import subprocess +import sys def checksum(data): @@ -97,7 +99,12 @@ def recover(source, destination): position = advance(position, used) headers += 1 # Toutes les validations précèdent la création de la copie. - subprocess.run(['cp', '--reflink=auto', '--sparse=always', str(source), str(destination)], check=True) + if sys.platform == 'darwin': + # cp BSD : clone APFS (-c), copie simple si le volume ne le permet pas. + if subprocess.run(['cp', '-c', str(source), str(destination)]).returncode: + shutil.copyfile(source, destination) + else: + subprocess.run(['cp', '--reflink=auto', '--sparse=always', str(source), str(destination)], check=True) with destination.open('r+b') as image: for target, data in writes: image.seek(target) diff --git a/ios6/tools/update_agent.py b/ios6/tools/update_agent.py new file mode 100644 index 0000000000..13fdd47002 --- /dev/null +++ b/ios6/tools/update_agent.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +"""Remplace l'agent invité par build/ios6-agent ; launchd (KeepAlive) le relance. + +Le fichier est écrit de façon atomique puis l'agent courant est arrêté. Lancer +ensuite `bin/ios6 stop` pour conserver la nouvelle version sur le disque. +""" +import time + +from simulator import Agent, ROOT, command + +def main(): + data = (ROOT / 'build/ios6-agent').read_bytes() + with Agent() as agent: + agent.call('put', '/usr/libexec/ios6-agent', '755', len(data), data=data) + agent.call('sync') + # Chaque client est servi par un fils : arrêter le processus lancé par launchd. + main_pid = next(line.split()[0] for line in command('ps').splitlines() + if line.split()[1:] == ['1', '0', 'ios6-agent']) + try: command('kill', main_pid, 15, timeout=5) + except Exception: pass # l'agent s'arrête avant de répondre + deadline = time.monotonic() + 30 + while True: + time.sleep(1) + try: print('agent relancé :', command('ping', timeout=5)); return + except Exception: + if time.monotonic() > deadline: raise SystemExit("L'agent ne répond plus") + +if __name__ == '__main__': main() diff --git a/ios6/tools/vnc_input.py b/ios6/tools/vnc_input.py new file mode 100644 index 0000000000..5a7aeb17dc --- /dev/null +++ b/ios6/tools/vnc_input.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +"""Relais VNC : transmet l'affichage de QEMU et convertit la souris en gestes tactiles. + +Le client VNC se connecte au relais (127.0.0.1:5907 par défaut). L'affichage +et les messages du client passent tels quels vers QEMU (127.0.0.1:5906), sauf +les événements souris et clavier : un appui suivi d'un relâchement du bouton +gauche devient un geste `touch` de l'agent (tap ou glissé, avec sa durée), le +bouton droit et la touche Échap envoient `home`. Les touches imprimables +deviennent `text `, Entrée, Retour arrière et Tab `key `. +""" +import argparse +import queue +import socket +import struct +import sys +import threading +import time + +from simulator import command + +WIDTH, HEIGHT = 320, 480 +KEY_ESCAPE, KEY_HOME = 0xff1b, 0xff50 +SPECIAL_KEYS = {0xff0d: 'return', 0xff8d: 'return', 0xff08: 'backspace', 0xff09: 'tab'} + +def recv_exact(sock, n): + data = bytearray() + while len(data) < n: + chunk = sock.recv(n - len(data)) + if not chunk: raise ConnectionError + data.extend(chunk) + return bytes(data) + +def pipe(src, dst): + try: + while chunk := src.recv(65536): dst.sendall(chunk) + except OSError: pass + finally: + for s in (src, dst): + try: s.shutdown(socket.SHUT_RDWR) + except OSError: pass + +def gestures(actions): + warned = False + while True: + args = actions.get() + try: command(*args) + except Exception as e: + if 'commande inconnue' not in str(e): print('geste refusé :', e, file=sys.stderr, flush=True) + elif not warned: + warned = True + print("clavier indisponible : l'agent invité n'a pas encore les commandes text/key", file=sys.stderr, flush=True) + +def clamp(v, top): return min(max(v, 0), top - 1) + +def client_messages(client, qemu, actions): + """Lit le flux client RFB message par message pour isoler la souris et le clavier.""" + version = recv_exact(client, 12); qemu.sendall(version) + if version != b'RFB 003.003\n': qemu.sendall(recv_exact(client, 1)) # type de sécurité + qemu.sendall(recv_exact(client, 1)) # ClientInit + buttons, press = 0, None + while True: + kind = recv_exact(client, 1)[0] + if kind == 5: + mask, x, y = struct.unpack('>BHH', recv_exact(client, 5)) + x, y = clamp(x, WIDTH), clamp(y, HEIGHT) + if mask & 1 and not buttons & 1: press = (x, y, time.monotonic()) + elif buttons & 1 and not mask & 1 and press: + x0, y0, t0 = press; press = None + ms = int((time.monotonic() - t0) * 1000) + if abs(x - x0) < 6 and abs(y - y0) < 6: x, y, ms = x0, y0, max(ms, 96) + actions.put(('touch', x0, y0, x, y, min(max(ms, 96), 5000))) + if mask & 4 and not buttons & 4: actions.put(('home',)) + buttons = mask + continue + if kind == 4: + down, key = struct.unpack('>B2xI', recv_exact(client, 7)) + if not down: continue + if key in (KEY_ESCAPE, KEY_HOME): actions.put(('home',)) + elif key in SPECIAL_KEYS: actions.put(('key', SPECIAL_KEYS[key])) + elif 0x20 <= key < 0x100 and key != 0x7f: actions.put(('text', chr(key))) + elif key & 0xff000000 == 0x01000000: actions.put(('text', chr(key & 0xffffff))) + continue + if kind == 255: + body = recv_exact(client, 11) # événement clavier étendu QEMU, ignoré + if body[0] != 0: raise ConnectionError(f'message QEMU {body[0]} non géré') + continue + if kind == 0: body = recv_exact(client, 19) + elif kind == 2: + head = recv_exact(client, 3); body = head + recv_exact(client, 4 * struct.unpack('>xH', head)[0]) + elif kind == 3: body = recv_exact(client, 9) + elif kind == 6: + head = recv_exact(client, 7); body = head + recv_exact(client, struct.unpack('>3xI', head)[0]) + elif kind == 150: body = recv_exact(client, 9) + elif kind == 248: + head = recv_exact(client, 7); body = head + recv_exact(client, 16 * head[4]) + elif kind == 250: + head = recv_exact(client, 8); body = head + recv_exact(client, head[7]) + else: raise ConnectionError(f'message client {kind} non géré') + qemu.sendall(bytes([kind]) + body) + +def serve(client, target, actions): + with client, socket.create_connection(target) as qemu: + threading.Thread(target=pipe, args=(qemu, client), daemon=True).start() + try: client_messages(client, qemu, actions) + except (ConnectionError, OSError) as e: + if str(e): print('connexion fermée :', e, file=sys.stderr, flush=True) + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('--port', type=int, default=5907) + p.add_argument('--qemu-port', type=int, default=5906) + a = p.parse_args() + actions = queue.Queue() + threading.Thread(target=gestures, args=(actions,), daemon=True).start() + server = socket.create_server(('127.0.0.1', a.port)) + print(f'Relais VNC tactile : vnc://127.0.0.1:{a.port}', flush=True) + while True: + client, _ = server.accept() + threading.Thread(target=serve, args=(client, ('127.0.0.1', a.qemu_port), actions), daemon=True).start() + +if __name__ == '__main__': main() -- 2.54.0

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