在VS2008中进行的缓冲区溢出实验
其中代码如下所示:
1: #include <stdio.h>
2: #include <stdlib.h>
3:
4: void why_here(void) //这个函数没有任何地方调用过
5: {
6: printf("why u here !n\n");
7: printf("you are traped here\n");
8: system("pause");
9: _exit(0);
10: }
11:
12:
13: int main(int argc,char * argv[])
14: {
15:
16:
17: int buff[1];
18: buff[3] = 0x004113c0; //buff[3]=0x0041111d; buff[3]=why_here;
19:
20:
21: system("pause");
22: return 0;
23: }
在代码中,没有任何地方调用了why_here函数,但是该程序的执行结果如下图所示:
可以看到,why_here函数被执行了,我们来看看调试过程中反汇编的结果:
1: int main(int argc,char * argv[])
2: {
3: 00411450 55 push ebp
4: 00411451 8B EC mov ebp,esp
5: 00411453 81 EC CC 00 00 00 sub esp,0CCh
6: 00411459 53 push ebx
7: 0041145A 56 push esi
8: 0041145B 57 push edi
9: 0041145C 8D BD 34 FF FF FF lea edi,[ebp-0CCh]
10: 00411462 B9 33 00 00 00 mov ecx,33h
11: 00411467 B8 CC CC CC CC mov eax,0CCCCCCCCh
12: 0041146C F3 AB rep stos dword ptr es:[edi]
13:
14:
15: int buff[1];
16: buff[3] = 0x004113c0;
17: 0041146E C7 45 04 C0 13 41 00 mov dword ptr [ebp+4],offset why_here (4113C0h)
18:
19:
20: system("pause");
21: 00411475 8B F4 mov esi,esp
22: 00411477 68 D8 57 41 00 push offset string "pause" (4157D8h)
23: 0041147C FF 15 B8 82 41 00 call dword ptr [__imp__system (4182B8h)]
24: 00411482 83 C4 04 add esp,4
25: 00411485 3B F4 cmp esi,esp
26: 00411487 E8 B9 FC FF FF call @ILT+320(__RTC_CheckEsp) (411145h)
27: return 0;
28: 0041148C 33 C0 xor eax,eax
29: }
30: 0041148E 52 push edx
31: 0041148F 8B CD mov ecx,ebp
32: 00411491 50 push eax
33: 00411492 8D 15 B4 14 41 00 lea edx,[ (4114B4h)]
34: 00411498 E8 EA FB FF FF call @ILT+130(@_RTC_CheckStackVars@8) (411087h)
35: 0041149D 58 pop eax
36: 0041149E 5A pop edx
37: 0041149F 5F pop edi
38: 004114A0 5E pop esi
39: 004114A1 5B pop ebx
40: 004114A2 81 C4 CC 00 00 00 add esp,0CCh
41: 004114A8 3B EC cmp ebp,esp
42: 004114AA E8 96 FC FF FF call @ILT+320(__RTC_CheckEsp) (411145h)
43: 004114AF 8B E5 mov esp,ebp
在程序的执行过程中,我们在监视窗口中,查看如下变量的值:
通过汇编代码我们可以看到,进入main函数后,堆栈操作过程如下:
1.将ebp压栈:push ebp
2.将ebp指向esp的位置:mov ebp, esp
3.将esp减去0xCCh字节:sub esp, 0CCh
4.将ebx, esi, edi 压栈:push ebx; push esi; push edi;
5.将ebp和esp+8的位置初始化为:0xCCCCCCCCh: 汇编代码9~12行
进入main函数过后的堆栈变化如下图所示:
通过上图可以发现,&buff[3]的地址就是main函数的返回地址eip存放的地方,我们在C代码的18行将buff[3]赋值为why_here的地址,所以eip就被修改成了why_here的地址,
这样在main函数返回的时候,就会跳转到why_here执行。如果这段代码的是恶意代码,那么将会对系统造成严重的损害。
总结:
一直都很懒,不想记录技术心得,今天又把缓冲区溢出拿出来学习了一下,虽然很简单,感觉还是受益匪浅。一定要理清楚x86堆栈的结构,要学会使用vs进行调试,在看C语言不行的时候就要进行反汇编。
这个程序,以前直接将buff[0]~buff[0x3d]的位置都赋值为why_here的地址,这样的话vs2008在运行的时候就会报错,说检查到堆栈溢出,这应该是ebp也被破坏了的原因。还有个问题就是:
我直接对buff[3]=(int)why_here;赋值或者对buff[3]=0x41111d;赋值都可以跳转到why_here的函数体。C语言代码的18行。
刚开始,我一直不明白,把why_here赋值给buff[3]其实得到的值也是0x41111d,这样就出现了将0x41111d和0x004113c0赋值给buff[3]都可以跳转到why_here的情况,即一个函数对应了两个地址。这让我非常的疑惑。
让我们来看看汇编代码的0x41111d和0x004113c0分别存放了什么,如下图所示:
0x41111d存放的是一条跳转指令,这条指令的目标地址是why_here的实际地址,即0x4113c0.
0x4113c0存放的是why_here的实际地址:
我们注意到0x41111d位置处,其实是一个函数的跳转表,
当我们去某个函数的名字,如why_here的时候,我们得到的是这个跳转表中某一项的地址,而不是函数的实际地址。
至于为什么这样做,我觉得应该和编译器有关,个人猜想:
1.把函数都放到一个跳转表中,只要跳转表的地址不变,那么函数实际存放的位置即使变了也无所谓,只需要修改jmp指令的目标地址就行了。
2.用户把函数名赋值给一个整形变量,得到的是跳转表中的地址,而不是实际存放函数的地址,如果怀有恶意的程序员修改了这个地址的话比直接修改实际地址带来的危害应该更小。