WOW64程序如何覆盖WMI可读取的自身命令行参数?
问题根因
64位Windows运行32位WOW64程序时,会为进程维护两份独立的PEB结构:
- 32位PEB:映射在32位地址空间,供32位程序自身、支持WOW64识别的枚举工具(如Process Explorer)读取
- 64位PEB:存储在64位地址空间,供系统内核、原生64位进程(包括WMI服务)读取
原有代码仅修改了32位PEB中存储的命令行内容,WMI读取进程信息时直接从64位PEB拉取未修改的原始命令行,因此出现掩码失效。
修复方案
在原有修改32位PEB命令行逻辑的基础上,额外检测当前是否为WOW64运行环境,若是则定位到64位PEB对应的命令行缓冲区,通过WOW64提供的跨地址空间内存写入接口覆写对应内容,即可覆盖所有读取场景。
修复后的完整代码如下:
#include <wchar.h> #include <windows.h> #include <winnt.h> #include <winternl.h> // WOW64跨位宽内存操作NT函数原型定义 typedef NTSTATUS(NTAPI* pfnNtWow64ReadVirtualMemory64)( HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, DWORD64 nSize, PDWORD64 lpNumberOfBytesRead ); typedef NTSTATUS(NTAPI* pfnNtWow64WriteVirtualMemory64)( HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, DWORD64 nSize, PDWORD64 lpNumberOfBytesWritten ); // 64位地址空间下UNICODE_STRING结构内存布局 typedef struct _UNICODE_STRING_64 { USHORT Length; USHORT MaximumLength; DWORD64 Buffer; } UNICODE_STRING_64; // 64位结构固定偏移(Windows 7至Win11全版本兼容) #define PEB64_PROCESS_PARAMS_OFFSET 0x20 // 64位PEB中ProcessParameters指针偏移 #define PARAMS64_CMDLINE_OFFSET 0x70 // 64位进程参数中CommandLine结构偏移 void MaskWow64CommandLine() { BOOL bIsWow64 = FALSE; if (!IsWow64Process(GetCurrentProcess(), &bIsWow64) || !bIsWow64) return; // 非WOW64场景无需额外处理 HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); auto NtWow64ReadVirtualMemory64 = (pfnNtWow64ReadVirtualMemory64)GetProcAddress(hNtdll, "NtWow64ReadVirtualMemory64"); auto NtWow64WriteVirtualMemory64 = (pfnNtWow64WriteVirtualMemory64)GetProcAddress(hNtdll, "NtWow64WriteVirtualMemory64"); if (!NtWow64ReadVirtualMemory64 || !NtWow64WriteVirtualMemory64) return; // 获取当前进程的64位PEB基址 DWORD64 qwPeb64Addr = 0; ULONG ulRetLen = 0; if (NtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, &qwPeb64Addr, sizeof(qwPeb64Addr), &ulRetLen) != 0 || !qwPeb64Addr) return; // 读取64位PEB中存储的进程参数指针 DWORD64 qwProcParams64Addr = 0; if (NtWow64ReadVirtualMemory64(GetCurrentProcess(), qwPeb64Addr + PEB64_PROCESS_PARAMS_OFFSET, &qwProcParams64Addr, sizeof(qwProcParams64Addr), nullptr) != 0 || !qwProcParams64Addr) return; // 读取64位进程参数中的命令行UNICODE_STRING结构 UNICODE_STRING_64 cmdLine64 = {0}; if (NtWow64ReadVirtualMemory64(GetCurrentProcess(), qwProcParams64Addr + PARAMS64_CMDLINE_OFFSET, &cmdLine64, sizeof(cmdLine64), nullptr) != 0 || !cmdLine64.Buffer || cmdLine64.Length == 0) return; // 分块覆写64位地址空间中的命令行缓冲区 const DWORD BLOCK_SIZE = 256; wchar_t wszMaskBlock[BLOCK_SIZE]; wmemset(wszMaskBlock, L'x', BLOCK_SIZE); DWORD64 qwTotalChars = cmdLine64.Length / sizeof(wchar_t); DWORD64 qwOffset = 0; while (qwOffset < qwTotalChars) { DWORD64 qwWriteChars = min((DWORD64)BLOCK_SIZE, qwTotalChars - qwOffset); NtWow64WriteVirtualMemory64( GetCurrentProcess(), cmdLine64.Buffer + qwOffset * sizeof(wchar_t), wszMaskBlock, qwWriteChars * sizeof(wchar_t), nullptr ); qwOffset += qwWriteChars; } } // 原始TEB/PEB获取逻辑 #if defined(_M_X64) // x64 PTEB tebPtr = reinterpret_cast<PTEB>(__readgsqword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self))); #else // x86 PTEB tebPtr = reinterpret_cast<PTEB>(__readfsdword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self))); #endif PPEB pebPtr = tebPtr->ProcessEnvironmentBlock; int main() { // 覆写32位PEB命令行 UNICODE_STRING* pCmdLine = &pebPtr->ProcessParameters->CommandLine; wmemset(pCmdLine->Buffer, L'x', pCmdLine->Length / sizeof(wchar_t)); // 覆写WOW64场景下64位PEB命令行 MaskWow64CommandLine(); getwchar(); return 0; }
补充说明
- 代码中使用的64位结构偏移在Windows 7及之后所有64位Windows版本中保持固定,无需做系统版本适配
- 该方案仅修改PEB中存储的命令行内容,不会触发内存保护异常,无进程崩溃风险
- 若需要覆盖进程内
GetCommandLineW接口的读取结果,可在上述逻辑基础上额外覆写该接口返回的缓冲区内容即可。
内容的提问来源于stack exchange,提问作者Joseph Sible-Reinstate Monica
相关产品推荐
相关产品推荐

