x86 Assembly
Introduktion
Assembly-sproget er det laveste niveau af menneskelæsbart sprog. Det er også det højeste niveau, som en binær fil pålideligt kan dekompileres til. Når vi reverse engineer malware, arbejder vi med assembly-kode, fordi den kompilerede binære fil ikke længere indeholder variabelnavne, funktionsnavne osv.
Opcodes og Operander
Binary og Hex
-
Kode gemmes på disken som binary (1'er og 0'er)
-
8 bits = 1 byte, ofte vist som hex
-
Eksempel:
10100101(binary) =0xA5(hex)
Opcodes
-
Opcodes er tal der svarer til instruktioner udført af CPU'en
-
En disassembler oversætter opcodes til assembly-instruktioner
Generelle Instruktioner
MOV-instruktionen
Flytter en værdi fra én lokation til en anden.
Syntaks:
mov destination, source
Eksempler:
mov eax, 0x5f ; Flyt konstant til register
mov ebx, eax ; Flyt register til register
mov eax, [0x5fc53e] ; Flyt værdi fra hukommelse til register
mov ebx, 0x5fc53e ; Flyt adresse til register
mov eax, [ebx] ; Flyt værdi fra hukommelse (adresse i ebx) til eax
mov eax, [ebp+4] ; Flyt værdi fra hukommelse (ebp+4) til eax
Vigtigt: - MOV kan ikke flytte direkte fra hukommelse til hukommelse
- Der skal bruges et register som mellemled
LEA-instruktionen (Load Effective Address)
Flytter adressen til source ind i destination (ikke værdien).
Syntaks:
lea destination, source
Eksempel:
lea eax, [ebp+4] ; Flyt adressen (ebp+4) til eax
Forskel fra MOV:
- mov eax, [ebp+4] → flytter værdien fra hukommelsen
lea eax, [ebp+4]→ flytter adressen (ebp+4)
Anvendelse: - Ofte brugt til aritmetiske operationer på registre i én instruktion
- Kompilere bruger det til optimering
NOP-instruktionen (No Operation)
Udfører ingen operation - går bare til næste instruktion.
Syntaks:
nop
Anvendelse: - Forbruger CPU-cyklusser mens man venter - NOP sled: Malware-forfattere bruger mange nop-instruktioner før shellcode for at sikre execution starter det rigtige sted
Shift-instruktioner
Skifter bits i et register til højre eller venstre.
Syntaks:
shr destination, count ; Shift right
shl destination, count ; Shift left
Eksempel:
00000010 shl 1 = 00000100 (samme som * 2)
00000100 shr 1 = 00000010 (samme som / 2)
Anvendelse: - Hurtigere end multiplikation/division med 2 eller potenser af 2
- Bit der skiftes ud går til Carry Flag (CF)
Rotate-instruktioner
Lignende shift, men bits roteres tilbage til den anden ende.
Syntaks:
ror destination, count ; Rotate right
rol destination, count ; Rotate left
Eksempel:
10101010 ror 1 = 01010101
01010101 rol 1 = 10101010
Flags (Status Registre)
Flags angiver resultatet af operationer. De vigtigste:
| Flag | Navn | Sættes når |
|---|---|---|
| ZF | Zero Flag | Resultatet er 0 |
| CF | Carry Flag | Resultatet er for stort/lille til destination |
| SF | Sign Flag | Resultatet er negativt / MSB er 1 |
| PF | Parity Flag | Antallet af 1-bits i resultatet er lige |
Aritmetiske Instruktioner
Addition og Subtraktion
Addition:
add destination, value ; destination = destination + value
Subtraktion:
sub destination, value ; destination = destination - value
Flags: - ZF sættes hvis resultatet er 0
- CF sættes hvis destination < value (ved subtraktion)
Multiplikation og Division
Multiplikation:
mul value ; edx:eax = eax * value (64-bit resultat)
-
Laveste 32 bits i EAX
-
Højeste 32 bits i EDX
Division:
div value ; eax = edx:eax / value, edx = rest
-
Kvotient i EAX
-
Rest i EDX
Increment og Decrement
Increment (forøg med 1):
inc eax ; eax = eax + 1
Decrement (reducer med 1):
dec eax ; eax = eax - 1
Logiske Instruktioner
AND-instruktion
Bitwise AND operation.
Syntaks:
and al, 0x7c
Logik: - Returnerer 1 kun hvis begge inputs er 1
- Ellers returnerer 0
Eksempel:
11111100 (0xFC)
AND
01111100 (0x7C)
--------
01111100 (0x7C)
OR-instruktion
Bitwise OR operation.
Syntaks:
or al, 0x7c
Logik: - Returnerer 1 hvis mindst én input er 1
- Returnerer 0 kun hvis begge inputs er 0
Eksempel:
10001100 (0x8C)
OR
01111100 (0x7C)
--------
11111100 (0xFC)
NOT-instruktion
Inverterer alle bits.
Syntaks:
not al
Eksempel:
11110000
NOT
--------
00001111
XOR-instruktion
Exclusive OR operation.
Syntaks:
xor al, 0x7c
Logik: - Returnerer 1 hvis inputs er forskellige
- Returnerer 0 hvis inputs er ens
Eksempel:
11111100 (0xFC)
XOR
01111100 (0x7C)
--------
10000000 (0x80)
Vigtig anvendelse:
xor eax, eax ; Nulstiller eax (mere optimeret end mov eax, 0)
Conditionals og Branching
TEST-instruktionen
Udfører bitwise AND men gemmer ikke resultatet - sætter kun ZF.
Syntaks:
test destination, source
Anvendelse: - Bruges ofte til at checke om operand er NULL
-
Eksempel:
test eax, eax(hvis eax=0, sættes ZF) -
Bruger færre bytes end at sammenligne med 0
CMP-instruktionen (Compare)
Sammenligner to operander ved subtraktion uden at gemme resultatet.
Syntaks:
cmp destination, source
Flags der sættes: - ZF = 1 hvis destination == source
-
CF = 1 hvis source > destination
-
ZF = 0 og CF = 0 hvis destination > source
JMP-instruktionen (Jump)
Springer ubetinget til en specifik lokation.
Syntaks:
jmp location
- Ændrer Instruction Pointer til location
Conditional Jumps (Betingede Spring)
Springer baseret på flag-værdier.
Almindelige conditional jumps:
| Instruktion | Betydning | Betingelse |
|---|---|---|
| JE / JZ | Jump if Equal / Jump if Zero | ZF = 1 |
| JNE / JNZ | Jump if Not Equal / Jump if Not Zero | ZF = 0 |
| JG / JNLE | Jump if Greater | ZF = 0 og SF = OF |
| JGE / JNL | Jump if Greater or Equal | SF = OF |
| JL / JNGE | Jump if Less | SF ≠ OF |
| JLE / JNG | Jump if Less or Equal | ZF = 1 eller SF ≠ OF |
| JA / JNBE | Jump if Above (unsigned) | CF = 0 og ZF = 0 |
| JB / JNAE | Jump if Below (unsigned) | CF = 1 |
Sammenligning resultat → Flags:
| Condition | ZF | CF | Bemærkning |
|---|---|---|---|
| eax == ebx | 1 | 0 | Lige store |
| eax > ebx | 0 | 0 | eax større |
| eax < ebx | 0 | 1 | eax mindre |
Stack og Funktionskald
PUSH-instruktionen
Pusher værdi på stacken.
Syntaks:
push source
Virkning: - Gemmer source på toppen af stacken
- ESP (Stack Pointer) dekrementeres
Specielle varianter:
pusha ; Push alle 16-bit registre (AX, BX, CX, DX, SI, DI, SP, BP)
pushad ; Push alle 32-bit registre (EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP)
POP-instruktionen
Popper værdi fra stacken.
Syntaks:
pop destination
Virkning: - Henter værdi fra toppen af stacken til destination
- ESP (Stack Pointer) inkrementeres
Specielle varianter:
popa ; Pop til alle 16-bit registre (DI, SI, BP, BX, DX, CX, AX)
popad ; Pop til alle 32-bit registre (EDI, ESI, EBP, EBX, EDX, ECX, EAX)
CALL-instruktionen
Udfører et funktionskald.
Syntaks:
call location
Hvad sker der: 1. Argumenter placeres på stacken eller i registre (afhænger af calling convention)
-
Return address pushes på stacken
-
Function prologue forbereder stacken
-
Function epilogue gendanner stacken ved retur
Praktiske Eksempler
Aritmetisk Kode
mov eax, 20h
mov ebx, 30h
add eax, ebx ; eax = 50h
nop
nop
sub eax, ebx ; eax = 20h
inc ebx ; ebx = 31h
dec ebx ; ebx = 30h
mul eax ; edx:eax = eax * eax
Stack-operationer
mov eax, 10h
mov ebx, 15h
mov ecx, 20h
mov edx, 25h
push eax ; Push 10h
push ebx ; Push 15h
push ecx ; Push 20h
push edx ; Push 25h
; LIFO - Last In First Out
pop eax ; eax = 25h (sidste pushed)
pop ebx ; ebx = 20h
pop ecx ; ecx = 15h
pop edx ; edx = 10h (først pushed)
CMP og TEST Sammenligning
Når værdier er lige:
mov eax, 10h
mov ebx, 10h
cmp eax, ebx ; ZF = 1, CF = 0
test eax, ebx ; ZF = 0 (hvis eax != 0)
Når eax > ebx:
mov eax, 20h
mov ebx, 10h
cmp eax, ebx ; ZF = 0, CF = 0
Når eax < ebx:
mov eax, 20h
mov ebx, 40h
cmp eax, ebx ; ZF = 0, CF = 1
LEA-eksempel
mov eax, 20h
mov ebx, 30h
add eax, ebx ; eax = 50h
mov [eax], ebx ; Gem 30h på adresse 50h
add ebx, 15h ; ebx = 45h
mov ecx, 6
mov [ebx+ecx], eax ; Gem eax på adresse (45h+6)
lea eax, [ebx+ecx] ; eax = 4Bh (adressen, ikke værdien)
push eax
push ebx
pop ecx ; ecx = 45h
Vigtige Pointer
Hukommelsesoperationer
-
Tilladt: Register → Hukommelse, Hukommelse → Register
-
IKKE tilladt: Hukommelse → Hukommelse direkte
Optimeringstricks
-
xor eax, eaxer hurtigere endmov eax, 0 -
Shift-instruktioner er hurtigere end mul/div med 2
-
test eax, eaxbruger færre bytes endcmp eax, 0 -
leakan udføre addition i én instruktion
Malware Indicators
-
NOP sleds: Mange nop-instruktioner før shellcode
-
pusha/pushad: Manuel assembly-injection (ofte shellcode)
-
TF (Trap Flag): Malware checker for debuggers
Source
https://tryhackme.com/room/x86assemblycrashcourse
Bemærkning
Noterne er struktureret og rettet med hjælp af LLM'en Claude.