本系列主要從彙編角度研究c++語言機制和彙編的對應關係。第一篇自然應該從最簡單的開始。c++的源**如下:
class my_class
void method(int n)
~my_class()
private :
int m_member;
};
int _tmain(int argc, _tchar* argv)
可以直接debug的時候看到assembly**,不過這樣獲得的**注釋比較少。比較理想的方法是利用vc編譯器的乙個選項/fas來生成對應的彙編**。/fas還會在彙編**中加入注釋註明和c++**的對應關係,十分有助於分析。build**便可以在輸出目錄下發現對應的.asm檔案。本文將逐句分析彙編**和c++的對應關係。
首先是winmain:
_text segment
_wmain proc
push ebp ; 儲存舊的ebp
mov ebp, esp ; ebp儲存當前棧的位置
push -1 ; 建立seh(structured exception handler)鏈
; -1表示表頭,沒有prev
push __ehhandler$_wmain ; seh異常處理程式的位址
mov eax, dword ptr fs:0 ; fs:0指向teb的內容,頭4個位元組是當前seh鏈的位址
push eax ; 儲存起來
sub esp, d8h ; 分配d8h位元組的空間
push ebx
push esi
push edi
lea edi, dword ptr [ebp-e4h] ; e4h = d8h + 4 * 3,跳過中間ebx, esi, edi
mov ecx, 36h ; 36h*4h=d8h,也就是用36h個cccccccch填滿剛才分配的d8h位元組空間
mov eax, cccccccch
rep stosd
mov eax, dword ptr ___security_cookie
xor eax, ebp
push eax ; ebp ^ __security_cookie壓棧儲存
lea eax, dword ptr [ebp-0ch] ; ebp-0ch是新的seh鏈的結構位址(剛壓入棧中的棧位址)
mov dword ptr fs:0, eax ; 設定到teb中作為當前active的seh鍊錶末尾
到此為止棧的內容是這樣的:
低位址security cookie after xor
edi
esi
ebx
local stack: d8h
old fs:0
__ehhandler$_wmain
ffffffffh
old ebp
高位址main接著後面呼叫my_class的建構函式
lea ecx, dword ptr [ebp-14h]
call ??0my_class@@qae@xz ; 呼叫my_class::my_class, ??my_class@@qae@xz是經過name mangling後的名字
mov dword ptr [ebp-4], 0 ; 進入__try塊,在main中有乙個隱式的__try/__except塊
接著呼叫my_class::method
push 10 ; 引數入棧
lea ecx, dword ptr [ebp-14h] ; 遵循thiscall呼叫協定,ecx存放的是this指標
call ?method@my_class@@qaexh@z ; 呼叫子程式my_class:method(10)
之後是析構:
mov dword ptr [ebp-e0h], 0 ; 用來放置返回值
mov dword ptr [ebp-4], -1 ; 標記try的正常結束
lea ecx, dword ptr [ebp-14h] ; a_class的位址作為this存入ecx
call ??1my_class@@qae@xz ; my_class::~my_class
mov eax, dword ptr [ebp-e0h] ; 返回值按照約定放入eax中
main函式退出**如下:
push edx
mov ecx, ebp
push eax
lea edx, dword ptr $ln7@wmain
call @_rtc_checkstackvars@8 ; 檢查棧
pop eax
pop edx
mov ecx, dword ptr [ebp-0ch] ; 取出之前儲存的舊的fs:0,並恢復
mov dword ptr fs:0, ecx
pop ecx
pop edi
pop esi
pop ebx
add esp, e4h ; 退掉分配的d8h + 建立seh鏈所需的0ch位元組
cmp ebp, esp
call __rtc_checkesp ; 檢查esp值,這個時候esp應該和ebp匹配,否則說明出現了棧不平衡的情況,這種情況下呼叫子程式報錯
mov esp, ebp ; 恢復ebp到esp
pop ebp ; 恢復原來的ebp值
ret 0
_wmain endp
專門用於seh的子程式。__unwindfunclet$_wmain$0當異常發生的時候被調,負責進行棧展開,主要是呼叫析構函式。__ehhandler$_wmain則是在exception被丟擲的時候呼叫。
text$x segment
__unwindfunclet$_wmain$0: ; 當seh發生的時候會呼叫該函式,析購a_class
lea ecx, dword ptr [ebp-14h] ; ecx = [ebp – 14h],也就是a_class的位址
jmp ??1my_class@@qae@xz ; 呼叫my_class::~my_class
__ehhandler$_wmain:
mov edx, dword ptr [esp+8] ; esp = 當前的fs:0, [esp + 8] = 之前的seh結構,也就是main中建立的
lea eax, dword ptr [edx+0ch] ; edx + 0ch = 當前的ebp,也就是main的ebp,此時不能直接使用ebp因為可能會從任意函式調過來,此時ebp是該函式的ebp,而不是main的ebp
mov ecx, dword ptr [edx-e0h] ; 之前存下去的__security_cookie ^ ebp
xor ecx, eax ; 再次和ebp相異或
call @__security_check_cookie@4 ; 此時ecx應該等於__security_cookie,否則說明棧的內容被惡意改動(或者程式設計錯誤)
mov eax, offset __ehfuncinfo$_wmain
jmp ___cxxframehandler3
text$x ends
my_class::my_class建構函式如下。建構函式本質上就是乙個全域性函式,名字是經過打亂的(name mangling),這樣可以和同一class和其他class的同名方法區別開來。不同編譯器有不同規則,因此不必過於深究。
乙個簡單函式的反彙編
void myfunction int a,int b int c a b 1 儲存ebp。ebp總是被我們用來儲存這個函式執行前的esp的值。執行完畢後,我們用ebp恢復esp 同時,呼叫此函式的上層函式也用ebp做同樣的事情。所以先把ebp壓入堆疊,返回之前彈出,避免ebp被我們改動。push ...
乙個簡單的verlig程式 乙個簡單C程式的介紹
我們前面學了c語言的一些理論知識,今天通過乙個簡單的程式先來看一看c語言程式是什麼樣子。然後再對程式中的 進行介紹。這個語句的功能是進行有關的預處理操作。include稱為檔案包含命令,後面尖括號的內容稱為標頭檔案或首檔案。此處指包含stdio.h系統標頭檔案,在下面主函式中使用的printf 函式...
objdump 反彙編乙個函式的指令碼
1 使用system.map檔案查詢函式的起始位址和結束位址 2 使用objdump d 反彙編,然後通過 start address和 stop address引數指定函式區間 3 使用nm n out.elf system.map 命令生成system.map檔案 bin sh routine ...