Windows PE 第十一章 动态加载技术

这章是PE原理的最后一章,网上也比较少有这章的总结

章节开篇介绍了windows虚拟地址空间分配

一般情况下,32位的机器上,地址空间从0x000000~0xFFFFFFFF,总大小为4GB。一般而言,虚拟地址空间分为两个区,即为用户空间和系统空间。虚拟地址低空间,即从0x00000000~0X7FFFFFFF的2GB为用户空间,而高地址0x80000000~0xFFFFFFFF被分配给了系统内核。高地址空间2GB内存是提供系统内核使用的。在这高地址空间中安排了操作系统的系统代码和数据,用户一般无法访问到这个地址空间。用户地址空间使用的是低地址2GB内存,其中包含了用户应用程序、调用的动态链接库、用户栈、用户可使用的空闲内存空间等。从整体上看,

Windows虚拟地址空间分配如下表:

用户态低2GB空间分配表

核心态高2GB空间分配表

动态库技术

Windows系统平台上,你可以将独立的程序模块创建为较小的DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试。在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。Microsoft Windows自己就将一些主要的系统功能以DLL模块的形式实现。例如IE中的一些基本功能就是由DLL文件实现的,它可以被其它应用程序调用和集成。一般来说,DLL是一种磁盘文件(通常带有DLL扩展名,是标准win32可执行文件-“PE”格式),它由全局数据、服务函数和资源组成,在运行时被系统加载到进程的虚拟空间中,成为调用进程的一部分,进程中所有线程都可以调用其中的函数。如果与其它DLL之间没有冲突,该文件通常映射到进程虚拟空间的同一地址上。DLL模块中包含各种导出函数,用于向外界提供服务。Windows在加载DLL模块时将进程函数调用与DLL文件的导出函数相匹配。

在Win32环境中,每个进程都复制了自己的读/写全局变量。如果想要与其它进程共享内存,必须使用内存映射文件或者声明一个共享数据段。DLL模块需要的堆栈内存都是从运行进程的堆栈中分配出来的。

DLL文件中包含一个导出函数表(存在于PE的.edata节中)。这些导出函数由它们的符号名和称为标识号的整数与外界联系起来。函数表中还包含了DLL中函数的地址。当应用程序加载DLL模块时时,它并不知道调用函数的实际地址,但它知道函数的符号名和标识号。动态链接过程在加载的DLL模块时动态建立一个函数调用与函数地址的对应表。如果重新编译和重建DLL文件,并不需要修改应用程序,除非你改变了导出函数的符号名和参数序列。

简单的DLL文件只为应用程序提供导出函数,比较复杂的DLL文件除了提供导出函数以外,还调用其它DLL文件中的函数。

每个DLL都有一个入口函数(DLLMain),系统在特定环境下会调用DLLMain。在下面的事件发生时会调用dll入口函数:1.进程装载DLL。2.进程卸载DLL。3.DLL在被装载之后创建了新线程。4. DLL在被装载之后一个线程被终止了。

静态调用

静态调用,也称为隐式调用,由编译系统完成对dll的加载和应用程序结束时dll卸载的编码(windows系统负责对dll调用次数的计数),调用方式简单,能够满足通常的要求。通常采用的调用方式是把产生动态连接库时产生的.lib文件加入到应用程序的工程中,想使用dll中的函数时,只须在源文件中声明一下。 lib文件包含了每一个dll导出函数的符号名和可选择的标识号以及dll文件名,不含有实际的代码。lib文件包含的信息进入到生成的应用程序中,被调用的dll文件会在应用程序加载时同时加载在到内存中。

动态调用

动态调用,即显式调用方式,是由编程者用api函数加载和卸载dll来达到调用dll的目的,比较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。在windows系统中,与动态库调用有关的函数包括:
①loadlibrary(或mfc 的afxloadlibrary),装载动态库。
②getprocaddress,获取要引入的函数,将符号名或标识号转换为dll内部地址。
③freelibrary(或mfc的afxfreelibrary),释放动态链接库。

隐式调用与显式调用的对比

         前面已经详细介绍了动态链接库的两种调用方法,相比之下,隐式调用在编程时比较简单,指定导入库文件后,不必考虑函数的重命名,就可以直接调用动态库函数。但由于隐式调用不能指定动态库的加载时机,因此在一个程序开始运行时,操作系统会将该程序需要的动态链接库都加载入内存,势必造成程序初始化的时间过长,影响用户体验。而显式调用采用动态加载的方法,用到什么加载什么,用完即释放,灵活性较高,可以使程序得到优化。具体运用中到底采用哪种方法,还要依实际情况而定。

发表评论

邮箱地址不会被公开。 必填项已用*标注