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..1f38a7942f --- /dev/null +++ b/ios6/agent/ios6-agent.c @@ -0,0 +1,942 @@ +/* + * 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 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]; + 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, "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; + 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 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) +{ + 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/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/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 0000000000..c758387f05 Binary files /dev/null and b/ios6/results/20261003-native-apps/native-app.png differ 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 0000000000..3b20bc7156 Binary files /dev/null and b/ios6/results/20261003-springboard/springboard.png differ diff --git a/ios6/results/20261003-springboard/validation-display.txt b/ios6/results/20261003-springboard/validation-display.txt new file mode 100644 index 0000000000..a9ebc52af8 --- /dev/null +++ b/ios6/results/20261003-springboard/validation-display.txt @@ -0,0 +1 @@ +OK : scanout DART, pages disjointes, composition alpha, limites des plans et acquittement VBlank. diff --git a/ios6/results/20261003-springboard/validation.txt b/ios6/results/20261003-springboard/validation.txt new file mode 100644 index 0000000000..5cc8ff3c85 --- /dev/null +++ b/ios6/results/20261003-springboard/validation.txt @@ -0,0 +1,2 @@ +OK : empreintes, DeviceTree, noyau inchangé, BSS, mémoire réservée, RAMDisk, NVRAM et source QEMU. +OK : quatre adaptations noyau, RAM relocalisée, disque HFSX indépendant, panneau LCD et framebuffer. diff --git a/ios6/results/20261003-springboard/verification.json b/ios6/results/20261003-springboard/verification.json new file mode 100644 index 0000000000..8402550617 --- /dev/null +++ b/ios6/results/20261003-springboard/verification.json @@ -0,0 +1,19 @@ +{ + "firmware": "iPhone2,1 iOS 6.1.6 10B500", + "capture_source": "evidence/probe-20261003T125116-664879Z/screen-023.png", + "capture_sha256": "ec37647301c0f4a5a80f4e47a8369a8043dededbdc05c8b8b66eced0ff646043", + "verification": "Écran d’accueil et dock inspectés visuellement ; capture QMP du framebuffer invité.", + "original_iphoneos2_unchanged": true, + "limitations": [ + "Geste de démarrage automatique via GraphicsServices", + "Pas encore de tactile commandé par la souris hôte", + "Pas de réseau, modem, audio ou accélération SGX", + "Trousseau de session RAM expérimental ; firmware non activé" + ], + "fresh_boot_reproduced": true, + "first_home_capture_seconds": 220, + "screen_lock_status": { + "locked": false, + "passcode": false + } +} diff --git a/ios6/src/keybag_init.c b/ios6/src/keybag_init.c new file mode 100644 index 0000000000..c03e30b4c2 --- /dev/null +++ b/ios6/src/keybag_init.c @@ -0,0 +1,126 @@ +/* Provisionnement expérimental du volume vierge ; exécuté uniquement dans la VM. */ +#include +#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..3123448a8c --- /dev/null +++ b/ios6/src/session_input.h @@ -0,0 +1,93 @@ +/* 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; +} + +/* 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 new file mode 100755 index 0000000000..0d4e1bb4a0 --- /dev/null +++ b/ios6/tools/build.sh @@ -0,0 +1,26 @@ +#!/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-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 .. +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..3c60b930cc --- /dev/null +++ b/ios6/tools/build_agent.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail +sim_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) +cd "$sim_root" +mkdir -p build +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 new file mode 100644 index 0000000000..1f24027625 --- /dev/null +++ b/ios6/tools/build_app.py @@ -0,0 +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,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=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') + 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([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 new file mode 100755 index 0000000000..8a59c3656c --- /dev/null +++ b/ios6/tools/build_netd.sh @@ -0,0 +1,9 @@ +#!/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 \ + -I"$(pkg-config --variable=includedir slirp)" $(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..fc18b30846 --- /dev/null +++ b/ios6/tools/recover_hfs.py @@ -0,0 +1,135 @@ +#!/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 shutil +import struct +import subprocess +import sys + + +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. + 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) + 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() deadline: raise SystemExit("L'agent ne répond plus") + +if __name__ == '__main__': main() diff --git a/ios6/tools/verify.py b/ios6/tools/verify.py new file mode 100644 index 0000000000..d108f4b4b1 --- /dev/null +++ b/ios6/tools/verify.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +"""Contrôles des vrais artefacts Apple et du contrat de chargement du prototype.""" +import hashlib +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' +EXPECTED = { + 'Kernelcache.dec': 'ec4787ac012567f9c10ba2d5ab611058545bce7e17cd95ef310eba94bfca9b78', + 'DeviceTree.dec': '6608eba10d4a1086a4d3f5d299b8b9582bcb27262206152ea4029ca1a25718ad', + 'RestoreRamdisk.dec': 'e5c7eff45a0e36b7f6fdda9246bc8c0ff0561d4cedd615035f3ebe23c3d235e8', +} + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--rootfs', action='store_true', help='Vérifie aussi le profil système complet') + options = parser.parse_args() + for name, digest in EXPECTED.items(): + assert hashlib.sha256((FW / name).read_bytes()).hexdigest() == digest, name + original = (FW / 'DeviceTree.dec').read_bytes() + tree, consumed = devicetree(original) + assert encode_tree(tree) == original[:consumed] + kernel = (FW / 'Kernelcache.dec').read_bytes() + image = (FW / 'kernel.bin').read_bytes() + info = macho(kernel) + assert info['entry'] == 0x80086084 + for s in info['segments']: + start = s['vmaddr'] - 0x80001000 + assert image[start:start + s['filesize']] == kernel[s['fileoff']:s['fileoff'] + s['filesize']] + assert not any(image[start + s['filesize']:start + s['vmsize']]) + boot = (FW / 'boot-data.bin').read_bytes() + assert struct.unpack_from('= 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('`, 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()