Windows十种注入方式

2021/12/26

Kkem Chen

进程注入是一种广泛应用于恶意软件和无文件攻击中的逃避技术,这需要在另一个进程的地址空间内运行自定义代码。进程注入提高了隐蔽性,一些技术也实现了持久性。 尽管有许多流程注入技术,在本博客中,我提供了十种在现实看到注入另一个进程运行恶意代码的技术。 我还提供了许多这些技术的屏幕截图,以便于逆向工程和恶意软件分析,协助针对这些常见技术进行检测和防御。

1. 经典的DLL注入方式

经典的DLL注入方式:通过CREATEREMOTETHREAD和LOADLIBRARY进行注入。

这种技术是用于将恶意软件注入另一个进程的最常用技术之一。 恶意软件将路径写入到其他进程的虚拟地址空间中的恶意动态链接库(DLL),并通过在目标进程中创建远程线程来确保远程进程加载它。

图1

恶意软件首先需要定位注入的进程(例如svchost.exe)。这通常通过调用三个应用程序接口(API)来搜索进程:CreateToolhelp32Snapshot,Process32First和Process32Next。 CreateToolhelp32Snapshot是用于枚举指定进程或所有进程的堆或模块状态的API,它返回一个快照。 Process32First检索快照中有关第一个进程的信息,然后在循环中使Process32Next来遍历它们。 找到目标进程后,恶意软件通过调用OpenProcess获取目标进程的句柄。

如图2所示,恶意软件调用VirtualAllocEx有一段空间来写入其DLL的路径。 恶意软件然后调用WriteProcessMemory写入分配的内存中的路径。 最后,为了让代码在另一个进程中执行,恶意软件调用API,如CreateRemoteThread,NtCreateThreadEx或RtlCreateUserThread,后两者是为文档化的函数。

然而,一般的想法是将LoadLibrary的地址传递给这些API之一,以便远程进程必须代表恶意软件执行DLL。CreateRemoteThread被许多安全产品跟踪和标记。 此外,它会在磁盘上的留下恶意DLL文件。考虑到攻击者注入代码的目的一般是防御,所以复杂的攻击者可能不会使用这种方法。 下面的截图显示了一个名为Rebhip的恶意软件便是利用了这种注入技巧。

图 2 Rebhip 蠕虫使用了经典的DLL注入方式


2. PE文件注入

恶意软件不会传递LoadLibrary的地址,而是将其恶意代码复制到现有的打开进程中,并使其执行(通过一个小的shellcode或通过调用CreateRemoteThread)。PE注入相比于LoadLibrary进行注入的一个优点是恶意软件不必在磁盘上释放恶意DLL。类似于第一种注入技术,恶意软件在主机进程(例如VirtualAllocEx)中分配内存,而不是写入“DLL路径”,它通过调用WriteProcessMemory写入其恶意代码。然而,使用这种方法的缺陷是更改了复制映像的基址。当恶意软件将其PE注入另一个进程时,它将具有不可预测的新基址,所以要求它动态地重新计算其PE的固定地址。为了克服这一点,恶意软件需要在主机进程中找到其重定位表地址,并通过循环遍历其重定位描述符来解析复制的映像的绝对地址。

图3

这种技术类似于其他技术,如反射式DLL注入和内存模块加载,因为它们不会将任何文件释放到磁盘。然而,内存模块加载和反射式DLL注入方法甚至更加隐蔽。它们不依赖任何额外的Windows API(例如,CreateRemoteThread或LoadLibrary),因为它们在内存中加载并执行自身。反射式DLL注入通过创建一个DLL,在执行时将自身映射到内存中,而不依赖于Window的加载器。内存模块加载类似于反射式DLL注入,注射器或加载器负责将目标DLL映射到内存而不是DLL映射本身。在之前的一篇博文中,这两种内存方法被广泛讨论。

在分析PE注入时,在调用CreateRemoteThread之前,看到循环(通常是两个“for”循环,一个嵌套在另一个循环中)是很常见的情况。这种技术在crypter(加密和模糊恶意软件的软件)中非常受欢迎。在图4中,样本使用的便是这种技术。该代码有两个嵌套循环来调整其重定位表,可以在调用WriteProcessMemory和CreateRemoteThread之前看到它。 “and 0x0fff”指令也是另一个很好的标志,显示前12位用于获取到包含重定位块的虚拟地址的偏移量。现在,恶意软件已经重新计算了所有必需的地址,所有它需要做的是将其起始地址传递给CreateRemoteThread并将其执行。

图4 在调用CreateRemoteThread之前,PE注入的循环结构示例


3. 进程HOLLOWING

进程HOLLOWING,又名进程替换和RUNPE。恶意软件可以执行被称为进程hollowing的技术,而不是将代码注入宿主程序(例如,DLL注入)。进程hollowing 发生于恶意软件从目标进程的内存中清空(镂空)合法代码并用恶意可执行文件覆盖目标进程的内存空间(如,svchost.exe)之时。

图5

恶意软件首先创建一个新进程,以挂起模式托管恶意代码。如图6所示,该程序通过调用CreateProcess并将流程创建标志设置为CREATE_SUSPENDED(0x00000004)完成。新进程的主线程被创建为挂起状态,直到ResumeThread函数被调用才会运行。接下来,恶意软件需要用恶意的有效载荷来替换合法文件的内容。这可以通过调用ZwUnmapViewOfSection或NtUnmapViewOfSection来取消映射目标进程的内存。这两个API基本上释放了一个部分指向的所有内存。现在内存被取消映射,加载器执行VirtualAllocEx为恶意软件分配新内存,并使用WriteProcessMemory将每个恶意软件的部分写入目标进程空间。恶意软件调用SetThreadContext将entrypoint指向已编写的新代码段。最后,恶意软件通过调用ResumeThread来恢复挂起的线程。

图6 Ransom.Cryak实现进程hollowing


4. 线程执行劫持

线程执行劫持,又名:挂起、注入、恢复 (SIR)。这种技术与先前讨论的进程hollowing技术有一些相似之处。在线程执行劫持中,恶意软件针对进程的现有线程,并避免任何嘈杂的进程或线程的创建操作。因此,在分析期间,您可能会看到对CreateToolhelp32Snapshot和Thread32First的调用,后跟OpenThread。

图7

在获取目标线程的句柄后,恶意软件通过调用SuspendThread来将线程置于挂起模式,最终执行注入。恶意软件调用VirtualAllocEx和WriteProcessMemory来分配内存并执行代码注入的操作。 该代码可以包含shellcode,恶意DLL的路径和LoadLibrary的地址。

图8 给出了使用这种技术的通用木马程序。 为了劫持线程的执行,恶意软件通过调用SetThreadContext修改目标线程的EIP寄存器(包含下一条指令的地址)。之后恶意软件恢复线程来执行它已写入主机进程的shellcode。 从攻击者的角度来看,SIR方法可能是有问题的,因为在系统调用中暂停和恢复线程可能导致系统崩溃。为避免这种情况,更加复杂的恶意软件如果遇到EIP寄存器在NTDLL.dll的范围内的情况,将会恢复然后重试。

图 8 一般的木马正在执行线程执行劫持


5. 通过SETWINDOWSHOOKEX进行HOOK注入

Hooking是一种用于拦截函数调用的技术。恶意软件可以利用挂钩函数,在特定线程触发事件时加载其恶意DLL。这项技术通常通过调SetWindowsHookEx函数来将钩子例程安装到钩子链中来完成。 SetWindowsHookEx函数有四个参数。第一个参数是事件的类型。这些事件反映了钩子类型的范围,并且从键盘上的键(WH_KEYBOARD)到输入到鼠标(WH_MOUSE),CBT等的不同。第二个参数是指向恶意软件想要在事件中调用的函数的指针执行。第三个参数是包含该函数的模块。因此,在调用SetWindowsHookEx之前,看到对LoadLibrary和GetProcAddress的调用是非常常见的。该函数的最后一个参数是挂钩过程与之关联的线程。如果此值设置为零,则所有线程在触发事件时执行操作。然而,恶意软件通常针对一个线程来减少干扰,因此也可以在SetWindowsHookEx之前查看调用CreateToolhelp32Snapshot和Thread32Next来查找和定位单个线程。一旦DLL被注入,恶意软件代表其线程id被传递给SetWindowsHookEx函数的进程执行其恶意代码。在图9中,Locky Ransomware实现了这种技术。

图 9 Locky Ransomware 的hook注入过程


6. 通过修改注册表进行注入以及维持注入

通过修改注册表进行注入以及维持注入 (例如APPINIT_DLLS, APPCERTDLLS, IFEO)。恶意软件可以使用Appinit_DLL, AppCertDlls, 以及 IFEO (映像劫持)这三个注册表项可以用于注入和维持注入,这三个注册表项具体的位置如下:

AppInit_DLLs

恶意软件可以将其恶意dll文件的位置插入到Appinit_Dlls注册表项下,以使其他进程加载这个dll文件。 此注册表项下的每个dll文件都会随着User32.dll的加载而同样加载到进程中。 User32.dll是用于存储图形元素(如对话框)的非常常用的库。 因此,当恶意软件修改这个注册表子项时,大多数进程都将加载恶意dll文件。 图10显示了木乃伊依赖这种技术进行注入和维持注入的方法。 它只需打开Appinit_Dlls注册表项,方法是调用RegCreateKeyEx,并通过调用RegSetValueEx来修改它的值。

图 10 Ginwui修改AppIniti_DLLs注册表项

AppCertDlls

这种方法与AppInit_DLLs方法非常相似,只是将此注册表项下的DLL加载到调用CreateProcess,CreateProcessAsUser,CreateProcessWithLogonW,CreateProcessWithTokenW和WinExec的每个进程中。

映像劫持 (IFEO)

IFEO通常用于调试目的。开发人员可以在此注册表项下设置“调试器值”,将程序附加到另一个可执行文件进行调试。 因此,每当启动可执行文件时,将附加到该程序。 要使用此功能,您可以简单地给出调试器的路径,并将其附加到要分析的可执行文件。 恶意软件可以修改此注册表项以将其注入到目标可执行文件中。 在图11中,Diztakun木马通过修改任务管理器的调试器值来实现此技术。

图11 : Diztakun木马修改IFEO注册表项


7. APC注入以及AtomBombing 内存注入

恶意软件可以利用异步过程调用(APC)控制另一个线程通过将其附加到目标线程的APC队列来执行其自定义代码。 每个线程都有一个APC队列,它们在目标线程进入可警醒状态时等待执行。如果调用SleepEx,SignalObjectAndWait,MsgWaitForMultipleObjectsEx,WaitForMultipleObjectsEx或WaitForSingleObjectEx函数,线程将进入可警醒状态。 恶意软件通常会查找处于可警醒状态的任何线程,然后调用OpenThread和QueueUserAPC将APC排队到线程。QueueUserAPC有三个参数:1)目标线程的句柄; 2)指向恶意软件想要运行的函数指针; 3)和传递给函数指针的参数。 在图12中,Amanahe恶意软件首先调用OpenThread来获取另一个线程的句柄,然后使用LoadLibraryA调用QueueUserAPC作为函数指针,将其恶意DLL注入另一个线程的APC队列中。

AtomBombing是一种首先由enSilo研究引入的技术,然后用于Dridex V4。 正如我们在前一篇文章中详细讨论的[译者注:链接见文末],该技术也是依赖APC注射。 但是,它是使用atom表写入另一个进程的内存。

图12: Almanahe执行APC注入


8. 通过SETWINDOWLONG进行窗口内存注入(EWMI)

EWMI依靠注入资源管理器托盘窗口的额外窗口内存,并在恶意软件家族中被多次使用使用,如Gapz和PowerLoader。在注册窗口类时,应用程序可以指定一些额外的内存字节,称为额外的窗口存储器(EWM)。 然而,EWM并不算是块很充裕的空间。 为了规避这个限制,恶意软件将代码写入explorer.exe的共享部分,并使用SetWindowLong和SendNotifyMessage来指定一个指向shellcode的函数指针,然后执行它。

图13 PowerLoader注入托盘窗口的额外窗口内存

当涉及到向共享部分的写入数据时,恶意软件有两个选择:它可以也创建一个共享空间,并将其映射到自身和另一个进程(例如,explorer.exe);第二个选择就是简单地打开已经存在的共享部分。 前者具有分配堆空间和调用NTMapViewOfSection以及其他一些API调用的开销,因此后一种方法被更频繁地使用。 恶意软件在共享部分中写入其shellcode后,使用GetWindowLong和SetWindowLong访问并修改“Shell_TrayWnd”的额外窗口内存。 GetWindowLong是用于将指定偏移量的32位值检索到窗口类对象的额外窗口存储器中的API,SetWindowLong用于更改指定偏移量的值。 通过这样做,恶意软件可以简单地更改窗口类中的函数指针的偏移量,并将其指向写入共享部分的shellcode。

像上面提到的大多数其他技术一样,恶意软件需要触发它编写的代码。 在以前讨论的技术中,恶意软件通过调用API(如CreateRemoteThread,QueueUserAPC或SetThreadContext)来实现这一点。 在EWMI方法中,恶意软件通过调用SendNotifyMessage触发注入的代码。 在执行SendNotifyMessage之后,Shell_TrayWnd接收并将控件传递给由SetWindowLong先前设置的值指向的地址。 在图13中,名为PowerLoader的恶意软件使用这种技术。


9. SHIMS注入

Microsoft向开发人员提供了Shims[译者注:Shim是一个工程术语,描述为了让两个物体更好地组装在一起而插入的一块木头或金属。在计算机编程中,shim是一个小型的函数库,用于透明地拦截API调用,修改传递的参数、自身处理操作、或把操作重定向到其他地方。Shim也可以用来在不同的软件平台上运行程序。],主要是为了向后兼容。 Shims允许开发人员将修补程序应用于程序,而无需重写代码。 通过利用Shims,开发人员可以告诉操作系统如何处理其应用程序。 Shims本质上是一种嵌入API并针对特定可执行文件的方式。 恶意软件可以利用Shims来实现注入可执行文件并维持注入。 Windows运行Shim引擎时,它加载二进制文件以检查shimming数据库,以便应用适当的修补程序。

有许多可以使用的修复程序,但是恶意软件还是更偏爱那些安全相关的(例如DisableNX,DisableSEH,InjectDLL等)。要安装shimming数据库,恶意软件可以使用各种方法。 例如,一个常见的方法是简单执行sdbinst.exe,并将其指向恶意的sdb文件。 在图14中,广告软件“Search Protect by Conduit”使用Shims进行注入和维持。 它在Google Chrome中执行“InjectDLL”shim以加载vc32loader.dll。 现在有一些用于分析sdb文件的工具,但是对于下面列出的sdb的分析,我使用了python-sdb,而没有使用现成的工具。

图14 Search Protect用于注入的SDB


10. IAT HOOKING和INLINE HOOKING

(又名应用层ROOTKITS)

IAT hooking 以及 inline hooking通常被称为用户级rootkit,恶意软件可以利用IAT HOOKING技术更改导入地址表。 当合法应用程序调用位于DLL中的API时,将执行替换的函数,而不是原始代码。 相比之下,使用Inline Hooking,恶意软件可以修改API函数本身。 在图15中,恶意软件FinFisher通过修改CreateWindowEx指向的位置执行IAT HOOKING。

图 15 FinFisher 通过更改CreateWindowEx指向的位置实现IAT HOOKING


总结

本文中,介绍了恶意软件在另一个进程中隐藏其活动的十种不同技术。 通常,恶意软件直接将其shellcode注入到另一个进程中,或者强制另一个进程加载其恶意DLL。 在表1中,我对各种技术进行了分类,并提供了样品,作为观察本文所涵盖的每种注射技术的参考。 整篇文章中的数字将有助于研究人员在反转恶意软件时识别各种技术。

表格1: 流程注入可以通过直接将代码注入到另一个进程中,或通过强制将DLL加载到另一个进程来完成

攻击者和研究人员经常发现新的技术来实现注入并提供隐藏自身的目的。这篇文章详细介绍了十种常见和新兴的技术,但还有其他的没有提及,如COMM劫持。 防御者在其使命中将永远不会“完成”检测和防止隐形进程注入的任务,因为入侵者永远不会停止创新。


comments powered by Disqus