Chương 21: Mã độc 64-bit (64-Bit Malware)


1. Tại sao cần viết mã độc 64-bit?

Mã độc 32-bit có thể chạy trên cả hệ điều hành 32-bit và 64-bit. Tuy nhiên, không thể chạy mã 32-bit bên trong tiến trình 64-bit. Khi processor đang ở chế độ 32-bit, nó không thể thực thi mã 64-bit, và ngược lại.

Nguyên tắc cốt lõi: Nếu mã độc cần chạy bên trong không gian tiến trình của một ứng dụng 64-bit, nó bắt buộc phải là 64-bit.

Các trường hợp bắt buộc phải dùng 64-bit


2. Sự khác biệt kiến trúc x64 so với x86

2.1. Thanh ghi (Registers)

Đặc điểmx86 (32-bit)x64 (64-bit)
Thanh ghi đa năngEAX, EBX, ECX… (32-bit)RAX, RBX, RCX… (64-bit)
Số lượng thanh ghi816 (thêm R8–R15)
Instruction pointerEIPRIP
Truy cập thanh ghi mới theo kích thướcR8D (32-bit), R8W (16-bit), R8L (8-bit)
Truy cập byte thấp nhất của RSP/RBP/RDI/RSIKhông cóSPL, BPL, DIL, SIL

Tóm tắt: RAX là phiên bản 64-bit của EAX. Vẫn có thể truy cập EAX (32-bit thấp) trong môi trường 64-bit.


2.2. RIP-Relative Addressing (Địa chỉ tương đối so với Instruction Pointer)

Đây là khác biệt quan trọng nhất giữa x64 và x86 liên quan đến shellcode và PIC (Position-Independent Code).

Trong x86 (absolute addressing):

; Địa chỉ tuyệt đối được mã hóa cứng vào lệnh
00401004  A1 74 33 40 00    mov eax, dword_403374
; Bytes 74 33 40 00 = địa chỉ 0x00403374
; Nếu file load  địa chỉ khác  lệnh này SAI, phải sửa lại

Trong x64 (RIP-relative addressing):

; Địa chỉ được lưu dưới dạng OFFSET so với RIP hiện tại
0000000140001058  8B 05 A2 D3 00 00    mov eax, dword_14000E400
; Bytes A2 D3 00 00 = offset 0x0000D3A2 tính từ RIP
; File load  bất kỳ đâu  lệnh vẫn trỏ đúng  Position-Independent

3. Calling Convention và Stack Usage trong x64

3.1. Quy ước truyền tham số

x64 Windows dùng convention gần giống fastcall của 32-bit:

Tham sốThanh ghi
Tham số 1RCX
Tham số 2RDX
Tham số 3R8
Tham số 4R9
Tham số 5+Stack

3.2. Sự khác biệt về Stack

graph LR subgraph 32-bit A[Đầu hàm] --> B[Stack tăng/giảm giữa hàm] B --> C[push/pop thoải mái] C --> D[Cuối hàm] end subgraph 64-bit E[Đầu hàm - cấp phát toàn bộ] --> F[Stack KHÔNG thay đổi] F --> G[Cuối hàm - giải phóng] end
  • 32-bit: Stack có thể tăng/giảm tự do bằng push/pop ở giữa hàm.
  • 64-bit: Stack chỉ được thay đổi ở đầu và cuối hàm. Không có push/pop ở giữa.

Quy tắc này không được processor bắt buộc, nhưng model xử lý exception của Microsoft 64-bit phụ thuộc vào nó. Vi phạm có thể gây crash khi có exception.


3.3. Phân biệt Leaf và Nonleaf Function

LoạiĐịnh nghĩaĐặc điểm
Leaf functionKhông gọi hàm nào khácStack đơn giản hơn
Nonleaf functionCó gọi ít nhất một hàm khácPhải cấp phát thêm 0x20 bytes shadow space

Shadow space (0x20 bytes): Cho phép hàm được gọi lưu lại 4 thanh ghi tham số (RCX, RDX, R8, R9) nếu cần.


3.4. So sánh disassembly: printf call

32-bit — rõ ràng, dễ đếm tham số:

004113C0  mov eax, [ebp+arg_0]
004113C3  push eax              ; tham số 4
004113C4  mov ecx, [ebp+arg_C]
004113C7  push ecx              ; tham số 3
004113C8  mov edx, [ebp+arg_8]
004113CB  push edx              ; tham số 2
004113CC  mov eax, [ebp+arg_4]
004113CF  push eax              ; tham số 1
004113D0  push offset aDDDD_    ; format string
004113D5  call printf
004113DB  add esp, 14h          ; 0x14 = 5 tham số × 4 bytes   ràng!

64-bit — khó đếm tham số hơn:

0000000140002C96  mov ecx, [rsp+38h+arg_0]
0000000140002C9A  mov eax, [rsp+38h+arg_0]
0000000140002C9E  mov [rsp+38h+var_18], eax   ; local var hay tham số? Khó biết!
0000000140002CA2  mov r9d, [rsp+38h+arg_18]   ; tham số 4
0000000140002CA7  mov r8d, [rsp+38h+arg_10]   ; tham số 3
0000000140002CAC  mov edx, [rsp+38h+arg_8]    ; tham số 2
0000000140002CB0  lea rcx, aDDDD_              ; tham số 1 (format string)
0000000140002CB7  call cs:printf

4. Prologue và Epilogue trong 64-bit

Mã 64-bit có cấu trúc prologue (đầu hàm) và epilogue (cuối hàm) rõ ràng:

; === PROLOGUE ===
00000001400010A5  mov [rsp+arg_0], ecx   ; lưu tham số 1 (32-bit → ECX)
00000001400010A9  push rdi               ; lưu thanh ghi được callee bảo toàn
00000001400010AA  sub rsp, 20h           ; cấp phát shadow space (nonleaf)

5. Exception Handling trong x64

32-bit64-bit
Cơ chếDùng stack (fs:[0] trỏ đến handler frame)Dùng bảng tĩnh trong PE file
Nguy cơExploit có thể ghi đè exception info trên stackAn toàn hơn, không lưu trên stack
Cấu trúc_IMAGE_RUNTIME_FUNCTION_ENTRY trong .pdata section

Hệ quả: Kỹ thuật khai thác SEH overwrite (phổ biến trong 32-bit) không áp dụng được trong 64-bit.


6. WOW64 — Windows 32-bit on Windows 64-bit

6.1. WOW64 là gì?

Subsystem cho phép ứng dụng 32-bit chạy trên OS 64-bit, xử lý các vấn đề tương thích về filesystem và registry.

6.2. Filesystem Redirection

Khi ứng dụng 32-bit truy cập C:\Windows\System32, WOW64 tự động redirect sang C:\Windows\SysWOW64.

Để ứng dụng 32-bit truy cập thư mục System32 thật:

C:\Windows\Sysnative   ← bypass redirect

Hoặc gọi API:

Wow64DisableWow64FsRedirection()  // tắt redirect cho thread hiện tại

6.3. Registry Redirection

HKEY_LOCAL_MACHINE\Software
        ↓ (32-bit app tự động redirect sang)
HKEY_LOCAL_MACHINE\Software\Wow6432Node

Các API registry có flag để chỉ định rõ muốn truy cập view 32-bit hay 64-bit:

RegCreateKeyEx(..., KEY_WOW64_64KEY, ...)  // truy cập view 64-bit
RegCreateKeyEx(..., KEY_WOW64_32KEY, ...)  // truy cập view 32-bit

7. Gợi ý phân tích mã độc 64-bit

7.1. Phân biệt Pointer và Data Value

Trong 64-bit, dễ hơn để nhận biết kiểu dữ liệu:

Thanh ghiKích thướcÝ nghĩa
RDX, RCX, R8, R9 (64-bit)64-bitRất có thể là pointer
ECX, EDX, R8D (32-bit)32-bitKhông phải pointer (pointer phải 64-bit)

Ví dụ so sánh:

; 32-bit: không biết tham số nào là pointer
004114F5  push eax    ; 32-bit, pointer hay integer?
004114F9  push ecx    ; 32-bit, pointer hay integer?
004114FA  call sub_411186

; 64-bit: có thể suy luận kiểu
140001148  mov rdx, [rsp+38h+var_18]  ; RDX = 64-bit → rất có thể là POINTER
14000114D  mov ecx, [rsp+38h+var_10]  ; ECX = 32-bit → KHÔNG phải pointer
140001151  call sub_14000100A

8. Công cụ hỗ trợ phân tích 64-bit

Công cụHỗ trợ 64-bit
OllyDbgKhông hỗ trợ
WinDbgHỗ trợ
IDA Pro (Standard)Không hỗ trợ x64
IDA Pro (Advanced)Hỗ trợ x64

9. Tổng kết

mindmap root((64-bit Malware)) Lý do tồn tại Kernel rootkit Code injection vào process 64-bit Shellcode khai thác app 64-bit Khác biệt kiến trúc Thanh ghi lớn hơn RAX RBX... 16 thanh ghi thay vì 8 RIP-relative addressing Calling Convention 4 tham số đầu qua RCX RDX R8 R9 Stack chỉ thay đổi ở đầu và cuối hàm Shadow space 0x20 bytes WOW64 Chạy app 32-bit trên OS 64-bit Filesystem redirect System32 ↔ SysWOW64 Registry redirect Wow6432Node Phân tích Register size → suy luận kiểu dữ liệu Prologue → đếm tham số và biến cục bộ Khó đếm tham số hơn 32-bit