1. Der einfache Teil
Es wird wieder technisch. Der erste Teil ist vielleicht noch von allgemeinem Interesse, der zweite Teil dann eher für an Informatik Interessierte, passend zum Stoff in der 13. Jahrgangsstufe in Bayern. (Solange noch Ferien sind, ist Nerdkram erlaubt!)
1.1 Der Klick im Menü
Ich habe mir ja im Kontextmenü einen Eintrag „Verknüpfung in Home erstellen“ angelegt, um mir zu gegebenen Elementen auf einfache Art eine Verknüpfung in meinem Homeverzeichnis anlegen zu können:

Was passiert unter der Motorhaube, wenn ich auf eine Datei rechtsklicke und dann diesen Menüeintrag aufrufe? Das steht in einer Datei mit folgendem Inhalt, der den Kontextmenü-Eintrag festlegt:
[Nemo Action]
Active=true
Name=Verknüpfung in Home erstellen
Exec=ln -s %F "/home/rau/Verknüpfung mit %N"
Icon-Name=folder_home
Selection=s
Extensions=any;
Quote=double
Am wichtigsten ist wohl die Zeile, die angibt, was ausgeführt wird:
Exec=ln -s %F "/home/rau/Verknüpfung mit %N"
Dabei ist %F ein Platzhalter für das aktuell ausgewählte Element mitsamt dem Pfad dorthin, und %N ein Platzhalter für den Namen des aktuell ausgewählten Elements. Ausformuliert heißt der auszuführende Teil dann, wenn etwa „Beispieldatei.odt“ angeklickt ist:
ln -s "/home/rau/Beispieldatei.odt" "/home/rau/Verknüpfung mit Beispieldatei.odt"
Aha! Das ist also, was wirklich geschieht, wenn man im Menü klickt.
1.2 Der Aufruf in der Kommandozeile
Ich kann das Menü auch umgehen und diese Zeile direkt in die Befehlszeile eingeben, das bewirkt das gleiche. Frühers mussten wir immer so arbeiten, ganz ohne Mausklicks, da gab es allenfalls mal den Norton Commander.

Was hier passiert: Der Befehl „ln“ wird benutzt, um zur Beispieldatei.odt eine symbolische („-s“) Verknüpfung mit dem Namen „Verknüpfung mit Beispieldatei.odt“ anzulegen.
Dabei ist „ln“ einer von vielen Befehlen, die es in Linux gibt, ähnlich natürlich auch bei Windows: „rm“ löscht Dateien, „mkdir“ erzeugt ein Verzeichnis, und so weiter. All das muss man sich nicht merken, weil das Menü im Datei-Explorer einem das abnimmt: Man klickt mit der Maus etwas an, dann wird der darunter liegende Befehl ausgeführt.
1.3 Das Starten des Programms
Wobei „Befehl“ die tatsächliche Situation vielleicht verschleiert. „ln“ ist natürlich ein Befehl, aber vor allem ist es ein Programm. Befehle sind Programme. Wenn ich in die Kommandozeile „ln“ eintippe, wird das Programm „ln“ ausgeführt. Das Programm „ln“ befindet sich in /usr/bin, erreichbar auch unter /bin, ebenso wie andere Programme: mkdir, rm, mv, man, cp, curl und viele andere.
1.4 Das Programm unter der Lupe
Schauen wir uns das Programm „ln“ einmal an, wie es auf der Festplatte aussieht, nämlich als eine Reihe von Zahlen. So ein Werkzeuge zum Anschauen heißt Hex-Editor. Hex-Editoren gibt es zuhauf, in manchen Kreisen gelten sie schon als Hackerwerkzeug.
Die Zahlen (im Hexadezimalsystem, also von 0 über 9 bis F), aus denen die Datei besteht, stehen paarweise geordnet in dem großen mittleren Bereich. Nach gängigem System sind manchen Zahlenpaaren druckbare Zeichen zugeordnet, also Buchstaben und so, diese Entsprechungen stehen in der rechten Spalte:

Das Bild zeigt nur den Anfang der Datei. Wie man an der Unverständlichkeit sieht, ist es keine Textdatei, die von einem anderen Programm zeilenweise ausgeführt wird, also nicht etwa Python-Code, sondern wohl ein assembliertes Programm in Maschinensprache.
Ganz am Anfang der Datei fallen einem an zweiter bis vierter Stelle die Zahlen 454C46 auf, weil die nach Standardcodierung als ELF interpretiert werden. Das sieht nicht nach Zufall aus, es gibt auch keine weiteren sinnvoll zu lesenden Zeichen in der Nähe. (Als herkömmliche Zeichen zu lesen sind von den 256 verschiedenen Werten etwa hundert.) Also suchen wir doch mal, was ELF-Dateien eigentlich sind.
1.5 Exkurs: Dateianfänge
Auch wenn Windows das vor einem zu verbergen sucht: Man erkennt oft an der Endung eines Dateinamens, um welche Art Datei es sich handelt: .docx sind Worddateien etwa. Allerdings: Wenn ich die Endung ändere, bleibt die Datei natürlich gleich, also ist das nur ein Indiz, ein Hilfsmittel. Ein weiteres, technischeres und nicht so leicht zu änderndes Hilfsmittel sind mitunter die Anfänge von Dateien, also die ersten paar Bytes:
- Alle assemblierten Java-Dateien erkennt man nicht nur an der Endung
.class, sie fangen auch noch alle mit CAFEBABE an. Also, eigentlich mit den 4 Byte, die durch die Zahlenpaare CA FE BA BE repräsentiert werden. Hier schauen wir uns die Zahlen an, nicht die Zahlen-als-Zeichen. (Dass die Zahlen hier wie Buchstaben aussehen, macht es nicht leichter.) - Alle .exe-Dateien beginnen mit den 2 Byte 4D 5A. Hier sind, anders als vorhin, nicht die Zahlen interessant, sondern die als Zeichen interpretierten Zahlen, denn die sind „MZ“, und der Programmierer, der das eingeführt hat, heißt Mark Zbikowski, und hat sich so ein bisschen verewigt.
- Alle zip-Dateien beginnen mit 50 4B 03 04 (eventuell auch 50 4B 05 06 oder 50 4B 07 08), dazu gehören .zip und .docx und .odt und .epub und .jar und viele mehr. Hier enthalten weder die Zahlen noch die als Zeichen interpretierten Zahlen einen Scherz oder Hinweis. (Zum korrekten Dateiformat gehört in allen Fällen natürlich viel mehr als nur der Anfang, der ist tatsächlich nur ein schnelles Signal.)
Eine Liste solcher signifikanter Dateianfänge gibt es bei Wikipedia: https://en.wikipedia.org/wiki/List_of_file_signatures. Wir lernen dabei: ELF steht für „Executable and Linkable Format“, und da steht am Anfang tatsächlich immer 7F 45 4C 46. Was eine ELF-Datei ist, dazu weiter unten mehr.
2. Der schwierigere Teil, mit Basteln
2.1. Den Befehl „machnlink“ erzeugen
Wenn man eine Kopie des Programms „ln“ unter dem Namen „machnlink“ in /bin anlegt, kann man in der Kommandozeile den Befehl „machnlink“ verwenden, indem man schreibt:
machnlink -s "/home/rau/Beispieldatei.odt" "/home/rau/Verknüpfung mit Beispieldatei.odt"
Juhu! Wir haben unser erstes Linuxprogramm geschrieben! Also, jedenfalls angelegt. Das ist aus verschiedenen Gründen langfristig keine sinnvolle Idee, aber zum Ausprobieren ganz lustig.
2.2 Den Befehl „machnlink“ verändern
Weil mir das Arbeiten im eigentlich geschützten Bereich /bin zu brenzlig wird, verschiebe ich mein Progrämmchen machnlink nach $HOME und kann es von dort in der Kommandozeile mit ./machnlink aufrufen. Geht weiterhin und ich muss nicht als root arbeiten:
./machnlink -s Beispieldatei.odt „Verknüpfung mit Beispieldatei.odt"
Wenn ich irgendwelche Zahlen in dem Programm machnlink verändere, ist es danach sehr wahrscheinlich kaputt. Aber ich könnte doch ein paar der Zahlen verändern, die wohl dazu gedacht sind, als Zeichen interpretiert zu werden, sprich: Text. Weiter unten in der Datei ist nämlich eine Menge englischer Text mit Erklärung zum Programm, der angezeigt wird. Irgendwann am Ende stößt man zum Beispiel auf:
In the 1st form, create a link to TARGET with the name LINK_NAME. In the 2nd form, create a link to TARGET in the current directory.
Das scheint Teil einer Anleitung zu sein. Wenn ich mit machnlink --h mir den Hilfetext anzeigen lasse, steht da nämlich:
In der 1. Form: Eine Verknüpfung namens LINK_NAME auf ZIEL erstellenIn der 2. Form: Eine Verknüpfung auf ZIEL im aktuellen Verzeichnis erstellen
Mir wird die deutsche Übersetzung dieses Hilfetexts angezeigt, die nicht in der Programmdatei selbst gespeichert ist. Aus nachvollziehbaren Gründen sind alle Übersetzungen in anderen, zentralen Dateien gespeichert, das ist bei Wordpress übrigens auch so. Wenn ich den englischen Text aber ändere, so dass da steht In the XXX form, dann wird dafür keine Übersetzung in der Übersetzungsdatei gefunde, gefunden und mir wird der geänderte Originaltext gezeigt. Damit haben wir nicht nur selbst ein neues Programm geschaffen, sondern es auch modifiziert!
Aber es ist natürlich Unsinn, so brutal mit dem Hexeditor an diesen Programmen herumzupfuschen.
2.3 Den Befehl „machnlink“ disassemblieren
So ein Programm wie ln wird wahrscheinlich in einer Hochsprache geschrieben, vermutlich in der Sprache C. Na gut, schauen wir nach: Ja, in C, und hier ist der Code. Das kann man sich deshalb so leicht anschauen, weil der Linux-Code quelloffen ist.
Damit aus dem Quelltext ein ausführbares Programm wird, wird er in Maschinensprache übersetzt. Das ist die einzige Sprache, die der Computerchip tatsächlich versteht, die aber für den Menschen fast völlig unverständlich ist. Dieser Vorgang heißt assemblieren. Das Ergebnis ist die ausführbare Datei – die mit den vielen verwirrenden Zahlen oben.
Man kann so ein Programm auch gleich in einer Assembler-Sprache schreiben und in Maschinensprache übersetzen lassen. Assemblersprache ist für Menschen mit Erfahrung noch verständlich. Während man theoretisch aus vielen verschiedenen Hochsprachen ein Maschinensprachen-Programm erzeugen kann, gibt es praktisch nur eine Assemblersprache für einen gegebenen Computerchip. Deswegen kann man aus dem Maschinensprachenprogramm in der Regel das Assemblersprachenprogramm rekonstruieren.
Dieser Vorgang heißt disassemblieren und ist in Deutschland möglicherweise illegal, ganz sicher, wenn es um Geschäftsgeheimnisse geht. Ich kenne mich rechtlich nicht genau aus. Durch Disassemblieren kann man sich anschauen, wie ein Programm eigentlich funktioniert, man kann auf Schwachstellen stoßen und sie ausnutzen oder melden. Wie gesagt, möglicherweise illegal.
Aber das Programm „machnlink“ darf ich sicher disassemblieren, schließlich steht oben:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Einen ersten Einblick kriegt man mit dem Aufruf von objdump -d machnlink, hier wieder nur der Anfang:

Wer in der 13. Jahrgangsstufe Informatik hat, erkennt zumindest, dass das wohl tatsächlich eine Assemblersprache ist.
2.4 Die Sache mit dem Disassemblieren
Man kann also aus der weitgehend maschinensprachlichen ELF-Datei, oder aus einer anderen assemblierten Datei, den Assemblercode rekonstruieren. Zumindest meistens, und im Prinzip. Theoretisch kann man nie sicher sein, dass das dann auch stimmt, weil die Sache so trivial nicht ist, jedenfalls aus Sicht der theoretischen Informatik; in der Praxis dürfte das aber gut funktionieren, da kenne ich mich aber nicht aus.
Kann man am Ende sogar die ursprüngliche C-Datei wiederherstellen, mit den ursprünglichen Variablennamen und allem dazu? Wenn man eine reine Maschinencode-Datei hat, dann nicht. Früher war das wohl immer so. Heute wird beim Assemblieren aus der Hochsprache oft zusätzliche Information mitgeliefert, die das Rückübersetzen in die Hochsprache erleichtert. So lässt sich aus assemblierten Java-Klassendateien leicht der ursprüngliche Java-Code rekonstruieren, und auch ELF-Dateien enthalten sehr viel zusätzliche Information. ELF-Dateien sind sozusagen Maschinensprache plus Extras.
2.5 Doch mal einen eigenen Befehl schreiben
Der nächste Schritt ist unweigerlich: Könnte ich nicht gleich ein Programm in Assemblersprache schreiben? Nichts so Kompliziertes wie den ln-Befehl natürlich.
Zu diesem Zweck müsste ich allerdings die Assemblersprache für meinen Prozessor lernen. Soweit bin ich noch lange nicht. Aber das Beispiel aus dem Tutorial, das kann ich nachmachen. (Hier ein vielleicht noch besseres Tutorial.) Die folgenden Zeilen schreibe ich in eine Textdatei namens hello.asm:
; ----------------------------------------------------------------------------
; Writes "Hello, World" to the console using only system calls. Runs on 64-bit Linux only.
; To assemble and run:
;
; nasm -felf64 hello.asm && ld hello.o && ./a.out
; ----------------------------------------------------------------------------
global _start
section .text
_start: mov rax, 1 ; system call for write
mov rdi, 1 ; file handle 1 is stdout
mov rsi, message ; address of string to output
mov rdx, 13 ; number of bytes
syscall ; invoke operating system to do the write
mov rax, 60 ; system call for exit
xor rdi, rdi ; exit code 0
syscall ; invoke operating system to exit
section .data
message: db "Hello, World", 10 ; note the newline at the end
Wenn die Textdatei gespeichert ist, rufe ich die zwei folgenden Befehle auf:
nasm -f elf64 -o 'hello.o' 'hello.asm'
ld -o 'hello.out' 'hello.o'
Der erste erzeugt mir aus der Assemblersprachen-Datei „hello.asm“ eine Art Zwischendatei, der zweite macht aus der Zwischendatei dann die endgültige ausführbare ELF-Datei, die hier „hello.out“ heißt. Und wenn ich in der Kommandozeile „./hello.out“ eintippe, wird das Programm ausgeführt und gibt „Hello, World“ aus.
Natürlich kann ich auf andere Art „Hello, World“ ausgeben, indem ich das Programm Bash nutze, das Programm Python, das Programm Java. Aber hier wird eben kein solches anderes Programm benutzt.
2.6 Die Probe aufs Exempel
Dann wollen wir mal das hello.out disassemblieren, also schauen, ob wir aus der ELF-Datei den Assemblercode rekonstruieren. Diesmal tippe ich eine Variante des Befehls ein, objdump --disassemble-all hello.out, und erhalte folgendes vollständiges Ergebnis:
hello.out: Dateiformat elf64-x86-64
Disassembly of section .text:
0000000000401000 <_start>:
401000: b8 01 00 00 00 mov $0x1,%eax
401005: bf 01 00 00 00 mov $0x1,%edi
40100a: 48 be 00 20 40 00 00 movabs $0x402000,%rsi
401011: 00 00 00
401014: ba 0d 00 00 00 mov $0xd,%edx
401019: 0f 05 syscall
40101b: b8 3c 00 00 00 mov $0x3c,%eax
401020: 48 31 ff xor %rdi,%rdi
401023: 0f 05 syscall
Disassembly of section .data:
0000000000402000 <message>:
402000: 48 rex.W
402001: 65 6c gs insb (%dx),%es:(%rdi)
402003: 6c insb (%dx),%es:(%rdi)
402004: 6f outsl %ds:(%rsi),(%dx)
402005: 2c 20 sub $0x20,%al
402007: 57 push %rdi
402008: 6f outsl %ds:(%rsi),(%dx)
402009: 72 6c jb 402077 <_end+0x67>
40200b: 64 fs
40200c: 0a .byte 0xa
Der obere Teil, section .text, sieht sehr ähnlich aus wie unser ursprünglicher Assemblertext. Das passt also. Der untere Teil, section .data, sieht aber nicht sinnvoll aus. Ist er auch nicht: Beim Disassemblieren wurden die Zahlen 48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 0a nach bestem Wissen und Gewissen in den rechts stehenden Assemblercode übersetzt. Diese 13 Zahlen sollen aber gar nicht Maschinensprache sein und sind gar nicht zum Ausführen gedacht, sie sind vielmehr die Zeichenfolge Hello, World, wie man leicht auszählen kann. (Das Zeichen 0a am Ende bedeutet: neue Zeile.)
Die Aufteilung in markierte Sektionen in ELF hilft, solche Fehler zu vermeiden, ich wollte das hier nur mal forcieren und demonstrieren.
3. Verständnisvolles Nicken
Ich weiß, dass das alles ungeheuer kompliziert aussieht. Allein schon diese komischen Befehle. Es ist aber tatsächlich ganz einfach – sobald man es erst einmal mit eigenen Händen gemacht hat. Kann auch nichts passieren dabei. Die Werkzeuge dazu gibt einem Linux gleich mit. Klar, unter Windows und MacOS geht das auch alles, da gibt es auch einen Hexeditor und einen Assembler und Disassembler. Irgendwie, irgendwo. Auf Linux finde ich das alles viel leichter.
Schreibe einen Kommentar