什么是DLL

动态链接库(Dynamic-Link-Library,缩写dll), 是微软公司在微软windows操作系统中实现共享函数库概念的一种实现方式。这些库函数的扩展名是.DLL、.OCX(包含ActiveX控制的库)或者.DRV(旧式的系统的驱动程序)

DLL劫持一直深受黑客们的喜欢,利用此技术可以实现启动木马后门,游戏外挂插件的注入,绕过UAC等操作。

DLL加载顺序

DLL是以文件的形式存在在硬盘中,Microsoft官方完整记录了DLL搜索顺序

微软的DLL劫持可以分为三个阶段:

  • 无保护阶段:Windows XP SP2之前
  • 保护阶段:Windows XP SP2之后,Windows 7之前
  • 进一步保护阶段:Windows 7之后

Windows XP SP2之前,DLL文件的加载顺序如下:

  1. 进程对应的应用程序所在目录;
  2. 加载 DLL 时所在的当前目录;
  3. 系统目录即 SYSTEM32 目录(通过 GetSystemDirectory 获取);
  4. 16位系统目录即 SYSTEM 目录;
  5. Windows目录(通过 GetWindowsDirectory 获取);
  6. PATH环境变量中的各个目录;

Windows XP SP2之后:

微软为了防止DLL劫持漏洞的产生,在XP SP2之后,添加了一个SafeDllSearchMode的注册表属性。注册表路径如下:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode

当SafeDllSearchMode的值设置为1,即安全DLL搜索模式开启时(“安全DLL查找模式”默认是启用的),查找DLL的目录顺序如下:

  1. 应用程序所在目录;
  2. 系统目录SYSTEM32目录;
  3. 16位系统目录即SYSTEM 目录。该项只是为了向前兼容的处理,可以不考虑;
  4. Windows目录。通常是C:\Windows;
  5. 加载 DLL 时所在的当前目录;
  6. 环境变量PATH中所有目录。需要注意的是,这里不包括App Paths注册表项指定的应用程序路径。

Windows 7之后:

微软为了更进一步的防御系统的DLL被劫持,将一些容易被劫持的系统DLL写进了一个注册表项中,那么凡是此项下的DLL文件就会被禁止从EXE自身所在的目录下调用,而只能从系统目录即SYSTEM32目录下调用。注册表路径如下:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

Windows 10中的KnownDLLs如下

寻找DLL劫持

劫持系统DLL

分析一个应用程序是否存在劫持系统DLL的漏洞,通常需要几个步骤:

  1. 启动应用程序
  2. 使用Process Monitor等类似软件查看该应用程序启动后加载的动态链接库。
  3. 从该应用程序已经加载的DLL列表中,查找在上述“KnownDLLs注册表项”中不存在的DLL。
  4. 编写从上一步获取到的DLL的劫持DLL。
  5. 将编写好的劫持DLL放到该应用程序目录下,重新启动该应用程序,检测是否劫持成功。

如果对以上步骤都没问题还是没有实现劫持的话,具体可能有以下情况

  • DLL不在KnownDLLs注册表中但是已经被微软做了保护,比如ntdll.dll等系统核心dll
  • 宿主进程在调用LoadLibrary函数时使用了“绝对路径”
  • 宿主进程对调用的DLL进行了校检,比如文件MD5、HASH等值
  • 宿主调用DLL时使用了SetDllDirectory函数把当前目录从DLL的搜索顺序列表中删除

劫持应用DLL

只要宿主没有对自己的DLL做校检的话就可以进行劫持替换。

Note:当我们找到了一个可以劫持的DLL的时候,用于劫持的DLL文件需要劫持原DLL文件的所有导出函数,不然无法正常执行。可以用工具辅助生成DLL,例如:AheadLib

案例分析

本文以有道云笔记Windows桌面版v6.10.1.0版本为例。

发现过程

通过Process Monitor的观察,以及windows系统中DLL的默认加载顺序,可以发现有道云笔记在加载msimg32.dll的时候先在应用程序所在目录查找该DLL文件,再从系统目录中查找,并且该DLL文件不在KnownDLLs目录中。由于应用程序目录中并不存在msimg32.dll,所以我们可以利用该DLL文件实现DLL劫持。

漏洞验证

使用AheadLib生成用于劫持的DLL文件源码,该源码主要是导出了msimg32.dll的完整函数接口,保证劫持之后不影响程序正常运行。

然后我们可以在源码中插入我们需要的恶意代码。 DllMain部分代码片段如下图所示,为了验证漏洞,我们让其在劫持成功的时候弹出消息提示窗口。

编译生成同名的“msimg32.dll”DLL文件,并将其放置在有道云笔记的应用程序目录下,该目录通常为任意用户可读可写。

当用户运行、或者系统自动启动有道云笔记的时候,便会触发我们的恶意代码,弹出提示框。并且由于该软件的用户通常会设置开机自启,并且该DLL会在软件运行过程中被多次加载,所以该漏洞还可能被用于后门持久化。

一些关于DLL劫持的参考资料

https://www.freebuf.com/articles/system/243791.html
https://www.anquanke.com/post/id/225911
https://bbs.pediy.com/thread-263668.htm
https://blog.csdn.net/Dome_/article/details/100623838
https://mp.weixin.qq.com/s/SOOzP6tVT0R3kSBkRogbhA
https://www.ascotbe.com/2020/11/13/DynamicLinkLibraryHijack/
https://github.com/strivexjun/AheadLib-x86-x64
https://www.write-bug.com/article/1883.html