10 Soal ASM ke C
5 Soal 32-bit (x86) dan 5 Soal 64-bit (x64) standar ujian UTS. Setiap soal dilengkapi analisis langkah demi langkah dari nol (identifikasi offset lokal, bukan menebak nama), visualisasi tabel stack memory, dan rekonstruksi kode bahasa C final.
push ebp
mov ebp, esp
sub esp, 16
mov DWORD PTR [ebp-4], 15
mov DWORD PTR [ebp-8], 25
mov edx, DWORD PTR [ebp-4]
mov eax, DWORD PTR [ebp-8]
add eax, edx
mov DWORD PTR [ebp-12], eax
mov eax, DWORD PTR [ebp-12]
leave
ret
Langkah 1: Identifikasi Prolog & Alokasi Memori Stack
Instruksi push ebp; mov ebp, esp membentuk basis stack frame baru. Instruksi sub esp, 16 memesan memori 16 byte pada stack untuk variabel lokal.
Langkah 2: Tracking Offset Stack & Nilai
| Offset Relatif (Address) | Ukuran | Nilai Saat Ini | Inferensi Variabel C |
|---|---|---|---|
[ebp - 4] | 4 Byte (DWORD) | 15 (0x0000000F) | int a = 15; |
[ebp - 8] | 4 Byte (DWORD) | 25 (0x00000019) | int b = 25; |
[ebp - 12] | 4 Byte (DWORD) | a + b = 40 (0x00000028) | int c = a + b; |
[ebp] | 4 Byte | Saved EBP | Frame Pointer Caller |
Langkah 3: Analisis Instruksi Body & Epilog
mov edx, [ebp-4]menyalin nilai 15 ke EDX.mov eax, [ebp-8]menyalin nilai 25 ke EAX.add eax, edxmenghitung25 + 15 = 40, disimpan di EAX.mov [ebp-12], eaxmenyimpan hasil penjumlahan ke[ebp-12].mov eax, [ebp-12]memuat hasil ke EAX sebagai return value sebelumleave; ret.
Kode C Final:
int soal1() {
int a = 15;
int b = 25;
int c = a + b;
return c;
}
push ebp
mov ebp, esp
sub esp, 16
mov DWORD PTR [ebp-4], 10
mov DWORD PTR [ebp-8], 20
mov eax, DWORD PTR [ebp-4]
cmp eax, DWORD PTR [ebp-8]
jle .L2
mov DWORD PTR [ebp-12], 100
jmp .L3
.L2:
mov DWORD PTR [ebp-12], 200
.L3:
mov eax, DWORD PTR [ebp-12]
leave
ret
Langkah 1: Analisis Offset Variabel
| Offset Relatif | Nilai Awal | Inferensi Variabel C |
|---|---|---|
[ebp - 4] | 10 | int x = 10; |
[ebp - 8] | 20 | int y = 20; |
[ebp - 12] | Uninitialized (Nantinya 100 atau 200) | int result; |
Langkah 2: Evaluasi Alur Kontrol Percabangan
mov eax, [ebp-4]: memuatxke EAX.cmp eax, [ebp-8]: membandingkanxdengany(operasix - y).jle .L2: Jump if Less or Equal (jikax <= y, lompat ke label.L2).- Jika kondisi
x <= ySALAH (artinyax > y), alur berlanjut:result = 100;lalujmp .L3(melewati blok else). - Di label
.L2:result = 200;. - Di label
.L3: Mengembalikanresultmelalui EAX.
Kode C Final:
int soal2() {
int x = 10;
int y = 20;
int result;
if (x > y) {
result = 100;
} else {
result = 200;
}
return result;
}
push ebp
mov ebp, esp
sub esp, 16
mov DWORD PTR [ebp-4], 1
mov DWORD PTR [ebp-8], 10
jmp .L_COND
.L_BODY:
mov eax, DWORD PTR [ebp-4]
add eax, eax
mov DWORD PTR [ebp-4], eax
.L_COND:
cmp DWORD PTR [ebp-4], 100
jle .L_BODY
mov eax, DWORD PTR [ebp-4]
leave
ret
Langkah 1: Identifikasi Pola Perulangan (Jump to Condition)
Pola compiler GCC standar saat mengoptimasi perulangan: inisialisasi variabel, lompat langsung ke pengecekan kondisi (jmp .L_COND). Jika kondisi terpenuhi (jle .L_BODY), ulangi tubuh loop.
| Offset Relatif | Nilai Awal | Inferensi Variabel C |
|---|---|---|
[ebp - 4] | 1 | int n = 1; |
[ebp - 8] | 10 | Variabel tidak digunakan (dead code/unused int dummy = 10;) |
Langkah 2: Operasi di dalam Body Loop
add eax, eax : menggandakan nilai EAX (ekuivalen n = n * 2 atau n += n).
Kondisi keluar: loop berjalan selama n <= 100.
Kode C Final:
int soal3() {
int n = 1;
while (n <= 100) {
n = n * 2; // atau n += n;
}
return n;
}
push ebp
mov ebp, esp
sub esp, 16
mov DWORD PTR [ebp-4], 0 ; total = 0
mov DWORD PTR [ebp-8], 1 ; i = 1
jmp .L_FOR_TEST
.L_FOR_BODY:
mov eax, DWORD PTR [ebp-8]
add DWORD PTR [ebp-4], eax ; total += i
add DWORD PTR [ebp-8], 1 ; i++
.L_FOR_TEST:
cmp DWORD PTR [ebp-8], 10 ; compare i with 10
jle .L_FOR_BODY
mov eax, DWORD PTR [ebp-4]
leave
ret
Tabel Stack Memory:
| Offset Stack | Nilai Inisial | Peran Variabel C |
|---|---|---|
[ebp - 4] | 0 | int total = 0; |
[ebp - 8] | 1 | int i = 1; |
Penjelasan Langkah Perulangan:
- Inisialisasi
total = 0dan counteri = 1. - Lompat ke
.L_FOR_TEST: membandingkanidengan10. - Jika
i <= 10, eksekusi body:total += i, kemudian incrementi++. - Ketika
i = 11, kondisijlegagal, keluar loop dan returntotal.
Kode C Final:
int soal4() {
int total = 0;
for (int i = 1; i <= 10; i++) {
total += i;
}
return total;
}
; --- Fungsi Pembantu: hitung(a, b) ---
hitung:
push ebp
mov ebp, esp
mov edx, DWORD PTR [ebp+8] ; argumen 1
mov eax, DWORD PTR [ebp+12] ; argumen 2
imul eax, edx
pop ebp
ret
; --- Fungsi Utama: caller() ---
caller:
push ebp
mov ebp, esp
sub esp, 16
mov DWORD PTR [ebp-4], 7
mov DWORD PTR [ebp-8], 6
push DWORD PTR [ebp-8] ; push arg2 (6)
push DWORD PTR [ebp-4] ; push arg1 (7)
call hitung
add esp, 8 ; caller cleans up stack
mov DWORD PTR [ebp-12], eax
mov eax, DWORD PTR [ebp-12]
leave
ret
Analisis Konvensi cdecl:
Parameter didorong ke stack dari kanan ke kiri (push [ebp-8] lalu push [ebp-4]). Di dalam fungsi hitung:
[ebp + 8]merujuk pada argumen pertama (nilai 7).[ebp + 12]merujuk pada argumen kedua (nilai 6).imul eax, edxmelakukan perkalian:7 * 6 = 42.- Setelah
call hitung, perintahadd esp, 8membersihkan 2 argumen (2 * 4 bytes) dari stack.
Kode C Final:
int hitung(int a, int b) {
return a * b;
}
int caller() {
int x = 7;
int y = 6;
int res = hitung(x, y);
return res;
}
; --- swap(int *p1, int *p2) ---
swap:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi ; p1 (pointer 8-byte)
mov QWORD PTR [rbp-16], rsi ; p2 (pointer 8-byte)
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax] ; temp = *p1
mov DWORD PTR [rbp-20], eax ; simpan temp di stack
mov rax, QWORD PTR [rbp-16]
mov edx, DWORD PTR [rax] ; edx = *p2
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax], edx ; *p1 = *p2
mov rax, QWORD PTR [rbp-16]
mov edx, DWORD PTR [rbp-20] ; edx = temp
mov DWORD PTR [rax], edx ; *p2 = temp
nop
pop rbp
ret
; --- main_test() ---
main_test:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 100 ; a = 100
mov DWORD PTR [rbp-8], 200 ; b = 200
lea rdx, [rbp-8]
lea rax, [rbp-4]
mov rsi, rdx ; arg 2 = &b
mov rdi, rax ; arg 1 = &a
call swap
leave
ret
Langkah 1: Identifikasi Passing Parameter via Register RDI & RSI
Pada System V AMD64, rdi menampung alamat &a dan rsi menampung alamat &b yang dihasilkan oleh instruksi lea.
Langkah 2: Tracking Pointer Dereferencing
mov rax, [rbp-8]diikutimov eax, [rax]: membaca nilai integer yang ditunjuk oleh pointerp1(dereference*p1).DWORD PTR [rbp-20]menyimpan nilai sementara (int temp = *p1).*p1 = *p2;dan*p2 = temp;. Ini adalah algoritma penukaran nilai (swap) menggunakan pointer.
Kode C Final:
void swap(int *p1, int *p2) {
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void main_test() {
int a = 100;
int b = 200;
swap(&a, &b);
}
sum_array:
push rbp
mov rbp, rsp
sub rsp, 32
; Inisialisasi array 4 elemen int arr[4] = {10, 20, 30, 40};
mov DWORD PTR [rbp-24], 10 ; arr[0]
mov DWORD PTR [rbp-20], 20 ; arr[1]
mov DWORD PTR [rbp-16], 30 ; arr[2]
mov DWORD PTR [rbp-12], 40 ; arr[3]
mov DWORD PTR [rbp-4], 0 ; sum = 0
mov DWORD PTR [rbp-8], 0 ; i = 0
jmp .L_ARR_COND
.L_ARR_BODY:
mov eax, DWORD PTR [rbp-8] ; eax = i
cdqe ; convert dword to qword (rax = (long)i)
mov edx, DWORD PTR [rbp-24+rax*4] ; edx = arr[i] (scale 4 bytes)
add DWORD PTR [rbp-4], edx ; sum += arr[i]
add DWORD PTR [rbp-8], 1 ; i++
.L_ARR_COND:
cmp DWORD PTR [rbp-8], 3 ; i <= 3
jle .L_ARR_BODY
mov eax, DWORD PTR [rbp-4] ; return sum
leave
ret
Visualisasi Stack Array Memory:
| Address Offset | Nilai | Indeks Array |
|---|---|---|
[rbp - 24] | 10 (0x0A) | arr[0] |
[rbp - 20] | 20 (0x14) | arr[1] |
[rbp - 16] | 30 (0x1E) | arr[2] |
[rbp - 12] | 40 (0x28) | arr[3] |
[rbp - 8] | Counter i (0..4) | int i |
[rbp - 4] | Total Sum | int sum |
Analisis Formula SIB (Scale-Index-Base):
[rbp-24 + rax*4] : Base alamat array berada di rbp-24. Karena elemennya bertipe int (4 bytes), indeks rax dikalikan 4 untuk melompat antar elemen memori.
Kode C Final:
int sum_array() {
int arr[4] = {10, 20, 30, 40};
int sum = 0;
for (int i = 0; i <= 3; i++) { // atau i < 4
sum += arr[i];
}
return sum;
}
; Struct Student { int id; int score; char grade; }
get_total:
push rbp
mov rbp, rsp
sub rsp, 16
; inisialisasi struct di stack [rbp-16]
mov DWORD PTR [rbp-16], 101 ; s.id = 101 (offset 0)
mov DWORD PTR [rbp-12], 95 ; s.score = 95 (offset 4)
mov BYTE PTR [rbp-8], 65 ; s.grade = 'A' (ASCII 65, offset 8)
mov eax, DWORD PTR [rbp-12] ; ambil score
add eax, 5 ; score + 5 bonus
leave
ret
Analisis Layout Memori Struct:
| Offset dari [rbp-16] | Ukuran | Nilai | Field Struct C |
|---|---|---|---|
+0 byte ([rbp-16]) | 4 byte int | 101 | int id; |
+4 byte ([rbp-12]) | 4 byte int | 95 | int score; |
+8 byte ([rbp-8]) | 1 byte char | 'A' (65) | char grade; |
Kode C Final:
struct Student {
int id;
int score;
char grade;
};
int get_total() {
struct Student s;
s.id = 101;
s.score = 95;
s.grade = 'A';
return s.score + 5;
}
fib:
push rbp
mov rbp, rsp
push rbx ; callee-saved register
sub rsp, 24
mov DWORD PTR [rbp-20], edi ; n (parameter pertama)
cmp DWORD PTR [rbp-20], 1
jg .L_RECURSE
mov eax, DWORD PTR [rbp-20] ; return n jika n <= 1
jmp .L_END
.L_RECURSE:
mov eax, DWORD PTR [rbp-20]
sub eax, 1
mov edi, eax ; arg1 = n - 1
call fib
mov ebx, eax ; simpan hasil fib(n-1) di ebx
mov eax, DWORD PTR [rbp-20]
sub eax, 2
mov edi, eax ; arg1 = n - 2
call fib
add eax, ebx ; hasil = fib(n-2) + fib(n-1)
.L_END:
add rsp, 24
pop rbx
pop rbp
ret
Langkah Analisis Rekursi:
- Base Case:
cmp [rbp-20], 1; jg .L_RECURSE. Jikan <= 1, langsung lompat ke akhir dan me-returnn. - Recursive Call 1: Memanggil
fib(n - 1), return value disimpan sementara di registerebx. - Recursive Call 2: Memanggil
fib(n - 2), lalu menjumlahkaneax = fib(n-2) + ebx.
Kode C Final:
int fib(int n) {
if (n <= 1) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
crypto_func:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi ; val
mov DWORD PTR [rbp-8], esi ; key
mov eax, DWORD PTR [rbp-4]
xor eax, DWORD PTR [rbp-8] ; eax = val ^ key
shl eax, 2 ; eax = eax << 2
and eax, 0x000000FF ; eax = eax & 0xFF
pop rbp
ret
Analisis Operasi Bitwise:
rdimenampung argumen 1 (val), disimpan di[rbp-4].rsimenampung argumen 2 (key), disimpan di[rbp-8].xor eax, [rbp-8]: Bitwise XORval ^ key.shl eax, 2: Bitwise Shift Left sebanyak 2 bit(val ^ key) << 2.and eax, 0xFF: Bitwise AND dengan masker 8-bit (0xFF)((val ^ key) << 2) & 0xFF.
Kode C Final:
int crypto_func(int val, int key) {
int res = (val ^ key) << 2;
return res & 0xFF;
// atau ringkas: return ((val ^ key) << 2) & 0xFF;
}