windows堆栈溢出简易测试代码 逆向分析

本文是对前面发的“windows堆栈溢出简易测试代码”里的代码进行逆向分析,还是逆向一下比较完善。工具:OD

1.main函数的反汇编代码

Code

main函数的源代码里面就只有一个函数调用和return 0语句。在反汇编代码里面貌似有很多代码,我们需要一一来分析一下:

     1. main函数里面的函数调用是对应与第11行代码,call 00401005,因为这里面只有两个call,而后一个是call _chkesp。

     2. 在call 00401005之前,就是堆栈操作和初始化操作了,第1,2行代码是为了保持堆栈平衡的。

     3. 第3行代码在堆栈上保留了一段0x40长度的空闲空间。

     4. 第4,5,6行是保存寄存器。第7~10行主要是执行stos操作,向edi指向的地址(指向堆栈保留的空闲空间),赋值eax的值0xCCCCCCCC,长度为 ecx(0x10)* sizeof(dword),长度刚好为0x40。

     5. 至于这0x40字节的空间到底有什么用,网上的大虾说是为函数内所有局部变量的开辟的。所有的准备工作做好了之后。

     6. 第12行代码是对eax寄存器清0操作。

     7. 第13,14,15是恢复前面保存的寄存器。

     8. 第16行是调整esp,因为前面开辟了一个段0x40的空间,所以执行的是add esp, 40操作。

     9. 第17,18是对堆栈进行检查。

     10. 第19,20,21行平衡堆栈并返回。

以上是对整个main函数的分析,当然我们的主要目的是分析堆栈如何溢出的,重点是看在执行call 00401005前后堆栈的变化,在这之前,我们先分析一下00401005出的反汇编代码,也就是fun1()的代码。

2. 函数fun1()的反汇编代码

 1 004010D0  |   55            push    ebp
 2 004010D1  |.  8BEC          mov     ebp, esp
 3 004010D3  |.  83EC 48       sub     esp, 48
 4 004010D6  |.  53            push    ebx
 5 004010D7  |.  56            push    esi
 6 004010D8  |.  57            push    edi
 7 004010D9  |.  8D7D B8       lea     edi, dword ptr [ebp-48]
 8 004010DC  |.  B9 12000000   mov     ecx, 12
 9 004010E1  |.  B8 CCCCCCCC   mov     eax, CCCCCCCC
10 004010E6  |.  F3:AB         rep     stos dword ptr es:[edi]
11 004010E8  |.  C745 FC 00000>mov     dword ptr [ebp-4], 0
12 004010EF  |.  8D45 FC       lea     eax, dword ptr [ebp-4]
13 004010F2  |.  8945 F8       mov     dword ptr [ebp-8], eax
14 004010F5  |.  8B4D F8       mov     ecx, dword ptr [ebp-8]
15 004010F8  |.  83C1 08       add     ecx, 8
16 004010FB  |.  894D F8       mov     dword ptr [ebp-8], ecx
17 004010FE  |.  8B55 F8       mov     edx, dword ptr [ebp-8]
18 00401101  |.  C702 0F104000 mov     dword ptr [edx], 0040100F
19 00401107  |.  33C0          xor     eax, eax
20 00401109  |.  5F            pop     edi
21 0040110A  |.  5E            pop     esi
22 0040110B  |.  5B            pop     ebx
23 0040110C  |.  8BE5          mov     esp, ebp
24 0040110E  |.  5D            pop     ebp
25 0040110F  \.  C3            retn

第1~10行的代码和main函数第1~10行的代码都一样,唯一的区别在于保留的空间长度是0x48。第19~25行的代码也差不多,主要是恢复寄存器和堆栈,并返回。下面主要分析第11行到18行的代码:

     1. 第11行向ebp-4所指向的变量赋予0,长度为一个dword,4个字节,ebp-4指向的是第一个变量,即我们的iRet。这一行等价于int iRet = 0;

     2. 第12,13行是向ebp-8所指向的变量赋予ebp-4。lea eax, dword ptr[ebp-4] 等价于eax = ebp - 4。ebp-8指向的是第二个变量,即我们的pRet。(如果不知道为什么,可以查阅变量是如何在堆栈中分布的)这2行等价于int *pRet = &iRet;

     3. 第14行是把ebp-8对应的变量的值赋予ecx,为此ecx = ebp - 4。第15行执行的是ecx += 8操作,这样ecx = ebp + 4了。这2行等价于pRet = pRet + 2;

     4. 第16,17行进过两次mov操作,把ecx的值传给了edx,现在edx = ecx = ebp+4。第18行把0040100F赋予edx指向的地址,而0040100F对应的指令是jmp fun2。这3行等价于*pRet = (int )&fun2;

ebp+4指向的存储单元存储的是函数调用后的返回地址,而由上面的4可以看出来,返回地址被修改成了0040100F,这样,当函数返回的时候,就会去执行0040100F处的指令,即jmp fun2。从而执行函数fun2()。

3. 静态分析完成后,我们看看动态的情况是怎么样的。在执行call 00401005后,运行到fun1()函数的第2行时,堆栈如下

0012FF30存储的是fun1()函数的返回地址0040113D,0012FF2C所存储的是ebp:0012FF2C。当程序继续执行到fun2()的第19行后,堆栈如下

再看看0012FF30存储的值已经变成了0040100F了,即返回地址被改掉了。

整个分析过程到此就结束了。

posted @ 2008-12-14 13:24  暗之魂  阅读(1218)  评论(0编辑  收藏  举报