欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > UE4外挂实现分析-PC端-附源码

UE4外挂实现分析-PC端-附源码

2025/4/19 16:30:50 来源:https://blog.csdn.net/Runnymmede/article/details/144229488  浏览:    关键词:UE4外挂实现分析-PC端-附源码

UE4外挂实现分析-PC端

游戏分析

分析工具:

Cheat Engine 7.5

x64dbg

IDA Pro

参考文章:

UE4逆向笔记之GWORLD GName GameInstance - 小透明‘s Blog

【项目源码下载】https://download.csdn.net/download/Runnymmede/90079718

本次分析的游戏使用UE4.22引擎开发,外挂实现功能有透视和自瞄,两项功能都基于游戏内玩家对象和敌人对象的坐标位置实现。UE4游戏内对象的结构如下图所示

在这里插入图片描述

图片中的对象偏移与UE引擎版本相关,存在误差。

根据上图的关系,游戏中所有的对象都挂在UWorld下面,通过UWorld->GameInstance->ULocalPlayer->LocalPlayer->PlayerController->Actor可以获取到游戏玩家的Actor对象,进而获取玩家的坐标等信息

通过PWorld->ULevel->ActorCountPWorld->ULevel->ActorArray可以遍历游戏中所有的Actor对象,包括敌人的Actor对象,进而获取敌人坐标信息,在一局游戏中,PWorld指针与UWorld相同

CE分析UWorld

开启游戏使用CE打开游戏进程

寻找游戏内能直接获取的与玩家信息有关的详细数据,游戏中子弹数量能够直接查看到准确数值,并且方便控制,因此使用CE查找子弹数量的地址

首先搜索准确的32位整数50

在这里插入图片描述

开枪减少子弹数量,继续搜索48

在这里插入图片描述

只剩两个地址,修改这两个地址处的值,查看游戏内子弹数量是否发生变化,

在这里插入图片描述

可以确定子弹数量储存在0x1E3EDF40684地址处,对该地址进行指针分析

在这里插入图片描述

根据GameInstancePlayerController的偏移关系0x38 -> 0x0 -> 0x30过虑到如下指针链

在这里插入图片描述

其中存在条指针链

"ShooterClient.exe"+02F6E6E8->0xD80->0x38->0x0->0x30->0x3B0->0x778->0x584
"ShooterClient.exe"+02F71060->0x160->0x38->0x0->0x30->0x3B0->0x778->0x584

因此可以分析出UWorldShooterClient.exe+02F6E6E8ShooterClient.exe+02F71060

UWorld = [ShooterClient.exe+0x02F71060]
GameInstance = [UWorld+0x160]
ULocalPlayer = [GameInstance+0x38]
LocalPlayer=[ULocalPlayer]
PlayerController = [LocalPlayer+0x30]
PlayerActor = [PlayerController+03B0]ReadProcessMemory(hProcess, (LPVOID)((BYTE*)baseAddr + 0x2E6E0C0), (LPVOID)&GName, 8, NULL);

继续使用浮点数模糊搜索玩家坐标、视角信息等,由于已经确定玩家子弹数量地址,因此可以缩小搜索范围在0x1E3EDF40684附近

得到如下指针信息

bullet = [[PlayerActor+0x778]+0x584]
posi_x = [[PlayerActor+0x3A0]+0x1A0]
posi_y = [[PlayerActor+0x3A0]+0x1A4]
posi_z = [[PlayerActor+0x3A0]+0x1A8]
persp_x = [[PlayerActor+0x3A0]+0x154]
persp_y = [[PlayerActor+0x3A0]+0x174]

基于上述信息,还能确定ULevel

ULevel = [UWorld+0x30]

ActorCountActorArray的偏移可以使用CE的结构体分析功能

在这里插入图片描述

经过分析,确定ActorCountActorArray的偏移

ActorCount = [ULevel+0xA0]
ActorArray = [ULevel+0x98]

遍历ActorArray可以获得游戏内所有的Actor对象,包含了敌人对象,但还需要识别是否为敌人,所以还需要查找对象的Name

CE分析GName

UE4.23以下版本使用的GName算法如下

BYTE *GetName(int id)
{int idx0 = id / 0x4000;int idx1 = id % 0x4000;BYTE *NameArray = [GName + idx0 * 8];BYTE *Name = [NameArray + idx1 * 8] + 0xC;return Name;
}

使用CE搜索进程内存,查找关键字符串ByteProperty

在这里插入图片描述

如果上一个字符串为None,则表示搜索到了正确位置,此处为游戏对象的Name表

由于ByteProperty字符串id为1,可以根据GetName算法逆推GName

搜索地址0x1E3D5EA0024-0xC

在这里插入图片描述

搜索0x1E3D5E80008-1*8

在这里插入图片描述

搜索0x1E3D5E70080

在这里插入图片描述

可以确定GName为ShooterClient.exe+2D310B0ShooterClient.exe+2E6E0C0

编写代码验证上述分析的偏移

int main()
{LPCWSTR procName = L"ShooterClient.exe";DWORD dwPID;HANDLE hProcess;LPVOID baseAddr;dwPID = getDwPidByName(procName);printf("PID: %d\n", dwPID);hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);if (hProcess == NULL){printf("open process failed\n");return 0;}baseAddr = getModuleBase(dwPID);printf("proc base: 0x%llx\n", baseAddr);LPVOID UWorld;LPVOID GName;LPVOID GameInstance;LPVOID ULocalPlayer;LPVOID LocalPlayer;LPVOID PlayerController;LPVOID PlayerActor;LPVOID PlayerPosition;LPVOID ULevel;DWORD ActorCount;LPVOID ActorArray;// 读取UWorldReadProcessMemory(hProcess, (LPVOID)((BYTE*)baseAddr + 0x02F71060), (LPVOID)&UWorld, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)UWorld + 0x160), (LPVOID)&GameInstance, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)GameInstance + 0x38), (LPVOID)&ULocalPlayer, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)ULocalPlayer, (LPVOID)&LocalPlayer, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)LocalPlayer + 0x30), (LPVOID)&PlayerController, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)PlayerController + 0x3B0), (LPVOID)&PlayerActor, 8, NULL);printf("\n");printf("UWorld: 0x%llx\n", UWorld);printf("GameInstance: 0x%llx\n", GameInstance);printf("ULocalPlayer: 0x%llx\n", ULocalPlayer);printf("LocalPlayer: 0x%llx\n", LocalPlayer);printf("PlayerController: 0x%llx\n", PlayerController);printf("PlayerActor: 0x%llx\n", PlayerActor);printf("\n");ReadProcessMemory(hProcess, (LPVOID)((BYTE*)UWorld + 0x30), (LPVOID)&ULevel, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)ULevel + 0xA0), (LPVOID)&ActorCount, 4, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)ULevel + 0x98), (LPVOID)&ActorArray, 8, NULL);// 读取玩家坐标FLOAT posi[3];ReadProcessMemory(hProcess, (LPVOID)((BYTE*)PlayerActor + 0x3A0), (LPVOID)&PlayerPosition, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)PlayerPosition+0x1A0), (LPVOID)posi, 0xC, NULL);printf("\n");printf("posi: [%f, %f, %f]\n", posi[0], posi[1], posi[2]);// 读取玩家视角FLOAT persp_x, persp_y;ReadProcessMemory(hProcess, (LPVOID)((BYTE*)PlayerPosition + 0x154), (LPVOID)&persp_x, 0x4, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)PlayerPosition + 0x174), (LPVOID)&persp_y, 0x4, NULL);printf("perspective: [%f, %f]\n", persp_x, persp_y);printf("\n");printf("ULevel: 0x%llx\n", ULevel);printf("ActorCount: %d\n", ActorCount);printf("ActorArray: 0x%llx\n", ActorArray);// 读取GNameReadProcessMemory(hProcess, (LPVOID)((BYTE*)baseAddr + 0x2E6E0C0), (LPVOID)&GName, 8, NULL);printf("GName: 0x%llx\n", GName);printf("\n");// 遍历ActorArryfor (DWORD i = 0; i < ActorCount; i++){LPVOID AActor;DWORD id;LPVOID PNameArray;LPVOID PName;CHAR name[0x100];ReadProcessMemory(hProcess, (LPVOID)((BYTE*)ActorArray + i * 8), (LPVOID)&AActor, 8, NULL);if (ReadProcessMemory(hProcess, (LPVOID)((BYTE*)AActor + 0x18), (LPVOID)&id, 4, NULL)){ReadProcessMemory(hProcess, (LPVOID)((BYTE*)GName + (id / 0x4000) * 8), (LPVOID)&PNameArray, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)PNameArray + (id % 0x4000) * 8), (LPVOID)&PName, 8, NULL);if (ReadProcessMemory(hProcess, (LPVOID)((BYTE*)PName + 0xC), (LPVOID)name, 0x100, NULL)){printf("%d: %s\n", i, name);}}}return 0;
}

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=assets%2F2023-11-12-21-35-26-image.png&pos_id=img-xid30bLc-17332748937

在这里插入图片描述
在这里插入图片描述

玩家的坐标、视角都已经找到了,并且Actor对象的name识别也成功了,猜测BotPawn_C为机器人玩家的Actor对象,猜测其坐标算法与玩家相同,CE结构体分析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

发现按照Actor->0x3A0->0x1A0的偏移确实可以找到机器人坐标

机器人玩家坐标计算如下

posi_x = [[AActor+0x3A0]+0x1A0]
posi_y = [[AActor+0x3A0]+0x1A4]
posi_z = [[AActor+0x3A0]+0x1A8]

修改以下代码可以获取所有机器人玩家坐标

// printf("%d: %s\n", i, name);
if (!strcmp(name, "BotPawn_C"))
{LPVOID botPosition;FLOAT botPosi[3];ReadProcessMemory(hProcess, (LPVOID)((BYTE*)AActor + 0x3A0), (LPVOID)&botPosition, 8, NULL);ReadProcessMemory(hProcess, (LPVOID)((BYTE*)botPosition + 0x1A0), (LPVOID)botPosi, 0xC, NULL);printf("bot: [%f, %f, %f] \n", botPosi[0], botPosi[1], botPosi[2]);
}

在这里插入图片描述

至此,已实现获取玩家坐标、玩家视角、敌人坐标的目标,对坐标数据进行数学处理,使用GUI工具绘制到屏幕上,即可实现透视效果,同样可以通过计算玩家视角需要转动的角度,实现自瞄的功能。

外挂分析
VMP脱壳DUMP

在这里插入图片描述

入口push call,典型vmp,使用API断点回溯确定程序逻辑是否加密

根据外挂实现原理,读取进程需要使用ReadProcessMemoryAPI,而这之前还需要使用OpenProcessAPI打开进程,OpenProcess需要的参数为进程PID,但是该外挂程序不需要提供PID,因此该外挂运行早期会使用某些方式获取目标游戏的PID,需要利用Thelp32功能,对CreateThelp32Snapshot下断点

在这里插入图片描述

ScyllaHide过VMP反调试

在这里插入图片描述

3处nop断下后F9运行

在这里插入图片描述

Thelp32断下,第一次是VMP反调试调用的,忽略掉,F9运行

在这里插入图片描述

Thelp32第二次断下,分析调用栈回溯

在这里插入图片描述

发现此处为典型的msvc编译器主函数调用入口

在这里插入图片描述

因此该层为start,可以确定程序逻辑未加密,向上找到程序入口点OEP

在这里插入图片描述

对OEP下断点,取消Thelp32断点,重新运行程序,3次nop之后OEP断下

在这里插入图片描述

使用Scylla插件DUMP外挂内存

在这里插入图片描述

使用Fix Dump修复DUMP文件的导入数据

在这里插入图片描述

删除带X的FThunk

运行恢复后的DUMP文件hack_dump_SCY.exe,外挂功能正常

在这里插入图片描述

外挂脱壳完成

外挂逻辑分析

使用IDA Pro打开脱壳后的hack_dump_SYC.exe分析逻辑,动态调试之后对主函数注释如下,程序中的字符串大部分被加密,算法比较简单,但是使用动态调试也可以直接得到解密之后的字符串

在这里插入图片描述

主要功能就是打开游戏进程,获取游戏加载地址,创建窗口等操作,API断点回溯时断下的位置在GetPidByName函数中

在这里插入图片描述

作弊的主要逻辑在CheatProc过程函数中

在这里插入图片描述

showGUI函数调用imGUI库在屏幕上显示窗口

在这里插入图片描述

这里使用GetAsyncKeyStateAPI判断HOME键是否被按下,HOME按下之后切换GUI显示状态

在这里插入图片描述

CheatMain里第一个和最后一个函数是用来刷新屏幕上显示的文本标签的,可以直接忽略

继续进入到cheatMain函数,这里是主要的外观逻辑实现

在这里插入图片描述

在这里插入图片描述

首先使用ReadProcessMemoryAPI读取进程内存,获取UWorldGName等数据,偏移的计算在游戏分析部分得到的偏移基本相同,对所有的全局变量进行注释,方便后续分析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

遍历游戏中所有的Actor对象,并且获取对象的name,与BotPawn_C进行比较,判断该AActor是否为机器人

在这里插入图片描述

是机器人时读取机器人坐标,根据玩家坐标、窗口分辨率计算是否在屏幕显示范围内,是的话则会在屏幕上显示玩家与机器人的距离

此部分还计算了机器人在屏幕上显示坐标与窗口中心的距离,循环结束后保持与屏幕中心距离最近的机器人坐标,用于自瞄功能

在这里插入图片描述

自瞄功能同样使用GetAsyncKeyState判断按键是否按下,这里判断的是鼠标右键,当鼠标右键按下时,修改玩家视角使其瞄向距离屏幕中心最近的机器人

至此,外挂程序功能分析完成。

【项目源码下载】https://download.csdn.net/download/Runnymmede/90079718

热搜词