Diassemblieren: Unterschied zwischen den Versionen

Aus C und Assembler mit Raspberry
KKeine Bearbeitungszusammenfassung
KKeine Bearbeitungszusammenfassung
 
(2 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 88: Zeile 88:
|}
|}


---
=== Beispiele für ARM64 ===


### Beispiele für ARM64
==== Einfaches Disassemblieren ====
<syntaxhighlight lang="shell">
objdump -d ./program
</syntaxhighlight>


#### Einfaches Disassemblieren
==== Ohne Register-Aliase (z. B. `wzr` statt `w0`) ====
```bash
<syntaxhighlight lang="shell">
objdump -d ./blink
objdump -d -M no-aliases ./program
```
</syntaxhighlight>


#### Ohne Register-Aliase (z. B. `wzr` statt `w0`)
==== Mit klassischen Register-Namen (x0–x30) ====
```bash
<syntaxhighlight lang="shell">
objdump -d -M no-aliases ./blink
objdump -d -M reg-names=std ./program
```
</syntaxhighlight>


#### Mit klassischen Register-Namen (x0–x30)
==== Nur `.text`-Sektion ====
```bash
<syntaxhighlight lang="shell">
objdump -d -M reg-names=std ./blink
objdump -d --section=.text ./program
```
</syntaxhighlight>


#### Nur `.text`-Sektion
==== Disassemblieren roher Binärdaten ====
```bash
objdump -d --section=.text ./blink
```
 
#### Disassemblieren roher Binärdaten
Wenn du z. B. ein 1:1-Memory-Dump oder reinen Maschinencode ohne ELF-Struktur hast:
Wenn du z. B. ein 1:1-Memory-Dump oder reinen Maschinencode ohne ELF-Struktur hast:
```bash
<syntaxhighlight lang="shell">
objdump -D -b binary -m aarch64 yourfile.bin
objdump -D -b binary -m aarch64 yourfile.bin
```
</syntaxhighlight>


---
=== Option `-M` im Detail ===
 
### Option `-M` im Detail


`-M` steht für **Disassembler-Modifikatoren** – mehrere können kombiniert werden:
`-M` steht für **Disassembler-Modifikatoren** – mehrere können kombiniert werden:


- `no-aliases`   
* `no-aliases`   
  Zeigt dir "echte" Instruktionen, keine Vereinfachungen.
: Zeigt dir "echte" Instruktionen, keine Vereinfachungen.
  > Beispiel: Statt `mov x0, x1` sieht man `orr x0, x1, xzr`.
: > Beispiel: Statt `mov x0, x1` sieht man `orr x0, x1, xzr`.


- `reg-names=std`   
* `reg-names=std`   
  Standardregister (x0–x30, sp, pc).
: Standardregister (x0–x30, sp, pc).


- `reg-names=raw`   
* `reg-names=raw`   
  Zeigt numerische Register: `r0`, `r1`, ...
: Zeigt numerische Register: `r0`, `r1`, ...


- `reg-names=apcs`   
* `reg-names=apcs`   
  Zeigt Register gemäß dem **ARM Procedure Call Standard** (z. B. `a1`, `v1`, `fp`, `lr`).
: Zeigt Register gemäß dem **ARM Procedure Call Standard** (z. B. `a1`, `v1`, `fp`, `lr`).


---
=== Die `-s` Option: Rohdaten anzeigen ===


## Die `-s` Option: Rohdaten anzeigen
Die Option `-s` (kurz für `--full-contents`) zeigt den "Hexdump des gesamten Inhalts" der Datei, geordnet nach Sektionen.


Die Option `-s` (kurz für `--full-contents`) zeigt den **Hexdump des gesamten Inhalts** der Datei, geordnet nach Sektionen.
==== Beispiel: ====
<syntaxhighlight lang="shell">
objdump -s ./program
</syntaxhighlight>


### Beispiel:
==== Beispielausgabe: ====
```bash
<syntaxhighlight lang="shell">
objdump -s ./blink
```
 
### Beispielausgabe:
```text
Contents of section .text:
Contents of section .text:
  0000 52800000 d2800021 d2800442 d2800003  R...!...B....
  0000 52800000 d2800021 d2800442 d2800003  R...!...B....
  0010 d2800004 8b030084 d2800025 d2800026  .........%...&
  0010 d2800004 8b030084 d2800025 d2800026  .........%...&
```
</syntaxhighlight>


### Bedeutung:
Bedeutung:
- Linke Spalte: Offset innerhalb der Sektion.
* Linke Spalte: Offset innerhalb der Sektion.
- Rechte Spalten: Hex-Werte der Bytes.
* Rechte Spalten: Hex-Werte der Bytes.
- Ganz rechts (manchmal): ASCII-Darstellung, wenn druckbar.
* Ganz rechts (manchmal): ASCII-Darstellung, wenn druckbar.


---
=== Kombination mit `-d` und `-s` ===


## Kombination mit `-d` und `-s`
Oft ist es sinnvoll, `-d` und `-s` "gemeinsam" zu nutzen – z. B., um zu sehen, wie ein Maschinenbefehl wirklich kodiert ist:


Oft ist es sinnvoll, `-d` und `-s` **gemeinsam** zu nutzen – z. B., um zu sehen, wie ein Maschinenbefehl wirklich kodiert ist:
<syntaxhighlight lang="shell">
 
objdump -ds ./program
```bash
</syntaxhighlight>
objdump -ds ./blink
```


Oder: gezielt auf eine bestimmte Sektion fokussieren:
Oder: gezielt auf eine bestimmte Sektion fokussieren:
```bash
<syntaxhighlight lang="shell">
objdump -s --section=.rodata ./blink
objdump -s --section=.rodata ./program
```
</syntaxhighlight>


So kannst du **Strings, Konstanten oder Sprungtabellen** sichtbar machen, die nicht disassembliert, aber trotzdem mitverarbeitet werden.
So kannst du "Strings, Konstanten oder Sprungtabellen" sichtbar machen, die nicht disassembliert, aber trotzdem mitverarbeitet werden.


---
=== Zusammenfassung – wann `-s` nützlich ist: ===


## Zusammenfassung – wann `-s` nützlich ist:
* Debugging von Konstanten/Strings: Gibt Einblick in `.rodata`, `.data`, `.bss` etc.
* Verstehen von Maschineninstruktionen: Hex-Werte → ARM64-Opcode
* Analyse von Binärdateien ohne Symbole: Du siehst trotzdem, "was im Speicher liegt"
* Reverse Engineering: Hex-Dumps helfen, Kontrollstrukturen und Datenlayout zu erkennen


| Situation | Warum `-s` sinnvoll ist |
Tipps:
|----------|--------------------------|
* Nur Funktionssymbol disassemblieren
| Debugging von Konstanten/Strings | Gibt Einblick in `.rodata`, `.data`, `.bss` etc. |
: Wenn du Symbole hast (nicht `strip` benutzt):
| Verstehen von Maschineninstruktionen | Hex-Werte → ARM64-Opcode |
: <syntaxhighlight lang="shell">
| Analyse von Binärdateien ohne Symbole | Du siehst trotzdem, **was im Speicher liegt** |
| Reverse Engineering | Hex-Dumps helfen, Kontrollstrukturen und Datenlayout zu erkennen |
 
---
 
### Bonus: Nur Funktionssymbol disassemblieren
 
Wenn du Symbole hast (nicht `strip` benutzt):
```bash
objdump -d ./blink --disassemble=_start
objdump -d ./blink --disassemble=_start
```
</syntaxhighlight>
 
* Wenn deine Datei keine Symbole enthält, verwende zusätzlich `nm` oder `readelf`, um Adressen herauszufinden.
---
* Kombiniere `objdump` mit `grep` zur gezielten Analyse:
 
: <syntaxhighlight lang="shell">
### Abschluss: Tipps
 
- Wenn deine Datei keine Symbole enthält, verwende zusätzlich `nm` oder `readelf`, um Adressen herauszufinden.
- Kombiniere `objdump` mit `grep` zur gezielten Analyse:
  ```bash
   objdump -d ./blink | grep "<_start>"
   objdump -d ./blink | grep "<_start>"
  ```
</syntaxhighlight>


== Negative Zahlen beim Disassemblieren mit `objdump` ==


=== 1. Immediates werden hexadezimal dargestellt ===


`objdump` zeigt "sofortige Werte (Immediates)" standardmäßig in "Hexadezimalform" – unabhängig davon, ob es sich ursprünglich um negative Zahlen handelte.


* Beispiel (Assemblercode):
: <syntaxhighlight lang="asm">
mov x0, #-1
</syntaxhighlight>


* Disassemblierung:
: <syntaxhighlight lang="shell">
mov x0, #0xffffffffffffffff
</syntaxhighlight>


- `#-1` wird als `0xFFFFFFFFFFFFFFFF` dargestellt.
- Das ist die **Zweierkomplement-Darstellung** von `-1` in 64 Bit.


=== 2. Offsets bei Sprüngen und Speicherzugriffen ===


"Branches", "LOAD/STORE-Offsets" oder Stackzugriffe können negative Offsets haben. Ob `objdump` das als negativ darstellt, hängt vom Befehl ab – oft wird auch hier nur der "Zweierkomplementwert" in Hex gezeigt.


* Beispiel:
: <syntaxhighlight lang="asm">
ldur x0, [sp, #-16]
</syntaxhighlight>


* Disassemblierung:
: <syntaxhighlight lang="shell">
ldur x0, [sp, #-0x10]
</syntaxhighlight>


Hier ist `objdump` klug genug, das tatsächlich als "negativen Offset" anzuzeigen!


Aber: Das klappt "nicht bei allen Instruktionen"! Manchmal musst du selbst das Offset interpretieren.


=== 3. Unsignierte Darstellung in Datenfeldern ===


Bei Verwendung von `objdump -s` (Hexdump der Daten) werden "alle Bytes als unsigned" dargestellt – also es gibt "keine negativen Zahlen" im Dump selbst, nur 0–255 (0x00–0xFF).


* Beispiel:
: <syntaxhighlight lang="C">
.int32 -42
</syntaxhighlight>


: <syntaxhighlight lang="shell">
objdump -s -j .data a.out
</syntaxhighlight>


* Disassemblierung:
: <syntaxhighlight lang="shell">
d6 ff ff ff
</syntaxhighlight>


 
Das ist `0xFFFFFFD6`, also `-42` als "signed 32-Bit" im Zweierkomplement.
 
 
 
 
 
 
 
 
 
 
== Diassemblieren ==
-> objdump -s -d HelloWorld.o > HelloWorld.diassem
 
 
 
Hello.o:    file format elf64-littleaarch64
 
Contents of section .text:
0000 200080d2 e1000058 a20180d2 080880d2  ......X........
0010 010000d4 000080d2 a80b80d2 010000d4  ................
0020 00000000 00000000                    ........       
Contents of section .data:
0000 48656c6c 6f20576f 726c6421 0a        Hello World!. 
 
Disassembly of section .text:
 
0000000000000000 <_start>:
  0: d2800020 mov x0, #0x1                  // #1
  4: 580000e1 ldr x1, 20 <_start+0x20>
  8: d28001a2 mov x2, #0xd                  // #13
  c: d2800808 mov x8, #0x40                  // #64
  10: d4000001 svc #0x0
  14: d2800000 mov x0, #0x0                  // #0
  18: d2800ba8 mov x8, #0x5d                  // #93
  1c: d4000001 svc #0x0
...
 
 
== negative Zahlen ==
-> Beispiel: 5 + -3
  3 in 1 byte is 0x03 or 0000 0011.
  Inverting the bits is
  1111 1100
  Add 1 to get
  1111 1101 = 0xFD
  Now add
  5 + 0xFD = 0x102 = 2
 
movadd.o:    file format elf64-littleaarch64
 
Contents of section .text:
0000 001980d2 812580d2 0200018b a00080d2  .....%..........
0010 41008092 0200018b 000080d2 a80b80d2  A...............
0020 010000d4                            ....           
 
Disassembly of section .text:
 
0000000000000000 <_start>:
  0: d2801900 mov x0, #0xc8                  // #200
  4: d2802581 mov x1, #0x12c                // #300
  8: 8b010002 add x2, x0, x1
  c: d28000a0 mov x0, #0x5                  // #5
  10: 92800041 mov x1, #0xfffffffffffffffd    // #-3
  14: 8b010002 add x2, x0, x1
  18: d2800000 mov x0, #0x0                  // #0
  1c: d2800ba8 mov x8, #0x5d                  // #93
  20: d4000001 svc #0x0

Aktuelle Version vom 10. April 2025, 17:01 Uhr

Einleitung

Das Disassemblieren ist der Prozess, bei dem aus Maschinencode wieder menschenlesbarer Assembler-Code erzeugt wird. Es ist ein wichtiges Werkzeug für:

  • Debugging: Nachvollziehen, was das Programm tatsächlich macht.
  • Reverse Engineering: Verstehen fremder Binärdateien.
  • Optimierung: Erkennen ineffizienter Codeabschnitte.
  • Lernen: Verstehen, wie Hochsprachen wie C übersetzt werden.

Werkzeuge

Für ARM64 unter Linux – z. B. auf dem Raspberry Pi – bieten sich diese Tools an:

objdump

Teil von `binutils`. Zeigt dir den Assembler-Code aus Objektdateien oder Binärprogrammen an. Dies ist auch meine bevorzugte Art, um etwas zu Diassemblieren.

objdump -d ./program

Optionen:

  • `-d` disassembliert ausführbare Abschnitte.
  • `-M reg-names=std` (optional): zeigt Standard-Registerbezeichnungen (x0–x30).
  • `-M no-aliases` (zeigt die "echten" Instruktionsnamen statt Pseudonyme).

Beispiel:

objdump -d -M reg-names=std ./program

gdb

Mit `gdb` (GNU Debugger) kannst du direkt im Programmfluss disassemblieren.

gdb ./program
(gdb) disassemble _start

Praktische Tipps

  • Möchtest du den Code in einem Editor ansehen, kannst du die Ausgabe in eine Datei leiten:
objdump -d -M reg-names=std ./program > program.diassem
  • Achte auf die Sectionen: `.text` ist der Code, andere wie `.data` oder `.bss` enthalten keine Instruktionen.
  • Prüfe, ob deine Datei nicht mit Strip bearbeitet wurde (`strip` entfernt Debugsymbole).
  • Wenn du aus einer reinen Binärdatei (ohne ELF-Header) disassemblieren willst, nutze `objdump` mit `--target binary` und `--architecture aarch64`.

Beispiel:

objdump -D -b binary -m aarch64 your.bin

Worauf achten?

  • Alignment: ARM64 nutzt 4-Byte ausgerichtete Instruktionen.
  • Branch-Adressen: Bei `b`, `bl`, `cbz`, `ret` und Co. kannst du nachvollziehen, wo dein Programm hin springt.
  • System Calls: erkenne z. B. `mov x8, #93; svc 0` als `exit(0)`.
  • Speicherzugriffe: beachte, welche Register für Adressierung verwendet werden.

`objdump` im Detail: Parameter und ihre Bedeutung

`objdump` ist ein sehr vielseitiges Werkzeug. Besonders beim Disassemblieren von ARM64-Binärdateien ist es hilfreich, die wichtigsten Optionen zu kennen.

Grundstruktur des Befehls

objdump [OPTIONEN] DATEI

Wichtige Optionen beim Disassemblieren

Option Beschreibung
`-d` Disassembliert alle **ausführbaren Segmente** (z. B. `.text`).
`-D` Disassembliert **die gesamte Datei**, auch nicht-ausführbare Abschnitte. Nützlich bei rohen Binärdateien oder unüblichen Formaten.
`-M <option>` Legt das Disassemblierungsformat fest. Mehrere Optionen sind durch Kommas trennbar (z. B. `-M reg-names=std,no-aliases`).
`--section=<name>` Disassembliert **nur eine bestimmte Sektion**, z. B. `.text`.
`--start-address=0xADDR` Beginnt das Disassemblieren **ab dieser Adresse**.
`--stop-address=0xADDR` Hört bei dieser Adresse auf.
`--architecture=aarch64` Setzt die Architektur explizit, z. B. bei rohen Binärdateien.
`-b binary` Interpretiert die Datei als **rohe Binärdaten** (keine ELF-Struktur).
`-s` Rohdaten anzeigen

Beispiele für ARM64

Einfaches Disassemblieren

objdump -d ./program

Ohne Register-Aliase (z. B. `wzr` statt `w0`)

objdump -d -M no-aliases ./program

Mit klassischen Register-Namen (x0–x30)

objdump -d -M reg-names=std ./program

Nur `.text`-Sektion

objdump -d --section=.text ./program

Disassemblieren roher Binärdaten

Wenn du z. B. ein 1:1-Memory-Dump oder reinen Maschinencode ohne ELF-Struktur hast:

objdump -D -b binary -m aarch64 yourfile.bin

Option `-M` im Detail

`-M` steht für **Disassembler-Modifikatoren** – mehrere können kombiniert werden:

  • `no-aliases`
Zeigt dir "echte" Instruktionen, keine Vereinfachungen.
> Beispiel: Statt `mov x0, x1` sieht man `orr x0, x1, xzr`.
  • `reg-names=std`
Standardregister (x0–x30, sp, pc).
  • `reg-names=raw`
Zeigt numerische Register: `r0`, `r1`, ...
  • `reg-names=apcs`
Zeigt Register gemäß dem **ARM Procedure Call Standard** (z. B. `a1`, `v1`, `fp`, `lr`).

Die `-s` Option: Rohdaten anzeigen

Die Option `-s` (kurz für `--full-contents`) zeigt den "Hexdump des gesamten Inhalts" der Datei, geordnet nach Sektionen.

Beispiel:

objdump -s ./program

Beispielausgabe:

Contents of section .text:
 0000 52800000 d2800021 d2800442 d2800003  R...!...B....
 0010 d2800004 8b030084 d2800025 d2800026  .........%...&

Bedeutung:

  • Linke Spalte: Offset innerhalb der Sektion.
  • Rechte Spalten: Hex-Werte der Bytes.
  • Ganz rechts (manchmal): ASCII-Darstellung, wenn druckbar.

Kombination mit `-d` und `-s`

Oft ist es sinnvoll, `-d` und `-s` "gemeinsam" zu nutzen – z. B., um zu sehen, wie ein Maschinenbefehl wirklich kodiert ist:

objdump -ds ./program

Oder: gezielt auf eine bestimmte Sektion fokussieren:

objdump -s --section=.rodata ./program

So kannst du "Strings, Konstanten oder Sprungtabellen" sichtbar machen, die nicht disassembliert, aber trotzdem mitverarbeitet werden.

Zusammenfassung – wann `-s` nützlich ist:

  • Debugging von Konstanten/Strings: Gibt Einblick in `.rodata`, `.data`, `.bss` etc.
  • Verstehen von Maschineninstruktionen: Hex-Werte → ARM64-Opcode
  • Analyse von Binärdateien ohne Symbole: Du siehst trotzdem, "was im Speicher liegt"
  • Reverse Engineering: Hex-Dumps helfen, Kontrollstrukturen und Datenlayout zu erkennen

Tipps:

  • Nur Funktionssymbol disassemblieren
Wenn du Symbole hast (nicht `strip` benutzt):
objdump -d ./blink --disassemble=_start
  • Wenn deine Datei keine Symbole enthält, verwende zusätzlich `nm` oder `readelf`, um Adressen herauszufinden.
  • Kombiniere `objdump` mit `grep` zur gezielten Analyse:
  objdump -d ./blink | grep "<_start>"

Negative Zahlen beim Disassemblieren mit `objdump`

1. Immediates werden hexadezimal dargestellt

`objdump` zeigt "sofortige Werte (Immediates)" standardmäßig in "Hexadezimalform" – unabhängig davon, ob es sich ursprünglich um negative Zahlen handelte.

  • Beispiel (Assemblercode):
mov x0, #-1
  • Disassemblierung:
mov x0, #0xffffffffffffffff

- `#-1` wird als `0xFFFFFFFFFFFFFFFF` dargestellt. - Das ist die **Zweierkomplement-Darstellung** von `-1` in 64 Bit.

2. Offsets bei Sprüngen und Speicherzugriffen

"Branches", "LOAD/STORE-Offsets" oder Stackzugriffe können negative Offsets haben. Ob `objdump` das als negativ darstellt, hängt vom Befehl ab – oft wird auch hier nur der "Zweierkomplementwert" in Hex gezeigt.

  • Beispiel:
ldur x0, [sp, #-16]
  • Disassemblierung:
ldur x0, [sp, #-0x10]

Hier ist `objdump` klug genug, das tatsächlich als "negativen Offset" anzuzeigen!

Aber: Das klappt "nicht bei allen Instruktionen"! Manchmal musst du selbst das Offset interpretieren.

3. Unsignierte Darstellung in Datenfeldern

Bei Verwendung von `objdump -s` (Hexdump der Daten) werden "alle Bytes als unsigned" dargestellt – also es gibt "keine negativen Zahlen" im Dump selbst, nur 0–255 (0x00–0xFF).

  • Beispiel:
.int32 -42
objdump -s -j .data a.out
  • Disassemblierung:
d6 ff ff ff

Das ist `0xFFFFFFD6`, also `-42` als "signed 32-Bit" im Zweierkomplement.