再谈关于缓冲器溢出

来源:互联网 发布:nba2konline韩德君数据 编辑:程序博客网 时间:2024/06/05 10:38

废话少说(说一句废话,这是入门级的)

C/C++的代码:

 

// buffer overflow code by cto@renshenguo.com   #include <windows.h>#include <stdio.h>void fnHack()   //the host never expect to run this{   printf("Your computer has been hacked!\n"); //example for hack action    exit(0);    //exit process}int main(int argc, char* argv[]){int var[4] = {NULL};var[5] = (int)fnHack;printf("The host is running in normal way.\n");return 0;}

 

直接编译:

 

--------------------Configuration: BufferOverflow - Win32 Debug--------------------Build : warning : failed to (or don't know how to) build 'F:\CODES\VISUAL C++\BufferOverflow\Debug\BufferOverflow.pch'Compiling...BufferOverflow.cppf:\codes\visual c++\bufferoverflow\bufferoverflow.cpp(16) : fatal error C1010: unexpected end of file while looking for precompiled header directiveError executing cl.exe.BufferOverflow.exe - 1 error(s), 1 warning(s)

 

Oops!这种情况多数是预编译头引起的:

 


然后选择如下图,确定

 

重新编译:

 

Deleting intermediate files and output files for project 'BufferOverflow - Win32 Debug'.--------------------Configuration: BufferOverflow - Win32 Debug--------------------Compiling...BufferOverflow.cppLinking...BufferOverflow.exe - 0 error(s), 0 warning(s)


然后我们试着运行一下:

 

 

我们没有去调用fnHack函数,但是它却执行力,究竟是怎么回事?

我们先看看这些代码编译以后的反汇编是什么样一副尊容:

@ILT+0(_main):00401005   jmp         main (00401070)@ILT+5(?fnHack@@YAXXZ):0040100A   jmp         fnHack (00401020)1:    // buffer overflow code by cto@renshenguo.com2:    #include <windows.h>3:    #include <stdio.h>4:5:    void fnHack()   //the host never expect to run this6:    {   printf("Your computer has been hacked!\n"); //example for hack action00401020   push        ebp00401021   mov         ebp,esp00401023   sub         esp,40h00401026   push        ebx00401027   push        esi00401028   push        edi00401029   lea         edi,[ebp-40h]0040102C   mov         ecx,10h00401031   mov         eax,0CCCCCCCCh00401036   rep stos    dword ptr [edi]00401038   push        offset string "Your computer has been hacked!\n" (0042001c)0040103D   call        printf (004012b0)00401042   add         esp,47:        exit(0);    //exit process00401045   push        000401047   call        exit (00401120)8:    }0040104C   pop         edi0040104D   pop         esi0040104E   pop         ebx0040104F   add         esp,40h00401052   cmp         ebp,esp00401054   call        __chkesp (00401330)00401059   mov         esp,ebp0040105B   pop         ebp0040105C   ret9:10:   int main(int argc, char* argv[])11:   {   int var[4] = {NULL};00401070   push        ebp00401071   mov         ebp,esp00401073   sub         esp,50h00401076   push        ebx00401077   push        esi00401078   push        edi00401079   lea         edi,[ebp-50h]0040107C   mov         ecx,14h00401081   mov         eax,0CCCCCCCCh00401086   rep stos    dword ptr [edi]00401088   mov         dword ptr [ebp-10h],00040108F   xor         eax,eax00401091   mov         dword ptr [ebp-0Ch],eax00401094   mov         dword ptr [ebp-8],eax00401097   mov         dword ptr [ebp-4],eax12:       var[5] = (int)fnHack;0040109A   mov         dword ptr [ebp+4],offset @ILT+5(fnHack) (0040100a)13:       printf("The host is running in normal way.\n");004010A1   push        offset string "The host is running in normal wa"... (00420044)004010A6   call        printf (004012b0)004010AB   add         esp,414:       return 0;004010AE   xor         eax,eax15:   }004010B0   pop         edi004010B1   pop         esi004010B2   pop         ebx004010B3   add         esp,50h004010B6   cmp         ebp,esp004010B8   call        __chkesp (00401330)004010BD   mov         esp,ebp004010BF   pop         ebp004010C0   ret__chkesp:00401330   jne         __chkesp+3 (00401333)00401332   ret00401333   push        ebp00401334   mov         ebp,esp00401336   sub         esp,000401339   push        eax0040133A   push        edx0040133B   push        ebx0040133C   push        esi0040133D   push        edi0040133E   push        offset string "The value of ESP was not properl"... (004200a0)00401343   push        offset string "" (0042009c)00401348   push        2Ah0040134A   push        offset string "i386\\chkesp.c" (0042008c)0040134F   push        100401351   call        _CrtDbgReport (004046b0)00401356   add         esp,14h00401359   cmp         eax,10040135C   jne         __chkesp+2Fh (0040135f)0040135E   int         30040135F   pop         edi00401360   pop         esi00401361   pop         ebx00401362   pop         edx00401363   pop         eax00401364   mov         esp,ebp00401366   pop         ebp00401367   ret


汇编语言很头疼,不过其实也不是很难,我们来逐个看看:

10:   int main(int argc, char* argv[])
11:   {   int var[4] = {NULL};
00401070   push        ebp    ;ebp入栈,ebp是基址指针,指向堆栈底部(内存地址较大),关于堆栈机制可以查阅汇编语言
00401071   mov         ebp,esp    ;esp传送到ebp,esp是堆栈指针,指向堆栈顶部(内存地址较小)
00401073   sub         esp,50h     ;esp减少0x50个字节,其实就是堆栈大小增加0x50=80个字节
00401076   push        ebx    ;ebx入栈,很多入栈的操作一般就是两个目的:保护寄存器,参数传递,ebx是存储器指针
00401077   push        esi     ;esi入栈,esi是串操作源指针
00401078   push        edi     ;edi入栈,edi是串操作目的指针
00401079   lea         edi,[ebp-50h]    ;ebp-0x50就是原来栈顶esp-0x50,也就是新分配对战的底部,内存地址较大
0040107C   mov         ecx,14h    ;将0x14传送到ecx(通用寄存器)
00401081   mov         eax,0CCCCCCCCh    ;将0xCCCCCCCC传送到eax通用寄存器,这是VC++变量的默认值,CC的二进制是11001100
00401086   rep stos    dword ptr [edi]        ;填充新分配的堆栈空间
00401088   mov         dword ptr [ebp-10h],0      ;将0传送到ebp-0x10的内存单元(0x10=16也就是int var[4])
0040108F   xor         eax,eax       ;对eax异或,任何数值异或自身最终都使自身清零
00401091   mov         dword ptr [ebp-0Ch],eax ;将0传送到以下几个单元(也就是初始化数组var[4] = {NULL})
00401094   mov         dword ptr [ebp-8],eax
00401097   mov         dword ptr [ebp-4],eax

*var[4]的存储情况如下:


12:       var[5] = (int)fnHack;
0040109A   mov         dword ptr [ebp+4],offset @ILT+5(fnHack) (0040100a)   ;这一条指令很关键,他填充的内存单元超出了var数组的范围(填充ebp就已经非法)
13:       printf("The host is running in normal way.\n");
004010A1   push        offset string "The host is running in normal wa"... (00420044)
004010A6   call        printf (004012b0)      ;printf函数有一个必选参数,后面任意多个参数,函数的调用都是先参数入栈,再call函数
004010AB   add         esp,4      ;esp+4,这条指令是主调函数自己清理堆栈(上面push了一个双字参数,如果用汇编语言,各级多数人用pop指令)
14:       return 0;
004010AE   xor         eax,eax  ;清零,据说效率比mov快,也有说pentium以后一样的:eax是累加器
15:   }

 

函数已经执行完了,似乎没有什么错误,也没有调用call fnHack之类的指令出现,那么继续往下看才能看到问题了

004010B0   pop         edi           ;把以前入栈的寄存器全部出栈,其实就是恢复寄存器的值
004010B1   pop         esi
004010B2   pop         ebx
004010B3   add         esp,50h  ;栈顶加0x50就是释放函数增加的堆栈
004010B6   cmp         ebp,esp ;比较栈底和栈顶,影响标志位,ebp和esp的值不变(可以判断堆栈是否空了或者非法,防程序员不小心搞崩了堆栈)
004010B8   call        __chkesp (00401330)
004010BD   mov         esp,ebp ;恢复栈顶
004010BF   pop         ebp           ;恢复栈底(基址指针)
004010C0   ret

我们看看__chkesp这个函数是怎么回事:

__chkesp:
00401330   jne         __chkesp+3 (00401333)      ;不相等则跳转,即如果add  esp,50h后堆栈还有,则转到ret以后
00401332   ret
00401333   push        ebp            ;可以看出这里也是一个函数,至于怎么清零堆栈甚至抛出崩溃,我们不管,我们的代码肯定是相等的
00401334   mov         ebp,esp
00401336   sub         esp,0
00401339   push        eax
0040133A   push        edx
0040133B   push        ebx
0040133C   push        esi
0040133D   push        edi
0040133E   push        offset string "The value of ESP was not properl"... (004200a0)
00401343   push        offset string "" (0042009c)
00401348   push        2Ah
0040134A   push        offset string "i386\\chkesp.c" (0042008c)
0040134F   push        1
00401351   call        _CrtDbgReport (004046b0)
00401356   add         esp,14h
00401359   cmp         eax,1
0040135C   jne         __chkesp+2Fh (0040135f)
0040135E   int         3
0040135F   pop         edi
00401360   pop         esi
00401361   pop         ebx
00401362   pop         edx
00401363   pop         eax
00401364   mov         esp,ebp
00401366   pop         ebp
00401367   ret

回到main函数的后面看看

004010BD   mov         esp,ebp ;恢复栈顶
004010BF   pop         ebp           ;恢复栈底(基址指针),注意这一行,他会从堆栈顶部弹出到ebo寄存器
004010C0   ret       ;这里微软很XX,其实编译后是retn,相当于一条pop eip指令,也就是说ebp的值将被填充为@ILT+5(fnhack),即 jmp fnHack (00401020)

当然,CPU只是个机器,他会机械地跳转到fnHack去执行....

以更好的方法是编译release程序,然后用OllyIce去跟踪,你就发现EIP(指令指针的值)在执行retn后变成了0040100A!!!

不会?杆单了,看图:

当然如果你的是什么创天中文VC++之类的话,那只能自己去对号入座了(应该是设置活动配置之类的)

选择release,确定,rebuild all就可以了!

OllyIce的我就不说了,那个很多人都会,因为很多人都是从跟某些人学黑客开始接触编程的(这也是我近期才发现的)

那么,别人原来的程序又没有fnHack,这些东西有什么意义?

 

不错,但是源代码还是可以注入的,关于注入我现在先不谈了,另外缓冲区溢出还可能是字符串,比如网站

如果用专门编写的程序向存在溢出的网站POST带有汇编指令的请求,那么服务器的CPU将机械的执行那些指令

包括 net user hacker 123456 /add,写到这里,you know that!只要把跳转指令指向shellexecute这个API即可

(什么?不同机器不同?不错不同的机器API的地址可能不同,但是相同的系统必定相同,如同时XP SP3的话,因为32位都是内存映射的,每个进程有4G的独立空间)

 

另外一个应用是,把他指向内置的授权函数!

一些所谓的破解版下载下来仍然需要注册,其实如果它真的是破解版,只要随便输入一堆注册码,或者直接点注册就成功了就是这个原因

不过这仍然是比较初步的破解,比较牛插的是直接用jmp跳过检查,连提示注册都没有

 

水平问题,我不知道我写的是否够确切,各位看的是否够明白.

我知道有部分人会不屑于顾,因为他们是那些OOXX的人.这也是我后来才知道的(IT行业,很多半桶水的在搞软件,搞出来就想卖钱,太多了.但是也有一些高手确实很高手,我发现了,我真的发现了!!!)