插件开发手册

插件产品介绍
Game-Plug 是一款功能强大的 Windows 逆向开发插件,兼容 VC++、Delphi、VB、按键精灵、火山 PC、等多种编程语言调用。它面向编程人员提供窗口、内存、线程、汇编、HOOK、注入、Call、按键、封包、驱动防检测等全方位操作命令,并配有对应的使用案例示范,简单易学、上手快、实用性强。
目录导航

在目标进程执行机器码 - AsmCall

接口 ID 148 | 分类:汇编 | 定义文件:x64GamePlug.idl

作用说明

支持32/64位汇编, 返回执行结果 rax/eax 的十六进制串(成功),失败返回空串用 GetAsmLastError 查原因。mode 0=stdcall 1=cdecl 2=thiscall(thiscall 第一个参数自己在代码里 mov ecx,对象);timeout 毫秒 0=默认 5000;bitness 1=按 64 位汇编执行 0=按 32 位汇编执行 其他数值=自动(用 AsmAdd 最近一次判断出的位数,从没 AsmAdd 过则按目标进程位数)。插件会自动包一层执行壳(保存恢复寄存器和栈),用户代码最后写不写 ret 都行;汇编代码位数和目标进程位数不一致会直接报错

参数说明
参数名方向类型说明
hwnd[in]__int64 (int64)目标窗口句柄(64 位整数,接口类型为 hyper);插件按它取到所属进程,并在该进程里创建线程执行机器码。句柄为 0、取不到进程、或目标就是插件宿主自己时都会失败
asmHexCode[in]string (BSTR)由 AsmAdd 逐句返回并直接拼接好的机器码十六进制串(BSTR 宽字符串)。允许夹空格、制表符和换行,但去掉空白后长度必须是偶数且只含 0-9 / A-F(大小写均可),否则解析失败
mode[in]long调用约定编号:0=stdcall、1=cdecl、2=thiscall(thiscall 的第一个参数要自己在汇编里写 mov ecx,对象)。三种模式用的是同一套执行壳,区别只在代码写法提示;只能传 0~2
timeoutMs[in]long等待执行完成的毫秒数;传 0 按默认 5000 毫秒处理。超时只记录错误并返回空串,不会结束目标进程里的线程,所以汇编代码不要写死循环。不要传负数
bitness[in]long汇编代码的位数:1=按 64 位执行、0=按 32 位执行、其它数值=自动(先用本线程最近一次 AsmAdd 判断出的位数,从没 AsmAdd 过则按目标进程位数)。汇编位数与目标进程位数不一致会直接报错不执行
VC++ 调用示例(x64,控制台工程)
// 说明:本示例为完整 x64 控制台工程,整段复制覆盖新建控制台工程的 main.cpp 即可编译运行。
//   1. VS2019 新建「控制台应用」,项目属性 -> 平台选 x64,才能加载 64 位 DLL。
//   2. 项目属性 -> C/C++ -> 预编译头 -> 改为「不使用预编译头」,否则默认找 pch.h 会报 C1010。
//   3. 把 x64Game-Plug.dll 放到 exe 同目录(#import 与免注册加载都需要)。
//   4. 源码保存为 UTF-8 带 BOM,避免中文乱码;全站用 regsvr32 注册也可(需管理员)。
#include <iostream>
#include <clocale>
#include <windows.h>
#include <comdef.h>

// 免注册加载:用 IGamePlug 智能指针装载
// rename 是为避开 windows.h 的 FindWindow 宏冲突,接口里的 FindWindow 在 C++ 侧改名为 FindWindowGP
#import "x64Game-Plug.dll" no_namespace rename("FindWindow", "FindWindowGP")

static HMODULE      g_hDll = NULL;
static IGamePlugPtr g_plug;

// ---------- 免注册:加载 DLL 并创建对象(发布时把 DLL 放 exe 同目录)----------
static bool LoadPlug() {
    const wchar_t* paths[] = {
        L"x64Game-Plug.dll",
        L"..\x64Game-Plug.dll"
    };
    for (int i = 0; i < (int)(sizeof(paths) / sizeof(paths[0])); ++i) {
        g_hDll = ::LoadLibraryW(paths[i]);
        if (g_hDll) break;
    }
    if (!g_hDll) {
        std::cout << "LoadLibrary failed: put x64Game-Plug.dll next to the exe" << std::endl;
        return false;
    }
    typedef HRESULT (__stdcall *GamePlugCreateFn)(IGamePlug**);
    GamePlugCreateFn fn = (GamePlugCreateFn)::GetProcAddress(g_hDll, "GamePlugCreate");
    if (!fn) { std::cout << "GetProcAddress GamePlugCreate failed" << std::endl; return false; }
    HRESULT hr = fn(&g_plug);
    if (FAILED(hr)) { std::cout << "GamePlugCreate failed: 0x" << std::hex << hr << std::endl; return false; }
    return true;
}
int main() {
    if (!LoadPlug()) { system("pause"); return 1; }
    // 授权(收费功能需先传入授权码,去 https://www.game-plug.cn/ 获取)
    g_plug->SetUsername(_bstr_t(L"U12-XXXXXXXXXXXXXXXX"));

    // ---------- 调用插件本命令方法如下 ----------
    try {
        __int64 hwnd = (__int64)(LONG_PTR)::FindWindowW(NULL, L"目标窗口标题");  // 换成你的目标窗口标题;句柄按 64 位整数传
        _bstr_t asmHexCode = g_plug->AsmAdd(_bstr_t(L"mov rax,0x123"));  // 实际用法:把多句 AsmAdd 的结果直接拼接起来
        long mode = 0;  // 0=stdcall 1=cdecl 2=thiscall
        long timeoutMs = 0;  // 0 表示默认 5000 毫秒,不要传负数
        long bitness = 1;  // 1=64 位 0=32 位 其它=自动;本例目标为 64 位进程
        _bstr_t s = g_plug->AsmCall(hwnd, asmHexCode, mode, timeoutMs, bitness);
        std::wcout << L"执行结果(rax/eax): " << (wchar_t*)s << std::endl;
    } catch (_com_error& e) {
        std::wcout << L"COM error: " << e.ErrorMessage() << std::endl;
    }

    g_plug->Release();
    system("pause");
    return 0;
}

返回值

返回执行结果 rax(64 位目标)/ eax(32 位目标)的十六进制串,按内存小端原样输出(64 位为 16 个十六进制字符、32 位为 8 个);失败返回空串,用 GetAsmLastError 查中文原因。

常见注意事项
  • 汇编文本按 Intel 语法书写;AsmAdd 返回的大写十六进制串可以直接拼接成完整机器码,再交给 AsmCall 执行。两个方法失败时都返回空串而不是抛异常,请用 GetAsmLastError 查中文原因。
  • 错误串与「最近一次翻译用的位数」都是线程本地状态:同一线程内各次调用互相衔接,跨线程不共享;多线程调用时请在各自线程里组织同一段代码的翻译与执行。
  • 插件会自动包一层执行壳:入口建立栈帧并保存寄存器、call 用户代码、把 rax/eax 写入共享结果区、恢复寄存器后 ret;用户代码最后写不写 ret 都行(没写 ret 会直落到壳的尾部),不会把目标进程的栈弄乱。三种 mode 用的是同一套壳。
  • 汇编代码位数必须和目标进程位数一致,不一致直接报错不执行(例如 64 位进程里跑 32 位写法会失败)。bitness 显式传 1 / 0 时优先按它判定,其余值走自动:先看本线程最近一次 AsmAdd 判断出的位数,从没 AsmAdd 过则按目标进程位数。
  • hwnd 必须按 64 位整数传(接口类型为 hyper),不要用 32 位 LONG 存句柄,否则高位会被截断;目标窗口所属进程不能是插件宿主自己,源码会拒绝 pid 等于当前进程的情况。
  • timeoutMs=0 按 5000 毫秒处理;超时不会结束目标进程里的远程线程(避免破坏目标进程),只记录错误并返回空串。负数会被转成很大的等待值,请不要传负数。
  • 打开目标进程需要 PROCESS_CREATE_THREAD / VM_OPERATION / VM_WRITE / VM_READ 等权限;目标开了 CFG、ACG 之类防护时创建远程线程会失败。句柄无效、机器码格式不合法、mode 不在 0~2 都会返回空串,具体原因用 GetAsmLastError 查。
  • 机器码十六进制串允许夹空格、制表符和换行,但去掉空白后长度必须是偶数且只含 0-9 / A-F;建议直接用 AsmAdd 的返回值拼接,不要手工改写。
  • 本方法为收费功能,调用前需先执行 SetUsername 传入有效授权码(示例开头已激活该调用,发布时请改为你自己的授权码);未授权时返回空串且不抛异常。