一、前言
我之前所编写的用于模拟计算机病毒的对话框程序都是exe文件,所以运行时必将会产生一个进程,产生进程就非常容易被发现。而为了不被发现,可以选择将对话框程序创建为DLL文件。这种文件会加载到已有进程的地址空间中,这样就不会再次创建出进程,隐蔽性相对较好,DLL注入也是恶意程序总会使用的手段。这次我带算用几篇文章的篇幅来论述DLL注入的问题,而这篇文章就首先来讨论一下如何把我之前的对话框程序改写为DLL文件。
二、编写对话框DLL程序
这里我依旧使用VC++6.0,创建一个简单的Win32 Dynamic Link Library项目,之后系统就会自动为我们生成如下代码:
// HackedDll.cpp : Defines the entry point for the DLL application. // #include "stdafx.h" BOOL APIENTRY DllMain( HANDLE hModule, //模块句柄 DWORD ul_reason_for_call, //DllMain函数被调用的原因 LPVOID lpReserved //Windows保留参数 ) { return TRUE; }
这里的DllMain函数中的第二个参数表明该DLL被调用的原因。为了针对不同的情况进行处理(比如当进程加载DLL时,进行资源申请的操作,而在卸载该DLL时,进行资源的释放操作),可以按照以下模板所示编写程序:
BOOL APIENTRY DllMain( HANDLE hModule, //模块句柄 DWORD ul_reason_for_call, //DllMain函数被调用的原因 LPVOID lpReserved //Windows保留参数 ) { switch( ul_reason_for_call ) { case DLL_PROCESS_ATTACH: //当DLL被某进程加载时DllMain被调用 { break; } case DLL_PROCESS_DETACH: //当DLL被某进程卸载时DllMain被调用 { break; } case DLL_THREAD_ATTACH: //进程中有线程被创建时DllMain被调用 { break; } case DLL_THREAD_DETACH: //进程中有线程结束时DllMain被调用 { break; } } return TRUE; }
根据这个模板,结合我们的目的,创建一个MsgBox函数用于对话框的显示:
void MsgBox() { MessageBox(0,"You have been hacked! (by J.Y.)","Warning",0); return; }
然后在主函数的DLL_PROCESS_ATTACH下添加:
case DLL_PROCESS_ATTACH: //当DLL被某进程加载时DllMain被调用 { MsgBox(); break; }
最后在DllMain上方添加一个导出函数:
extern "C" _declspec(dllexport) void MsgBox();
编译以上的代码,能够生成两个重要的文件,一个是HackedDll.dll,另一个是HackedDll.lib。前者是DLL文件,后者是库文件,库文件中包含着导出函数的相关信息。
三、DLL的调用
对于一个DLL文件,可以选择使用静态调用和动态调用。个人觉得动态调用更加有用,所以这里只讨论动态调用的方法。动态调用的程序如下:
#include <windows.h> int main() { HINSTANCE hInst; //动态加载DLL hInst = LoadLibrary("HackedDll.dll"); if (hInst == NULL) { MessageBox(0, "HackedDll.dll文件不存在", "DLL加载失败", 0); return 0; } typedef void (*PFUNMSG)(); //获取DLL的导出函数 PFUNMSG pFunMsg = (PFUNMSG)GetProcAddress(hInst, "MsgBox"); if (pFunMsg == NULL) { MessageBox(0,"获取函数地址失败", "DLL加载失败", 0); return 0; } // pFunMsg(); return 0; }
编译生成可执行文件,然后将之前编写好的HackedDll.dll文件与该exe文件放在同一目录下,执行,就可以调用DLL文件中的对话框函数了:
图1 调用DLL
这里需要说明的是,上述程序中“return0”上面的“pFunMsg()”被我注释起来了,原因是由于我在DLL中的设置是只要DLL被加载,那么MsgBox()函数就会自动被执行,如果“pFunMsg()”不被注释,程序运行中就会执行两次MsgBox()函数。如果我在DLL文件中没有设定加载时启动,那么就应该去掉上述程序中的注释。
四、用PEiD查看DLL文件
在逆向工程中,PEiD这款软件经常会被使用到,因为运用它可以查看PE文件的结构。而DLL文件也属于PE文件。这里我把HackedDll.dll文件拖拽到PEiD的界面上,它就会自动解析出该DLL文件的PE结构,如图所示:
图2 用PEiD查看DLL文件结构
在这里可以看到该文件是由VC6开发的,属于DLL文件。单击“Subsystem”右边的“>”按钮,打开“PE Details”界面,在“Directory Information”中单击“ExportTable” 右边的“>”按钮,就可以打开“Exports Viewer”。由于我所编写的DLL文件中只有一个导出函数MsgBox(),那么该导出表中就只有一个导出项。如下图所示:
图3 查看导出项
当以后编写比较复杂的DLL文件时,用PEiD这款软件就能够很清晰地看出整个程序的内部元素。在整个《反病毒攻防研究》系列中,会多次使用到这个程序。而在以后的《安全类软件编写》系列中,也会尝试编写一个类似的程序。
五、DLL注入的查杀方法
由于DLL是要依托于其它进程才能够“存活”的,所以是不能够用正常手段(比如在cmd下)进行查杀的。所以这里我打算采用Icesword(冰刃)这款软件。
首先运行TestMain.exe程序用于加载HackedDll.dll,此时弹出对话框(如图1所示),然后打开冰刃,选择“进程”,就能够列出目前系统中的所有进程项。找到TestMain.exe,在它上面单击右键,在弹出的菜单中选择“模块信息”,就能够查看与该进程相关的所有模块,如图所示:
图4 利用冰刃查看模块信息
这里可以在选中“HackedDll.dll”后,选择“卸载”或者“强制卸载”,那么这个DLL文件就会从“模块信息”中消失。此时之前弹出的对话框并没有变化,但是当单击对话框中的“确定”按钮后,就会弹出“DLL加载失败”对话框:
图5 DLL成功卸载
这正是我之前的程序中用于错误处理的程序,这也就说明了,该DLL文件的确已经成功卸载。之后就可以依据图4中显示出的该DLL的路径,删掉该DLL文件就可以了。
六、小结
这次编写了一个DLL版的模拟病毒程序,在以后的研究中会多次用到。可见DLL程序并不难编写,重要的还是一套流程,按照“模板”编写就可以了。可以说,随着DLL研究的开展,我们的技术研究也渐渐步入了中级阶段。