在缓冲区溢出攻击程序设计中,用Strcpy()构造攻击字符串是很常见的,这两天在做一个小例子的调试过程中,没想到还出了点问题。
以前调试溢出程序时,拿的都是网上的攻击代码,很多时候只要将跳转地址处理好就行,一直没注意这样的小问题,现在认真看了下后,把它给弄明白了。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
// jmp esp address
#define jmpesp "/x12/x45/xfa/x7f"
//jmp to shellcode
#define jmpcode "/xEB/xEE"
char shellcode[] ="/x90/x90/x90/x90/x90/x90/x90/x90";
void overflow()
{
char name[8];
char Buff[20];
memset(Buff, 0x90, sizeof(Buff)-1);
strcpy(Buff, shellcode);
strcpy(Buff+12, jmpesp);
strcpy(Buff+16,jmpcode);
strcpy(name,Buff);
return;
}
void main()
{
printf("begin overflow!!");
overflow();
printf("ok,end!!");
return;
}
关于上面这个溢出小例子的原意是这样的:
在overflow()中首先为name分配了八字节的空间,先将Buff空间全设置为0x90,然后用来保存超常字符串,当执行strcpy(name,Buff)时导致溢出发生,用jmp esp地址覆盖返回地址,然后执行jmpcode指令,再次跳转至shellcode处执行相应代码。从设计思路以及具体实现上来看,这个程序完全可以达到预期效果,但实际调试中,程序并没有按照预期设想执行,竟能够打印出“ok,end!!”字符串,这是怎么回事?
在跟踪调试的过程中,发现返回地址没有被覆盖,于是查看执行完strcpy(name,Buff)后name所指地址空间的内容,竟然是
"/x90/x90/x90/x90/x90/x90/x90/x90/x00",
竟然有个结束符,把字符串给截断了,只导致EBP被改写,而返回地址并没有改变,使得流程按正常情况执行。
跟踪strcpy执行流程发现,它以四字节在源串与目标串之间拷贝数据,之后对末尾部分处理,本例执行
strcpy(Buff, shellcode)时,
当拷贝完八字节后,比较源串数据后执行如下语句
mov byte ptr [edi],dl
此时edi指向Buff+8,dl值为/x00,因此在strcpy(name,Buff)时就将八字节的/x90和一字节的/x00写入Buff地址,只改写了EBP的一个字节,没有改写到返回地址。
如果如此定义:
char shellcode[] ="/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90";
那么拷贝时产生的末尾/x00字节,将被strcpy(Buff+12, jmpesp)语句拷贝的内容覆盖,成为一个完整的字符串,程序正常溢出,跳转。
ok!