【干货】如何使用CE进行游戏逆向?10:Shared code处理
文摘
社会
2025-01-02 00:11
广西
本期教程我们来讨论一下共享代码的处理。首先,什么是共享代码呢?你们是否曾考虑过一个问题?在我们修改代码段以实现无敌效果时,我们发现了一个现象:无论是敌人、友军还是我们自己,都变得无敌了,无法被击败。你们有没有想过为什么会这样呢?因为我们只修改了一个位置,就是这个位置,用于比较血量的地址,以及比较血量的指令。而后面这个 JJ,我们只是把它改成了 GMP,让它永远执行血量大于零的逻辑。既然我们只改了这一个地方,就实现了所有人的无敌,那是不是说明一个问题?就是我们所有人的血量都经过这里的比较,然后都经过这里的跳转。所有人的血量都经过这两条代码,或者说这两条代码负责处理我们所有人的血量。所有玩家的血量都是共享这两条代码,所以它被称为共享代码。那么,我们要如何解决这个问题呢?首先,我们点击这条卡片指令,右键查找此指令访问的地址。我们来验证一下我们的猜想。这时候这里面是空的,因为我们还没有访问任何玩家的血量,因为这条指令还没有访问任何玩家的血量。然后我们回到游戏,比方说打一下我们的友军,再去打一下我们自己,在再去找一起炸吧。你会发现,这里边多了好几条。这说明什么?说明这条代码不仅访问我们的友军,还访问我们的敌军,还会访问我们自己的血量,我们所有人的血量都会走这里。那既然如此,我们肯定得想办法让程序区分一下,到底是在对我们造成伤害,还是在对敌人造成伤害,从而比较谁的血量。如果是比较我们的话,就让程序让我们无敌;如果是在比较敌人或者友军的话,就让程序执行正常的逻辑。怎么去达到这个目标呢?我们选择这个卡片指令,选择工具自动汇编模板,代码注入确定。这个时候,它帮我们写好了一堆代码。这块是干什么的?我给你们讲一下它的原理。就是在我们选中指令的位置,或者说也有可能带上附近的其他指令,做一个跳转。跳转到哪呢?跳转到这个“new mm”这里。“new mm”实际上就是“new memory”的意思。他这块说了是一块已分配的内存,具有读取、写入和执行的权限。我之前讲过了,代码是具有只读和可执行权限的。那为什么这还有个写入呢?是因为我推测是因为方便我们写代码吗?因为我们写代码的话会修改代码的内容,所以说必须要有写入属性。总之就是一块内存。然后呢,他又说在此处放置您的代码,也就是说我们要把代码写在这个位置。你们可能听了以后会有点懵。我们这边画个示意图来看一下。首先有一个白色的箭头,它就相当于是游戏原本的逻辑,游戏原来的代码是一条一条从上往下顺着执行的。然后我们在其中,我们某个比较感兴趣的地方做一个hook,就是说做一个跳转,我们让它跳转到一块别的位置,跳转到一个青色的这块代码。这块代码,青色的里面的内容是我们自己感兴趣的,是我们自己想实现的一个逻辑。我们在这个青色的位置写代码。写完了以后,你是不是还得给他跳回去啊?你如果不跳回去的话,这个青色的就会顺着往下执行,那不就崩溃了吗?因为你根本就没有回到游戏的逻辑上,所以说你执行完以后,你还要给他跳回到原来的位置,对不对?你还要跳回来。那这样的话,它的逻辑就是这样的,从这执行到这,然后顺着执行,再从这执行到这,又回到了游戏上。这就是一个hook的基本原理。这个CE的代码注入原理也是类似的。它将我们选中的位置跳到一个叫“new mm”的内存块,执行完了以后再跳回到下面继续执行。所以说,我们要在这里面实现一个判断,当前玩家是不是我们自己。如果是我们自己,就让程序让我们无敌;如果不是我们自己,就让程序执行原来正常的逻辑。我也不是很喜欢用这个东西。像这种生成的代码我也不是很爱看,有点乱。我们就把没用的删掉,把这三个删了,然后把这个“exist”下面对应的这些标签也删掉。删掉以后,就剩下这些了。那么我们怎么判断呢?我们就是比较当前的扣血、当前他比较血量的这个玩家是不是我们自己。我们观察一下我们选中的那条指令。如果你听过我们之前的课,或者说你自己动手掌握的话,你就会知道这个EC是一个生命值偏移。我们之前也分析过,很显然既然它是生命值偏移,就说明ESI是这个对象,有可能是我们自己,有可能是敌人,有可能是友军。那不管他是谁,只要他是对象,我们就可以判断他这个对象是不是我们当前的玩家,也就是说是不是我们自己。所以说怎么比较呢?我们在这里与我们的地址进行一个比较,如果相同的话,就说明是我们自己这个玩家。是我们自己的话,我们就希望他实现一个无敌。那怎么才能无敌呢?我们给他GE,正当他代表是相同的时候才跳转。我们跳转到什么位置呢?我们再来分析一下。当我们的血量大于零的时候,会执行这个JJ,他会跳转到一个什么位置呢?它会跳转到下面这个箭头指向的CMP的位置。也就是说,只要血量大于零,就会跳转到这里来。那血量大于零就说明我们还活着吗?所以说我们希望跳到的位置是这里。把这条代码的地址给他复制出来,复制出来以后把它写在这里,就实现了一个当当前比较的对象是我们的时候,是我们自己的时候,就跳转到一个无敌的位置。那如果说不是我们自己就怎么办呢?不是我们自己的话,接着比较。我们直接把他这条代码复制过来,复制过来以后,我们来捋一下逻辑。首先我们把它这条片改成一个跳转,跳转到我们这个地方来了。跳转到这来以后,我们首先是比较了当前对象和我们自己,如果说相等的话,相等的话会跳到这个位置,这个位置就对应一个无敌的位置。那如果说不相等的话,那只要执行到这,一定是不相等的,因为只要是和我们相等的话,已经跳走了,已经跳到那个位置去了,所以说凡是能执行到这儿的全是不相等的。但不相等的话,我们就比较一下血量,把它原来这条代码执行一下。执行完以后呢,我们再让他回到这个位置就行了,再让他回到JJ这个位置,让他该跳转就跳转,该不跳转就不跳转。所以说我们把这条代码给它复制出来,放到这儿。我们再捋一次逻辑啊,首先我们在这选择的代码注入,就说明它会从这跳转到我们这个位置。跳转到这儿以后,我们做了什么事呢?首先拿这个对象和我们自己进行了一个比较,如果说相等的话,我们就跳到一个血量大于零的位置,就实现了无敌效果。那如果说不相等的,不相等的话,我们希望它执行原来的逻辑吗?因为如果不相等,我希望你该活活该死死。所以说我首先把你原来这条代码,我原封不动地复制了一份,复制一份放到这儿,紧接着我又跳转。跳到了什么位置呢?跳到了这条代码下面的位置,因为我刚才把它复制到这儿以后,就说明在这已经执行过一次了,所以说我想让它恢复原状,想让他继续去执行他该做的事情的话,我只需要跳转到这条指令的下面这个位置。然后跳回去以后,它就会顺着往下一点点执行嘛。该跳转的话,如果说血量大于零,它仍然会跳转到这,如果说死了的话,就说明血量小于等于零嘛,就一定会顺着往下走。走的话,就执行他死亡的逻辑。我们来试一下效果啊,执行确定嗯。确定以后,我们回到游戏看一下到底是不是我们的友军还能不能打死啊。试一下啊,这个敌人看到了吧,敌人可以打死。再回到友军试一下,友军也可以打死。然后我们拿个手雷炸自己,诶你会发现好像炸不死我们自己了对吧。然后我们把这个把这个敌人的攻击打开,让他攻击我们啊,哦你会发现我就站着让他们打,我就站着不动,让他们打,会发现怎么也打不死我。但是我拿枪打他们,你会发现该死还是死,但是我是不可能死的。看到了吧,敌人还是能死,友军也是可以被打死的,但是我是不可能死的。最后,我们再补充一点理论知识。我们之前讲过这个compare这样说过,它是进行一个比较。那我们最常见的就是比较完了以后,紧跟着一个条件跳转。比如说在游戏里边我们见到的这个JJ指令,那这个JJ呢它实际上是等价于JA,就是大于跳转,而GL等价于GB,它是小于跳转,JE呢我们刚才用到了JE,它是等于跳转,它就等于JZ。如果说你对英文稍微敏感一点的话,你可能会立马就想到这些单词是什么。实际上这个G比较好想吧,这是来自于jump这个词,我们只取了他三个发音的字母吧,Jump gmp。那剩下的是什么?EABGZGL是什么呢?实际上这个E啊它是equal,而这个Z它是来自于单词zero,实际上是在这指的并不是zero,而是指的是Z标志位,它是来自于标志寄存器。标志寄存器叫做eflags,在64位里面叫做rflags。你们如果想深入了解的话,去搜一下company指令是如何影响这个eflags或者rflags的,你就会知道这个Z代表的是什么东西,它是怎么根据这个Z进行跳转的。而这些AGBL是什么意思呢?A是above,B是below,G是greater,L是例子。你如果说知道这些的话,你再去记这些所有的指令,你会发现他们都是来自于英文单词,而且你也不用去记了,你看一眼立马就知道它代表的是什么意思。当然如果你说你英文实在是比较不太好的话,你可能看了也记不住,那没办法,那你只能去死记硬背,或者说只能用到的时候去查。查多了,你自然也就记住了。还有呢,就是有时候你会看到他们中间还可以加一个词,叫做not。就是不飞的意思吗,非。所以说加上一个比方说JE,它是等于,你加一个JN,取一个N的话,它就是不等于的意思。然后还有什么JA就是大于跳转,那JNA就是小于,就是小于等于的时候跳转。然后这B是小于跳转,所以GNB是大于等于的时候跳转。那么你理解了这些以后,是不是就好记多了呢?那我们本期教程就讲到这里了。