th-DLL劫持

DLL劫持已经是一个很古老成熟对技术了,以前就有看到看雪上有许多人用来做游戏外挂(2008年左右甚至更早以前),近期我了解到现在还有许多对人使用DLL劫持做权限维持,因为之前只是一直知道这个技术原理但是却没有实际使用过,并且最近看到有人开源了一份非常不错的检测工具,于是就打算动手做一下相关对分析研究。

DLL劫持简介

DLL全称dynamic-link library,即动态链接库,是一个包含可以由多个程序同时使用的代码和数据的库。比如,windows操作系统中的Comdlg32.dll 包含常见的对话框相关导出函数,当我们在程序中加载该DLL后,就能使用该DLL中的函数来实现“打开”对话框。

用大白话解释,每一个DLL都有一些特定的功能,包含该DLL即可使用这些功能。
normalDll

而DLL劫持其实就是在不破坏目标DLL的功能的情况下,增加一些恶意代码,使程序执行恶意代码。有两种思路,第一种是编写恶意DLL做函数转发,大致流程如下所示
hajackload

第二种思路是直接将恶意代码写入到正常DLL中。
effectnormal

另外,还有一类利用思路也归并到DLL劫持当中,这里我们就称之为遗弃DLL劫持吧。

遗弃DLL劫持方式

这种方式个人感觉最方便,只需要一个免杀的DLL即可(当然免杀也不简单),所以优先讲这个。但在介绍利用函数转发方式的DLL劫持方法之前,必须要先介绍一下Windows对于指定DLL的搜索方式。

DLL搜索顺序

当在代码中使用LoadLibrary(“hello.dll”)函数来加载DLL时,操作系统便会开始搜索DLL。
在系统搜索DLL之前,它会检查以下内容:
1、如果已在内存中加载具有相同模块名称的DLL,则系统将使用加载的DLL,无论它在哪个目录中,系统不搜索DLL。
2、如果DLL位于KnownDLLs列表中,则系统将使用其已知DLL(以及已知DLL的相关DLL,如果有),系统不搜索DLL。

默认情况下启用安全DLL搜索模式。要禁用此功能则需要创建HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager \SafeDllSearchMode注册表值并将其设置为0。

默认开启的SafeDllSearchMode搜索顺序如下:
1、进程对应的应用程序所在目录
2、系统目录(使用GetSystemDirectory函数获取此目录的路径)
3、16位系统目录(c:\windows\system没有函数可以获取此目录的路径,但会搜索它)
4、Windows目录(使用GetWindowsDirectory函数获取此目录的路径)
5、当前工作目录(Current Directory)
6、PATH环境变量中列出的目录

搜索DLL的顺序
Win10虚拟机下的DLL搜索情况,注意到当前工作目录最后才搜索,与MSDN描述不太一样。

禁用SafeDllSearchMode后的搜索顺序如下:
1、进程对应的应用程序所在目录
2、当前工作目录(Current Directory)
3、系统目录(使用GetSystemDirectory函数获取此目录的路径)
4、16位系统目录(c:\windows\system没有函数可以获取此目录的路径,但会搜索它)
5、Windows目录(使用GetWindowsDirectory函数获取此目录的路径)
6、PATH环境变量中列出的目录

注: KnownDLLs

注册表位置:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs。
KnownDLLs注册表项下包含一系列常见的系统dll,如usp10.dll、lpk.dll、shell32.dll、user32.dll

如果创建注册表项
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\ExcludeFromKnownDlls
并指定具体dll名称,可以使KnownDLLs列表中同名的dll保护失效,修改后需要重启才能生效。

产生原因

程序或者服务尝试加载系统中不存在的DLL

NOTFOUND

那么怎么知道哪些不存在的DLL是程序或者服务会去尝试加载的呢?这里就必须要推荐一下国外大牛的开源工具–DLLSPY,利用该工具的静态检测方法就可以找到遗弃DLL,该工具用法十分简单,github上写得很清楚。

利用方法

由于程序或者服务会根据名称在系统中搜索指定DLL,那么利用方式十分简单,直接将恶意DLL文件修改为程序搜索或者服务尝试加载的DLL名称,然后将该恶意DLL放在任意的DLL搜索目录下即可。可以使用cobaltstrike生成的becaon.dll进行测试,当程序或者服务加载该DLL后即可得到Beacon(需关闭Defender)。

函数转发的DLL劫持方式

这种方式已经是很传统的方法了,说存在了十几年也不是太夸张的说法。这还可以理解成一种MITM Attack(中间人攻击)。

产生原因

程序没有对加载的DLL进行校验。

利用方法

先介绍怎么用DLLHijcker完成这件事
1、下载脚本安装了相关的依赖后,运行该脚本,参数为要劫持的目标DLL文件,然后会在当前目录下生成一个VS2019的项目。
2、打开该项目后修改Hijack函数的函数体,可以只是修改shellcode,也可以修改加载shellcode的方式(该加载方式来自于MSF,特征明显,无法绕过Defender),修改后编译该DLL文件。
3、将原始DLL文件移动到搜索顺序优先级低的文件夹底下。
4、将恶意DLL移动至目标DLL的文件下。

再介绍一下手工的利用方法,利用IDA查看DLL的导出表,记录每个导出函数的函数名以及参数类型,然后自己再新建一个DLL项目,编写相关的代码。
注意x86与x64的函数转发实现方式是不同的。手工的方式就不仔细叙述了,感兴趣的话可以利用DLLHijacker生成x86与x64的DLL文件,自己理解一下相关代码(其实代码逻辑很简单),有不懂的可以去看雪查看相关资料。

篡改正常DLL的劫持方式

这种方式最好的利用方式就是使用The Backdoor Factory(BDF),该工具最初于2014年发布,最后一次更新是在2017年。至于为什么最好等利用方式是这个呢,原因是我自己目前还没有花时间来完整手工复现一次…,这里立一个Flag,等以后手工复现完后再来更新这一小节。

产生原因

程序没有对加载的DLL进行校验。

利用方法

1、下载BDF,用-f指定要篡改的目标DLL,-s指定SHELLCODE。
2、将目标DLL修改为.bak后缀(保险措施)。
3、将篡改后的DLL文件移动至目标DLL目录下。

SUMMARY

其实还有另一种方法可以做DLL劫持,即在恶意DLL文件中篡改程序使用LoadLibrary的句柄返回,将原先要返回恶意DLL的句柄修改为返回原始DLL的句柄,这种方式是通过修改LDR_DATA_TABLE_ENTRY结构体中的BaseAddress来实现的,具体可以看一下看雪的帖子。我在实际复现的时候也遇到了很多问题,最后的解决方法也蛮奇怪的,将vs2017升级成vs2019后编译即可解决加载的程序一直崩溃的问题。等以后有时间的时候我再深入研究一下,该方法较传统的劫持是否存在什么优势。

另外,在做相关的分析研究的时候,踩了不少坑。
比如x86与x64的函数转发方式是不相同的,一开始没找到x64该怎么做劫持,后来问来问去总算是理解了其中的原因并找到可用的方法。然后在加载shellcode的时候又出现各种各样导致程序崩溃的bug,厚着脸请教大牛并用windbg一步一步调试,最后总算是解决了问题(虽然加载方式不能免杀)。
又比如利用函数转发的方式劫持某个程序的DLL时,发现该程序在加载了恶意DLL后直接崩溃,利用Process Monitor 定位问题,发现是程序不会再次搜索该DLL所在的目录(普通用户权限可修改的目录),只会按照系统搜索路径来搜索DLL,即使我在恶意DLL中用绝对路径去加载正常的DLL也不行,于是只能将原始DLL放到系统搜索目录下(这一步需要管理员权限)。

本来想把这些背后的工作和弯路也好好地写出来,可是发现这些不擅长的东西现阶段我还没能力好好地描述清晰,希望以后自己能力提高后能输出更加清晰明了的技术文章。


1、要理解程序流程和操作系统原理,利用VS的调试功能,对DLL进行单步调试,结合内存信息来确认情况。目前我薄弱的地方就是对进程在内存中的信息理解不深刻。
2、因为加载shellcode的方式是用createProcess创建另一个进程,然后将shellcode写入该进程中,因此用vs是无法得知shellcode是否能正常调用的。此时要结合windbg来使用,创建进程后,记录进程号,使用windbg附加到该进程上即可对shellcode在内存中的情况进行分析。
3、虽然我对程序流程和操作系统原理有一定的认识,但是深入到底层细节则是知识盲区,且平时写的代码很少涉及到系统底层,像shellcode该如何加载、进程该如何注入等方法很多都是复制现有的。

感谢以下的大牛们,让我收获了很多知识:
https://www.cyberark.com/threat-research-blog/dllspy-tighten-your-defense-by-discovering-dll-hijacking-easily/
https://3gstudent.github.io/3gstudent.github.io/%E5%88%A9%E7%94%A8BDF%E5%90%91DLL%E6%96%87%E4%BB%B6%E6%A4%8D%E5%85%A5%E5%90%8E%E9%97%A8/
https://github.com/InoriJam/DLL-hijack-X64
https://github.com/anhkgg/SuperDllHijack