接着我们来开始找游戏数据,以编写自动打怪为目标,找齐这个功能所需要的所有的数据。第一个需要用到的数据就是角色属性。
角色血量基址查找
直接搜索当前人物的血值
修改血值之后再次扫描
接着我们修改第二个数值,然后打开角色属性。发现第一个数值也会跟着修改,说明第一个数值的来源是第二个地址
对这个地址下内存访问断点,断点断下。[rax+0x1AC]是人物血量,这里就可以获得人物血量的两层偏移
[[rsi+0x65C]+0x1AC]
这里继续追rsi的来源
rsi来源于rdx
rdx来源于rbx
rbx来源于rax
进入函数发现rax来源于rbx
rbx来源于rcx
rcx来源于[rcx+0x248]
[[[rcx+0x248]+0x65C]+0x1AC]
再上层有个0x71C的偏移
[[[[rcx+0x71C]+0x248]+0x65C]+0x1AC]
rcx来源于rax,继续跟上面的call
最后可以追到一个基地址
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x1AC]
分析角色其他属性
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x1AC] 血量
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x1B0] 最大血量
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x1B4] 蓝量
[[[0x00007FF77C447050+0x71C]+0x248]+0x638] 角色名字
分析角色名字
直接扫描角色名字
把不带后缀的地址全部拉下来,里面有一个绿色的基地址,可以直接用这个基地址
通过修改的方式 确定人物名称的地址
对这个地址下硬件访问断点,断点断下,返回上层
发现这里是调用的系统的函数,接着在这个函数重新下断点
此时[rdi+0x638]是我们要追的地址
rdi来源于rcx
rcx来源于rsi
rsi来源于rax,而rax上面的call就是我们追血量时经过的第一个call。那么角色名字就等于
[[[0x00007FF77C447050+0x71C]+0x248]+0x638]
搜索角色坐标
我们在人物的对象下面并没有找到角色的坐标,那么说明这个坐标有可能是加密了。最快的方法是找到发包函数,然后通过发包函数找到走路call,看走路call传入的参数是什么
这里用CE的话用未知初始值和变动的值去扫描,这里过程省略
[[[0x00007FF77C447050+0x71C]+0x248]+80] X坐标
[[[0x00007FF77C447050+0x71C]+0x248]+84] Y坐标
[[[0x00007FF77C447050+0x71C]+0x248]+88] Z坐标
数据整理
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x1AC] 血量
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x1B0] 最大血量
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x1B4] 蓝量
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x13C] 等级
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x14C] 经验
[[[[0x00007FF77C447050+0x71C]+0x248]+0x65C]+0x150] 最大经验
[[[0x00007FF77C447050+0x71C]+0x248]+0x638] 角色名字
[[[0x00007FF77C447050+0x71C]+0x248]+80] X坐标
[[[0x00007FF77C447050+0x71C]+0x248]+84] Y坐标
[[[0x00007FF77C447050+0x71C]+0x248]+88] Z坐标
偏移:"MMOGame-Win64-Shipping.exe"+0x2F67050
代码编写
首先在_stuObj中添加人物相关的属性
struct _stuObj
{
int m_StuType; //0 人物
//--------------------------------公用------------------------------------------------
wstring m_Name; //名字
DWORD m_Obj; //对象
DWORD m_ID; //ID
//--------------------------------角色------------------------------------------------
DWORD m_Level; //等级
DWORD m_Exp; //经验
DWORD m_MaxExp; //最大经验
}
然后在GameData.h
中添加一个函数声明
//人物属性
_stuObj GetRoleData();
函数实现如下:
//获取角色属性
_stuObj GetRoleData()
{
_stuObj selfdata;
selfdata.m_StuType = Em_Role;
//坐标
selfdata.m_Obj_Pos.x = ReadFloat(g_SelfObj + 0x80);
selfdata.m_Obj_Pos.y = ReadFloat(g_SelfObj + 0x84);
selfdata.m_Obj_Pos.z = ReadFloat(g_SelfObj + 0x88);
//名字
DWORD nameAddr = ReadDword(g_SelfObj + 0x638);
selfdata.m_Name = ReadWChar(nameAddr);
//属性
DWORD dwAttrBase = ReadDword(g_SelfObj + 0x65C);
selfdata.m_Obj_HP = ReadDword(dwAttrBase + 0x1AC);
selfdata.m_Obj_MaxHP = ReadDword(dwAttrBase + 0x1B0);
selfdata.m_Obj_MP = ReadDword(dwAttrBase + 0x1B4);
selfdata.m_Level = ReadDword(dwAttrBase + 0x13C);
selfdata.m_Exp = ReadDword(dwAttrBase + 0x14C);
selfdata.m_MaxExp = ReadDword(dwAttrBase + 0x150);
return selfdata;
}
接着输出一下人物信息
//输出角色信息
case Em_Role:
{
__OutputDebugStringW(L"名字:%s 等级:%d 血量:%d 最大血量:%d 蓝量:%d 当前经验:%d 升级经验:%d 坐标(x:%f y:%f z:%f)"
, m_Name.c_str(), m_Level, m_Obj_HP, m_Obj_MaxHP, m_Obj_MP, m_Exp, m_MaxExp, m_Obj_Pos.x, m_Obj_Pos.y, m_Obj_Pos.z);
}
break;
然后在MFC的主窗口界面中添加一个按钮响应事件
//人物数据
void MainWnd::OnBnClickedButton1()
{
GetRoleData().OutputDebugInfo();
}
按钮事件输出人物数据相关信息。实际效果如图:
这样我们就取到了人物相关的数据了。下一篇文章我们来找周围遍历相关的数据。
Github:https://github.com/TonyChen56/GameReverseNote
完整代码:https://download.csdn.net/download/qq_38474570/79498815