# Binary 102: Linux 32-bit Assembly

## 1\. Cú pháp Intel và AT&T

Khi làm việc với Assembly trên Linux thì chúng ta bắt gặp nhiều nhất là hai dạng cú pháp: Intel và AT&T. Có nhiều công cụ khi thực hiện disassembly một ELF file thì mặc định nó sẽ cho đầu ra theo cú pháp AT&T. Tuy nhiên chúng ta sẽ học và làm việc chủ yếu với cú pháp của Intel.

Để minh họa, tôi sử dụng **objdump** để disassembly một [mẫu](https://github.com/PacktPublishing/Binary-Analysis-Cookbook/blob/master/Chapter-02/32bit/ch02-helloworld) theo cả hai cú pháp như sau:

<details data-node-type="hn-details-summary">
<summary>Cú pháp AT&amp;T</summary>
<pre class="not-prose"><code class="language-powershell">$ objdump -d ch02-helloworld
</code></pre><p><code>ch02-helloworld: file format elf32-i386</code></p><p><code>Disassembly of section .text:<br>08048060 &lt;_start&gt;:<br>8048060: 31 c0 xor %eax,%eax<br>8048062: 31 db xor %ebx,%ebx<br>8048064: 31 c9 xor %ecx,%ecx<br>8048066: 31 d2 xor %edx,%edx<br>8048068: b0 04 mov $0x4,%al<br>804806a: fe c3 inc %bl<br>804806c: 68 64 21 0a 00 push $0xa2164<br>8048071: 68 57 6f 72 6c push $0x6c726f57<br>8048076: 68 6c 6f 2c 20 push $0x202c6f6c<br>804807b: 68 48 65 6c 00 push $0x6c6548<br>8048080: 89 e1 mov %esp,%ecx<br>8048082: b2 0f mov $0xf,%dl<br>8048084: cd 80 int $0x80<br>8048086: 31 c0 xor %eax,%eax<br>8048088: 31 db xor %ebx,%ebx<br>804808a: b0 01 mov $0x1,%al<br>804808c: cd 80 int $0x80</code></p><p></p>
</details><details data-node-type="hn-details-summary">
<summary>Cú pháp Intel</summary>
<pre class="not-prose"><code class="language-powershell">$ objdump -d -M intel ch02-helloworld
</code></pre><p><code>ch02-helloworld: file format elf32-i386</code></p><p><code>Disassembly of section .text:<br>08048060 &lt;_start&gt;:<br>8048060: 31 c0 xor eax,eax<br>8048062: 31 db xor ebx,ebx<br>8048064: 31 c9 xor ecx,ecx<br>8048066: 31 d2 xor edx,edx<br>8048068: b0 04 mov al,0x4<br>804806a: fe c3 inc bl<br>804806c: 68 64 21 0a 00 push 0xa2164<br>8048071: 68 57 6f 72 6c push 0x6c726f57<br>8048076: 68 6c 6f 2c 20 push 0x202c6f6c<br>804807b: 68 48 65 6c 00 push 0x6c6548<br>8048080: 89 e1 mov ecx,esp<br>8048082: b2 0f mov dl,0xf<br>8048084: cd 80 int 0x80<br>8048086: 31 c0 xor eax,eax<br>8048088: 31 db xor ebx,ebx<br>804808a: b0 01 mov al,0x1<br>804808c: cd 80 int 0x80</code></p><p></p>
</details>

Đầu ra của **objdump** cơ bản có 4 cột, theo chiều từ trái sang phải ta có:

*   Cột 1: Địa chỉ các lệnh trong Assembly
    
*   Cột 2: Opcode của lệnh và toán hạng
    
*   Cột 3: Mã lệnh Assembly
    
*   Cột 4: Các toán hạng: nguồn, đích
    

Như vậy sự khác biệt rõ rệt nhất giữa cú pháp Intel và AT&T nằm ở cột thứ 4. Ta đúc rút ra được công thức như sau:

<details data-node-type="hn-details-summary">
<summary>Cú pháp AT&amp;T</summary>
<pre class="not-prose"><code class="language-powershell">&lt;instruction&gt; &lt;source&gt;,&lt;dest&gt;</code></pre>
</details><details data-node-type="hn-details-summary">
<summary>Cú pháp Intel</summary>
<pre class="not-prose"><code class="language-powershell">&lt;instruction&gt; &lt;dest&gt;,&lt;source&gt;</code></pre>
</details>

Bảng dưới đây tổng hợp những sự khác biệt chính giữa cú pháp Intel và AT&T:

| Name | Intel | AT&T |
| --- | --- | --- |
| Comments | `;` | `//` |
| Instructions | Untagged `add` | Tagged with operand sizes: `addq` |
| Registers | `eax`, `ebx`, etc. | `%eax%`, `%ebx%`, etc. |
| Immediates | `0x100` | `$0x100` |
| Indirect | `[eax]` | `(%eax)` |
| General indirect | `[base + reg + reg * scale + displacement]` | `displacement(reg, reg, scale)` |

## 2\. Các thanh ghi trong x86 Assembly

### 2.1. CPU và Endianness

Trước khi tìm hiểu về các thanh ghi trong x86 Assembly, hãy tìm hiểu một chút về Bộ vi xử lý mà máy tính chúng ta đang sử dụng. Mở Terminal và chạy một số lệnh bên dưới như sau:

```powershell
$ lscpu
Architecture:          i686
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
...
Vendor ID:             GenuineIntel
...
Flags:                 fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss nx pdpe1gb rdtscp lm constant_tsc arch_perfmon xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat md_clear flush_l1d arch_capabilities
```

Kết quả của lệnh này cung cấp một số thông tin quan trọng như:

*   Architecture: Kiến trúc Bộ VXL
    
*   CPU op-mode(s): Chế độ hoạt động của VXL
    
*   Byte Order: Đây là thông tin quan trọng, cho biết loại Endianness, nó sẽ quy định kiểu cách mà nó lưu dữ liệu trên bộ nhớ (Register, Stack)
    
*   Flags: Cho biết những loại thanh ghi mở rộng mà CPU hỗ trợ
    

Trên các bộ Vi xử lý của Intel sử dụng quy ước `Little Endian`. Để dễ hình dung và so sánh giữa `Big-Endian` và `Little-Endian` hãy xem hình bên dưới đây: Giả sử ta cần biểu diễn một số nguyên 32-bits có giá trị là `0x0A0B0C0D`

![](https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/ce3762bd-fb2b-401e-8511-c2a60d9da46a.png align="center")

Tham khảo: [https://en.wikipedia.org/wiki/Endianness](https://en.wikipedia.org/wiki/Endianness)

Để giải thích ngắn gọn và dễ hiểu về `Big-Endian` và `Little-Endian`, ta thống nhất một số quy tắc như sau. Vẫn lấy số nguyên 32-bits `0x0A0B0C0D` làm ví dụ:

1.  Byte có trọng số lớn nhất sẽ nằm ngoài cùng bên trái -> `0x0A`
    
2.  Byte có trọng số nhỏ nhất sẽ nằm ngoài cùng bên phải -> `0x0D`
    
3.  Theo chiều từ trái sang phải thì các byte lần lượt có trọng số giảm dần: `0x0A`, `0x0B`, `0x0C`, `0x0D`
    
4.  Địa chỉ trên bộ nhớ sẽ được đánh tăng dần theo chiều từ trái sang phải và từ trên xuống dưới: `a`, `a+1`, `a+2`, `a+3`
    

<details data-node-type="hn-details-summary">
<summary>Ta có "công thức" như sau khi phân biệt giữa <code>Big-Endian</code> và <code>Little-Endian</code>:</summary>
<ul><li><p><code>Big-Endian</code> quy định BYTE có trọng số lớn nhất được lưu ở ô nhớ có địa chỉ nhỏ nhất còn BYTE có trọng số nhỏ nhất được lưu ở ô nhớ có địa chỉ lớn nhất.</p></li><li><p><code>Little-Endian</code> quy định BYTE có trọng số lớn nhất được lưu ở ô nhớ có địa nhỉ lớn nhất còn BYTE có trọng số nhỏ nhất được lưu ở ô nhớ có địa chỉ nhỏ nhất.</p></li></ul>
</details>

Để cho dễ hiểu thì ta chỉ cần nhớ rằng: Với `Big-Endian` dữ liệu được biểu diễn theo cách thông thường từ trái qua phải, không thay đổi. Còn với `Little-Endian` thì dữ liệu sẽ bị đảo ngược lại theo các byte. Ví dụ để biểu diễn `0x1A2B3C4D` thì: Với `Big-Endian` sẽ giữ nguyên `0x1A2B3C4D` còn với `Little-Endian` sẽ là: `0x4D3C2B1A`

Trong thực tế hay gặp dữ liệu lưu trên ổ cứng (Hard Disk) hay dữ liệu hiển thị trong các công cụ phân tích gói tin (Wireshark/TCPDump) sẽ ở dạng `Big-Endian`. Còn dữ liệu khi được nạp lên bộ nhớ (RAM) thì sẽ ở dạng `Little-Endian`.

### 2.2. Các thanh ghi thường gặp

Các bộ vi xử lý hiện đại ngày nay đã phát triển thêm và gia tăng về số lượng các thanh ghi, phục vụ các tác vụ như: tính toán số học dấu phẩy động, hay thanh ghi thực hiện một chức năng riêng nào đó,.v.v.. Tuy nhiên, khi làm việc với Assembly 32-bits thì chỉ cần chú ý đến một số thanh ghi phổ biến sau đây:

<details data-node-type="hn-details-summary">
<summary>Các thanh ghi chung</summary>
<p>Hay còn được biết đến nhóm các thanh ghi đa năng, được CPU sử dụng như bộ nhớ siêu tốc trong việc tính toán, dùng làm biến tạm, tham số,.v.v..</p><ul><li><p><strong>EAX</strong>: Đa mục đích, thường lưu giá trị trả về của một hàm. Chia nhỏ được thành: AX (16-bits), AH (8-bits), AL (8-bits)</p></li><li><p><strong>EBX</strong>: Đa mục đích, thường được sử dụng như một con trỏ tới dữ liệu (nằm trong thanh ghi phân đoạn - DS, khi ở chế độ phân đoạn). Chia nhỏ được thành: BX, BH, BL.</p></li><li><p><strong>ECX</strong>: Dùng trong các vòng lặp, được dùng như biến đếm. Chia nhỏ được thành: CX, CH, CL.</p></li><li><p><strong>EDX</strong>: Dùng để lưu dữ liệu, cho các hoạt động I/O và phép toán số học. Chia nhỏ được thành: DX, DH, DL.</p></li><li><p><strong>ESI</strong>: Dùng trong các thao tác với chuỗi, thường trỏ đến chuỗi nguồn. Chia nhỏ được thành: SI và SIL</p></li><li><p><strong>EDI</strong>: Tương tự như ESI nhưng trỏ đến chuỗi đích. Chia nhỏ được thành: DI và DIL</p></li><li><p><strong>ESP</strong>: Thanh ghi con trỏ ngăn xếp, luôn trỏ tới đỉnh hiện thời của ngăn xếp. Dùng khi có các thao tác trên Stack. Chia nhỏ được thành: SP và SPL</p></li><li><p><strong>EBP</strong>: Thanh ghi con trỏ cơ sở (hay Frame Pointer). Khi một Stack Frame được cấp, có thể dựa vào ESP để xác định vị trí của: Return address, Parameter, Local variable,.v.v.. Chia nhỏ được thành: BP và BPL</p></li></ul>
</details><details data-node-type="hn-details-summary">
<summary>Các thanh ghi đoạn</summary>
<p>Bao gồm: CS, SS, DS, ES, FS và GS. Việc dùng các thanh ghi này phụ thuộc vào mô hình bộ nhớ của hệ điều hành, chỉ quan tâm nó dưới góc độ người lập trình Assembly. Ngày nay các Hệ điều hành phổ biến (Windows, Linux, FreeBSD,.v.v..) đều đã chuyển sang sử dụng chế độ phân trang (Flat mode) thay cho phân đoạn. Tuy nhiên, các thanh ghi này vẫn được sử dụng trong một số trường hợp nhất định.</p>
</details><details data-node-type="hn-details-summary">
<summary>Thanh ghi cờ - EFLAGS</summary>
<p>Thanh ghi này rộng 32-bits, với mỗi vị trí bit được đánh tương ứng với một cờ hiệu. Các cờ hiệu này sẽ có 2 trạng thái là: 1 (cờ được bật) và 0 (cờ bị xóa). Các cờ này bị thay đổi khi gặp các lệnh/thao tác tính toán. Chúng ta không cần thiết phải nhớ hết các cờ, mà chỉ cần lưu ý một số cờ sau:</p><ul><li><p><strong>Zero Flag (ZF - Cờ không - Bit thứ 6)</strong>: Được bật khi kết quả phép toán bằng 0 hoặc kết quả so sánh bằng nhau.</p></li><li><p><strong>Carry Flag (CF - Cờ nhớ - Bit thứ 0)</strong>: Được bật khi có mượn hoặc nhớ bit MSB</p></li><li><p><strong>Parity Flag (PF - Cờ chẵn lẻ - Bit thứ 2)</strong>: Được bật khi tổng số bit 1 trong kết quả là chẵn, nếu là lẻ thì cờ bị xóa</p></li><li><p><strong>Sign Flag (SF - Cờ dấu - Bit thứ 7)</strong>: Cờ này được bật khi bit MSB của kết quả bằng 1 tức đây là một kết quả âm</p></li><li><p><strong>Overflow Flag (OF - Cờ tràn - Bit thứ 11)</strong>: Được bật khi thực hiện phép tính với hai số cùng dấu mà kết quả là số có dấu</p></li><li><p><strong>Direction Flag (DF - Cờ hướng - Bit thứ 10)</strong>: Xác định hướng của thao tác chuỗi. Khi được bật hướng từ địa chỉ cao → thấp. Khi cờ đc xóa thì hướng từ địa chỉ thấp → cao.</p></li><li><p><strong>Trap Flag (TF - Cờ bẫy - Bit thứ 8)</strong>: Được bật để sử dụng chế độ gỡ lỗi, CPU sẽ chỉ thực hiện một lệnh tại một thời điểm</p></li></ul>
</details><details data-node-type="hn-details-summary">
<summary>Thanh ghi con trỏ lệnh (IP/EIP)</summary>
<p>Luôn trỏ đến địa chỉ của lệnh kế tiếp sẽ được thực thi vì vậy nó quan trọng trong khai thác. Khi kiểm soát được thanh ghi này, có thể trỏ đến shellcode.</p>
</details>

Bảng tổng hợp các thanh ghi (các thanh ghi 64-bits sẽ được trình bày ở bài sau):

![](https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/7b06ed11-befc-47fd-b245-3abf2c5e8d93.png align="center")

Tham khảo: [https://en.wikibooks.org/wiki/X86\_Assembly/X86\_Architecture](https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture)

Bảng tổng hợp các cờ và vị trí:

![](https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/80268782-a5d8-4f6f-bce8-2afb40e70b46.png align="center")

Tham khảo: [https://en.wikibooks.org/wiki/X86\_Assembly/X86\_Architecture](https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture)

## 3\. Các lệnh thường gặp trong x86 Assembly

<details data-node-type="hn-details-summary">
<summary>Các lệnh liên quan đến Stack</summary>
<ul><li><p><code>PUSH</code>: Dùng để đẩy (thêm/cất) dữ liệu vào đỉnh ngăn xếp. Khi dữ liệu được thêm vào ngăn xếp thì đồng thời thanh ghi <code>ESP</code> cũng bị giảm đi.</p></li><li><p><code>POP</code>: Dùng để lấy dữ liệu ra từ đỉnh ngăn xếp. Khi dữ liệu được lấy ra khỏi ngăn xếp thì đồng thời thanh ghi <code>ESP</code> cũng sẽ được tăng lên.</p></li></ul>
</details><details data-node-type="hn-details-summary">
<summary>Các lệnh Logics - Các phép toán thao tác bits</summary>
<ul><li><p><code>AND</code>: Kết quả của <code>AND</code> bằng 1 nếu như hai bit là 1, ngược lại bằng 0</p></li><li><p><code>OR</code>: Kết quả của phép <code>OR</code> bằng 0 nếu như hai bit là 0, ngược lại bằng 1</p></li><li><p><code>NOT</code>: Phép phủ định, cho kết quả ngược lại</p></li><li><p><code>XOR</code>: Hai bit giống nhau thì bằng 0, khác nhau thì bằng 1</p></li></ul>
</details><details data-node-type="hn-details-summary">
<summary>Các lệnh cộng - trừ - nhân - chia</summary>
<ul><li><p>Lệnh <code>ADD</code>: Toán hạng đích = Toán hạng đích + Toán hạng nguồn (Intel Syntax)</p></li><li><p>Lệnh <code>SUB</code>: Toán hạng đích = Toán hạng đích - Toán hạng nguồn (Intel Syntax)</p></li><li><p>Lệnh <code>MUL</code>: Toán hạng đích = Toán hạng đích * Toán hạng nguồn. Toán hạng đích tùy thuộc vào kích thước của toán hạng nguồn, thường toán hạng đich sẽ tương ứng với: <code>EAX</code>, <code>AX</code>, <code>AL</code></p></li><li><p>Lệnh <code>DIV</code>: Toán hạng đích = Toán hạng đích / Toán hạng nguồn. Tương tự như <code>MUL</code> nhưng là phép chia.</p></li></ul>
</details><details data-node-type="hn-details-summary">
<summary>Các lệnh tăng - giảm giá trị</summary>
<ul><li><p>Lệnh <code>INC</code> và <code>DEC</code>: Tương ứng với: Toán hạng đích = Toán hạng đích + 1 và Toán hạng đích = Toán hạng đích - 1 (Intel Syntax)</p></li></ul>
</details><details data-node-type="hn-details-summary">
<summary>Một số lệnh khác</summary>
<ul><li><p>Lệnh <code>MOV</code>: Chuyển dữ liệu giữa: thanh ghi với thanh ghi, thanh ghi với ô nhớ,.v.v..</p></li><li><p>Lệnh <code>LEA</code>: Tương tự MOV nhưng toán hạng đích (Intel Syntax) thường là các thanh ghi còn toán hạng nguồn là địa chỉ ô nhớ.</p></li><li><p>Lệnh <code>XCHG</code>: Hoán vị nội dung 2 toán hạng: Swap(Toán hạng đích, Toán hạng nguồn)</p></li></ul>
</details>

## 4\. x86 Assembly System Calls trên Linux

Thông thường các chương trình độc hại hay shellcode thường thực hiện các cuộc gọi hệ thống (system calls) một cách trực tiếp, mà không sử dụng các hàm có trong thư viện. Nếu nắm chắc được phần này thì ở các chương sau khi phân tích các chương trình độc hại như: Bind Shell, Reverse Shell, Polymorphism Shell sẽ không gặp khó khăn nhiều.

### 4.1. System call number, Ngắt và Man Page

Trong x86 Assembly, shellcode thường thực hiện một System call thông qua việc gọi ngắt `INT 0x80`. Các ngắt này sẽ dựa vào các `System Call Number` được định nghĩa trong Header file của thư viện hệ thống để tìm đến hàm cần gọi. Có nhiều header file trong hệ thống nhưng trong phần này và các phần tiếp theo khi phân tích Shellcode 32-bits sẽ chủ yếu tra cứu các API trong tệp: `/usr/include/i386-linux-gnu/asm/unistd_32.h` hoặc `/‌arch/‌x86/‌include/‌generated/‌uapi/‌asm/‌unistd_32.h`. Ví dụ dưới đây cho biết hàm `exit` có System call number là `1`:

```powershell
$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h
#ifndef _ASM_X86_UNISTD_32_H
#define _ASM_X86_UNISTD_32_H 1

#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
...
```

Khi đã biết System call number của một hàm thì cần phải biết được hàm đó sử dụng như nào, truyền các tham số ra làm sao. Sử dụng `Man Page` trên Linux để tra cứu. Ví dụ cách dùng `exit`:

```powershell
$ man 2 exit
```

Kết quả sẽ được như sau cho biết hàm này nhận vào một số nguyên, báo hiệu mã trả về khi kết thúc một chương trình:

```powershell
EXIT(3)                                             Linux Programmer's Manual                                             EXIT(3)
NAME
       exit - cause normal process termination
SYNOPSIS
       #include <stdlib.h>
       void exit(int status);
...
```

Các Parameter truyền vào khi gọi hàm tuân theo quy tắc sau đây:

![](https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/3c4e20e3-ec42-4827-82cc-c0ca97d71c0b.png align="center")

Tham khảo: [https://en.wikibooks.org/wiki/X86\_Assembly/Interfacing\_with\_Linux#Via\_interrupt](https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Via_interrupt)

<details data-node-type="hn-details-summary">
<summary><strong>Ta có "công thức" cần nhớ:</strong></summary>
<ul><li><p>x86 Assembly thực hiện System Call thông qua lệnh: <code>INT 0x80</code> gọi là <code>Ngắt 0x80</code></p></li><li><p>Thanh ghi <code>EAX/AX</code> sẽ lưu <code>System Call Number</code> và <code>Result</code> của System Call.</p></li><li><p>Các tham số theo thứ tự sau: <code>EBX</code>, <code>ECX</code>, <code>EDX</code>, <code>ESI</code>, <code>EDI</code>, <code>EBP</code></p></li><li><p>Tra cứu các <code>System Call Number</code> tại: <code>unistd_32.h</code></p></li><li><p>Tra cứu các API bằng <code>Man Page</code> của Linux</p></li></ul>
</details>

### 4.2. Phân tích một chương trình x86 Assembly đơn giản

Bây giờ hãy thực hành phân tích một chương trình x86 Assembly đơn giản, trước tiên hãy chép đoạn mã dưới đây vào một text editor bất kỳ:

```powershell
  1 ; hello world ASM - ch02-helloworld.asm
  2
  3 global _start
  4 section .text
  5
  6 _start:
  7         ; write(int fd, const void *buf, size_t count)
  8         xor    eax,eax
  9         xor    ebx,ebx
 10         xor    ecx,ecx
 11         xor    edx,edx
 12         mov    al,0x4
 13         inc    bl
 14         push   0x000a2164
 15         push   0x6c726f57
 16         push   0x202c6f6c
 17         push   0x6c6548
 18         mov    ecx,esp
 19         mov    dl,0xf
 20         int    0x80
 21
 22         ; exit(int status)
 23         xor    eax,eax
 24         xor    ebx,ebx
 25         mov    al,0x1
 26         int    0x80
```

Lưu lại với tên tùy ý với đuôi `.asm` sau đó thực hiện biên dịch và liên kết chương trình bằng command sau:

```powershell
$ nasm -f elf32 -o ch02-helloworld.o ch02-helloworld.asm
$ ld -o ch02-helloworld ch02-helloworld.o
$ chmod +x ch02-helloworld
$ ./ch02-helloworld
Hello, World!
```

<details data-node-type="hn-details-summary">
<summary>Giải thích chi tiết chương trình</summary>
<ul><li><p>Dòng 8, 9, 10, 11: Khởi tạo giá trị 0 cho các thanh ghi EAX, EBX, ECX, EDX</p></li><li><p>Dòng 12: Gán <code>AL = 0x4</code>. Đây chính là <code>System Call Number</code> của hàm <code>write</code> được định nghĩa trong file header <code>unistd_32.h</code> (<code>#define __NR_write 4</code>). Tra cứu hàm này trong Man Page ta được: <code>write(int fd, const void *buf, size_t count);</code></p></li><li><p>Dòng 13: Gán <code>BL = 0x1</code>. Nghĩa là <code>fd = STDOUT</code> (Ngoài ra: <code>0=STDIN</code>; <code>2=STDERR</code>)</p></li><li><p>Dòng 14, 15, 16, 17: Đẩy dữ liệu dạng Hexa lên Stack. Dựa theo <code>Little-Endian</code> ta sẽ decode dữ liệu này như sau:</p></li><li><p></p></li></ul><pre class="not-prose"><code class="language-python">$ python
&gt;&gt;&gt; a = '000a2164'.decode('hex')
&gt;&gt;&gt; b = '6c726f57'.decode('hex')
&gt;&gt;&gt; c = '202c6f6c'.decode('hex')
&gt;&gt;&gt; d = '6c6548'.decode('hex')
&gt;&gt;&gt; final = a + b + c + d
&gt;&gt;&gt; final[::-1] # Little-Endian, Reverse bytes
'Hello, World!\n\x00'</code></pre><ul><li><p>Vì x86 Assembly chỉ hỗ trợ độ rộng 32-bits nên sẽ phải <code>PUSH</code> 4 lần, mỗi lần tối đa 4 bytes. Ở cuối có ký <code>\n</code> để xuống dòng và ký tự <code>\0</code> báo hiệu kết thúc chuỗi.</p></li><li><p>Dòng 18: <code>ECX</code> trỏ đến chuỗi đã <code>PUSH</code> lên Stack. Nghĩa là <code>*buf = 'Hello, World!\n\x00'</code></p></li><li><p>Dòng 19: <code>EDX = 0xF</code>. Nghĩa là <code>count = 0xF</code></p></li><li><p>Dòng 20: Gọi ngắt bằng lệnh: <code>INT 0x80</code></p></li><li><p>Dòng 23, 24: Tương tự 8, 9, 10, 11: Khởi tạo <code>EAX</code>, <code>EBX</code> về giá trị bằng <code>0</code></p></li><li><p>Dòng 25: <code>AL = 0x1</code> nghĩa là <code>System Call Number = 1</code> tương tự trên, đây là hàm <code>exit</code> (<code>#define __NR_exit 1</code>) và được mô tả như sau trong Man Page: <code>void exit(int status);</code></p></li></ul>
</details><details data-node-type="hn-details-summary">
<summary>Tóm lại, chương trình có hai khối chính</summary>
<ul><li><p>Khối đầu thực hiện hàm <code>write</code> và khối sau thực hiện hàm <code>exit</code>:</p></li><li><p></p></li></ul><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/dda4ce1d-a05d-4514-83ac-6ab52034d768.png" isuploading="false" align="center"><ul><li><p>Tham khảo: <a target="_self" rel="noopener noreferrer nofollow" class="text-primary underline underline-offset-2 hover:text-primary/80 cursor-pointer" href="https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md" style="pointer-events: none;">https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md</a></p></li><li><p>Khi đó ta có thể viết lại đơn giản chương trình thành như sau:</p></li><li><p></p></li></ul><pre class="not-prose"><code class="language-c">write(fd=1, *buf='Hello, World!\n\x00', count=15);
exit(status=0);</code></pre>
</details>

## 5\. Cấu trúc tệp ELF32 trên Linux

**ELF - Executable and Linking Format**: Là định dạng tệp thực thi phổ biến trên Linux, nó có thể là các chương trình phần mềm, các thư viện, các drivers hay Linux Kernel Module,.v.v.. Việc hiểu cấu trúc của tệp ELF32 giúp người phân tích có cái nhìn tổng quan, hiểu được đặc tính kỹ thuật của tệp. Xác định được loại tệp cũng như cấu trúc tệp tin là giai đoạn đầu trong bất kỳ quá trình phân tích Binary nào.

### 5.1. Cấu trúc cơ bản tệp ELF

Cấu trúc tệp ELF được định nghĩa trong header file: `/usr/include/elf.h`. Về cơ bản nó có ba phần chính: ELF Header, Program Header Table và Section Header Table.

![](https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/b57fc24f-aa15-4219-9647-be1a7f58cc5f.png align="center")

Tham khảo: [https://en.wikipedia.org/wiki/Executable\_and\_Linkable\_Format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format)

**Trong đó:**

<details data-node-type="hn-details-summary">
<summary>ELF Header</summary>
<ul><li><p>Nằm ở đầu tệp ELF, chứa thông tin cơ bản về tệp: Magic, Type, Machine, Entry Point, Start of Program Headers/Section Headers, Number of Program Headers/Section Headers, Size of ELF Header/Program Headers/Section Headers,.v.v..</p></li><li><p></p></li></ul><pre class="not-prose"><code class="language-c">/* The ELF file header.  This appears at the start of every ELF file.  */
#define EI_NIDENT (16)
typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf32_Half    e_type;                 /* Object file type */
  Elf32_Half    e_machine;              /* Architecture */
  Elf32_Word    e_version;              /* Object file version */
  Elf32_Addr    e_entry;                /* Entry point virtual address */
  Elf32_Off     e_phoff;                /* Program header table file offset */
  Elf32_Off     e_shoff;                /* Section header table file offset */
  Elf32_Word    e_flags;                /* Processor-specific flags */
  Elf32_Half    e_ehsize;               /* ELF header size in bytes */
  Elf32_Half    e_phentsize;            /* Program header table entry size */
  Elf32_Half    e_phnum;                /* Program header table entry count */
  Elf32_Half    e_shentsize;            /* Section header table entry size */
  Elf32_Half    e_shnum;                /* Section header table entry count */
  Elf32_Half    e_shstrndx;             /* Section header string table index */
} Elf32_Ehdr;
</code></pre>
</details>
<details data-node-type="hn-details-summary">
<summary>Program Header Table</summary>
<ul><li><p>Bảng chứa các Program Header (hay còn được gọi là các Segment Header). Mỗi Segment chứa 0 hoặc nhiều Section</p></li><li><p></p></li></ul><pre class="not-prose"><code class="language-c">/* Program segment header.  */
typedef struct
{
  Elf32_Word    p_type;                 /* Segment type */
  Elf32_Off     p_offset;               /* Segment file offset */
  Elf32_Addr    p_vaddr;                /* Segment virtual address */
  Elf32_Addr    p_paddr;                /* Segment physical address */
  Elf32_Word    p_filesz;               /* Segment size in file */
  Elf32_Word    p_memsz;                /* Segment size in memory */
  Elf32_Word    p_flags;                /* Segment flags */
  Elf32_Word    p_align;                /* Segment alignment */
} Elf32_Phdr;
</code></pre>
</details>
<details data-node-type="hn-details-summary">
<summary>Section Header Table</summary>
<ul><li><p>Bảng chứa các Section Header, mỗi Section sẽ được quy định có quyền: đọc, ghi hay thực thi và nó lưu: dữ liệu hay code thực thi,.v.v..</p></li></ul><pre class="not-prose"><code class="language-c">/* Section header.  */
typedef struct
{
  Elf32_Word    sh_name;                /* Section name (string tbl index) */
  Elf32_Word    sh_type;                /* Section type */
  Elf32_Word    sh_flags;               /* Section flags */
  Elf32_Addr    sh_addr;                /* Section virtual addr at execution */
  Elf32_Off     sh_offset;              /* Section file offset */
  Elf32_Word    sh_size;                /* Section size in bytes */
  Elf32_Word    sh_link;                /* Link to another section */
  Elf32_Word    sh_info;                /* Additional section information */
  Elf32_Word    sh_addralign;           /* Section alignment */
  Elf32_Word    sh_entsize;             /* Entry size if section holds table */
} Elf32_Shdr;</code></pre>
</details>

### 5.2. Trích xuất Metadata trong ELF32

Phần này giới thiệu một số công cụ nguồn mở, miễn phí và ưu tiên có sẵn trên Linux vì chúng ta đang tập chung phân tích các tệp ELF32:

<details data-node-type="hn-details-summary">
<summary><a target="_blank" rel="noopener noreferrer nofollow" class="text-primary underline underline-offset-2 hover:text-primary/80 cursor-pointer" href="https://man7.org/linux/man-pages/man1/readelf.1.html" style="pointer-events: none;"><strong>READELF</strong></a>: Công cụ dòng lệnh (CLI), có sẵn trên Linux, hiển thị rất nhiều thông tin về một tệp ELF</summary>
<details data-node-type="hn-details-summary" class="editor-details"><summary class="details-summary"><strong>Một số option hữu ích</strong></summary><div data-type="details-content" class="details-content"><ul><li><p><code>[-h|--file-header]</code>: Hiển thị ELF header</p></li><li><p><code>[-l|--program-headers|--segments]</code>: Hiển thị Segment headers (Program header)</p></li><li><p><code>[-S|--sections|--section-headers]</code>: Hiển thị Section header</p></li><li><p><code>[-e|--headers]</code>: Kết hợp của: <code>-h -l -S</code></p></li><li><p><code>[-s|--symbols|--syms]</code>: Hiển thị Symbol section</p></li><li><p><code>[-d|--dynamic]</code>: Hiển thị Dynamic section</p></li><li><p><code>[-a|--all]</code>: Hiển thị tất cả thông tin, là kết hợp của: <code>-h -l -S -s -r -d -V -A -I</code></p></li><li><p><code>[-x &lt;number or name&gt;|--hex-dump=&lt;number or name&gt;]</code>: Dump hex của một Section</p></li><li><p><code>[-p &lt;number or name&gt;|--string-dump=&lt;number or name&gt;]</code>: Dump string của một Section</p></li><li><p><code>[-W|--wide]</code>: Làm cho output không bị ngắt khi vượt quá 80 ký tự.</p></li></ul></div></details><details data-node-type="hn-details-summary" class="editor-details"><summary class="details-summary"><strong>Show ELF header</strong>: Chứa các thông tin cơ bản</summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/b3eb845e-47da-408b-829a-2f71bbbb0705.png" isuploading="false" align="center"></div></details><details data-node-type="hn-details-summary" class="editor-details"><summary class="details-summary"><strong>Show Program header</strong></summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/09c35c80-fcfc-4ae3-96a5-d17ec4991111.png" isuploading="false" align="center"></div></details><details data-node-type="hn-details-summary" class="editor-details"><summary class="details-summary"><strong>Show Section header</strong>: Chú ý section <code>.text</code> được gắn Flag là: <code>X</code></summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/7dd32918-935d-43a7-97f9-756f9cb3ffcd.png" isuploading="false" align="center"></div></details><details data-node-type="hn-details-summary" class="editor-details"><summary class="details-summary"><strong>Show Symbol Table</strong>: Bảng này cho biết các biến, các hàm mà chương trình sử dụng</summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/3395602a-cd58-4f30-b313-0b9680d693b6.png" isuploading="false" align="center"></div></details><details data-node-type="hn-details-summary" class="editor-details"><summary class="details-summary"><strong>Dump Strings một Section</strong>: dùng <code>-p</code></summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/72bbe832-4381-4b5c-8f71-c6075652ae5c.png" isuploading="false" align="center"></div></details><details data-node-type="hn-details-summary" class="editor-details"><summary class="details-summary"><strong>Dump một Section</strong>: dùng <code>-R</code> hoặc <code>-x</code> đều có thể dump được</summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/d2b426cb-ee24-46bb-9504-7a7672f52bc6.png" isuploading="false" align="center"></div></details>
</details><details data-node-type="hn-details-summary">
<summary><a target="_self" rel="noopener noreferrer nofollow" class="text-primary underline underline-offset-2 hover:text-primary/80 cursor-pointer" href="https://github.com/horsicq/XELFViewer" style="pointer-events: none;"><strong>XELFViewer</strong></a>: Công cụ giao diện đồ họa (GUI), hỗ trợ đa nền tảng (Windows, Linux, macOS), dễ sử dụng.</summary>
<details data-node-type="hn-details-summary" open="open" class="editor-details"><summary class="details-summary"><strong>Thông tin thêm về công cụ</strong></summary><div data-type="details-content" class="details-content"><ul><li><p>Tác giả của XELFViewer là NTInfo, cũng là tác giả nhiều công cụ nổi tiếng khác như: Detect It Easy (DiE), XAPKDetector, XVolkolak, XOpcodeCalc, Nauz File Detector(NFD), x64dbg Plugin Manager,.v.v..</p></li></ul></div></details><details data-node-type="hn-details-summary" open="open" class="editor-details"><summary class="details-summary"><strong>ELF Header</strong></summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/e724a5e1-46c2-4285-af7e-292ed1dfcd74.png" isuploading="false" align="center"></div></details><details data-node-type="hn-details-summary" open="open" class="editor-details"><summary class="details-summary"><strong>Section Header</strong></summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/a897f4b6-5908-45ec-8ec9-67566902f9c2.png" isuploading="false" align="center"></div></details><details data-node-type="hn-details-summary" open="open" class="editor-details"><summary class="details-summary"><strong>Program Header</strong></summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/d5eca52f-d535-488d-ac9f-40bddc4cd974.png" isuploading="false" align="center"></div></details><details data-node-type="hn-details-summary" open="open" class="editor-details"><summary class="details-summary"><strong>Symbol Table</strong></summary><div data-type="details-content" class="details-content"><img class="rounded-lg max-w-full h-auto" src="https://cdn.hashnode.com/uploads/covers/6698c3b850049834bdc8b385/62e8d762-b5ea-4ebb-9c2d-e62681ee6ea4.png" isuploading="false" align="center"></div></details>
</details>
