[2021] 2021腾讯游戏安全技术复赛pc客户端安全wp

a1辅助网提供[2021] 2021腾讯游戏安全技术复赛pc客户端安全wp的下载地址,长期提供破解软件,各种线报福利等,67235是一个很好的福利资源网站

Qfrost 发表于 2021-4-12 14:21
我编辑页面显示都是正常的,麻烦站务帮我检查一下吧

md会禁止使用discuz的img标签功能,你预览是可以,发出来就不行了,主要原因还是你插入的方法不对,不需要复杂到复制图片地址,只需要点击图片就可以了,你看下我给你发的帮助链接,看下操作。

大师傅tql[2021] 2021腾讯游戏安全技术复赛pc客户端安全wp
tql  支持!感谢分享~[2021] 2021腾讯游戏安全技术复赛pc客户端安全wp

题目说明:

  1. shootgame是一个游戏,hack.exe是游戏shootergame的一个外挂程序。
  2. 运行shootgame游戏,运行hack.exe,成功执行外挂功能并分析外挂实现过程。
  3. 实现一个与hack.exe的功能相同的,但是游戏逻辑原理不同的外挂程序。(游戏逻辑原理不同:指外挂程序对读写游戏数据结构或代码的攻击内容不同,并不是读写内存方式、注入内存方式、外挂核心核心代码载体差异的不同)

评分标准:满分10分

  1. flag(2分)    成功执行hack功能,给出外挂执行成功的flag。
  2. 代码(3分)    与hack.exe外挂功能相同,但实现原理与hack.exe不同的程序源码,仅需要提供可编译的工程完整代码,不需要提供已经编译出来的bin文件。
  3. 文档(4分)    详细描述解题过程,如涉及编写程序,必须提供源代码。
  4. 时间(1分)    正确提交flag、代码、文档的顺序,第1名计1分,后每1名-0.05分。

很明显,今年是写挂了。本fw通了两个宵只能很勉强的做出几个简单的功能。羡慕带哥直接dump SDK两小时生成带几十个功能的盖。(师傅们tql

hack.exe

加载程序进IDA,main函数开头解密字符串,试图打开hack.dat文件,因为dat文件不存在,所以hack.exe退出

我随便创了一个文件然后往里面写了一些东西,然后hack.exe就可以往下运行读取其中的内容然后进decode解密,decode函数里面是SSE优化后的解密算法,其中有两个分支,若长度大于0x40会走SSE优化的算法,若小于则走下面的分支,但本质的解密算法是一样的。具体算法后面给出分析

这一部分是计算shellcode的长度,shellcode会用于dll的加载

再下面v9就是decode函数对hack.dat解密出来的内容,通过取值取出ProcName,结合上下文可以知道这个ProcName就是游戏的进程名,在下面的 CreateToolhelp32Snapshot + Process32First 进程枚举中搜索游戏进程

再往下走下面两个循环分别是解密出flag和check flag

如果check成功则解密出 flag:%s 字符串并调用printf函数输出flag

下面会解密出一个dll并申请空间用于后续的注入

起动调程序,通过setIP手动的让程序运行decode函数解密DLL,然后通过idc脚本对其dump确定是一个dll文件,附dump.idc

#include <idc.idc> #define PT_LOAD              1 #define PT_DYNAMIC           2 static main(void) {         auto ImageBase,StartImg,EndImg;         auto i,dumpfile;         StartImg=0x19B11F51440;         EndImg=0x19B11F51440 + 0xFA00;         if(dumpfile = fopen("D:\DumpFile","wb"))         {              dump(dumpfile,StartImg,EndImg);             fclose(dumpfile);         }  } static dump(dumpfile,startimg,endimg)  {         auto i;         auto size;         size = endimg-startimg;         for ( i=0; i < size; i=i+1 )          {                 fputc(Byte(startimg+i),dumpfile);         } }

利用注入器将DLL注入到游戏中,弹出提示,测试发现带有右键自瞄的外挂功能

回过头来继续看hack.exe。后面一大段都是解密出各种函数(这里就不一一截图了)

最后将dll,shellcode写入到游戏进程空间并起远线程调用shellcode加载dll

内容分析完了,用x64dbg对decode函数动态调试,获得解密逻辑,通过flag反推出正确hack.dat文件  附exp.py string = "2RSRhrofoWtLeLrJCSlTireznrtx.oeLxuehyyAwbpCOZq0tsS7MZyVdOUoE8x00x00x00" # code = [ #     0x32,0x52,0x53,0x52,0x68,0x72,0x6F,0x66,0x6F,0x57,0x74,0x4C,0x65,0x4C,0x72,0x4A #     ,0x43 ,0x53 ,0x6C ,0x54 ,0x69 ,0x72 ,0x65 ,0x7A ,0x6E ,0x72 ,0x74 ,0x78 ,0x2E ,0x6F ,0x65 ,0x4C   #     ,0x78 ,0x75 ,0x65 ,0x68 ,0x79 ,0x79 ,0x41 ,0x77 ,0x62 ,0x70 ,0x43 ,0x4F ,0x5A ,0x71 ,0x30 ,0x74   #     ,0x73 ,0x53 ,0x37 ,0x4D ,0x5A ,0x79 ,0x56 ,0x64 ,0x4F ,0x55 ,0x6F ,0x45 ,0x38 ,0x0, 0x0, 0x0] code = [ord(x) for x in string]  i = 0; j = 0; k = 0; for i in range(4):     for j in range(16):         code[k] += 0x13;         code[k] = code[k] & 0xFF         code[k] ^= 0x3F;         k += 1  for i in range(len(code)):     print(hex(code[i]), end=" ")  with open("hack.dat", 'wb') as fp:     fp.write(bytes(code))

可以看到外挂成功启动并输出flag

Flag: 2RSRhrofoWtLeLrJCSlTireznrtx.oeLxuehyyAwbpCOZq0tsS7MZyVdOUoE8

DumpFile.dll

直接看这个dll发现很乱,来回看了一下发现是封装了一个hook引擎进去。外挂注入后可以实现右键自瞄的功能,同时只有敌人离自己在一定范围内才会触发自瞄。因为是按键自瞄,直接想到GetAsyncKeyState函数。IDA看dll的导入表果然看到交叉引用,只有sub_180005050一个函数调用过该函数,确定此处为作弊功能

发现浮点数写操作,0x398很像是一个偏移,附加上去跟了一下

动调跟了发现这两个值分别是角色的上下摇摆角和左右偏移角。向上回溯寻找v16的来源定位到这个地方

获得计算公式,发现此处的基址是出于作弊模块空间的,回到初始化函数找到原基址,输入CE

以此为入手点,从头看这个作弊函数。这个大循环就是自瞄了

GetAimTarget函数会尝试匹配符合条件的自瞄目标,匹配成功则返回目标对象,否则返回-1,其也是通过枚举角色结构列表来实现的

然后会获取每个角色的名字进行匹配

会在CampName函数中对名字进行字符串匹配

下面是距离计算逻辑

通过屏幕中心点与敌人的屏幕坐标 距离 来匹配距离准星 最近的敌人,若能匹配到,敌人对象就会通过v3返回。至此GetAimTarget函数分析完毕。

通过GetAimTarget取到的敌人对象,则通过内存对其取坐标,易分析得0x164,0x168,0x16C分别为对象的X,Y,Z坐标

最后会对坐标做角度变换和归一化处理,最终计算得到上下摇摆角和左右偏移角填入游戏数据

至此外挂功能函数分析完毕

外挂攻击的数据(实现自瞄的方式)

[[[[[ShooterClient.exe + 2F71060 + 160] + 38] + 0] + 30] + 398]    // 上下摇摆角 [[[[[ShooterClient.exe + 2F71060 + 160] + 38] + 0] + 30] + 39C]    // 左右偏移角

外挂实现

写了两个外挂,编译环境均为  visual studio 2019 x64 release

Cheat1

纯外部跨进程的通过修改镜头角度的实现右键自瞄

#include <iostream> #include <stdio.h> #include <stdlib.h> #include <cstring> #include "Offset.hpp" #include "Myd3d.hpp" #include "GameControler.hpp" // #include "GameControlerByDriver.hpp" using namespace std;  CGameControler GameControler("ShooterClient.exe"); D3DXVECTOR3 local_pos, target_pos;  string GetName(DWORD64 Entity) {     DWORD64 Index = GameControler.read<DWORD>((Entity + 0x18));     DWORD64 Base = GameControler.read<DWORD64>((DWORD64)GameControler.GetGameBase() + offset_Name);     DWORD64 tmp1 = GameControler.read<DWORD64>(Base + 8 * ((Index) / 0x4000));      DWORD64 tmp2 = GameControler.read<DWORD64>(tmp1 + 8 * ((Index) % 0x4000));     return GameControler.ReadString((LPCVOID)(tmp2 + 0xC), 64); }  int Cheat() {     DWORD64 Uworld = GameControler.read<DWORD64>((DWORD64)GameControler.GetGameBase() + offsets_UWorld);     // printf("Uworld:%llxn", Uworld);     DWORD64 ULevel = GameControler.read<DWORD64>(DWORD64(Uworld + offsets_Ulevel));     DWORD64 AActor = GameControler.read<DWORD64>(DWORD64(ULevel + offsets_Actor));     // printf("AActor:%pn", AActor);     DWORD num = GameControler.read<DWORD>(DWORD64(ULevel + offsets_Actor) + 8);      DWORD64 GameInstance = GameControler.read<DWORD64>(DWORD64(Uworld + offsets_GameInstance));     DWORD64 LocalPlayer = GameControler.read<DWORD64>( GameControler.read<DWORD64>(DWORD64(GameInstance + offsets_LocalPlayer)) );     DWORD64 PlayerController = GameControler.read<DWORD64>(DWORD64(LocalPlayer + offsets_PlayerController));     DWORD64 LocalPawn = GameControler.read<DWORD64>(DWORD64(PlayerController + offsets_LocalPawn));     // printf("LocalPawn:%pn", LocalPawn);     // printf("My HP:%fnn", GameControler.read<float>((DWORD64)(LocalPawn + offsets_Health)));     DWORD64 playobject = GameControler.read<DWORD64>(DWORD64(LocalPawn + offsets_PlayObject));     float MyX = GameControler.read<float>(DWORD64(playobject + offsets_TargetX));     float MyY = GameControler.read<float>(DWORD64(playobject + offsets_TargetY));     float MyZ = GameControler.read<float>(DWORD64(playobject + offsets_TargetZ));     // printf(" Local : X: %f t Y : %f t Z : %fn", MyX, MyY, MyZ);     local_pos = GameControler.read<D3DXVECTOR3>(playobject + offsets_TargetX);      DWORD64* EntityList = (DWORD64*)malloc((num + 1) * sizeof(DWORD64*));     GameControler.Read((LPCVOID)AActor, EntityList, num * sizeof(DWORD64*));     for (int i = 0; i < num; ++i) {         DWORD64 Entity = EntityList[i];         if (Entity == LocalPawn)    continue;         if (!Entity)                continue;          float HP = GameControler.read<float>((DWORD64)(Entity + offsets_Health));         if (HP <= 0.f)               continue;          string sName = GetName(Entity);         if (strcmp(sName.c_str(), "BotPawn_C"))   continue;          DWORD64 playobject = GameControler.read<DWORD64>(DWORD64(Entity + offsets_PlayObject));         float targetX = GameControler.read<float>(DWORD64(playobject + offsets_TargetX));         float targetY = GameControler.read<float>(DWORD64(playobject + offsets_TargetY));         float targetZ = GameControler.read<float>(DWORD64(playobject + offsets_TargetZ));          // printf(" target[%d] : %s : X: %f t Y : %f t Z : %fn", i, sName.c_str(),  targetX, targetY, targetZ);         target_pos = GameControler.read<D3DXVECTOR3>(playobject + offsets_TargetX);     }     free(EntityList);     // printf("n");      if (GetAsyncKeyState(2) != 0) {          D3DXVECTOR3 angle = { 0, 0, 0 };         float diff_x = target_pos.x - local_pos.x;                   // X差         float diff_y = target_pos.y - local_pos.y;                   // Y差          angle.x = atan2f(target_pos.z - local_pos.z, sqrtf((float)(diff_x * diff_x) + (float)(diff_y * diff_y)) ) * 57.295784;         angle.y = (float)((float)(atan2f(diff_y, diff_x) * 360.0) * 0.25) / 1.5707963;         if (angle.x < 0.0)             angle.x = angle.x + 360.0;         if (angle.y < 0.0)             angle.y = angle.y + 360.0;         GameControler.write<D3DXVECTOR3>(PlayerController + offsets_Pitch, angle);         // printf("Write X : %f t Y : %fn", angle.x, angle.y);      } }  int main() {     printf("Strat Cheating......n");     while (1) {         Cheat();         // Sleep(500);     } }

Cheat2

思路:因为要实现自瞄,而镜头又不知道还有什么别的攻击方式,因此换了一个思路,把子弹出发的坐标改到敌人的坐标上,这样可以开枪直接就可以打死敌人 并且可以无视任何建筑物。我从子弹数量入手,找到游戏开枪的地方,开枪的地方一定会有一个子弹开始坐标,我们先用CE搜索子弹数量,并对其下访问断点

定位到了子弹数量减少处。因为要实现修改子弹发射点到敌人坐标处,我们需要找到子弹发射的地方,因此这里向上回溯

回溯了一层发现大量的call,我们在头部下断,粗粗看了一下这些call,发现在子弹减少call的上一个call里调用了rand函数。

因为弹道是具有随机性的, rand函数引起了我的注意,然后想起第一天在百度上搜到的关于这款游戏的开发文档的源码

上下看了一下发现其他部分也很像,认为这个call就是武器开火函数

[/img][img=300,0]

从call的头部下断 然后在游戏中开枪 再往下可以看到调用了函数取了一个坐标,我不知道这个坐标具体是个什么坐标 但是肯定是跟弹道有关的坐标

我通过在此处下断点,面对墙壁开枪,并修改了这些值,发现墙壁上没有出现弹孔,认为这些值就是控制子弹的起始坐标,而下面这个call就是碰撞call。如果能修改这个参数为敌人的坐标,应该就可以实现子弹全图自瞄。 我采用了外部注入shellcode hook此处,跳转到写入的自瞄shellcode上,完成了参数的修改。经测试,确实可以达到开枪后子弹能直接打中敌人并且无视建筑物的效果。 同时,在我添加bot后发现bot开枪也是经过的这个函数 所以我们hook这个函数 不仅可以让bot的子弹打不中我们 我们还可以打死这些bot (同时实现了无敌跟子弹穿墙追踪的效果)

下面贴一下shellcode

// AimBotShell push rcx                                                | 保护寄存器 压栈 push rdx                                                   | 保护寄存器 压栈 mov rcx,6666666666666666                            | target_pos_addr mov rdx,qword ptr ds:[rcx]                              | 读取xy 两个8字节 所以直接用rdx mov qword ptr ss:[rsp+70],rdx                           | 更改0x60这个位置 因为前面保护寄存器压栈了0x10字节,所以这里是0x70  mov edx,dword ptr ds:[rcx+8]                            | 读取z坐标 4字节 所以是edx mov dword ptr ss:[rsp+78],edx                           | pop rdx                                                 | 恢复寄存器 pop rcx                                                 | 恢复寄存器 先进后出 mulss xmm8,xmm0                                     | 执行被破坏的原代码 mulss xmm7,xmm0                                     | addss xmm8,dword ptr ss:[rsp+60]                        | push 66666666                                       |跳回原处 mov dword ptr ss:[rsp+4],6666                           | ret                                             |
// JmpShell 为了不影响寄存器 我们用栈做跳板 这样还能支持跨4gb跳转 push    66666666h mov     dword ptr [rsp+4], 6666h retn

待外挂启动后,会先将shellcode注入游戏空间,并跨进程的不断枚举游戏对象列表,发现存活的敌人便将其坐标写入自瞄坐标内存上,shellcode会在每次开枪的时候将自瞄坐标内存上的值替换到碰撞call的参数上,以此就可以实现站着不动开枪杀死全图敌人且无敌的效果。

// Cheat2.cpp include <iostream> #include <stdio.h> #include <stdlib.h> #include <cstring> #include "Offset.hpp" #include "Myd3d.hpp" #include "GameControler.hpp" // #include "GameControlerByDriver.hpp" using namespace std;  CGameControler GameControler("ShooterClient.exe");  D3DXVECTOR3 target_pos; DWORD64 target_pos_addr, payload_addr; BYTE AimBotShell[] = "x51x52x48xB9x66x66x66x66x66x66x66x66x48x8Bx11x48x89x54x24x70x8Bx51x08x89x54x24x78x5Ax59xF3x44x0Fx59xC0xF3x0Fx59xF8xF3x44x0Fx58x44x24x60x68x66x66x66x66xC7x44x24x04x66x66x00x00xC3"; /* AimBotShell push rcx | 保护寄存器 压栈 push rdx | 保护寄存器 压栈 mov rcx, 6666666666666666 | target_pos_addr mov rdx, qword ptr ds : [rcx] | 读取xy 两个8字节 所以直接用rdx mov qword ptr ss : [rsp + 70] , rdx | 更改0x60这个位置 因为前面保护寄存器压栈了0x10字节,所以这里是0x70 mov edx, dword ptr ds : [rcx + 8] | 读取z坐标 4字节 所以是edx mov dword ptr ss : [rsp + 78] , edx | pop rdx | 恢复寄存器 pop rcx | 恢复寄存器 先进后出 mulss xmm8, xmm0 | 执行被破坏的原代码 mulss xmm7, xmm0 | addss xmm8, dword ptr ss : [rsp + 60] | push 66666666 | 跳回原处 mov dword ptr ss : [rsp + 4] , 6666 | ret | */  BYTE JmpShell[] = "x68x66x66x66x66xC7x44x24x04x66x66x00x00xC3"; /* JmpShell push    66666666h mov     dword ptr [rsp+4], 6666h retn */  string GetName(DWORD64 Entity) {     DWORD64 Index = GameControler.read<DWORD>((Entity + 0x18));     DWORD64 Base = GameControler.read<DWORD64>((DWORD64)GameControler.GetGameBase() + offset_Name);     DWORD64 tmp1 = GameControler.read<DWORD64>(Base + 8 * ((Index) / 0x4000));     DWORD64 tmp2 = GameControler.read<DWORD64>(tmp1 + 8 * ((Index) % 0x4000));     return GameControler.ReadString((LPCVOID)(tmp2 + 0xC), 64); }  int Cheat() {      DWORD64 Uworld = GameControler.read<DWORD64>((DWORD64)GameControler.GetGameBase() + offsets_UWorld);     // printf("Uworld:%llxn", Uworld);     DWORD64 ULevel = GameControler.read<DWORD64>(DWORD64(Uworld + offsets_Ulevel));     DWORD64 AActor = GameControler.read<DWORD64>(DWORD64(ULevel + offsets_Actor));     DWORD num = GameControler.read<DWORD>(DWORD64(ULevel + offsets_Actor) + 8);     // 取对象数量      DWORD64 GameInstance = GameControler.read<DWORD64>(DWORD64(Uworld + offsets_GameInstance));     DWORD64 LocalPlayer = GameControler.read<DWORD64>( GameControler.read<DWORD64>(DWORD64(GameInstance + offsets_LocalPlayer)) );     DWORD64 PlayerController = GameControler.read<DWORD64>(DWORD64(LocalPlayer + offsets_PlayerController));     DWORD64 LocalPawn = GameControler.read<DWORD64>(DWORD64(PlayerController + offsets_LocalPawn));     // printf("My HP:%fnn", GameControler.read<float>((DWORD64)(LocalPawn + offsets_Health)));      DWORD64* EntityList = (DWORD64*)malloc((num + 1) * sizeof(DWORD64*));     GameControler.Read((LPCVOID)AActor, EntityList, num * sizeof(DWORD64*));     for (int i = 0; i < num; ++i) {         DWORD64 Entity = EntityList[i];         if (Entity == LocalPawn)    continue;         if (!Entity)                continue;          float HP = GameControler.read<float>((DWORD64)(Entity + offsets_Health));         if (HP <= 0.f)               continue;          string sName = GetName(Entity);         if (strcmp(sName.c_str(), "BotPawn_C"))   continue;          DWORD64 playobject = GameControler.read<DWORD64>(DWORD64(Entity + offsets_PlayObject));         float targetX = GameControler.read<float>(DWORD64(playobject + offsets_TargetX));         float targetY = GameControler.read<float>(DWORD64(playobject + offsets_TargetY));         float targetZ = GameControler.read<float>(DWORD64(playobject + offsets_TargetZ));          // printf(" target[%d] : %s : X: %f t Y : %f t Z : %fn", i, sName.c_str(),  targetX, targetY, targetZ);         target_pos = GameControler.read<D3DXVECTOR3>(playobject + offsets_TargetX);         GameControler.Write((LPVOID)target_pos_addr, target_pos, 12);  // 写入敌人坐标至自瞄坐标     }     free(EntityList); }  int main() {      printf("Strat Cheating......n");     target_pos_addr = (DWORD64)VirtualAllocEx(GameControler.GetHandle(), NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);     if (target_pos_addr == NULL) {         printf("VirtualAllocEx target_pos_addr Error!  Error Code:%dn", GetLastError());         exit(0);     }     payload_addr = (DWORD64)VirtualAllocEx(GameControler.GetHandle(), NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);     if (payload_addr == NULL) {         printf("VirtualAllocEx payload_addr Error!  Error Code:%dn", GetLastError());         exit(0);     }      DWORD64 HookAddress = (DWORD64)GameControler.GetGameBase() + 0x51C48C;     DWORD64 ReturnAddress = (DWORD64)GameControler.GetGameBase() + 0x51C49C;     *(DWORD64*)(AimBotShell + 0x4) = target_pos_addr;     *(DWORD*)(AimBotShell + 0x2E) = *(DWORD32*)(&ReturnAddress);     *(DWORD*)(AimBotShell + 0x36) = *(DWORD32*)((DWORD64)(&ReturnAddress) + 4);      *(DWORD*)(JmpShell + 0x1) = *(DWORD32*)(&payload_addr);                      *(DWORD*)(JmpShell + 0x9) = *(DWORD32*)((DWORD64)(&payload_addr) + 4);       GameControler.Write((LPVOID)payload_addr, AimBotShell, sizeof(AimBotShell) - 1);      DWORD OldProtect{ 0 };     BOOL nStatus = TRUE;     nStatus = VirtualProtectEx(GameControler.GetHandle(), (LPVOID)HookAddress, 0x1000, PAGE_EXECUTE_READWRITE, &OldProtect);     GameControler.Write((LPVOID)HookAddress, JmpShell, sizeof(JmpShell) - 1);     nStatus = VirtualProtectEx(GameControler.GetHandle(), (LPVOID)HookAddress, 0x1000, OldProtect, &OldProtect);      printf("target_pos_addr:%llx t payload_addr:%llxn", target_pos_addr, payload_addr);     if (nStatus == TRUE)         printf("Cheat Init Success!n");     else {         printf("Cheat Init Fail!t Error Code:%dn", GetLastError());         exit(-1);     }     while (1) {         Cheat();         // Sleep(500);     } } 

至此,外挂实现完毕

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register] [2021] 2021腾讯游戏安全技术复赛pc客户端安全wp

x

部分文章来自互联网,侵权删除www.a1fz.com/

www.a1fz.com A1fz网专注于福利分享,各种破解软件学习资料,视频教程等等,如有侵权告知管理员删除
A1fz.com,福利吧,宅男福利,宅男,福利社,福利,有福利 » [2021] 2021腾讯游戏安全技术复赛pc客户端安全wp

发表评论