手动映射PE时AllocConsole导致cmd.exe输出无法捕获的问题
解决GUI程序内存映射cmd.exe输出重定向问题
问题根源
cmd.exe与powershell.exe的输出机制存在差异:powershell优先使用标准文件句柄(WriteFile)输出,而cmd.exe会优先检测控制台环境,直接写入控制台屏幕缓冲区(通过WriteConsole系列API)。当GUI程序调用AllocConsole后,系统会创建独立的控制台对象,此时手动替换的标准句柄没有与cmd.exe的控制台访问逻辑绑定,导致输出无法流入匿名管道。
具体解决思路
1. 跳过AllocConsole,直接构建无控制台环境
GUI程序无需调用AllocConsole,直接创建匿名管道并设置为进程标准句柄,让cmd.exe认为自身处于无控制台环境,从而强制使用标准I/O输出:
// 创建可继承的匿名管道 SECURITY_ATTRIBUTES sa = {0}; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; HANDLE hReadPipe, hWritePipe; CreatePipe(&hReadPipe, &hWritePipe, &sa, 0); // 关闭默认的标准句柄(GUI程序初始无控制台句柄) CloseHandle(GetStdHandle(STD_INPUT_HANDLE)); CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); CloseHandle(GetStdHandle(STD_ERROR_HANDLE)); // 设置管道为新的标准句柄 SetStdHandle(STD_INPUT_HANDLE, hReadPipe); SetStdHandle(STD_OUTPUT_HANDLE, hWritePipe); SetStdHandle(STD_ERROR_HANDLE, hWritePipe); // 标记管道句柄可继承,确保映射的PE能访问 SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
2. 手动修正映射PE的PEB标准句柄配置
RunPE逻辑会替换当前进程的PEB,需确保新PE的ProcessParameters中标准句柄指向管道:
// 获取当前PEB指针 PPEB pPeb = (PPEB)__readfsdword(0x30); // 修改PEB中的标准I/O句柄 pPeb->ProcessParameters->StandardInput = hReadPipe; pPeb->ProcessParameters->StandardOutput = hWritePipe; pPeb->ProcessParameters->StandardError = hWritePipe;
3. Hook控制台输出API,强制重定向到管道
如果cmd.exe仍直接调用WriteConsole系列API,需Hook这些函数并将输出转发到管道句柄:
// 示例Hook WriteConsoleW typedef BOOL(WINAPI* pWriteConsoleW)(HANDLE, const WCHAR*, DWORD, LPDWORD, LPVOID); pWriteConsoleW origWriteConsoleW = (pWriteConsoleW)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "WriteConsoleW"); BOOL WINAPI hookedWriteConsoleW(HANDLE hConsoleOutput, const WCHAR* lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved) { // 直接写入管道句柄 return WriteFile(hWritePipe, lpBuffer, nNumberOfCharsToWrite * sizeof(WCHAR), lpNumberOfCharsWritten, NULL); }
4. 清理控制台相关环境变量
构建映射PE的环境块时,移除PROMPT、CONSOLE_TITLE等控制台专属环境变量,避免cmd.exe检测到控制台环境:
// 遍历当前环境块,过滤掉控制台相关变量 LPWSTR envBlock = GetEnvironmentStringsW(); LPWSTR newEnv = (LPWSTR)LocalAlloc(LPTR, 4096); LPWSTR ptr = envBlock, newPtr = newEnv; while (*ptr) { if (wcsstr(ptr, L"PROMPT=") == NULL && wcsstr(ptr, L"CONSOLE_TITLE=") == NULL) { wcscpy(newPtr, ptr); newPtr += wcslen(newPtr) + 1; } ptr += wcslen(ptr) + 1; } // 将新环境块设置到PEB的ProcessParameters中 pPeb->ProcessParameters->Environment = newEnv;
验证要点
- 确保管道句柄的
bInheritHandle属性为TRUE,且通过SetHandleInformation标记为可继承。 - 映射PE前,确认当前进程的标准句柄和PEB中的标准句柄均指向管道。
- 若仍失败,可通过API监控工具确认cmd.exe调用的输出API类型,针对性Hook。
内容的提问来源于stack exchange,提问作者Octoberfest7
相关产品推荐
相关产品推荐

