裸函数
裸函数
编译器不会做出保存现场、提升堆栈、恢复现场等功能,在调用函数时只会生成CALL指令
一、普通函数
-
声明
int plus(int a, int b) { return a + b; }
-
汇编表示
- 函数代码及断点设置
- 调用普通函数时编译器的处理
参数处理
CALL调用
外平栈
返回值处理
- JMP跳转
- 函数内部处理
提升堆栈
保存现场
设置缓冲区
函数功能实现
恢复现场
函数返回
二、裸函数
-
声明
void __declspec(naked) plus(int a, int b) { }
-
汇编表示
- 函数代码及断点设置
- 调用裸函数时编译器的处理
参数处理
CALL调用
堆栈平衡
- 执行call时发生错误
并未生成ret指令,导致函数无法返回
- 使用
__asm{}
手动添加汇编代码
- 加上返回值与函数功能实现
- 上方实现功能的汇编代码为简化版本,如按照常规方法因为以下方式
#include "stdafx.h"
int __declspec(naked) plus(int a, int b)
{
__asm {
//保留调用前的栈底
push ebp
//替身堆栈
mov ebp,esp
sub esp,0x40
//保留现场
push ebx
push esi
push edi
//填充缓冲区
mov eax,0xCCCCCCCC
mov ecx,0x10
lea edi,dword ptr ds:[ebp-0x40]
rep stosd
//函数功能
mov eax,dword ptr ds[ebp+0x8]
add eax,dword ptr ds[ebp+0xC]
//恢复现场
pop edi
pop esi
pop ebx
//降低堆栈
mov esp,ebp
pop ebp
ret
}
}
int main(int argc, char* argv[])
{
int num = plus(1, 2);
return 0;
}
三、总结
经过以上实验,不难看出,裸函数可以看作是不交由编译器自动处理的函数,可以说是编译器不管这个函数,其内部功能需要开发者手动编写