22.第二阶段x64游戏实战-分析周围对象类型
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
本次游戏没法给
内容参考于:微尘网络安全
上一个内容:21.第二阶段x64游戏实战-分析采集物偏移
上一个内容里发现采集物的名字通过我们的列表展示出来是空的,然后又去找了采集物名字的偏移,这里就有一个问题,现在名字的偏移有两个npc(包含我们的角色、附近玩家、游戏中的npc)是一个偏移,采集物名字又是另外的偏移,什么时候用采集物的偏移什么时候用npc的偏移?游戏中它自身也会有这个问题,所以游戏中它肯定是有一个对象(对象是一个抽象的概念它指的可以是玩家、附近玩家、游戏中的npc、采集物等,它可以指任意一个东西,当前说的对象指的是玩家、附近玩家、游戏中的npc、采集物)类型,比如1表示我们的角色、2表示的是附近玩家、3表示游戏中的npc、4表示采集物等这样的数据,本次就来找这个类型是什么,找到类型之后,就可以通过类型判断什么时候用什么偏移了。
开始找类型,首先如下图红框,来到上一个内容得到采集物名字的位置
如下图红框,一设置断点它就会断下来,下图红框位置是采集物的名字,为什么会断下来?因为我的角色里采集物很近,游戏中访问采集物名字了
如下图,人物就在采集物旁边
接下来移动我们的角色,让它远离采集物,移动到采集物在地图上看不到了为止,如下图让角色远离采集物之后再次设置断点它就不会断下来了,当前位置前面的代码肯定有一行判断了类型,接下来就开始找远离了采集物在什么位置设置断点它还会断下来,为了防止有其它问题在开始之前记得把我们之前写的读取附近npc代码关闭
就算找在什么位置断下来也是有技巧的,下图rsi在上一个内容中看到的它是采集物对象,然后就找rsi在哪赋值的,通过找它来找断点位置
如下图通过往上滑看到还有位置读取了rsi+0x150位置,给他设置断点它也不会断,继续往上滑
通过不断的往上滑和设置断点,发现下图位置设置断点之后会断下来
然后在下图位置设置断点不会断下来
当移动到有矿石的位置它才会断下来
它断不断取决于下图红框位置的代码 test eax,eax,test和je配合意思是值是0的时候je会跳转
下图置位的意思是赋值,也就是把zf标识设置成1
然后eax的值来自于下图红框call,所以就要进入这个call
设置断点
取消断点
然后按F7进入call
代码的分析,大体就是判断rdx和r8是不是相等,rdx和r8应该就是类型了
如下图按减号(-号)回到上一层,可以看到r8来自于一个基址,rdx来自于rax
r8的值是一个字符串这个字符串Resource应该就是采集物的类型了,Resource中文是资源的意思
然后找一下rax的值哪来的,如下图红框,rax的值来自于call rax+0x70这个函数
通过分析下图红框的三行代码,可以知道这个应该rax+0x70是一个对象的虚函数(下面就写虚函数在内存中的样子和介绍)
设置断点
取消断点
按F7进入call rax + 0x70,如下图它就一行代码
然后跳转到 0x00007FF6BE5253E0 这个地址 MouseTarget中文意思鼠标目标
然后移动角色到采集物位置,继续在下图红框位置设置断点,这个游戏如果不移动到采集物附近,rax的值不会变
如下图断点查看当前rax的值
如下图打开我们的遍历周围列表,可以发现rcx是对象地址
通过上面的分析知道了,对象继承了一个通用类,虚函数就是父类中的函数,这是C++中的概念,不了解C++只要记得上面三行的写法就是调用虚函数(见多了就记住了),虚函数有多个对象首地址存放的就是虚函数列表,rax+0x70虚函数用来得到当前对象的类型,下面手动找虚函数,查看它的类型,下图红框是一个怪物的对象
跳转到对象位置
然后在内存窗口中跳转过去
然后右击选择地址
然后偏移0x70位置
右击选择在反汇编中转到指定QWORD
如下图跳转之后
然后复制 lea rax, ds:[0x00007FF6BE524FC8]中 0x00007FF6BE524FC8这个地址,这个怪物的类型是PlayerNPC
再看一个我们自己 PlayerMySelf,MySelf中文我自己的意思
现在就找到类型了,然后开始计算 lea rax, ds:[0x00007FF6BE524EA8] 里面的0x00007FF6BE524EA8怎么取,去它要用到下图红框的两个东西,下一行代码的地址和当前代码+0x3位置
为什么是当前代码+0x3位置,如下图
计算公式 A1 09 82 00 这是小端序存储用的时候要反着写(我们自己用的Windows系统一般都是小端序存储)
总结:
现在我们要取类型的话,首先获取对象,然后获取虚函数表,然后得到0x70位置的虚函数,然后取加0x3位置的数据就得到了A1 09 82 00,然后在取+0x7位置也就是下一行代码的地址,然后让它俩相加就得到了类型
开始写代码,下图红框位置是列表显示类型的代码
然后获取名字的代码
VDataStu GameData::TraverseBN(QWORD point) {// [[[[ReadDword64(B_number + 8) + 0x10]+ 0x28] + 0x1B0] + 0x18] + 0x8/**判断是否选中(或者说找到目标的标记)*/if (!ReadByte(point + 0x19)) {Role_Stu Rstu;Rstu.m_Objadr = ReadDword64(point + 0x28);// 获取树中的数据/*Rstu.m_ID = ReadInt(Rstu.m_Objadr + 0x44);*//**ReadDword64(Rstu.m_Objadr) + 0x70) 得到虚函数ReadDword64(ReadDword64(Rstu.m_Objadr) + 0x70) + 3) 得到虚函数+0x3位置的数据,也就是A1 09 82 00ReadDword64(ReadDword64(Rstu.m_Objadr) + 0x70) + 7) 这个是得到下一行代码*/QWORD address = ReadDword64(ReadDword(ReadDword64(ReadDword64(Rstu.m_Objadr) + 0x70) + 3) + ReadDword64(ReadDword64(Rstu.m_Objadr) + 0x70) + 7);Rstu.type_name = ReadStrA((char*)address);QWORD addr = ReadDword64(ReadDword64(Rstu.m_Objadr + 0x1b0) + 0x18);Rstu.m_HP.ptr = ReadFloat(addr + 8) * 100;// 判断 type_name字符串是不是以Resource结尾,如果是就说明当前是采集物if (Rstu.type_name.find("Resource") != string::npos)// 得到采集物名字Rstu.m_Name = ReadStrA((char*)(ReadDword64(ReadDword64(Rstu.m_Objadr + 0x150) + 0x8)));elseRstu.m_Name = ReadStrA((char*)(addr + 0x30));Rstu.fPos.x = ReadFloat(Rstu.m_Objadr + 0x5c);Rstu.fPos.z = ReadFloat(Rstu.m_Objadr + 0x60);Rstu.fPos.y = ReadFloat(Rstu.m_Objadr + 0x64);m_Around.m_data.push_back(Rstu);TraverseBN(ReadDword64(point));// 遍历左边的树TraverseBN(ReadDword64(point + 0x10));// 遍历右边的树}return m_Around;
}
效果图: