首页
社区
课程
招聘
[旧帖] [原创]缓冲区溢出的简单例子,适合初学者 0.00雪花
2010-4-4 11:01 5145

[旧帖] [原创]缓冲区溢出的简单例子,适合初学者 0.00雪花

2010-4-4 11:01
5145
前几天在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


我们知道,函数的参数和局部变量是在栈中存放的,所以我们在程序中所定义的局部变量i和数组a,我们可以在栈内存中查看,不难发现情况如下:

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

到这里,我们就可以看出其中的猫腻了,程序中for循环一共要执行11次,可是我们所定义的数组a只有10个元素,在执行第11次循环时,发生了溢出,程序把存放在a下面的计数器i也赋值为0了,此时i又小于等于10了,这样就会无限制的执行下去,i永远也不可能超过10,程序就成了无限循环。

总结:这是一个很简单很简单的例子,不过对于这个例子相信对于初学者理解缓冲区溢出,会有帮助。由于在对数组a进行赋值操作时,没有检查数组边界,造成了把下一块内存中的数据覆盖,导致程序的死循环。但是当有心人发现这一点后,就可以加以利用,这里是覆盖了计数器i的值,可是在经过精心的计算后,可以把函数的返回地址覆盖(我们知道,在调用子函数时,会把子函数要返回的指令地址压入堆栈,在子函数执行完后,会把存入栈中的母函数地址pop给EIP,从而返回母函数继续运行),那样就可以得到程序的控制权,使程序跳转到自己想要的地方,有时这也是破解的一种法方。因此,我们在今后的写程序中也要注意这一点。

到这里就结束了,希望对于初学者们能有一下帮助,写的不好,请多包含。(附上程序源码)

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞6
打赏
分享
最新回复 (24)
雪    币: 146
活跃值: (1210)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
曹无咎 1 2010-4-4 11:11
2
0
楼主写的不错,支持一个
雪    币: 435
活跃值: (1132)
能力值: ( LV13,RANK:388 )
在线值:
发帖
回帖
粉丝
bitt 5 2010-4-4 16:55
3
0
楼主写的不错哦,动手能力比我强,
不过严格说这个只能算数组越界访问,还不算栈溢出
可以看看failwest大大的漏洞分析专题
http://bbs.pediy.com/showthread.php?t=56445
相信能写出这样的文章,看完这个一定会对溢出攻击有更深刻的了解
雪    币: 55
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hackjack 2010-4-4 17:04
4
0
不错吗?值得学习。
雪    币: 727
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木叶ss 2010-4-4 18:15
5
0
学习学习...........
雪    币: 521
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
softcheck 2010-4-4 18:35
6
0
多谢指点,呵呵,我去看看,大家一起学习讨论哈
雪    币: 339
活跃值: (51)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
ghban 2010-4-4 18:52
7
0
原来a[10]=0; 时VC不报错的 呵呵
雪    币: 521
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
softcheck 2010-4-4 19:21
8
0
[QUOTE=ghban;785908]原来a[10]=0; 时VC不报错的 呵呵[/QUOTE]

呵呵,在C和C++中,数组名相当于指针,可以直接访问内存,在数组越界时,是不报错的,这一点在C#和Java中的是不会出现的,因为他们依赖于虚拟机,编译时出现数组越界会直接报错,不会出现上面的情况。
雪    币: 133
活跃值: (113)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hearmecryle 1 2010-4-4 19:45
9
0
过来膜拜failwest大大的
雪    币: 521
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
softcheck 2010-4-5 17:45
10
0
都这么长时间了,版主怎么还不来翻贴发邀请码呀十几天了都
雪    币: 36
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gaocai 2010-4-5 18:19
11
0
非常棒,学习了。
雪    币: 57
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
moviebat 2010-4-5 20:58
12
0
有点茅舍顿开的感觉,谢谢啦
雪    币: 116
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
olxdf 2010-4-6 11:13
13
0
非常感谢楼主的讲解,只是小弟有一个小问题还不是很了解,希望给小弟讲解一下

在反汇编中有这么一段,下面一段在程序中起了什么作用啊?我怎么感觉没什么作用一样?

0040101C  |.  B9 1B00>mov ecx,1B
00401021  |.  B8 CCCC>mov eax,CCCCCCCC
00401026  |.  F3:AB   rep stos dword ptr es:[edi]
雪    币: 521
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
softcheck 2010-4-6 12:22
14
0
这段代码是对在栈中分配的局部变量空间进行初始化
mov ecx,1B                                将1Bh赋值给计数器ECX,表示重复操作1Bh次
mov eax,CCCCCCCC                          将CCCCCCCCh 填充变量空间
rep stos dword ptr es:[edi]               控制循环上面的操作,使用ECX做计数器


非常感谢你的提问,希望能解决你的问题
雪    币: 75
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
iEXE 2010-4-10 16:29
15
0
哦、我想我知道为 堆栈是向下增长的了...
雪    币: 28
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
haterh 2010-4-11 21:57
16
0
楼主辛苦了啊,不过这样发的代码看的好累啊,前面从jmp到rep stos都是编译器生成的标准代码,这里要是能说明一下就好了啊

如果能集中关注一下最关键的代码,再精炼一下就更好了,毕竟是写给像我这样的初学者看的嘛,感谢楼主分享。
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
水沐年华 2010-4-14 12:00
17
0
不错嘛,学习中
雪    币: 48
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mynewname 2010-4-14 20:35
18
0
这个说穿了就是数组越界覆盖了另一个变量,这样得邀请码太简单了吧
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
fayuly 2010-4-14 22:03
19
0
学习学习。。。。。。。
雪    币: 101
活跃值: (963)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
shandog 2010-4-14 22:52
20
0
终于明白什么叫溢出了
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
abcdefgkkk 2010-4-14 23:41
21
0
还不错....
雪    币: 63
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
潜行修身 2010-4-14 23:57
22
0
要是可以再深入下就更好了。。
雪    币: 21
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
pqqop 2010-4-15 11:16
23
0
呵呵 我是小菜菜,学习了!!
雪    币: 62
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiaodengzi 2010-4-15 12:34
24
0
没完全懂,收下慢慢学习了!
雪    币: 99
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
weihuowang 2010-4-17 13:26
25
0
学习学习...........
游客
登录 | 注册 方可回帖
返回