Compare commits

..
2 Commits
Author SHA1 Message Date
Victor Bodinaud 2ece6aadbc Faire tourner le simulateur iOS 6 sur macOS, avec souris et clavier
- Compilation sur macOS : en-têtes OpenSSL et libslirp de Homebrew, apps et
  agent compilés avec le clang de Xcode (cible 5.1, code ARM : le ld récent
  ne pose plus le bit Thumb sur LC_MAIN ni sur les IMP Objective-C).
- Sauvegarde du disque : copie par clone APFS (cp -c) au lieu des options GNU.
- Agent : commandes text et key (événements GS clavier matériel construits à
  la main, appui vide puis relâchement portant le caractère).
- Fenêtre macOS native (macos/, tools/build_mac_app.sh) : affichage via le VNC
  local, souris convertie en gestes, clavier transmis à l'agent.
- tools/update_agent.py remplace l'agent à chaud ; tools/vnc_input.py ajoute
  la souris et le clavier à un client VNC classique.
2026-10-05 11:49:27 +02:00
Workbench IAandClaude Opus 5.5 525f410046 Ajouter le modèle iPhone 3GS ios6-n88 et le simulateur iOS 6
- hw/arm/ios6_n88.c : machine ios6-n88 (Cortex-A8, 256 Mio, PL192,
  UART, affichage logiciel) qui démarre iOS 6.1.6 (10B500) jusqu'à
  SpringBoard ; compilée sous CONFIG_IPOD_TOUCH_2G.
- ios6/ : outils de développement natif (bin/ios6, agent invité root,
  relais PPP/libslirp netd, app SimulatorLab, smoke_test.py), préparation
  du firmware et du disque, preuves de validation dans ios6/results/.
- tools/build.sh compile ce dépôt dans ios6/build sans toucher au build/
  de l'émulateur iPod ; procédure macOS dans ios6/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:11:16 +00:00
62 changed files with 7922 additions and 0 deletions

No files matched your search

+5
View File
@@ -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.
+471
View File
@@ -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)
+2
View File
@@ -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'))
+7
View File
@@ -0,0 +1,7 @@
.venv/
build/
firmware/
references/
evidence/
__pycache__/
run/
+39
View File
@@ -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.
+377
View File
@@ -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/<identifiant>.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-<date>/` 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.
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>com.apple.springboard.launchapplications</key><true/>
<key>com.apple.backboardd.launchapplications</key><true/>
</dict></plist>
+942
View File
@@ -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 <n> [info]\n" ou
* "ERR <n>\n", suivi de n octets. Pour "put", n octets de contenu suivent la ligne de requête.
*/
#include <CoreFoundation/CoreFoundation.h>
/* 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 <arpa/inet.h>
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <net/if.h>
/* 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 <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <spawn.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <termios.h>
#include <notify.h>
#include <unistd.h>
#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/<id> */
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: exec<TAB>programme<TAB>args..."); 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: launch<TAB>bundle-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);
}
/* fetch<TAB>http://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: fetch<TAB>http://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[<TAB>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: put<TAB>chemin<TAB>mode octal<TAB>taille + 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: get<TAB>chemin"); 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: kill<TAB>pid|nom[<TAB>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 <chaîne UTF-8> : 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);
}
}
}
}
+657
View File
@@ -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 <arpa/inet.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <slirp/libslirp.h>
#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);
}
}
+42
View File
@@ -0,0 +1,42 @@
#import <UIKit/UIKit.h>
@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 <UIApplicationDelegate> { 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; }
Executable
+4
View File
@@ -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" "$@"
+291
View File
@@ -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<Int32>.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<sockaddr_in>.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..<u16(head, 1) {
let r = try s.read(12)
let x = u16(r, 0), y = u16(r, 2), w = u16(r, 4), h = u16(r, 6)
let encoding = Int32(bitPattern: UInt32(u32(r, 8)))
if encoding == -223 { resize(w, h); continue }
guard encoding == 0 else { return }
try readRect(s, x, y, w, h)
}
publish()
try request(s, incremental: true)
case 1:
let head = try s.read(5)
_ = try s.read(6 * u16(head, 3))
case 2: break
case 3:
let head = try s.read(7)
_ = try s.read(u32(head, 3))
default: return
}
}
}
private func resize(_ w: Int, _ h: Int) {
width = w; height = h
pixels = [UInt8](repeating: 0, count: w * h * 4)
}
private func readRect(_ s: Socket, _ x: Int, _ y: Int, _ w: Int, _ h: Int) throws {
guard x + w <= width, y + h <= height else { throw POSIXError(.EINVAL) }
try pixels.withUnsafeMutableBytes { buf in
for row in 0..<h {
try s.readInto(buf.baseAddress! + ((y + row) * width + x) * 4, w * 4)
}
}
}
private func request(_ s: Socket, incremental: Bool) throws {
try s.write([3, incremental ? 1 : 0, 0, 0, 0, 0] + be16(width) + be16(height))
}
private func publish() {
let data = Data(pixels) as CFData
guard let provider = CGDataProvider(data: data) else { return }
let info = CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue)
let image = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32,
bytesPerRow: width * 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: info,
provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)
DispatchQueue.main.async { self.onFrame?(image) }
}
}
// MARK: - Vue de l'écran
final class ScreenView: NSView {
let agent = Agent()
var screenSize = CGSize(width: 320, height: 480)
private var press: (CGPoint, Date)?
override var isFlipped: Bool { true }
override var acceptsFirstResponder: Bool { true }
override func acceptsFirstMouse(for event: NSEvent?) -> 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()
+3
View File
@@ -0,0 +1,3 @@
meson>=1.0,<2
distlib>=0.3,<1
capstone>=5,<6
@@ -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"
}
@@ -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"
]
@@ -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"
}
}
}
@@ -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."
}
@@ -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
}
+955
View File
@@ -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
@@ -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"
}
@@ -0,0 +1 @@
OK : empreintes, DeviceTree, noyau inchangé, BSS, mémoire réservée, RAMDisk, NVRAM et source QEMU.
+31
View File
@@ -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
@@ -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"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@@ -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"
]
}
@@ -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"
]
@@ -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
}
}
@@ -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
@@ -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"
}
@@ -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"
]
@@ -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
}
}
@@ -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] <Error>: 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
Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

@@ -0,0 +1 @@
OK : scanout DART, pages disjointes, composition alpha, limites des plans et acquittement VBlank.
@@ -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.
@@ -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
}
}
+126
View File
@@ -0,0 +1,126 @@
/* Provisionnement expérimental du volume vierge ; exécuté uniquement dans la VM. */
#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <CoreFoundation/CoreFoundation.h>
#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();
}
+93
View File
@@ -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);
}
+26
View File
@@ -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.'
+18
View File
@@ -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
+29
View File
@@ -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()
+22
View File
@@ -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'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>CFBundleIdentifier</key><string>org.ios6emu.mac</string>
<key>CFBundleName</key><string>iOS6 Simulator</string>
<key>CFBundleExecutable</key><string>iOS6Simulator</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleShortVersionString</key><string>1.0</string>
<key>LSMinimumSystemVersion</key><string>13.0</string>
<key>NSHighResolutionCapable</key><true/>
</dict></plist>
PLIST
codesign -s - --force "$app" >/dev/null
echo "$app"
+9
View File
@@ -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
+73
View File
@@ -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('<QQQII', b, mo + 32*i) for i in range(mc)]
self.images = []
for i in range(ic):
address, _, _, name, _ = struct.unpack_from('<QQQII', b, io + 32*i)
self.images.append((address, self.string(name)))
self.images.sort()
def string(self, offset):
return self.data[offset:self.data.index(0, offset)].decode(errors='replace')
def offset(self, address):
for base, size, offset, _, _ in self.maps:
if base <= address < base + size:
return offset + address - base
raise ValueError(hex(address))
def resolve(self, address):
base, name = max(i for i in self.images if i[0] <= address)
header = self.offset(base)
cursor = header + 28
symbols = []
for _ in range(struct.unpack_from('<I', self.data, header + 16)[0]):
cmd, size = struct.unpack_from('<II', self.data, cursor)
if cmd == 2:
so, count, st, _ = struct.unpack_from('<4I', self.data, cursor + 8)
for i in range(count):
no, _, _, _, value = struct.unpack_from('<IBBHI', self.data, so + 12*i)
if base <= value <= address:
symbols.append((value, self.string(st + no)))
cursor += size
previous = max(symbols) if symbols else None
named = [s for s in symbols if s[1] != '<redacted>']
return name, previous, max(named) if named else None
def main():
parser = argparse.ArgumentParser()
parser.add_argument('addresses', nargs='+', type=lambda a: int(a, 0))
parser.add_argument('--slide', type=lambda a: int(a, 0), default=0)
parser.add_argument('--bytes', type=int, default=0)
args = parser.parse_args()
cache = Cache()
for runtime in args.addresses:
address = (runtime - args.slide) & ~1
name, previous, named = cache.resolve(address)
print(f'{runtime:#x} -> {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()
+67
View File
@@ -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()
+83
View File
@@ -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('<II', data, offset)
offset += 8
assert nprops < 10000 and nchildren < 10000
props = {}
for _ in range(nprops):
name, size = struct.unpack_from('<32sI', data, offset)
size &= 0x7fffffff
offset += 36
assert offset + size <= len(data)
props[name.rstrip(b'\0').decode()] = data[offset:offset + size]
offset += (size + 3) & ~3
children = []
for _ in range(nchildren):
child, offset = devicetree(data, offset)
children.append(child)
return (props, children), offset
def encode_tree(node):
props, children = node
result = bytearray(struct.pack('<II', len(props), len(children)))
for name, value in props.items():
result += struct.pack('<32sI', name.encode(), len(value))
result += value + b'\0' * (-len(value) % 4)
for child in children:
result += encode_tree(child)
return bytes(result)
def walk(node, parent=''):
props, children = node
path = parent + '/' + props.get('name', b'?').rstrip(b'\0').decode(errors='replace')
yield path, props
for child in children:
yield from walk(child, path)
def macho(data):
magic, cpu, subcpu, kind, ncmds, cmdsize, flags = struct.unpack_from('<7I', data)
assert magic == 0xfeedface and cpu == 12
segments = []
entry = None
offset = 28
for _ in range(ncmds):
cmd, size = struct.unpack_from('<II', data, offset)
assert size >= 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('<II', data, offset + 8)
assert flavor == 1 and count == 17
entry = struct.unpack_from('<I', data, offset + 16 + 15 * 4)[0]
offset += size
assert entry is not None
return dict(entry=entry, segments=segments)
if __name__ == '__main__':
root = pathlib.Path(__file__).resolve().parents[1]
print(json.dumps(macho((root / 'firmware/Kernelcache.dec').read_bytes()), indent=2))
tree, _ = devicetree((root / 'firmware/DeviceTree.dec').read_bytes())
for path, props in walk(tree):
print(path)
for key, value in props.items():
if key in ('name',):
continue
if value and all(32 <= x < 127 or x == 0 for x in value):
formatted = repr(value.rstrip(b'\0').decode())
elif len(value) % 4 == 0 and len(value) <= 128:
formatted = ' '.join(hex(x) for x in struct.unpack('<' + 'I' * (len(value) // 4), value))
else:
formatted = value[:64].hex() + ('...' if len(value) > 64 else '')
print(' ', key, formatted)
+51
View File
@@ -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('<I', data, 16)[0]):
cmd, size = struct.unpack_from('<II', data, offset)
if cmd == 2:
symoff, count, stroff, strsize = struct.unpack_from('<4I', data, offset + 8)
for i in range(count):
name, kind, section, desc, value = struct.unpack_from('<IBBHI', data, symoff + 12 * i)
end = data.find(b'\0', stroff + name, stroff + strsize)
if value and end >= 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()
+43
View File
@@ -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()))
+99
View File
@@ -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('<I', b, 4)[0] == 12
self.maps, self.sections, self.names = [], {}, {}
cursor = 28
symbols = indirect = None
for _ in range(struct.unpack_from('<I', b, 16)[0]):
cmd, size = struct.unpack_from('<II', b, cursor)
if cmd == 1:
_, va, vs, offset, fs, _, _, count, _ = struct.unpack_from('<16s8I', b, cursor + 8)
self.maps.append((va, fs, offset))
for i in range(count):
section = struct.unpack_from('<16s16s9I', b, cursor + 56 + 68*i)
self.sections[section[0].rstrip(b'\0').decode()] = section[2:]
elif cmd == 2:
symbols = struct.unpack_from('<4I', b, cursor + 8)
elif cmd == 11:
indirect = struct.unpack_from('<II', b, cursor + 56)
cursor += size
if symbols and indirect:
so, _, strings, _ = symbols
for section in self.sections.values():
va, size, _, _, _, _, flags, first, stride = section
if flags & 255 != 8:
continue
for i in range(size // stride):
index = struct.unpack_from('<I', b, indirect[0] + 4*(first+i))[0]
name = struct.unpack_from('<I', b, so + 12*index)[0] + strings
self.names[va + i*stride] = self.cstring_offset(name)
if '__objc_classlist' in self.sections:
va, size, *_ = self.sections['__objc_classlist']
for pointer in range(va, va + size, 4):
cls = self.word(pointer)
for obj, prefix in ((cls, '-'), (self.word(cls), '+')):
ro = self.word(obj + 16) & ~3
name = self.string(self.word(ro + 16))
methods = self.word(ro + 20)
if not methods:
continue
stride, count = self.word(methods) & 0xfffc, self.word(methods + 4)
for i in range(count):
method = methods + 8 + i*stride
selector = self.string(self.word(method))
self.names[self.word(method + 8) & ~1] = f'{prefix}[{name} {selector}]'
def offset(self, address):
for va, size, offset in self.maps:
if va <= address < va + size:
return offset + address - va
raise ValueError(f'Adresse non présente dans le fichier : {address:#x}')
def word(self, address):
return struct.unpack_from('<I', self.data, self.offset(address))[0]
def cstring_offset(self, offset):
return self.data[offset:self.data.index(0, offset)].decode(errors='replace')
def string(self, address):
return self.cstring_offset(self.offset(address))
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('image', type=pathlib.Path)
parser.add_argument('addresses', nargs='+', type=lambda x: int(x, 0))
parser.add_argument('--slide', type=lambda x: int(x, 0), default=0)
parser.add_argument('--bytes', type=int, default=0)
args = parser.parse_args()
macho = MachO(args.image)
for runtime in args.addresses:
address = (runtime - args.slide) & ~1
previous = max((a for a in macho.names if a <= address), default=None)
print(f'{runtime:#x} -> {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()
+149
View File
@@ -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('<BBH12s', 0x70, 0, len(nvram) // 16, b'common')
checksum = sum(nvram[:16])
nvram[1] = (checksum & 255) + (checksum >> 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('<HH', 1, 5)
args += u32(0x80000000, pa, 0x0f000000, pa + 0x03000000)
args += u32(pa + 0x0f000000, 0, 320 * 4, 320, 480, 32)
args += u32(0, dt_pa + 0x80000000 - pa, len(dt))
cmd = b'-v debug=0x14e serial=3 rd=md0 keepsyms=1'
if options.rootfs:
# launchctl utilise sinon les plists précompilées du cache xpcd,
# en ignorant les configurations et le job de provisionnement locaux.
cmd += b' amfi_allow_any_signature=1 launchctl_enforce_codesign=0'
assert len(cmd) < 256
args += cmd.ljust(256, b'\0') + u32(0, 0x10000000)
boot = args.ljust(0x4000, b'\0') + dt
output_name = 'boot-data.bin' if options.profile == 'original' else f'boot-{options.profile}.bin'
if options.rootfs:
output_name = f'boot-{options.profile}-rootfs.bin'
(FW / output_name).write_bytes(boot)
report = dict(kernel_entry=hex(info['entry']), physical_entry=hex(pa + 0x86084),
kernel_bytes=len(flat), boot_args_bytes=len(args), devicetree_bytes=len(dt),
kernel_patches=patches, kernel_file=kernel_name, command_line=cmd.decode(), profile=options.profile,
removed_devices=removed, boot_file=output_name)
(ROOT / 'evidence/boot-layout.json').write_text(json.dumps(report, indent=2) + '\n')
print(json.dumps(report, indent=2))
if __name__ == '__main__':
main()
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
"""Crée le disque de développement, en conservant le disque de démonstration."""
import argparse,json,pathlib,plistlib,subprocess
from simulator import ROOT,DISK,pid,lifecycle_lock
from recover_hfs import recover
HFS='/srv/homes/iphoneos2/ipod-emu/xpwn/b/hfs/hfsplus'
def main():
p=argparse.ArgumentParser(description=__doc__);p.add_argument('--checkpoint',type=pathlib.Path);p.add_argument('--reset',action='store_true');a=p.parse_args()
with lifecycle_lock():
if pid('qemu'): p.error('Arrêter la VM avant de préparer le disque')
if DISK.exists() and not a.reset: p.error('Disque existant ; --reset requis pour le remplacer')
subprocess.run(['bash',str(ROOT/'tools/build_agent.sh')],check=True)
temp=DISK.with_name('rootfs-dev-new.hfs');temp.unlink(missing_ok=True)
if a.checkpoint:recover(a.checkpoint,temp)
else:subprocess.run(['cp','--reflink=auto','--sparse=always',str(ROOT/'firmware/rootfs-vm.hfs'),str(temp)],check=True)
config={'Label':'org.ios6-emu.agent','ProgramArguments':['/usr/libexec/ios6-agent'],'RunAtLoad':True,'KeepAlive':True}
local=ROOT/'build/ios6-agent.plist';local.write_bytes(plistlib.dumps(config))
for args in [('add',ROOT/'build/ios6-agent','/usr/libexec/ios6-agent'),('chmod','755','/usr/libexec/ios6-agent'),('add',local,'/System/Library/LaunchDaemons/org.ios6-emu.agent.plist')]:
subprocess.run([HFS,str(temp),*map(str,args)],check=True)
# Conserver l'image précédente tant que la nouvelle n'est pas prête.
temp.replace(DISK)
print(DISK)
if __name__=='__main__':main()
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""Télécharge et extrait le firmware Apple de référence, avec vérifications."""
import hashlib
import json
import pathlib
import struct
import subprocess
import urllib.request
import zipfile
import zlib
ROOT = pathlib.Path(__file__).resolve().parents[1]
FW = ROOT / 'firmware'
URL = 'https://secure-appldnld.apple.com/iOS6.1/091-3457.20140221.Btt3e/iPhone2,1_6.1.6_10B500_Restore.ipsw'
SHA256 = '37a4b3bb0cbc0cc4c94f4ec2987f23c9207644c2669084084c4a78e8325e6b91'
KEYS_URL = 'https://raw.githubusercontent.com/LukeZGD/Legacy-iOS-Kit-Keys/af6bf5934dc61ed557a967a3f42ab7fb8ed8c45e/iPhone2,1/10B500/index.html'
def download(url, dest):
if dest.exists():
return
temporary = dest.with_suffix(dest.suffix + '.part')
with urllib.request.urlopen(url, timeout=60) as response, temporary.open('wb') as out:
while block := response.read(1024 * 1024):
out.write(block)
temporary.replace(dest)
def lzss(data):
assert data[:8] == b'complzss'
checksum, expected, compressed = struct.unpack_from('>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()
+165
View File
@@ -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()
+41
View File
@@ -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()
+173
View File
@@ -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()
+135
View File
@@ -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()
+212
View File
@@ -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)<size:
chunk=self.stream.read(min(65536,size-len(body)))
if not chunk: raise RuntimeError('Réponse tronquée')
body.extend(chunk)
if header[0]=='ERR': raise RuntimeError(body.decode(errors='replace'))
return bytes(body), header[2] if len(header)>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()<end:
if not pid('qemu'): raise RuntimeError('VM arrêtée')
try:
last=command('screen',timeout=3)
if 'locked=0' in last: return last
if 'passcode=0' in last: unlock()
except (OSError,RuntimeError,ValueError): pass
time.sleep(2)
raise TimeoutError('Interface non disponible : '+last)
def unlock():
s=command('screen'); command('wake')
if 'locked=0' in s: return
if 'passcode=0' not in s: raise RuntimeError('Code de verrouillage actif')
time.sleep(.5); command('touch',50,433,245,433,800)
def screenshot(path):
path=Path(path).resolve(); path.parent.mkdir(parents=True,exist_ok=True)
with Monitor() as m: m.call('screendump',filename=str(path),format='png')
return str(path)
def stop(discard=False):
with lifecycle_lock():
if not pid('qemu'): return
if not discard:
command('sync'); time.sleep(1)
raw=RUN/'checkpoint.hfs'; clean=RUN/'checkpoint-clean.hfs'
with Monitor() as m:
m.call('stop')
try:
if not discard:
m.call('human-monitor-command',**{'command-line':f'pmemsave 0x100000 0x6f000000 "{raw}"'})
if raw.stat().st_size!=0x6f000000: raise RuntimeError('Instantané incomplet')
clean.unlink(missing_ok=True)
from recover_hfs import recover
recover(raw,clean)
os.replace(clean,DISK)
m.call('quit')
except BaseException:
m.call('cont'); raise
if pid('netd'): os.kill(pid('netd'),signal.SIGTERM)
for _ in range(100):
if not pid('qemu') and not pid('netd'): break
time.sleep(.05)
else: raise RuntimeError('Les processus de la VM ne se sont pas arrêtés')
raw.unlink(missing_ok=True)
def install(app):
app=Path(app).resolve()
if app.suffix!='.app' or not app.is_dir(): raise ValueError('Bundle .app invalide')
files=sorted(app.rglob('*'))
if any(f.is_symlink() or not (f.is_dir() or f.is_file()) for f in files):
raise ValueError('Liens symboliques et fichiers spéciaux non pris en charge')
info=plistlib.loads((app/'Info.plist').read_bytes())
bundle=info['CFBundleIdentifier']; executable=info['CFBundleExecutable']
if not isinstance(executable,str) or not executable or '/' in executable or executable in ('.','..'): raise ValueError('Exécutable invalide')
binary=(app/executable).read_bytes()
if binary[:4]!=b'\xce\xfa\xed\xfe' or int.from_bytes(binary[4:8],'little')!=12: raise ValueError('Exécutable ARM32 Mach-O requis')
if not isinstance(bundle,str) or not bundle or not bundle.isascii() or not all(c.isalnum() or c in '._-' for c in bundle): raise ValueError('Identifiant invalide')
if bundle.startswith('com.apple.'): raise ValueError('Identifiant réservé au système')
destination='/Applications/'+bundle+'.app'
with Agent() as a:
a.call('mkdir',destination)
for f in files:
remote=destination+'/'+str(f.relative_to(app))
if f.is_dir(): a.call('mkdir',remote)
else:
data=f.read_bytes(); a.call('put',remote,'755' if f.name==executable else '644',len(data),data=data)
a.call('sync')
# Enregistrement des nouveaux bundles par le scan de démarrage d'iOS.
stop(); start(); wait_ready()
return bundle
def main():
p=argparse.ArgumentParser(description=__doc__); sub=p.add_subparsers(dest='cmd',required=True)
for name in ('start','status','home','unlock','apps','front','net','respring'): sub.add_parser(name)
s=sub.add_parser('stop');s.add_argument('--discard',action='store_true')
w=sub.add_parser('wait');w.add_argument('seconds',type=int,nargs='?',default=360)
s=sub.add_parser('screenshot');s.add_argument('path',nargs='?',default=str(RUN/'screen.png'))
for name in ('launch','install','logs'): s=sub.add_parser(name);s.add_argument('value')
s=sub.add_parser('tap');s.add_argument('x',type=float);s.add_argument('y',type=float)
s=sub.add_parser('swipe');s.add_argument('coords',type=float,nargs=4);s.add_argument('--ms',type=int,default=600)
s=sub.add_parser('get');s.add_argument('remote');s.add_argument('local')
s=sub.add_parser('put');s.add_argument('local');s.add_argument('remote')
s=sub.add_parser('exec');s.add_argument('args',nargs=argparse.REMAINDER)
a=p.parse_args(); c=a.cmd
if c=='start': start(); print('VM démarrée. Attendre : bin/ios6 wait')
elif c=='wait': print(wait_ready(a.seconds))
elif c=='stop': stop(a.discard); print('VM arrêtée'+(' sans sauvegarde' if a.discard else ', disque sauvegardé'))
elif c=='status': print(json.dumps({'running':bool(pid('qemu')),'pid':pid('qemu'),'vnc':'127.0.0.1:5906'},indent=2))
elif c=='screenshot': print(screenshot(a.path))
elif c=='unlock': unlock(); print(command('screen'))
elif c=='install': print(install(a.value))
elif c=='tap': print(command('touch',a.x,a.y,a.x,a.y,96))
elif c=='swipe': print(command('touch',*a.coords,a.ms))
elif c=='get':
with Agent() as agent: data,_=agent.call('get',a.remote)
Path(a.local).write_bytes(data)
elif c=='put':
data=Path(a.local).read_bytes()
with Agent() as agent: agent.call('put',a.remote,'644',len(data),data=data)
elif c=='logs':
log=command('get','/var/log/ios6-emu.log');print('\n'.join(x for x in log.splitlines() if a.value in x)[-64000:])
elif c=='launch': print(command('launch',a.value))
elif c=='exec':
with Agent() as agent: data,code=agent.call('exec',*a.args)
sys.stdout.buffer.write(data);sys.exit(int(code or 0))
else: print(command(c))
if __name__=='__main__':
try: main()
except (OSError,ValueError,RuntimeError,TimeoutError,KeyError) as e: sys.exit(str(e))
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""Teste une vraie app native installée, le tactile, les fichiers et le réseau."""
import argparse
from datetime import datetime, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import threading
import time
from simulator import Agent, ROOT, command, screenshot, wait_ready
BUNDLE = 'org.ios6emu.SimulatorLab'
STATE = '/var/mobile/Library/Caches/SimulatorLab-result.txt'
def await_value(read, expected, timeout=15):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
actual = read().strip()
if actual == expected: return
time.sleep(.3)
raise AssertionError(f'Attendu {expected!r}, obtenu {actual!r}')
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', type=str)
args = parser.parse_args()
stamp = datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')
output = ROOT / (args.output or f'run/reports/{stamp}')
output.mkdir(parents=True, exist_ok=True)
results = {'date': stamp, 'bundle': BUNDLE, 'checks': []}
def passed(name):
results['checks'].append(name)
print('PASS:', name, flush=True)
wait_ready()
command('home')
await_value(lambda: command('front'), 'com.apple.springboard')
passed('retour à SpringBoard')
command('launch', BUNDLE)
await_value(lambda: command('front'), BUNDLE)
time.sleep(2)
passed('lancement natif par SpringBoardServices')
command('touch', 160, 367, 160, 367, 96)
await_value(lambda: command('get', STATE), 'count=0')
passed('bouton de remise à zéro')
for expected in range(1, 4):
command('touch', 160, 296, 160, 296, 96)
await_value(lambda: command('get', STATE), f'count={expected}')
passed('trois appuis UIKit : count=3')
with Agent() as agent:
data = b'ios6-agent-roundtrip\x00\xff\n'
agent.call('put', '/tmp/ios6-smoke-test.bin', '644', len(data), data=data)
assert agent.call('get', '/tmp/ios6-smoke-test.bin')[0] == data
agent.call('rm', '/tmp/ios6-smoke-test.bin')
passed('transfert binaire aller-retour')
class Handler(BaseHTTPRequestHandler):
def log_message(self, *args): pass
def do_GET(self):
body = b'ios6-network-roundtrip'
self.send_response(200)
self.send_header('Content-Length', str(len(body)))
self.end_headers()
self.wfile.write(body)
server = ThreadingHTTPServer(('127.0.0.1', 0), Handler)
threading.Thread(target=server.serve_forever, daemon=True).start()
try:
response = command('fetch', f'http://10.0.2.2:{server.server_port}/test')
assert '200 OK' in response and 'ios6-network-roundtrip' in response, response
finally:
server.shutdown(); server.server_close()
passed('HTTP invité vers serveur local via PPP')
try:
command('touch', -1, 0, 160, 296, 96)
except RuntimeError:
passed('coordonnées tactiles invalides refusées')
else:
raise AssertionError('Coordonnées invalides acceptées')
logs = command('get', '/var/log/ios6-emu.log')
logs = '\n'.join(line for line in logs.splitlines() if 'SimulatorLab' in line)
assert 'count=3' in logs, 'Trace NSLog absente'
(output / 'app.log').write_text(logs + '\n')
passed('récupération des traces NSLog')
screenshot(output / 'native-app.png')
(output / 'validation.json').write_text(json.dumps(results, indent=2, ensure_ascii=False)+'\n')
print(output, flush=True)
if __name__ == '__main__': main()
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""Remplace l'agent invité par build/ios6-agent ; launchd (KeepAlive) le relance.
Le fichier est écrit de façon atomique puis l'agent courant est arrêté. Lancer
ensuite `bin/ios6 stop` pour conserver la nouvelle version sur le disque.
"""
import time
from simulator import Agent, ROOT, command
def main():
data = (ROOT / 'build/ios6-agent').read_bytes()
with Agent() as agent:
agent.call('put', '/usr/libexec/ios6-agent', '755', len(data), data=data)
agent.call('sync')
# Chaque client est servi par un fils : arrêter le processus lancé par launchd.
main_pid = next(line.split()[0] for line in command('ps').splitlines()
if line.split()[1:] == ['1', '0', 'ios6-agent'])
try: command('kill', main_pid, 15, timeout=5)
except Exception: pass # l'agent s'arrête avant de répondre
deadline = time.monotonic() + 30
while True:
time.sleep(1)
try: print('agent relancé :', command('ping', timeout=5)); return
except Exception:
if time.monotonic() > deadline: raise SystemExit("L'agent ne répond plus")
if __name__ == '__main__': main()
+93
View File
@@ -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('<HH', boot) == (1, 5)
virt, phys, usable, top = struct.unpack_from('<4I', boot, 4)
assert virt == 0x80000000 and phys == 0x40000000
assert 0x42000000 + (FW / 'RestoreRamdisk.dec').stat().st_size <= top
assert top <= phys + usable <= 0x4f000000
dt_va, dt_len = struct.unpack_from('<II', boot, 48)
dt_offset = dt_va - virt + phys - 0x41000000
tree, end = devicetree(boot, dt_offset)
assert end - dt_offset == dt_len
nodes = dict(walk(tree))
assert nodes['/device-tree']['model'] == b'iPhone2,1\0'
ramdisk = struct.unpack('<II', nodes['/device-tree/chosen/memory-map']['RAMDisk'])
assert ramdisk == (0x42000000, (FW / 'RestoreRamdisk.dec').stat().st_size)
nvram = nodes['/device-tree/chosen']['nvram-proxy-data']
offset = 0
while offset < len(nvram):
size = struct.unpack_from('<H', nvram, offset + 2)[0] * 16
assert size >= 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('<I', boot, 52)[0]
size = (FW / 'rootfs-vm.hfs').stat().st_size
assert size == 0x6f000000 and 0x100000 + size < 0x70000000
assert nodes['/device-tree/chosen/memory-map']['RAMDisk'] == struct.pack('<II', 0x100000, size)
assert 'secure-root-prefix' not in nodes['/device-tree']
assert nodes['/device-tree/arm-io/mipi-dsim/lcd']['lcd-panel-id'] == struct.pack('<I', 0xE50486)
with (FW / 'rootfs-vm.hfs').open('rb') as disk:
disk.seek(1024)
header = disk.read(512)
blocksize, blocks, free = 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()
+120
View File
@@ -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('<I', 0xff204060) * 1024)
write(0x7d008000, struct.pack('<I', 0xff102030) * 1024)
write(0x7c000000, struct.pack('<III', 0x0e000001, 0x0d008001, 0x0b000001))
write32(0xbfe00008, 0x0c000001)
write32(0x85400024, 0x3c000000)
write32(0x85400030, (320 << 16) | 6)
# Un pixel rouge prémultiplié à 50 %, superposé en (2, 1).
write32(0x7b000000, 0x80800000)
for offset, value in {4: 0x30, 0x40: 0x310700, 0x44: 0x3c002000,
0x48: 1, 0x50: 0x10001, 0x54: 0x20001}.items():
write32(0x85400000 + offset, value)
qmp('screendump', filename=str(tmp / 'screen.ppm'))
pixels = (tmp / 'screen.ppm').read_bytes()
assert pixels[:15] == b'P6\n320 480\n255\n'
pixels = pixels[15:]
def rgb(x, y):
offset = (y * 320 + x) * 3
return tuple(pixels[offset:offset + 3])
assert rgb(0, 0) == (32, 64, 96)
assert rgb(63, 3) == (32, 64, 96)
assert rgb(64, 3) == (16, 32, 48), 'Frontière de page DART'
assert rgb(2, 1) == (144, 32, 48), 'Composition alpha'
assert rgb(0, 7) == (0, 0, 0), 'Limites du plan'
write32(0x8540000c, 0xffffffff)
write32(0x85400008, 1)
command('clock_step 16666667')
assert int(command('readl 0x8540000c')[0], 0) & 1
write32(0x8540000c, 1)
assert int(command('readl 0x8540000c')[0], 0) == 0
qmp('quit')
process.wait(timeout=5)
assert process.returncode == 0
finally:
for client in sockets:
client.close()
if process.poll() is None:
process.kill()
process.wait()
print('OK : scanout DART, pages disjointes, composition alpha, limites des plans et acquittement VBlank.')
if __name__ == '__main__':
main()
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""Relais VNC : transmet l'affichage de QEMU et convertit la souris en gestes tactiles.
Le client VNC se connecte au relais (127.0.0.1:5907 par défaut). L'affichage
et les messages du client passent tels quels vers QEMU (127.0.0.1:5906), sauf
les événements souris et clavier : un appui suivi d'un relâchement du bouton
gauche devient un geste `touch` de l'agent (tap ou glissé, avec sa durée), le
bouton droit et la touche Échap envoient `home`. Les touches imprimables
deviennent `text <caractère>`, Entrée, Retour arrière et Tab `key <nom>`.
"""
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()