C、Pascal 等高級語言讓開發者專注於問題領域,透過資料抽象與結構化程式設計,將硬體資源管理交給編譯器。然而,編譯器遵循特定慣例,使我們在閱讀反組譯碼時仍能追蹤高級語言結構。本文詳細對照高級語言建構與其對應的組合語言實現。
編譯器將變數名稱綁定至特定記憶體位址(編譯期暫時位址,連結載入後重定位)。在組合語言中,變數存取表現為方括號內的有效位址運算元。
; C: int my_age = 32;
mov word ptr [007E85DA], 20h
; C: char my_name[11] = "lianzi2000";
; 假設基址 007E85DC
; [007E85DC] = 'l', [007E85DD] = 'i' ...
; C: char *your_name; your_name = my_name;
mov [007E85F0], 007E85DC ; your_name 存放 my_name 位址
register int i; 提示編譯器將頻繁使用的變數置於暫存器(通常為 ESI、EDI),避免記憶體存取延遲,提升效能。
多維陣列在記憶體中以列優先順序連續存放。存取 i_array[1][1] 需換算為一維索引 1*3+1=4。組合碼常見模式:
lea ebx, i_array_addr ; 基址入 EBX
mov edx, 4 ; 索引 4 (編譯期或執行期確定)
mov word ptr [ebx+edx*2], 16h ; 比例因子 2 (word)
比例因子(1,2,4,8)完美對應基本型別大小,極大簡化陣列定址。
結構成員連續存放,但可能因對齊(字/雙字邊界)插入填補位元組。務必使用 sizeof 取得真實大小,而非成員大小相加。成員存取透過基址加偏移:
; struct { int age; int sex; float height; float weight; } marry;
; marry.sex = 0;
lea ebx, marry_addr
mov word ptr [ebx+2], 0 ; sex 偏移 2 位元組
物件類似,但成員函數程式碼位於程式碼段,物件中僅存放函數指標。
C 語言採用 呼叫者清理堆疊、參數右至左壓入 慣例。32 位元環境下預設壓入 32 位雙字。
; long lresult = comb(2, 3);
push 3
push 2
call comb_addr
mov dword ptr [lresult_addr], eax
子程式進入時堆疊狀態:
[ESP] 返回位址
[ESP+4] 參數 2 (3)
[ESP+8] 參數 1 (2)
標準序言建立堆疊幀:
push ebp
mov ebp, esp
sub esp, 4 ; 區域變數 temp (4 bytes)
此後:[ebp+8] 為第一參數,[ebp+0Ch] 為第二參數,[ebp-4] 為區域變數 temp。
mov eax, [ebp-4] ; 返回值放入 EAX
mov esp, ebp ; 撤銷區域變數空間
pop ebp ; 恢復呼叫者 EBP
retn 8 ; 彈出返回位址,並調整 ESP 清除 8 位元組參數
retn 8 確保堆疊平衡。區域變數 temp 隨函數返回消失,切勿返回區域變數指標。
Intel 提供高級語言支援指令:
ENTER size, 0 等同 push ebp / mov ebp,esp / sub esp,sizeLEAVE 等同 mov esp,ebp / pop ebp理解高級語言建構在組合語言層面的具體呈現,是逆向工程與效能優化的核心技能。變數定址、陣列索引換算、結構對齊、堆疊幀建立與銷毀,每一環節都蘊含編譯器的設計智慧。掌握這些對應關係,讓你在面對反組譯碼時如履平地。