![c9613a78c96e941a56a671c43b34a3e6.png](https://i-blog.csdnimg.cn/blog_migrate/a751c459bb53f4d091e0dc07b73aa8cc.png)
反射型DLL注入
DLL 注入
首先,我们需要了解什么是反射型DLL注入。
DLL 注入
就是将 DLL(动态链接库,是一个包含可由多个程序,同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32.dll 执行与对话框有关的常见函数。)放进某个进程的地址空间里,让它成为那个进程的一部分。要实现 DLL 注入,首先需要打开目标进程。
DLL 注入也被很多合法软件广泛的使用,但恶意软件通常采用 DLL 注入在另一进程的存储空间内伪装其操作。DLL 注入是一个过程,该过程使另一个正在运行的进程加载并执行注入者的任何代码。看上去相当的危险,但它也有很多合法用途。例如,没有它,debug 调试器就不能运行。
![9942edc0f36f2b359d24fb669a27124e.png](https://i-blog.csdnimg.cn/blog_migrate/d4975a3ecda2d4a33b29b386abdf504d.jpeg)
实现 DLL 注入可参考:http://blog.opensecurityresearch.com/2013/01/windows-dll-injection-basics.html
反射型 DLL 注入
反射DLL注入用于将DLL加载到进程中,而不必将其放置在主机的文件系统上。
反射 DLL 注入的过程如下:
- 使用 RWX 权限打开目标进程,并为 DLL 分配足够大的内存。
- 将 DLL 复制到分配的内存空间中。
- 计算 DLL 中用于执行反射加载的导出的内存偏移量。
- 使用反射性加载器函数的偏移地址作为入口,调用 CreateRemoteThread(或等效的未公开的 API 函数,如 RtlCreateUserThread)开始在远程进程中执行。
- 反射式加载器功能使用适当的 CPU 寄存器查找目标进程的进程环境块(PEB),并使用该寄存器在内存 kernel32.dll 和任何其他所需库中查找地址。
- 解析的 KERNEL32 出口目录中找到所需的 API 功能,如内存地址 LoadLibraryA,GetProcAddress和VirtualAlloc。
- 然后使用这些函数将 DLL(自身)正确加载到内存中,并调用其入口点 DllMain。
这一过程比较复杂,值得深思。
反射型 DLL 注入: https:// github.com/stephenfewer /ReflectiveDLLInjection
反射型 DLL 注入的假设是,调用 DLL 的入口点足以执行 DLL 的全部功能。但是,往往并非如此。Microsoft 建议开发人员尽量减少在 DllMain 中的工作量,并进行“延迟初始化”,避免加载其他库或创建新线程。然后,主要入口点位于另一个函数中,该函数在 DLL 加载后将单独调用。
有人提出一种更强大的 DLL 注入技术,该技术试图遵循 Microsoft 概述的 DLL 最佳实践。它通过向目标进程动态写入一些引导程序 Shellcode 来实现此目的,该进程将加载 DLL(使用LoadLibraryA),然后查找并调用另一个导出的入口点函数(使用 GetProcAddress)。尽管这是对传统 DLL 注入的重大改进,但不是反射型的。
因此,将这一技术进行改进,来实现更完美的反射型 DLL 注入。
来源: https:// disman.tl/2015/01/30/an -improved-reflective-dll-injection-technique.html
怎样实现反射型 DLL 注入?
其基本过程如下:
- 打开目标进程并分配内存。
- 将 DLL 复制到分配的内存中。
- 将入口点函数名称的哈希值和该函数的所有参数复制到 DLL 之后的内存空间中。
- 复制一些引导程序的 shellcode,该 shellcode 调用带有指向在第3步中复制的数据的指针的修改的反射式加载器。
- 使用 shellcode 开头的地址作为入口点,在目标进程中创建一个远程线程。
需要引导程序 Shellcode 的原因是由于我们能够使用 Stephen Fewer 的原始反射 DLL 注入技术传递给反射加载器的数据量有限。
CreateRemoteThread 具有以下声明:
HANDLE WINAPI CreateRemoteThread(
_In_ HANDLE hProcess,
_In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_ LPDWORD lpThreadId
);
其中 LPTHREAD_START_ROUTINE lpStartAddress 是所创建的线程的执行入口点。该函数的原型为:
typedef DWORD (__stdcall *LPTHREAD_START_ROUTINE) (
[in] LPVOID lpThreadParameter
);
如上,这仅需要一个 LPVOID 参数。反射型 DLL 注入技术将反射型加载器的地址 CreateRemoteThread 作为 lpStartAddress 参数传递,因此它只能传递单个 void 指针。这将有利于反射型加载器仅调用 DllMain,而后者将 void 指针作为其 lpvReserved 参数。
但是,如果我们希望反射型加载器在加载 DLL 之后调用其他导出,则需要为其提供更多信息!我们将通过使用一些引导程序 Shellcode 将其他参数传递给反射型加载器函数来实现。因此反射型加载器函数将声明:
DWORD WINAPI ReflectiveLoader( LPVOID lpParameter, LPVOID lpLibraryAddress, DWORD dwFunctionHash, LPVOID lpUserData, DWORD nUserdataLen );
在这里的 dwFunctionHash 是要调用的导出函数名称的哈希值,lpUserData 是一堆数据(大小为nUserdataLen),当反射加载程序调用导出函数时,我们将其传递给该函数。
下面,我们不仅要在 DLL 的远程进程中分配内存,我们还要包括用于 shellcode(64字节就足够了)和一定的数据空间:
DWORD nBufferSize = dwLength // size of the DLL
+ nUserdataLen
+ 64; // shellcode buffer
// alloc memory (RWX) in the host process for the image...
lpRemoteLibraryBuffer = VirtualAllocEx(hProcess, NULL, nBufferSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!lpRemoteLibraryBuffer)
break;
printf("Allocated memory address in remote process: 0x%pn", lpRemoteLibraryBuffer);
// write the image into the host process...
if (!WriteProcessMemory(hProcess, lpRemoteLibraryBuffer, lpBuffer, dwLength, NULL))
break;
ULONG_PTR uiReflectiveLoaderAddr = (ULONG_PTR)lpRemoteLibraryBuffer + dwReflectiveLoaderOffset;
// write our userdata blob into the host process
ULONG_PTR userdataAddr = (ULONG_PTR)lpRemoteLibraryBuffer + dwLength;
if (!WriteProcessMemory(hProcess, (LPVOID)userdataAddr, lpUserdata, nUserdataLen, NULL))
break;
ULONG_PTR uiShellcodeAddr = userdataAddr + nUserdataLen;
Shellcode 将有两个目标:
- 使用我们的附加参数调用反射型加载器函数
- ExitThread() 进行适当的清理,以便我们可以从调用过程中获取线程的退出代码。
首先,我们获得的内存地址 ExitThread,该内存地址对于每个进程都应该相同(因为大多数(几乎所有)进程都加载了 kernel32.dll)。
HMODULE kernel32 = LoadLibraryA("kernel32.dll");
FARPROC exitthread = GetProcAddress(kernel32, "ExitThread");
接下来,我们将引导程序 Shellcode 写入缓冲区,然后将其写入分配的远程进程内存空间。shellcode 的 x86 版本很简单,因为我们只需要在调用反射加载程序之前以相反的顺序(按照__stdcall 调用约定)将参数推入堆栈即可,x64 shellcode 更为复杂,因为调用约定要求使用寄存器来传递参数。
最后,我们将 shellcode 写入远程进程,并创建一个使用 shellcode 地址作为入口点的远程线程:
// finally, write our shellcode into the host process
if (!WriteProcessMemory(hProcess, (LPVOID)uiShellcodeAddr, bootstrap, i, NULL))
break;
// Make sure our changes are written right away
FlushInstructionCache(hProcess, lpRemoteLibraryBuffer, nBufferSize);
// create a remote thread in the host process to call the ReflectiveLoader!
hThread = CreateRemoteThread(hProcess, NULL, 1024 * 1024, (LPTHREAD_START_ROUTINE)uiShellcodeAddr, lpParameter, (DWORD)NULL, &dwThreadId);
反射型加载器功能本身与 Fewer 的原始版本相比没有太大变化,除了传递给它的附加参数以及随后在 DLL 中调用选择的导出的部分外。加载 DLL 之后,它将调用 DllMain(理想情况下为null),然后调用我们的导出。
下面就介绍今天的主角:
sRDI✨(Shellcode反射DLL注入)
它可以基于 Shellcode 实现反射型 DLL 注入,并且能够将 DLL 转换为独立的 Shellcode。
项目地址:https://github.com/monoxgas/sRDI
如果我们不想接触磁盘,则需要某种注入技术。可以写一个反射型加载的 DLL,但是反射性 DLL 注入会留下可以检测到的内存伪像。
而 sRDI 可以把一个普通的 DLL 文件转换为一段不依赖任何位置的 Shellcode。
相对于标准 RDI,使用 sRDI 的一些优点:
- 你可以转换任何 DLL为无位置依赖的 shellcode,并且可以使用标准的 shellcode 注入技术来使用它。
- 你的 DLL 中不需要写任何反射加载器代码,因为反射加载器是在 DLL 外部的 shellcode 中实现的。
- 合理使用权限,没有大量的 RWX 权限数据。
- 还可以根据选项,抹掉 PE 头特征。
组成简介
sRDI 的所有功能基于以下两个组件:
- 一个C语言项目,可将 PE Loader 编译为 Shellcode
- 转换代码负责将 DLL、RDI 和用户数据进行绑定
由以下元素组成:
- ShellcodeRDI:编译 DLL 加载器的 Shellcode
- NativeLoader:需要时,将 DLL 转换为 shellcode,然后注入内存
- DotNetLoader:NativeLoader 的 C# 实现
- Python ConvertToShellcode.py:将 DLL 转换为 shellcode
- Python EncodeBlobs.py:对已编译的 sRDI 进行编码,进行静态嵌入
- PowerShell ConvertTo-Shellcode.ps1:将 DLL 转换为 shellcode
- FunctionTest:导入 sRDI 的 C 函数,进行调试测试
- TestDLL:示例DLL,包括两个导出函数,用于后续的加载和调用
DLL 不需要使用 RDI 进行编译,但是该技术具有交叉兼容性。
优势
#1 – 隐秘的持久性
- 使用服务器端 Python 代码(sRDI)将 RAT 转换为 shellcode
- 将 shellcode 写入注册表
- 设置计划的任务以执行基本的加载程序 DLL
- 加载程序读取 shellcode 并注入(少于20行C代码)
优点: RAT 或加载器都不需要了解 RDI 或使用 RDI 进行编译。装载机可以保持小巧而简单,避免警告。
#2 – 侧面加载
- 让你的 RAT 在内存中运行
- 编写 DLL 以执行额外的功能
- 将 DLL 转换为 shellcode(使用 sRDI)并本地注入
- 使用 GetProcAddressR 查找导出的函数
- 执行 X 次附加功能,而无需重新加载 DLL
优点: 使您的初始工具更轻巧,并根据需要添加功能。加载一次 DLL 并像使用其他任何 DLL 一样使用它。
#3 – 依赖关系
- 从磁盘读取现有的合法 API DLL
- 将 DLL 转换为 shellcode(使用 sRDI)并将其加载到内存中
- 使用 GetProcAddress 查找所需的功能
优点: 避免使用监视工具来检测 LoadLibrary 调用。访问 API 函数而不会泄漏信息。(WinInet,PSApi,TlHelp32,GdiPlus)
使用✔
使用 python 将 DLL 转换为 shellcode
from
使用 C# 加载程序将 DLL 加载到内存中
DotNetLoader
使用 python 脚本转换 DLL 并使用本机 EXE 加载
python
使用 powershell 转换 DLL 并使用 Invoke-Shellcode 加载
Import-Module .Invoke-Shellcode.ps1
Import-Module .ConvertTo-Shellcode.ps1
Invoke-Shellcode -Shellcode (ConvertTo-Shellcode -File TestDLL_x64.dll)
搭建
sRDI 是使用Visual Studio 2015(v140)和 Windows SDK 8.1 构建的。python 脚本是使用 Python 3 编写的。
Python和Powershell脚本位于:
- PythonConvertToShellcode.py
- PowerShellConvertTo-Shellcode.ps1
构建项目后,其他二进制文件将位于:
- binNativeLoader.exe
- binDotNetLoader.exe
- binTestDLL_<arch>.dll
- binShellcodeRDI_<arch>.bin
测试test⌛
让我们编译一个简单的 x86 DLL,执行弹出 2 个记事本进程:
![cbaee09543c3713ef9d5d843ad0aadd6.png](https://i-blog.csdnimg.cn/blog_migrate/023e972a8d1de5198b435a7a915c3473.png)
将 DLL 转换为 shellcode。我们将得到一个以十进制值表示的 shellcode 字节数组:
$sc = ConvertTo-Shellcode VBOXSVRExperimentsmessageboxmessageboxDebugmessagebox.dll
![a23fd62e9ff08cfe0bfe1beb7f5898b0.png](https://i-blog.csdnimg.cn/blog_migrate/dbc4c3bf5b0a64256b57b699efd30513.png)
让我们将它们转换为十六进制:
$sc2 = $sc | % { write-output ([System.String]::Format('{0:X2}', $_)) }
![76a5ef1a346ca22e7d5c08dc6e88d4f6.png](https://i-blog.csdnimg.cn/blog_migrate/587032e36ab51fe1f78ef10321806448.png)
将它们全部加入并打印到文本文件:
$sc2 -join "" > shell.txt
![e9f06cf792f80bf1c2ad1f58248523c1.png](https://i-blog.csdnimg.cn/blog_migrate/d0450ca22de816aae9fcc21e4715c7a0.jpeg)
使用我们之前获得的 shellcode 创建一个新的二进制文件:
![c4465301e28e28e14f5555855e58fb0f.png](https://i-blog.csdnimg.cn/blog_migrate/38a6db27271b96ca65a1e58b6d40c26d.jpeg)
为了加载和执行 shellcode,我们将把它作为资源放入二进制文件中。从 PE 资源加载和执行 Shellcode:
![5c7da2d9a7ebb0e3addf53e7bb1f4331.png](https://i-blog.csdnimg.cn/blog_migrate/3bea28b92acdc5da7b3df683c72cc444.jpeg)
编译并运行二进制文件。如果 shellcode 成功运行,我们应该会看到两个 notepad.exe 进程弹出窗口:
![966fed53cb0f05715992bcfc21074f6b.gif](https://i-blog.csdnimg.cn/blog_migrate/d88d7f201e66b04a7093e47f90f114ac.gif)
有关 sRDI 的内容就简单介绍到这里。更多有关 CTF 的内容请前往 二向箔安全 进行学习,最近推出了一系列免费的网络安全技能包,有关CTF、渗透测试、网络攻防、黑客技巧尽在其中,学它涨姿势 。
![47f1d5b529daa2b734bb1432e5d8432d.png](https://i-blog.csdnimg.cn/blog_migrate/d2409040a608d1f02ae650719395bc78.jpeg)