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了,即返回地址被改掉了。
整个分析过程到此就结束了。
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了,即返回地址被改掉了。
整个分析过程到此就结束了。
相关文章推荐
- 对各个挑战代码的性能测试和功能分析
- ms05016分析及测试代码
- Linux移植随笔:对tslib库的ts_test测试程序代码的一点分析
- 室内清扫机器人环境搭建与算法测试简易代码
- 【学习逆向工程,分析机器代码】(二)(C++篇)
- canvas元素简易教程(8)(大部分转自火狐,自己只写了简单的代码分析)
- Jmeter测试框架学习总结之代码分析
- 微信短链接秒进支付宝拆红包的逆向分析与代码获取(不用写任何代码)
- [置顶] 恶意代码--windows脚本wscript恶意样本逆向分析
- 基于visual c++之windows核心编程代码分析(38)实践服务器压力测试-SYN请求
- canvas元素简易教程(7)(大部分转自火狐,自己只写了简单的代码分析)
- 使用 Contest 分析测试用例的代码覆盖率
- 软件测试___findbugs静态代码分析工具认识
- PHP "十二五" 简易 BLOG 代码- 未测试- 06
- 恶意代码逆向分析基础入门
- Simens NX (原UG)内部代码逆向分析 / Inner code Reverse analysis of NX software
- “机器狗”病毒驱动部分逆向分析注释(C代码)
- 逆向分析之核心代码的分析
- Linux下gcov和lcov代码覆盖率分析(C/C++覆盖率在NGINX测试中的应用)
- 室内清扫机器人环境搭建与算法测试简易代码