EMZETT.
Login

Kurz: read (Nr. 0) liest von einem Dateideskriptor (0 = Tastatur/stdin). Um die Beispiele reproduzierbar zu halten, lesen wir hier aus einer Datei, die wir selbst anlegen:

Teil des Kurses Assembler (x86-64)

Kapitel 6 von 8 im Kurs Assembler (x86-64) (Abschnitt „Betriebssystem“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Eingabe lesen

read (Nr. 0) liest von einem Dateideskriptor (0 = Tastatur/stdin). Um die Beispiele reproduzierbar zu halten, lesen wir hier aus einer Datei, die wir selbst anlegen:

section .data
    pfad    db "/tmp/asm_demo.txt", 0
    inhalt  db "Hallo aus einer Datei", 10
    laenge  equ $ - inhalt
 
section .bss
    puffer  resb 64
    zeile   resb 32
 
section .text
    global _start
 
_start:
    ; open(pfad, O_WRONLY|O_CREAT|O_TRUNC, 0644)
    mov rax, 2
    mov rdi, pfad
    mov rsi, 0x241
    mov rdx, 0644o
    syscall
    mov r12, rax              ; Dateideskriptor merken
 
    ; write(fd, inhalt, laenge)
    mov rax, 1
    mov rdi, r12
    mov rsi, inhalt
    mov rdx, laenge
    syscall
 
    ; close(fd)
    mov rax, 3
    mov rdi, r12
    syscall
 
    ; open(pfad, O_RDONLY)
    mov rax, 2
    mov rdi, pfad
    xor rsi, rsi
    xor rdx, rdx
    syscall
    mov r12, rax
 
    ; read(fd, puffer, 64)
    xor rax, rax
    mov rdi, r12
    mov rsi, puffer
    mov rdx, 64
    syscall
    mov r13, rax              ; Anzahl gelesener Bytes
 
    ; Ausgabe auf stdout
    mov rax, 1
    mov rdi, 1
    mov rsi, puffer
    mov rdx, r13
    syscall
 
    ; close und unlink (Datei löschen)
    mov rax, 3
    mov rdi, r12
    syscall
    mov rax, 87               ; unlink
    mov rdi, pfad
    syscall
 
    mov rax, 60
    xor rdi, rdi
    syscall

Ausgabe:

Hallo aus einer Datei

Fehler melden Systemaufrufe über negative Rückgabewerte in rax (-ENOENT usw.).

Kommandozeilenargumente und Umgebung

Beim Start eines Programms liegt auf dem Stack: argc, dann die Zeiger argv[0], argv[1] …, ein Nullzeiger und die Umgebungsvariablen:

section .bss
    puffer resb 32
 
section .text
    global _start
 
_start:
    mov rax, [rsp]            ; argc (Anzahl der Argumente)
    call ausgabe_zahl
    mov rbx, [rsp + 8]        ; argv[0] = Programmname
    xor rcx, rcx
.laenge:
    cmp byte [rbx + rcx], 0
    je .fertig
    inc rcx
    jmp .laenge
.fertig:
    mov rax, rcx
    call ausgabe_zahl         ; Länge des Programmnamens (./prog → 6)
 
    mov rax, 60
    xor rdi, rdi
    syscall
 
; --- Hilfsroutine: gibt die Zahl in RAX dezimal aus (mit Zeilenumbruch), alle Register bleiben erhalten ---
ausgabe_zahl:
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
    mov rcx, puffer + 20        ; Ende des Puffers
    mov byte [rcx], 10          ; Zeilenumbruch ans Ende
    mov rbx, 10
.schleife:
    xor rdx, rdx
    div rbx                     ; RAX = RAX / 10, RDX = Rest
    add dl, '0'                 ; Ziffer in ASCII umwandeln
    dec rcx
    mov [rcx], dl
    test rax, rax
    jnz .schleife
    mov rax, 1                  ; write
    mov rdi, 1
    mov rsi, rcx
    lea rdx, [puffer + 21]
    sub rdx, rcx
    syscall
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    ret

Ausgabe:

1
6

Wichtige Linux-Systemaufrufe

Nr.NameZweck
0readlesen
1writeschreiben
2openDatei öffnen
3closeschließen
9mmapSpeicher anfordern
39getpidProzess-ID
57forkProzess kopieren
59execveProgramm starten
60exitbeenden
87unlinkDatei löschen
35nanosleepwarten

Die vollständige Liste: man 2 syscalls, /usr/include/asm/unistd_64.h.

Programme analysieren (Reverse Engineering)

Assembler ist das Werkzeug, um fertige Programme zu verstehen:

objdump -d -M intel programm | less    # Disassemblieren
gdb ./programm
(gdb) break _start        # Haltepunkt
(gdb) run
(gdb) stepi               # einen Befehl ausführen
(gdb) info registers      # Register ansehen
(gdb) x/8gx $rsp          # Speicher ab dem Stack anzeigen (8 Wörter)
(gdb) x/s 0x402000        # Zeichenkette an Adresse anzeigen
(gdb) disassemble

Weitere Werkzeuge: Ghidra (kostenloser Dekompilierer der NSA), radare2/Cutter, IDA Free, strace (zeigt Systemaufrufe eines Programms), ltrace, readelf, nm, strings.

strace ./hallo           # Systemaufrufe live: write(1, "Hallo, Welt!\n", 13) = 13

Compiler zeigen ihren Assembler-Code mit gcc -S -O2 -masm=intel datei.c und im Browser mit Compiler Explorer (godbolt.org).

Merke

  • Systemaufrufe sind die Schnittstelle zum Betriebssystem: read, write, open, close, exit
  • Fehler stehen als negative Werte in rax
  • Beim Start liegen argc, argv und die Umgebung auf dem Stack
  • Zum Analysieren: objdump, gdb, strace, Ghidra, Compiler Explorer

Übungsaufgabe

Schreibe ein Programm, das mit getpid (Nr. 39) die Prozess-ID abfragt und prüft, dass sie größer als 0 ist.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Der Stack und Funktionen

Weiter: Fließkomma und SIMD

Alle Kapitel: Assembler (x86-64) im Überblick