本次题目基于 UE4 引擎 开发的简化版射击游戏 Demo。游戏中存在多个 被刻意修改的异常功能点,导致角色行为、渲染效果等出现明显异常。参 赛者需通过逆向分析定位并修复这些问题,使游戏恢复正常逻辑。修复方 法包括但不仅限于静态修复或动态修改代码(inline hook 等)。 游戏内,左右两侧陀螺仪分别是控制人物的移动和转向,点击屏幕进行子弹 射击,没有跳跃功能。 本赛题没有检测调试工具,如果机器闪退,说明游戏版本不兼容,请及时更 换设备

评分标准

  1. 需要分析出游戏中存在的异常功能, 分析出越多异常功能得分越高。 如果仅修复异常,而没有给出异常功能的原理分析,不得分。注意某个异常功能可能存在多个影响因素
  2. 注意修复方案必须保证游戏所有原生 .so 库正常加载。通过库加载失败的方法修复,不得分。分析报告越详细、越全面,获得的分数也会越高。

也就是要完成下面的步骤

  1. 通过逆向和查看软件,分析出异常功能点
  2. 探究其原理
  3. 给出修复方法(同时保证原生库正常加载)

  1. 左陀螺仪移动异常:移动不是一步步走,而是直接朝着某个方向移动到碰撞发生
  2. 右陀螺仪无异常
  3. 子弹射击有异常:并非朝着视角正中心发射,而是有向着某个地方发射,其发射方式尚不清楚
  4. 渲染效果异常:人物存在透视情况

首先尝试静态分析 包名 com.ACE2025.Game

Android UE4 逆向

得到引擎是 4.27 版本,

https://dev.epicgames.com/documentation/zh-cn/unreal-engine/android-quick-start?application_version=4.27

可能需要学习一下 ue,我注意到这个项目和官方的 First Person 很像,可能就是通过修改而来,那么贴图也被修改了

竟然是 debugable 的,还挺良心

https://dev.epicgames.com/documentation/en-us/unreal-engine/first-person-template?application_version=4.27

找到一个长得很像的 demo,开始对照逆向

通过 dump sdk 搞定偏移

GWorld 得查这个字符串才行(靠上面一点)Travel aborted - new world is the same as current world

GWorld 0xAFAC398  
GName 0xADF07C0  
GUObject 0xAE34A98
Class: DefaultPawn.Pawn.Actor.Object
	float BaseTurnRate;//[Offset: 0x27c, Size: 0x4]
	float BaseLookUpRate;//[Offset: 0x280, Size: 0x4]
	PawnMovementComponent* MovementComponent;//[Offset: 0x288, Size: 0x8]
	SphereComponent* CollisionComponent;//[Offset: 0x290, Size: 0x8]
	StaticMeshComponent* MeshComponent;//[Offset: 0x298, Size: 0x8]
	bool bAddDefaultMovementBindings;//(ByteOffset: 0, ByteMask: 1, FieldMask: 1)[Offset: 0x2a0, Size: 0x1]
	void TurnAtRate(float Rate);// 0x938eadc
	void MoveUp_World(float Val);// 0x938eb8c
	void MoveRight(float Val);// 0x938ec3c
	void MoveForward(float Val);// 0x938ecec
	void LookUpAtRate(float Rate);// 0x938ea2c

之后是找 libGame.so 去看看外挂逻辑

是在 .init_array 里面起了一个线程,线程运行的主函数是 sub_1B9C

sub_30A0 sub_29A4 sub_2B6C sub_2D1C 之类的函数都是解密字符串的

0xB80 的函数是通过 pid 和库函数名找基地址的

  1. 择映射文件路径:
    • 如果第一个参数小于 0,读取 /proc/self/maps(当前进程)
    • 否则,使用 /proc/%d/maps 格式读取特定进程的映射
  2. 打开并读取内存映射文件:
    • 调用 fopen 打开映射文件
    • 检查是否成功打开
  3. 循环处理映射文件内容:
    • 循环读取映射文件的每一行(使用 fgets
    • 对于每一行,检查是否包含目标库名(通过 strstr
  4. 解析内存地址:
    • 当找到目标库名时,提取该行的第一个部分(内存地址)
    • 使用 strtok 分割字符串,获取地址部分
    • 使用 strtoul 将十六进制字符串转换为基址值

找关键结构:https://www.cnblogs.com/revercc/p/17641855.html dump skd:https://github.com/revercc/UE4Dumper.git

GWorld 得查这个字符串才行(靠上面一点)Travel aborted - new world is the same as current world 照着 UE4 源码看

GWorld 0xAFAC398  
GName 0xADF07C0  
GUObject 0xAE34A98

dump 到 sdcard 下,然后 pull

#!/bin/sh
adb push libs/arm64-v8a/ue4dumper64 /data/local/tmp/ue4dumper64
adb shell "su -c 'chmod 755 /data/local/tmp/ue4dumper64'"
adb shell "su -c '/data/local/tmp/ue4dumper64 --newue+ --strings --gname 0xADF07C0 --package com.ACE2025.Game --output /sdcard/Download'"
adb shell "su -c '/data/local/tmp/ue4dumper64 --newue+ --objs --gname 0xADF07C0 --guobj 0xAE34A98 --package com.ACE2025.Game --output /sdcard/Download'"
./ue4dumper64 --newue+ --sdkw --gname 0xADF07C0 --gworld 0xAFAC398 --package com.ACE2025.Game --output /sdcard/Download
./ue4dumper64 --newue+ --actors  --gname 0xADF07C0 --gworld 0xAFAC398 --package com.ACE2025.Game

dump 结果储存在 dump/ 下

分析外挂

函数 sub_1B9C 分析

第一眼看有:

  • sub_30A0 - 解密特定数据的函数
  • sub_B80 - 可能从文件获取配置或地址的函数
  • usleep(0x2710u) - 10 毫秒延时,可能用于降低性能影响

辅助函数分析

sub_30A0 - 数据解密函数

int8_t *__fastcall sub_30A0(int8_t *p_ones, int8_t *p_unkown_bytes) {
  if (dword_B664 == 1)
    return p_ones;
    
  for (i = 0; i < 10; ++i)
    p_ones[i] = p_unkown_bytes[i + 26] ^ p_unkown_bytes[i % 26];
    
  dword_B664 = 1;
  return p_ones;
}

类似的还有 sub_29A4 sub_2B6C sub_2D1C

0xB80 的函数

memory_patch_loop 是通过 pid 和库函数名找基地址的

  1. 择映射文件路径:
    • 如果第一个参数小于 0,读取 /proc/self/maps(当前进程)
    • 否则,使用 /proc/%d/maps 格式读取特定进程的映射
  2. 打开并读取内存映射文件:
    • 调用 fopen 打开映射文件
    • 检查是否成功打开
  3. 循环处理映射文件内容:
    • 循环读取映射文件的每一行(使用 fgets
    • 对于每一行,检查是否包含目标库名(通过 strstr
  4. 解析内存地址:
    • 当找到目标库名时,提取该行的第一个部分(内存地址)
    • 使用 strtok 分割字符串,获取地址部分
    • 使用 strtoul 将十六进制字符串转换为基址值

继续分析函数

decrypt_data_xor(library_name, encrypted_data);// libUE4.so
lib_base = find_library_base(0xFFFFFFFF, library_name);
app->base = lib_base;
v19 = sA0_1->base > 0x10000uLL;
if ( !v19 ){
    break;
}
base = app->base;
app->gworld =*(base + 0xAFAC398);
if ( sA0_2->gworld >= 0x10000uLL )
            break;

后面根据 SDK.txt 内容看偏移

Level SDK 看这里是 padding,因为这里和具体 UE4 版本有关了,参考 ue4dumper 的偏移量:

void patchUE422_64(){
    //Class: FNameEntry
    FNameEntryToNameString = 0xc;
    //Class: UStruct
    UStructToSuperStruct = 0x40;
    UStructToChildren = 0x48;
    //Class: UFunction
    UFunctionToFunctionFlags = 0x98;
    UFunctionToFunc = 0xc0;
    //Class: ULevel
    ULevelToAActors = 0x98;
    ULevelToAActorsCount = 0xa0;
}

继续用 dumper 分析 Actor 偏移情况

newue+ --actors --gname 0xADF07C0 --gworld 0xAFAC398 --package com.ACE2025.Game --output /sdcard/Download     <
Process name: com.ACE2025.Game, Pid: 28221
Base Address of libUE4.so Found At 744acbf000
UWorld: 7455c6b398 | World: 739672d060 | Name: FirstPersonExampleMap
Level: 739cba68c0 | Name: PersistentLevel
ActorList: 7396756880, ActorCount: 47

Id: 0, Addr: 7399787a20, Actor: WorldInfo
Id: 1, Addr: 739c95f380, Actor: LightmassImportanceVolume
Id: 2, Addr: 7399790d00, Actor: EditorCube8
Id: 3, Addr: 7399790ac0, Actor: EditorCube9
Id: 4, Addr: 7399792800, Actor: EditorCube10
Id: 5, Addr: 73997925c0, Actor: EditorCube11
Id: 6, Addr: 7399792380, Actor: EditorCube12
Id: 7, Addr: 7399792140, Actor: EditorCube13
Id: 8, Addr: 7399791f00, Actor: EditorCube14
Id: 9, Addr: 7399791cc0, Actor: EditorCube15
Id: 10, Addr: 7399791a80, Actor: EditorCube16
Id: 11, Addr: 7399791840, Actor: EditorCube17
Id: 12, Addr: 7399791600, Actor: EditorCube18
Id: 13, Addr: 73997913c0, Actor: EditorCube19
Id: 14, Addr: 7399791180, Actor: EditorCube20
Id: 15, Addr: 7399790f40, Actor: EditorCube21
Id: 16, Addr: 739e94e080, Actor: TemplateLabel
Id: 17, Addr: 7396737100, Actor: SkySphereBlueprint
Id: 18, Addr: 73997937c0, Actor: AtmosphericFog
Id: 19, Addr: 7399792ec0, Actor: SphereReflectionCapture
Id: 20, Addr: 7399790880, Actor: Floor
Id: 21, Addr: 7399790640, Actor: Wall1
Id: 22, Addr: 7399790400, Actor: Wall2
Id: 23, Addr: 73997901c0, Actor: Wall3
Id: 24, Addr: 739e94e2c0, Actor: Wall4
Id: 25, Addr: 7399792c80, Actor: BigWall
Id: 26, Addr: 7399792a40, Actor: BigWall2
Id: 27, Addr: 739c95ec00, Actor: NetworkPlayerStart
Id: 28, Addr: 7399793340, Actor: LightSource
Id: 29, Addr: 7398d6b0a0, Actor: PostProcessVolume
Id: 30, Addr: 7399793100, Actor: SkyLight
Id: 31, Addr: 739c957180, Actor: DefaultPhysicsVolume
Id: 32, Addr: 739cbacbc0, Actor: MyProjectGameMode
Id: 33, Addr: 739668e080, Actor: GameSession
Id: 34, Addr: 739668f4c0, Actor: ParticleEventManager
Id: 35, Addr: 73995d6d00, Actor: GameNetworkManager
Id: 36, Addr: 744392c800, Actor: AIController
Id: 37, Addr: 739e94d780, Actor: FirstPersonExampleMap_C
Id: 38, Addr: 73958c93a0, Actor: FirstPersonCharacter_C
Id: 39, Addr: 7399585b50, Actor: ThirdPersonCharacter
Id: 40, Addr: 739c958a80, Actor: GameStateBase
Id: 41, Addr: 7395f06970, Actor: AbstractNavData-Default
Id: 42, Addr: 73958cece0, Actor: PlayerController
Id: 43, Addr: 744395cf00, Actor: PlayerState
Id: 44, Addr: 73962c5580, Actor: PlayerCameraManager
Id: 45, Addr: 7398d67120, Actor: CameraActor
Id: 46, Addr: 744395c480, Actor: MyProjectHUD

之后找到 ActorList 中指向偏移 0xA63BE28 的那个 Actor

好像一般 Actors 数组获取应该是:先用 Level + 0xd8 获取 ActorCluster 指针,然后用 ActorCluster + 0x28 获取 Actors 数组,这里直接用引擎的 offset 了

*p_now_actor = *(int64_t **)(app->now + 8LL * app->i);
v32 = (unsigned __int64)*p_now_actor < 0x10000;
if ( v32 ){
 
  goto LABEL_29;
}
app->p_now_actor = *p_now_actor;
app->now_actor = *(_QWORD *)app->p_now_actor;
*p_now_actor_1 = app->now_actor;
if ( *p_now_actor_1 - app->lib_base == 0xA63BE28LL )

但是 dump 出来的东西里没有,推测是覆盖了,导致换了一个,接下去看

之后将 patched 位置 true

float RecoilAccumulationRate;//[Offset: 0x538, Size: 0x4]
CharacterMovementComponent* CharacterMovement;//[Offset: 0x288, Size: 0x8]

pawn charactor class

CharacterMovementComponent class

app->p_RecoilAccumulationRate4 = (char *)*p_now_actor + 0x538;
*(_DWORD *)app->p_RecoilAccumulationRate4 = 0;// 修改后坐力
app2 = app;
*(_QWORD *)p_CharacterMovement = (*p_now_actor)->move;
app2->move = *(move **)p_CharacterMovement;
*(_QWORD *)&app->p_MaxAcceleration = *(_QWORD *)p_CharacterMovement + 416LL;
*(_QWORD *)&app->MaxAcceleration = *(_QWORD *)&app->p_MaxAcceleration;
**(_DWORD **)&app->MaxAcceleration = 0x4E6E6B28;// 15625000f
sA0_4->p_movement = *(_QWORD *)p_CharacterMovement;
*(_DWORD *)(app->p_movement + 396LL) = 0x4E6E6B28; //float MaxWalkSpeed;//[Offset: 0x18c, Size: 0x4]
::patch_applied_flag = 1;

总结就是把后坐力关了,把加速度和上限给调高了,在这里把最终的赋值语句 nop 掉就行了

之后用 mt 重打包回去

继续分析异常

自瞄

尝试搜索 setrotation,在 Class: Controller.Actor.Object 搜到了

好像一般 UE 不是这么搞的?不管了

Id: 42, Addr: 73958cece0, Actor: PlayerController

Actor 里面有一个 Controller 控制位置和方向之类的,看看这个类:

现在是 Id: 42, Addr: 73941de020, Actor: PlayerController

所以应该找找 0x7390cce680+0x288 = 0x7390cce908

./stackplz -n com.ACE2025.Game --brk 0x6d8593cf88:rw --stack

在几个位置:

Process name: com.ACE2025.Game, Pid: 12999
Base Address of libUE4.so Found At 6db3d12000
UWorld: 6dbecbe398 | World: 6d8607e040 | Name: FirstPersonExampleMap
Level: 6d8c768440 | Name: PersistentLevel
ActorList: 6d86858c80, ActorCount: 47

0000000008b3860c

0000000008b3869c

0000000008b387c0 # w 修改点

函数 sub_8B3861C

[12999|13053] event_addr:0x6d8593cf88 hit_count:5, Backtrace:
  #00 pc 0000000008b387c0  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #01 pc 000000000670f3f8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #02 pc 000000000670feac  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #03 pc 0000000009268e34  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #04 pc 0000000009266e00  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #05 pc 0000000008fa0588  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #06 pc 0000000008f9f6f0  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #07 pc 0000000008f9f370  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #08 pc 0000000008fa7354  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #09 pc 00000000091fdb88  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #10 pc 00000000067cebac  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #11 pc 00000000067ce72c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #12 pc 00000000067cde20  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #13 pc 00000000091f9c00  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #14 pc 00000000091f73b8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #15 pc 0000000008d3b75c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #16 pc 0000000008c068ec  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #17 pc 0000000005af53b8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #18 pc 0000000005af3510  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so

[12999|13053] event_addr:0x6d8593cf88 hit_count:6, Backtrace:
  #00 pc 0000000008b387c0  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #01 pc 0000000008f9b600  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #02 pc 0000000008fa7354  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #03 pc 00000000091fdb88  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #04 pc 00000000067cebac  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #05 pc 00000000067ce72c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #06 pc 00000000067cde20  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #07 pc 00000000091f9c00  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #08 pc 00000000091f73b8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #09 pc 0000000008d3b75c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #10 pc 0000000008c068ec  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #11 pc 0000000005af53b8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #12 pc 0000000005af3510  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so

12 个的是一般情况,18 个是额外的

把 18 那个调用的 B 给 nop 就行了

function patch_rotate() {
    var lib = Module.findBaseAddress(so_name)
    if (!lib) {
        console.log("[x] cannot find lib");
    }
 
    var targetAddress=lib.add(0x670f3f8)
    Memory.patchCode(targetAddress, 4, code => {
        const writer = new Arm64Writer(code, { pc: targetAddress });
        writer.putNop();
        writer.flush();
    });
}

子弹乱飞

尝试定位到角色类,参考 https://dev.epicgames.com/documentation/zh-cn/unreal-engine/3---implementing-projectiles?application_version=4.27#%E5%AE%9E%E7%8E%B0%E5%8F%91%E5%B0%84%E5%87%BD%E6%95%B0 这个官方教程,能够大概了解发射东西是怎么实现的:

void AFPSCharacter::Fire()
{
	// 试图发射发射物。
	if (ProjectileClass)
	{
		// 获取摄像机变换。
		FVector CameraLocation;
		FRotator CameraRotation;
		GetActorEyesViewPoint(CameraLocation, CameraRotation);
 
		// 设置MuzzleOffset,在略靠近摄像机前生成发射物。
		MuzzleOffset.Set(100.0f, 0.0f, 0.0f);
 
		// 将MuzzleOffset从摄像机空间变换到世界空间。
		FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
 
		// 使目标方向略向上倾斜。
		FRotator MuzzleRotation = CameraRotation;
		MuzzleRotation.Pitch += 10.0f;
 
		UWorld* World = GetWorld();
		if (World)
		{
			FActorSpawnParameters SpawnParams;
			SpawnParams.Owner = this;
			SpawnParams.Instigator = GetInstigator();
 
			// 在枪口位置生成发射物。
			AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
			if (Projectile)
			{
				// 设置发射物的初始轨迹。
				FVector LaunchDirection = MuzzleRotation.Vector();
				Projectile->FireInDirection(LaunchDirection);
			}
		}
	}
}

首先是在 Charact 类里面有一个 AFPSCharacter::Fire 方法,在前面有提前绑定的操作 PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);,在开火的函数里面:

  1. 先找到摄像机的位置和朝向
  2. 往前一点点有一个移动矩阵,计算生成位置=摄像机位置 + 移动矩阵 * 摄像机朝向
  3. 生成朝向赋值摄像机朝向,向上抬一点
  4. 调用 SpawnActor,参数 ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams
  5. 调用生成的 Actor 的方法 FireInDirection

角色的 SKD 中的方法有:

现在 Id: 38, Addr: 6d821fc6a0, Actor: FirstPersonCharacter_C

没找到具体 fire 的函数,但是相关的成员还是找到了:

  • class MyProjectProjectile* ProjectileClass; // [Offset: 0x510, Size: 0x8] 这个是发射出去的东西
  • SceneComponent* FP_MuzzleLocation; // [Offset: 0x4c8, Size: 0x8] 这个是发射出去的位置

下个断点:

./stackplz -n com.ACE2025.Game --brk 0x6D821FCBB0:rw --brk 0x6D821FCB68:rw --stack 
findBTFAssets btf_file=a12-5.10-arm64_min.btf
[*] save maps to maps_20627.txt
set breakpoint at kernel:false, addr:0x6d821fcb68, type:3
start 1 modules
[20627|20678] event_addr:0x6d821fcb68 hit_count:1, Backtrace:
  #00 pc 000000000670f48c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #01 pc 000000000670feac  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #02 pc 0000000009268e34  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #03 pc 0000000009266e00  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #04 pc 0000000008fa0588  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #05 pc 0000000008f9f6f0  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #06 pc 0000000008f9f370  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #07 pc 0000000008fa7354  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #08 pc 00000000091fdb88  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #09 pc 00000000067cebac  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #10 pc 00000000067ce72c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #11 pc 00000000067cde20  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #12 pc 00000000091f9c00  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #13 pc 00000000091f73b8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #14 pc 0000000008d3b75c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #15 pc 0000000008c068ec  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #16 pc 0000000005af53b8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #17 pc 0000000005af3510  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so

[20627|20678] event_addr:0x6d821fcb68 hit_count:2, Backtrace:
  #00 pc 000000000670fdd4  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #01 pc 000000000670f654  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #02 pc 000000000670feac  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #03 pc 0000000009268e34  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #04 pc 0000000009266e00  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #05 pc 0000000008fa0588  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #06 pc 0000000008f9f6f0  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #07 pc 0000000008f9f370  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #08 pc 0000000008fa7354  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #09 pc 00000000091fdb88  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #10 pc 00000000067cebac  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #11 pc 00000000067ce72c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #12 pc 00000000067cde20  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #13 pc 00000000091f9c00  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #14 pc 00000000091f73b8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #15 pc 0000000008d3b75c  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #16 pc 0000000008c068ec  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #17 pc 0000000005af53b8  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so
  #18 pc 0000000005af3510  /data/app/~~1bxVHLcN_QNIlP4v2RHkWQ==/com.ACE2025.Game-M2L3aUB2Sn8YNEaDJpLBsQ==/lib/arm64/libUE4.so

0x670feac 嗯?怎么这么熟悉,不是之前 patch 过自瞄的函数吗?看来这就是我们需要重点分析的对象了

v36 = (float *)*((_QWORD *)a1 + 0x99); // 在这里read了发射位置?
 
x = location[116];
y = location[117];
z = location[118]; // 拿出来 

往上翻翻,if ( *((_QWORD *)a1 + 75) ) 这个块应该是一个开火状态标志,

v3 = rand(); // 获取随机数
v4 = a1[334]; // 获取 a1 对象偏移 334*sizeof(float) 处的浮点数,可能是基础散射度/力度等
// 下面这行是核心计算,看起来在调整某个值,可能是子弹的初始偏移角度或力度
v6 = a1[336] + (float)(v4 * (float)((float)((float)((float)(v3 & 0xFFFFFF) / 16777000.0) + -0.5) + a1[332]));
// (v3 & 0xFFFFFF) / 16777000.0: 将一个24位随机数归一化到 [0.0, 1.0) 区间
// + -0.5: 变为 [-0.5, 0.5) 区间
// + a1[332]: 再加上一个 a1 对象的成员值作为偏移
// * v4: 乘以 v4 进行缩放
// + a1[336]: 累加到 a1[336] 上
a1[335] = a1[335] + (float)(a1[331] * v4); // 另一个属性调整
a1[336] = v6; // 更新 a1[336]