调用MessageBoxA无弹窗问题:跨进程注入WPF等程序失败排查
问题描述
本人有多年C#开发经验,现学习C语言并开展低阶项目。尝试通过跨进程注入调用MessageBoxA到空白WPF App、记事本(32/64位)、计算器等程序,WPF App与注入程序均编译为x64版本,调试输出显示所有步骤均执行成功,但始终无MessageBox弹窗,且代码量比网上示例多,寻求问题原因。
调试输出
Process ID for WpfApp1.exe found: 948 Target process found. PID: 948 Opened handle to process. Handle to user32.dll loaded successfully. Address of MessageBoxA: 00007ffc67fc8b70 Allocated memory at remote address: 00000262a8500000 Successfully wrote message to remote memory. Remote thread created successfully. Remote thread completed.
注入代码
#include <stdio.h> #include <windows.h> #include <tlhelp32.h> #include <unistd.h> DWORD GetPID(const char *processName); int main(void) { DWORD processId = GetPID("WpfApp1.exe"); if (!processId) { printf("Process not found\n"); fflush(stdout); return 1; } printf("Target process found. PID: %lu\n", processId); fflush(stdout); HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); if (!hProcess) { printf("Failed to open process. Error code: %lu\n", GetLastError()); fflush(stdout); return 1; } printf("Opened handle to process.\n"); fflush(stdout); // Explicitly load user32.dll HMODULE hUser32 = LoadLibrary("user32.dll"); if (!hUser32) { printf("Failed to load user32.dll. Error code: %lu\n", GetLastError()); fflush(stdout); CloseHandle(hProcess); return 1; } printf("Handle to user32.dll loaded successfully.\n"); fflush(stdout); LPVOID msgBoxAddress = (LPVOID)GetProcAddress(hUser32, "MessageBoxA"); if (!msgBoxAddress) { printf("Failed to find address of MessageBoxA. Error code: %lu\n", GetLastError()); fflush(stdout); FreeLibrary(hUser32); CloseHandle(hProcess); return 1; } printf("Address of MessageBoxA: %p\n", msgBoxAddress); fflush(stdout); LPVOID remoteString = VirtualAllocEx(hProcess, NULL, 256, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!remoteString) { printf("Failed to allocate memory in target process. Error code: %lu\n", GetLastError()); fflush(stdout); FreeLibrary(hUser32); CloseHandle(hProcess); return 1; } printf("Allocated memory at remote address: %p\n", remoteString); fflush(stdout); if (!WriteProcessMemory(hProcess, remoteString, "Hello World!", 13, NULL)) { printf("Failed to write to remote memory. Error code: %lu\n", GetLastError()); fflush(stdout); VirtualFreeEx(hProcess, remoteString, 0, MEM_RELEASE); FreeLibrary(hUser32); CloseHandle(hProcess); return 1; } printf("Successfully wrote message to remote memory.\n"); fflush(stdout); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)msgBoxAddress, remoteString, 0, NULL); if (!hThread) { printf("Failed to create remote thread. Error code: %lu\n", GetLastError()); fflush(stdout); VirtualFreeEx(hProcess, remoteString, 0, MEM_RELEASE); FreeLibrary(hUser32); CloseHandle(hProcess); return 1; } printf("Remote thread created successfully.\n"); fflush(stdout); WaitForSingleObject(hThread, INFINITE); printf("Remote thread completed.\n"); fflush(stdout); VirtualFreeEx(hProcess, remoteString, 0, MEM_RELEASE); FreeLibrary(hUser32); CloseHandle(hThread); CloseHandle(hProcess); return 0; } DWORD GetPID(const char *processName) { PROCESSENTRY32 processEntry; HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { printf("Failed to create snapshot. Error code: %lu\n", GetLastError()); fflush(stdout); return 0; } processEntry.dwSize = sizeof(processEntry); if (Process32First(hSnapshot, &processEntry)) { do { if (strcmp(processEntry.szExeFile, processName) == 0) { DWORD pid = processEntry.th32ProcessID; CloseHandle(hSnapshot); printf("Process ID for %s found: %lu\n", processName, pid); fflush(stdout); return pid; } } while (Process32Next(hSnapshot, &processEntry)); } printf("Failed to find process: %s\n", processName); fflush(stdout); CloseHandle(hSnapshot); return 0; }
分析与解决
核心错误:线程函数签名不匹配
CreateRemoteThread要求线程函数的签名为:
DWORD WINAPI ThreadProc(LPVOID lpParameter);
但MessageBoxA的签名是:
int WINAPI MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
直接将MessageBoxA作为线程函数传入,会导致参数传递完全错误:
- 传入的
remoteString被当作第一个参数HWND(无效窗口句柄) lpText、lpCaption、uType参数为栈中随机值
这种情况下MessageBoxA无法正确解析参数,自然不会弹出窗口,甚至可能直接返回错误码。
冗余代码说明
在注入进程中调用LoadLibrary("user32.dll")完全多余:
- 目标进程作为GUI程序,默认已经加载了
user32.dll - 系统核心DLL(如
user32.dll、kernel32.dll)在同架构的所有进程中基地址一致,直接通过GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA")即可获取正确地址。
修正方案
最可靠且通用的方式是使用DLL注入,这也是网上示例常用的方法:
步骤1:编写注入用DLL
#include <windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // DLL被加载到目标进程时调用MessageBoxA MessageBoxA(NULL, "Hello from injected DLL!", "Injection Success", MB_OK); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
将此代码编译为x64 DLL(与目标进程架构一致)。
步骤2:修改注入程序
改为通过CreateRemoteThread调用目标进程的LoadLibraryA加载上述DLL:
#include <stdio.h> #include <windows.h> #include <tlhelp32.h> DWORD GetPID(const char *processName); int main(void) { DWORD processId = GetPID("WpfApp1.exe"); if (!processId) { printf("Process not found\n"); fflush(stdout); return 1; } printf("Target process found. PID: %lu\n", processId); fflush(stdout); HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); if (!hProcess) { printf("Failed to open process. Error code: %lu\n", GetLastError()); fflush(stdout); return 1; } printf("Opened handle to process.\n"); fflush(stdout); // 获取LoadLibraryA的地址(系统DLL地址全局一致) LPVOID loadLibAddr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA"); if (!loadLibAddr) { printf("Failed to get LoadLibraryA address. Error code: %lu\n", GetLastError()); fflush(stdout); CloseHandle(hProcess); return 1; } // DLL的绝对路径,确保目标进程能访问到 const char* dllPath = "C:\\Full\\Path\\To\\Your\\Inject.dll"; size_t pathLength = strlen(dllPath) + 1; // 在目标进程中分配内存存放DLL路径 LPVOID remotePath = VirtualAllocEx(hProcess, NULL, pathLength, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!remotePath) { printf("Failed to allocate remote memory. Error code: %lu\n", GetLastError()); fflush(stdout); CloseHandle(hProcess); return 1; } printf("Allocated remote memory for DLL path: %p\n", remotePath); fflush(stdout); // 将DLL路径写入目标进程内存 if (!WriteProcessMemory(hProcess, remotePath, dllPath, pathLength, NULL)) { printf("Failed to write DLL path to remote memory. Error code: %lu\n", GetLastError()); fflush(stdout); VirtualFreeEx(hProcess, remotePath, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } printf("Wrote DLL path to remote memory successfully.\n"); fflush(stdout); // 创建远程线程调用LoadLibraryA加载DLL HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibAddr, remotePath, 0, NULL); if (!hThread) { printf("Failed to create remote thread. Error code: %lu\n", GetLastError()); fflush(stdout); VirtualFreeEx(hProcess, remotePath, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } printf("Remote thread created successfully.\n"); fflush(stdout); WaitForSingleObject(hThread, INFINITE); printf("Remote thread completed.\n"); fflush(stdout); // 清理资源 VirtualFreeEx(hProcess, remotePath, 0, MEM_RELEASE); CloseHandle(hThread); CloseHandle(hProcess); return 0; } DWORD GetPID(const char *processName) { PROCESSENTRY32 processEntry; HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { printf("Failed to create snapshot. Error code: %lu\n", GetLastError()); fflush(stdout); return 0; } processEntry.dwSize = sizeof(processEntry); if (Process32First(hSnapshot, &processEntry)) { do { if (strcmp(processEntry.szExeFile, processName) == 0) { DWORD pid = processEntry.th32ProcessID; CloseHandle(hSnapshot); printf("Process ID for %s found: %lu\n", processName, pid); fflush(stdout); return pid; } } while (Process32Next(hSnapshot, &processEntry)); } printf("Failed to find process: %s\n", processName); fflush(stdout); CloseHandle(hSnapshot); return 0; }
注意事项
- 确保注入程序、目标进程、DLL三者架构一致(均为x64或均为x86)
- DLL路径必须是绝对路径,否则目标进程可能找不到DLL
- 运行注入程序需要管理员权限(否则可能无法打开目标进程)
内容的提问来源于stack exchange,提问作者JohnA
相关产品推荐
相关产品推荐

