释放技能分析
接着我们需要拿到释放技能的数据
首先来到明文发包call头部
释放技能让断点断下
此时第一次返回的call是选怪的封包call。所有的传奇类的游戏,都需要先选中怪物,然后在走近怪物,最后攻击
我们在人物打怪的中途断下,此时返回的第一层地址才是技能call。这一层的call被加密了。
我们需要返回到第二层。
| mov dword ptr ss:[rsp+0x30],eax | rsp+0x30 计数器
| movzx eax,byte ptr ss:[rsp+0x88] |
| mov byte ptr ss:[rsp+0x28],al | rsp+0x28 1
| mov rax,qword ptr ss:[rsp+0x80] |
| mov qword ptr ss:[rsp+0x20],rax | rsp+0x20 怪物坐标
| movzx r9d,dil | 技能等级
| movzx r8d,bx | 技能ID
| mov edx,esi | 怪物ID
| call 0x7FF7F9B75260 | 释放技能call
这个call的参数分析如上。技能call实际上就是普通攻击call
因为每个角色都自带一个F1的普通攻击,这个技能也是有技能ID的。
但是问题来了,这个call需要走近目标以后进行攻击才会断下,这就需要多调用一次走路call,那么这个call就不是我们要的call。
我们需要的是调用以后自动走到怪物面前,然后进行攻击。这样就得切换一下切入点了。
定位普通攻击call
我们可以从选中怪物的ID或者对象入手。因为如果要释放技能的话,选中怪物的ID和对象必定是要传入的参数,找到了选中怪物的ID或者对象就相当于找到了技能call
首先选中一个怪物,搜索未知的初始值
然后选另一个怪物,搜索变动的值。重复这个步骤
重点分析这一批绿色的地址
其中,有几个地址在未选中状态下,数值为0
然后选中时,数值又产生了变化
这里用十六进制显示,这几个地址存放的应该是怪物的对象。其中有一个地址存放的是选中的怪物对象
当我们选中以后,把其中一半的地址改为0,此时人物不再进行攻击,那么说明这三个地址有一个存放的是选中的怪物对象
最终通过修改数值,可以确定唯一的选中的怪物对象
然后在这个位置下一个写入断点,让断点断下
这里通用需要先F9步过一次,不然会返回到一个没有效果的选怪的call。需要让这个断点第二次断下的时候返回。
这个call就是我们要的攻击怪物的call,里面没有技能相关的参数。那么这个call应该是个普通攻击的call。
这个call在传入怪物对象以后,会自动走到对象周围,从某种程度上来说,已经解决了我们的问题。可以不需要调用寻路call了。
远程职业的话不需要这个call,因为直接释放技能就能打怪,不需要走到怪物身边。
数据整理
释放技能call
| 48:8BC8 | mov rcx,rax |
| 8B05 AC540902 | mov eax,dword ptr ds:[0x7FF6A22212D0] |
| 894424 30 | mov dword ptr ss:[rsp+0x30],eax |
| 0FB68424 88000000 | movzx eax,byte ptr ss:[rsp+0x88] |
| 884424 28 | mov byte ptr ss:[rsp+0x28],al |
| 48:8B8424 80000000 | mov rax,qword ptr ss:[rsp+0x80] |
| 48:894424 20 | mov qword ptr ss:[rsp+0x20],rax |
| 44:0FB6CF | movzx r9d,dil |
| 44:0FB7C3 | movzx r8d,bx |
| 8BD6 | mov edx,esi |
| E8 B091BFFF | call 0x7FF69FD85000 | 释放技能
普通攻击call
| 83C8 01 | or eax,0x1 |
| 8905 A4E5E301 | mov dword ptr ds:[0x7FF6A1FDC338],eax |
| 48:8D05 35D17E00 | lea rax,qword ptr ds:[0x7FF6A098AED0] |
| 48:8905 86E5E301 | mov qword ptr ds:[0x7FF6A1FDC328],rax |
| 48:8D0D 37D57000 | lea rcx,qword ptr ds:[0x7FF6A08AB2E0] |
| E8 724B4100 | call 0x7FF6A05B2920 | 普通攻击call
一共两个call,一个给近战职业用,一个给远程类职业用
代码编写
//释放技能
void Fn_UseSkill(_stuObj Monster, _stuObj Skill)
{
//计数器+=1
DWORD dwCount = ReadDword(g_GameAddr + UseSkillCount);
dwCount += 1;
//再释放技能
typedef void(*PFnUseSkill)(QWORD rcx, QWORD rdx, QWORD r8, QWORD r9, _stuPos* pos, QWORD one, QWORD count);
PFnUseSkill UserSkill = (PFnUseSkill)(g_GameAddr + UseSkillCall);
UserSkill(g_PublicRcx, Monster.m_ID, Skill.m_ID, Skill.m_Level, &Monster.m_Obj_Pos, 1, dwCount);
}
这里暂时只写一个释放技能call,剩下的普通攻击call各位可以自己去编写。到这里自动打怪所需要的数据就已经全部找齐了,下一篇文章我们开始编写自动打怪的代码。
Github:https://github.com/TonyChen56/GameReverseNote
完整代码:https://download.csdn.net/download/qq_38474570/79498815