裸函数及调用约定

裸函数

  在函数名前面加上  __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

 

posted @ 2018-01-20 18:34  newen  阅读(2577)  评论(0编辑  收藏  举报