需求:读取游戏玩家的重要属性,血量/最大血量 魔法/最大魔法 经验 等级 技能点灯 并且显示在自动化助手的UI界面中。
拆解:
一、分析游戏角色数据,并且还原成C++类
二、分析并且锁定游戏玩家基址
角色数据的分析与C++还原:
这个类头就是在1DF3E034
获取基址:
1、爆搜
直接搜这个地址(看运气)
发现两个全局的地址
发现有sro_client.exe+C37D3C地址可以随着登出置零,登入来变成基址,可以确定这就是人物的一个基址
2、通过蓝量找
人物的结构体和怪物的结构体很有可能是同样的一个结构体,不是同一个类那也是同一个基类,或者是兄弟类,这就表达了他们在内存中布局的方式是差不多的,这个时候用MP的话,MP是通用属性,怪物和人都有这个属性,如果是都有的属性,通过它去找基址实际上是不太好的,因为游戏中处理这个数据一般都是用的通用方法,一定会有非常复杂的方法来自动获得两者的对象,面对这种通用的属性的情况只有UI的显示这一种例外。虽然怪物的属性都有,但不太可能显示,人物的蓝量是要显示的。作为游戏的设计者肯定是要用最快最便捷的方法来获得蓝量显示到UI界面上,这个时候我们不要去找写入,而是去找访问
一定能找到UI访问的,先随便看一个
直接看就瞬间找到了基址
一般在不同游戏里情况都不一样,但都不会太复杂,一般都是可以轻松的找到,因为游戏开发的时候也不会用很复杂的形式去处理UI显示
要找独立出现的点
3、找写入(错误的思想)
找写入然后用x32dbg
进入函数
每一段CMP进入那个函数都是断头函数,处理异常的, 1037D4C应该是记录了末端,上面函数也就是搜索,就没必要看了
这是个典型的遍历链表结构
看函数堆栈
这个函数是可以得到edi的
传一个指针进去就会写成一个想要的东西,
这又要知道ebx
往前跳
再往上就不好追了 ,这个函数没碰就瞬间触发
推断游戏应该就是通过这个函数来更新人物数据的
测试发现 这个地方是用来同步数据的操作 就没必要追了
前面一定有一种用来管理数据组织的过程 一定很麻烦
往前追,发现就是一个常断的函数了 ,一直追肯定是可以追到基址,但很麻烦。
像这种人物怪物通用的方法就很麻烦,向分析这类问题,就要想好切入点,出现的地方越少就越有利
独一无二的东西还有 人物属性 技能点等等,一般游戏不会为这些设计独立的方法,因为怪物或者其他对象本身可能没有往往不会为这些设计独立的方法,这个地方就很容易把基址给暴露出来
通过技能点一找访问直接找到了
再看写入
会发现写入的情况下,也使用了基址
所以要越独特越好
再试一个经验
也是很容易直接就看到了
看看经验的写入
这个稍微复杂了一点。
数据还原:
#pragma once
#include"SRO_String.h"
//角色怪物对象
typedef class AIM
{
int unknownB1[68];//unknown
public:
SRO_String Name;//角色名字
protected:
int unknownB43[223];//unknownl
public:
int MP;//蓝量
int MaxHP;//最大血量
int MaxMP; //最大蓝量
int HP;//血量
protected:
int unknownB10[152];//
public:
float x;//X坐标
float h;//H坐标
float y;//Y坐标
protected:
int unknownB28[107];//
public:
char Lv;//等级
protected:
char unLv[3];
int unknownB13;//
public:
int Exp;//经验值
protected:
int unknownB16[3];///
public:
int SkillPoint;//技能点数
protected:
char UnUSERage;//怒气
char UnknownH33;//
public:
char Rage;//怒气值
char RageEx;//怒气值副本
protected:
int unknowmN31[452];//unknown
}*PAIM;
在初始化地方绑定对应地址就可以实现读取了