DLL的高级操作技术

——Windows核心编程学习手札之二十

显示加载DLL模块:

HINSTANCE LoadLibrary(PCTSTR pszDLLPathName);

HINSTANCE LoadLibraryEx(

PCTSTR pszDLLPathName,

HANDLE hFile,

DWORD dwFlags);

上面两个函数找出用户系统上的文件映像,并将DLL的文件映像映射到调用进行的地址空间中,函数返回的HINSTANCE值用于标识文件映像映射到的虚拟内存地址,如果DLL不能被映射到进程的地址空间,则返回NULL,调用GetLastError可了解详细错误信息。LoadLibraryEx函数中的参数hFile保留使用,现在传递NULL;参数dwFlags有如下设置意义:

1)DON’T_RESOLVE_DLL_REFERENCES

传递该参数告诉系统将DLL映射到调用进程的地址空间中,有两点作用:一是DLL被映射到进程的地址空间中,通常要调用DLL的入口函数DllMain,该标志则告诉系统不需要调用DllMain;二是当DLL内部还需要调用另一个DLL,系统一般会自动加载这些被该DLL所调用的DLL,该标志告诉系统不自动将其他DLL加载到进程的地址空间中。

2)LOAD_LIBIRARY_AS_DATAFILE

该标志在下面情况是有用的:只包含资源不包含函数的DLL,传递该标志下使DLL的文件映像能够映射到进程的地址空间中。

3)LOAD_WITH_ALTERED_SEARCH_PATH

设置该标志按照下面顺序搜索文件:pszDLLPathName参数中设定的目录,进程的当前目录,Windows的系统目录,Windows目录,Path环境变量中列出的目录;

显示卸载DLL模块:

当进程中的线程不再需要DLL中的引用符号时,可以从进程的地址空间中显示卸载DLL:

BOOL FreeLibrary(HINSTANCE hinstDll);

传递HINSTANCE值,以便标识要卸载的DLL,也通过调用下面的函数从进程的地址空间中卸载DLL:

VOID FreeLibraryAndExitThread(

HINSTANCE hinstDll,

DWORD dwExitCode);

该函数是在Kernel32.dll中实现的,如下:

VOID FreeLibraryAndExitThread(HINSTANCE hinstDll,

DWORD dwExitCode){

FreeLibrary(hinstDll);

ExitThread(dwExitCode);}

该函数适用于下面情形:编写一个DLL,当它被初次映射到进程的地址空间中,该DLL便创建一个线程,当所创建的线程完成操作时,通过调用FreeLibrary函数从进程的地址空间卸载DLL,并且终止运行,然后立即调用ExitThread,如果线程分开调用FreeLibrary和ExitThread,就会出现问题,问题是调用FreeLibrary会立即从进程的地址空间中卸载DLL,返回时,包含对ExitThread调用的代码就不可以使用,因此线程将无法执行任何代码,导致访问违规,终止整个进程。如果线程调用FreeLibraryAndExitThread,函数调用FreeLibrary使DLL被卸载,而下步执行指令是在Kernel32.dll中(不是刚被卸载的DLL),意味着线程能够继续执行下去调用ExitThread终止线程且不返回。

显示链接到一个输出符号:

一旦DLL模块被显示加载,线程就要获取引用的符号的地址,方法是:

FARPROC GetProcAddress(

HINSTANCE hinstDll,

PCSTR pszSymbolName);

如:FARPROC pfn=GetProcAddress(hinstDll,”someFunInDll”);

参数pszSymbolName的原型是PCSTR而非PCTSTR,因此只接受ANSI字符串而不能将Unicode字符串传递给该函数,因为编译器和链接程序总是将符号名作为ANSI字符串存储在DLL的输出节中,参数pszSymbolName也可以指明想要的地址的符号的序号,如:

FARPROC pfn=GetProcAddress(hinstDll,MAKEINTRESOURCE(2));

假设知道调用的函数序号是被赋予了2的值。

DLL的进入点函数:

一个DLL拥有单个进入点函数:

BOOL WINAPI DllMain(HINSTANCE hinstDll,DWORD fdwReason,PVOID fImpLoad){

switch(fdwReason){

case DLL_PROCESS_ATTACH:

break;

case DLL_THREAD_ATTACH:

break;

case DLL_THREAD_DETACH:

break;

case DLL_PROCESS_DETACH:

break;

}

Return TRUE;

}

1)DLL_PROCESS_ATTACH通知

当DLL被初次映射到进程的地址空间时,系统将调用该DLL的DllMain函数,传递fdwReason的值即为DLL_PROCESS_ATTACH。

2)DLL_PROCESS_DETACH通知

DLl从进程的地址空间中被卸载时,系统将调用DLL的DllMain函数,fdwReason即传递该值。

3)DLL_THREAD_ATTACH通知

当在一个进程中创建线程时,系统要查看当前映射到该进程的地址空间中的所有DLL文件映像,并调用每个文件映像带有DLL_THREAD_ATTACH值DllMain函数,告诉所有的DLL执行每个线程的初始化操作,新创建的线程负责执行DLL的所有DllMain函数中的代码,只有当所有的DLL都有机会处理该通知时,系统才运行新线程开始执行其线程函数。

4)DLL_THREAD_DETACH通知

线程终止的首选是使其线程函数返回,调用ExitThread函数告诉系统,线程要终止运行,但系统并不立即撤消,相反系统取出即将被撤消的线程,并让它调用已经映射的DLL的所有带有DLL_THREAD_DETACH值DllMain函数,通知所有的DLL执行每个线程的清除操作。

DllMain与C/C++运行期库:

如使用Microsoft的Visual C++编译器来创建DLL,需要得到C/C++运行期库的初始帮助,如创建的DLL中包含一个全局变量,而这个全局变量是个C++类的实例,在DllMain函数使用该全局变量之前,需调用其构造函数,这个工作由C/C++运行期库的DLL启动代码来完成。当链接DLL时,链接程序将DLL的进入点函数嵌入产生的DLL文件映像,使用链接程序的/ENTRY开关来设定该函数的地址。按照默认设置,使用Microsoft的链接程序并设定/DLL开关时,链接程序假设进入点函数称为_DllMainCRTStartup,该函数包含在C/C++运行期的库文件中,并且在链接DLL时被静态链接到DLL的文件映像中。当DLL文件映像被映射到进程的地址空间中时,系统实际先调用_DllMainCRTStartup函数,而不是调用DllMain函数。_DllMainCRTStartup函数负责对C/C++运行期库进行初始化,并且确保_DllMainCRTStartup收到DLL_PROCESS_ATTACH通知时创建任何全局或静态C++对象,当执行任何C/C++运行期初始化时,_DllMainCRTStartup函数将调用DllMain函数。当DLL调用DLL_PROCESS_DETACH通知时,系统再次调用_DllMainCRTStartup函数,该函数即调用DllMain函数,当DllMain返回时,_DllMainCRTStartup就为DLL中的任何全局或静态C++对象调用析构函数。

延迟加载DLL:

延迟加载DLL是个隐含连接的DLL,是等到代码引用DLL中包含的一个符号时才进行加载,对下列情况比较有用:

1)应用程序使用若干DLL,初始化时间就比较长,此时在进程运行时分开加载各个DLL,则不需要一次将所有DLL映射到进程地址空间中;

2)如果调用代码中的一个新函数,在无该函数的旧版本系统上运行,加载程序会报告错误,并且不允许应用程序运行。延迟加载可以先让应用程序运行,运行时发现新函数在旧版本系统上无法运行,则不调用该函数。

延迟加载都像平常一样创建DLL模块及其可执行模块,但需修改两个链接程序开关,并且重新链接可执行模块:

/Lib:DelayImp.lib

/DelayLoad:MyDll.dll

Lib开关告诉链接程序将一个特殊函数delayLoadHelper嵌入可执行模块,DelayLoad开关则告诉链接程序:

1)从可执行模块的输入节中删除MyDll.dll,当进程初始化时,操作系统的加载程序就不会显示加载DLL;

2)将新的Delay Import(延迟输入)节,也称为.tidata嵌入可执行模块,以指明那些函数正在从MyDll.dll中输入;

3)通过转移到对delayLoadHelper函数的调用,转换到对延迟加载函数的调用;

若要卸载延迟加载的DLL,首先创建可执行文件时,设定另一个链接开关程序/delay:unload,其次,修改源代码,在想要卸载DLL是调用:

BOOL __FunLoadDelayLoadedDLL(PCSTR szDll);

链接程序开关/delay:unload该素链接程序将另一个节放入文件中,该节包含了清除已经调用的函数时需要的信息,可以再次调用delayLoadHelper函数。当调用__FunLoadDelayLoadedDLL时,想要卸载的延迟加载的DLL名字传递,该函数进入文件中的未卸载节,并清除DLL的所有函数地址,然后__FunLoadDelayLoadedDLL调用FreeLibrary卸载该DLL。注意,不能自己调用FreeLibrary来卸载DLL,否则函数的地址将不会被清除,这样,当下次调用DLL中的函数时,会导致访问违规。注意二,调用__FunLoadDelayLoadedDLL时,传递的DLL名字不应该包含路径,名字中的字母应和将DLL名字传递给/delayload链接程序开关时使用的字母大小写相同。注意三,如果不打算卸载延迟加载的DLL,则不要设置/delay:unload链接程序开关,且可执行文件的长度应该较小。

DLL的高级操作技术——Windows核心编程学习手札之二十相关推荐

  1. 插入DLL和挂接API——Windows核心编程学习手札之二十二

    插入DLL和挂接API --Windows核心编程学习手札之二十二 如下情况,可能要打破进程的界限,访问另一个进程的地址空间: 1)为另一个进程创建的窗口建立子类时: 2)需要调试帮助时,如需要确定另 ...

  2. 未处理异常和C++异常——Windows核心编程学习手札之二十五

    未处理异常和C++异常 --Windows核心编程学习手札之二十五 当一个异常过滤器返回EXCEPTION_CONTINUE_SEARCH标识符时是告诉系统继续上溯调用树,寻找另外的异常过滤器,但当每 ...

  3. 窗口消息——Windows核心编程学习手札之二十六

    窗口消息 --Windows核心编程学习手札之二十六 Windows允许一个进程至多建立10000个不同类型的用户对象(user object):图符.光标.窗口类.菜单.加速键表等,当一个线程调用一 ...

  4. 异常处理程序和软件异常——Windows核心编程学习手札之二十四

    异常处理程序和软件异常 --Windows核心编程学习手札之二十四 CPU负责捕捉无效内存访问和用0除一个数值这种错误,并相应引发一个异常作为对错误的反应,CPU引发的异常称为硬件异常(hardwar ...

  5. 线程本地存储器——Windows核心编程学习手札之二十一

    线程本地存储器 --Windows核心编程学习手札之二十一 C/C++运行期库使用线程本地存储器,运行期库是在多线程应用程序出现前设计的,因此运行期库里的大多数函数是用于单线程应用程序的.函数strt ...

  6. Unicode——Windows核心编程学习手札之二

    Unicode --Windows核心编程学习手札之二 处理软件本地化的核心在于处理不同的字符集.文本串一直作为一系列单字节字符进行编码,并在结尾处放上一个零,当调用strlen函数时,获取以/0结尾 ...

  7. 结束处理程序——Windows核心编程学习手札之二十三

    结束处理程序 --Windows核心编程学习手札之二十三 使用SEH可以只关注程序要完成任务,而运行中发生的错误,系统将会发现并通知.Windows引入SHE是为了便于操作系统的开发,使用SHE所造成 ...

  8. DLL基础——Windows核心编程学习手札之十九

    DLL基础 --Windows核心编程学习手札之十九 Windows API中的所有函数都包含在DLL中,3个最重要的DLL是Kernel32.dll,它包含用于管理内存.进程和线程的各个函数:Use ...

  9. 内存映射文件——Windows核心编程学习手札之十七

    内存映射文件 --Windows核心编程学习手札之十七 与虚拟内存一样,内存映射文件保留地址空间,并将物理存储器提交给该区域,差别在于所提交的物理存储器是磁盘上有文件存在的空间,而非系统的页文件,一旦 ...

最新文章

  1. 国家、数据、治理:排列组合文字游戏下的思考(附PPT下载)
  2. 青龙羊毛——可推(搬运)
  3. 前端--关于CSS盒模型
  4. SPOJ Python Day2: Prime Generator
  5. ubuntu10.10编译qtopia-2.2.0 问题总结及分析
  6. 配置环境_JavaJDK环境变量配置
  7. LeetCode 123. 买卖股票的最佳时机 III(动态规划)
  8. 计算机漏洞英语怎么说,漏洞英文,漏洞英文发音bug。
  9. java怎么模拟查询账户余额_spring boot + mybatis 模拟银行系统余额查询、转账、存取钱功能实现...
  10. JUnit5 + JMockit 知识整理
  11. 【续篇】再次调戏勒索软件大黑客
  12. VS2019 配色_OPPO Enco M31颜值太顶了,斩获 A'设计大奖赛金奖,引领时尚潮流|oppo|大奖赛|无线耳机|配色|时尚|卡特...
  13. STC12C5A60S2
  14. java程序员 技术成长路线
  15. 电商订单系统,你该如何设计
  16. 理解“卷积” Understanding Convolutions
  17. 为网站配置免费的HTTPS证书 4-4
  18. 玩转树莓派 一、为你的树莓派烧录系统镜像
  19. Jmeter正则表达式提取器的使用
  20. UVA 10242 Fourth Point

热门文章

  1. 延大计算机文化基础课程作业,基于项目学习的大学《计算机文化基础课》教学设计...
  2. mac brew 安装_无用技能之 Mac 安装relion 步骤
  3. 自动化监控--手动添加itme监控项详解
  4. win2003禁止web等目录执行exe,bat,com的方法
  5. Shell脚本实现生成SSL自签署证书
  6. day04--课后练习
  7. 停止复制代理后AWT缓存组的行为
  8. 分治算法求乘方a^b 取余p(divide and conquer)
  9. Java基础——左移和右移
  10. 奥比中光大白(3D结构光)摄像头测试发现对着灯光过曝问题