So injizieren Sie Code in den Linux-Kernel ohne Quellcode und Debug-Symbole
Stellen Sie sich folgendes Szenario vor: Sie haben ein fertiges Linux-Kernel-Binary für ein Mobilgerät oder ein eingebettetes ARM64-Board. Kein Quellcode verfügbar, Debuginformationen wurden beim Build entfernt, und Sie müssen einen Systemaufruf abfangen oder Ihre eigene Logik direkt auf Kernel-Ebene hinzufügen.
Der übliche Ansatz in solchen Fällen ist langwierig und mühsam. Sie müssen das Binary in IDA Pro oder Ghidra reverse-engineeren, Funktions-Offsets manuell finden und fragile Patches schreiben. Ein Entwickler unter dem Nickname bmax121 решил упростить этот процесс und veröffentlichte das KernelPatch-Projekt.
Warum KernelPatch benötigt wird
KernelPatch ist ein Framework zum Patchen und Hooken des Linux-Kernels, wenn Sie nur mit einem gestrippten Kernel-Image wie vmlinux oder boot.img arbeiten können.
Wenn ein Kernel ohne Debuginformationen erstellt wird, verweigern traditionelle Debugging-Tools wie kprobes ohne zusätzliche Workarounds die Zusammenarbeit. KernelPatch löst dieses Problem eigenständig. Es stellt die Symboltabelle direkt aus dem Kernel-Binary wieder her und bietet eine benutzerfreundliche API zur Modifikation von ausführbarem Code.
Das Projekt ist nützlich für Reverse Engineers, Android-Sicherheitsforscher und Low-level-Softwareentwickler für eingebettete Systeme.
Was das Framework leisten kann
Der Entwickler hat mehrere wichtige Funktionen in das Projekt eingebaut, um mit dem Kernel zu arbeiten:
- Symbolsuche ohne Quellcode. Das Tool parst interne
kallsyms-Strukturen innerhalb des fertigen Images. Es benötigt keine externenSystem.map-Dateien oder Quellcode. Ein ähnlicher Ansatz wird in demvmlinux-to-elf-Utility verwendet, aus dem der Autor einige Ideen übernommen hat. - Zwei Code-Injection-Modi. Sie können einen statischen Patch auf das Image anwenden (zum Beispiel
boot.imgauf der Festplatte überschreiben) oder Code dynamisch zur Laufzeit laden. - Funktionsabfangung via Inline Hook. Für die ARM64-Architektur basiert die Aufrufabfangung auf der Arbeit von
android-inline-hook. Das Framework behandelt relative Adressierungsbefehle (b,bl,adrp) korrekt, um die Ausführung des Originalcodes nicht zu unterbrechen. - Systemaufruf-Hooks. Das Ersetzen von Adressen in der
sys_call_table-Tabelle erfordert nur wenige Befehle. - Integriertes SU-Modul. Für Android-Geräte enthält das Projekt eine eigene Superuser-Implementierung.
Wie es intern funktioniert
Um Benutzermodule (KPM — Kernel Patch Modules) dynamisch zu laden, benötigt das Framework Speicher innerhalb des Kernels. Dafür enthält KernelPatch einen kompakten TLSF-Allocator. Er hilft dabei, ausführbare Speicherseiten (ROX) direkt im Kernel-Adressraum zuzuweisen.
Bei der Durchführung eines Inline Hooks liest das Framework die ersten Anweisungen der Zielfunktion, bildet einen sogenannten Trampoline aus ihnen und schreibt einen Sprung zu Ihrem Handler. Wenn Ihr Code die Ausführung beendet, kehrt die Kontrolle über den Trampoline zur Originalfunktion zurück.
+-------------------+ +---------------------+ +-------------------+
| Целевая функция | ---> | Ваш обработчик KPM | ---> | Трамплин (Orig) |
| (инструкция JMP) | | (пользовательский) | | (продолжение) |
+-------------------+ +---------------------+ +-------------------+
Systemanforderungen und Einschränkungen
Das Projekt hat klare Anwendungsgrenzen, die im Voraus wichtig zu kennen sind:
- Nur die ARM64-Architektur wird unterstützt. Es gibt noch keine Versionen für x86_64 oder RISC-V.
- Kernel-Versionen — von Linux 3.18 bis 6.6.
- Die Hauptanforderung ist, dass der Kernel mit dem
CONFIG_KALLSYMS=y-Flag erstellt worden sein muss.
Wenn der Firmware-Hersteller CONFIG_KALLSYMS vollständig in der Build-Phase entfernt hat, kann KernelPatch Funktionsadressen nicht automatisch parsen. Glücklicherweise ist dieses Flag in den meisten Android- und ARM-Geräte-Firmwares aktiviert.
Praktisches Verwendungsbeispiel
Angenommen, Sie müssen alle Prozessstarts über den sys_execve-Aufruf auf einem Test-Android-Gerät verfolgen, möchten aber den Kernel nicht aus dem Vendor-Quellcode neu erstellen.
Zuvor hätten Sie ein vollständiges Kernel Loadable Module (LKM) erstellen, Probleme mit der digitalen Signaturprüfung lösen und das Fehlen eines Modulladers im Kernel in Kauf nehmen müssen.
Mit KernelPatch sieht der Prozess anders aus:
- Sie schreiben einen kleinen C-Code mit der Abfanglogik.
- Kompilieren ihn ins KPM-Format.
- Übergeben dem KernelPatch-Tool Ihr
boot.imgund das kompilierte KPM-Modul. - Erhalten ein modifiziertes
boot.imgund flashen es via Fastboot.
Nach dem Booten des Geräts geht jeder Aufruf von execve zuerst durch Ihren Handler.
Die README des Projekts ist recht knapp gehalten, und die gesamte detaillierte Dokumentation muss aus dem Quellcode und den Dateien im doc/-Ordner studiert werden. Nichtsdestotrotz entwickelt sich das Projekt aktiv und hat bereits über 14.000 Sterne auf GitHub gesammelt.
Wenn Sie übrigens das Framework selbst nicht für Experimente benötigen, sondern eine fertige Root-Lösung für Android suchen, empfiehlt der Autor einen Blick auf sein Schwesterprojekt — APatch. Dies ist eine höhere Alternative zu Magisk und KernelSU, die KernelPatch als Basis verwendet.
Für Sicherheitsforscher und ARM64-Entwickler ist KernelPatch ein großartiger Fund, der Dutzende von Stunden bei der Analyse von Closed-Source-Kernels spart.
Ähnliche Projekte