您的位置:首页 > 编程语言

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

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

1.main函数的反汇编代码

Code

1 00401120 | 55 push ebp

2 00401121 |. 8BEC mov ebp, esp

3 00401123 |. 83EC 40 sub esp, 40

4 00401126 |. 53 push ebx

5 00401127 |. 56 push esi

6 00401128 |. 57 push edi

7 00401129 |. 8D7D C0 lea edi, dword ptr [ebp-40]

8 0040112C |. B9 10000000 mov ecx, 10

9 00401131 |. B8 CCCCCCCC mov eax, CCCCCCCC

10 00401136 |. F3:AB rep stos dword ptr es:[edi]

11 00401138 |. E8 C8FEFFFF call 00401005

12 0040113D |. 33C0 xor eax, eax

13 0040113F |. 5F pop edi

14 00401140 |. 5E pop esi

15 00401141 |. 5B pop ebx

16 00401142 |. 83C4 40 add esp, 40

17 00401145 |. 3BEC cmp ebp, esp

18 00401147 |. E8 C4030000 call _chkesp

19 0040114C |. 8BE5 mov esp, ebp

20 0040114E |. 5D pop ebp

21 0040114F \. C3 retn
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了,即返回地址被改掉了。

整个分析过程到此就结束了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: