寫在前面
此系列是本人一個字一個字碼出來的,包括示例和實驗截圖。本人非計算機專業,可能對本教程涉及的事物沒有了解的足夠深入,如有錯誤,歡迎批評指正。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閑錢,可以打賞支持我的創作。如想轉載,請把我的轉載信息附在文章后面,並聲明我的個人信息和本人博客地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 (一)羽夏看C語言——簡述 ,方便學習本教程。
前篇答疑解惑
答案:不會。
解答:我相信親手做實驗的人都會知道的。
函數
一個程序必須有一個函數,比如C語言中的main函數
,也可以實現別的函數實現功能。本篇文章將從匯編的角度介紹函數,本次實驗的代碼十分簡單。為了防止干擾,請在項目屬性中關閉增量鏈接
和支持僅我的代碼調試
(具體作用請搜索科普,它們會生成一些匯編代碼來實現相應的功能)。
int main()
{
int a = 5;
return 0;
}
得到的反匯編如下:
通過匯編可以看出,函數生成的匯編采用ebp尋址
的方式。前三句匯編進行用ebp尋址的准備
和提棧准備緩沖區
的操作,提供的緩沖區用來給函數內的局部變量用的。
有些生成的反匯編可能是這樣的版本:
push ebp
mov ebp,esp
sub esp,0x40
push ebx
push esi
push edi
lea edi,[ebp-0x40]
mov ecx,0x10
mov eax,0xcccccccc
rep stosd
mov dword ptr [ebp-8],5
xor eax,eax
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
有些人可能看不懂以下代碼:
lea edi,[ebp-0x40]
mov ecx,0x10
mov eax,0xcccccccc
rep stosd
上面的代碼是將0xCC
填充滿整個緩沖區,可幫助檢測到堆棧緩沖區溢出,這是一種常見的攻擊,威脅程序的安全性。0xCC
即為我們調試的普通斷點,匯編為int 3
,即VS
按下F9
下斷點(代碼行左邊的小紅點)。
函數調用
int test(int a, int b)
{
return a + b;
}
int main()
{
int a = test(1, 2);
return 0;
}
然后看一下反匯編:
可以看出函數調用時會通過push
傳遞參數,然后call
一個地址跳轉到test函數
,通過eax
作為返回值
。調用完畢后用add esp,8
平棧,這個所謂的一種調用約定
,被稱為__cdecl
。當然調用約定不知這一種,你也可以自定義,不過得從匯編上實現。
調用約定
當一個函數被調用時,函數的參數會被傳遞給被調用的函數和返回值會被返回給調用函數。函數的調用約定就是描述參數是怎么傳遞和返回值,由誰平衡堆棧,這就是所謂的調用約定。
我們先給出常見的調用約定,其他的調用約定自行科普:
調用約定 | __stdcall | __cdecl | __fastcall | __pascal |
---|---|---|---|---|
參數傳遞順序 | 從右到左 | 從左到右 | 從右到左 | 使用寄存器和棧 |
平棧 | 調用者 | 子程序 | 子程序 | 子程序 |
當然,在IDA
中你可能會看到其他調用約定,比如__thiscall
等。由於調用約定並不是我想重點講述的,具體細節自行科普。C語言默認的調用約定為__cdecl
,所以在 函數調用 部分已有示例。這部分就用__fastcall
做個為調用約定
示例。
int __fastcall test(int a, int b)
{
return a + b;
}
int main()
{
int a = test(1, 2);
return 0;
}
如下是反匯編: