网络安全编程:DLL编程
DLL(Dynamic LinkLibrary,动态连接库)是一个可以被其他应用程序调用的程序模块,其中封装了可以被调用的资源或函数。动态连接库的扩展名一般是DLL,不过有时也可能是其他的扩展名。DLL文件属于可执行文件,它符合Windows系统的PE文件格式,不过它是依附于EXE文件创建的进程来执行的,不能单独运行。一个DLL文件可以被多个进程所装载调用。
Windows操作系统下有非常多的DLL文件,有的是操作系统的DLL文件,有的是应用程序的DLL文件。使用DLL文件有什么好处呢?DLL是动态连接库,相对应地,有静态连接库。动态连接库是在EXE文件运行时被加载执行的,而静态连接库是OBJ文件进行连接时同时被保存到程序中的。动态连接库可以减少可执行文件的体积,在需要的时候进入内存;将软件划分为多个模块,可以按照模块进行开发,对于发布与升级也非常方便。在某些情况下,必须使用DLL才能完成一些工作内容。
本文通过一个简单的DLL程序来初步了解DLL程序的编写。
1. 编写简单的DLL程序
首先从一个简单的DLL程序开始,并在DLL程序中添加一个导出函数。所谓导出函数,就是DLL提供给外部EXE或其他类型的可执行文件调用的函数。当然,DLL本身也可以自身进行调用。
DLL程序的入口函数不是main()函数,也不是WinMain()函数,而是DllMain()函数,该函数的定义如下:
参数说明如下。
hinstDLL: 该参数是当前 DLL 模块的句柄,即本动态连接库模块的实例句柄。
fdwReason: 该参数表示 DllMain()函数被调用的原因。
该参数的取值有4种,也就是说存在4种调用DllMain()函数的情况,这4个值分别是DLL_PROCESS_ATTACH(当DLL被某进程加载时,DllMain()函数被调用)、DLL_PROCESS_DETACH(当DLL被某进程卸载时,DllMain()函数被调用)、DLL_THREAD_ATTACH(当进程中有线程被创建时,DllMain()函数被调用)和DLL_THREAD_DETACH(当进程中有线程结束时,DllMain()函数被调用)。
lpvReserved:保留参数,即不被程序员使用的参数。
启动VC6集成开发环境,创建一个DLL工程。创建一个“A simple DLL Project”类型的工程,VC生成代码如下:
在生成的代码中,函数定义处有一个APIENTRY的函数修饰符。该修饰符为一个宏,其定义如下:
由于DllMain()函数不止一次地被调用,根据调用的情况不同,需要执行不同的代码,比如当进程加载该DLL文件时,可能在DLL中要申请一些资源;而在卸载该DLL时,则需要将先前自身所申请的资源进行释放。出于种种原因,在编写DLL程序时,需要把DllMain()函数的结构写成如下形式:
这是一个switch/case结构,这样写可以达到根据不同的调用原因执行不同的代码。
2. 给DLL添加一个简单的导出函数
上面的代码只是一个简单的DLL程序的开始,并没有实际的意义。对于DLL文件来说,DllMain()并不是必需的。按照DLL文件的本质作用是为其他的可执行文件提供使用,那么DLL程序中需要编写能够提供其他程序使用的函数,这些公开提供给其他程序使用的函数被称为导出函数。在上面代码的基础上添加一个导出函数,定义如下:
extern "C"表示该函数以 C 方式导出。由于源代码是.CPP 文件,因此,如果按照C++的方式导出的话,那么在编译后函数名会被名字粉碎,导致在动态调用该函数时就会极为不方便。__declspec(dllexport)的作用是声明一个导出函数,将该函数从本DLL 中开放提供给其他模块使用。
MsgBox()函数的实现如下:
该函数在被调用时会在MessageBox窗口的标题栏处显示其所在进程的进程名。
这样,第一个DLL文件的编写就完成了。编译连接该代码,查看编译和连接的输出情况会发现VC共生成了2个文件,分别是“FirstDll.dll”和“FirstDll.lib”,前者是供其他可执行程序使用的DLL文件,其中包含了程序员编写的代码、导出函数,而后者是一个库文件,其中包含一些导出函数的相关信息,供调用DLL文件中导出函数函数的程序员编译时使用。
导出DLL中的函数有两种方法,这是其中的一种。另外一种方式是建立一个.DEF的文件来定义导出哪些函数。函数除了可以通过函数名导出外,还可以通过序号进行导出。建立.DEF文件可以较为方便地管理DLL项目中的导出函数(总比在代码中逐个找__declspec(dllexport)要方便很多)。由于这里的代码比较短小,因此使用了__declspec(dllexport)这种定义方法。
3. 对DLL程序的调用方法一
DLL程序是无法单独运行的,它需要通过编写一个EXE程序(当然也可以在另外的DLL程序中调用)来调用这个DLL文件中的导出函数。在VC集成开发环境中添加一个测试项目,在工作区的“Workspace‘FirstDll’:1 project(s)”上单击右键,在弹出的菜单中选择“Add New Project to Workspace”,如图1所示。
图1 添加对DLL进行测试的项目
添加一个控制台的项目,然后编写对DLL进行调用的测试代码,具体如下:
pragma comment (lib, "FirstDll")告诉连接器需要在FirstDll.lib文件中找到DLL中导出函数的信息。
对以上代码进行编译连接,VC会产生一个连接错误,如图2所示。
图2 连接出错信息
这个错误是因为连接器找不到“FirstDll.lib”文件。将“FirstDll.lib”复制到测试项目的目录下,然后添加到测试工程中,再次进行编译连接就成功了。运行编写好的测试程序,会弹出一个错误对话框,如图3所示。
图3 运行测试程序时的错误信息
根据错误提示可以看出是缺少要测试的DLL文件,也就是“FirstDll.dll”文件。将其复制到与可执行文件相同的目录下,然后再次运行,程序可以顺利地被执行。
一般在发布DLL文件时,需要将DLL文件、Lib文件和.h文件同时发布,当然有一个说明文档或手册会显得更加专业。
4. 对DLL程序的调用方法二
前一种方法属于静态调用,其方式是通过连接器将DLL函数的导出函数写进可执行文件。现在使用第二种方法来调用DLL中的函数,这种方法相对于前一种方法是动态调用。动态调用不是在连接时完成的,而是在运行时完成的。动态调用不会在可执行文件中写入DLL的相关信息。现在来写一个关于动态调用的测试程序,该程序的创建方法与静态调用的方法相同,这里不再复述。
动态调用DLL函数的代码如下:
对代码进行编译连接都正常通过。但是请注意,这个程序中并没有用到#pragmacomment()指令,也没有通过lib在程序中留下相关的导入信息。运行编译连接好的程序,程序会给出提示“FirstDll.dll文件不存在”。按照前面的方法,将FirstDll.dll文件复制到与测试程序相同的目录下,运行测试程序,程序执行成功。
DLL的动态加载调用是非常有用的。在第一个测试程序中,如果测试系统的装载器无法找到DLL文件,那么系统会直接报错而退出。而在第二个测试程序中,如果测试程序无法找到DLL文件,则由程序给出一个错误的提示,同时程序其实可以继续往下执行,而不会影响其他代码的运行(当然,由于DLL无法加载可能会损失部分的功能)。明白了动态加载调用和静态加载调用的区别,那么它们的优缺点就很清楚了。静态加载调用使用方便,而动态加载调用灵活性较好。
在有些情况下,必须使用动态加载调用的方法来使用DLL中的导出函数。比如函数OpenThread(),该函数在VC6自带的PSDK中没有提供LIB文件和函数原型定义,没有LIB文件就无法连接成功(在新版的PSDK中有该函数对应的LIB文件)。在这种情况下,只能使用LoadLibrary()和GetProcAddress()这两个函数来动态加载调用OpenThread()函数(其实有很多情况下,在使用DLL文件中的导出函数时是找不到对应的LIB文件的,比如ntdll.dll中的很多函数虽然有导出,但是系统没有提供与其对应的LIB文件)。
现在了解一下LoadLibrary()函数和GetProcAddress()函数的定义。LoadLibrary()函数的定义如下:
该函数只有一个参数,即要加载的DLL文件的文件名。该函数调用成功,则返回一个模块句柄。
GetProcAddress()函数的定义如下:
该函数有两个参数,分别如下。
hModule: 该参数是模块句柄,通常通过 LoadLibrary()函数或 GetModuleHandle()函数获得;
lpProcName:该参数指定要获得函数地址的函数名称。
该函数调用成功,则返回lpProcName指向的函数名的函数地址。
5. 查看DLL程序导出函数的工具介绍
前面介绍DLL编程时提到了导出函数,这里介绍两款查看DLL程序的导出函数的工具。其中一款是VC自带的工具“Depends”,另一款工具是一个功能更加强大的可以用来查看PE结构和识别加壳信息的工具“PEID”。
首先用“Depends”来查看DLL的导出函数,该工具可以在VC6的安装菜单下找到,具体位置为“开始”→“程序”→“Microsoft VisualStudio 6.0”→“Microsoft Visual Studio 6.0Tools”→“Depends”。打开该程序,依次单击菜单项“File”→“Open”,在“打开”对话框中找到所写的FirstDll.dll文件,选中并打开(也可以直接进行拖曳),其工作窗口中显示了FirstDll.dll的信息,如图4所示。
图4 Depends显示界面
在图4的右下角区域范围显示的是该DLL文件导出的函数。从图4中可以看出,FirstDll.dll文件只导出一个MsgBox函数。
对于Depends的介绍就这么多,现在来看另外一个工具“PEID”。该工具是用来识别软件“指纹”信息(开发环境、版本、加壳信息等)的。将FirstDll.dll文件拖曳到PEID界面上,PEID会自动解析出该DLL文件的PE结构信息,界面如图5所示。
图5 PEID显示界面
图6 PE结构详情
在图6中的PE结构详细信息的下半部分有个“目录信息”,其中的第一个目录信息就是导出表信息,单击“导出表”最右侧的“大于号”按钮,出现“导出查看器”界面,如图7所示。
图7 导出查看器
从图7中可以看出,FirstDll.dll文件只有一个导出函数MsgBox(),只存在一个导出项。导出函数的信息与Depends相同。
推荐系统
番茄花园 Windows 10 极速企业版 版本1903 2022年7月(32位) ISO镜像快速下载
语言:中文版系统大小:3.98GB系统类型:Win10新萝卜家园电脑城专用系统 Windows10 x86 企业版 版本1507 2022年7月(32位) ISO镜像高速下载
语言:中文版系统大小:3.98GB系统类型:Win10笔记本&台式机专用系统 Windows10 企业版 版本1903 2022年7月(32位) ISO镜像快速下载
语言:中文版系统大小:3.98GB系统类型:Win10笔记本&台式机专用系统 Windows10 企业版 版本1903 2022年7月(64位) 提供下载
语言:中文版系统大小:3.98GB系统类型:Win10雨林木风 Windows10 x64 企业装机版 版本1903 2022年7月(64位) ISO镜像高速下载
语言:中文版系统大小:3.98GB系统类型:Win10深度技术 Windows 10 x64 企业版 电脑城装机版 版本1903 2022年7月(64位) 高速下载
语言:中文版系统大小:3.98GB系统类型:Win10电脑公司 装机专用系统Windows10 x64 企业版2022年7月(64位) ISO镜像高速下载
语言:中文版系统大小:3.98GB系统类型:Win10新雨林木风 Windows10 x86 企业装机版2022年7月(32位) ISO镜像高速下载
语言:中文版系统大小:3.15GB系统类型:Win10
热门系统
- 1华硕笔记本&台式机专用系统 GhostWin7 32位正式旗舰版2018年8月(32位)ISO镜像下载
- 2雨林木风 Ghost Win7 SP1 装机版 2020年4月(32位) 提供下载
- 3深度技术 Windows 10 x86 企业版 电脑城装机版2018年10月(32位) ISO镜像免费下载
- 4电脑公司 装机专用系统Windows10 x86喜迎国庆 企业版2020年10月(32位) ISO镜像快速下载
- 5深度技术 Windows 10 x86 企业版 六一节 电脑城装机版 版本1903 2022年6月(32位) ISO镜像免费下载
- 6深度技术 Windows 10 x64 企业版 电脑城装机版2021年1月(64位) 高速下载
- 7新萝卜家园电脑城专用系统 Windows10 x64 企业版2019年10月(64位) ISO镜像免费下载
- 8新萝卜家园 GhostWin7 SP1 最新电脑城极速装机版2018年8月(32位)ISO镜像下载
- 9电脑公司Ghost Win8.1 x32 精选纯净版2022年5月(免激活) ISO镜像高速下载
- 10新萝卜家园Ghost Win8.1 X32 最新纯净版2018年05(自动激活) ISO镜像免费下载
热门文章
常用系统
- 1深度技术 Windows 10 x86 企业版 电脑城装机版2018年4月(32位) ISO镜像免费下载
- 2电脑公司 装机专用系统Windows10 x86企业版2020年4月(32位) ISO镜像快速下载
- 3雨林木风系统 Ghost XP SP3 新春特别 装机版 YN2020年2月 ISO镜像高速下载
- 4新萝卜家园Ghost Win8.1 X64位 纯净版2020年2月(自动激活) ISO镜像高费下载
- 5新萝卜家园 GhostWin7 SP1 电脑城极速装机版2018年10月(32位) 最新版ISO镜像下载
- 6笔记本系统Ghost Win8.1 (32位) 极速纯净版2019年11月(免激活) ISO镜像高速下载
- 7深度技术Ghost Win8.1 x32位 特别纯净版2022年3月(免激活) ISO镜像高速下载
- 8番茄花园Ghost Win7 x64 SP1稳定装机版2021年4月(64位) 高速下载
- 9电脑公司 装机专用系统Windows10 x64 企业版2020年1月(64位) ISO镜像高速下载
- 10笔记本系统Ghost Win8.1 (32位) 极速纯净版2022年4月(免激活) ISO镜像高速下载