总所周知,使用scanf,printf,strcpy等这些古老的函数会被VS警告,但是这有什么坏处呢?会造成堆栈溢出,不怀好意的人如果发现了这个漏洞就会写入自己的代码干坏事(VS2008以上已经修复该bug,不过还是不建议使用这些函数,而是使用_s版本的)。
下面是我刚弄出来的一个,还很简陋,就直接贴上来了。
#include
#include
#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
看似这段代码没啥问题,其实危机四起。
00 /$ 55 PUSH EBP 00 |. 89E5 MOV EBP,ESP 00 |. 81EC SUB ESP,88 00 |. A1 D MOV EAX,DWORD PTR DS:[<&msvcrt._iob>] ; || 0040129E |. 08 MOV DWORD PTR SS:[ESP+8],EAX ; || 004012A2 |. C74424 04 003>MOV DWORD PTR SS:[ESP+4],flow.00 ; || 004012AA |. C70424 023040>MOV DWORD PTR SS:[ESP],flow.00 ; ||ASCII "1.txt" 004012B1 |. E8 8A050000 CALL
; |\freopen 004012B6 |. 8D45 88 LEA EAX,DWORD PTR SS:[EBP-78] ; | 004012B9 |. MOV DWORD PTR SS:[ESP],EAX ; | 004012BC |. E8 6F050000 CALL
; \gets 004012C1 |. B8 00000000 MOV EAX,0 004012C6 |. C9 LEAVE 004012C7 \. C3 RETN
这是上面的代码的反汇编代码。
由于gets函数不会管str的大小,也不管是不是超过了100,看见东西就往里面写,写完的数据的栈会变成这样。
0022FED0 0022FEE0 ASCII "User32.dll" 0022FED4 00 flow.00 0022FED8 77C2FC80 OFFSET msvcrt._iob 0022FEDC 7C9301E0 ntdll.7C9301E0 0022FEE0 0022FEE4 642E3233 0022FEE8 48006C6C 0022FEEC 6F6C6C65 0022FEF0 726F5720 0022FEF4 0021646C 0022FEF8 60EC8360 0022FEFC 22FEE068 0022FF00 1D7BB800 0022FF04 D0FF7C80 0022FF08 EB68016A 0022FF0C FE 0022FF10 0022FEEB ASCII "Hello World!" 0022FF14 EAB8006A 0022FF18 FF77D507 0022FF1C 006A61D0 0022FF20 81CB12B8 0022FF24 33D0FF7C 0022FF28 C0 0022FF2C 0022FF30 0022FF34 0022FF38 0022FF3C 0022FF40 0022FF44 0022FF48 0022FF4C 0022FF50 0022FF54 0022FF58 0022FF5C 0022FEF8 0022FF60 77C0AE00 msvcrt.77C0AE00 0022FF64 7C ntdll.7C
从而覆盖了正常的堆栈数据,当执行到RETN的时候,堆栈里面的数据就变成了这样。
0022FF5C 0022FEF8 0022FF60 77C0AE00 msvcrt.77C0AE00 0022FF64 7C ntdll.7C 0022FF68 003F2C90 0022FF6C 004012ED 返回到 flow.004012ED 来自 flow.00
注意栈顶是0022FEF8,当执行RETN的时候,eip就会变成0022FEF8,这就意味着下一条代码就在你刚才写进去的堆栈里面,我发一下1.txt里面的内容。

里面的代码是这样:

可以看出,在里面调用了LoadLibraryA,MessageBoxA,ExitProcessA等API,使得一个看似没有弹窗能力的窗口弹出了一个hello world的消息窗。
如果把这段代码换成你写的其他功能的,就有可能在你不知情的情况下干坏事。
附:VS2008及以上解决此漏洞的方法。
00 /$ 83EC 68 SUB ESP,68 00 |. A1 00 MOV EAX,DWORD PTR DS:[] 00 |. 33C4 XOR EAX,ESP 0040100A |. 64 MOV DWORD PTR SS:[ESP+64],EAX 0040100E |. FF15 A0 CALL DWORD PTR DS:[<&MSVCR90.__iob_func>>; MSVCR90.__p__iob 00 |. 50 PUSH EAX ; /stream 00 |. 68 F PUSH Overflow.004020F4 ; |mode = "r+" 0040101A |. 68 F PUSH Overflow.004020F8 ; |path = "1.txt" 0040101F |. FF15 A CALL DWORD PTR DS:[<&MSVCR90.freopen>] ; \freopen 00 |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C] 00 |. 50 PUSH EAX 0040102A |. 68 00 PUSH Overflow.00 ; /format = "%s" 0040102F |. FF15 9C CALL DWORD PTR DS:[<&MSVCR90.scanf>] ; \scanf 00 |. 8B4C24 78 MOV ECX,DWORD PTR SS:[ESP+78] 00 |. 83C4 14 ADD ESP,14 0040103C |. 33CC XOR ECX,ESP 0040103E |. 33C0 XOR EAX,EAX 00 |. E8 0 CALL Overflow.00 00 |. 83C4 68 ADD ESP,68 00 \. C3 RETN
在里面有一个Call Overflow.00,在操作str之前,会在尾部写入一个标记,这个标记和当前系统的时间有关,然后保存,最后操作完堆栈里面比较这个标记是否被改变,如果改变就直接结束进程,这就使得eip不能指向你写入的堆栈了。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/213230.html原文链接:https://javaforall.net
