Bab 6: Aristektur Intel X86/AMD64
Ini merupakan arsitektur yang paling populer, dipakai di PC saat ini dan sebagian tablet/ponsel. Arsitektur ini juga sangat rumit karena berkembang dan backward compatible dengan versi sebelumnya. Akan sangat sulit untuk menjelaskan keseluruhan arsitektur ini, anggap saja ini hanya perkenalan, banyak detail spesifik yang akan dibahas ketika membahas sistem operasi tertentu (Linux, Windows, macOS).
Dari sisi reverser, jika hanya ingin mereverse engineer aplikasi biasa (user mode application) ada banyak hal yang bisa dilewati. Jika ingin melakukan reverse engineering sampai level device driver atau kernel, maka ada banyak hal yang perlu dipelajari. Contohnya dalam hal managemen memori: di sisi kernel kita perlu memahami masalah paging, managemen memori, dsb, tapi di sisi aplikasi, ini tidak bisa diubah, hanya bisa dipakai.
Dokumentasi
Dokumentasi seluruh instruksi X86 dan AMD64 dapat dibaca di situs Intel: IntelĀ® 64 and IA-32 Architectures Software Developer Manuals. AMD memiliki sedikit perbedaan dalam instruksi tingkat lanjut, ini bisa dibaca di situs AMD Developer Guides, Manuals & ISA Documents.
Versi
Arsitektur ini berkembang dari 25 tahun yang lalu, dan setiap update sejak 8086 masih kompatibel dengan versi sebelumnya: dari mulai 8086 (mikroprosessor 16 bit), 80286 (masih 16 bit tapi mendukung virtual memory dan memory protection), 80386 (mulai versi 32 bit), sampai chip yang terbaru yang mendukung 64 bit. Sejarah lengkap perkembangan arsitektur x86 bisa dilihat di Wikipedia.
Karena dibuat kompatibel dengan sebelumnya, ilmu mengenai arsitektur lama sebagian besar masih terpakai di arsitektur baru. Misalnya pengetahuan mengenai register yang kompatibel dengan yang lama. Register RAX (64 bit), 32 bit rendahnya ada di register EAX (32 bit, muncul sejak 386), dan 16 bit rendahnya juga ada di register AX (16 bit, ada sejak 8086) dan 8 bit rendahnya ada di register AL (8 bit, ada sejak 8086).
Sebelum Pentium, setiap rilis chip dengan arsitektur x86 menggunakan versi berurut (8086, 80186, 80286, 80386, dan 80486), tapi kemudian Intel memilih menggunakan nama pentium untuk 586, dan seterusnya aristektur 686 menggunakan nama yang berbeda di sisi Intel dan AMD.
Intel berusaha membuat arsitektur baru yang tidak kompatibel dengan seri x86 yang dinamakan Itanium, tapi ini kurang sukses untuk end user walaupun sukses di Enterprise. Sementara itu AMD membuat instruction set AMD64 yang kompatibel dengan x86. Akhirnya Intel bekerjasama dengan AMD dan membuat chip yang kompatibel dengan chip AMD64. Kadang AMD64 ini disebut juga dengan x86_64.
Proses boot
Saat ini proses booting PC sudah cukup rumit dan bisa menjadi satu buku sendiri, jadi saya tidak akan membahas detail proses ini. Secara umum ada dua proses booting: yang lama memakai BIOS (Basic Input Output System) dan yang baru memakai UEFI (Universal Extensible Firmware Interface). Sebagai catatan arti Universal di sini adalah bisa berlaku untuk sistem selain x86.
Pada arsitektur yang memakai BIOS, setelah prosessor menjalankan BIOS, maka kendali ada di tangan BIOS. Semua BIOS menggunakan konvensi yang sama: meload sektor pertama floppy (boot sector) atau harddisk (master boot record/MBR) dan meload ke alamat 0000:7c00. Setelah isi diload ke memori, BIOS akan mengecek apakah dua byte terakhir adalah 055 diikuti 0xAA, jika iya, maka akan dilakukan jump ke 0000:7c00.
Dari titik tersebut, kode dalam boot sector/MBR bisa meload sektor-sektor berikutnya ke memori dan melakukan JUMP ke memori tersebut. Karena ukuran boot sector hanya 512 byte, maka isi boot sector ini biasanya sangat sederhana, hanya memanggil fungi BIOS untuk meload sektor lain dan menampilkan error jika gagal. Bagian yang diload oleh boot sector ini yang biasanya disebut sebagai boot loader.
Bootloader akan melakukan berbagai setup, misalnya pindah ke mode 32 bit atau 64 bit, lalu membaca filesystem dan mencari konfigurasi untuk booting. Setelah itu kernel bisa diload. Contoh bootloader adalah GRUB, LILO, dan NTLDR.
Dari penjelasan di atas bisa dibayangkan repotnya semua langkah ini hanya demi kompatibilitas dengan sistem lama: ada BIOS yang meload satu sektor saja dari disk, lalu satu sektor itu meload bootloader (banyak sektor), dan bootlaoder akan meload sistem operasi. Ini disederhanakan dalam UEFI.
Arsitektur yang memakai UEFI lebih mudah: Firmware UEFI bisa meload file UEFI langsung dari filesystem. File ini adalah executable khusus yang bisa berjalan tanpa sistem operasi. Aplikasi UEFI memanggil kode dalam Firmware UEFI untuk melakukan berbagai aksi misalnya meload sektor dari disk atau bahkan mengakses jaringan.
Selain aplikasi UEFI, kita juga bisa membuat Boot service driver yang merupakan driver agar UEFI bisa mengakses device khusus atau Runtime Driver yang akan tetap ada setelah sistem operasi berjalan.
Jadi di dalam UEFI: firmware akan langsung meload file bootloader, dan bootloader itu hanya perlu meload OS dengan opsi yang diset oleh pengguna.
Sistem operasi
Jumlah sistem operasi untuk Intel x86 ada sangat banyak, beberapa yang terkenal antara lain:
- DOS
- Windows
- Linux dan segala turunannya (ChromeOS, Android, FirefosOS)
- FreeBSD/NetBSD/OpenBSD
- Solaris
Banyak yang dulu terkenal tapi sekarang sudah tidak banyak dipakai. misalnya:
- OS/2 dan eComStation
- Netware
Dan banyak yang lain yang kurang terkenal tapi masih aktif dikembangkan misalnya:
- Plan 9
- GNU/Hurd
- Haiku
- KolibriOS
- ReactOS
- HelenOS
Masing-masing sistem operasi biasanya memiliki:
- Format file yang berbeda, walau ada juga format standar seperti ELF yang dipakai di beberapa sistem operasi
- Memiliki cara yang berbeda untuk memanggil fungsi sistem operasi
- Memiliki built in library yang berbeda (kecuali jika mengimplementasikan standar tertentu misalnya POSIX)
- Melakukan managemen memori dengan cara yang berbeda (kecuali jika memang ingin kompatibel, seperti ReactOS yang ingin kompatibel dengan Windows)
Tools
Compiler
Jumlah compiler untuk arsitektur X86/AMD64 tidak terhitung jumlahnya. Hampir semua bahasa pemrograman ada compilernya. Beberapa compiler C yang populer adalah:
- Clang (Selain C juga mendukung berbagai bahasa)
- GCC (Selain C juga mendukung berbagai bahasa)
- MSVC (dari Microsoft)
Disassembler
Disassembler yang populer adalah IDA Pro, tapi selain itu juga ada banyak yang lain, misalnya: radare, sourcerer (untuk 16 bit). Secara umum berbagai debugger juga memiliki fungsi disassembler.
Simulator
Arsitektur x86 bisa disimulasikan dengan emulator CPU Qemu, DOSBOX, dan juga menggunakan hypervisor.
Debugger
Beberapa debugger yang terkenal:
- GDB: Linux, Windows dan berbagai sistem operasi lain
- WinDBG: Windows
- OllyDbg: Windows
Masih banyak juga debugger lain yang sudah lama tidak didukung misalnya SoftICE untuk Windows 9x, DEBUG.COM untuk DOS.
Arsitektur
X86 menggunakan arsitektur Von Neuman. Jumlah register, mode akses memori, dan berbagai fitur lain berkembang selama beberapa puluh tahun.
Register
Register arsitektur X86 berkembang dari sejak 8086, yang memiliki register 16 bit:
AX : dipecah menjadi AH dan AL
BX : dipecah menjadi BH dan BL
CX : dipecah menjadi CH dan CL
DX : dipecah menjadi DH dan DL
SI : source index
DI : destination index
BP : base pointer
SP : stack pointer
IP : program counter
Ada register flags yang tidak bisa diakses langsung dengan menggunakan nama, tapi bisa diakses dengan sedikit trik sperti ini:
PUSHF
POP AX
Dan untuk mengesetnya:
PUSH AX
POPF
8086 mendukung segmented addressing dengan register: CS
(Code Segment), DS
(Data Segment), ES
(Extra Segment) dan SS
(Stack Segment).
Sejak arsitektur 32 bit (sejak 80386), register-register tersebut diperluas:
EAX, EBX, ECX, EDX: versi 32 bit dari AX, BX, CX, DX, LSB dari versi 32 bit tetap diakses dari AX/BX/CX/DX
ESI, EDI, EBP, ESP, EIP: versi 32 bit dari SI, DI, BP, SP, IP
Segmen baru: FS dan GS
Sejak arsitektur 64 bit, registernya diperluas lagi:
RAX, RBX, RCX, RDX: versi 64 bit dari EAX, EBC, ECX, dan EDX
Register 64 bit general purpose baru: r8 sampai r15
RSI, RDI, RBP, RSP, RIP: versi 64 bit dari ESI, EDI, EBP, ESP, EIP
Register XMM/SSE
Sejak Pentium MMX ada 8 register khusus ditambahkan untuk SIMD (Single Instruction Multiple Data). Register ini adalah xmm0
sampai xmm7
, pada mode 64 bit ada 8 register tambahan dari xmm8
sampai xmm15
Mode eksekusi
Arsitektur x86 memiliki berbagai mode eksekusi, 16/32/64 bit. Di mode 16 bit, semua register 32/64 bit tidak bisa diakses, di mode 32 bit, register 64 bit tidak bisa diakses. Ketika boot pertama kali, prosessor akan masuk mode 16 bit. Di prosessor 32 bit, bootloader bisa beralih ke mode 32 bit, dan di prosessor 64 bit, bootloader bisa beralih ke mode 64 bit.
Managemen Memori
Pada arsitektur 16 bit (8086), ini sangat sederhana, siapapun (program apapun) bisa mengakses program lain, termasuk juga milik sistem operasi ataupun BIOS. Karena ukuran register hanya 16 bit, maka teorinya alamat yang bisa diakses hanya 64 kilobyte, tapi 8086 mendukung segmentasi sederhana dengan register CS/DS/ES/SS, agar memori yang bisa diakses sampai dengan 1 megabyte. Alamat memori yang diakses adalah:
16 x SegmentReg + Offset.
Ketika 80286 diperkenalkan, ditambahkan protected mode sehingga memori yang bisa diakses bisa sampai 16 megabyte. Akses memori tidak lagi perkalian segment dengan nilai konstan, tapi merupakan index ke segment descriptor. Ini merupakan tabel yang berisi base address, jadi memori yang diakses menejadi:
SegmentDescriptor[SegmentReg] + Offset
Untuk arsitektur 32 bit dan 64 bit, ditambahkan lagi fitur baru yaitu paging, dengan fitur ini jika memori yang diakses tidak ditemukan, maka akan terjadi page fault, sistem operasi bisa menggunakan ini untuk fitur swap memory. Selain itu di versi 32 bit ada fitur memory protection, dengan ini sistem operasi bisa membatasi memori mana yang boleh diakses oleh suatu program.
Instruksi
Jumlah instruksi arsitektur x86 saat ini sudah sangat banyak, banyak yang redundan, dan banyak juga instruksi sangat spesifik untuk kasus tertentu (misalnya instruksi AES). Encoding instruksi sifatnya variabel length, artinya ada instruksi yang butuh 1 byte, 2 byte, 3 byte, dan maksimum sampai dengan 15 byte.
Karena sifatnya variable length, maka serangkaian byte bisa memiliki banyak arti jika kita melakukan decoding mulai dari titik yang berbeda. Hal ini banyak digunakan oleh malware untuk teknik anti disassembler.
Contoh Program Kecil
Berikut ini beberapa contoh program kecil (dalam assembly) menggunakan NASM.
Contoh program 16 bit DOS
DOS mendukung format file COM (tanpa header sama sekali) dan file EXE (memiliki executable header). Program kecil ini formatnya COM dan menggunakan interrupt DOS (interrupt 21) untuk memanggil fungsi print yang diterminasi dengan karakter dollar ($).
bits 16
org 100h
mov dx,msg
mov ah,9
int 21h
ret
msg db 'Contoh kode COM (DOS), Intel x86 16 bit',0Dh,0Ah,'$'
Informasi mengenai berbagai interrupt di DOS bisa dilihat di Ralph Brown's Interrupt List
Contoh program 32 bit Linux
Di Linux kita bisa memanggil fungsi puts milik libray C atau memanggil langsung fungsi kernel (melakukan syscall
) dengan interrupt 0x80.
Contoh berikut ini menggunakan syscall, diambil dari http://asm.sourceforge.net/intro/hello.html
section .text
global _start ;must be declared for linker (ld)
_start: ;tell linker entry point
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db 'Hello, world!',0xa ;our dear string
len equ $ - msg ;length of our dear string
dan untuk mengcompilenya
nasm -f elf contoh.asm
ld -melf_i386 contoh.o -o contoh
Contoh program 64 bit Linux
Pada mode 64 bit di Linux, untuk mengakses kernel digunakan instruksi syscall
dengan nomor syscall yang berbeda
Menggunakan informasi dari http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ saya ubah program sebelumnya menjadi seperti ini:
bits 64
section .text
global _start ;must be declared for linker (ld)
_start: ;tell linker entry point
mov rdx,len ;message length
mov rsi,msg ;message to write
mov rdi,1 ;file descriptor (stdout)
mov rax,1 ;system call number (sys_write)
syscall
mov rax,60 ;system call number (sys_exit)
syscall
section .data
msg db 'Hello, world!',0xa ;our dear string
len equ $ - msg ;length of our dear string
dan untuk mengcompilenya
nasm -f elf64 contoh64.asm
ld contoh64.o -o contoh64
Calling convention
Ada begitu banyak sistem operasi dan compiler untuk x86 sehingga jumlah calling convention juga ada sangat banyak. Daftar lengkap bisa dilihat di:
https://en.wikipedia.org/wiki/X86_calling_conventions
Beberapa calling convention yang umum akan dibahas lebih lanjut di bab yang menjelaskan masing-masing sistem operasi.
Copyright © 2009-2018 Yohanes Nugroho