目前已经定位到动态库了,现在就来看看里面的功能逻辑是咋写的,这个环节的话其实跟reverse感觉关系已经不大了,更多的是需要对UE4引擎的理解。今天花了点时间,结合dump的游戏SDK、libUE4,把这个文件里的一些函数和变量名给还原了出来。
0x1 找到入口函数
1.首先是要定位到它的入口函数,一般来说动态库会用__attribute__((constructor)) void _init()指定,这个函数会在dlopen返回前调用。IDA里ctrl+s看segment表,找.init_array,定位到了第二个函数就是外挂逻辑的起始函数。
init.png (28.67 KB, 下载次数: 0)
下载附件
2024-8-7 17:07 上传
[Asm] 纯文本查看 复制代码int __cdecl hackmain()
{
pthread_t v1[2]; // [xsp+0h] [xbp-20h] BYREF
v1[1] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
pthread_create(v1, 0LL, (void *(*)(void *))mainthread_42FA0, 0LL);
return __android_log_print(3LL, &byte_E81A4, (const char *)&xmmword_E81B0);
}
进那个mainthread_42FA0里看看
[Asm] 纯文本查看 复制代码__int64 mainthread_42FA0()
{
v0 = Ram_uintptr_t_3CE10(&qword_E8198);
v1 = qword_E9940;
libUE4_E91D0 = v0;
if ( !qword_E9940 )
{
for ( i = *(_QWORD *)(v0 + 0xXXXXXXX); ; i = *(_QWORD *)(libUE4_E91D0 + 0xXXXXXX) )
{
qword_E9940 = i;
sleep(1u);
v1 = qword_E9940;
if ( qword_E9940 )
break;
}
}
if ( *(__int64 (__fastcall **)(__int64, __int64))(v1 + 16) != sub_42F30 )
{
qword_E9A48 = *(__int64 (__fastcall **)(_QWORD, _QWORD))(v1 + 16);
*(_QWORD *)(v1 + 16) = sub_42F30;
}
inlinehook(libUE4_E91D0 + 0xXXXXXX, &ESP_42E44, &qword_E9A40);
inlinehook(libUE4_E91D0 + 0xXXXXXX, bulletTrack_3F4D8, &qword_E9A20);
return 0LL;
}
这个ESP_42E44,和bulletTrack_3F4D8就是功能相关的代码。
0x2 绘制逻辑
首先看看绘制的实现,他这个绘制是引擎层的,hook的UGameViewportClient::PostRender(this*,UCanvas*)这个函数
[Asm] 纯文本查看 复制代码// UGameViewportClient::PostRender
__int64 __fastcall ESP_42E44(__int64 gameViewportclient, __int64 Ucanvas)
{
if ( !myUcanvas_E9288 )
myUcanvas_E9288 = Ucanvas;
windowWidth_E91E4 = ANativeWindow_getWidth();
windowHeight_E91E0 = ANativeWindow_getHeight();
screenSizeX_E91DC = RAM_int_3F82C(Ucanvas + 0x40);
v4 = RAM_int_3F82C(Ucanvas + 0x44);
K2DrawTextFuncPtr_E92A8 = (__int64 (__fastcall *)(_QWORD, __int64, float, float, float, float, float, float, float, float, float))(libUE4_E91D0 + K2DrawLineOffset_E802C);
K2DrawLineFuncPtr_E92B0 = (__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, long double, float, long double, long double, long double, long double, long double, long double, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD))(libUE4_E91D0 + K2DrawTextOffset_E8030);
screenSizeY_E91D8 = v4;
unknownFunc_E9210 = (__n128 (__fastcall *)(_QWORD, _QWORD, _QWORD))(libUE4_E91D0 + unknownFuncOffset1_E804C);
initData_3F710(); //
engineFontInit_3F5BC();
myESPLogic_3F934(screenSizeX_E91DC, screenSizeY_E91D8);// 外挂的绘制逻辑
return qword_E9A40(gameViewportclient, Ucanvas);
}
还原它initData和engineFontInit里初始化数据的变量名
[Asm] 纯文本查看 复制代码__int64 initData_3F710()
{
__int64 v0; // x0
__int64 WeaponCoreComponent; // x0
__int64 result; // x0
Uworld_E9960 = RAM_uintptr_t_3D610(libUE4_E91D0 + Uworld_E8038);
Ulevel_E9968 = RAM_uintptr_t_3D610(Uworld_E9960 + 0x30);
Array_E9970 = RAM_uintptr_t_3D610(Ulevel_E9968 + 0x98);
arrayCount_E9978 = RAM_int_3F82C(Ulevel_E9968 + 0xA0);
v0 = RAM_uintptr_t_3D610(libUE4_E91D0 + LocalPlayer_E8044);
selfPawn_E9208 = RAM_uintptr_t_3D610(v0 + 0x30);
PlayerCameraManager_E9200 = RAM_uintptr_t_3D610(selfPawn_E9208 + PlayerCameraManagerOffset_E8010);
selfPawn_E9980 = RAM_uintptr_t_3D610(selfPawn_E9208 + ControllerWeaponViewPawnOffset_E8014);
WeaponCoreComponent = RAM_uintptr_t_3D610(selfPawn_E9980 + 0xXXXX);
WeaponDataAsset_E9988 = RAM_uintptr_t_3D610(WeaponCoreComponent + 0xXXX);
localPlayer_E9958 = RAM_uintptr_t_3D610(libUE4_E91D0 + LocalPlayer_E8044);
result = 1LL;
lineofsighttoFuncPtr_E9218 = (__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD, long double, long double, long double))(libUE4_E91D0 + 0xXXXXXXX);
return result;
}
[Asm] 纯文本查看 复制代码__int64 __fastcall engineFontInit_3F5BC()
{
v10 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
gEngine_E9948 = RAM_uintptr_t_3D610(libUE4_E91D0 + gEngineOffset_E8034);
TinyFont_E9298 = RAM_uintptr_t_3D610(gEngine_E9948 + 0x30);
MediumFont_E92A0 = RAM_uintptr_t_3D610(gEngine_E9948 + 0x70);
LargeFont_E9290 = RAM_uintptr_t_3D610(gEngine_E9948 + 0x90);
v5 = 16;
v8 = &v5;
v9 = 4LL;
LegacyFontSize = RAM_uintptr_t_3D610(gEngine_E9948 + 0x30) + 0x134;
v7 = 4LL;
if ( (getpid() & 0x80000000) == 0 )
{
v0 = getpid();
syscall(dword_E805C, v0, &v8, 1LL, &v6, 1LL, 0LL);
}
v4 = 12;
v1 = RAM_uintptr_t_3D610(gEngine_E9948 + 0x90);
v8 = &v4;
v9 = 4LL;
v6 = v1 + 0x134;
v7 = 4LL;
result = getpid();
if ( (result & 0x80000000) == 0 )
{
v3 = getpid();
return syscall(dword_E805C, v3, &v8, 1LL, &v6, 1LL, 0LL);
}
return result;
}
myESPLogic_3F934就是外挂插入的绘制逻辑,这个函数太长了就不放了,里面也没啥好看的,就是常规的读世界数组,读一些人物状态,拿人物Vector3坐标,把数据转换成屏幕的Vector2的坐标,调用UE4的Kismet2的绘制函数在引擎层绘制(一般来说用的是Ahud和gameViewPortClient)
0x3 追踪逻辑
他这个hook 的应该是shootWeaponComponent里射击角度逻辑的函数,通过实际实践了一下得出float a4,float a5是子弹的射击角度
[Asm] 纯文本查看 复制代码__int64 __fastcall bulletTrack_3F4D8(__int64 a1, __int64 a2, long double a3, float a4, float a5)
{
if ( byte_E9020 && qword_E9950 )
{
v8 = *(float *)&qword_E9998 - *(float *)&qword_E99B8;
v9 = *((float *)&qword_E9998 + 1) - *((float *)&qword_E99B8 + 1);
*(float *)&a3 = atanf(
(float)(*(float *)&dword_E99A0 - *(float *)&dword_E99C0)
/ sqrtf((float)(v8 * v8) + (float)(v9 * v9)))
* -57.296;
v10 = atanf(v9 / v8) * 57.296;
a4 = v10 + 180.0;
if ( v8
0x4 小结
至此,这个样本分析完了,这个so里主要功能没有加密,所以分析起来比较简单,而且这个代码也是越看越眼熟,好像用的是github上的一个CSGOM的模板。如果遇到那种加密和混的比较厉害的,想偷懒的话感觉能先找一些比较常用的函数,例如pthread——create、getnativewindow这种,找到之后从那个函数倒着往回分析。
最近还研究了内核模块通信读取、断点,hook内核函数隐藏信息,编译的时候隐藏符号等,了解了一下比较常见的用户态检测,后面有空的时候也记录一下。