zsky 发表于 2020-3-17 17:19

对CS1.6游戏的分析及心得

本帖最后由 L15263458908 于 2020-4-28 15:14 编辑

## 前言
前段时间分析了一下CS1.6游戏,一直没有时间整理思路写帖子,最近,由于疫情关系,忽然想起了这件事,又重新回顾了一下,写下这篇文章与网友们互相交流学习。
本文主要是讲一下指针扫描器和Ultimap的具体使用以及它的强大之处,然后讲一下自己分析游戏数据的一些心得。

## 使用指针扫描器快速寻找基址
使用CE寻找基址,一般的思路就是

* 查找游戏数据,找到数据的临时地址
* 右键查看谁改变了这个地址指向的数据,得到一个地址X + 偏移
* 然后我们再内存搜索地址X,得到很多地址Ys,一个个右键查看谁改变了这个地址Y指向的数据,刷新游戏
然后再找地址

就这样,一层层的去上找,不断尝试,最终找到绿色的即为基址。可见这种找基址的方式非常的麻烦,这里介绍一个神奇的工具,CE的指针扫描器 ,可以帮助快速找到基址
我们开局游戏,添加几个机器人,(添加机器人的目的是,因为找到基址后,即便不刷新游戏,有的基址指向的数据是不断变化的,其实里面是敌人和队友的基址,为了排除这种情况,添加几个机器人)
拿HP做实现,找到HP的临时地址,右键->对这个地址进行指针扫描



可以发现找到了403个数据



然后我们进行筛选,这里我们可以通过最后一层偏移进行筛选,我们对HP数据右键->找出是什么改写了这个地址,扔个手雷使自己掉血




可以发现,最后一层偏移是160,现今HP是4,下面进行筛选





经过筛选后,进行排序,然后双击第一个,添加到数据列表,退出游戏重新打开,发现里面指向的数据仍然是自己的HP,它就是基址。
其实在筛选的过程中,还有一些其他技巧,比如这个例子,在找到改变HP的地址后,可以查看相关地区汇编代码来寻找倒数第2层偏移



比如这里,可以发现mov eax, ,而 eax + 160是HP的地址,所以可以判断倒数第2层偏移是4,这样在筛选的时候可以加上二层偏移,当然也可以再往上找寻找倒数第3层偏移,使筛选的结果更准确。


## 通过搜索部分数据猜测游戏结构体来分析其他数据
通过上面指针扫描器的方法,我们又找到了一些其他数据的基址

属性 | 地址   
-|-
坐标X|[[+7C]+4]+8
坐标Y|[[+7C]+4]+C
坐标Z|[[+7C]+4]+10
HP| [[+7C]+4]+160
是否掉血(0免疫伤害)| [[+7C]+4]+16C
护甲 |[[+7C]+4]+1BC
阵营(1恐怖分子2反恐精英)|[+7C]+1C8
我的金钱|[+7C]+1CC
随地购物(1)|[+7C]+3C0
准星指向(0墙1友2敌)|[+7C]+710
当前枪子弹数目|[[+7C]+5EC]+CC
当前枪后坐力|[[+7C]+5EC]+100
是否正在使用武器|[[+7C]+5EC]+D4
左键冷却|[[+7C]+5EC]+B8

通过分析的这些数据可以猜测一下人物的结构体
+7c]             存放的是整个人物的基址
[+7c]+4]         存放的关于游戏内人物具体属性的具体地址
[+7c]+5EC]      存放的是使用武器的地址
我们通过猜测结构体,来到具体的内存位置,对人物进行操作,然后对内存进行观察,可以发现一些其他好玩的数据,这里我们来武器的地址为例



首先在地址列表中添加一个地址,指向当前的使用武器,通过切换武器来找到每个武器的地址,进而对相关内存区域进行对比
打开分析数据窗口,填入当前主武器,副武器,刀子的地址





我们切换武器和左键点击,观察内存,对3个数据进行对比
我们可以很明显的发现3个变化,即+D4这个区域,如果为1的话,表示当前武器正在使用
+B8这个位置,如果持续点击左键的话,这个位置的数据会增加



我们将刀子+B8的数据锁定为-1,就会出现2秒17刀的效果,,,


![](https://pic.liesio.com/2020/04/28/688f934a0a709.gif)
通过这种方式,不断进行内存数据的对比,也可以发现很多其他好玩的数据,感兴趣的网友自行尝试。这里讲解的主要目的主要是通过猜测游戏结构体来获取游戏的相关数据



## 使用Ultimap分析手雷CALL实现自定义手雷爆炸位置

CE有个强大的功能是Ultimap,可以帮助我们筛选代码,帮助找CALL





类似于CE的内存数据筛选,只不过Ultimap是代码筛选,可以搜索程序中的CALL, 比如我们要找手雷爆炸的CALL,可以点击开始,然后晃动窗口,人物随机走动,发射子弹等操作,然后点代码没有运行,筛选除去很多没用的代码,然后扔个手雷,暂停游戏,点代码没有运行,等手雷爆炸后,点代码已经运行,上图中的 代码没有调用计数 可以筛选函数执行的次数,比如可以扔2个手雷等爆炸后,次数设置为2,点击代码没有调用计数按钮就可以筛选

启用Ultimap需要开启DBVM,这个需要电脑支持才可以,点击帮助,关于,可以看到电脑是否支持




强烈建议使用CE7.0版本,因为我用其他版本的时候,使用Ultimap会失败,甚至有的版本显示系统不支持DBVM,但是7.0不会。还有如果成功开启之后,Banch目标始终为0的话,需要用管理员身份打开目标程序,比如这里需要以管理员身份运行CS1.6




下面来找手雷爆炸CALL来实现自定义手雷爆炸的位置
我们按照上述的步骤进行一点点筛选



最终我们找到了这个地址



对其进行HOOK即可改变手雷爆炸位置

## 通过分析堆栈找到准星指向敌人基址,实现吸人

前面已经说过[+7C]+710 在这个地址处存放着准星指向的相关数据,比如如果对准敌人,这个地址的数据是2,对准队友是1,对准墙什么的是0,我想实现一个只要准星对准敌人或队友就可以把他吸过来的功能,这个时候就需要获取准星指向人的地址

对[+7C]+710下断,看是什么改写了这个地址,游戏中添加3个机器人,控制台启用bot_stop 1使机器人停止行动

我们把准星从墙移动到队友身上



浏览内存区域



这里我最初是想着通过向上查汇编代码的方式来看看能不能找到队友基址的函数,但是失败了
虽然没有找到,但是肯定有,于是,换种思路,能不能先找到准星对准人物基址,然后再去内存中搜
按照上面指针扫描的方式找到3个机器人的基址(搜血量,然后指针扫描)



我们在这个那个地址处下断点,将准星移动到敌人上,程序断下,搜索敌人基址



然后再把准星移动到队友身上,程序断下,发现地址又变为了队友的基址



再观察程序运行到此处时ESP,就是指向人物的基址

```asm
mp.dll+9C0EC - 89 7D 00            - mov ,edi
```

当然指向墙的时候,存放的是其他的数据,因此我们对此HOOK的时候需要判断一下数据是不是人物基址
```cpp
BOOL IsPeopleBase(DWORD base)
{
      if (dwPointPeopleBase > 0){
                if (!(0XF0000000 & dwPointPeopleBase))//人物基址前4位通常是0
                        return TRUE;
                try{//如果不是的话也有可能是人物基址,判断血量即可
                        float blood = *(float *)(*(DWORD *)(dwMyBase + 4) + 160);
                        if (blood < 100 && blood > 0)
                              return TRUE;
                }
                catch (...)      {
                        return FALSE;
                }
      }      
      return FALSE;
}
```
这里贴出按E吸人,按T瞬移过去的全部代码

```cpp
#include "stdafx.h"

#define PATCHLENGTH      6
#define OFFSET 0X9C0C9                        //HOOK mp.dll的偏移位置

//0AD2C0C9    50            push eax
//0AD2C0CA    6A 00         push 0x0
//0AD2C0CC    51            push ecx
//0AD2C0CD    6A 01         push 0x1
typedef struct _stPos{
      float x;
      float y;
      float z;
}stPos, *pstPos;

HMODULE hMpdll = 0;                                                      //保存mp.dll的基址
HMODULE hModule = 0;                                                //保存cstrike.exe的基址
BYTE byHookPatch = {};                        //保存打的补丁
DWORD dwHookAddr;                                                      //保存HOOK的地址
DWORD dwHookRetAddr = 0;                                        //保存HOOK函数的返回点
BOOL IsHooked = FALSE;
DWORD dwPointPeopleBase;                                        //保存指向人的基址
DWORD dwMyBase;                                                                //保存我的基址
pstPos pMyPos;                                                                //我的坐标
pstPos pOtherPos;                                                      //指向人坐标

void __declspec(naked) HookProc()
{
      __asm {
                pushad;
                pushfd;

                mov eax, dword ptr ss : ;      
                mov dword ptr ds : , eax;      //保存指向人物的基址
               
                popfd;
                popad;

                push eax;
                push 0x0;
                push ecx;
                push 0x1;
                jmp dword ptr ds:;
      }
}

void GetHookInfo() {
      hMpdll = GetModuleHandle("mp.dll");
      hModule = GetModuleHandle(NULL);
      dwHookAddr = (DWORD)hMpdll + OFFSET;
      dwHookRetAddr = dwHookAddr + PATCHLENGTH;
      memset(&byHookPatch, 0X90, PATCHLENGTH);
      byHookPatch = 0XE9;
      *(DWORD *)&byHookPatch = (DWORD)HookProc - dwHookAddr - 5;
}

void SetHook() {
      DWORD dwOldProtect;
      if (!IsHooked) {
                VirtualProtect((LPVOID)(hMpdll + OFFSET), PATCHLENGTH, PAGE_EXECUTE_READWRITE, &dwOldProtect);
                WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwHookAddr, byHookPatch, PATCHLENGTH, NULL);
                VirtualProtect((LPVOID)(hMpdll + OFFSET), PATCHLENGTH, dwOldProtect, NULL);
                IsHooked = TRUE;
      }
}

void GetPosInfo() {
      dwMyBase = *(DWORD *)((DWORD)((DWORD *)*((DWORD *)((DWORD)hModule + 0X11069BC))) + 0X7C);
      pMyPos = pstPos(*(DWORD *)(dwMyBase + 4) + 8);
}

void CopyPos(pstPos a, pstPos b)
{
      a->x = b->x;
      a->y = b->y;
      a->z = b->z;
}

BOOL IsPeopleBase(DWORD base)
{
      if (dwPointPeopleBase > 0){
                if (!(0XF0000000 & dwPointPeopleBase))
                        return TRUE;
                try{
                        float blood = *(float *)(*(DWORD *)(dwMyBase + 4) + 160);
                        if (blood < 100 && blood > 0)
                              return TRUE;
                }
                catch (...)      {
                        return FALSE;
                }
      }      
      return FALSE;
}

DWORD WINAPI ThreadFunc(LPVOID lParam)
{
      GetHookInfo();
      SetHook();;
      GetPosInfo();
      while(TRUE){
                if (GetAsyncKeyState('E')){                        //按E吸人
                        if (IsPeopleBase(dwPointPeopleBase)) {
                              pOtherPos = pstPos(*(DWORD *)(dwPointPeopleBase + 4) + 8);
                              CopyPos(pOtherPos, pMyPos);
                        }
                }
                else if (GetAsyncKeyState('T')) {      //按T瞬移至指向人身边
                        if (IsPeopleBase(dwPointPeopleBase)) {
                              pOtherPos = pstPos(*(DWORD *)(dwPointPeopleBase + 4) + 8);
                              CopyPos(pMyPos, pOtherPos);
                        }
                }
                Sleep(100);
      }
      return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,DWORDul_reason_for_call,LPVOID lpReserved){
    switch (ul_reason_for_call){
    case DLL_PROCESS_ATTACH:
                CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
                break;
    }
    return TRUE;
}

```


!(https://pic.liesio.com/2020/03/31/15fbdfdc7e631.gif)

## 成果展示

最终,经过不断尝试,自己另外还制作了一个CS1.6的小辅助




实现功能的视频演示: (https://www.bilibili.com/video/av68192542)

辅助源码:(https://github.com/LiZiQiang191/CS1.6_Hook)

最后,制作不易,如果你觉得本文对你有帮助的话,希望你动动小手点点下面的评分,谢谢~~

xia1999 发表于 2020-3-17 17:53

GJH588 发表于 2020-3-17 17:43
看完了,学到了一点知识,谢谢楼主

emmmm,和室友在宿舍打游戏坑一波

xia1999 发表于 2020-3-17 21:37

炸弹二锅头 发表于 2020-3-17 19:46
哈哈哈哈,你这个太不厚道了,会被打的;记得以前上计算机课的时候,和班上同学打CS就有同学 ...

我高中的时候,也是这样子的。微机课全班男生连局域网打CS,一个课堂整成了网吧,然后有一次我开了gua,然后。。。那一次,我所向披靡。幸亏我没有改名,下课后都在骂是谁作弊

karius 发表于 2020-3-17 17:33

發自肺腑想說一句,,,牛逼!!!!{:1_921:}

zsky 发表于 2020-3-17 17:34

karius 发表于 2020-3-17 17:33
發自肺腑想說一句,,,牛逼!!!!

求评分,求评分:lol

IceRainow 发表于 2020-3-17 17:39

能过175平台的反作弊吗{:1_904:}

gtp_ 发表于 2020-3-17 17:40

厉害了。。。

adslbbxin 发表于 2020-3-17 17:42

大老牛逼!厉害厉害

GJH588 发表于 2020-3-17 17:43

看完了,学到了一点知识,谢谢楼主{:301_1004:}

fzp2077 发表于 2020-3-17 17:43

然而这有什么用{:1_904:}

zsky 发表于 2020-3-17 17:44

fzp2077 发表于 2020-3-17 17:43
然而这有什么用

有用的人自然有用

aixiaodemj 发表于 2020-3-17 17:52

谢谢分享 提供了一种思路 从以前拿CE改小游戏到现在一直很感兴趣
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 对CS1.6游戏的分析及心得