這幾天學(xué)習(xí)匯編,分析了一下 c++ 中函數(shù)調(diào)用(cdecl 和 fastcall 方式)過程的匯編碼,記錄如下:
程序例子
struct tagTest
{
int n1;
long n2;
DWORD n3;
};
long funtest1(tagTest p1,int p2,LPCTSTR lpszP3)
{ // 普通函數(shù)
p1.n1 = 3;
LPCTSTR lpszxx = lpszP3;
p1.n3 = p2;
return 300;
}
long __stdcall funtest2(tagTest p1,int p2,LPCTSTR lpszP3)
{ // stdcall 函數(shù)
p1.n1 = 3;
LPCTSTR lpszxx = lpszP3;
p1.n3 = p2;
return 300;
}
1、普通調(diào)用(cdecl)。
調(diào)用方 C++ 代碼:
long ixx = 0;
tagTest tag1={34,6,87};
ixx = funtest1(tag1,i2,"asdffffffdddddd");
生成的匯編碼:
long ixx = 0; 0104171E mov dword ptr [ixx],0
tagTest tag1={34,6,87}; 01041738 mov dword ptr [tag1],22h ; 成員賦值 0104173F mov dword ptr [ebp-10h],6 ; 成員賦值 01041746 mov dword ptr [ebp-0Ch],57h ; 成員賦值 ixx = funtest1(tag1,i2,"asdffffffdddddd"); 0104174D push offset CAnonymousAsmTestApp::`vftable'+0F4h (11E60B0h) ; 入棧參數(shù)
; "asdffffffdddddd" 的地址。這里顯示似乎有問題(實(shí)際地址是對(duì)的) 01041752 mov edx,dword ptr [i2] 01041755 push edx ; 入棧參數(shù) i2 01041756 sub esp,0Ch ; 在棧中分配參數(shù) tag1 的空間 01041759 mov eax,esp 0104175B mov ecx,dword ptr [tag1] 0104175E mov dword ptr [eax],ecx ; 入棧 tag1.n1 01041760 mov edx,dword ptr [ebp-10h] 01041763 mov dword ptr [eax+4],edx ; 入棧 tag1.n2 01041766 mov ecx,dword ptr [ebp-0Ch] 01041769 mov dword ptr [eax+8],ecx ; 入棧 tag1.n3 0104176C call funtest1 (1041680h) ; 調(diào)用函數(shù)。
;注意:這里同時(shí)將返回地址(下條指令的地址) 也入棧(這里是4字節(jié));
; 所以,函數(shù)中取得參數(shù)時(shí),需要從當(dāng)前 ESP 中加上 4 字節(jié)! 01041771 add esp,14h ; 由調(diào)用者清參數(shù)棧 01041774 mov dword ptr [ixx],eax ; 獲取返回值
注意:已經(jīng)關(guān)閉了編譯器的某些優(yōu)化選項(xiàng),使匯編碼更易讀!
調(diào)用方代碼總結(jié):
-
參數(shù)從右向左依次入棧。
-
棧指針(ESP)從底部向頂部依次減小。也就是說,棧頂?shù)刂沸。粭5椎刂反蟆?/div>
- 如果參數(shù)是結(jié)構(gòu)(struct),則直接移動(dòng)棧頂指針,預(yù)留出結(jié)構(gòu)的大小;然后用 mov 指令逐步將成員拷貝到預(yù)留出來的??臻g!
-
call 指令調(diào)用目標(biāo)地址
-
調(diào)用方清棧
-
獲得返回值
函數(shù)中生成的匯編碼
long funtest1(tagTest p1,int p2,LPCTSTR lpszP3) {
00351680 push ebp ; 基址指針入棧 00351681 mov ebp,esp ; 將函數(shù)入口點(diǎn)的棧指針作為當(dāng)前基址指針。
;顧名思義,基址指針就是進(jìn)入函數(shù)時(shí)的棧頂指針! 00351683 push ecx ; 在棧中分配局部變量空間。
;ecx 中的值無關(guān)緊要,只是預(yù)留一個(gè) 4 字節(jié)空間而已 p1.n1 = 3; 00351684 mov dword ptr [p1],3 ; 賦值 LPCTSTR lpszxx = lpszP3; 0035168B mov eax,dword ptr [lpszP3] ; [lpszP3] 地址應(yīng)該就是 ebp - 4 0035168E mov dword ptr [lpszxx],eax ; 賦值(通過 eax) p1.n3 = p2; 00351691 mov ecx,dword ptr [p2] 00351694 mov dword ptr [ebp+10h],ecx ; 賦值(通過 ecx) return 300; 00351697 mov eax,12Ch ; 返回值放在 eax } 0035169C mov esp,ebp ; 恢復(fù)棧:清除局部變量 0035169E pop ebp ; 恢復(fù)基址指針 0035169F ret ; 返回(從棧中彈出返回地址,然后 jmp )
2、_stdcall調(diào)用。
調(diào)用方 C++ 代碼:
int i2=3; long ixx = 0;
tagTest tag1={34,6,87};
ixx = funtest2(tag1,i2,"asdffffffdddddd");
生成的匯編碼:
int i2=3; 010A1759 mov dword ptr [i2],3 long ixx = 0; 010A1760 mov dword ptr [ixx],0
tagTest tag1={34,6,87}; 010A1767 mov dword ptr [tag1],22h 010A176E mov dword ptr [ebp-0Ch],6 010A1775 mov dword ptr [ebp-8],57h
ixx = funtest2(tag1,i2,"asdffffffdddddd"); 010A177C push offset CAnonymousAsmTestApp::`vftable'+104h (12460C0h) ; 入棧參數(shù)
; "asdffffffdddddd" 的地址。這里顯示似乎有問題(實(shí)際地址是對(duì)的)
; 和 cdecl 一樣,參數(shù)從右邊開始入棧
010A1781 mov eax,dword ptr [i2] 010A1784 push eax 010A1785 sub esp,0Ch 010A1788 mov ecx,esp 010A178A mov edx,dword ptr [tag1] 010A178D mov dword ptr [ecx],edx 010A178F mov eax,dword ptr [ebp-0Ch] 010A1792 mov dword ptr [ecx+4],eax 010A1795 mov edx,dword ptr [ebp-8] 010A1798 mov dword ptr [ecx+8],edx 010A179B call funtest2 (10A16B0h) 010A17A0 mov dword ptr [ixx],eax ; 取得返回值。
; 注意:這里沒有清棧的代碼,由函數(shù)自己清棧!
注意:已經(jīng)關(guān)閉了編譯器的某些優(yōu)化選項(xiàng),使匯編碼更易讀!
調(diào)用方代碼總結(jié):
-
參數(shù)從右向左依次入棧。
-
棧指針(ESP)從底部向頂部依次減小。也就是說,棧頂?shù)刂沸?;棧底地址大?/div>
- 如果參數(shù)是結(jié)構(gòu)(struct),則直接移動(dòng)棧頂指針,預(yù)留出結(jié)構(gòu)的大小;然后用 mov 指令逐步將成員拷貝到預(yù)留出來的棧空間!
-
call 指令調(diào)用目標(biāo)地址
-
返回時(shí),由函數(shù)自己清棧(通過 ret 指令)
-
獲得返回值
函數(shù)中生成的匯編碼
long __stdcall funtest2(tagTest p1,int p2,LPCTSTR lpszP3) { 010A16B0 push ebp 010A16B1 mov ebp,esp 010A16B3 push ecx p1.n1 = 3; 010A16BD mov dword ptr [p1],3 LPCTSTR lpszxx = lpszP3; 010A16C4 mov eax,dword ptr [lpszP3] 010A16C7 mov dword ptr [lpszxx],eax p1.n3 = p2; 010A16CA mov ecx,dword ptr [p2] 010A16CD mov dword ptr [ebp+10h],ecx
return 300; 010A16D0 mov eax,12Ch } 010A16D5 mov esp,ebp 010A16D7 pop ebp 010A16D8 ret 14h ; 返回并清棧。
;(從棧中彈出返回地址,返回;然后ESP增加14H,14H為參數(shù)棧的字節(jié)數(shù))
|