免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
如果看不懂、不知道现在做的什么,那就跟着做完看效果,代码看不懂是正常的,只要会抄就行,抄着抄着就能懂了
内容参考于:易道云信息技术研究院
上一个内容:88.利用游戏中的函数实现技能显示
上一个内容中把逆向得到的函数用c++实现了并且可以正常获取中文名,本次分析一下游戏中使用的哈希算法,看看游戏中是怎样去做的,分析哈算算法的目的,上一个内容中做了逆向的封装(通过调用游戏函数得到中文名),找名字的这件事就是一个查表的事情,哈希就是解决查表的事情,查询的速度快,其它游戏也肯定会跟哈希这种结构类似,到时候看一眼应该就能知道。
然后在解析怪物信息的时候有一个怪物列表,怪物里面都有一个编号(那个数据包开头部分8字节的数字)
我们用了0x200大小的数组来存放怪物的信息,如下图
然后如果要去更新怪物的属性,该怎么去更新?要遍历这个数组,去找要更新的怪物,找到之后把怪物的指针返回出来,然后对返回的指针进行修改,这有什么问题吗?没有什么问题,如果怪物列表只是0x200的大小是没有任何问题的,但如果怪物列表有两千万个怪物,一次查找最大要两千万次,光循环就两千万次,这就查询的会很慢,然后为了查询的速度更快,就有了一系列的算法,比如说二分算法,二分算法有一个缺点,它需要排序,它查的效率也没有特别高,但比线性(无算法)的查法快很多,然后哈希,哈希首先有一个哈希表,其实就是个数组,既然是数组它就会有大小,实际使用时它会很大,要比质数大,然后有了这个哈希表之后怎样查的更快呢?比如数组大小是10,然后现在的id是1,这时用1/10取余数,1/10余数是1,然后这时就把数据放到数组的1位置,2、3、4、5。。。也都是同理,然后哈希表就这么点东西数据那么多,这怎么放的进去?这肯定会有冲突,如果哈希表大小是质数的情况下,就有发生冲突的可能性,这是数学上的一个特性,然后有冲突的时候,就让数组的值是一个链表结构,比如1会放到数组下标1位置11也会放到数组下标1位置,但是这个11是存放在1数据里的,这是散列哈希,如下图
这个哈希跟查技能有什么关系?技能找名字也是一个查表的操作,游戏中的语言包它可能涉及到几千几万条,如下图根据一个英文去找一个中文
这时如果给一个英文,然后去查表,这时没法用哈希,如上图全是字符串不是数字了,哈希是找的东西/数组长度,这时就要用到一个东西了,哈希函数,就是把字符串转换成一个数字,哈希函数怎么设计?只要满足字符串转数字就可以,哈希函数自己实现就各种各样的都有,但时有一个优劣的问题,就是哈希表是一个有限的大小,但是字符串的组合是无限的,这种无限的变成有限的肯定会发生冲突,这时候就会变成数组里面存链表,然后用到链表了查询速度就会又低了,所以哈希函数,如果给1000个字符串做出来的算法,最好不同的字符串能取得不同的结果,虽然达不到,但是尽量不用冲突,冲突率越低算法就会越高效,下图一个简单的哈希函数(乘法哈希)我感觉可以用哈希套哈希的结构,下图中31*hash+id[i]这里面的31可以是任意数字
现在了解了哈希,然后开始看游戏是中的哈希是怎样的
首先来到下图位置,下图位置是获取中文名字的位置
然后下图红框里的函数,给它一个字符串,它会返回一个数字,通过上方的哈希说明,可以大胆的猜测它是哈希函数
通过断点可以看出它给了函数一个字符串
0x10295FB0函数执行完得到一个数字
然后断点住,按f7进入这个函数开始分析
首先从中文表结构+0x8C位置取出一个数字
然后比较中文表结构+0x29与0的关系
如果不是0就返回-1
然后把参数(也就是英文id)放到了edx里
然后再ebx+0x88(中文表+0x88位置)位置取出了一个数字,这个数字可能是哈希表的大小
然后调用 0x10293E50 函数
然后按F7进入 0x10293E50 函数
然后取出字符串第一个字符,cl寄存器是1字节的
然后一个if判断,如果是0就返回,这意思是,字符串以0结尾,这就说明如果是0说明这个字符串没有内容,是个空的
然后有一个乘法运算,这个算法跟上面简单的哈希函数例子一样
然后查了一个表
通过阿斯克码表,可以看出这个0x103C21E0这个表是为了大小写问题,如果是大写也变成小写
然后看到下图位置之后,就能明白它实际上跟上方我们的简单例子是一样的操作
通过下图红框里的两行代码,完成了 hash=31*hash+id[i]这样的代码
然后搞完一遍如果有字符串继续循环
然后现在知道了游戏哈希函数的逻辑,然后在看它插入哈希表的操作,然后在ret位置打断点跳出哈希函数
取消断点
然后按F8,如下图可以看到eax得到的值,这种数字被称为哈希值
然后有了哈希值就该去用哈希值/哈希表大小了,看到下图有div这个指令,div ecx这个意思是eax=eax/ecx这样的代码,eax是被除数,ecx是除数
看上方1字节的指令直观,下面不够直观,看懂了上面下面的应该能看到,如果看不懂 百度搜索 div指令什么意思 这种或这种类似的关键词
然后ecx的值是从ebp+1得来的,上面怀疑过ebp的值是哈希表的大小,在这也验证了
执行完div指令
然后游戏中使用了余数,下图的内存里的数据不对,忘记乘以4了,然后代码也执行过去了,没法重新截图了
它取出了一个D220
然后D220与哈希表比较大小
jae是高于跳转,所以如果D220大于哈希表的大小就返回,就是如果哈希表是10个大小,意思就是得出的值不能超过10,使用余数的话这个判断没必要写,它就不可能超过10
然后ebx还是中文表结构,然后中文表结构+0x80位置是中文表,下图位置取查了中文表
现在的一个情况是游戏中有一个表存放了一个索引,然后用这个索引去查中文表,那上面说没必要的判断这时是有必要的了
然后得到中文
中文
这里有一个东西,用ASII(阿斯克码表)方式查看的时候,发现中文后面跟着它的英文,如下图红框,内存地址与上图都是一个只是显示的编码不一样,上图是UTF-16编码下图是阿斯克码
然后在存放中文的结构里也存放了中文对应的英文
然后我们的代码也改成了跟游戏一样
然后来到0x1035EF66函数
按f7进入函数
然后执行了一个跳转
然后跳转到了0x1035EE5E位置,其它的不知道是干嘛的
然后跳转之后的代码
然后比较eax与ecx是否一致,这里就是一个比较字符串的操作,与stcmp函数意思一样,这里直接跳过在ret位置打断点跳出函数
然后回到下图位置,然后一致就给返回了
然后找个不一致的,它会产生哈希冲突
然后发现它返回了-1,没找到东西,然后在换一个不一样的数据
这时从哈希表里找到的中文结构头部4字节不是-1了
这时没有执行到0x102976C0函数。这说明文字结构第一个内容是链表指向下一个数据的东西
然后代码也改了
整理一下,哈希表里面放的第一个链表,然后链表的内容是中文表里的某一个,然后中文表第一个是next下一个链表位置,然后-1表示没有链表,所以找不到,也就是这个英文没有对应的文字
到这应该能感觉出,这种算法分析起来并不是很难,只要知道入参的意思,然后带着入参去一步一步跟着断点去走,就可以很清晰的分析出算法来
因为就改了两行代码,所以代码不会贴出来但会放到码云
总结:
中文表结构+0x8C位置是哈希表,中文表结构+0x88位置是哈希表大小,中文表结构+0x80位置是语言表