上一篇文章里,我们对技能名字进行分析,得到了一个数组,但是经过观察,我们发现这个数组是一个临时存放的数组,只有当技能栏打开时才会显示当前一栏的技能,这样我们在获取所有技能信息时是很不方便的,所以下面我们要换一个方向找到真正的技能库遍历。
在之前找到的数组上下F2断点,并获取同一个技能的对象,我们会发现及时从新打开技能栏,获取到的技能对象都是一样的,只是存放的地址发生了变化。所以我们可以通过CE对这个地址进行扫描,并通过重新打开技能栏来过滤掉临时存放的地址。
分别在数据窗口中观察这两个地址,我们发现第二个地址的结构和临时存放的结构是一样的,也就是说这里就是拷贝到临时地址中的数组。
在这个地址上下硬件访问断点,切换技能栏,游戏断到一个新的数组位置
这里同样是一个+8偏移和一个*8+4的数组,在ebx+8处下断,观察断下的值,发现里面的内容是固定不变的,重新打开技能栏,ebx也不会重新生成。
我们继续分析ebx的来源,在上面可以得到来源于一个CALL的返回eax
在CALL上下断,分析参数,发现这个CALL只有一个参数,而参数的值很可能就是技能类型的ID
到CALL里面分析eax的来源,得到来源于局部变量[ebp-10],而这个局部变量在上面的以结构体参数传到CALL中并被赋值。
到CALL 9CB360中分析来源,首先得到一个+10偏移
继续向上分析,可以得到一个数组套链表的结构
对代码的走向进行分析,可以得出数组套链表的节点+8中存放的数值会反复的与外面传入的技能类型ID进行比较,所以说这个位置存放的就是技能类型ID
执行到返回后,继续分析ecx的来源,可以得到+20偏移
再次执行到返回可以得到基地址
既然得到了基地址,是不是说我们的分析结束了呢?
不!还没有结束!
我们对数组套链表的结构进行观察后发现,这里的元素和节点实在是太多了,我们需要继续分析传入到CALL中的技能类型ID的来源
我们执行到返回后得到了一个数组
直接用CE扫描这个ebx可以得到一个基地址
接下来分析数组里面元素,可以得出以下公式
[[0x047CECD0]+138]+n*4+4 技能类型
n=1为1转类型ID
n=2为2转类型ID
n=3为3转类型ID
n=4为4转类型ID
而0转的技能类型所有职业都为0
可以说,追到这里我们就已经可以通过关联来获取到完整遍历了,但是如果为了提高一点代码效率,我们可以用技能类型ID经过加密计算获取到数组套链表的数组下标,这部分代码就在数组套链表的上面
[[[0x03CF46D8]+20+4]+(G%[[0x03CF46D8]+20+8])*4]+4 链表
最终我们可以整理出公式如下
技能类型ID 循环右移5 得到结果为G
[[[0x03CF46D8]+20+4]+(G%[[0x03CF46D8]+20+8])*4]+8 ID
[[[0x03CF46D8]+20+4]+(G%[[0x03CF46D8]+20+8])*4]+10 对象
链表+4 链表
链表+8 ID
链表+10 对象
当ID==技能类型ID时,取出对象
==========================
[对象+8]-4 技能数组元素数量
[[对象+8]+n*8+4]+4 技能ID
[[[对象+8]+n*8+4]+8]+0 技能名字ASCII
完整的技能遍历已经获取到了,虽然有些复杂,不过成就感是慢慢的,当然如果有小伙伴觉得这个办法太麻烦,也可以用传参调CALL的方式获取,被检测到了不要哭哦。
从安全角度来讲,这个部分正向代码的设计其实要比逆向麻烦很多,如果能在关键的CALL中加入弱VM并加入本地检测,会得到意想不到的效果。
玩游戏,学逆向,做安全,欢迎感兴趣的小伙伴关注我们。