Belajar Assembly x86-64
(gpfault.net)- Ini adalah pengenalan artikel pertama dari seri untuk pemula assembly x86-64
- Menyediakan penjelasan pemasangan alat dan struktur dasar berdasarkan sistem 64-bit modern
- Menggunakan Flat Assembler (FASM) dan WinDbg sebagai alat utama untuk pengembangan dan debugging
- Mencakup ringkasan pengetahuan inti yang dibutuhkan di praktik nyata seperti format PE, impor DLL, dan calling convention Windows
- Berfokus pada pengalaman praktik menulis program sederhana untuk keluar dan prosedur debugging
Pengantar dan makna
- Saat pertama kali mempelajari assembly x86, penulis pernah mengikutinya di kampus dengan pendekatan berbasis lingkungan lama (16-bit, DOS, memori tersegmentasi)
- Karena saat ini prosesor 64-bit sudah menjadi arus utama, seri ini hanya membahas lingkungan x86-64 yang benar-benar digunakan dan menyingkirkan semua elemen lama
- Tutorial ini berfokus pada pengembangan program 64-bit yang berjalan di lingkungan sistem operasi Windows
- Dimulai dari kode minimum yang mengakses OS secara langsung tanpa menggunakan library
- Artikel ini ditujukan bagi pengembang yang baru ingin belajar assembly, dengan asumsi memiliki pengetahuan dasar C/C++
Menyiapkan alat pengembangan
Assembler
- CPU hanya dapat menafsirkan machine code yang sulit dipahami manusia, dan assembly language adalah bentuk kode yang dapat dibaca manusia untuk itu
- Program yang mengubah assembly language menjadi machine code disebut assembler
- Assembly x86-64 tidak memiliki standar tunggal, sehingga setiap assembler memiliki sintaks dan cara kerja yang berbeda
- Seri ini menggunakan Flat Assembler(FASM), yang kecil, mudah digunakan, serta menyediakan sistem makro dan editor yang kuat
Debugger
- Untuk menganalisis kode assembly yang ditulis dan mengamati alur eksekusinya, debugger digunakan sebagai alat yang wajib
- WinDbg direkomendasikan, dan memungkinkan pengecekan serta manipulasi register, memori, dan kode assembly secara terpisah
- Dapat diinstal dengan memilih komponen saja dari Windows 10 SDK
- Melalui debugger, keadaan internal program, struktur memori, dan perubahan register dapat diamati secara langsung
Sudut pandang pemrograman assembly
Struktur CPU dan instruction set
- CPU hanya dapat melakukan tindakan terbatas sesuai instruction set tertentu
- Instruksi adalah unit kerja dasar yang dapat dijalankan CPU
- Setiap instruksi bekerja sangat sederhana bersama parameternya (menyimpan nilai, operasi aritmetika, dan sebagainya)
- Dalam pemrograman tingkat rendah dan debugging, memahami bahwa struktur ini adalah dasar dari semua konsep tingkat tinggi merupakan hal yang penting
Register
- Register adalah area memori khusus yang sangat cepat dan tertanam di dalam CPU
- Di x86-64 terdapat 16 general-purpose register, semuanya berukuran 64-bit
- Setiap register dapat diakses sebagian dalam satuan byte, word, dan doubleword
| Register | Byte bawah | Word bawah | Doubleword bawah |
|---|---|---|---|
| rax | al | ax | eax |
| rbx | bl | bx | ebx |
| rcx | cl | cx | ecx |
| rdx | dl | dx | edx |
| rsp | spl | sp | esp |
| rsi | sil | si | esi |
| rdi | dil | di | edi |
| rbp | bpl | bp | ebp |
| r8~r15 | r8b~r15b | r8w~r15w | r8d~r15d |
rspberfungsi sebagai stack pointer,rsi/rdiberfungsi sebagai indeks pemrosesan string, sehingga beberapa register memiliki tujuan khususripadalah instruction pointer, danrflagsadalah register khusus yang menyimpan flag status hasil operasi
Memori dan alamat
- Memori bekerja seperti array byte berurutan mulai dari indeks 0
- Pada arsitektur x86 lama, metode segment-offset wajib digunakan, tetapi pada x86-64 semua memori diperlakukan sebagai ruang alamat flat
- Dalam praktiknya, sistem operasi dan hardware memetakan ruang alamat virtual tiap proses ke memori fisik secara dinamis
- Artinya, bahkan alamat virtual yang sama akan berkorespondensi dengan memori fisik yang berbeda pada proses yang berbeda
- Instruksi dan data berada di memori yang sama (arsitektur von Neumann), dan ini berbeda dari arsitektur Harvard seperti AVR yang digunakan pada Arduino, yang menyimpan data secara terpisah
Menulis program assembly pertama
- Setelah memasang FASM, lakukan praktik menulis dan membangun kode program sederhana berikut
format PE64 NX GUI 6.0
entry start
section '.text' code readable executable
start:
int3
ret
Penjelasan kode
format PE64 NX GUI 6.0: Menentukan format executable yang akan dihasilkan FASM, di sini berupa PE (Portable Executable) 64-bit GUIentry start: Mendefinisikan entry point tempat program akan mulai masuk, dan eksekusi dimulai dari label (start) tersebutsection '.text' code readable executable: Menentukan bahwa ini adalah bagian kode dari PE, yaitu area yang dapat dieksekusistart:: Memberi nama pada entry point yang telah ditentukan sebelumnyaint3: Breakpoint untuk debugger yang menghentikan program sementara untuk memeriksa keadaanret: Instruksi yang mengambil alamat dari stack dan memindahkan kontrol ke lokasi tersebut; pada program ini menghasilkan respons keluar secara langsung
Praktik debugging
-
Buka executable (.exe) program di atas di WinDbg dan siapkan berbagai jendela seperti disassembly dan register
-
Tekan F5 agar program mencapai breakpoint, lalu tekan F8 untuk mengeksekusi satu instruksi setiap kali (langkah demi langkah)
-
Perubahan register (
ripdan lainnya) dapat diamati secara real-time -
Setelah
retdijalankan, kontrol diserahkan ke sistem operasi, lalu proses berlanjut dengan pemanggilanRtlExitUserThreaduntuk mengakhiri thread dan proses -
Perhatian: jika keluar hanya dengan instruksi
ret, proses bisa tetap tersisa tergantung ada tidaknya eksekusi background tambahan selain thread tersebut, sehingga untuk keluar secara normal sebaiknya selalu memanggil ExitProcess
Format PE dan impor DLL
Gambaran struktur impor fungsi DLL
- Fungsi WinAPI seperti ExitProcess berada di KERNEL32.DLL
- Untuk menggunakan fungsi eksternal seperti ini, tabel impor (.idata section) pada executable harus disusun
- Import Directory Table (IDT) pada section idata berisi informasi alamat RVA seperti nama DLL, nama fungsi, dan IAT/ILT
- IAT (Import Address Table) akan ditimpa saat runtime oleh OS loader dengan alamat fungsi yang sebenarnya
- Hint/Name Table terdiri dari nama setiap fungsi dan informasi hint
Contoh definisi section .idata di FASM
section '.idata' import readable writeable
idt:
dd rva kernel32_iat
dd 0
dd 0
dd rva kernel32_name
dd rva kernel32_iat
dd 5 dup(0)
name_table:
_ExitProcess_Name dw 0
db "ExitProcess", 0, 0
kernel32_name: db "KERNEL32.DLL", 0
kernel32_iat:
ExitProcess dq rva _ExitProcess_Name
dq 0
- db/dw/dd/dq : Menyisipkan nilai dalam satuan byte/word/doubleword/quadword (8 byte)
- rva : Menghitung alamat virtual relatif (Relative Virtual Address) dari simbol
- IAT dan Name Table dapat disusun secara manual untuk mereferensikan fungsi DLL
Calling convention Windows 64-bit (MS x64 Calling Convention)
- Standar aturan yang menentukan cara pengiriman argumen dan penggunaan stack saat memanggil fungsi
- Di Windows 64-bit digunakan Microsoft x64 Calling Convention
- Karakteristik utama:
- Stack pointer harus selalu selaras 16 byte
- 4 argumen integer/pointer pertama menggunakan register rcx, rdx, r8, r9
- 4 argumen floating-point pertama dimasukkan ke xmm0~xmm3
- Argumen tambahan menggunakan stack
- Terlepas dari jumlah argumen, shadow space 32 byte harus disediakan di stack
- Pembersihan stack menjadi tanggung jawab pemanggil
Contoh pemanggilan ExitProcess
format PE64 NX GUI 6.0
entry start
section '.text' code readable executable
start:
int3
sub rsp, 8 * 5
xor rcx, rcx
call [ExitProcess]
section '.idata' import readable writeable
idt:
dd rva kernel32_iat
dd 0
dd 0
dd rva kernel32_name
dd rva kernel32_iat
dd 5 dup(0)
name_table:
_ExitProcess_Name dw 0
db "ExitProcess", 0, 0
kernel32_name db "KERNEL32.DLL", 0
kernel32_iat:
ExitProcess dq rva _ExitProcess_Name
dq 0
Analisis kode baru
-
sub rsp, 8 * 5: Menyesuaikan stack pointer (menyediakan 40 byte), sekaligus menangani alignment 16 byte dan penyediaan shadow space -
xor rcx, rcx: Menetapkan 0 ke registerrcxsebagai argumen pertama (digunakan sebagai kode EXIT) -
call [ExitProcess]: Melompat ke alamat fungsiExitProcessyang benar-benar dicatat di import table -
Saat dieksekusi langkah demi langkah di WinDbg, perubahan pada stack pointer (
rsp) dan registerrcx, serta alur terminasi proses dapat diamati secara langsung
Penutup
- Artikel ini memandu alur umum assembly x86-64 secara praktik mulai dari setup alat dasar, format PE, impor DLL, calling convention x64, hingga penulisan program pertama dan debugging
- Pada bagian berikutnya akan dibahas implementasi fitur yang lebih beragam dan kode nyata
1 komentar
Komentar Hacker News
Ingin membagikan proyek yang telah dikembangkan selama beberapa tahun
https://asm-editor.specy.app
Ini adalah IDE interaktif online yang mendukung berbagai bahasa assembly seperti M68K, MIPS, RISC-V, dan X86
Memiliki banyak fitur untuk mengajarkan pemrograman assembly
Juga bisa di-embed ke situs web lain
Tidak tahu bahwa ada fitur akses langsung ke byte beralamat rendah pada register pengindeks pointer (misalnya pada 16/32-bit,
si/esibisa diakses sebagaisil)Konsepnya mirip dengan mengakses
aldariax/eaxPenasaran apakah opcode yang baru ditambahkan di x86_64 benar-benar ada
Jadi merasa perlu memeriksa lagi spesifikasi platformnya
Ini ditanyakan murni karena rasa ingin tahu
Membagikan materi pengantar assembly yang ditulis sendiri
https://www.nayuki.io/page/a-fundamental-introduction-to-x86-assembly-programming
Mencoba optimasi dengan assembly karena penasaran apakah dispatch emulator CPU buatan sendiri bisa dibuat lebih cepat daripada C++
Sudah mencoba menjalankan program Fibonacci, tetapi hasilnya sama sekali tidak mendekati
Akhirnya hanya digabungkan dengan opsi nonaktif secara bawaan
Meski begitu, tetap yakin pasti ada cara untuk membuatnya lebih cepat
https://github.com/libriscv/libriscv/blob/master/lib/libriscv/amd64/inaccurate_dispatch.nasm
Sedikit meningkatkan performa sambil mempelajari cara akses memori
Tabel lompat diperkecil dari 64-bit menjadi 32-bit dan ditempatkan di bagian
.textagar bisa diakses secara RIP-relativeProgram Fibonacci tidak membutuhkan banyak bytecode
Sangat ingin mendengar tips tentang hal-hal yang masih bisa ditingkatkan
Tidak terlalu paham konteksnya, tetapi rasanya perbedaannya mungkin bukan karena mekanisme dispatch (cara fetch instruksi), melainkan karena perbedaan implementasi instruksi yang sebenarnya
Sebagai optimasi, register yang diemulasikan bisa dipetakan ke register x86-64 asli agar sama sekali tidak tumpah ke memori
Dengan begitu, operasi seperti
addbisa dijalankan langsung tanpa mengambil dari memoriNamun, pendekatan ini membuat penulisan emulator jauh lebih merepotkan
Ini adalah materi pengantar x86 assembly yang bisa dipraktikkan langsung di browser
Contohnya bisa langsung dijalankan tanpa setup lokal khusus
https://shikaan.github.io/assembly/x86/guide/2024/09/08/x86-64-introduction-hello.html
Sebagai catatan, ini ditulis sendiri
Karena tampaknya langsung di-assemble dengan NASM lalu menjalankan binarinya, jadi jadi penasaran soal keamanannya
Awalnya mengira itu junferno hanya dari foto profilnya
Bahkan hanya dengan sekali mencoba assembly, pemahaman secara keseluruhan bisa jadi lebih dalam, jadi ini selalu menjadi pengalaman yang bagus
Tidak perlu sampai membuat proyek besar, jadi disarankan untuk memberanikan diri dan mencoba sedikit secara langsung
Membagikan tautan diskusi HN saat itu (2020)
https://news.ycombinator.com/item?id=24195627
Senang karena ini memakai sintaks assembly gaya Intel
Ingin mencoba membuat sesuatu dengan assembly, tetapi tidak punya ide khusus
Ini adalah game memecahkan teka-teki dengan semacam pseudo-assembly
Rasanya game seperti ini bisa memuaskan keinginan untuk bermain-main dengan assembly