裸函數
在函數名前面加上 __deplspec(naked),此時,編譯器對該函數不會進行任何處理。
想要在c語言里面寫匯編的語法,使用vc中輸入__asm
對於一個裸函數而言,就是編譯器不會為這個函數生成代碼,想用匯編怎么寫就怎么寫,如果什么都不寫,一定會報錯,因為沒有生成ret。
1 #include<stdio.h> 2 void __deplspec(naked) Function() 3 { 4 __asm 5 { 6 //保留棧底 7 push ebp ;此時 esp=esp+4 8 //提升棧底 9 mov ebp,esp ;此時 ebp=esp 10 sub esp,0x40 ;此處0x40即為緩沖區大小,可寫任意數值 11 12 //保存現場 13 push ebx 14 push edi 15 push esi 16 17 //往緩沖區中寫入數據 18 mov eax,0xCCCCCCCC 19 ;0xCCCC在gb2312中是燙字,寫C語言的時候出現燙燙燙就是這個原因,屯屯屯是0xCDCD,是堆的問題 20 mov ecx,0x10 ;此處的0x10是0x40除以4得到的 21 lea edi,dword ptr ds:[ebp-0x40] 22 rep stosd 23 24 //實現函數功能的地方 25 26 //恢復現場 27 pop esi 28 pop edi 29 pop ebx 30 //降低棧底 31 mov esp,ebp ;降低esp 32 pop ebp ;降低ebp 33 ret 34 } 35 36 } 37 38 void main() 39 { 40 Function(); 41 }
在上面的例子中,我沒有傳入參數,假如傳遞參數,一般是在函數調用前push入堆棧,具體再后面會提到。
實現兩個數之和的代碼:
1 #include<stdio.h> 2 void __deplspec(naked) Add(int x,int y) 3 { 4 __asm 5 { 6 //保留棧底 7 push ebp ;此時 esp=esp+4 8 //提升棧底 9 mov ebp,esp ;此時 ebp=esp 10 sub esp,0x40 ;此處0x40即為緩沖區大小,可寫任意數值 11 12 //保存現場 13 push ebx 14 push edi 15 push esi 16 17 //往緩沖區中寫入數據 18 mov eax,0xCCCCCCCC 19 ;0xCCCC在gb2312中是燙字,寫C語言的時候出現燙燙燙就是這個原因,屯屯屯是0xCDCD,是堆的問題 20 mov ecx,0x10 ;此處的0x10是0x40除以4得到的 21 lea edi,dword ptr ds:[ebp-0x40] 22 rep stosd 23 //實現兩個數之和的功能 24 mov eax,dword ptr ds:[ebp+0x8] 25 add eax,dword ptr ds:[ebp+0xC] 26 27 28 //恢復現場 29 pop esi 30 pop edi 31 pop ebx 32 //降低棧底 33 mov esp,ebp ;降低esp 34 pop ebp ;降低ebp 35 ret 8 36 } 37 38 } 39 40 void main() 41 { 42 add(2,3); 43 }
假如存在局部變量,局部變量的存儲地方是在緩沖區中。例如,
實現參數求和之后在加上一個常數z的代碼:
1 #include<stdio.h> 2 void __deplspec(naked) Add(int x,int y) 3 { 4 __asm 5 { 6 //保留棧底 7 push ebp ;此時 esp=esp+4 8 //提升棧底 9 mov ebp,esp ;此時 ebp=esp 10 sub esp,0x40 ;此處0x40即為緩沖區大小,可寫任意數值 11 12 //保存現場 13 push ebx 14 push edi 15 push esi 16 17 //往緩沖區中寫入數據 18 mov eax,0xCCCCCCCC 19 ;0xCCCC在gb2312中是燙字,寫C語言的時候出現燙燙燙就是這個原因,屯屯屯是0xCDCD,是堆的問題 20 mov ecx,0x10 ;此處的0x10是0x40除以4得到的 21 lea edi,dword ptr ds:[ebp-0x40] 22 rep stosd 23 24 //實現的功能 25 mov dword ptr ds:[ebp-0x4],1 ;ebp-0x4處存放局部變量z,z=1 26 27 mov eax,dword ptr ds:[ebp+0x8] 28 add eax,dword ptr ds:[ebp+0xC] 29 add eax,dword ptr ds:[ebp+0x10] 30 ;參數之和部分 31 32 add eax,dword ptr ds:[ebp-0x4] ;局部變量求和的部分 33 34 //恢復現場 35 pop esi 36 pop edi 37 pop ebx 38 //降低棧底 39 mov esp,ebp ;降低esp 40 pop ebp ;降低ebp 41 ret 0xC 42 } 43 44 } 45 46 void main() 47 { 48 add(2,3,4); 49 }
根據代碼可以得到,局部變量存放在ebp-0x4開始往低地址,參數是存放在ebp+0x8開始
調用約定
外平棧是指在函數外面平衡堆棧
內平棧是指在函數內部平衡堆棧
例如上面的例子中最后ret 0xC,就是內平棧,因為是在函數內部
外平棧一般是這樣
call myfunction add esp,xxxxx ;且ret后面無數字 call myfunction call function_pinghengduizhan