让宏指令调用支持UTF-8字符编码。
之前的代码中没有对字符串进行转码(转码为UTF-8),所以我们都是从内存里面把UTF-8格式的字符串给抓出来用的,这节课我们就把中文的支持给它添加上。
我们把自写功能接口这个目录名改为001-自写API接口,然后把AnsiToUtf8函数复制到AsciiUnicodeUtf8.cpp的最后,并把该函数原型添加上AsciiUnicodeUtf8.h里面。
修改宏指令测试函数:
这里的调用我们先暂时这样写,先测试一下看看。
我们把执行宏指令函数复制一份,放到游戏功能接口.cpp中,并添加进TGAME类里面。
进游戏测试,没问题,但是最终我们的目的是在编辑框里面输入宏指令并能够执行成功,目前来看的话我们只是转码成功了。
由于我们刚写的那个AnsiToUtf8函数只支持多字节字符集转UTF-8,而不是Unicode转UTF-8,所以我们暂时先把工程的字符集改为使用多字节字符集,编译后出现问题,我们修复一下:
我们为编辑框添加关联的值变量:
给这个编辑框值变量一个初始值``:
我们用xdbg附加wow游戏后,在角色已死亡的时候(复活有两种方式,一种是天使复活,还有一种是找到尸体),我们在xdbg中转到wow模块,搜索当前模块、常数6B0B50,搜索这个明文包被调用的地方会发现有600多个,如果你要做脱机辅助的话就都要分析一遍,由于我们不做脱机的,所以只需要分析我们自己需要的部分:
然后,凡是我们没有按下游戏界面上的那个释放灵魂按钮就断下的地方,都是一些心跳包、其他功能(转向、),我们都把那些地方的断点取消掉,然后在点击释放灵魂按钮,断下来的地方就极有可能是释放灵魂的组包。
当然也有可能,我们调用这个CALL更上层的CALL(我们双击转到上图右下角堆栈中的返回地址0085898A那里),也可能断下,但究竟是不是还需要我们验证,也可能是上面好几层(分析看看能不能找到技能字符串名称)。
我们转到ecx看看:
上图选中的部分是我们包的内容,其中数值5有可能是包的大小,23FCD350是包的缓冲区,之前我们在分析技能的时候分析过:
取消掉该断点后,又在另外一个地方也断下了,当然也有可能和释放灵魂没有太大的关系:
越到后面的断点和释放灵魂有关的可能性就越小,越靠前可能性越大一些,后面的包内容我们也可以进去看看:
看看包里面都有些什么内容:
后面断下来的地方还非常多,我们都拷贝记录一下:
差不多就是说,我们点击那个释放灵魂按钮后,发了8个左右的包,我们回到游戏后就会在死亡状态下看到天使这个NPC:
我们用鼠标右键点击天使又断下几个地方,我们记录下来并一一取消这些断点:
我们点击上图接受按钮之后,看看都有哪些地方会调用这个明文包的CALL:
记录下来之后把断点取消掉,让游戏跑起来,又断到其他地方去了:
虽然说越靠后的组包,可能性越小,但是万一是后面的组包呢,也是有这个可能性的;
如上图所示,和天使复活有关的、点击接受按钮后的组包大概有这3个。
右键点击NPC的时候有4个组包,释放灵魂的大概有7个组包,但是哪个是真正的释放灵魂,哪个是真正的右键点击NPC,哪一个又是真正的天使复活的指令,就需要我们下节课写代码来测试(写一个结构来组包并调用相应的CALL进行测试),当然我们现在重点分析第一次的组包,因为后面的组包可能性都是很小的,一般来说第一个的概率高达90%。
我们先来看一个鼠标右击NPC的包分析过程:
+10位置的数值可能是包的大小,+4位置是包缓冲区,+0位置是包对象的vftable,我们来看看+4位置的包的内容:
这两个组合起来才是一个完整的包对象以及包的内容,也就是说当我们正常用鼠标右键单击NPC的时候,就需要组这个包,
这个就是说你要访问的是哪个NPC(怪物),你就把它的GUID传进去,而13D应该是打开NPC的指令,这是一个固定的整数,用来作为一个调用的约定,我传的是13D这个数值,就是表示要去访问、打开、右键单击这个NPC,大概就是这一个意思,这是我们的一个猜测。
我们再来看看天使复活时候的包以及包内容:
压栈的是ecx,我们看到包的内容基本没有变化,只有+4位置的包缓冲区地址变了,我们再看看包缓冲区的内容:
可以看到包缓冲区里面的释放灵魂指令15A没有变化 ,是固定的。
我们接着再看看点击天使复活的接受按钮,所组的包:
压栈的是edx,包的内容是:
我们测试一下看看这个ID1、ID2究竟是不是玩家自己的ID,我们转到获取玩家对象的CALL那里下个断点:
在该CALL下断返回后获得的eax就是角色指针,+30地址的内容我们发现并不是5B015407这个ID,说明5B015407可能是天使NPC的,具体究竟是不是,我们可以在下节课打印一下怪物列表一比较就知道了。
这节课我们就来写代码进行测试。
为了更加的通用,我们修改一下明文组包这个函数:
我们测试一下,如果不成功的话我们就需要分析后面几个CALL的组包,特殊的情况,它有可能需要几个组包一起使用才可以,但是一般只使用一个组包就可以了,具体我们需要做测试。
释放灵魂成功,已经来到天使这边了,这时就有两步需要做了:
访问NPC的组包和点击接受按钮复活的组包。
我们要看一下buf中填写的ID是否是天使的,可以遍历怪物列表来验证。
经测试失败,不能打开NPC;
我们先把天使复活的组包,测试看看能不能在不打开NPC的情况下复活,可以的话我们就跳过打开NPC的功能:
离天使非常近的情况下经测试成功,第二个组包是成功的。
我们继续分析鼠标右键单击NPC的功能:
我们发现是NPC的ID不对造成的打开NPC的测试失败,说明天使的ID是动态变化的(每次重新打开游戏都会变),这种情况的话,我们就需要动态的遍历到天使的ID,把它取出来。
经过我们测试(多次点击打开NPC按钮,NPC对话框的界面一直不出现,点击复活后成功复活,说明只是NPC对话框的界面没有显示出来;封包只有效果,不会有界面),目前这种情况说明需要调用更上层的CALL。
一键调用的话,就是说这3个功能一起调用:
经测试我们发现不能一键复活,还需要让角色移动到天使NPC附近才能复活,所以我们需要遍历怪物列表,把天使的坐标获取到,然后就可以移动到天使那里进行复活。
我们的遍历怪物代码有问题,遍历不全,这次来完善一下。
从上图我们可以看到,在对怪物对象首结点进行是偶数判断后,第179行获得了对象后,却没有对该对象进行%2的是偶数判断,while条件中的是偶数仍然是怪物对象首结点的判断结果。
由于之前我们没有加上这样一句对象是偶数的判断,就会造成while循环中的对象地址是单数,进而导致R4(对象+0x20)
获取对象产生异常,程序提前就终止了,遍历就不全了。
我们精简一下代码,让它更加的完善:
遍历不全,之前的代码还有一个地方有问题,我们可以看到遍历到第6个的时候(i=5),程序就退出了。
我们把窗体的TopMost属性设置为true,Minimize设置为true增加最小化按钮,最大化一般是不需要的。
我们增加几条辅助调试的打印输出的语句,最后发现没有走到235行printf打印的结束遍历链表那里(如下图选中那行只是开始遍历,没有i=37的结束遍历链表),说明还是有问题的,只有走到237行这里才表示我们的遍历是成功的,没有走到这一行的话,那代表代码里肯定是有问题的。
这里多半是遍历链表这个函数有问题。
我们从上图看到 i=36完成后,在 i=37 开始遍历链表的时候出错了,目前可以确定出错的地方就是遍历链表这个函数里面,初步来看的话,可能是打印怪物对象信息函数里面出的问题:
但是这时候又测试成功了,代码还是不稳定,我们之后再做完善。
目前怀疑的话是185行打印怪物对象信息这个函数里面出的问题:
我们就可以从该函数头部开始或者从怀疑的地方开始加入异常处理,据猜测是获取背包对象名字这里出的问题:
在它出错的时候弹窗,让它停下来:
我们在上一层也给它加上一个大的异常处理,把整个函数都给包含进来,因为如果不是背包名字那里出的异常的话,那就会传到这一层:
我们可以把上图异常处理中的MessageBox 给注释掉,这样它还是可以遍历后面的结点的,但是我们这里暂时把这个信息包留着:
比如说我们还可以把__try这个开始的块向后移,移到utf8对象名字的赋值那里(说明此时前面的那部分代码没有问题),这样我们就可以一步一步的缩小范围,最终确定出错代码的位置。
这种数值类型的我们用CE搜内存:
我们可以通过这个对象的起始地址,给它规定范围,大致是多少范围(如下图所示的起始和停止),一般来说这个耐久度和这个对象(装备对象)的地址很近,当然这也不是绝对的:
我们一下就搜到了,但是这个值究竟是不是我们的耐久度,我们把它给改一下就知道了(用CE修改该地址数值24为11)。
我们可以看到耐久度变成11了,说明这个地址肯定就是耐久度了。
这个时候我们用CE的调试器找出是什么访问了这个地址:
我们鼠标指上去(如上图所示)之后,显示出了几条汇编指令,从这里来看的话,它的第一个偏移是+D8。
显示反汇编程序看看代码的流程,看看前面的偏移(和eax相关的偏移)是多少:
我们把上图中的寄存器环境给记录下来,而edi等于2DD905C8,恰好是学徒长袍这件装备的地址。
我们再用xdbg来看看装备的上限:
如下图内存窗口中所示,装备上限35紧挨着耐久度:
我们在CE中修改耐久度为333,可以看到学徒长袍的耐久度上限就成333了。
修理单个装备和修理所有装备,都是要向服务器组包发包的,要参考第29课的知识点:
可以看到组包的功能有400多个,我们现在所需要的组包也就几个。
转到游戏,只要不是修理相关的断点我们都取消掉:
我们在锻造师那里打开该NPC,凡是断下来的全部取消掉,然后点击下图所示修理所有物品:
复制上图选中部分代码作为记录,因为发给服务器组包的话,修理所有装备的话该组包的格式中可能就只有一个指令,修改单个装备的话就可能会有该装备的GUID,具体是什么样的我们还是要看一下才知道。
根据上图所示,在内存窗口中转到eax所代表的地址处:
也就是3A9FB50是包对象,我们把包的内容复制出来记录一下:
再来看看包对象+4位置的包缓冲区内容:
要把上图至少21个字节的内容复制记录下来。
实际上上图+14位置选中的部分是没有用的。
我们这里修理的所有对象,理论上来说是不需要F1300000和4E013851这两个物品ID的,所以这两个有可能是玩家或者NPC的ID:
此时我们可以判断,该ID是售卖装备NPC的ID。
取消该断点,继续运行游戏,把又断下来的断点都取消掉;
然后点击修理单个装备,再点击耐久度不满的装备(学徒长袍):
我们发现修理单个装备的包缓冲区的内容,和修理所有装备的相比,多了两个数值0x8277和0x40000000:
这两个数值是学徒长袍的物品ID。
只要你不更换角色、不换服务器、不退出游戏、游戏版本没有更新,这些ID一般不会变;另外如果你又买了一件学徒长袍,那么新的学徒长袍的ID肯定是不一样的,每一份都具有一个独立的GUID。
上图明文包对象第一个成员不是我们分析的,由于不知道这个有什么用,所以错了也没关系,我们先测试看看,不行的话再换后面注释的正确数值。
我们首先手动打开NPC(这步不能少),如果发现装备耐久度满的话,就去打打怪挨挨打,让耐久度掉一些,以便测试。
然后跑到NPC那里打开他以及你的角色信息,进行测试:
测试后发现没有成功,关闭角色面板重新再打开刷新一下,发现还是没有成功,说明组包的第一个成员内容是有作用的,需要修改成正确的数值再试试;另外包的长度我们也没有修改正确,也可能是由于包长度问题导致测试失败的。
发现还是没有成功,我们先遍历一些怪物装备物品,看看该学徒长袍的物品GUID发生变化了没有,经遍历查找发现该GUID没有发生变化,NPC的GUID也没有变,所以可能还是我们包的分析有问题。
我们在明文发包EX函数里面加一段调试信息,看看它是否有执行到这个地方来:
从调试信息输出来看,明文组包EX函数肯定是调用到了。
可能我们分析的这个包分析错了,我们再来重新分析一下:
取消掉不相关的断点,直到游戏能跑起来。
我们发现调用6B0B50这个CALL前面还有很多使用了包对象参数的CALL,有可能需要调用前面这些CALL来初始化这个对象,而且我们看到左下角内存窗口中的包对象内容还有0x100等数据,我们把包对象范围扩大一些(到+24那里)再试试;
包缓冲区的内容应该就是0x15个字节,之前这块的分析应该没有错:
我们重新把这个包给组一下做一下尝试:
也有一种情况,就是装备已经修理好了,但是没有刷新过来,我们小退一下游戏,再进去可能数据就更新过来了(修理所有装备的功能要简单一点,它没有物品ID等多余的参数;修理单个的要复杂一些)。
我们测试一下,去打怪让角色死亡然后复活(也可以直接用指令.die死亡,复活后装备的耐久度就会掉一些),打开角色面板和背包,可以看到装备的耐久度全都下降了,现在测试就比较方便了;
此时再用我们的代码注入进去试一下:
修理好了,测试成功,可能的确与包内容后面那些我们之前没有添加进去的数据(0x100)有关系:
但是也可能没有关系,可以下去自己测试一下,比如上图39、40行的设置为0,41行仍为0x100,这样试试看;另外不打开该NPC的时候也可以试试;修理所有装备的自己下去组一下包尝试一下。
在怪物列表里面遍历装备的耐久度,说是怪物列表,实际上就是在所有装备、背包、地面上的东西、怪物、NPC都在这个怪物列表里面,遍历所有这些对象的耐久度。
我们写代码在所有的怪物数组里面把装备遍历出来,并且打印它的当前耐久度和耐久度上限。
我们在项目的游戏功能测是目录中添加一个”装备相关”目录和一个“背包相关”目录、以及一个”怪物相关”目录:
添加“装备数组.cpp”源文件和”装备数组.h”头文件,再把之前遍历怪物相关的几个函数的代码都拷贝过来:
把获取最近对象函数名改为遍历装备,删除和遍历装备无关的代码,我们只需要一个简单的遍历就可以了:
我们把遍历链表的参数修改一下:
上面两个图中的保存较近对象这个函数也不需要了,删除掉。
上图选中部分也不需要了,删除掉。
接着继续修改遍历链表函数(之前写的该函数是有问题的):
我们来看如何判断是否是装备对象:
从上图可以看到所有分类是3的基本上都是装备,这是一个地方可以判断;
另外一个地方是vftable这个虚函数表:
这两个地方都可以用来判断装备,我们目前可以考虑从分类这里进行判断,这样精确一点,因为如果真正是游戏的话,只要这个游戏有一点点更新的话,这个虚函数表的地址是会发生变化的,我们还要重新分析这个虚函数表,但是分类这个它不会发生变化。
所以这里我们要先读出对象的分类:
我们之前的代码里写有获取对象类型的函数,把它们的函数原型拷贝到“装备数组.h”头文件中就可以使用了:
把前面的打印怪物信息函数改名为打印装备信息,修改该函数:
如果是装备的话肯定是没有这个怪物名字(偏移不对),装备名字的话看一下之前的代码是怎么获取名字的:
装备对象的名字也是通过“获取背包对象名字”这个接口来获取的(名字获取到之后还要进行转码)。
打印对象信息这里不需要坐标和距离这些信息了,删除掉。
获取耐久度的话我们专门写两个函数:
最后我们用主线调用一下遍历装备这个函数:
让打印出来的装备信息格式化对齐一下:
测试发现打印出的计数为0,耐久度也不对,名字为空,好像是把其他的对象也传进来了,没有过滤到,我们看看上图随便选中的一个对象:
检查代码发现是因为分类没有做判断,如上图所示修改即可(if(分类1==3) 打印装备信息(对象);
),这样如果是NPC、怪物的话就不会打印了。
可以看到除了身上的装备外,背包里面的装备也遍历出来了(我们在NPC那里进行修理的话,可能只需要修理身上的装备);
如果你只需要遍历身上穿的装备的话,就需要下节课分析一下这个装备数组了。
另外,学徒之靴、学徒衬衣、轻型飞刀这几个确实是没有耐久度的,它们是新手装备是没有耐久的,只有自己买的或者打的装备才有耐久度。
那么我们怎么把这两者(身上装备和背包)区分开呢?
之前我们分析背包的时候,知道背包实际上是一个8字节的数组,背包这个数组每个格子里面存放的是物品的8字节的GUID;而我们从上图也可以看到,角色信息里面这些身上装备也是一个一个的格子,它大概率的应该也是一个数组,类似于我们背包数组,我们下节课一起来分析这个装备数组。
装备分析原理实际上和我们背包分析的原理是一样的,在背包分析的时候,我们是通过移动背包第一格来在CE中搜索寻找相关联地址的,实际上你移动任意一格的对象都可以作为一个突破口;
同样的,我们打开的角色对象面板的装备,你也可以把它当做一个背包,比如说我们就以装备上的学徒长袍为例,把它移动到背包里面:
此时这个位置就空出来了,这个位置一般就是一个固定的数值,要么是负1,要么是0,一般是0的概率要大一些;
然后我们再把学徒长袍移上去,游戏要把就是把对象地址写到胸部这格,要么就是把这个对象的序号或者ID写到这里面,移上去之后呢就是说相应的内存位置它肯定有东西写到这个相应的内存,只要你游戏不重新启动,那么你移动一个东西上去,那么它就会向装备这个位置写相应的数据;
这里如果它跟背包数组一样的话,那么写入的应该就是我们装备的ID:
我们先把学徒长袍移开,那么这个格子就空出来了,那么,我们在CE中搜4个字节的0,接着我们再把这个物品放上去,那么它就是一个非0的数值,也可以说成是大于0的,因为CE这里设置的数值类型都是按照无符号数来计算的:
而且这个时候我们没有去变动它,所以我们可以搜未变动的数值(一直点击再次扫描可以搜很多次,并且可以移动一下其他装备,但是对胸部这个格子是没有影响的):
然后再次把学徒长袍给移开,再次搜精确数值0,这样结果一下子就少很多了,我们还可以移开其他装备,胸部那个位置依然是0,但是这样子再次扫描又可以过滤掉很多东西,再把移开的其他装备再装备回去,然后再次扫描,反复几次。
然后我们把学徒长袍放上去,搜值大于0的数值,这个可以反复的搜,而且它没有变动,可以搜未变动的数值,并且在结果比较少的情况下,我们可以手动删除掉结果中反复变化的红色的内容:
然后我们点一下装备上的学徒长袍,但是我们并没有真正的移除(还没有将学徒长袍移动到背包里面):
所以还是搜未变动的数值就过滤到只有36个结果了,然后我们再把学徒长袍放回去,还是没有变动,再次把它给移开移动到背包里面,然后把没有变动的内容给删除掉,或者直接搜索精确数值0就又能过滤掉很多内容,然后穿上其他装备,再搜精确数值0,或者搜未变动的数值;再把学徒长袍穿回去,再搜大于0的数值,脱下其他装备或移动其他装备,再搜搜未变动的数值,或者搜大于0的数值。
我们可以看到0x8277是学徒长袍的ID1,而地址值29BB7118+4=29BB711C就是学徒长袍ID2的地址了,这样来看,这两个地址就是这个物品相关的地址了,它们在装备数组里面的可能性比较大,其他的可能性不大。
我们给ID1这个地址值下一个访问断点:
我们可以看到上图反汇编代码的确也是数组的访问方式,它究竟是否是一个数组,我们把CE的调试器给卸载掉,用xdbg附加游戏进去看一下,用xdbg查看内存的话更加的方便一点。
我们在xdbg中转到7543D3位置,我们看到这里之前我们分析背包的时候分析过,它和背包对象差不多同样的结构,只不过它们位于不同的偏移,那么具体是多少,我们在内存窗口中转到29BB7118这个地方下一个硬件访问断点来看一下:
然后在游戏中把鼠标移动到角色面板胸部那里的学徒长袍上,xdbg断下:
我们看到eax的值是4,ecx是29BB70F8,那么29BB70F8+4*8+4
这个公式中的8应该就是那个物品ID1和ID2占的8个字节,那么我们再来看一下之前分析的背包那课:
我们猜测,偏移就不是172了,这里好像加的是4,具体是不是我们在xdbg中看一下:
我们看到29BB70F8肯定不是角色对象,但是我们也不知道[角色对象+8]
是不是等于29BB70F8。
而29BB70F8+4*8+4
这个公式的数值是ID2,所以7543D3和7543D6这两个地址处取得是ID,所以我们还得往前找ecx的来源。
我们可以看到这个ecx是来自于上层CALL0062E214那一层的:
上图选中的的这两行也是关键点,我们复制记录下来。
目前它的下标的值是4,我们把它替换进去在内存窗口中看一下,发现的确是ID2那里,那么我们现在就要来看一下edi是什么了。
可以看到edi的值就是一个对象的地址,但是这个对象是否是玩家我们不知道,我们复制记录下来,等会我们遍历一下角色对象就知道了,如果是的话那么我们装备的基址偏移就有了。
在测试的时候如果程序或者游戏卡住了(异常了),我们重新进一下游戏,再重新测试验证就行了。
我们在CE中看一下角色对象的虚函数表地址:
现在我们就能确定这个edi就是我们玩家的角色对象,具体是不是我们在CE中把这个值给带入公式里面看一下:
而且我们从装备信息里面可以看到,学徒长袍恰好在下标为4的格子里,学徒衬衣在下标为5的格子里。
我们在xdbg的内存窗口中转到公式2C4D9010+18F0+4
,计算出的2C4DAE78就是装备数组的基址,我们按8字节查看:
我们可以看到这里面的GUID并不是按照装备信息的格子顺序来的,这里面可能有背包里的,我们先来遍历一下我们的装备,定向文件,遍历怪物,然后点一下控制台关闭文件句柄,查看调试信息,然后与上图内存里面的GUID进行对比:
对比发现,内存中装备数组中的下标和我们在游戏中看到的装备格子的下标顺序不一样:
它这个顺序有点乱,但是装备都在内存中装备数组里面,我们整理分析一下规律。
说明从8281开始就是我们的背包了,我们把背包中第2格的炉石移动到第一格:
可以看到8281移动到前8字节内存地址处了,说明上图选中的前面这一大段就是装备的,从8281所在这个地址开始就是背包的了。
我们把背包里面倒数第二个装备轻型飞刀放到背包第2格里面:
我们在上图内存中数一下背包第1格的8287前面一共有23格,这23格都是装备的,23格后是背包。
从上图我们可以看到,装备数组和背包数组它们的基址是不一样的,但是从上面内存窗口中看到这两者是紧挨着的,而且在游戏我们打开角色面板数一下装备格子发现一共也只有20个,多出来的那3格不知道做什么用的。
由于怪物数组里面也包含了背包数组,所以下节课我们只需要通过上图公式[角色对象+18F0+4]+下标*8
就可以遍历出所有装备的ID1和ID2了(下标限定为0到22),这样就不会有炉石等背包的东西了。
我们之前分析怪物数组的时候知道,在把ID取出来之后,可以通过调用4D4BB0这个函数来获得背包对象,也可以获得装备对象,实际上所有对象都可以通过ID1、ID2来获取:
因为装备数组里面每一个成员存放的就是ID1、ID2,再通过调用4D4BB0这个函数来获得这个装备对象,然后打印出这个装备的信息,但是这样的话效率不高,而且循环的时候每一次都要调用这个CALL,而这个CALL里面都要循环的去遍历,这样的效率不是很高;
在这里我们讲一个更高效的办法,就是在上一节课的基础上,我们反向的来操作:
因为上节课我们是遍历所有对象,然后把所有对象的ID1、ID2给取出来,然后用这个ID1和ID2与装备数组里的ID1、ID2遍历比较,如果遍历所有对象取出来的这个ID,如果它在这前20格里面,那么说明这个就是我们的装备对象,这样我们就能够获得角色面板装备信息里面的所有装备,而不会去获取背包里面的装备,我们就这样把这两者给区分开了,所以我们就用更优的第二种方法去解决,我们就不用去调用4D4BB0这个CALL了,就是说思路有两种思路,我们用后面这一种思路去实现,因为我们已经有遍历所有对象的代码了,我们只需要改一下就能够实现。
我们接着上一节课的代码来写:
因为之前我们遍历的是整个背包和装备,都已经打印出来了,现在我们要把背包里面的那部分装备给屏蔽掉,我们要给它加一个判断:
我们只要判断ID1和ID2这两个是否在这个装备数组里面存在,如果是属于我们装备数组的,那么我们就打印,不是的说明它是属于背包数组,我们把这两者给区分开,我们写一个函数就可以了。
首先我们要获取一下角色对象的指针,然后在通过公式计算出装备数组的基址:
实际上这个属于装备数组函数不用传对象参数进来,直接传ID1和ID2进来和我们这里通过公式读取的id1、id2比较就可以了(因为ID1和ID2我们在上一层的打印装备信息函数里面读取出来了),传对象进来的话你还要读一下取出这个对象的ID1、ID2。
打印出来的信息是0的话肯定就是背包里的,是1的话就是装备。
我们编译运行,点击遍历装备按钮:
我们发现打印出来的包含了背包和装备里面的东西。
经过分析我们发现是由于装备数组的基址我们少加了一个4,如上图修改后打印正确: