Windows平台:如何在目标进程执行main()前将其挂起?
在Windows中实现进程main()执行前挂起的可行方案
由于WMI的IWbemObjectSink::Indicate触发时机存在延迟,此时目标进程已经完成初始化并开始执行启动代码,因此无法直接通过该机制实现main()前挂起。以下是几种更可靠的方案:
方案1:内核模式进程创建回调(最底层,无延迟)
通过Windows内核API PsSetCreateProcessNotifyRoutineEx注册回调,能在进程创建的最早期(用户态代码尚未执行时)捕获事件,直接挂起进程。需要编写内核驱动,适用于系统级监控场景。
核心代码示例(内核驱动):
#include <ntddk.h> VOID ProcessCreateCallback(PCREATE_PROCESS_NOTIFY_INFORMATION_EX pInfo) { if (pInfo->CreateProcess) { // 过滤目标进程(示例:匹配目标进程名) UNICODE_STRING targetProcessName; RtlInitUnicodeString(&targetProcessName, L"target.exe"); if (RtlCompareUnicodeString(&pInfo->ImageName, &targetProcessName, TRUE) == 0) { // 直接挂起进程 ZwSuspendProcess(pInfo->ProcessHandle); } } } NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(ProcessCreateCallback, FALSE); if (!NT_SUCCESS(status)) { return status; } DriverObject->DriverUnload = [](PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); PsSetCreateProcessNotifyRoutineEx(ProcessCreateCallback, TRUE); }; return STATUS_SUCCESS; }
注意:Windows要求内核驱动必须签名(测试环境可通过
bcdedit /set testsigning on禁用签名验证)。
方案2:用户态WH_CBT全局钩子
利用WH_CBT钩子在进程初始化阶段(线程尚未执行用户态代码前)注入到目标进程,挂起其主线程。该方案无需驱动,但需要编写注入DLL。
核心代码示例(钩子DLL):
#include <windows.h> #include <tlhelp32.h> HHOOK g_hCbtHook = NULL; LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HCBT_CREATEWND) { // 获取当前进程信息,匹配目标进程 DWORD currentPid = GetCurrentProcessId(); TCHAR szProcessName[MAX_PATH] = {0}; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, currentPid); if (hProcess) { GetModuleBaseName(hProcess, NULL, szProcessName, MAX_PATH); if (_tcscmp(szProcessName, _T("target.exe")) == 0) { // 挂起当前线程(目标进程的主线程) HANDLE hThread = GetCurrentThread(); SuspendThread(hThread); } CloseHandle(hProcess); } } return CallNextHookEx(g_hCbtHook, nCode, wParam, lParam); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 注册全局WH_CBT钩子 g_hCbtHook = SetWindowsHookEx(WH_CBT, CbtProc, hModule, 0); break; case DLL_PROCESS_DETACH: if (g_hCbtHook) { UnhookWindowsHookEx(g_hCbtHook); } break; } return TRUE; }
方案3:CREATE_SUSPENDED标志创建进程(可控启动场景)
如果你的程序可以主动触发目标进程的启动,直接调用CreateProcess时指定CREATE_SUSPENDED标志,进程创建后会立即处于挂起状态,此时尚未执行任何用户态代码。
核心代码示例:
#include <windows.h> int main() { STARTUPINFO si = {sizeof(STARTUPINFO)}; PROCESS_INFORMATION pi = {0}; // 创建挂起状态的目标进程 BOOL ret = CreateProcess( L"target.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi ); if (ret) { // 此时进程已挂起,未执行main(),可按需执行内存修改等操作 // ResumeThread(pi.hThread); // 恢复进程执行 CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } return 0; }
方案4:改进WMI监控方案(仅作补充,可靠性有限)
若必须保留WMI监控,可在Indicate中挂起进程后,将主线程的指令指针重置到进程的PE入口点,强制进程重新执行启动流程,覆盖已执行的早期代码。但该方法依赖PE结构,不同编译器的启动流程可能存在差异。
核心代码示例:
#include <windows.h> #include <wbemidl.h> void Indicate(long lObjectCount, IWbemClassObject** apObjArray) { for (int i = 0; i < lObjectCount; i++) { VARIANT varPid; HRESULT hr = apObjArray[i]->Get(L"ProcessId", 0, &varPid, NULL, NULL); if (SUCCEEDED(hr) && varPid.vt == VT_I4) { DWORD pid = varPid.lVal; HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (hProcess) { // 枚举进程主线程 HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnap != INVALID_HANDLE_VALUE) { THREADENTRY32 te = {sizeof(THREADENTRY32)}; if (Thread32First(hThreadSnap, &te)) { do { if (te.th32OwnerProcessID == pid) { HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID); if (hThread) { // 挂起线程 SuspendThread(hThread); // 重置线程上下文到PE入口点 CONTEXT ctx = {0}; ctx.ContextFlags = CONTEXT_INTEGER; GetThreadContext(hThread, &ctx); HMODULE hModule = NULL; GetModuleHandleEx(GHME_GET_FROM_ADDRESS, (LPCVOID)ctx.Rip, &hModule); if (hModule) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDosHeader->e_lfanew); ctx.Rip = (DWORD64)((BYTE*)hModule + pNtHeaders->OptionalHeader.AddressOfEntryPoint); SetThreadContext(hThread, &ctx); } CloseHandle(hThread); break; } } } while (Thread32Next(hThreadSnap, &te)); } CloseHandle(hThreadSnap); } CloseHandle(hProcess); } VariantClear(&varPid); } } }
内容的提问来源于stack exchange,提问作者Jefferdai
相关产品推荐
相关产品推荐

