反病毒攻防研究第003篇:简单程序漏洞的利用

一、前言

之前文章中所研究的“病毒”都是可执行文件(EXE格式),都是传统意义上的恶意程序,它们在被用户双击运行后,就开始执行自身代码,实现相应的功能,从而对用户的计算机产生威胁。而这次我打算讨论一种特殊的情况,也就是利用正常程序所存在的漏洞,仅仅通过文本文档(TXT格式),来实现我们的对话框的启动。所以这篇文章的讨论重点就在于简单的漏洞发掘以及运用ShellCode实现漏洞的利用。在此不会讨论复杂的情况,仅仅用浅显的例子来说明这些问题,因为即便是现实中的复杂情况,其基本原理是相似的。这也是为以后的篇章中讨论更加复杂的情况打下基础。

二、编写含有漏洞的程序

在现今的软件开发中,尽管程序员的水平在提高,编程技巧在不断进步,但是大部分人对于计算机安全的概念还是比较模糊的,真正掌握计算机安全技术的人毕竟还是少数。特别是计算机安全往往还涉及到系统底层原理、汇编甚至是机器码,这就更加令人望而却步。我在这里讨论的就是一个含有漏洞的程序,它含有缓冲区溢出漏洞。缓冲区溢出攻击是一种非常有效而常见的攻击方法,在被发现的众多漏洞中,它占了大部分。

以下就是本次所研究的程序:

#include <stdio.h>#include <string.h>#include <windows.h>#define PASSWORD "1234567890"int CheckPassword(char *pPassword){int nCheckFlag;char szBuffer[30];nCheckFlag = strcmp(pPassword, PASSWORD);strcpy(szBuffer, pPassword); //存在溢出漏洞return nCheckFlag;}int main(){int nFlag = 0;char szPassword[1024];FILE *fp;LoadLibrary("user32.dll");if(!(fp=fopen("password.txt", "rw+"))){return 0;}fscanf(fp,"%s",szPassword);nFlag=CheckPassword(szPassword);if(nFlag){printf("Incorrect password!\n");}else{printf("Correct password!\n");}fclose(fp);getchar();return 0;}

这里来讲解一下程序的运行流程。main函数中首先会打开当前目录下的password.txt文件,然后调用CheckPassword函数,该函数会对从password.txt文件读取出来的内容与字符串“1234567890”进行比较,用于验证密码是否正确,之后将用户所输入的密码拷贝到子函数自己创建的数组中,再返回到主函数,最后对用户所输入的密码是否正确进行显示。

这个程序中之所以要用TXT文件来保存用户输入的密码,就是为了方便之后的讨论与观察。而在子函数中将用户输入的密码拷贝到一个数组中,仅仅是为了创造一个缓冲区溢出的漏洞,也是为了方便之后的讨论。在现实中,可能难以出现这样的情况。但是原理是一样的,缓冲区溢出漏洞出现的原因就是因为没能检测待拷贝数据的大小,而直接将该数据复制到另一个缓冲区中,从而使得恶意程序得到了攻击的机会。

三、漏洞原理的分析

不论是对于本篇文章所讨论的最简单的缓冲区溢出的漏洞,还是复杂的,可能会在未来的文章中讨论的堆溢出以及SEH的利用,其核心可以说都是利用了指针(或者说是相应的地址)来做文章。对于这次的程序来说,首先需要从反汇编的角度简单讲一下程序的执行原理。

主函数中会调用CheckPassword函数,那么在反汇编中,就需要找到调用该函数的位置,这个很简单:

004010F3 E8 0DFFFFFF call 00401005

之所以能很快确定函数的位置,是因为它在源程序中就在fscanf函数的后面,那么反汇编中,他的位置也在fscanf的后面。这里有必要简单说一下call的实现原理。它分为两步,第一步是向栈中压入当前指令在内存中的位置,即保存返回地址(EIP所保存的地址,也就是call的下一条指令,EIP入栈);第二步是跳转到所调用函数的入口处。进入这个call,来到以下反汇编代码处:

00401020 55push ebp00401021 8BECmov ebp,esp00401023 83EC 64sub esp,64

可以说每一个函数的开始,其反汇编结果都是这么几句。主要是三步:第一步需要保存当前栈帧状态值,,以备后面恢复本栈帧时使用(EBP入栈);第二步将当前栈帧切换到新栈(将ESP值装入EBP,更新栈帧底部);第三步给新栈帧分配空间(把ESP减去所需空间的大小,抬高栈帧)。

这里需要说明的是,以上所分析的是程序的Debug版,如果是Release版,由于做了优化,可能会有些不同,但也是遵循基本原理,在这里就不再对Release版进行讨论。

现在来看看堆栈中的情况,按照内存地址递减的顺序,EBP相比EIP位于内存的低地址处,这两个寄存器在栈中是挨着的。再来看看CheckPassword函数结尾处的反汇编代码:0040106C 8BE5mov esp,ebp0040106E 5Dpop ebp0040106F C3retn

可见这里首先将esp指向了当前栈帧的栈底,之后从栈中弹出原始ebp的值,这样就实现了恢复栈帧的操作。之后的retn语句,就是再次弹出栈顶的值(EIP),并转去执行EIP所指向的语句,也就是之前的call的下一条语句。

分析至此,就可以发现,我们可以通过修改EIP所保存的值(指令地址),来实现跳转到我们自己的“病毒代码”地址处的目的。当然这里我不会利用反汇编工具进行修改,而是直接运用password.txt这个文本文档来实现。

四、定位EIP

知道了漏洞利用的原理,那么接下来就需要确定EIP的位置。EIP的位置尽管可以通过反汇编工具获得,但是在此我不想用这种简单的方法,而是运用Windows的报错对话框实现EIP的定位。当EIP被覆盖为一个无效地址后,系统就会提示出错,报错对话框就能够显示出来究竟是哪个地址(EIP)出错,这里可以通过一些小技巧来定位。

当然,每个人往往有自己认为最好的定位方法,我个人比较喜欢的方法是运用26个小写字母连同26个大写字母组成一长串数据进行测试,这样一次性就能够测试52个字节的长度。具体方法是将这52个字母写入password.txt文件,运行程序查看是否报错,如果不报错,就再写入52个字母,直至报错为止。幸运的是,在这个程序中,前52个字母就使得报错对话框弹出了:带着感恩的心启程,学会爱,爱父母,爱自己,爱朋友,爱他人。

反病毒攻防研究第003篇:简单程序漏洞的利用

相关文章:

你感兴趣的文章:

标签云: