前几天在PYG里面看到了一段代码,很有意思,差点就把我给迷惑住了,在经过Think之后,想通了其中的猫腻,为了验证一下想法,就把代码编译运行,然后用OD跟了一下,确认了一下自己的想法。个人认为这是一个新手理解缓冲区溢出的很不错的例子,同时也可以锻炼一下自己的逆向分析能力,其实很简单,大牛们请飘过,新手可以参考学习一下。希望版主能发个邀请码,如果能加精的话那就再好不过了,小弟感激涕零。嘻嘻
首先请看那段代码(C语言):
代码:
#include<stdio.h> void main() { int i=0; int a[]={1,2,3,4,5,6,7,8,9,10}; for(i=0;i<=10;i++) { a[i]=0; printf("Hello World!\n"); } }
(使用环境 windows XP+VC6.0)
这段代码经过VC 6.0编译后,运行之,我们看到了程序的结果,在控制台无限制输出了“HelloWorld!”,这是为什么呢?
下面我们用OD调试一下,即可发现其中的原因
OD加载程序,一步步跟,程序在经过初始化运行环境之后,我们来到了这里:
代码:
00401270 |. 8B15 8C>mov edx,dword ptr ds:[427C8C] 00401276 |. 52 push edx 00401277 |. A1 847C>mov eax,dword ptr ds:[427C84] 0040127C |. 50 push eax 0040127D |. 8B0D 80>mov ecx,dword ptr ds:[427C80] 00401283 |. 51 push ecx 00401284 |. E8 7CFD>call Text.00401005 ;/////////注意,这是Main函数,F7跟进
代码:
00401005 /$ /E9 0600>jmp Text.00401010 ;Main函数入口 0040100A | |CC int3 0040100B | |CC int3 0040100C | |CC int3 0040100D | |CC int3 0040100E | |CC int3 0040100F | |CC int3 00401010 |> \55 push ebp ;Main函数开始 00401011 |. 8BEC mov ebp,esp 00401013 |. 83EC 6C sub esp,6C ;在栈中分配局部变量空间 00401016 |. 53 push ebx 00401017 |. 56 push esi 00401018 |. 57 push edi 00401019 |. 8D7D 94 lea edi,dword ptr ss:[ebp-6C] 0040101C |. B9 1B00>mov ecx,1B 00401021 |. B8 CCCC>mov eax,CCCCCCCC 00401026 |. F3:AB rep stos dword ptr es:[edi] 00401028 |. C745 FC>mov dword ptr ss:[ebp-4],0 ; i=0; 0040102F |. C745 D4>mov dword ptr ss:[ebp-2C],1 ; a[0]=1; \ 00401036 |. C745 D8>mov dword ptr ss:[ebp-28],2 ; a[1]=2; | 0040103D |. C745 DC>mov dword ptr ss:[ebp-24],3 ; a[2]=3; | 00401044 |. C745 E0>mov dword ptr ss:[ebp-20],4 ; a[3]=4; | 0040104B |. C745 E4>mov dword ptr ss:[ebp-1C],5 ; a[4]=5; |初始化a数组 00401052 |. C745 E8>mov dword ptr ss:[ebp-18],6 ; a[5]=6; | 00401059 |. C745 EC>mov dword ptr ss:[ebp-14],7 ; a[6]=7; | 00401060 |. C745 F0>mov dword ptr ss:[ebp-10],8 ; a[7]=8; | 00401067 |. C745 F4>mov dword ptr ss:[ebp-C],9 ; a[8]=9; | 0040106E |. C745 F8>mov dword ptr ss:[ebp-8],0A ; a[9]=10; / 00401075 |. C745 FC>mov dword ptr ss:[ebp-4],0 ; for(i=0; 0040107C |. EB 09 jmp short Text.00401087 0040107E |> 8B45 FC /mov eax,dword ptr ss:[ebp-4] ; 把 i 放入EAX 00401081 |. 83C0 01 |add eax,1 ; i++; 00401084 |. 8945 FC |mov dword ptr ss:[ebp-4],eax 00401087 |> 837D FC> cmp dword ptr ss:[ebp-4],0A ; i<=10 0040108B |. 7F 1A |jg short Text.004010A7 0040108D |. 8B4D FC |mov ecx,dword ptr ss:[ebp-4] 00401090 |. C7448D >|mov dword ptr ss:[ebp+ecx*4-2C],0 00401098 |. 68 A42F>|push Text.00422FA4 ; /Arg1 = 00422FA4 ASCII "Hello World! " 0040109D |. E8 3E00>|call Text.004010E0 ; \printf("Hello World!\n); 004010A2 |. 83C4 04 |add esp,4 004010A5 |.^ EB D7 \jmp short Text.0040107E ; 当i<=10时 继续循环 004010A7 |> 5F pop edi 004010A8 |. 5E pop esi 004010A9 |. 5B pop ebx 004010AA |. 83C4 6C add esp,6C 004010AD |. 3BEC cmp ebp,esp 004010AF |. E8 AC00>call Text.00401160 004010B4 |. 8BE5 mov esp,ebp 004010B6 |. 5D pop ebp 004010B7 \. C3 retn
代码:
0012FF54 00000001 \ 0012FF58 00000002 | 0012FF5C 00000003 |数组a在栈中存放 0012FF60 00000004 | 0012FF64 00000005 | 0012FF68 00000006 | 0012FF6C 00000007 | 0012FF70 00000008 | 0012FF74 00000009 | 0012FF78 0000000A / 0012FF7C 00000000 在数组a下一个地址,存放的是我们在程序中的计数器 i
总结:这是一个很简单很简单的例子,不过对于这个例子相信对于初学者理解缓冲区溢出,会有帮助。由于在对数组a进行赋值操作时,没有检查数组边界,造成了把下一块内存中的数据覆盖,导致程序的死循环。但是当有心人发现这一点后,就可以加以利用,这里是覆盖了计数器i的值,可是在经过精心的计算后,可以把函数的返回地址覆盖(我们知道,在调用子函数时,会把子函数要返回的指令地址压入堆栈,在子函数执行完后,会把存入栈中的母函数地址pop给EIP,从而返回母函数继续运行),那样就可以得到程序的控制权,使程序跳转到自己想要的地方,有时这也是破解的一种法方。因此,我们在今后的写程序中也要注意这一点。
到这里就结束了,希望对于初学者们能有一下帮助,写的不好,请多包含。(附上程序源码)