进程注入原理——提权会用到本质上就是在进程的内存地址上做一些hack改动


0x00 前言

进程注入是一种广泛应用于恶意软件和无文件攻击中的逃避技术,这意味着可以将自定义代码运行在另一个进程的地址空间内。进程注入提高了隐蔽性,也实现了持久化。尽管有非常多的进程注入技术,但是本文我只列举了10种常见的技术。我还提供了这些技术的相关截图以便逆向和恶意软件的分析,并帮助防御这些技术。

0x01 通过CreateRemoteThread和LoadLibrary的DLL注入

这是进程注入最常见的技术。恶意软件将恶意的动态链接库的路径写入另一个进程的虚拟地址空间内,通过在目标进程中创建远程线程来确保远程进程加载它。

http://p1.qhimg.com/t0140045ec12c9f16f1.png

恶意软件首先需要选择目标进程(例如svchost.exe)。通常使用API:CreateToolhelp32Snapshot, Process32First, 和 Process32Next来完成。CreateToolhelp32Snapshot是个用于枚举指定进程或所有进程的堆或模块的状态,并且它返回一个快照。Process32First得到快照中的第一个进程的信息,然后Process32Next来遍历所有的进程。在找到目标进程后,恶意软件调用OpenProcess得到目标进程的句柄。

如上图所示,恶意软件调用VirtualAllocEx得到写入路径的内存空间。然后调用WriteProcessMemory在分配的内存中写入路径(动态链接库)。最后,调用API(如CreateRemoteThread、NtCreateThreadEx、RtlCreateUserThread)使得另一个进程执行代码。后两个API是未文档化的。然而,通常想法是将LoadLibrary的地址传入这些API中的一个,以便远程进程执行DLL。

CreateRemoteThread被许多安全产品跟踪并标记。而且,它在磁盘上面留下了一个恶意的DLL可供检测。考虑到攻击者注入代码最常见的目的是逃避防御,高明的攻击者不会使用这种方式。下面是名为Rebhip(Sha256: 07b8f25e7b536f5b6f686c12d04edc37e11347c8acd5c53f98a174723078c365)的恶意软件使用了这种技术。

http://p1.qhimg.com/t015953793fe4b1b377.png

0x02 可执行文件注入(PE注入)

恶意软件并不传入LoadLibrary的地址,而是拷贝恶意代码到打开的进程中并执行(通过小段shellcode或者调用CreateRemoteThread)。与LoadLibrary相比,PE注入的优势是恶意软件不需要在磁盘中释放一个恶意的DLL。与上个技术类似,恶意软件在目标进程中分配内存,调用WriteProcessMemory将恶意代码而不是DLL路径写入内存。然而,这种方式的缺陷是被复制的映像的基址的改变。当一个恶意软件注入PE到另一个进程,它的新基址是不可预料的,需要动态重新计算PE的地址。为了完成这个,恶意软件需要找到目标进程的重定位表,并根据它的重定位描述解析被复制映像的绝对地址。

http://p1.qhimg.com/t01f0d9c7071cfb9eba.png

这种技术和其他技术很类似,如反射DLL注入和内存模块加载,因为他们都不释放任何文件到磁盘。但是,内存模块和反射DLL注入更加隐蔽。他们不依赖Windows API(如CreateRemoteThread或LoadLibrary),因为他们在内存中加载并执行自身。反射DLL注入通过在内存中创建一个DLL映射执行,而不依赖Windows的加载器。内存模块加载和反射DLL注入类似,其不同之处只是在于内存模块加载的注入器或加载器负责映射目标DLL到内存中而不是DLL自身映射。在之前的博文中,讨论过这两种技术。

当分析PE注入时,在调用CreateRemoteThread之前通常能看见循环(通常是两个for循环,一个嵌套在另一个中)。这种技术在crypter(加密和混淆软件)中非常流行。下图中的样本(Sha256: ce8d7590182db2e51372a4a04d6a0927a65b2640739f9ec01cfd6c143b1110da)充分利用了这种技术。在调用WriteProcessMemory和CreateRemoteThread之前有两层循环来处理重定位。“and 0x0fff”指令也是一个比较好的表征,它标明了头12位用于得到包含重定位块的虚拟地址的偏移量。现在恶意软件重新计算了所有需要的地址了,只需要将起始地址传入CreateRemoteThread并执行就行了。

http://p9.qhimg.com/t01f85d795b2cdc8564.png

0x03 进程hollow(又名进程替换和RunPE)

恶意软件有一种技术叫进程hollow,而不是注入代码到程序中(如DLL注入)。进程hollow发生在恶意软件unmap目标进程的合法内存代码,并使用恶意的代码覆写目标进程的内存(如svchost.exe)的时候。

http://p3.qhimg.com/t0116deee4505a63f3d.png

恶意软件首先以挂起模式创建一个新进程来容纳恶意代码。如下图(Sha256: eae72d803bf67df22526f50fc7ab84d838efb2865c27aef1a61592b1c520d144),以CREATE_SUSPENDED (0x00000004)为参数调用CreateProcess。新进程的主线程创建后就处于挂起状态,直到调用ResumeThread才会继续执行。接下来,恶意软件需要使用恶意的payload来填充合法文件的内容。调用ZwUnmapViewOfSection或者NtUnmapViewOfSection来unmap目标进程的内存。这两个API将释放section指向的所有内存。内存unmap之后,使用WriteProcessMemory将恶意软件的节写入目标进程。调用SetThreadContext将入口点指向它已写入的新的代码节。最后,调用ResumeThread恢复挂起进程的执行。

http://p3.qhimg.com/t01106bf8827e00947f.png

0x04 线程执行劫持(又名挂起、注入并恢复)

这种技术类似于进程hollow。在线程执行劫持中,恶意软件的目标是进程中已存在的线程,而且没有创建任何进程或线程。因此,在分析期间你可能看见CreateToolhelp32Snapshot和Thread32First、OpenThread。

http://p4.qhimg.com/t01a8f5eee4da871bf3.png

在得到目标线程的句柄后,恶意软件调用SuspendThread将线程挂起。调用VirtualAllocEx和WriteProcessMemory来分配内存并执行代码注入。代码包含shellcode,恶意DLL的路径和LoadLibrary的地址。

下图(Sha256: 787cbc8a6d1bc58ea169e51e1ad029a637f22560660cc129ab8a099a745bd50e)描述了一个普通木马是如何使用这种技术的。为了劫持线程的执行,恶意软件调用SetThreadContext修改目标线程的EIP寄存器(包含下条执行指令的地址的寄存器)。随后,恶意软件恢复线程继续执行它已写入到宿主进程的shellcode。从攻击者的角度看,这种方式是有问题的,因为在系统调用的中途挂起并恢复线程可能引起系统崩溃。为了避免这种情况的发生,更复杂的利用技术是,一旦EIP寄存器在NTDLL.dll中就恢复并重试。

http://p8.qhimg.com/t0141eaa35b05d7bc65.png

0x05 通过SetWindowsHookEx钩子注入

钩子是用于拦截函数调用的一种技术。恶意软件能利用指定线程中事件触发来加载他们的恶意DLL。通常使用SetWindowsHookEx来安装消息钩子。SetWindowsHookEx有4个参数。第一个参数是事件的类型。事件有很多的类型,有键盘按键(WH_KEYBOARD)和鼠标输入(WH_MOUSE)等。第二个参数是个函数指针,指向恶意软件想要处理事件的函数。第3个参数是包含函数的模块。因此,通常可以看见LoadLibrary、GetProcAddress、SetWindowsHookEx。最后一个参数是消息钩子关联的线程。如果值为0,则针对所有线程。然而,只针对某个线程的目标会小很多,因此也可能看见CreateToolhelp32Snapshot和Thread32Next。一旦DLL被注入后,恶意软件将执行恶意代码。下图中,勒索软件Locky(Sha256: 5d6ddb8458ee5ab99f3e7d9a21490ff4e5bc9808e18b9e20b6dc2c5b27927ba1)就使用了这种技术。

http://p6.qhimg.com/t01e2dd4174a11fb5ba.png

0x06 通过注册表修改(如AppInit_DLLs,AppCertDlls,IFEO)

Appinit_DLL, AppCertDlls, IFEO(映像劫持)可以用于注入和持久化。完整的路径如下:

http://p1.qhimg.com/t010bb2abc9d38fe67d.png

AppInit_DLLs

恶意软件能在AppInit_DLLs键下插入恶意的DLL的路径,以便其他进程加载。该键下每个DLL会被加载到所有的加载User32.dll的进程中。User32.dll是常见的Windows基础库。因此,当恶意软件修改这个子键时,大量进程将加载恶意的DLL。下图中,木马Ginwui(Sha256: 9f10ec2786a10971eddc919a5e87a927c652e1655ddbbae72d376856d30fa27c)依赖了这种技术。它通过调用RegCreateKeyEx打开AppInit_DLLs键,并调用RegSetValueEx修改它。

http://p6.qhimg.com/t01ee81af5664384e09.png

AppCertDlls

这种方式类似与AppInit_DLLs,除了该键下的DLL会加载到调用Win32 API CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, CreateProcessWithTokenW, WinExec的进程中。

IFEO

IFEO通常用于调试。开发者能在该键下设置调试器,来附加调试。因此,当可执行文件启动时,附加到它的程序也会启动。为了使用这功能,你能简单的设置调试器的路径,并附加到你想分析的可执行文件上。下图,木马Diztakun(Sha256: f0089056fc6a314713077273c5910f878813fa750f801dfca4ae7e9d7578a148)使用了这种技术,它修改了任务管理器的调试器的值。

http://p6.qhimg.com/t011e2ee5119c6dac5c.png

0x07 APC注入和AtomBombing

恶意软件利用异步过程调用(APC)来强制另一个线程执行附加到APC队列的自定义代码。每个线程都有一个APC队列,当线程进入可变状态(编辑注: 这里疑为英文原文的拼写错误,有两处使用了alterable state一词,而一处使用了alertable state,疑应为alterable state)时,可以被执行。当调用SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx,  WaitForSingleObjectEx时进入可变状态。恶意软件通常查询线程是否处于可变状态,然后调用OpenThread和QueueUserAPC来向线程插入APC。QueueUserAPC有3个参数:1. 目标线程的句柄 2. 恶意软件想要执行的函数指针 3. 传给函数的参数。下图,恶意软件Amanahe(Sha256: f74399cc0be275376dad23151e3d0c2e2a1c966e6db6a695a05ec1a30551c0ad)首先调用了OpenThread来得到另一个线程的句柄,然后调用QueueUserAPC,以LoadLibrary作为函数指针注入恶意DLL。

AtomBombing由enSilo首次提出,然后在Dridex V4中使用。正如之前博文中讨论的,这种技术也依赖APC注入。然而它使用atom表来写入到另一个进程的内存。

http://p9.qhimg.com/t019b085ae98c15f99a.png

0x08 通过SetWindowLong的窗口内存注入(EWMI)

EWMI依赖注入到资源管理器托盘窗口内存中,并在恶意软件家族Gapz和PowerLoader中使用多次。当注册一个窗口类时,应用程序能指定额外的内存字节,称为额外的窗口内存(EWM)。然而,在EWM中没有太多的空间。为了规避这个限制,恶意软件将代码写入explorer.exe的共享段中,并使用SetWindowLong和SendNotifyMessage得到一个指向shellcode的函数指针,然后执行它。

当写入共享段时,恶意软件有两个选项。它能创建一个共享段自己映射到另一个进程(如explorer)中,或者打开一个已存在的共享段。前者有分配堆内存的开销,而且还要调用NtMapViewOfSection等API,因此后者更常用。在恶意软件将shellcode写入共享段后,使用GetWindowLong和SetWindowLong来访问并修改Shell_TrayWnd的额外的窗口内存。GetWindowLong是用于通过32位值作为偏移得到窗口类对象中额外窗口内存,同时使用SetWindowLong能改变指定偏移的值。通过完成这个,恶意软件能改变窗口类中的函数指针,将它指向共享段的shellcode。

和上述的技术一样,恶意软件需要触发写入的代码。之前说,它是通过调用类似CreateRemoteThread,SetThreadContext,QueueUserAPC这些API来实现的。与之前不同的是,这种技术是通过使用SendNotifyMessage来触发代码执行的。

一旦执行SendNotifyMessage,Shell_TrayWnd将接收到并将控制移交给SetWindowLong设置的地址。下图,名为PowerLoader(Sha256: 5e56a3c4d4c304ee6278df0b32afb62bd0dd01e2a9894ad007f4cc5f873ab5cf)的恶意软件使用了这种技术。

http://p1.qhimg.com/t0114751b13bb61774d.png

http://p4.qhimg.com/t01cf9411a4a4456c64.png

0x09 使用Shims注入

微软提供了Shims给开发者,这主要是为了向后兼容。Shims允许开发者不必重写代码就能修复程序。通过利用shims,开发者告诉操作系统如何处理他们的应用程序。Shims本质是Hook API的方式。恶意软件能利用shims实现注入和持久化。当加载二进制时,Windows运行Shim引擎以检查shim数据库,以便使用合适的修复。

有很多修复可以利用,但是恶意软件最喜欢的是一些安全相关的(如DisableNX, DisableSEH, InjectDLL等)。为了安装一个shim数据库,恶意软件部署了多种方式。例如,常见的一种方式是执行sdbinst.exe,并将它指向恶意的sdb文件。如下图,一个广告软件“Search Protect by Conduit”(Sha256: 6d5048baf2c3bba85adc9ac5ffd96b21c9a27d76003c4aa657157978d7437a20),使用shim来实现了持久化和注入。它执行一个“InjectDLL”shim到谷歌chrome中加载vc32loader.dll。有一些现成的工具可以分析sdb文件,下面是我使用python-sdb分析的结果。

http://p3.qhimg.com/t018af4fb21e51760f0.png

0x0A IAT hook和Inline hook(应用层rootkit)

IAT hook和inline hook通常也叫应用层rootkit。IAT hook使用用于改变导入地址表的技术。当合法的程序调用位于DLL中API时,将会执行被替换的API。相反,在inline hook中,恶意程序修改API函数本身。如下图,恶意软件FinFisher(Sha256: f827c92fbe832db3f09f47fe0dcaafd89b40c7064ab90833a1f418f2d1e75e8e),IAT就hook了CreateWindowEx。

http://p9.qhimg.com/t019a0fb8ad70270301.png

0x0B 总结

本文中,我描述了恶意软件用于隐藏自身行为的10种不同的技术。通常,恶意软件直接注入shellcode到另一个进程中或者强制其他进程加载恶意DLL。如下表,我已经将不同的技术进行了分类,并提供了样本,用于查看在本文提到的每个注入技术。这可以帮助研究者用于在逆向时识别各种技术。

http://p4.qhimg.com/t01ccc88d73747ebc98.png

攻击者和研究员一直在研究新的注入和隐蔽的技术。本文介绍了10种常见的技术,但是还有其他的,如COM劫持。防御者任重道远。