vmp1 发表于 2022-8-14 12:09:03

浅谈FPS静默自瞄实现方法

分析:我不知道大家有没有仔细思考过一个根源问题,那就是为什么FPS存在准星这个东西?答案很简单:用来瞄准目标。所以我们能准星指哪打哪。可是问题来了,请问游戏是怎么知道我们准星瞄准的是哪里?打的是哪里?请注意,这里我说是两个问题。我相信大部分人都没有真正去思考过这个问题,因为他们基本上都把这两个问题归结为一个问题。其实在我们不断深层的挖掘这个问题的时候,答案已经显得容见。那就是游戏会不会把我们从瞄准到射击敌人的这个过程分为两个部分,一个是准星的变动(准星的变动所带来的的影响为视角的变动,下面我都以视角来描述),另一个是实际射击。为了确认这两个想法是否正确,我们用游戏实践一下。第一步得找到视角的坐标,也就是鼠标XY。不难发现随着鼠标XY变化,视角也在变化,故第一个想法成立。我们来看看第二个,既然用于实际射击,那么在我们开枪的时候鼠标XY必定作为参数传进来,此时我们只需要找到开枪call下断,然后开一枪后看一下堆栈是否存在鼠标XY即可。断点断下后往下一翻,果然有鼠标XY,那么就说明从瞄准到射击的确分为两个部分。当我们确认这两个想法之后,好玩的事来了。我们回忆一下,我们是不是要实现一个准星在无需瞄准人的前提下开枪也能对敌人造成伤害的功能,那么我们是不是可以做一下手脚?当我们开枪的时候我们不用游戏本身所提供的视角坐标,而是用我们提供的坐标,这样就能在不影响原视角的情况下让子弹往我们提供的坐标射击,于是文章开头的那个效果图就出来了。至此,子弹追踪就已经实现。不过这种子弹追踪的实现方式在江湖中有它自己的名字-----视角追踪或静默自瞄。这里说一下,之所以也称之为静默自瞄,只因为该追踪本质上还是属于自瞄,只不过是我们原视角在不产生变动的情况下进行了自瞄。实现:分析也分析完了,那么怎么去实现呢?两种方式:第一种、需要大量的逆向功底,就是分析开枪call在什么时候被赋值了实际射击坐标,然后hook即可实现。第二种、有句古话“擒贼先擒王”,既然开枪的时候需要去读取视角坐标,那么我们就直接在访问视角坐标的代码段进行处理岂不美哉。Tip: 第二种方法虽然不如第一种方法要求的技术含量高,但是根据本人这段时间的测试,发现很多游戏都能实现,并且危害性大。故讲解该种方法的实现,也希望游戏产商早日和谐该功能。我们来访问一下视角坐标发现有三条,而且无论我们改变视角还是开枪,都不会出现新的访问,那么我们只能从这三条处理。我们抛开第三条不看,因为第三条是属于给视角坐标赋值语句,我们要看的只是访问语句。这样就只剩下两条了,我们先点击第一条看看。我们再来看第二条语句的这两段代码几乎相同,都是把视角坐标取出来后赋值给另一个地址。经过我们上面分析,瞄准和射击分为两个部分,他们都是需要去访问读取视角坐标的,巧的是这里对视角坐标访问恰好就是有两个,那么我们可以怀疑一下这两个语句分别就是对应瞄准和射击。我们直接nop看效果,先nop第一个因为eax和eax+4分别存储视角Y和X,所以得nop两条语句。接下来我们进入游戏看看我们发现nop之后虽然我们没法改变视角,但是我们会发现,鼠标XY竟然在改变,然后我们开枪,发现子弹依旧是打在墙上,这就尴尬了。。。。。我们添加一个机器人,我们走到他的面前,我们把准星对准他的头部,然后重新nop该处代码。神奇的一幕来,无论我们朝他头部开多少枪,就是打不了。为什么?我们还原代码发现我们准星此时指的是这个位置,此时我们把视角瞄准敌人后再次nop就变成了这样,我们用手枪打一下。发现敌人被我们爆头了。我们就能百分百确认了,该代码段为控制视角,真实控制射击的是第二条访问语句或者直接是鼠标XY。现在呢,已经可以写代码了,不过这里我只提供关键代码,其余代码大家可以自己完善。
[*]代码:
在研究的时候我们发现一旦nop之后,视角直接发生改变,而且直接把人物指向0的位置,是因为我们nop后视角坐标得不到赋值,就一直为0。那我们我们想让我们视角保持不变怎么办?那就得需要一个变量来记录。当我们开枪的时候,先把当前的视角坐标记录,然后把这个记录的值写入视角坐标,这样就达到一个锁视角的效果,然后再通过自瞄算法把自瞄角度算出来再写入鼠标XY中,最后实现追踪效果,追踪完毕后再恢复代码。那么如何确定视角坐标地址呢?首先我们已经找到控制视角的代码段了,不难发现,控制视角的关键语句为的两条client.dll+CB0D0 - 8B 44 24 04         - mov eax,
client.dll+CB0D4 - 8B 10               - mov edx,
client.dll+CB0D6 - 89 91 040F0000            - mov ,edx         --------->视角Y
client.dll+CB0DC - 8B 50 04             - mov edx,
client.dll+CB0DF - 89 91 080F0000            - mov ,edx         --------->视角X
client.dll+CB0E5 - 8B 40 08             - mov eax,
client.dll+CB0E8 - 89 81 0C0F0000            - mov ,eax
client.dll+CB0EE - C2 0400            - ret 0004ret 0004那么我们可以直接采用hook技术,把记录的值写入这两地址里即可。以下为部分代码//静默自瞄
      if (g_bBulletTrack)
      {
                if (GetAsyncKeyState(VK_LBUTTON) != 0 && dwTargetObj != 0)
                {
                  if (Data::getHp(dwTargetObj) > 0)
                  {
                        bAimLock = true;
                        AimAngle = Data::getAimAngle(Data::getBonePos(LocalPlayer.obj, Bone_Head), Data::getBonePos(dwTargetObj, Bone_Head));
                        AimPunch = Data::getAimPunch(LocalPlayer.obj);
                        AimAngle.x -= AimPunch.x;
                        AimAngle.y -= AimPunch.y;
                        BulletTrack::Track(true, AimAngle);
                  }
                }
                else
                {
                  BulletTrack::Track(false, AimAngle);
                  bAimLock = false;
                  fValue1 = 0;
                }
         }
      
      
    /*追踪实现*/
    void Track(bool state,Vector2 angle)
    {
      if (state == true)
      {
            g_dwTrackSwitch = 1; //开
            g_AimAngle = angle;
            lockScreenView(); //锁定视角
            WriteMem<FLOAT>(ReadMem<DWORD>(g_dwEngineModule + m_Mouse) + m_offsetMouseY , g_saveMouse.y);//写自瞄角度
            WriteMem<FLOAT>(ReadMem<DWORD>(g_dwEngineModule + m_Mouse) + m_offsetMouseY+4, g_saveMouse.x);
      }
      else
      {
            if (g_dwTrackSwitch == 1)
            {
                g_dwTrackSwitch = 0; //关
                resumeScreenView(); //恢复视角
            }
      }
    }防护:正如我在文中多次提到,该种追踪的实现方法在很多款游戏我已经实现了,页游到端游的部分游戏都会存在,并且没有一丝丝的检测。游戏厂商应该对此重视一下,稍微对其关键部位进行检测,并对鼠标XY的数据进行校验,以防不法分子对其进行篡改。
页: [1]
查看完整版本: 浅谈FPS静默自瞄实现方法