在msvc环境下,x86 cdecl 函数调用约定下,看别人的视频讲了栈溢出控制跳转的一个例子,但是没看明白,他改写了EIP的返回地址,按照网上栈帧的介绍
在函数调用时,生成新栈帧的时候,地址从高到低依次是:
- 函数参数
- 返回地址(EIP)
- 前栈帧的栈底EBP
- 当前栈帧的局部变量空间
他通过局部变量的地址进行越界,找到了栈帧的返回地址(函数调用结束后的下一条指令地址),改它,改成了另一个函数地址,从而改变了程序流程。但是仅仅这个操作不行,函数退出的时候会平栈,程序会崩。
到这里都没有问题,根据网上的介绍,都能理解。
然后就是他修复平栈问题的处理方案,他继续越界,改变了紧接着返回地址的下一个4字节存储空间,按照我的理解,这里应该是函数参数的栈空间啊。
按照他的搞法,然后还真的平了,这个是什么道理?
#pragma optimize("",off)
void test_no_param() {
char* str = "stack over flow.";
MessageBoxA(NULL,str, "stop", MB_OK | MB_ICONINFORMATION);
}
void test() {
//PUSH EBP
//MOV EBP,ESP
//0.
//SUB ESP,08
//int tmp[2] = { 0 };
//tmp[sizeof(tmp)/sizeof(int)+1] = (int)test_no_param;
//1.
//SUB ESP,04
int tmp = 0;
//EBP=(&tmp)+1
//EIP=(&tmp)+2
*((&tmp) + 2) = (int)test_no_param;
//POP EBP
//RET; cdecl
//RET 4; stdcall
}
int add_param(int a, int b)
{
int c = 0;
c = a + b;
printf("%d+%d=%d\n",a,b,c);
return c;
}
int add(int a, int b)
{
int tmp[2] = { 0 };
tmp[3] = (int)add_param;
//tmp[4]放的是什么呢?网上说是下一条的指令指针地址
//???为什么呢?
tmp[4] = (int)test;
tmp[5] = a;
tmp[6] = b;
tmp[4] = a;
return 0;
}
int main() {
//test();
add(3, 2);
printf("wow.\n");
}
#pragma optimize("",on)