设为首页 加入收藏

TOP

《Windows核心编程系列》谈谈DLL高级技术(一)
2014-11-24 00:12:00 来源: 作者: 【 】 浏览:69
Tags:Windows 核心 编程 系列 谈谈 DLL 高级技术

本篇文章将介绍DLL显式链接的过程和模块基地址重定位及模块绑定的技术。

第一种将DLL映射到进程地址空间的方式是直接在源代码中引用DLL中所包含的函数或是变量,DLL在程序运行后由加载程序隐式的载入,此种方式被称为隐式链接。

第二种方式是在程序运行时,通过调用API显式的载入所需要的DLL,并显式的链接所想要链接的符号。换句话说,程序在运行时,其中的一个线程能够显式的将该DLL调用到进程地址空间中,并得到DLL中某函数的在进程地址空间的虚拟地址,然后调用该函数。此种方式被称为显式链接。

注意:显式载入某DLL时,不需要该dll的Lib文件,且exe文件中并不包含该dll的导入表。

显示载入DLL模块的步骤:

线程可以调用LoadLibrary将一个DLL映射到进程地址空间。

HMODULE LoadLibrary(PCTSTR pszDLLPathName);

该函数会试图对程序想载入的DLL进行定位,并试图将该DLL映射到调用进程的地址空间中。返回是DLL在调用进程的虚拟地址。即模块的句柄。如果无法将DLL载入到进程地址空间中返回值为NULL.

与它类似的另一个函数

HMODULE LoadLibraryEx(PCTSTR pszDLLPathName,HANDLE hFile,DWORD dwFlags);

也可以实现将DLL载入到进程地址空间的目的。具体请参考MSDN。

加载后如果程序不再需要该DLL,可以调用FreeLibrary将DLL从进程地址空间中卸载:

BOOL FreeLibrary( HMODULE hInstDll );

也可以调用FreeLibraryEx卸载某DLL。

以下函数不仅具有从进程地址空间卸载某DLL的功能,还能退出调用线程:

VOID FreeLibraryAndExitThread(HMODULE hInstDll,DWORD dwExitCode)

{

FreeLibrary(HMODULE hInstDll);

ExitThread(dwExitCode);

}

刚见到时或许你会觉得它很多余。考虑下面的情形:

我们调用一个DLL,该DLL中的代码会创建一个线程,当此线程完成工作后,可以调用FreeLibrary和ExitThread将DLL从进程地址空间中卸载,并终止自己。由于线程是由DLL创建的,线程执行的代码也在DLL中,当线程调用FreeLibrary将它所在的DLL卸载的时候,它后续要执行的代码已不再进程地址空间中了,试图执行不存在的代码可能会导致访问违规,导致进程被终止。

如果线程调用FreeLibraryAndExitThread,此函数在Kernel32.dll中,FreeLibraryAndExitThread函数调用FreeLibrary将线程函数所在的DLL卸载后,其所属DLL Kernel32.dll仍在进程地址空间内,FreeLibraryAndExitThread函数继续执行调用ExitThread,后续代码仍然存在,不会导致访问违规。

每个DLL在进程中都有一个使用计数。LoadLibrary(Ex)会增加其计数,FreeLibrary(Ex)和FreeLibraryAndExitThread会递减其计数。例如:当程序第一次调用LoadLibrary来载入一个DLL时,系统会将此DLL映射到进程地址空间中,并将此DLL的使用计数加一。如果线程后来再次调用LoadLibrary(Ex)时,系统不会将此DLL再次映射到进程地址空间,仅仅递增此DLL的使用计数。为了从进程地址空间中撤销对该DLL的映射,线程必须调用FreeLibrary(Ex)两次。第一次是将此DLL的使用计数减为1,第二次减为0。当系统发现某DLL的使用计数已经为0时,会从进程地址空间卸载此DLL。此时如果线程试图显式调用DLL中的函数将会导致访问违规。

系统会在每个进程中为DLL维护一个使用计数,在本进程调用LoadLibrary仅仅是增加DLL在本进程的使用计数。如果进程A中的一个线程执行了LoadLibrary("Mydll.dll");进程B的某一线程也调用LoadLibrary("Mydll.dll");那么该DLL会被映射到A,B两个进程空间中去,且在A和B进程的使用计数都为1。

调用FreeLibrary("Mydll.dll");也仅仅是递减DLL在本进程内的使用计数。

HMODULE GetModuleHandle(PCTSTR pszModuleName);

该函数可以用来检测某DLL是否被映射到了进程地址空间。如果返回值为NULL,则此DLL未被载入。

当给pszModuleName传NULL时,函数会返回应用程序可执行文件的句柄。

显式链接导出符号

显式载入某个DLL后,线程可以通过调用以下函数来得到它要引用的符号的地址。

FARPROC GetProcAddr(HMODULE hInstDll, PCSTR pszSymbolName);

hInstDll标识导出符号所在的DLL的句柄。它是LoadLibrary(Ex),或是GetModuleHandle所返回的句柄。

pszSymbolName用于标识导出符号。

pszSymbolName可以有两种形式:

第一种:用符号名来指定我们想要得到哪个符号的地址。

如:FARPROC pfn=GetProcAddress(hInstDll,"MyProc");

它是以0结尾的字符串。要注意此字符串是ANSI类型的。因为编译器、链接器始终都是将符号的名称以ANSI字符串的形式保存在DLL的导出段。

第二种:用序号来指定我们想要那个符号的地址。

如:FARPROC pfn=GetProcAddress(hInstDll,MAKERESOURCE(2));

这种方法假定我们知道某个导出符号在某DLL中的序号为2。应该明确的是Microsoft强烈反对使用序号。

使用序号的形式要比使用字符串速度慢,因为系统需要对一字符串标识的符号名进行字符串比较。使用第二种方法即使该序号并没有与任何导出函数相对应,GetProcAddress也会返回非NULL值。其实这个地址是无效的,访问此地址可能会导致访问违规。

注意:使用GetProcAddress返回的函数指针来调用函数之前,需要将它转换成与函数签名相匹配的类型。

例如:

typedef void (CALLBACK *PFN_DUM_MOUDLE)(MODULE hModule);

它是与voi

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇《windows核心编程系列》谈谈使用.. 下一篇VS2008快捷键使用技巧

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: