Mengendalikan alur eksekusi CPU: jmp tanpa syarat, perbandingan dengan cmp lalu jump kondisional je/jne/jg/jl untuk integer signed dan ja/jb untuk unsigned, loop dan jrcxz, serta call/ret yang menjadi dasar pemanggilan fungsi di episode 8.

Sejauh ini program kalian selalu berjalan lurus dari atas ke bawah. Padahal kekuatan sebuah program ada pada kemampuannya mengambil keputusan — dan di assembly, keputusan dibuat dengan jump. Episode 6 membedah cara CPU mengubah RIP (ingat episode 2?) lewat instruksi jmp dan conditional jump.
Mengapa ini penting? Karena semua struktur bahasa tingkat tinggi — if, for, while, switch — pada akhirnya diterjemahkan compiler menjadi jump. Saat kalian membaca objdump di episode 16 atau menulis exploit di episode 17, kalian akan terus membaca pola cmp → jXX. Kuasai sekarang, dan sisanya akan terasa familiar.
Instruksi jmp memaksa RIP menunjuk ke label target, tanpa syarat apa pun:
_start:
jmp skip_data
; instruksi di sini tidak akan pernah dieksekusi
skip_data:
mov rax, 60
mov rdi, 0
syscallPerhatikan bahwa jmp di sini maju (forward). jmp juga bisa mundur (backward) — pola yang dipakai untuk loop:
section .data
msg db ".", 0x0a
len equ $-msg
section .text
global _start
_start:
mov rcx, 5 ; counter
loop_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, len
syscall
dec rcx
jnz loop_start ; lompat ke loop_start jika rcx != 0
mov rax, 60
mov rdi, 0
syscallIni program yang mencetak . sebanyak 5 kali. Bandingkan dengan for (int i=0; i<5; i++) di C — keduanya identik secara semantik.
cmp menghitung op1 - op2 secara internal tanpa menyimpan hasil, hanya memperbarui flag di RFLAGS (episode 5). Perbandingan ini kemudian "dibaca" oleh conditional jump:
cmp rax, rbx ; hitung rax - rbx, set ZF/SF/CF/OF
; flag sudah berubah, nilai rax tidak berubahSetelah cmp, kalian memilih jump berdasarkan flag. Penting untuk membedakan dua keluarga conditional jump:
| Instruksi | Kondisi | Arti |
|---|---|---|
je | ZF = 1 | sama dengan (==) |
jne | ZF = 0 | tidak sama (!=) |
jg | ZF=0 dan SF=OF | lebih besar (>) |
jge | SF=OF | lebih besar atau sama (>=) |
jl | SF≠OF | lebih kecil (<) |
jle | ZF=1 atau SF≠OF | lebih kecil atau sama (<=) |
| Instruksi | Kondisi | Arti |
|---|---|---|
ja | CF=0 dan ZF=0 | above (>) |
jae | CF=0 | above or equal (>=) |
jb | CF=1 | below (<) |
jbe | CF=1 atau ZF=1 | below or equal (<=) |
Warning
Jangan pernah mencampur signed dan unsigned. cmp rax, 5 lalu ja akan salah jika rax berisi angka negatif: di bit level, 0xFFFFFFFFFFFFFFFF (yaitu -1 signed) lebih besar dari 5 dalam perbandingan unsigned. Pilih keluarga jump sesuai tipe data kalian — ini sumber bug klasik pada program yang menangani ukuran dan panjang.
section .data
greater db "rax lebih besar", 0x0a
glen equ $-greater
smaller db "rax lebih kecil", 0x0a
slen equ $-smaller
section .text
global _start
_start:
mov rax, 42
mov rbx, 7
cmp rax, rbx
jle else_branch
; if (rax > rbx)
mov rax, 1
mov rdi, 1
mov rsi, greater
mov rdx, glen
syscall
jmp done
else_branch:
; else
mov rax, 1
mov rdi, 1
mov rsi, smaller
mov rdx, slen
syscall
done:
mov rax, 60
mov rdi, 0
syscallPerhatikan pola jle else_branch untuk melompati blok if ketika kondisinya salah, lalu jmp done setelah blok if agar eksekusi tidak jatuh ke else. Ini persis seperti yang compiler hasilkan untuk if/else dalam bahasa tingkat tinggi.
x86-64 menyediakan instruksi loop khusus yang menggabungkan counter dan jump:
loop label → kurangi rcx, lompat ke label jika rcx masih bukan nol.jrcxz label → lompat jika rcx adalah nol (biasanya dipakai untuk skip saat panjang = 0). mov rcx, 5
repeat:
; lakukan sesuatu 5 kali
loop repeat ; dec rcx; jnz repeatTip
Dalam praktik, compiler modern cenderung memakai dec + jnz daripada loop, karena loop memiliki latency yang lebih tinggi di sebagian besar CPU modern. Namun loop tetap penting dipahami — kalian akan menemukannya di banyak kode lama dan di materi reverse engineering.
call dan ret adalah jembatan menuju episode 8. Ketika call func dieksekusi:
func.Ketika ret dieksekusi:
call._start:
call my_func ; simpan alamat kembali ke stack, lompat
mov rax, 60
mov rdi, 0
syscall
my_func:
; do something
ret ; kembali ke instruksi setelah callInilah mekanisme paling penting untuk struktur program — dan juga titik rapuh bagi keamanan (buffer overflow di episode 17 mengincar alamat kembali ini). Kita bedah stack frame, prologue, dan epilogue secara penuh di episode 8.
cmp urutan operan: cmp rax, rbx membedakan "rax dibanding rbx". Membaliknya akan membalik logika jump.jmp setelah blok if → eksekusi jatuh ke blok else.loop mengubah rcx → jangan pakai rcx untuk hal lain di dalam loop jika memakai loop.call tanpa ret (atau sebaliknya) akan mengacaukan alamat kembali; kita bedah di episode 8.Pada episode 6 ini, kalian telah menguasai cara CPU mengambil keputusan.
Inti yang harus dibawa pulang:
jmp = lompat tanpa syarat; bisa maju dan mundur.cmp op1, op2 menghitung op1 - op2 hanya untuk set flag.je/jne/jg/jl/jge/jle; unsigned: ja/jae/jb/jbe — jangan dicampur.loop = dec rcx; jnz; jrcxz untuk cek nol.call mendorong alamat kembali ke stack, ret mem-pop-nya.Di episode 7 selanjutnya, kita bedah operasi aritmetika, logika, dan bitwise: add/sub, mul/imul dan div/idiv yang rumit, and/or/xor/not, serta shift shl/shr/sar dan rotate rol/ror — termasuk trik shift untuk perkalian dan pembagian cepat yang dipakai compiler setiap hari. Pastikan flag di episode ini sudah kalian pahami, karena aritmetika akan memainkannya.