Linux ist ein Interpreter
(astrid.tech)- initrd wird als Programmeinheit definiert, die der Kernel direkt interpretiert und ausführt, wodurch Linux als eine Art Interpreter neu gedeutet wird
- Mit
kexec,base64undcpiowird eine rekursive Linux-Distribution gebaut, die sich selbst neu bootet; die initrd führt sich immer wieder selbst aus - Wenn das
/init-Skript sein eigenes cpio-Image ausgibt, entsteht eine selbstreplizierende initrd in Quine-Form - Anhand der ELF-Ausführungsstruktur,
ld.soundbinfmt_miscwird erläutert, wie sich die Interpreter-Schichtung bis in den Kernel hinein fortsetzt - Mit
kexecoder QEMU lässt sich auf Linux ein weiteres Linux in endrekursiver Form ausführen, wodurch die Grenzen zwischen Kernel, Virtualisierung und Interpreter experimentell erweitert werden
Reverse Engineering von rkx.gz und die Struktur einer selbstrekursiven initrd
- Der Befehl
curl https://astrid.tech/rkx.gz | gunzip | sudo shlädt ein 20 MB großes base64-kodiertes Shell-Skript herunter und führt es aus- Das Skript prüft Root-Rechte und kontrolliert das Vorhandensein von
kexec,base64undcpio - Es dekodiert die base64-Daten, erzeugt daraus ein cpio-Archiv namens
rund extrahiert daraus ein Kernel-Image namensk - Mit
kexecwerdenkals Kernel undrals Ramdisk geladen und anschließend ausgeführt
- Das Skript prüft Root-Rechte und kontrolliert das Vorhandensein von
- In
r.cpiobefinden sich die Dateien/bin,/initundk;kist ein Linux-6.18.18-Kernel-Image,/initliegt als Shell-Skript vor/initmountet/proc, bündelt dann das aktuelle Dateisystem per cpio nach/rund startet anschließend perkexecerneut/kund/r- Das Ergebnis ist eine rekursive Linux-Distribution, die sich selbst fortlaufend neu bootet
Die Sicht auf den Linux-Kernel als Interpreter
- initrd ist nicht nur eine einfache Ramdisk zum Booten, sondern kann als Programm verstanden werden, das der Linux-Kernel interpretiert und ausführt
- Wie bei
curl | shoderpython3 script.pyist auch initrd ein Eingabeprogramm, das vom Kernel ausgeführt wird - Daher fungiert der Linux-Kernel als Interpreter, der initrd interpretiert
- Wie bei
- Diese Struktur ähnelt einer Tail-Call-Optimierung
kexecüberschreibt den vorherigen Kernel nicht, sondern lädt und startet ihn in einem neuen Speicherbereich- Jeder Kernel behält keinen vorherigen Zustand und wird durch einen neuen „Stack-Frame“ ersetzt
Quine und die Selbstreplikation von initrd
- Ein Quine ist ein Programm, das sich selbst ausgibt
- Wenn das
/init-Skript am Endecat /rausführt, gibt es ein cpio aus, das mit ihm selbst identisch ist - In diesem Fall entsteht ein Quine des Linux-initrd-Interpreters
- Da alle Dateien im RAM auf
tmpfsexistieren, findet keine tatsächliche Festplatten-I/O statt
- Wenn das
ELF, ld.so und die Schichten des Interpreters
- ELF-Binärdateien enthalten im Header den Pfad zum Interpreter (
ld-linux-x86-64.so.2)- Bei der Ausführung startet der Kernel zunächst
ld.so, undld.solädt anschließend die dynamischen Bibliotheken des ELF und führt das Programm aus - Daher kann auch ELF als eine Art Interpreter-Sprache betrachtet werden
- Bei der Ausführung startet der Kernel zunächst
/bin/shwird vonld.sointerpretiert, undld.sowird direkt vom Kernel interpretiertld.soist ein statisch gelinktes ELF und kann daher direkt vom Kernel ausgeführt werden- Dadurch entsteht der Basisfall der Interpreter-Schichtung
Ausführung von CPIO über binfmt_misc
- Mit
binfmt_misclassen sich Dateien mit bestimmten Magic Bytes über einen festgelegten Interpreter ausführen- Beispiel für einen Registrierungsbefehl:
echo ':cpio:M::\x30\x37\x30\x37\x30\x31::/path/to/my/script.sh:' > /proc/sys/fs/binfmt_misc/register - Mit dieser Einstellung lassen sich per
chmod +xmarkierte CPIO-Dateien direkt ausführen
- Beispiel für einen Registrierungsbefehl:
- Über QEMU kann ein Skript, das CPIO als initrd ausführt, als Interpreter registriert werden
- QEMU bootet eine virtuelle Maschine mit dem angegebenen Kernel und der angegebenen initrd
- Dadurch wird der Interpreter der CPIO-Datei zu dem von QEMU gestarteten Linux-Kernel
Rekursive Interpreter und die „seltsamste Schleife“
- Ein QEMU-basierter Interpreter erzeugt einen neuen Stack-Frame einer Linux-Umgebung
- In einer Struktur, in der auf Linux ein weiteres Linux läuft, ist eine Verschachtelung bis an die Speichergrenze möglich
- Ersetzt man dies durch einen
kexec-basierten Interpreter, wird eine tail-call-optimierte rekursive Linux-Ausführung möglich
- Wenn in
/initbinfmt_miscregistriert und anschließend/rausgeführt wird, ist eine initrd komplett, die sich selbst ausführt/rist der nächste Init-Prozess im CPIO-Format und interpretiert bei seiner Ausführung erneut sich selbst
Fazit
- initrd ist nicht nur ein simples Boot-Werkzeug, sondern eine Programmeinheit, die der Linux-Kernel interpretiert
- Mit
kexecundbinfmt_misclässt sich Linux selbst rekursiv wie ein Interpreter ausführen - Diese Struktur ist ein experimentelles Konzept, das die Grenzen zwischen Kernel, Virtualisierung, Interpreter und selbstreplizierenden Programmen aufweicht
- Der zugehörige Quellcode ist im GitHub-Repository ifd3f/rekexec veröffentlicht
2 Kommentare
Unwissen macht offenbar mutig … Solche Artikel sollte man besser vermeiden.
Hacker-News-Kommentare
Beim Lesen dieses Artikels litt ich unter zu vielen Missverständnissen
Ein cpio-Archiv ist kein Dateisystem. Der Autor verwendet initramfs, das auf tmpfs basiert. Linux kann cpio nach tmpfs extrahieren. Ein Archiv aus Dateien und Verzeichnissen ist für sich genommen kein Programm
Nur weil etwas ähnlich aussieht, ist es nicht dasselbe. Ein Binärprogramm wird auf der CPU ausgeführt, und wenn es einen Interpreter gibt, dann versteckt er sich in der Hardwareumgebung. Das liegt außerhalb des Zuständigkeitsbereichs des Kernels
Um ein Shell-Skript auszuführen, braucht man eine Shell, die dieses Skript interpretiert. Der Autor lässt diesen Teil aus und verwechselt Kernel und Shell-Programm
Linux kann auch ohne initramfs oder ramdisk kompiliert werden und trotzdem den Userspace eines Dateisystems ausführen
Die Formulierung „Linux initrd interpreter“ ist wirklich eine falsche Beschreibung
ld.soELF in den Speicher entpackt und den Entry-Point ausführt oder der Kernel initramfs entpackt und den Entry-Point ausführtIst nicht jedes OS in Kernel-Rechten eine Art Maschinencode-Interpreter?
Der Artikel ist in Ordnung, wenn man ihn als mentales Modell „Linux ist ein Interpreter“ versteht, aber wörtlich genommen ist er falsch
Wenn man es nicht als Interpretation auf CPU-Befehlsebene betrachtet, sondern als Rolle des Kernels beim Orchestrieren von Ausführungsformaten wie ELF, Shebang-Skripten und initramfs, ist es plausibler. Die Verwirrung scheint daher zu kommen, dass zwei Bedeutungen von „Interpreter“ vermischt werden
Der Kernpunkt ist nicht, ob die Metapher stimmt, sondern dass sie zeigt, wie stark das Konzept von „Ausführung“ von der Umgebung abhängt
„Alles ist ein Interpreter?“
Turings Theta Combinator
In einem früheren Artikel der Serie sagte der Autor, er habe sein eigenes VPS-Image gebaut, weil er den Object Storage von Contabo nicht nutzen wollte
Ich denke, irgendwo zwischen dem Extrem, 50 Stunden zu investieren, um 1,50 Dollar im Monat zu sparen, und dem anderen Extrem, 250.000 Dollar für Tokens auszugeben, gibt es einen Mittelweg.
Wenn man sich die Infrastrukturkosten nicht leisten kann, könnte eher ein soziales Problem als mangelnde technische Kompetenz vorliegen. Sich darauf zu versteifen, Doom mit
curlauszuführen, wirkt auf mich nicht produktivIn
man ld.sosteht ausdrücklich, dass der dynamische Linker, der in der.interp-Sektion von ELF gespeichert ist, ausgeführt wird. Schon der Sektionsname ist interessantLinux ist als programmierbare Schnittstelle sehr nützlich. Mit Windows geht das auch, aber Linux scheint dafür besser geeignet zu sein
Ich finde die GUI unter Windows besser, aber auch GNOME oder KDE sind unbequem. Deshalb nutze ich fluxbox, icewm und manchmal xfce oder mate-desktop. Heutzutage bevorzuge ich eine einfache und schnelle Umgebung. Die meisten Aufgaben erledige ich über die Kommandozeile und beim Bearbeiten von Code