[笔记]Windows核心编程《二十》DLL的高级操作技术(二)

简介: [笔记]Windows核心编程《二十》DLL的高级操作技术(二)

1.2 显式卸载DLL模块 FreeLibrary

当进程中的线程不再需要 D L L中的引用符号时,可以从进程的地址空间中显式卸载 D L L,方法是调用下面的函数:

BOOL FreeLibrary(HINSTANCE hinstD11);

HINSTANCE:HINSTANCE值,以便标识要卸载的 D L L。该值是较早的时候调用LoadLibrary(Ex)而返回的值。

也可以通过调用下面的函数从进程的地址空间中卸载 DLL:

VOID FreeLibraryAndExitThread(
  HINSTANCE hinstDll,
  DWORD dwExitCode
);

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

VOID FreeLibraryAndExitThread(HINSTANCE hinstD11,DWORD dwExitCode){
  FreeLibrary(hinstDll);
  ExitThread(dwExitCode);
}

微软创建FreeLibraryAndExitThread原因:

假定你要编写一个 DLL,当它被初次映射到进程的地址空间中时,该 D L L就创建一个线程。当该线程完成它的操作时,它通过调用FreeLibrary函数,从进程的地址空间中卸载该 DLL,并且终止运行,然后立即调用ExitThread。

但是,如果线程分开调用 FreeLibrary和ExitThread,就会出现一个严重的问题。这个问题是调用FreeLibrary会立即从进程的地址空间中卸载 DLL。当调用的FreeLibrary返回时,包含对ExitThread调用的代码就不再可以使用,因此线程将无法执行任何代码。这将导致访问违规,同时整个进程终止运行。

  • LoadLibrary和LoadLibraryEx 这两个函数用于对与特定的库相关的进程使用计数进行递增;
  • FreeLibrary和FreeLibraryAndExitThread这两个函数则用于对库的每个进程的使用计数进行递减。

第一次加载DLL系统将DLL的文件映像映射到调用进程的地址空间中,并将 D L L的使用计数设置为1。

第二次同一进程的线程加载同一DLL,则不会再次映射DLL到进程的地址空间,系统只是将与该进程的D L L相关的使用计数递增1。

GetModuleFileName

调用GetModuleFileName函数线程就能够确定D L L是否已经被映射到进程的地址空间中。

只有当尚未MyLib.dll被映射到进程的地址空间,才LoadLibrary

HINSTANCE hinstDll = GetModu1eHand1e("MyLib"); // DLL extension assumed
if (hinstDll == NULL) {
  hinstDll = LoadLibrary("MyLib");ll DLL extension assumed}

如果只有 DLL的HINSTANCE值,那么可以调用 GetModuleFileName函数,确定 DLL(或. e x e)的全路径名:

DWORD GetModuleFi1eName(
  HINSTANCE hinstModule,
  PTSTR pszPathName,
  DWORD cchPath
);

hinstModule:是D L L(或. e x e)的HINSTANCE 。

pszPathName:是该函数将文件映像的全路径名放入的缓存的地址。

cchPath:用于设定缓存的大小(以字符为计量单位)。

1.3 显式链接到一个输出符号

一旦D L L模块被显式加载,线程就必须获取它要引用的符号的地址,方法是调用下面的函数:

FARPROC GetProcAddress(
  HINSTANCE hinstD11,PCSTR pszSymbo1Name 
);

hinstDll:调用LoadLibrary (Ex)或GetModuleFi1eName函数而返回的,它用于设定包含符号的D L L的句柄。

pszSymbolName

  1. 以 0结尾的字符串
FARPROC pfn = GetProcAddress(hinstD11,"SomeFuncInDll");
  1. 用于指明你想要其地址的符号的序号
FARPROC pfn = GetProcAddress(hinstDll,MAKEINTRESOURCE(2));

调用GetProcAddress的第一种方法比第二种方法要慢。

但非常不建议使用序号,因为当传递的 序号尚未分配给任何导出的函数,会返回非NULL值,误以为你已经拥有一个有效的地址。

二、DLL的进入点函数 DllMain

如果你创建一个只包含资源的DLL,就不必实现该函数。

如果确实需要在 D L L中接受通知信息,可以实现类似下面的进入点函数:

B00L WINAPI DllMain(HINSTANCE hinstDll,DWORD fdwReason,PVOID fImpLoad){
  switch (fdwReason){ 
    case DLL_PROCESS_ATTACH:
      //The DLL is being mapped into the process's address space.
      break;
    case DLL_THREAD_ATTACH:
      //A thread is being created.
      break;
    case DLL_THREAD_DETACH:
      //A thread is exiting cleanly.
      break;
    case DLL_PROCESS_DETACH:
      //The DlL is being unmapped from the process's address space.
      break;
  }
  return(TRUE);//Used only for DLL_PROCESS_ATTACH
}

hinstDll:包含了D L L的实例句柄,这个值用于标识D L L的文件映像被映射到进程的地址空间中的虚拟内存地址。

fImpLoad:如果D L L是隐含加载的,那么该参数将是个非 0值,如果D L L是显式加载的,那么它的值是0。

fdwReason:用于指明系统为什么调用该函数。该参数可以使用 4个值中的一个。DLL_PROCESS_ATTACH、DLL_THREAD_DETACH、DLL_THREAD_DETACH、DLL_PROCESS_DETACH。

注意:

必须记住,DLL使用DllMain函数来对它们进行初始化。

当你的DllMain函数执行时,同一个地址空间中的其他DLL可能尚未执行它们的DllMain函数。

这意味着它们尚未初始化,因此你应该避免调用从其他DLL中输入的函数。此外,你应该避免从DllMain内部调用LoadLibrary(Ex)和FreeLibrary函数,因为这些函数会形式一个依赖性循环。

Platform SDK文档说,你的D l l M a i n函数只应该进行一些简单的初始化,比如设置本地存储器(第 2 1章介绍),创建内核对象和打开文件等。你还必须避免调用 User、Shell、ODBC、COM、RPC和套接字函数(即调用这些函数的函数),因为它们的DLL也许尚未初始化 ,或者这些函数可能在内部调用LoadLibrary(Ex)函数,这同样会形成一个依赖性循环。

2.1 DLL_PROCESS_ATTACH通知

  • 当DLL被初次映射到进程的地址空间中时,系统将调用该 DLL的DllM a i n函数,给它传递参数fdwReason的值DLL_PROCESS_ATTACH。
  • 当处理DLL_PROCESS_ATTACH时,D L L应该执行D L L中的函数要求的任何与进程相关的初始化。
  • 当处理DLL_PROCESS_ATTACH时,如果DLL的任何一个DllMain函数返回FALSE,指明初始化没有取得成功,系统便终止整个进程的运行。LoadLibrary就会返回NULL。

2.2 DLL_PROCESS_DETACH通知

  • D L L从进程的地址空间中被卸载时,系统将调用 D L L的DllMain函数,给它传递fdwReason的值DLL_PROCESS_DETACH。当D L L处理这个值时,它应该执行任何与进程相关的清除操作。

注意:

如果因为系统中的某个线程调用了TerminateProcess而使进程终止运行,那么系统将不调用带有DLL_PROCESS_DETACH值的D L L的D l l M a i n函数。这意味着映射到进程的地址空间中的任何D LL都没有机会在进程终止运行之前执行任何清除操作。这可能导致数据的丢失。只有在迫不得已的情况下,才能使用 TerminateProcess函数。

2.3 DLL_THREAD_ATTACH通知

当在一个进程中创建线程时,系统要查看当前映射到该进程的地址空间中的所有 DLL文件映像,并调用每个文件映像的带有DLL_THREAD_ATTACH值的DllMain函数。

2.4 DLL_THREAD_DETACH通知

2.5 顺序调用DllMain

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

三、延迟加载DLL

介绍

VC++6.0的特性——延迟加载DLL。

它能够使 D L L的操作变得更加容易。这个特性称为延迟加载 D L L。延迟加载的 D L L是个隐含链接的 D L L,它实际上要等到你的代码试图引用 D L L中包含的一个符号时才进行加载。

应用场景

以下情况延迟加载是有用的:

  • 应用初始化加载的DLL过多,导致程序初始化时间过长。使用延迟加载,在程序不同阶段分时加载DLL可以解决这个问题。
  • 新版本系统的函数,在老版本系统中不存在时,使用延迟加载,在加载dll之前判断系统版本,不存在则不运行新函数。

特性

四、函数转发器

函数转发器是D L L的输出节(导出段)中的一个项目,用于将对一个函数的调用转至另一个 DLL中的另一个函数。

五、已知的DLL

操作系统提供的某些D L L得到了特殊的处理。这些D L L称为已知的D L L。它们与其他D L L基本相同,但是操作系统总是在同一个目录中查找它们,以便对它们进行加载操作。

六、DLL转移

D L L转移特性能够强制操作系统的加载程序首先从你的应用程序目录中加载文件模块。只有当加载程序无法在应用程序目录中找到该文件时,它才搜索其他目录。

为了强制加载程序总是首先查找应用程序的目录,要做的工作就是在应用程序的目录中放入一个文件。该文件的内容可以忽略,但是该文件必须称为 AppName.local。例如,如果有一个可执行文件的名字是 SuperApp.exe,那么转移文件必须称为SuperApp.exe.local。

七、改变模块的位置

每个可执行模块和D L L模块都有一个首选的基地址,用于标识模块应该映射到的进程地址空间中的理想内存地址。当创建一个可执行模块时,链接程序将该模块的首选基地址设置为0x0040 0000。如果是DLL模块,链接程序设置的首选基地址是 0x1000 0000。

八、绑定模块

总结

1.LoadLibrary和LoadLibraryEx区别?


相关文章
|
27天前
|
XML C# 数据格式
掌握了在Windows平台上查看DLL依赖的方法
掌握了在Windows平台上查看DLL依赖的方法
172 4
|
2月前
|
网络协议 API Windows
MASM32编程调用 API函数RtlIpv6AddressToString,windows 10 容易,Windows 7 折腾
MASM32编程调用 API函数RtlIpv6AddressToString,windows 10 容易,Windows 7 折腾
|
2月前
|
Windows
[原创]用MASM32编程获取windows类型
[原创]用MASM32编程获取windows类型
|
2月前
|
JavaScript 前端开发 API
MASM32编程通过WMI获取Windows计划任务
MASM32编程通过WMI获取Windows计划任务
|
2月前
|
API Windows
MASM32编程获取Windows当前桌面主题名
MASM32编程获取Windows当前桌面主题名
|
3月前
|
vr&ar C# 图形学
WPF与AR/VR的激情碰撞:解锁Windows Presentation Foundation应用新维度,探索增强现实与虚拟现实技术在现代UI设计中的无限可能与实战应用详解
【8月更文挑战第31天】增强现实(AR)与虚拟现实(VR)技术正迅速改变生活和工作方式,在游戏、教育及工业等领域展现出广泛应用前景。本文探讨如何在Windows Presentation Foundation(WPF)环境中实现AR/VR功能,通过具体示例代码展示整合过程。尽管WPF本身不直接支持AR/VR,但借助第三方库如Unity、Vuforia或OpenVR,可实现沉浸式体验。例如,通过Unity和Vuforia在WPF中创建AR应用,或利用OpenVR在WPF中集成VR功能,从而提升用户体验并拓展应用功能边界。
68 0
|
3月前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
246 0
|
Windows 数据安全/隐私保护 网络协议
|
6天前
|
监控 安全 网络安全
Windows Server管理:配置与管理技巧
Windows Server管理:配置与管理技巧
26 3
|
9天前
|
存储 安全 网络安全
Windows Server 本地安全策略
由于广泛使用及历史上存在的漏洞,Windows服务器成为黑客和恶意行为者的主要攻击目标。这些系统通常存储敏感数据并支持关键服务,因此组织需优先缓解风险,保障业务的完整性和连续性。常见的威胁包括勒索软件、拒绝服务攻击、内部威胁、恶意软件感染等。本地安全策略是Windows操作系统中用于管理计算机本地安全性设置的工具,主要包括用户账户策略、安全选项、安全设置等。实施强大的安全措施,如定期补丁更新、网络分段、入侵检测系统、数据加密等,对于加固Windows服务器至关重要。