现在我们已经有了人物的数据和周围对象的数据,还差一个技能数据和释放技能call,就可以完成自动打怪的功能。
接着我们来找技能遍历的数据
技能遍历
以技能的当前经验为突破口
直接搜索当前经验值
筛选得到唯一的一个数值
下访问断点,让断点断下,此时[rax]就是我们要追的数值
[rax]
rax来源于上面的call
这个call传入了一个技能ID和一个基地址作为参数,我们进入call内追rax的来源
经过这三个步骤rax变成了
[rax*10+rdx+4]
这里我们要追一个rdx
技能名字
直接搜索技能名字,把没有后缀的拿下来
然后确定唯一的一个地址
下访问断点,重新打开技能栏
返回上层,这里要追的是[r15+0x38C]
[r15+0x38C]
r15来源于rax,来源于[r12]
[[r12]+0x38C]
r12来源于rcx+0x4
[[rcx+0x4]+0x38C]
rcx来源于r8+rax*4
[[r8+rax*4+0x4]+0x38C]
继续追r8
r8来源于[rbp+0x130]
[rbp+0x130]是一个数组,我们来追踪这个数组的来源
往上找引用[rbp+0x130]的位置,发现这里会往这个地址循环写入值
每一次进来都会写入一个,进入call内单步跟踪,看他什么时候往里面写值
当代码执行到这里将对象写入到数组,这里要追rax来源,rax来源于[rbp+0x4]
rbp来源于r8
r8是上层call的参数,也就是eax和rdx
然后根据这一段代码整理
ID=r8+14*n
r8来源于[rax],来源于[rsp+0x28],而rax此时是一个基地址,继续往上找到来源
[rsp+0x28]来源于rbx
rbx来源于rax
rax来源于rcx+0x1E8
rcx来源于基地址
数据整理
偏移:0x2F783A4
[0x00007FF7035083A4+0x1E8]+n*14 技能ID
[0x00007FF7035083A4+0x1E8]+n*14+4 技能对象
[[[0x00007FF7035083A4+0x1E8]+n*14+4]+0x38C]+0 技能名字
[[0x00007FF7035083A4+0x1E8]+n*14+4]+0x74 技能等级 DWORD
[[0x00007FF7035083A4+0x1E8]+n*14+4]+0x3E0 技能最大等级 DWORD
代码编写
接着来编写技能遍历的代码,同样在stu.h中新增技能对象相关的字段
//--------------------------------技能------------------------------------------------
BOOL m_Skill_Level; //技能等级
BOOL m_Skill_MaxLevel; //最大等级
接着在GameData.h中新增一个函数
//技能遍历
_stuObjs GetSkillData();
数组的遍历相对来说比较简单,只需要一个函数即可。
//技能遍历
_stuObjs GetSkillData()
{
_stuObjs skilllist;
//技能数组首地址
QWORD qSkillArr = ReadQword(g_GameAddr + SkillArray + 0x1E8);
//数组大小
DWORD dwSize = ReadDword(g_GameAddr + SkillArray + 0x1E8 + 0x8);
//开始遍历
for (unsigned int i = 0; i < dwSize; i++)
{
_stuObj skill;
skill.m_StuType = Em_Skill;
//ID
skill.m_ID = ReadDword(qSkillArr + i * 0x14);
//对象
skill.m_Obj = ReadDword(qSkillArr + i * 0x14 + 0x4);
//等级
skill.m_Skill_Level = ReadDword(skill.m_Obj + 0x74);
//最大等级
skill.m_Skill_MaxLevel = ReadDword(skill.m_Obj + 0x3E0);
//名字
DWORD nameaddr = ReadDword(skill.m_Obj + 0x38C);
skill.m_Name = ReadWChar(nameaddr);
//保存
skilllist.m_data.push_back(skill);
}
return skilllist;
}
然后输出技能相关的信息
//输出技能信息
case Em_Skill:
{
__OutputDebugStringW(L"对象:%x ID:%x 名字:%s 等级:%d 最大等级:%d"
, m_Obj, m_ID, m_Name.c_str(), m_Skill_Level, m_Skill_MaxLevel);
}
break;
实际效果如图:
下一篇文章我们来找明文封包call。
Github:https://github.com/TonyChen56/GameReverseNote
完整代码:https://download.csdn.net/download/qq_38474570/79498815