使用CreateRemoteThread跨进程调用带参数函数时参数值异常的问题排查
问题分析与解决方案
你遇到的参数乱码问题,本质上是两个关键错误导致的:
1. 传递的参数地址属于本地进程,目标进程无法访问
你的params结构体是在当前进程的虚拟内存中创建的,而Windows中每个进程都有独立的虚拟地址空间,目标进程根本无法读取本地进程内存地址中的数据,所以收到的自然是乱码/无效值。
正确的做法是:先在目标进程的内存中分配一块空间,把本地的参数数据写入到这块空间,再把目标进程内的内存地址传递给远程函数。
2. 导出函数的签名不符合CreateRemoteThread的要求
CreateRemoteThread要求线程函数的签名必须是:
DWORD WINAPI ThreadProc(LPVOID lpParameter);
也就是只能接收一个LPVOID类型的参数。而你的show_message_dialog直接接收两个参数,把它作为线程函数调用会导致调用栈混乱,参数解析完全错误。
你需要在DLL中编写一个适配的线程入口函数,通过这个函数来中转参数,再调用你的目标函数。
修正后的完整代码
第一步:修改DLL代码,添加适配的线程入口
#include <windows.h> #include <string> // 定义参数结构体(注意:这个结构体需要在注入端和DLL端保持一致的内存布局) struct my_parameters { int i; double d; }; // 你的原始目标函数 void show_message_dialog(const int first_value, const double second_value) { const auto text = std::to_string(first_value); const auto caption = std::to_string(second_value); MessageBoxA(nullptr, text.c_str(), caption.c_str(), MB_ICONINFORMATION); } // 适配CreateRemoteThread的线程入口函数(需要导出) extern "C" __declspec(dllexport) DWORD WINAPI RemoteThreadEntry(LPVOID lpParam) { auto params = static_cast<my_parameters*>(lpParam); show_message_dialog(params->i, params->d); return 0; }
第二步:修改注入端代码,正确传递参数
#include <windows.h> #include <cstdint> struct my_parameters { int i; double d; }; // 假设你已经实现了这个函数 DWORD get_process_id_from_process_name(const char* process_name); int main() { auto params = my_parameters{3, 1.1}; const auto process_id = get_process_id_from_process_name("target_process.exe"); if (process_id == 0) { // 处理进程查找失败的情况 return 1; } // 打开目标进程,需要足够的权限 auto* process_handle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, FALSE, process_id); if (!process_handle) { // 处理打开失败,比如调用GetLastError()查看错误码 return 1; } // 1. 在目标进程中分配可读写的内存,用于存放参数 void* remote_params = VirtualAllocEx( process_handle, nullptr, sizeof(my_parameters), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ); if (!remote_params) { CloseHandle(process_handle); return 1; } // 2. 将本地的参数数据写入目标进程的内存 SIZE_T bytes_written; BOOL write_success = WriteProcessMemory( process_handle, remote_params, ¶ms, sizeof(my_parameters), &bytes_written ); if (!write_success || bytes_written != sizeof(my_parameters)) { VirtualFreeEx(process_handle, remote_params, 0, MEM_RELEASE); CloseHandle(process_handle); return 1; } // 3. 调用远程线程入口函数(替换为你的RemoteLibraryFunction调用) RemoteLibraryFunction( process_handle, "my_injected_dll.dll", "RemoteThreadEntry", remote_params, sizeof(my_parameters), nullptr ); // 4. 清理资源:释放目标进程中的参数内存,关闭进程句柄 VirtualFreeEx(process_handle, remote_params, 0, MEM_RELEASE); CloseHandle(process_handle); return 0; }
汇编层面的调试方法
如果还是遇到问题,可以用x64dbg或WinDbg这样的调试器来排查:
- 附加到目标进程:启动调试器,附加到你要注入的目标进程。
- 确认DLL已加载:在调试器的模块列表中找到
my_injected_dll.dll,确认它已经被注入。 - 在线程入口函数上下断点:找到
RemoteThreadEntry的内存地址(可以通过导出函数名定位),下断点。 - 触发注入:运行你的注入程序,当断点命中时,检查寄存器和栈:
- 首先看
RCX寄存器(x64调用约定),它应该存放的是目标进程中remote_params的地址,确认这个地址是有效的。 - 然后读取这个地址指向的内存,检查
int i和double d的值是否和你传入的3、1.1一致。 - 进入
show_message_dialog函数后,检查参数:x64下first_value在RCX,second_value在XMM0寄存器;x86下参数是从右往左压栈,栈顶返回地址之后,依次是second_value(8字节)、first_value(4字节),检查这些值是否正确。
- 首先看
- 排查内存写入问题:如果参数值不对,回到注入端,检查
VirtualAllocEx的返回地址是否有效,WriteProcessMemory的bytes_written是否等于sizeof(my_parameters),确认数据正确写入到了目标进程。
内容的提问来源于stack exchange,提问作者BullyWiiPlaza
相关产品推荐
相关产品推荐

