C和汇编混合编程--------函数调用后ebp、esp值问题
今天老师又给了一个程序,让我们分析,记录一下分析过程
程序:
#include "stdio.h"
#include "string.h"
char *shellcode="\x64\x65\x66\x67\x68\x69\x70\x71\x05\x10\x40\x00";
void fun1(int a, int b)
{
printf("fun1 run!para a=%d,b=%d\n",a,b);
char aa[4]={0};
strcpy(aa,shellcode);
}
void fun2(int a)
{
printf("fun2 run! para a=%d\n",a);
}
void fun3(int a,int b,int c)
{
printf("fun3 run! para a=%d,b=%d,c=%d\n",a,b,c);
}
int main(int argc, char* argv[])
{
printf("begin\n");
fun1(1,2);
printf("end\n");
return 0;
}
结果:
fun2函数执行了,没有输出来end
首先我们来看看为什么fun2执行了,将程序反汇编
当执行完char aa[4]={0},我们发现0x0019fed0这个地址存放这个数据,此时我们看一下0x0019fed4和0x0019fed8内的数据
当执行完strcpy时,0x0019fed0到0x0019fed8存放的内容是shellcode的内容,为什么是这个数据,看这篇文章(https://blog.csdn.net/qq_41683305/article/details/104282462),继续往下执行
当执行到ret命令时,我们观察esp的值,发现存放的内容是00401005,这个是什么?我们找到fun2的地址
发现和fun2的地址一样,这就是为什么会执行fun2
为什么不会输出end呢?
下面继续分析,按照上面的继续执行,当执行到ret时,此时esp存放的数据是00000001,我们会到这个地址执行
00000001啥都不是,所以不会执行end
下面来解决问题
现在我们来理一理思路,程序先执行fun1,然后去执行fun2,然后到00000001执行,没有返回来,我们要注意,00000001是我们程序fun1(1,2)中的1,所以我们将1改成我们要fun2执行完跳转的地址,就可以继续执行了,这个地址就是下图中的地址,跳转到这里继续执行
我们来改一下,程序:
#include "stdio.h"
#include "string.h"
char *shellcode="\x64\x65\x66\x67\x68\x69\x70\x71\x05\x10\x40\x00";
void fun1(int a, int b)
{
printf("fun1 run!para a=%d,b=%d\n",a,b);
char aa[4]={0};
strcpy(aa,shellcode);
}
void fun2(int a)
{
printf("fun2 run! para a=%d\n",a);
}
void fun3(int a,int b,int c)
{
printf("fun3 run! para a=%d,b=%d,c=%d\n",a,b,c);
}
int main(int argc, char* argv[])
{
printf("begin\n");
fun1(0x401191,2);
printf("end\n");
return 0;
}
我们可以看到fun1里填的0x401191不是0x40118e,这是1和0x401191和所占的字节不同导致的
继续报错,提示堆栈没有平衡,继续分析
我们分析一下,当调用fun1函数前,esp的值如图
fun2执行完,执行完下图的add esp,8时,esp的值为
本应该执行完add esp,8,esp的值应该是调用fun1函数前的值,结果差了4,找到原因了,补上:
#include "stdio.h"
#include "string.h"
char *shellcode="\x64\x65\x66\x67\x68\x69\x70\x71\x05\x10\x40\x00";
void fun1(int a, int b)
{
printf("fun1 run!para a=%d,b=%d\n",a,b);
char aa[4]={0};
strcpy(aa,shellcode);
}
void fun2(int a)
{
printf("fun2 run! para a=%d\n",a);
}
void fun3(int a,int b,int c)
{
printf("fun3 run! para a=%d,b=%d,c=%d\n",a,b,c);
}
int main(int argc, char* argv[])
{
printf("begin\n");
fun1(0x401191,2);
__asm{
sub esp,4
}
printf("end\n");
return 0;
}
栈都平衡了,为什么还会报错呢,这就和检查有没有平衡栈的机制有关了
ebp存放的是调用函数前的esp,函数调用最后需要平衡栈数据,也就是将esp还原成调用函数前的esp,如果比较ebp和esp相等就不报错
没错还和ebp有关,在执行strcpy时,把原来的ebp内容修改了,所以会报错,继续改,还原原来的ebp
#include "stdio.h"
#include "string.h"
char *shellcode="\x64\x65\x66\x67\x68\x69\x70\x71\x05\x10\x40\x00";
void fun1(int a, int b)
{
printf("fun1 run!para a=%d,b=%d\n",a,b);
char aa[4]={0};
strcpy(aa,shellcode);
}
void fun2(int a)
{
printf("fun2 run! para a=%d\n",a);
}
void fun3(int a,int b,int c)
{
printf("fun3 run! para a=%d,b=%d,c=%d\n",a,b,c);
}
int main(int argc, char* argv[])
{
printf("begin\n");
fun1(0x401191,2);
__asm{
sub esp,4
mov ebp,0x19ff30
}
printf("end\n");
return 0;
}
执行,完美解决问题
总结:
- 检查栈有没有平衡,是根据ebp和esp的值,有一个修改都要还原
- ebp存放的是调用函数前的esp,函数调用最后需要平衡栈数据,也就是将esp还原成调用函数前的esp,如果比较ebp和esp相等就不报错,就是下图这两条语句检查的
- esp和ebp的值最后要相同