热血江湖 YGOnline.exe —— 「按钮点击call」纠正版(走事件 0x409 派发) 时间:2026-09-28 15:45 目标:D:\rxjh2025\client\YGOnline.exe(32位,无ASLR,模块基址恒 0x400000) 本次实测:PID 544(另一进程 4292 未进游戏) ================================================================================ 一、纠正:三个名字,三条不同的路,别混用 -------------------------------------------------------------------------------- 名字 走的路 能点交易窗/组队窗按钮? ───────────────────────────────────────────────────────────────────────────── ① Call stdcall 转发 fn(参数1,参数2,参数3) ✗ (程序集1:通用函数调用封装) ② 内挂丸_点控件 0925 版:控件池 0x3C35758 → [控件+4] 父窗口 ✗ → 消息 0x3F4(助手专用) ③ 按钮点击call 事件 0x409 + 控件ID → 事件分发器 [0x0188C60C] ✓ 通用 (本版) · 为什么 Call 不行:它是 `fn(参数1,参数2,参数3)` 的 stdcall 转发,汇编是 leave / pop eax / xchg [esp],eax / jmp eax —— 只负责把参数压好栈, **设不了 ecx**。而按钮点击处理是 thiscall(ecx = 事件分发器对象), ecx 不对就是乱跳。(想硬用 Call,得先 VirtualAlloc 一块可执行内存把自己那段桩子写进去 再 Call(桩子地址),没必要。) · 为什么 内挂丸_点控件 不行:它是给助手(内挂丸)那批控件做的,消息号 0x3F4、 ecx 走 [控件+4](父窗口)。交易窗/组队窗按钮是另一套:消息号 0x409、 ecx 走全局事件分发器 [0x0188C60C]。拿旧版点交易窗按钮**不会报错、但按钮不响应**。 二、活体实证:游戏自己就是这么点的(PID 544 实测字节) -------------------------------------------------------------------------------- 按钮点击的代码在 0xCFB623(ButtonClass::消息处理 的 msg=8 分支),逐字节读出来: 0xCFB623 6A 00 push 0 参数3 8B 55 FC mov edx,[ebp-4] 按钮对象 8B 42 0C mov eax,[edx+0C] ★控件ID 在按钮对象 +0x0C 50 push eax 参数2 = 控件ID 68 09 04 00 00 push 0x409 参数1 = 事件号 8B 0D 0C C6 88 01 mov ecx,[0188C60C] 事件分发器对象 8B 11 mov edx,[ecx] 它的虚表 8B 0D 0C C6 88 01 mov ecx,[0188C60C] ecx = 分发器(this) 8B 42 04 mov eax,[edx+4] 虚表 slot1 FF D0 call eax 90 nop ← 之后没有 add esp,被调方自清栈 本版机器码与它逐条一致(区别只有:控件ID 写成立即数、末尾补 C3 让它能独立执行): 6A 00 | 68 <控件ID 4字节小端> | 68 09 04 00 00 | 8B 0D 0C C6 88 01 | 8B 11 | 8B 0D 0C C6 88 01 | 8B 42 04 | FF D0 | C3 事件分发器本体(实测): [0x0188C60C] = 0x5E80AA0(pid544)/ 0xDB06658(pid4292) ← 每进程不同,必须现读 虚表 = 0x010B0F88(两进程一致 → 镜像内静态虚表,本版固定) 虚表 slot1(派发) = 0x5ADA00 函数头:55 8B EC 83 EC 2C 89 4D F8 8B 45 08 89 45 FC 81 7D FC F8 03 00 00 7F 44 push ebp ; mov ebp,esp ; sub esp,0x2C ; mov [ebp-8],ecx(存 this); mov eax,[ebp+8](= 参数1 = 事件号); cmp eax,0x3F8 ; jg +0x44 → 确认它认 ecx 当 this、并按事件号分派,这条链就是"点按钮"的正式入口。 三、本次实测按钮(同名同址,ID 每会话变) -------------------------------------------------------------------------------- 交易窗 槽 1236 对象 0x46049D88 +0x38C (908) 对象 0x51DC1218 ID 6700 ★左按钮 +0x394 (916) 对象 0x51DC8BC8 ID 6701 ★右按钮 +0x37C (892) 对象 0x51DC1D28 ID 6698 (本阶段未启用) 组队窗 槽 1256 对象 0x35404950 +0x3B8 (952) ID 7034 +0x3C0 (960) ID 7035 四、代码(按 ①②③④ 顺序粘) ================================================================================ ① 常量表 -------------------------------------------------------------------------------- .版本 2 .常量 IFManager基址, "25740828" .常量 控件ID偏移, "12" .常量 交易窗口槽, "1236" .常量 组队窗口槽, "1256" .常量 交易接受, "908" .常量 交易拒绝, "916" .常量 组队接受, "952" .常量 组队拒绝, "960" -------------------------------------------------------------------------------- ② 按钮点击call(核心:派发事件 0x409) -------------------------------------------------------------------------------- .版本 2 .子程序 按钮点击call .参数 控件ID, 整数型 .局部变量 代码, 字节集 代码 = 机器码到字节集 (“6A0068”) + 取字节集左边 (到字节集 (控件ID), 4) + 机器码到字节集 (“68090400008B0D0CC688018B118B0D0CC688018B4204FFD0C3”) 调用字节集 (全局_进程句柄, 代码) 返回 () -------------------------------------------------------------------------------- ③ 点窗口按钮(自动取 ID,不用你管 ID 是多少) -------------------------------------------------------------------------------- .版本 2 .子程序 点窗口按钮, 逻辑型 .参数 槽偏移, 整数型 .参数 按钮偏移, 整数型 .局部变量 接口管理, 整数型 .局部变量 窗口对象, 整数型 .局部变量 按钮对象, 整数型 .局部变量 控件ID, 整数型 接口管理 = 内存_读整数型 (全局_进程句柄, #IFManager基址) .如果真 (接口管理 ≤ 65536) 返回 (假) .如果真结束 窗口对象 = 内存_读整数型 (全局_进程句柄, 接口管理 + 槽偏移) .如果真 (窗口对象 ≤ 65536) 返回 (假) .如果真结束 按钮对象 = 内存_读整数型 (全局_进程句柄, 窗口对象 + 按钮偏移) .如果真 (按钮对象 ≤ 65536) 返回 (假) .如果真结束 控件ID = 内存_读整数型 (全局_进程句柄, 按钮对象 + #控件ID偏移) .如果真 (控件ID ≤ 0) 返回 (假) .如果真结束 按钮点击call (控件ID) 返回 (真) -------------------------------------------------------------------------------- ④ 调用示例(配合窗口弹出判定 + 窗口槽位表数据类型) -------------------------------------------------------------------------------- .版本 2 .子程序 处理交易窗 .如果真 (窗口是否弹出 (窗口.交易) = 假) 返回 () .如果真结束 点窗口按钮 (窗口.交易, #交易接受) 返回 () -------------------------------------------------------------------------------- 换成右按钮就是 #交易拒绝;要点组队窗的两个按钮,改成: 点窗口按钮 (窗口.组队, #组队接受) 点窗口按钮 (窗口.组队, #组队拒绝) -------------------------------------------------------------------------------- 五、机器码逐字节说明 ================================================================================ 6A 00 push 0 参数3(恒 0) 68 xx xx xx xx push <控件ID> 参数2 = 控件ID,小端 4 字节 68 09 04 00 00 push 0x409 参数1 = 事件号(低字节在前) 8B 0D 0C C6 88 01 mov ecx,[0x0188C60C] 事件分发器对象 8B 11 mov edx,[ecx] 虚表 8B 0D 0C C6 88 01 mov ecx,[0x0188C60C] ecx 重新装回分发器(this) 8B 42 04 mov eax,[edx+4] 虚表 slot1 FF D0 call eax 调用(被调方自清 3 个参数) C3 ret 独立执行时收尾;整段是纯代码,不用还栈 0x0188C60C 在机器码里是**立即数**(不是指针解引用):8B 0D 后面直接跟 disp32。 控件ID 位置用 `取字节集左边 (到字节集 (控件ID), 4)` 拼——正是你要的写法, 不要手工拆字节,也不要用 `到字节集 (到短整数 ())`。 六、坑(四条) -------------------------------------------------------------------------------- 1. **控件ID 千万别写死**。同机两轮实测:交易窗按钮 6700/6701 → 换客户端变 6703/6704; 组队窗 7034/7035 也是每次递增分配。一律从 `按钮对象 + 12` 现读(子程序③已经做了)。 2. **栈由被调方清**。游戏原文 0xCFB642 call 之后紧接 nop,没有 add esp —— 所以我们 不要在后面补 `add esp,0xC`。若你那边执行后偶发崩溃,再试补 `81 C4 0C 00 00`。 3. **走的是分发器直连**,跳过按钮自身的前置检查(enable/焦点等)。正常场景没问题; 若要完整走按钮原逻辑,就是 ecx=按钮对象、push 0 / push 0 / push 8 / call 0xCFB4B0。 4. **两个依赖的名字按你工程改**:本文件用的是 `机器码到字节集` 和 `调用字节集 (句柄, 字节集)`。 如果你工程里执行器另有名字(比如走 DLL 内部直接置入代码执行),只改子程序②这两行, ③④ 都不用动。 附:相关文件 D:\AI空间\rxjh\窗口弹出状态与遍历_0928.txt 点按钮机器码原始记录(第八节) D:\AI空间\rxjh\窗口槽位表_数据类型_0928.txt 窗口槽偏移(窗口.交易 = 1236) D:\AI空间\rxjh\分析报告汇总\按钮点击call_置入代码版.txt 0925 旧版(0x3F4 那条路,只对助手有效) D:\ce_mcp\_t40.py / _t41.py 本次活体实测脚本