Lässt es sich mit cremefarbenem Papier, einer Tönung, mehr Kontrast oder größerer Schrift besser lesen? Wählen Sie Ihre Farben und Ihre Schrift, einmalig für alle Seiten.

Kostenlose Lektion

x86-64-Assembler lesen

Nach dieser Lektion: Ich kann wenige Zeilen x86-64-Assembler lesen und sagen, was sie berechnen.

Lektion, 60 Min.7 BefehleReverse-Engineering-Lab: Programme mit gdb, radare2, strace und ltrace in einer VM lesen

Die Sprache des Prozessors

Assembler ist die lesbare Schreibweise für die Befehle, die ein Prozessor ausführt. In diesem Modul ist es x86-64-Assembler, wie ihn die meisten PCs und Server verstehen.

Wer wenige Zeilen davon lesen kann, sieht in einem Programm ohne Quelltext, wo es etwas vergleicht und wohin es springt. Mehr brauchen Sie dafür nicht.

Am Ende dieser Lektion

Notiz

Ich kann wenige Zeilen x86-64-Assembler lesen und sagen, was sie berechnen.

Schlüsselwörter

BegriffWas er bedeutetBeispiel
Registerein winziger Speicher im Prozessoreax
Befehleine Anweisung an den Prozessormov
Operanddie Zahl oder das Register, auf das ein Befehl wirkt
Flagein Merkzettel im Prozessor, etwa ob der letzte Vergleich gleich war
Sprungein Befehl, der an eine andere Stelle springtjne
Rückgabewertdas Ergebnis einer Funktion, bei x86-64 im Register eax

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Eine Funktion mit einer Entscheidung

Ein Laden gibt Inhabern einer Kundenkarte 5 Euro Rabatt. Die Funktion rabatt bekommt den Preis und eine Zahl für die Karte. Ist die Karte 1, zieht sie 5 ab, sonst gibt sie den Preis unverändert zurück.

So sieht der Quelltext aus. Sie bauen daraus gleich ein Programm und löschen ihn wieder.

Der Quelltext von rabatt

Code: c
int rabatt(int preis, int karte) {
    if (karte == 1) {
        return preis - 5;
    }
    return preis;
}
int main(void) {
    return rabatt(40, 1) == 35 ? 0 : 1;
}

Den Übungsordner anlegen, falls er fehlt

Befehl
mkdir -p ~/uebung

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

In den Übungsordner wechseln

Befehl
cd ~/uebung

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Den Quelltext von rabatt schreiben

Befehl
printf '%s\n' 'int rabatt(int preis, int karte) {' '    if (karte == 1) {' '        return preis - 5;' '    }' '    return preis;' '}' 'int main(void) {' '    return rabatt(40, 1) == 35 ? 0 : 1;' '}' > rabatt.c

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Das Programm bauen

Befehl
gcc -O0 -no-pie -fno-stack-protector -o rabatt rabatt.c

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Die Befehle von rabatt ansehen

Befehl
objdump -d -M intel --no-show-raw-insn --disassemble=rabatt rabatt | grep '^ '

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Jede Zeile hat einen Befehl und Operanden

Die Option -M intel schreibt das Ziel zuerst: mov eax, 5 legt die 5 in eax. Ohne die Option steht das Ziel hinten. --no-show-raw-insn lässt die Bytes weg, und grep behält die Zeilen mit Befehlen.

Die Zeichen edi und esi sind Register. Bei x86-64 stehen die ersten zwei Argumente einer Funktion darin. Das Ergebnis steht am Ende in eax.

Beispiel: die Zeilen von rabatt

BefehlWas er tut
mov DWORD PTR [rbp-0x4],ediMerkt sich den Preis auf dem Stapel
mov DWORD PTR [rbp-0x8],esiMerkt sich die Karte auf dem Stapel
cmp DWORD PTR [rbp-0x8],0x1Vergleicht die Karte mit 1
jneSpringt weg, wenn beide nicht gleich sind
sub eax,0x5Zieht 5 vom Preis ab
retKehrt zurück, das Ergebnis liegt in eax

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

cmp vergleicht, der Sprung entscheidet

Der Befehl cmp rechnet nichts in ein Register. Er setzt nur die Flags: Waren beide Werte gleich oder nicht? Erst der nächste Befehl, hier jne („jump if not equal“), liest dieses Flag.

So wird aus if (karte == 1) ein Paar aus cmp und jne. Das Diagramm zeigt die zwei Wege durch rabatt.

Beispiel: dieselben Zeilen, zusammenhängend

Code: bash
endbr64
push   rbp
mov    rbp,rsp
mov    DWORD PTR [rbp-0x4],edi
mov    DWORD PTR [rbp-0x8],esi
cmp    DWORD PTR [rbp-0x8],0x1
jne    Marke_A
mov    eax,DWORD PTR [rbp-0x4]
sub    eax,0x5
jmp    Marke_B
Marke_A:
mov    eax,DWORD PTR [rbp-0x4]
Marke_B:
pop    rbp
ret

Das Programm starten

Befehl
./rabatt

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Den Rückgabewert von main lesen

Befehl
echo $?

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Ein häufiger Fehler

Warnung

Viele denken, cmp speichere ein Ergebnis in einem Register. Das tut es nicht. Der Befehl setzt nur Flags, und der nächste Sprung liest sie. Suchen Sie darum bei jedem Sprung den cmp oder test, der direkt davor steht.

Weiterführend

Ändern Sie karte == 1 in karte == 2, bauen Sie das Programm neu und lesen Sie die Zeile mit cmp. Welche Zahl steht jetzt am Ende?

Ändern Sie dann preis - 5 in preis - 7. Welche Zeile des Assemblers ändert sich? Prüfen Sie mit objdump, ob Sie recht hatten.

Über dieses Lab und seinen Quellcode: Reverse-Engineering-Lab: Programme mit gdb, radare2, strace und ltrace in einer VM lesen

Sie unterrichten eine Gruppe? Weisen Sie dieses Modul Ihren Studierenden zu, mit Handbuch für Lehrende, automatisch geprüften Aufgaben und einem Lab für jede Person.