x86, x64 und Assembly
x86, x64 und Assembly
Reverse Engineering findet oft auf Maschinenebene statt. Dafür braucht man ein Grundverständnis von CPU-Architektur, Registern, Stack, Speicherbereichen und Assembly.
Warum Assembly?
Assembly ist die niedrigste Ebene von Code, die für Menschen noch halbwegs lesbar ist.
Bei Malwareanalyse ist Assembly wichtig, weil:
- Quellcode meist nicht vorhanden ist
- Disassembler Maschinencode in Assembly übersetzen
- Kontrollfluss sichtbar wird
- Funktionsaufrufe und Parameter erkannt werden können
- verdächtige Logik nachvollziehbar wird
Register
Register sind sehr schnelle Speicherplätze direkt in der CPU.
Wichtige Registergruppen:
| Registertyp | Bedeutung |
|---|---|
| Instruction Pointer | Adresse der nächsten auszuführenden Instruktion |
| General-Purpose Registers | allgemeine Register für Werte, Adressen und Berechnungen |
| Stack Pointer | zeigt auf die aktuelle Position im Stack |
| Flags Register | speichert Ergebniszustände früherer Operationen |
Beispiele für x86/x64-Register sind EAX/RAX, EBX/RBX, ECX/RCX, EDX/RDX und EIP/RIP.
Status Flags
Flags zeigen Zustände nach Berechnungen an.
| Flag | Bedeutung |
|---|---|
| ZF | Zero Flag, Ergebnis war null |
| CF | Carry Flag, Übertrag oder Borrow |
| SF | Sign Flag, Vorzeichen des Ergebnisses |
| OF | Overflow Flag, arithmetischer Überlauf |
Bedingte Sprünge verwenden diese Flags.
Speicherbereiche
Ein Prozess nutzt verschiedene Speicherbereiche.
| Bereich | Inhalt |
|---|---|
| Code | ausführbarer Maschinencode |
| Data | globale und statische Daten |
| BSS | nicht initialisierte globale Daten |
| Heap | dynamisch angeforderter Speicher |
| Stack | Funktionsaufrufe, lokale Variablen, Rücksprungadressen |
Für Malwareanalyse sind diese Bereiche wichtig, weil dort Hinweise auf Verhalten, Daten und Kontrollfluss sichtbar werden.
Stack
Der Stack arbeitet nach dem Prinzip Last In, First Out.
Er wird verwendet für:
- Funktionsaufrufe
- lokale Variablen
- Argumente
- Rücksprungadressen
- temporäre Werte
Im Debugger ist der Stack oft hilfreich, um zu sehen, wie ein Programm an eine bestimmte Stelle gelangt ist.
Opcodes
Maschinencode besteht aus Bytes. Diese Bytes werden von der CPU als Instruktionen interpretiert.
Ein Disassembler zeigt aus diesen Bytes wieder lesbare Assembly-Befehle.
Dabei gehen aber viele Informationen verloren:
- Variablennamen
- Kommentare
- ursprüngliche Struktur
- Typinformationen
- Absicht des Codes
Wichtige Instruktionen
| Instruktion | Bedeutung |
|---|---|
mov | kopiert Werte |
add | addiert |
sub | subtrahiert |
and | bitweises UND |
or | bitweises ODER |
xor | bitweises XOR |
cmp | vergleicht Werte |
test | prüft Bits und setzt Flags |
jmp | springt unbedingt |
jz / je | springt, wenn Zero Flag gesetzt ist |
push | legt Wert auf den Stack |
pop | holt Wert vom Stack |
nop | keine Operation |
Kontrollfluss
Malwareanalyse lebt stark vom Kontrollfluss.
Wichtige Fragen:
- Wo beginnt die Ausführung?
- Welche Bedingungen entscheiden über den nächsten Schritt?
- Welche Funktionen werden aufgerufen?
- Gibt es Sprünge in ungewöhnliche Bereiche?
- Wird Code erst zur Laufzeit entschlüsselt oder entpackt?
Merksatz
Assembly wirkt zuerst fremd, aber es folgt klaren Regeln. Wer Register, Stack, Flags und Sprünge versteht, kann viele Binärprogramme deutlich besser lesen.
Zuletzt aktualisiert: 6. Juni 2026