Mastering Malware Analysis Chapter 5

Inspecting Process Injection and API Hooking

我们将深入研究各种进程注入技术,包括DLL注入和傀儡进程(Stuxnet引入的一种先进技术),并解释如何处理它们。然后将学习API hook, IAT hook和其他的hook技术

您将学习如何分析其他进程中注入的代码,通过内存取证检测它,检测不同类型的API钩子技术,并分析它们以检测**Man-in-the-Browser(MiTB)**攻击

为了使学习无缝衔接,这个章节被划分为下面几个主要的部分

  1. 理解进程注入
  2. DLL注入
  3. 深挖进程注入
  4. 代码注入的动态分析
  5. 针对进程注入的内存取证技术
  6. 理解API hook
  7. 探索IAT hook

理解进程注入

什么是进程注入?

进程注入是一组技术,允许您将代码块或整个动态链接库(dll)注入到另一个进程的内存中,并执行该代码。

在Windows 7及以上版本中,不允许向核心Windows进程(如explorer.exe)或其他用户的进程中执行注入。然而,它仍然将代码注入当前用户的浏览器和其他进程。

这种技术合法的使用在各种终端安全产品中来检测应用程序和为了沙箱的目的(在 Understanding API hooking ​这一节中将会看到),但是这种技术也被恶意软件作者滥用。

为什么进程注入?

对于恶意软件作者来说,进程注入的好处

  • 绕过简单的防火墙(阻止除了浏览器或其他允许的应用程序之外的所有应用程序连接互联网),通过注入代码,malware就能与C&C服务器进行通信
  • 通过在另一个未监控和未调试的进程中运行恶意代码来逃避调试器和其他动态分析或监视工具
  • 在被注入代码的进程中Hook APIs,可以对受害者进程的行为提供唯一的控制。
  • 保持持久性。通过将代码注入到后台进程,恶意软件可以在很少重启的服务器上保持持久性,而且不会在硬盘上留下文件。

DLL注入

Windows-supported DLL注入

Windows提供了特殊的注册表项,以便在满足特定条件的每个进程中加载DLL,比如

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs

该注册表项是恶意软件最常滥用的注册表项之一,用于将DLL代码注入其他进程并保持持久性。这里指定的库与加载user32.dll(主要用于UI的系统库)的每个进程一起加载。其实就是user32.dll会加载这个AppInit_DLLs中指定的DLL

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls

此注册表项中列出的库被加载到至少使用以下函数之一的每个进程中

  • CreateProcess
  • CreateProcessAsUser
  • CreateProcessWithLogonW
  • CreateProcessWithTokenW
  • WinExec

HKEY_CURRENT_USER\Software\Classes<AppName>\shellex\ContextMenuHandlers

这个路径加载一个shell扩展(一个DLL文件),以便向主Windows shell (explorer.exe)添加额外的功能。基本上,它可以被误用来加载恶意软件库作为explorer.exe的扩展。可以轻松地创建和修改此路径,无需任何管理特权。

原来的

新建的

https://www.4hou.com/posts/M19m

HKEY_CLASSES_ROOT树是HKEY_LOCAL_MACHINE和HKEY_CURRENT_USER中的注册表信息的合集。在执行合并时,Windows会给出HKCU树中的优先级。

也就是说,如果有key存在HKCU中,其优先级就高于HKLM中的相同key,也会最终合并到HKEY_CLASSES_ROOT树中。关于合并的更多说明可参见https://docs.microsoft.com/en-us/windows/desktop/sysinfo/hkey-classes-root-key

默认情况下,explorer启动时会从HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID{90AA3A4E-1CBA-4233-B8BB-535773D48449}\InProcServer32 key中加载Shell.dll。为了将恶意DLL加载到explorer.exe中,研究人员创建了一个HKCU\Software\Classes\CLSID{90AA3A4E-1CBA-4233-B8BB-535773D48449}\InProcServer32 key,然后将其默认值修改为恶意DLL。

果然重启explorer.exe后他会加载这个testDll.dll,并且杀毒软件并没有报毒,有点恐怖

一个简单的DLL注入技术

  1. 找到目标进程
  2. OpenProcess获得进程句柄
  3. 使用VirtualAllocEx,VirtualAllocExNuma, NtAllocateVirtualMemory或类似的函数分配一段空间用来要注入的DLL的路径,另外一个选项是CreateFileMapping -> MapViewOfFile or CreateSectionEx -> NtCreateSection的方式分配空间
  4. 使用WriteProcessMemory等api将恶意程序DLL的路径写入进程,NtWow64WriteVirtualMemory64,或者借助NtMapViewOfSection。
  5. 使用诸如 CreateRemoteThread / NtCreateThreadEx、SuspendThread -> SetThreadContext -> ResumeThread、QueueUserAPC /NtQueueApcThread 或者甚SetWindowHookEx 等 API 加载并执行这个 DLL,将 LoadLibraryA 的地址作为线程起始地址,将 DLL 路径的地址作为参数。

深挖进程注入

在这一节,我们将覆盖这个中级到高级的进程注入技术,这些技术不会在磁盘上留下任何痕迹,并且可以使无文件恶意软件保持持久性。

发现目标进程

利用进程快照(CreateToolhelp32Snapshot)的方式,然后去遍历所有的进程, 比如下面这一段是根据NAME得到PID的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DWORD GetPid(char * szName)
{
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapShot == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot ERROR CODE: %d\n", GetLastError());
return -1;
}
if (Process32First(hSnapShot, &pe32)) //遍历所有的进程
{
do
{
if (!strcmp(szName, pe32.szExeFile)) //如果名字相同,即说明找到了,则返回进程的ID
return pe32.th32ProcessID;
} while (Process32Next(hSnapShot, &pe32));
}
CloseHandle(hSnapShot);
return -1; //如果没有找到则返回-1
}

代码块注入

  1. 找到目标进程
  2. 在目标进程中准备内存
  3. 复制代码(ShellCode)到目标内存中
  4. 执行CreateRemoteThreadFunc

跟DLL注入非常的类似,最困难的工作是这个ShellCode的编写(位置独立,PE独立,就是不依赖于环境,在任何地方都能执行的代码)

(第8章,Handling Exploits and Shellcode

反射性DLL注入

反射式dll注入技术的优势在于可以使得恶意的dll通过 socket 等方式直接传输到目标进程内存并加载,期间无任何文件落地,安全产品的检测难度大大增加

Reflective loader 实现思路如下:

  1. 获得被注入进程未解析的 dll 的基地址,即下图第7步所指的 dll。
  2. 获得必要的 dll 句柄和函数为修复导入表做准备。
  3. 分配一块新内存去取解析 dll,并把 pe 头复制到新内存中和将各节复制到新内存中。
  4. 修复导入表和重定向表。
  5. 执行 DllMain() 函数。

https://mp.weixin.qq.com/s?__biz=Mzg2NjgzNjA5NQ==&mid=2247514915&idx=1&sn=7924af701a34f8088d56bf6a0a8dd4d5&source=41#wechat_redirect

更多细节可以在后面的进程注入内存取证技术一节中找到。

Stuxnet 秘密技术-Process Hollowing

其实就是傀儡进程,或者叫映像劫持,映像替换啥的。

这种机制完全的将Malware伪装成合法的程序,因为PEB和等效的EPROCESS仍然包含合法进程的信息,这有助于恶意软件绕过防火墙和内存取证工具

(这里MiniLCTF2022和2023出题都是用的这种技术。具体细节就不写了。。

动态分析代码注入

Debug it where it is

假设进程A要注入ShellCode到进程B中,那么我们就阻止注入,直接在进程A中调试ShellCode, 把EIP指到ShellCode的入口位置即可。

这种方法只能调试简单的。

附加到目标进程

在Malware执行CreateRemoteThread之前,附加到目标进程,或者将CreateRemoteThread的参数改为CREATE_SUSPEND

1
CreateRemoteThread(Process, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibrary, (LPVOID)Memory, CREATE_SUSPENDED, NULL);

处理Process Hollowing

对付这种,就是想办法找到要换的PE文件,可以在一些函数下断点

  • WriteProcessMemory
  • CreateSection,MapViewOfSection

通过这样,把要Load的PE单独dump到硬盘上,然后单独运行即可。

有一些特征来识别PE文件,比如MZ头,This program cannot run in DOS mode 字符串等等

针对进程注入的内存取证技术

在本节中,我们将了解用于检测不同类型的进程注入的不同技术。将使用Volatility来进行。

检测代码注入和反射性DLL注入

对于一个进程的内存,分为3种类型,MAP, IMAGE, PRIVATE

  • MAP就是共享的内存
  • IMAGE就是一个可执行文件的内存映射
  • PRIVATE就是VirtualAlloc申请的这种

进程虚拟地址空间的页面可能处于以下状态之一。

  • Free 进程对这个页面没有权限访问。
  • Reserved 这个页面被保留以便未来使用,它没有映射到真实的内存上,但是任何其他内存分配调用都不会占用这个空间,直到它被释放。
  • Committed 这个页面已经分配到真实的内存当中。

检测代码注入和反射性DLL注入的原理:检测以下几种类型的内存情况,因为并不常见

  1. 有EXECUTE权限的PRIVATE内存
  2. 是READ_WRITE_EXECUTE类型的内存

用Volatility,有一个malfind​的命令,可以检测代码注入(回头把Volatility学好了,这个图改为自己试验的图)

上面这张图是检测到了注入的PE文件在Adobe Reader process中,地址是0X3D0000,通过vaddump​命令, 在我们可以dump这个进程的所有内存。

该命令使用所谓的虚拟地址描述符(virtual Address descriptor, vad)转储进程内的所有内存区域,紧跟该进程的EPROCESS内核对象及其虚拟内存映射(及其等效物理内存页),虚拟地址描述符只是虚拟内存与其等效物理内存之间的映射器。Vaddump​会将所有的内存区域转储到一个单独的文件中。如下图所示

对于注入的PE文件,我们可以使用dlldump​而不是vaddump​将它们转储到磁盘(并重新构造它们的头和节,但不包含导入表),如下面的截图所示:

检测Process Hollowing

当这个恶意程序hollow out这个PE image的时候,Windows去除了内存空间和这个PE程序的何联系,所以,在这个地址处的任何分配变为PRIVATE,而且不代表任何IMAGE

这个关系的去除是在EPROCESS结构中的去除而不是PEB的去除

在Volatility中,有2个命令可以列举加载的模块

  • dlllist 从PEB结构体中列举(user mode)(这里其实就是检测那3个链InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList)
  • ldrmodules 从EPROCESS结构体中列举(kernel mode)(这里是检测的VAD,检测Mapped File)

http://akovid.blogspot.com/2014/04/difference-among-dlllistldrmodulesand.html

这两个命令之间的任何结果不匹配都可能表示Hollow Process

有多个不同类型的不匹配,他们代表不同类型的Process Hollowing

  • 当应用程序模块未链接到其PE文件时,如图5.14所示,它表示该进程已被挖空,并且恶意软件已加载在同一位置。
  • 当应用程序模块出现在dlllist结果中,但在ldrmodules结果中根本没有出现时,表示该进程已被挖空,并且恶意软件可能加载在另一个地址。malfind命令可以帮助我们找到新地址或使用vaddump转储该进程中的所有内存区域,并扫描它们以查找PE文件(搜索MZ魔数)。
  • 当应用程序出现在两个命令的结果中,并与应用程序的PE文件名链接,但两个结果中模块地址不匹配时,表示应用程序并未被挖空,但恶意软件已被注入,并且PEB信息已被篡改以链接到恶意软件而不是合法应用程序PE镜像。

(上面的第二点和第三点不是很懂, 之后弄清楚了回来补)

使用HollowFind 插件来检测ProcessHollowing

有一个名为HollowFind的插件可以组合所有这些命令。它会发现可疑的内存空间或进程被Hollowed-Out的证据,并返回这些结果,如下面的屏幕截图所示

理解API HOOKing

API hooking是恶意软件作者常用的技术,它截取Windows API调用以改变命令的输入或输出。它基于我们之前介绍的进程注入技术。

这种技术允许恶意软件作者完全控制目标进程,从而控制用户与该进程的交互体验,包括浏览器和网页、防病毒应用程序及其扫描文件等。通过控制Windows API,恶意软件作者还可以从进程内存和API参数中捕获敏感信息。由于API hooking被恶意软件作者广泛使用,因此它被合法地用于恶意软件沙箱和旧应用程序的向后兼容性等不同的合法用途。因此,Windows官方支持API hooking,正如我们将在本章后面看到的那样。

Why API hooking?

有很多原因恶意软件将使用API hooking

  • 隐藏malware存在(rootkits):为了让恶意软件在用户和反病毒扫描程序面前隐藏它的存在,它可能会钩住以下api:

    • Process listing APIs Process32First, Process32Next
    • File listing APIs FindFirstFileA and FindNextFileA
    • Registry 枚举APIs 例如 RegQueryInfoKey and RegEnumKeyEx
  • Stealing banking details (banking Trojans): 捕获HTTP messages, 注入代码在主页面,抓取发送的username和pin codes,它通常hook下面这些APIs

    • wininet.dll: InternetConnectA, HttpSendRequestA, InternetReadFile ws2_32.dll:WSARecv, WSASend
    • Firefox APIs such as PR_Read, PR_Write, and PR_Close.
  • 其他使用: HOOK CreateProcessA, CreateProcessAsUserA, and similar APIs植入子进程或者阻止启动进程

使用API HOOK

Inline API hooking

更改参数或者直接更改返回值(直接把eax置0,然后ret)

Inline API hooking with a 跳板

跳板的内容

这样的话,在HOOK函数里call跳板,然后到了API函数内部, 在API函数内部最后返回的时候ret, 就又到了HOOK函数了

这一增加的步骤使恶意软件对API及其输出有了更多的控制,例如,可以将JavaScript代码注入到InternetReadFile、PR_Read,或者API用来窃取凭据或将资金转移到不同的银行账户。

Inline API hooking with a 长度反汇编器

因为不确定开头有几个字节,可能会导致jmp回来的时候跳转到了指令的中间去了,这样就会导致错误,所以需要一个轻量级的长度反汇编器来记录开头的几个字节。

检测API HOOK 用内存取证

使用apihooks​命令,这个命令扫描进程库,搜索Hooked APIs(以Jmp开头或者Call开头的这种)

可以使用vaddump​dump下来,然后用IDA去分析

探索IAT HOOK

修改IAT表中的函数地址。