裸函数及调用约定
裸函数
在函数名前面加上 __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