【干货】《DNF》反外挂逆向教程04:周围遍历(下)
文摘
社会
2025-01-08 00:00
广西
本期教程我们继续讲解秒杀敌人的功能。上期教程我们已经找到了周围的数组,其中包含了各种对象,包括怪物在内。然而,我上期教程并没有使用这个数组进行代码编写,因为这个数组的对象来源是多方面的。我们需要注意的是,这个角色对象是从我们之前的调试工作中找到的,它们的来源是游戏数据的更新。打开这个 C1,搜索我的血量,但搜索结果并不理想,无法找到合适的值。我进行了一些断点读取和调试,最终定位到了偏移为 3528 的地方,并进行了一些操作。接下来,我们需要观察数组的结束位置,通常情况下,数组的开始位置是固定的,但结束位置会随着召唤怪物的增加而变化。当我们召唤一个怪物时,结束位置会增加一定的数值,清除怪物时则相反。这样一来,数组的长度就会随着游戏进程的变化而动态调整。在代码编写中,如果我们遍历这个数组,可能会出现多次遍历,导致在每秒钟内都会接收到新的对象,这些对象可能会干扰我们的代码运行。因此,在实际编程中,我并没有使用这个数组,而是通过其他方式找到了我需要的数据结构。这个数据结构可能是通过角色对象的首地址,或者是其他属性,比如名字、血量等来找到的。虽然具体的方法我并不清楚,但我们可以通过这些结构的特性来综合考虑和定位,因为不论是数组、二叉树还是其他数据结构,它们都有自己的特征。
比如说,在这个场景中,我们可以把数据结构比作一个二叉树。当我们查看这个结构的上一行或者下一行,或者离它很近的地方时,可能会发现一个成员数,比如在二叉树中有两个成员数的话,它就会显示二。类似地,数组也可能具有类似的结构。然而,在我们的游戏中,数组的结构并不是这样的。我们都见过游戏中的数组吧,它通常会有一个开始位置和一个结束位置。然后,这个数组会根据数据的大小进行调整,比如有一个八字节的大小,那么结束位置就会是开始位置加上八。链表也是类似的,如果链表里面有两条数据,那么它的底下就会显示二。所以,不论是数组还是链表,我们在查找的时候都可以采用相同的方法。接下来,我们可以通过一些实验来进一步验证。比如,我们可以召唤一个怪物,然后再召唤一个怪物,然后再清除一个怪物,然后观察结果是否符合我们的预期。如果我们能够通过这种方法找到我们需要的数组,那么我们就可以进行进一步的研究。最后,通过观察数组的结构,我们可以得到一些有用的信息,比如数组的结束位置、大小等。这些信息对我们理解游戏数据的结构和特性都是非常有帮助的。我们来看一下第二个。第二个是不是从这里开始,然后加上一个十六进制的数,再减去一个十六进制的数,就是另一个对象的起始位置。那这里就是我们的第二个数组了。我用的也是这个方法。可以复制一下这个地址,然后我们就要开始找了。在这个数组的起始位置下设置一个断点,读取八字节。RCX加上一个十六进制的数,然后再加上一个十六进制的数,看看这里是不是我们的对象。接着,我们继续运行,看一下下一个断点是什么。这时候的RCX是不是一个对象?我们可以复制一下这个地址来验证一下。如果验证成功,那我们就可以继续查找了。然后,我们可以通过一些实验来进一步验证。比如,我们可以召唤一个怪物,然后再召唤一个怪物,然后再清除一个怪物,然后观察结果是否符合我们的预期。如果我们能够通过这种方法找到我们需要的数组,那么我们就可以进行进一步的研究。我们可以通过观察数组的结构来获得更多的信息。比如,我们可以看到数组的结束位置、大小等等。这些信息对我们理解游戏数据的结构和特性都是非常有帮助的。我们来看一下第二个。但这底下我们看着就没意义了,所以这个二叉树我们就直接忽略了。接下来我们看一下第三个。在第三个对象中,我们可以看到一段地址。我们不是为了找这个而是要看一下这个。我们直接看这个地址,而不是设置断点。直接来到内存地址一零来看一下。实际上,这里也是一个链表。我们可以看到前两个节点是一个链表的第一行和第二行,而底下则是其他无关紧要的数据。我们首先要进入加八的地方,不是加一八,然后再来到内存地址二。这时候我们可以看到,加零的地方是指向下一个节点的。而加八的地方则是指向上一个节点的。然后下面的三个就是这个节点所带的内容。我们可以来看一下内存地址三。然而,这里看不太懂,所以就不继续看了。因为这个不是一个对象,所以我们再看一下这个。这里是一个对象了,第一行是一个虚函数,而第二行则是我们所谓的ID之类的东西。我们右键点击并读取这个地址。然后,我们就可以复制一个地址,加上3528,看一下这是不是一个血量。我们把血量改成零,然后打一下看看是不是死了。确认后,我们可以减号回来,回到内存地址一二。对于这个链表,我觉得它很有用。因为它里面没有存任何乱七八糟的数据,只存了人物和怪物的模型和对象。只要我们可以把自己的角色区分出来,其他对象我们都可以调用一个死亡函数。那么这个结构的地址在哪里呢?其实我们往上看两行,是不是就很熟悉了?是的,这里就是刚刚加一A0的地方,也就是这个数组。所以下次我们访问的时候也能找得到。我们右键点击并读取八字节,然后召唤一下,看看是否正常。总的来说,这个结构应该会很熟悉了。我们的第一个数组加1A0的地方是我们的第二个数组。然后我们再试试1B0在1B8,然后在1C0的地方是一个我们的链表的结构。我们把记录一下,1C0是链表结构。然后,这个链表结构的进入需要加一个八,要进一层。二叉树和这个列表都要进一层,都要加八。要把进加八的地方进一层,得到了第一个节点。然后呢,得到了第一个节点,节点再加一个二八,减一个2020,得到了这个叫做链表成员的周围对象。其实这个节点呢,加零的地方是不是下一个节点呢?那这个节点什么时候结束呢?我们应该到里面去进行一个访问。我们要访问到正好是这个节点的时候,右键进入一层,在这里下一个访问,在二八的地方加一个访问断点,读取八字节直接就断了。然后我们就要在这个循环里找一下他自己进的那一层。RBX加二八,然后我们就要到尾部来看了。RBX不可能是从外面传进来的,那只可能到底下呢看看有没有。这里进了一层,进入节点。然后呢,我们进入了节点,始终会进入到最后一个节点。它是怎么判断最后一个节点的?他是这样判断的,如果我们是不是进入节点啊,结束了是不是就应该不会再到循环头,然后继续往下运行了。可以看到这里有个判断,这里的判断就是跳出我们的这个循环的。只要我们这个判断成功了,就不会判断失败了。它是什么?RBX和RDR进行对比。它是什么?它是头部外面传进来的。EC01C0很熟悉吧?他是自己和自己进行对比。那么本期教程就说到这里。