一、环境与工具链
目标进程:魔域老端客户端(32位,非手游口袋版)
调试主机:Windows,管理员权限运行
核心工具:
• Cheat Engine 7.4+:内存扫描、数值变更筛选、访问断点
- x32dbg / OllyDbg 1.10:汇编级单步、堆栈观测、Call下断
• IDA Pro 7.5+:静态反编译、字符串交叉引用、函数边界还原
• Process Monitor:过滤客户端读写注册表/文件行为
- ScyllaHide插件:绕过IsDebuggerPresent等反调试检测
二、定位魔石商店购买Call(行为触发+参数回溯法)
1. 触发行为埋点
进游戏打开魔石商店→买1个低级魔石物品→记录扣减的魔石数→再买不同物品→记录新扣减值。CE附加game.exe,首次搜“未知初始值”,后续按“数值减少”“数值增加”反复过滤,锁定魔石余额地址。对该地址下“访问断点”,回到游戏再点一次购买,CPU断在商店扣费代码区。
2. 回溯调用栈到购买Call
断点命中后,在x32dbg调用栈窗向上翻帧,找到UI按钮消息派发后的业务函数,典型落地地址:
0063C133 push eax ; 魔石类型(普通魔石/绑定魔石等)
0063C134 push 0x0 ; 固定保留参
0063C136 push esi ; 购买数量
0063C137 push edi ; 物品ID
0063C138 mov ecx, 0x8FC440 ; 商店/背包对象指针(thiscall)
0063C13D call 0x6F29F0 ; 实际购买处理函数
调用约定:thiscall,ecx传对象,参数自右向左压栈。外部注入调用时需严格按此顺序压栈并保持堆栈平衡(被调函数内ret 0x10则外层无需额外add esp)。
3. 参数取值来源核验
• eax(魔石类型):从商店数据块反查,普通魔石=1、绑定魔石=2,依版本而异
• edi(物品ID):取自当前选中物品对象+0x0C字段
• esi(数量):UI输入框值,需做上限截断,避免服务端校验回滚
- ecx(对象指针):全局静态对象 0x8FC440,多版本稳定
三、魔石商店物品数据结构还原
1. 基地址与层级指针链
商店管理对象基址(老端常见):0x00922780
内存访问链:
• 物品总数: [0x00922780 + 0x4C] → DWORD
- 物品数组头指针: [[0x00922780 + 0x24] + 0x8] → 指向物品对象指针数组
• 第i个物品对象地址: [[[0x00922780 + 0x24] + 0x8] + i*4] → 解引用得对象基址
2. 单物品对象(GameItem)关键偏移
相对物品对象基址:
+0x0C DWORD 物品ID(与服务端itemtype表一致)
+0x25C DWORD 单价(消耗魔石数)
+0x34 DWORD 库存状态(0=可购,非0=缺货/限购)
+0x280 CHAR* 物品名称指针(加密,需走客户端解密Call还原宽字符)
对象尺寸:0x260 字节左右,遍历步长按此累加
3. 遍历公式(伪代码)
base = 0x922780
count = ReadDword(base + 0x4C)
arrPtr = ReadDword(ReadDword(base + 0x24) + 0x8)
for i in 0..count-1:
obj = ReadDword(arrPtr + i*4)
id = ReadDword(obj + 0x0C)
price = ReadDword(obj + 0x25C)
namePtr = ReadDword(obj + 0x280)
name = DecryptName(namePtr) ; 复用客户端明文解密Call
4. 汇编层遍历片段(地址示例 00731197 附近)
mov ecx, 0x922780
lea ebx, [ecx+0x24]
mov edx, ebx
mov eax, [edx+0x8]
lea edx, [eax+edi*4]
mov eax, [edx]
mov edi, [eax+0x0C] ; 取出物品ID供后续购买Call使用
四、版本差异与偏移校验
• ASLR/更新导致基址漂移:0x922780、0x8FC440 仅特定补丁版本固定,新端需用CE“指针扫描”重新找基址或提取特征码(如 8B 4D ? 8B 81 ? ? ? ? 结合字符串“魔石商店”交叉定位)
- 物品结构字段随资料片扩张:+0x25C价格字段在部分版本移至+0x268或合并进附属经济结构,需买不同价位物品做差分扫描确认
• 名称加密:直接读+0x280为密文,必须定位客户端内部 DecodeItemName 函数统一调用,禁止自行写解密表
五、完整调用封装参考(32位裸调用)
void CallBuyItem(DWORD itemId, DWORD qty, DWORD crystalType) {
__asm {
push crystalType ; eax等效(实际编译期置eax也可)
push 0x0
push qty
push itemId
mov ecx, 0x8FC440
call 0x6F29F0
add esp, 0x10 ; 若内部未清栈则外层平衡
}
}
调用前建议保存ebx/esi/edi,调用后恢复;正式发包前务必确认服务端限购/魔石余额校验,避免产生异常事务。
六、通用逆向四步法沉淀
1. 行为监控:买/不买、数量变更、魔石扣减,锁定变更地址
2. 数据回溯:访问断点→调用栈→UI消息派发→业务Call
3. 结构解析:基址→偏移→对象步长→字段语义(ID/价格/库存/名称)
4. 公式验证:改内存数量、伪造ID调用、观察服务端回包与客户端刷新一致性
上面这套流程覆盖从CE扫值、x32dbg断点回溯购买Call、基址链还原物品结构到封装调用的完整链路,偏移地址以老端 0x922780 / 0x8FC440 / Call 0x6F29F0 为基准,换版本按特征码重定位即可。
目标进程:魔域老端客户端(32位,非手游口袋版)
调试主机:Windows,管理员权限运行
核心工具:
• Cheat Engine 7.4+:内存扫描、数值变更筛选、访问断点
- x32dbg / OllyDbg 1.10:汇编级单步、堆栈观测、Call下断
• IDA Pro 7.5+:静态反编译、字符串交叉引用、函数边界还原
• Process Monitor:过滤客户端读写注册表/文件行为
- ScyllaHide插件:绕过IsDebuggerPresent等反调试检测
二、定位魔石商店购买Call(行为触发+参数回溯法)
1. 触发行为埋点
进游戏打开魔石商店→买1个低级魔石物品→记录扣减的魔石数→再买不同物品→记录新扣减值。CE附加game.exe,首次搜“未知初始值”,后续按“数值减少”“数值增加”反复过滤,锁定魔石余额地址。对该地址下“访问断点”,回到游戏再点一次购买,CPU断在商店扣费代码区。
2. 回溯调用栈到购买Call
断点命中后,在x32dbg调用栈窗向上翻帧,找到UI按钮消息派发后的业务函数,典型落地地址:
0063C133 push eax ; 魔石类型(普通魔石/绑定魔石等)
0063C134 push 0x0 ; 固定保留参
0063C136 push esi ; 购买数量
0063C137 push edi ; 物品ID
0063C138 mov ecx, 0x8FC440 ; 商店/背包对象指针(thiscall)
0063C13D call 0x6F29F0 ; 实际购买处理函数
调用约定:thiscall,ecx传对象,参数自右向左压栈。外部注入调用时需严格按此顺序压栈并保持堆栈平衡(被调函数内ret 0x10则外层无需额外add esp)。
3. 参数取值来源核验
• eax(魔石类型):从商店数据块反查,普通魔石=1、绑定魔石=2,依版本而异
• edi(物品ID):取自当前选中物品对象+0x0C字段
• esi(数量):UI输入框值,需做上限截断,避免服务端校验回滚
- ecx(对象指针):全局静态对象 0x8FC440,多版本稳定
三、魔石商店物品数据结构还原
1. 基地址与层级指针链
商店管理对象基址(老端常见):0x00922780
内存访问链:
• 物品总数: [0x00922780 + 0x4C] → DWORD
- 物品数组头指针: [[0x00922780 + 0x24] + 0x8] → 指向物品对象指针数组
• 第i个物品对象地址: [[[0x00922780 + 0x24] + 0x8] + i*4] → 解引用得对象基址
2. 单物品对象(GameItem)关键偏移
相对物品对象基址:
+0x0C DWORD 物品ID(与服务端itemtype表一致)
+0x25C DWORD 单价(消耗魔石数)
+0x34 DWORD 库存状态(0=可购,非0=缺货/限购)
+0x280 CHAR* 物品名称指针(加密,需走客户端解密Call还原宽字符)
对象尺寸:0x260 字节左右,遍历步长按此累加
3. 遍历公式(伪代码)
base = 0x922780
count = ReadDword(base + 0x4C)
arrPtr = ReadDword(ReadDword(base + 0x24) + 0x8)
for i in 0..count-1:
obj = ReadDword(arrPtr + i*4)
id = ReadDword(obj + 0x0C)
price = ReadDword(obj + 0x25C)
namePtr = ReadDword(obj + 0x280)
name = DecryptName(namePtr) ; 复用客户端明文解密Call
4. 汇编层遍历片段(地址示例 00731197 附近)
mov ecx, 0x922780
lea ebx, [ecx+0x24]
mov edx, ebx
mov eax, [edx+0x8]
lea edx, [eax+edi*4]
mov eax, [edx]
mov edi, [eax+0x0C] ; 取出物品ID供后续购买Call使用
四、版本差异与偏移校验
• ASLR/更新导致基址漂移:0x922780、0x8FC440 仅特定补丁版本固定,新端需用CE“指针扫描”重新找基址或提取特征码(如 8B 4D ? 8B 81 ? ? ? ? 结合字符串“魔石商店”交叉定位)
- 物品结构字段随资料片扩张:+0x25C价格字段在部分版本移至+0x268或合并进附属经济结构,需买不同价位物品做差分扫描确认
• 名称加密:直接读+0x280为密文,必须定位客户端内部 DecodeItemName 函数统一调用,禁止自行写解密表
五、完整调用封装参考(32位裸调用)
void CallBuyItem(DWORD itemId, DWORD qty, DWORD crystalType) {
__asm {
push crystalType ; eax等效(实际编译期置eax也可)
push 0x0
push qty
push itemId
mov ecx, 0x8FC440
call 0x6F29F0
add esp, 0x10 ; 若内部未清栈则外层平衡
}
}
调用前建议保存ebx/esi/edi,调用后恢复;正式发包前务必确认服务端限购/魔石余额校验,避免产生异常事务。
六、通用逆向四步法沉淀
1. 行为监控:买/不买、数量变更、魔石扣减,锁定变更地址
2. 数据回溯:访问断点→调用栈→UI消息派发→业务Call
3. 结构解析:基址→偏移→对象步长→字段语义(ID/价格/库存/名称)
4. 公式验证:改内存数量、伪造ID调用、观察服务端回包与客户端刷新一致性
上面这套流程覆盖从CE扫值、x32dbg断点回溯购买Call、基址链还原物品结构到封装调用的完整链路,偏移地址以老端 0x922780 / 0x8FC440 / Call 0x6F29F0 为基准,换版本按特征码重定位即可。

