安全中国首页 > 文章中心 > 安全防护
 
安全中国网友投稿专用上传FTP空间:
Ftp服务器:download.anqn.com
Ftp端口:21
用户名:anqn
密 码:anqn.com
 

4.4.2 向进程中植入代码(图)

更新时间:2008-8-28 0:12:57
责任编辑:流火
热 点:

4.4.2 向进程中植入代码

为了完成在栈区植入代码并执行,我们在上节的密码验证程序的基础上稍加修改,使用如下的实验代码。

#include <stdio.h>
#include <windows.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[44];
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
return authenticated;
}
main()
{
int valid_flag=0;
char password[1024];
FILE * fp;
LoadLibrary("user32.dll");//prepare for messagebox
if(!(fp=fopen("password.txt","rw+")))
{
exit(0);
}
fscanf(fp,"%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
}
fclose(fp);
}

这段代码在4.3节溢出代码的基础上修改了3处。
(1)增加了头文件windows.h,以便程序能够顺利调用LoadLibrary函数去装载user32.dll。
(2)verify_password函数的局部变量buffer由8字节增加到44字节,这样做是为了有足够的空间来“承载”我们植入的代码。
(3)main函数中增加了LoadLibrary("user32.dll")用于初始化装载user32.dll,以便在植入代码中调用MessageBox。
实验环境如表4-4-1所示。

表4-4-1 实验环境

 

推荐使用的环境

   

操作系统

Windows XP sp2

其他Win32操作系统也可进行本实验

编译器

Visual C++ 6.0

如使用其他编译器,需重新调试

编译选项

默认编译选项

VS2003VS2005中的GS编译选项会使栈溢出实验失败

build版本

debug版本

如使用release版本,则需要重新调试

说明:即便完全采用所推荐的实验环境,函数返回地址、MessageBoxA函数的入口地址等也需要重新确定,因为这些地址可能依赖于操作系统的补丁版本等。这些地址的确定方法在实验指导中均给出了详细的说明。
用VC6.0将上述代码编译(默认编译选项,编译成debug版本),得到有栈溢出的可执行文件。在同目录下创建password.txt文件用于程序调试。

 

我们准备在password.txt文件中植入二进制的机器码,在password.txt攻击成功时,密码验证程序应该执行植入的代码,并在桌面上弹出一个消息框显示“failwest”字样。

让我们在动手之前回顾一下我们需要完成的几项工作。
(1)分析并调试漏洞程序,获得淹没返回地址的偏移。
(2)获得buffer的起始地址,并将其写入password.txt的相应偏移处,用来冲刷返回地址。
(3)向password.txt中写入可执行的机器代码,用来调用API弹出一个消息框。

本节验证程序里verify_password中的缓冲区为44个字节,按照前边实验中对栈结构的分析,我们不难得出栈帧中的状态如图4.4.2所示。

如果在password.txt中写入恰好44个字符,那么第45个隐藏的截断符null将冲掉authenticated低字节中的1,从而突破密码验证的限制。我们不妨就用44个字节作为输入来进行动态调试。

出于字节对齐、容易辨认的目的,我们把“4321”作为一个输入单元。

buffer[44]共需要11个这样的单元。

第12个输入单元将authenticated覆盖;第13个输入单元将前栈帧EBP值覆盖;第14个输入单元将返回地址覆盖。
分析过后,我们需要进行调试验证分析的正确性。首先,在password.txt中写入11组“4321”,共44个字符,如图4.4.3所示

 
图4.4.3 制作溢出文件
如我们所料,authenticated被冲刷后,程序将进入验证通过的分支,如图4.4.4所示。
 
图4.4.4 验证栈的布局
用OllyDbg加载这个生成的PE文件进行动态调试,字符串拷贝函数过后的栈状态如图4.4.5所示。
 
图4.4.5 调试栈的布局

此时的栈区内存如表4-4-2所示。
表4-4-2 栈帧数据

局部变量名

偏移3处的值

偏移2处的值

偏移1处的值

偏移0处的值

buffer[0~3]

0x0012FAF0

0x31 (‘1’)

0x32 (‘2’)

0x33 (‘3’)

0x34 (‘4’)

……

9个双字)

0x31 (‘1’)

0x32 (‘2’)

0x33 (‘3’)

0x34 (‘4’)

buffer[40~43]

0x0012FB18

0x31 (‘1’)

0x32 (‘2’)

0x33 (‘3’)

0x34 (‘4’)

authenticated(被覆盖前)

0x0012FB1C

0x00

0x00

0x00

0x31 (‘1’)

authenticated(被覆盖后)

0x0012FB1C

0x00

0x00

0x00

0x00 (NULL)

前栈帧EBP

0x0012FB20

0x00

0x12

0xFF

0x80

返回地址

0x0012FB24

0x00

0x40

0x11

0x18

动态调试的结果证明了前边分析的正确性。从这次调试中,我们可以得到以下信息。
(1)buffer数组的起始地址为0x0012FAF0。
(2)password.txt文件中第53~56个字符的ASCII码值将写入栈帧中的返回地址,成为函数返回后执行的指令地址。
也就是说,将buffer的起始地址0x0012FAF0写入password.txt文件中的第53~56个字节,在verify_password函数返回时会跳到我们输入的字串开始取指执行。
我们下面还需要给password.txt中植入机器代码。
让程序弹出一个消息框只需要调用Windows的API函数MessageBox。MSDN对这个函数的解释如下。

int MessageBox(
HWND hWnd,   // handle to owner window
LPCTSTR lpText,  // text in message box
LPCTSTR lpCaption,  // message box title
UINT uType   // message box style
);

hWnd [in] 消息框所属窗口的句柄,如果为NULL,消息框则不属于任何窗口。
lpTex [in] 字符串指针,所指字符串会在消息框中显示。
lpCaption [in] 字符串指针,所指字符串将成为消息框的标题。
uType [in] 消息框的风格(单按钮、多按钮等),NULL代表默认风格。

我们将写出调用这个API的汇编代码,然后翻译成机器代码,用十六进制编辑工具填入password.txt文件。

题外话:熟悉MFC的程序员一定知道,其实系统中并不存在真正的MessagBox函数,对MessageBox这类API的调用最终都将由系统按照参数中字符串的类型选择“A”类函数(ASCII)或者“W”类函数(UNICODE)调用。因此,我们在汇编语言中调用的函数应该是MessageBoxA。多说一句,其实MessageBoxA的实现只是在设置了几个不常用参数后直接调用MessageBoxExA。探究API的细节超出了本书所讨论的范围,有兴趣的读者可以参阅其他书籍。

用汇编语言调用MessageboxA需要3个步骤。
(1)装载动态链接库user32.dll。MessageBoxA是动态链接库user32.dll的导出函数。虽然大多数有图形化操作界面的程序都已经装载了这个库,但是我们用来实验的consol版并没有默认加载它。
(2)在汇编语言中调用这个函数需要获得这个函数的入口地址。
(3)在调用前需要向栈中按从右向左的顺序压入MessageBoxA的4个参数。

为了让植入的机器代码更加简洁明了,我们在实验准备中构造漏洞程序的时候已经人工加载了user32.dll这个库,所以第一步操作不用在汇编语言中考虑。

MessageBoxA的入口参数可以通过user32.dll在系统中加载的基址和MessageBoxA在库中的偏移相加得到。具体的我们可以使用VC6.0自带的小工具“Dependency Walker”获得这些信息。您可以在VC6.0安装目录下的Tools下找到它,如图4.4.6所示。

 
图4.4.6 使用Depends

运行Depends后,随便拖拽一个有图形界面的PE文件进去,就可以看到它所使用的库文件了。在左栏中找到并选中user32.dll后,右栏中会列出这个库文件的所有导出函数及偏移地址;下栏中则列出了PE文件用到的所有的库的基地址。

1 2 下一页

 
相关文章
一日一文章
 
一日一软件
一日一动画