My Notion notes when learning about the basics of Assembly, Computer Architecture, and a bit of Operating Systems started from early-2023. This repo is basically my learning progress about assembly which will be updated continuously whenever I get confused.
I have an interest in low-level analysis, such as reversing a program, Return Oriented Programming, and exploiting ELF binary, which is why im learning this language. I often forget how things work in Assembly and Computer Architecture because it’s so hard to understand at the beginning. So when it happens, i usually take a note. And here's what I've learned so far.
- Memory Layout
- Stack Layout
- Register
- Memory Address
- System Call
- Addressing Modes
- Practice (Reversing a simple program) - (TODO)
Pembagian memori menjadi beberapa layout bertujuan sebagai proteksi memori agar segmen-segmen berbeda dapat diberi hak akses yang berbeda. Misal, segmen teks dapat diberi hak baca saja (read-only) untuk melindungi kode program dari modifikasi. Berikut adalah visualisasi layout memory untuk arsitektur 32 bit.
- Text Segment (Kode): Menyimpan instruksi-instruksi mesin dari program yang sedang berjalan. Instruksi ini membentuk kode eksekusi program.
- Data Segment: Berisi variabel yang diinisialisasi dengan nilai tertentu.
- Heap: Alokasi memori dinamis, seperti ketika program menggunakan fungsi
malloc()
ataunew
untuk mengalokasikan memori secara dinamis. - Stack: Menyimpan data dan informasi terkait fungsi-fungsi dalam program seperti variabel lokal, parameter fungsi, dan return address.
- Kernel Space: Area yg ditempati oleh kernel OS. Hanya kode kernel yang dapat mengakses ruang memori ini.
Stack bersifat LIFO (Last In, First Out), yang berarti data yang dimasukkan terakhir akan keluar pertama kali. Stack tumbuh dari address yang tinggi ke address yang lebih rendah. Namun, susunan variable local pada stack di memory tergantung compilernya
Stack biasanya digunakan untuk menyimpan data yang bersifat sementara, dan ketika fungsi selesai dieksekusi, frame stacknya dihapus dari stack. Hal ini berbeda dengan heap, yang digunakan untuk alokasi memori dinamis yang dapat bertahan lebih lama.
Setiap arsitektur prosesor memiliki layout yang berbeda.
Di dalam layout memory terdapat Stack yang berfungsi untuk mengelola function call dan return address. Stack ini sendiri terdiri atas beberapa bagian, yaitu sebagai berikut.
- Saved EBP: Nilai EBP yang disimpan pada awal frame. Lokasi awal (base) dari frame fungsi saat ini
- Return Address: Saat fungsi dipanggil, return address disimpan di dalam stack agar program dapat kembali ke instruksi berikutnya setelah function call selesai
Data dimasukkan ke dalam stack menggunakan operasi "push", sedangkan dihapus menggunakan operasi "pop". Parameter fungsi akan dipush ke dalam stack mulai dari parameter terakhir sampai parameter pertama.
Terdapat beberapa perbedaan antara arsitektur x86 dengan x86-64.
- Register: Pada arsitektur x86-64, nama register diawali dengan prefix “R” (Register). Contohnya seperti RAX, RBX, RCX, dan RDX.
- Parameter: Setiap parameter fungsi akan ditambahkan ke dalam register RDI, RSI, RDX, RCX, R8, dan R9 secara berurutan. Jika parameter lebih dari 6, maka akan disimpan di dalam stack.
Register dan stack merupakan 2 konsep yang berbeda. Register terletak di dalam prosesor, sedangkan stack terletak di dalam memori. Oleh karena itu register mampu menyimpan data untuk processing dengan cepat karena terletak di dalam CPU. Data pada register diakses secara langsung oleh prosesor tanpa perlu alamat memori.
Nama register berbeda-beda tergantung arsitekturnya. Misalnya RBX
merupakan register 64 bit, sedangkan EBX
adalah versi 32-bit dari register RBX
, begitupun dengan register lainnya. Pada arsitektur x86-64 terdapat register tambahan, yaitu r8
sampai r15
. Register tersebut hanya ada pada arsitektur x86-64 saja.
Note: Higher 8-bit register pada Index dan Pointer register tidak dapat diakses pada memori.
- Accumulator (RAX): menyimpan hasil dari operasi aritmetika dan logika, misal: return address dari fungsi
- Base Register (RBX): menyimpan alamat awal dari suatu data/array
- Counter Register (RCX): sering digunakan pada loop atau operasi yang memerlukan iterasi berulang.
- Data Register (RDX): register data tambahan dalam beberapa operasi aritmetika fatau logika.
- Destination Index (RDI): Biasa digunakan untuk menyimpan parameter pertama pada suatu fungsi
- Source Index (RSI): Biasa digunakan untuk menyimpan parameter kedua pada suatu fungsi
- Base Pointer (RBP): Pointer untuk mengakses variabel lokal dan parameter fungsi, menunjuk ke awal dari suatu stack frame
- Stack Pointer (RSP): Pointer yang menunjuk ke alamat top of stack, dan Menyimpan alamat memori dari stack.
- Instruction Pointer (RIP): Menyimpan memory address dari instruksi yang akan dieksekusi sehingga prosesor tahu instruksi mana yang harus dijalankan selanjutnya.
Alamat memori selalu direpresentasikan dalam bentuk heksadesimal. Dalam sistem bilangan heksadesimal, setiap digit mewakili 4 bit (setengah byte) data. Alamat memori pada sistem operasi dapat menyimpan 1 byte data. Oleh karena itu untuk mewakili 1 byte dalam notasi hexa, kita memerlukan dua digit hexadecimal.
Dalam sistem 32-bit, sebuah alamat memori memiliki panjang 32 bit (4 byte). Jika kita ingin merepresentasikan alamat memori itu dalam notasi hex, kita membutuhkan 8 digit hexa karena setiap digit mewakili 4 bit. Jadi, untuk memecah alamat memori 32-bit menjadi empat bagian, masing-masing direpresentasikan oleh dua digit heksadesimal, kita memperoleh total 8 digit heksadesimal.
Endianness adalah cara memori dalam mengurutkan address pada memori komputer. Data disusun dalam memori berdasarkan sistem endianness. LSB (Least Significant Bit) dan MSB (Most Significant Bit) adalah istilah yang digunakan dalam konteks representasi biner dari data.
- MSB adalah bit yang memiliki nilai paling besar atau paling signifikan dalam sebuah bilangan
- Misalnya pada representasi desimal, bilangan yang paling signifikan adalah digit pertama atau digit yang paling kiri.
- Contoh: Dalam bilangan biner 10100, bit paling kiri (atau pertama) adalah MSB, yaitu nilai 1.
- LSB adalah bit yang memiliki nilai paling kecil atau paling sedikit bobot/valuenya dalam suatu bilangan
- Misalnya pada representasi desimal, bilangan yang paling kecil adalah digit terakhir atau paling kanan.
- Contoh: Dalam bilangan biner 10100, bit paling kanan (atau terakhir) adalah LSB, yang memiliki nilai 0.
Pada dunia komputer, ada dua jenis endianness:
- Most Significant Bit (MSB) ditempatkan di alamat memori paling rendah (atau alamat yang lebih kecil).
- Nilai yang paling penting dalam representasi bilangan ditempatkan di paling kiri.
- Contoh: IBM PowerPC dan Motorola 68000 series
- Least Significant Bit (LSB) ditempatkan di alamat memori paling rendah (atau alamat yang lebih kecil).
- Bilangan yang ditempatkan di paling kiri adalah nilai yang paling kecil.
- Contoh: Intel x86, ARM, AMD, dan MIPS
- Base address: Alamat memori di mana suatu fungsi atau bagian dari program dimulai, atau dengan kata lain alamat memori yang menjadi titik awal untuk suatu area memori tertentu.
- Offset: Jarak antara alamat instruksi dengan base address dari suatu fungsi. Offset menunjukkan seberapa jauh alamat instruksi tersebut berada dari awal blok kode.
Jadi, base address adalah titik awal atau referensi, sedangkan offset adalah jarak atau pergeseran dari titik tersebut. Offset dapat dihitung dengan rumus :
Offset = AlamatInstruksi - Base Address
System call adalah special function yang memungkinkan program untuk berinteraksi dengan operating system. Program akan berpindah dari user mode ke kernel mode untuk mengakses layanan yang disediakan oleh kernel.
Ketika program melakukan syscall, kernel menggunakan nomor syscall (syscall NR) untuk menemukan fungsi kernel yang sesuai untuk dipanggil. Nomor ini digunakan untuk menentukan tindakan yang harus diambil oleh kernel.
Berbagai macam jenis syscall dan nomor uniknya dapat dilihat pada Linux Syscall Table berikut.
Pada arsitektur x86 (32-bit), nomor syscall akan berbeda dari yang digunakan pada arsitektur x86-64 (64-bit) karena ada perbedaan calling convention serta perbedaan jumlah syscall yang tersedia pada masing-masing arsitektur. Oleh karena itu, saat menulis program dalam bahasa assembly perlu diperhatikan nomor syscall yang sesuai dengan arsitektur yang digunakan.
arch | syscall NR | arg 0 | arg 1 | arg 2 | arg 3 | arg 4 | arg 5 |
---|---|---|---|---|---|---|---|
arm | r7 | r0 | r1 | r2 | r3 | r4 | r5 |
arm64 | x8 | x0 | x1 | x2 | x3 | x4 | x5 |
x86 | eax | ebx | ecx | edx | esi | edi | ebp |
x86-64 | rax | rdi | rsi | rdx | r10 | r8 | r9 |
Nomor system call akan selalu disimpan pada register rax
sedangkan kode file descriptor disimpan pada register rdi
(x86-64) atau ebx
(x86).
File descriptor:
- 0: stdin (standar input)
- 1: stdout (standar output)
- 2: stderr (standar error)
Berikut adalah system call yang sering digunakan:
- Fungsi: Membuka file.
- Parameter: Nama file yang akan dibuka, mode akses, dan mode pembuatan file.
- Another References: https://medium.com/@joshuaudayagiri/linux-system-calls-open-e194e6fe2395
mov rax, 2 ; Syscall NR untuk open
mov rdi, filename ; arg0: Nama file yg akan dibuka
mov rsi, [flags] ; arg1: Mode akses
mov rdx, [mode] ; arg2: Mode pembuatan file
syscall ; Interrupt (system call)
- Fungsi: Membaca data dari sebuah file deskriptor ke dalam buffer yang disediakan oleh program.
- Parameter: file deskriptor, alamat buffer tempat data akan disimpan, dan panjang data yang akan dibaca.
- File descriptor dapat bernilai 0, 1, atau 2. Namun, karena
read
berarti membaca input user, maka nilaifd
adalah 0 (stdin
)
mov rax, 0 ; Nomor syscall read
mov rdi, file_descriptor ; file descriptor untuk standard input adalah 0
mov rsi, buffer ; Alamat buffer untuk menyimpan data yang dibaca
mov rdx, buffer_size ; Panjang data yang akan dibaca
syscall ; Interrupt (system call)
- Fungsi: menulis data dari buffer ke sebuah file deskriptor
- Parameter: Parameter yang dibutuhkan untuk syscall
write
adalah file deskriptor, address buffer, dan panjang data yang akan ditulis. - File descriptor dapat bernilai 0, 1, atau 2. Namun, karena
write
berarti mengeluarkan output, maka nilaifd
adalah 1 (stdout
)
mov rax, 1 ; Nomor syscall untuk write
mov rdi, file_descriptor ; file descriptor untuk standard output adalah 1
mov rsi, buffer ; data yang akan ditulis
mov rdx, buffer_size ; Panjang data yang akan ditulis
syscall ; Interrupt (system call)
- Fungsi: mengakhiri proses yang sedang berjalan dan mengembalikan exit code ke OS.
- Parameter: exit code yang menunjukkan apakah proses keluar dengan sukses atau gagal.
mov rax, 60 ; Nomor syscall untuk exit
xor rdi, rdi ; Exit Code untuk keluar (0: sukses)
syscall ; Interrupt (system call)
- Nilai operand diberikan secara langsung di dalam instruksi.
- Nilai operand sudah tersedia dan tidak perlu diambil dari memori atau register lain.
- Source Operand adalah sebuah nilai konstan
MOV rax, 1
ADD rdi, 1
- Nilai operand adalah isi dari register tertentu.
MOV eax, ebx
ADD ecx, edx
- Alamat memori yang dituju (destination operand) diberikan secara langsung dalam instruksi.
- Source operand berisi alamat memori yang langsung ditentukan dalam instruksi.
section .data
ebx dd 42
section .text
global _start
_start:
mov eax, [ebx]
- Kurung siku
[]
menandakan pengambilan nilai dari alamat memori yang ditunjukkan di dalam kurung tersebut. - Pada kode tersebut,
mov eax, [ebx]
, registereax
akan berisi nilai yang terdapat di alamat memori yang ditunjukkan oleh nilai yang tersimpan di dalam registerebx
, bukan alamat memori itu sendiri. - Tanpa tanda kurung siku,
ebx
akan dianggap sebagai nilai langsung yang ingin dimuat ke dalameax
, bukan sebagai alamat memori yang mengandung nilai yang ingin dimuat. - Pada kode tersebut,
eax
akan bernilai42
- Alamat memori yang akan diakses tidak diberikan secara langsung dalam instruksi, tetapi diambil dari isi register
- Source operand adalah register yang berisi alamat memori yang akan diakses.
section .data
my_var dd 42
section .text
global _start
_start:
mov ebx, my_var
mov eax, [ebx]
- Alamat memori yang digunakan sebagai operand tidak diberikan secara langsung, tetapi dihitung berdasarkan lokasi instruksi saat itu.
- Sering digunakan dalam instruksi
jmp
.
1: MOV eax, 10
2: JMP 5
3: ADD eax, 5
4: ...
5: LABEL:
6: ...
- Misalnya, ketika program memiliki instruksi
JMP <label>
, RIP perlu tahu ke mana harus melompat. Tapi bagaimana RIP mengetahui di manalabel
berada? Jawabannya adalah dengan menggunakan relative addressing.
- Challenge URL:
- Goals:
- Challenge URL: https://microcorruption.com/debugger/New%20Orleans
- Goals: