You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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,
        &params,
        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这样的调试器来排查:

  1. 附加到目标进程:启动调试器,附加到你要注入的目标进程。
  2. 确认DLL已加载:在调试器的模块列表中找到my_injected_dll.dll,确认它已经被注入。
  3. 在线程入口函数上下断点:找到RemoteThreadEntry的内存地址(可以通过导出函数名定位),下断点。
  4. 触发注入:运行你的注入程序,当断点命中时,检查寄存器和栈:
    • 首先看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字节),检查这些值是否正确。
  5. 排查内存写入问题:如果参数值不对,回到注入端,检查VirtualAllocEx的返回地址是否有效,WriteProcessMemory的bytes_written是否等于sizeof(my_parameters),确认数据正确写入到了目标进程。

内容的提问来源于stack exchange,提问作者BullyWiiPlaza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 13:24:07