【原创】2021腾讯游戏安全技术复赛pc客户端安全wp

  • A+
所属分类:逆向工程

作者坛账号:Qfrost


题目说明:

  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退出
【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

再往下走下面两个循环分别是解密出flag和check flag
【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


起动调程序,通过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);
        }
}

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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


【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


回过头来继续看hack.exe。后面一大段都是解密出各种函数(这里就不一一截图了)
【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


 复制代码 隐藏代码
内容分析完了,用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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


Flag: 2RSRhrofoWtLeLrJCSlTireznrtx.oeLxuehyyAwbpCOZq0tsS7MZyVdOUoE8

DumpFile.dll

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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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


【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


获得计算公式,发现此处的基址是出于作弊模块空间的,回到初始化函数找到原基址,输入CE
【原创】2021腾讯游戏安全技术复赛pc客户端安全wp

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


以此为入手点,从头看这个作弊函数。这个大循环就是自瞄了
【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


会在CampName函数中对名字进行字符串匹配
【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


下面是距离计算逻辑

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

最后会对坐标做角度变换和归一化处理,最终计算得到上下摇摆角和左右偏移角填入游戏数据
【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

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

 复制代码 隐藏代码
[[[[[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搜索子弹数量,并对其下访问断点
【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp

[/img]

 【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


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

【原创】2021腾讯游戏安全技术复赛pc客户端安全wp


我通过在此处下断点,面对墙壁开枪,并修改了这些值,发现墙壁上没有出现弹孔,认为这些值就是控制子弹的起始坐标,而下面这个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);
    }
}

至此,外挂实现完毕

--

www.52pojie.cn


--

pojie_52

本文始发于微信公众号(吾爱破解论坛):【原创】2021腾讯游戏安全技术复赛pc客户端安全wp

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: