Skip to content

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)

  1. Return address pushes på stacken

  2. Function prologue forbereder stacken

  3. 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

  1. xor eax, eax er hurtigere end mov eax, 0

  2. Shift-instruktioner er hurtigere end mul/div med 2

  3. test eax, eax bruger færre bytes end cmp eax, 0

  4. lea kan 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.