
Ein IDA-Toolkit zur Analyse von iOS-Kernelcaches.
Dieser Fork wurde aktualisiert, um mit IDA 9.x / Python 3 / iOS 27.0 beta 1 zu funktionieren.
ida_kernelcache ist ein IDAPython-Modul für IDA Pro, das die Arbeit mit iOS-Kernelcaches erleichtert. Das Modul stellt Funktionen bereit, um:
__PRELINK_INFO-Segments des Kernels in ein Python-Wörterbuch.__got-Abschnitten und Stub-Funktionen in __stubs-Abschnitten.Die Hauptverarbeitungsfunktion ist dafür ausgelegt, vor jeder manuellen Analyse oder Reverse Engineering ausgeführt zu werden. Mit den Standardeinstellungen übersieht IDA im Kernelcache viele nützliche Informationen. Diese Skripte helfen IDA, indem sie die bekannte Struktur des Kernelcaches nutzen, um nützliche Informationen automatisch zu verbreiten.
Zusätzlich zur Standardfunktionalität des Moduls enthält ida_kernelcache mehrere Skripte, die die Analyse des iOS-Kernelcaches erleichtern. Beispielsweise kannst du die Skripte verwenden, um von einer Funktion verwendete C-Structs automatisch zu erzeugen.
Viele der in ida_kernelcache verwendeten Techniken wurden für memctl entwickelt und direkt von dort übernommen.
ida_kernelcache wurde mit IDA Pro 6.95 auf Kernelcaches für die iOS-Versionen 10.1.1, 11.0, 11.2, 11.3.1 und 12.0 beta getestet. Derzeit werden nur Arm64-Kernelcaches ab iOS 10 unterstützt.
Du benötigst eine bereits dekomprimierte Kernelcache-Datei, die in IDA geladen ist. Die URL zum Herunterladen eines bestimmten IPSW von Apple findest du online, und es gibt eine Reihe öffentlicher Tools (einschließlich memctl), die den Kernelcache dekomprimieren können.
Wähle in IDA in der Menüleiste „Datei“ -> „Skriptdatei...“ und dann das Skript ida_kernelcache.py im Hauptverzeichnis aus. Dadurch wird das Modul ida_kernelcache unter den Namen ida_kernelcache und kc in den IDAPython-Interpreter geladen. Gib in der IDAPython-Eingabeaufforderung kc.kernelcache_process() ein und drücke die Eingabetaste, um mit der Analyse des Kernelcaches zu beginnen. Diese Funktion führt alle wichtigen Analysen aus, die von ida_kernelcache unterstützt werden. Die Funktion läuft einige Minuten, während IDA neue Funktionen identifiziert und analysiert.
ida_kernelcache versucht, Benutzernamen für Adressen nicht zu überschreiben. Das bedeutet, dass die Ergebnisse möglicherweise nicht so gründlich ausfallen, wenn der Kernelcache vor der Initialisierung mit kernelcache_process manuell analysiert wurde, da benutzerdefinierte Namen die automatische Namensweitergabe blockieren können. Es gibt jedoch auch keine Garantie, dass ida_kernelcache eine frühere Analyse nicht durcheinanderbringt. Wenn du dich also entscheidest, kernelcache_process auf einer bereits analysierten Kernelcache-Datei auszuführen, erstelle zuerst ein Backup.
ida_kernelcache soll über ida_kernelcache.py geladen werden; die Untermodule im Verzeichnis ida_kernelcache sind nicht für das direkte Laden gedacht. ida_kernelcache stellt jedoch die Funktionalität vieler dieser Untermodule bereit. Hier ist, was jedes von ihnen tut:
ida_utilities:
Dieses Modul kapselt einige Funktionen von IDA, um eine benutzerfreundlichere API bereitzustellen. Besonders nützlich sind is_mapped, read_word, read_struct, force_function und ReadWords. is_mapped prüft, ob eine Adresse gemappt ist, und optional, ob sie einen bekannten Wert enthält. read_word liest ein Wort variabler Größe von einer Adresse. read_struct liest einen Strukturtyp in ein Python-Wörterbuch oder ein Python-Accessor-Objekt, was das Parsen von Datenstrukturen erheblich erleichtert. force_function versucht mehrere Tricks, um eine Adresse in IDA in den Anfang einer Funktion zu konvertieren. ReadWords ist ein Generator, um über Datenwörter und ihre Adressen in einem Bereich zu iterieren.
build_struct: Dieses interne Modul enthält Hilfsprogramme, um ein IDA-Struct automatisch anhand einer Folge von Zugriffen auf das Struct zu befüllen.
class_struct: Dieses Modul stellt Funktionen zur Verfügung, um IDA-Structs zu erzeugen, die C++-virtuelle Methodentabellen und Klassen repräsentieren. durchsucht die (symbolisierten) virtuellen Methodentabellen und erstellt IDA-Structs für virtuelle Methodenzeiger. führt eine Datenflussanalyse an den virtuellen Methoden durch, um Zugriffe auf die Felder jeder Klasse zu identifizieren, und erstellt dann IDA-Structs, die die Klassen repräsentieren. Anweisungen, die offenbar auf ein Feld verweisen, werden ebenfalls in Strukturoffset-Referenzen umgewandelt. Weitere Einzelheiten findest du in der Docstring des Moduls.
Das Skript ida_kernelcache_reload.py ist identisch mit ida_kernelcache.py, außer dass es das Modul ida_kernelcache und alle Untermodule neu lädt. Es ist hauptsächlich für die Entwicklung nützlich.
Das Verzeichnis scripts enthält Skripte, die ida_kernelcache verwenden, um eine bestimmte Analyse durchzuführen. Diese Skripte sind zu spezifisch, um Teil des Hauptmoduls ida_kernelcache zu sein, aber sie sind beim Reverse Engineering des Kernelcaches nützlich. Dazu gehören:
find_virtual_method_overrides.py: Ein Skript, um Nachkommen einer Klasse zu finden, die eine virtuelle Methode überschreiben, die den angegebenen String enthält. Passende Überschreibungen werden auf der Konsole ausgegeben.
populate_struct.py: Füllt Felder für eine C++-Klasse oder ein C-Struct aus, indem eine Datenflussanalyse ab der aktuellen Adresse durchgeführt wird.
process_external_methods.py:
Verarbeitet ein IOExternalMethod- oder IOExternalMethodDispatch-Array in eine Standardform für die Verwendung durch Fuzzing-Tools.
Wenn du den Hex-Rays-Dekompilierer verwendest, ist eines der interessanteren Features von ida_kernelcache die automatische C++-Klassenrekonstruktion, die die OSMetaClass-Informationen und die Datenflussanalyse nutzt, um IDA-Structs zu erstellen, die die im Kernelcache gefundenen Klassen repräsentieren. Diese Darstellungen können die Lesbarkeit der Pseudocode-Darstellung erheblich verbessern. Weitere Informationen findest du im Beitrag Reconstructing C++ classes in the iOS kernelcache using IDA Pro.
Mit iOS 12 führte Apple auf einigen Geräten ein neues Kernelcache-Format ein. Unter den Änderungen sind die Kernel-Pointer dieses neuen Kernelcaches markiert, um sie in einer Liste zu verketten, vermutlich damit iBoot den Kernel ohne die _PrelinkLinkKASLROffsets-Daten im Prelink-Wörterbuch verschieben kann. Die Analyse eines Standard-Kernelcaches in diesem Format in IDA ist aufgrund der fehlenden Querverweise schwierig. Details findest du im Artikel Analyzing the iOS 12 kernelcache's tagged pointers.
Wenn du die Zeiger im Kernelcache nur ohne weitere Verarbeitung von ihren Tags befreien möchtest, führe kc.tagged_pointers.untag_pointers() aus.
Ein Teil dieser Funktionalität lässt sich wahrscheinlich breiter anwenden als nur auf Apple-Kernelcaches (zum Beispiel Vtable-Analyse und Symbolweitergabe oder die meisten Funktionen in ida_utilities.py). Dennoch habe ich den Importbereich auf nur das Modul ida_kernelcache beschränkt, da ich nichts davon auf anderen Binärtypen getestet habe.
ida_kernelcache wird unter der MIT-Lizenz veröffentlicht.
Ein Großteil der Funktionalität in ida_kernelcache stammt aus memctl, das ebenfalls unter der MIT-Lizenz veröffentlicht ist. Weitere Quellen sind in den Kommentaren in den entsprechenden Dateien vermerkt.
Brandon Azad
initialize_vtable_structsinitialize_class_structsclasses:
Dieses Modul definiert den Typ ClassInfo, der Informationen über C++-Klassen im Kernelcache speichert, und stellt die Funktion collect_class_info bereit, um den Kernelcache nach Klassen zu durchsuchen und das globale Wörterbuch class_info mit einer Zuordnung von Klassennamen zu ClassInfo-Objekten zu füllen. Der Typ ClassInfo erfasst den Klassennamen, die OSMetaClass-Instanz, die virtuelle Methodentabelle und den Namen der Superklasse für jede C++-Klasse. Zusätzlich speichert jedes ClassInfo-Objekt Verweise auf die ClassInfo der Superklasse und die ClassInfo aller direkten Unterklassen, sodass die Klassenhierarchie leicht untersucht und durchlaufen werden kann. collect_class_info speichert außerdem die Menge aller virtuellen Methodentabellen im globalen Set vtables.
data_flow: Dieses interne Modul enthält Datenflussoperationen, die vom Rest von ida_kernelcache verwendet werden.
kernel:
Dieses Modul stellt die globalen Variablen base und prelink_info bereit. base ist die Basisadresse des Kernel-Images (der Beginn des Mach-O-Headers des Kernels). prelink_info ist das geparste __PRELINK_INFO-Wörterbuch.
kplist:
Dieses Modul stellt die Funktion kplist_parse zum Parsen von Kernel-Plists bereit.
metaclass:
Dieses Modul stellt die Funktion initialize_metaclass_symbols bereit, die für jede bekannte OSMetaClass-Instanz ein Symbol hinzufügt.
offset:
Dieses Modul stellt die Funktionen initialize_data_offsets und initialize_offset_symbols bereit. Erstere durchsucht die Segmente nach Zeigern, die in Offsets umgewandelt werden können. Letztere symbolisiert Offsets im __got-Abschnitt jedes Kexts, wenn das Ziel des Offsets ein Symbol besitzt.
segment:
Dieses Modul stellt die Funktion initialize_segments bereit, um IDAs Segmente nützlicher zu benennen. Standardmäßig scheint IDA die Segmentnamen zu erstellen, indem es eine Vermutung der Bundle-ID mit dem Mach-O-Abschnitt, der die Region beschreibt, kombiniert. initialize_segments extrahiert die echte Bundle-ID aus dem __PRELINK_INFO-Wörterbuch und benennt jedes Segment so um, dass es die Bundle-ID, das Mach-O-Segment und den Mach-O-Abschnitt enthält. Dadurch ist es beispielsweise möglich, zwischen __TEXT.__const und __DATA_CONST.__const zu unterscheiden. Dieses Modul stellt außerdem die Funktion kernelcache_kext (auf oberster Ebene erneut exportiert) bereit, um das Kext zu bestimmen, das die angegebene Adresse enthält (nur im alten iOS-11-Split-Kext-Kernelcache-Format).
stub:
Viele Kexts im Kernelcache enthalten Stub-Funktionen in einem __stubs-Abschnitt, die zu Funktionen im eigentlichen Kernel springen. Leider stellen diese Stubs eine Barriere für die Weitergabe von Querverweisen und Typinformationen dar. Dieses Modul löst diese Probleme nicht, erleichtert aber die Betrachtung von Stubs, indem es Stub-Funktionen automatisch umbenennt, sodass der Name der Zielfunktion sichtbar ist. Stubs und ihre Ziele werden in IDA zwangsweise in Funktionen umgewandelt, was dazu beiträgt, dass die Funktionen in IDA mit den Funktionen im ursprünglichen Quellcode übereinstimmen.
tagged_pointers: Das neue zusammengeführte Kernelcache-Format von iOS 12 hat die oberen 2 Bytes jedes Zeigers mit einem Offset markiert, um die Zeiger in einer Liste zu verketten. Dieses Modul enthält Funktionen zum Verarbeiten und Wiederherstellen dieser markierten Zeiger.
vtable:
Dieses Modul bietet viele nützliche Funktionen für die Arbeit mit virtuellen Methodentabellen, darunter vtable_length, convert_vtable_to_offsets, vtable_overrides, initialize_vtable_symbols und initialize_vtable_method_symbols. vtable_length prüft, ob die angegebene Adresse eine Vtable sein könnte, und gibt die Vtable-Länge zurück. Der Generator vtable_overrides zählt die virtuellen Methoden einer Klasse auf, die von der Superklasse verwendete virtuelle Methoden überschreiben. Die Funktion initialize_vtable_symbols fügt ein Symbol für den Anfang jeder identifizierten Vtable hinzu. initialize_vtable_method_symbols iteriert durch die überschriebenen Methoden in jeder Vtable und überträgt Symbole von der Superklasse auf die Unterklasse. Dies ist möglich, weil die meisten Basisklassen in IOKit in XNU mit relativ vollständigen Symbolinformationen definiert sind. Jede Methodenüberschreibung in der Vtable einer Unterklasse muss dieselbe Schnittstelle wie die Methode in der Superklasse erfüllen, was bedeutet, dass wir ein Symbol für die Überschreibung erzeugen können, indem wir den Namen der Superklasse im Symbol der virtuellen Methode der Superklasse durch den Namen der Unterklasse ersetzen. Wenn wir beispielsweise keinen Namen für die virtuelle Methode an Index 7 in der Klasse AppleKeyStore haben, aber wissen, dass die virtuelle Methode an Index 7 in ihrer Superklasse IOService __ZNK9IOService12getMetaClassEv heißt, können wir folgern, dass Index 7 in der Unterklasse __ZNK13AppleKeyStore12getMetaClassEv heißen sollte. Diese Technik kann verwendet werden, um die meisten virtuellen Methoden in den meisten Klassen zu symbolisieren.