IzayoiShiki
2024腾讯游戏安全安卓初赛复现

2024腾讯游戏安全安卓初赛复现

2024腾讯游戏安全竞赛安卓赛道初赛复现

前置准备

通过ida获取GWorldGNameGUObject的地址

GWorld

通过搜索Seamlesstravel FlushLevelStreaming字符串可以定位到GWorld的引用函数,在其上有一处地址会被初始化

image-20250121191838122

该地址便是GWorld的地址

字符串中若搜索不到Seamless字符串,有可能是以UTF-16格式储存的,可以通过alt+t进行搜索,但是会很慢

image-20250121192016712

还可以通过搜索UWorld相关的字符串进行定位,定位到对应函数,交叉引用看哪些函数调用了该函数,可以定位到GWorld的引用

GName

通过搜索ByteProperty字符串可以定位到FNamepool的构造函数,交叉引用该构造函数的调用,传入的参数地址即是GName的地址

image-20250121192311988

image-20250121192323011

GUObject

通过搜索CloseDisreagardForGC字符串对GUObject进行定位,在其之后对GUObject进行了引用

image-20250121210137921

GWorld:0xB32D8A8

GName:0xB171CC0

GUObject:0xB1B5F98

通过获取的三个地址,使用UE4dumper dump出所需Actor等内容

image-20250122210218644

通过id获取FName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function getFNameFromID(index) {
var FNameStride = 0x2
var offset_GName_FNamePool = 0x30;
var offset_FNamePool_Blocks = 0x10;

var offset_FNameEntry_Info = 0;
var FNameEntry_LenBit = 6;
var offset_FNameEntry_String = 0x2;

var Block = index >> 16;
var Offset = index & 65535;

var FNamePool = GName.add(offset_GName_FNamePool);
var NamePoolChunk = FNamePool.add(offset_FNamePool_Blocks + Block * 8).readPointer();
var FNameEntry = NamePoolChunk.add(FNameStride * Offset);

try {
if (offset_FNameEntry_Info !== 0) {
var FNameEntryHeader = FNameEntry.add(offset_FNameEntry_Info).readU16();
} else {
var FNameEntryHeader = FNameEntry.readU16();
}
} catch(e) {
return "";
}

var str_addr = FNameEntry.add(offset_FNameEntry_String);
var str_length = FNameEntryHeader >> FNameEntry_LenBit;
var wide = FNameEntryHeader & 1;
if (wide) return "widestr";

if (str_length > 0 && str_length < 250) {
var str = str_addr.readUtf8String(str_length);
return str;
} else {
return "None";
}
}

获取所有Actor地址和名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
var GUObject = {
getClass: function (obj) {
return ptr(obj).add(offset_UObject_ClassPrivate).readPointer();
},
getNameId: function (obj) {
try {
return ptr(obj).add(offset_UObject_FNameIndex).readU32();
}
catch (e) {
return 0;
}
},
getName: function(obj) {
if (this.isValid(obj)){
return getFNameFromID(this.getNameId(obj));
} else {
return "None";
}
},
getClassName: function(obj) {
if (this.isValid(obj)) {
var classPrivate = this.getClass(obj);
return this.getName(classPrivate);
} else {
return "None";
}
},
isValid: function(obj) {
return (ptr(obj) > 0 && this.getNameId(obj) > 0 && this.getClass(obj) > 0);
}
}

function getActorsAddr(){
var Level_Offset = 0x30
var Actors_Offset = 0x98

var Level = GWorld.add(Level_Offset).readPointer()
var Actors = Level.add(Actors_Offset).readPointer()
var Actors_Num = Level.add(Actors_Offset).add(8).readU32()
var actorsAddr = {};
for(var index = 0; index < Actors_Num; index++){
var actor_addr = Actors.add(index * 8).readPointer()
var actorName = GUObject.getName(actor_addr)
actorsAddr[actorName] = actor_addr;
}
return actorsAddr;
}

Section0

过法一:加血

通过获取的SDK,发现角色的生命值属性偏移为0x510

通过获取的Actors地址得到player地址,再根据偏移进行写入即可

1
2
3
4
5
6
7
8
9
10
11
function getPlayerAddr(){
var actorsAddr = getActorsAddr();
var playerAddr = actorsAddr["FirstPersonCharacter_C"];
return playerAddr;
}

function setPlayerHP(hp){
var playerAddr = getPlayerAddr();
var playerHP = playerAddr.add(0x510);
playerHP.writeFloat(hp);
}

过法二:穿墙

SDK中有函数SetActorEnableCollision,得到偏移

image-20250127153900606

在ida中根据偏移定位到中转函数,再根据return的函数定位到具体函数

image-20250127154425542

可以知道SetActorEnableCollision的真实地址为0x8C21320

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function setActorCollisionEnabled(actorAddr, bEnabled){
var functionAddr = moduleBase.add(0x8C21320);
var setActorCollisionEnabledFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'char']);
setActorCollisionEnabledFunc(ptr(actorAddr), bEnabled);
}

function cheatWall(){
var actorsAddr = getActorsAddr();
for (var actorName in actorsAddr){
if (actorName.includes("Wall")){
setActorCollisionEnabled(actorsAddr[actorName], 0);
}
}
}

过法三:瞬移

1
2
3
4
5
6
function setPlayerLocation(x, y, z){
var playerAddr = getPlayerAddr();
var functionAddr = moduleBase.add(0x8C3181C);
var setActorLocationFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'bool', 'pointer', 'bool', 'float', 'float', 'float']);
setActorLocationFunc(ptr(playerAddr), 0, ptr(0), 0, x, y, z);
}

Section1

先通过getActorLocation函数获取所有actor的位置,对所有高空actor调用SetVisibility函数使其可见

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
function setVisibility(actorAddr, bVisible, bForce){
var functionAddr = moduleBase.add(0x8E619BC);
var setVisibilityFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'int', 'int']);
setVisibilityFunc(ptr(actorAddr).add(0x130).readPointer(), bVisible, bForce);
}

class Vector {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
toString() {
return `(${this.x}, ${this.y}, ${this.z})`;
}
}

function dumpVector(vectorAddr){
const values = Memory.readByteArray(vectorAddr, 3 * 4);
const vec = new Vector(
new Float32Array(values, 0, 1)[0],
new Float32Array(values, 4, 1)[0],
new Float32Array(values, 8, 1)[0]
);
console.log(vec);
}

function getActorLocation(actorAddr){
var functionAddr = moduleBase.add(0x965ddf8);
var getActorLocationFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'pointer', 'pointer']);
var location = Memory.alloc(0x100);
try{
getActorLocationFunc(ptr(actorAddr), location, location);
dumpVector(location);
return location;
}
catch(e){
}
}

function cheatflag(){
var Level_Offset = 0x30
var Actors_Offset = 0x98

var Level = GWorld.add(Level_Offset).readPointer()
var Actors = Level.add(Actors_Offset).readPointer()
var Actors_Num = Level.add(Actors_Offset).add(8).readU32()
var actorsAddr = {};
for(var index = 0; index < Actors_Num; index++){
var actor_addr = Actors.add(index * 8).readPointer()
var vec = getActorLocation(actor_addr);
const values = Memory.readByteArray(vec, 3 * 4);
var z = new Float32Array(values, 8, 1)[0];
if (z > 2000){
try{
setVisibility(actor_addr, 1, 0);
}
catch(e){
}
}
}
}
1
Section1 = 8939

Section2

最开始尝试对所有Cube调用SetActorCollisionEnabled函数,发现没有用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function setActorCollisionEnabled(actorAddr, bEnabled){
var functionAddr = moduleBase.add(0x8C21320);
var setActorCollisionEnabledFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'char']);
setActorCollisionEnabledFunc(ptr(actorAddr), bEnabled);
}

function cheatCube(){
var actorsAddr = getActorsAddr();
for (var actorName in actorsAddr){
if (actorName.includes("Cube")){
setActorCollisionEnabled(actorsAddr[actorName], 1);
}
}
}

之后发现需要调用PrimitivewComponent中的SetCollisionEnabled函数,在表中便宜为0x660

image-20250201164501866

StaticMeshComponent属于PrimitiveComponent的子类,也可以调用该函数,通过StaticMeshActor获取对应地址

1
2
3
4
5
Class: StaticMeshActor.Actor.Object
StaticMeshComponent* StaticMeshComponent;//[Offset: 0x220, Size: 0x8]
bool bStaticMeshReplicateMovement;//(ByteOffset: 0, ByteMask: 1, FieldMask: 255)[Offset: 0x228, Size: 0x1]
enum NavigationGeometryGatheringMode;//[Offset: 0x229, Size: 0x1]
void SetMobility(byte InMobility);// 0x9967aa4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function setStaticMeshActorCollisionEnabled(actorAddr, bEnabled){
var functionAddr = actorAddr.add(0x220).readPointer().readPointer().add(0x660).readPointer();
var setStaticMeshActorCollisionEnabledFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'char']);
setStaticMeshActorCollisionEnabledFunc(ptr(actorAddr).add(0x220).readPointer(), bEnabled);
}

function cheatCube(){
var actorsAddr = getActorsAddr();
for (var actorName in actorsAddr){
if (actorName.includes("Cube")){
setStaticMeshActorCollisionEnabled(actorsAddr[actorName], 3);
}
}
}
1
Section2 = 008

Section3

SDK中有个getlastflag函数

1
2
Class: MyActor.Actor.Object
bool getlastflag();// 0x6a91fec

进入UE4.so中查找对应偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 sub_6A91A40()
{
char *v0; // x0
void *v1; // x0
void *v2; // x19
char *v3; // x0
__int64 (*v5)(void); // [xsp+8h] [xbp-8h]

sub_8C2D494();
v0 = (char *)malloc(0xBuLL);
strcpy(v0, "libplay.so");
v1 = dlopen(v0, 2);
if ( !v1 )
return 0LL;
v2 = v1;
v3 = (char *)malloc(0xEuLL);
strcpy(v3, "get_last_flag");
v5 = (__int64 (*)(void))dlsym(v2, v3);
if ( !dlerror() )
return 0LL;
strcpy((char *)malloc(0x21uLL), "input your flag in this variable");
return v5();
}

可以看到其调用了libplay.so中的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include<iostream>
#include<stdio.h>
using namespace std;

int main()
{
unsigned char enc[] = { 0x9D, 0x43, 0xB0, 0xD7, 0xD4, 0x53, 0x1C, 0x7D, 0xB4, 0xB6,
0xF6, 0x37, 0x23, 0x66, 0xDB, 0x92, 0x19, 0xDF, 0xCF, 0xF9,
0x9A, 0x92, 0xF2, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xD8, 0x98, 0x54, 0xC1, 0x64, 0x93, 0x56, 0x84,
0x38, 0x4F, 0x60, 0xBB, 0xA9, 0xA4, 0xCC, 0x88, 0x8D, 0x9F };
unsigned char table[] = { 0x31, 0xBB, 0x87, 0x09, 0xF8, 0xE4, 0xE7, 0x90, 0xF4, 0x99,
0xCC, 0x69, 0x5F, 0x04, 0x46, 0x89, 0x75, 0x5C, 0xF0, 0xCC,
0xBD, 0x2E, 0xA3, 0x68, 0x0F, 0xD6, 0xDC, 0x4E, 0x7A, 0x4D,
0x63, 0xD0, 0x60, 0x24, 0x2D, 0x75, 0x3C, 0x16, 0xFC, 0x41,
0x1D, 0x6E, 0xDF, 0xA4, 0x0D, 0xD3, 0xA6, 0x9D, 0xB9, 0x58,
0x88, 0xB2, 0xBB, 0x8D, 0x9F, 0x25, 0x1B, 0x11, 0xB0, 0x41,
0x2F, 0xCD, 0x10, 0xB6, 0x84 };
enc[32] ^= 0xD2u;
enc[33] ^= 0x94u;
enc[34] ^= 0x5Au;
enc[35] ^= 0xC1u;
enc[36] ^= 0x35u;
enc[37] ^= 0x85u;
enc[38] ^= 0x71u;
enc[39] ^= 0xBCu;
enc[40] ^= 0x71u;
enc[41] ^= 0x55u;
enc[42] ^= 0x5Bu;
enc[43] ^= 0xE7u;
enc[44] ^= 0x84u;
enc[45] ^= 0xEAu;
enc[46] ^= 0xA3u;
enc[47] ^= 0x72u;
enc[48] ^= 0x71u;
enc[49] ^= 0x61u;
enc[0] ^= 0xC8u;
enc[1] ^= 0x17u;
enc[2] ^= 0x81u;
enc[3] ^= 0xB1u;
enc[4] ^= 0xB7u;
enc[5] ^= 0x63u;
enc[6] ^= 0x7Bu;
enc[7] ^= 0x34u;
enc[8] ^= 0xEDu;
enc[9] ^= 0xF2u;
enc[10] ^= 0xB7u;
enc[12] ^= 0x47u;
enc[11] ^= 0x45u;
enc[13] ^= 0x1Cu;
enc[14] ^= 0xE3u;
enc[15] ^= 0xA2u;
enc[16] ^= 0x43u;
enc[17] ^= 0xEFu;
enc[18] ^= 0x97u;
enc[19] ^= 0x9Cu;
enc[20] ^= 0xF7u;
enc[21] ^= 0xA6u;
enc[22] ^= 0xC4u;
enc[23] ^= 0x76u;
table[0] ^= 0x70u;
table[1] ^= 0xF8u;
table[2] ^= 0xC2u;
table[3] ^= 0x39u;
table[4] ^= 0xBAu;
table[5] ^= 0xA0u;
table[6] ^= 0xA1u;
table[7] ^= 0xD7u;
table[8] ^= 0xBCu;
table[9] ^= 0xD0u;
table[10] ^= 0x86u;
table[11] ^= 0x22u;
table[12] ^= 0x13u;
table[13] ^= 0x49u;
table[14] ^= 8u;
table[15] ^= 0xC6u;
table[16] ^= 0x25u;
table[17] ^= 0xDu;
table[18] ^= 0xA2u;
table[19] ^= 0x9Fu;
table[20] ^= 0xE9u;
table[22] ^= 0xF5u;
table[21] ^= 0x7Bu;
table[25] ^= 0x8Fu;
table[23] ^= 0x3Fu;
table[24] ^= 0x57u;
table[27] ^= 0x2Fu;
table[26] ^= 0x86u;
table[28] ^= 0x18u;
table[30] ^= 7u;
table[29] ^= 0x2Eu;
table[31] ^= 0xB5u;
table[32] ^= 6u;
table[33] ^= 0x43u;
table[34] ^= 0x45u;
table[35] ^= 0x1Cu;
table[36] ^= 0x56u;
table[37] ^= 0x7Du;
table[38] ^= 0x90u;
table[39] ^= 0x2Cu;
table[40] ^= 0x73u;
table[42] ^= 0xAFu;
table[43] ^= 0xD5u;
table[41] ^= 1u;
table[44] ^= 0x7Fu;
table[45] ^= 0xA0u;
table[46] ^= 0xD2u;
table[47] ^= 0xE8u;
table[49] ^= 0x2Fu;
table[50] ^= 0xF0u;
table[48] ^= 0xCFu;
table[52] ^= 0xC1u;
table[51] ^= 0xCBu;
table[53] ^= 0xBCu;
table[54] ^= 0xADu;
table[55] ^= 0x16u;
table[56] ^= 0x2Fu;
table[57] ^= 0x24u;
table[58] ^= 0x86u;
table[59] ^= 0x76u;
table[60] ^= 0x17u;
table[61] ^= 0xF4u;
table[62] ^= 0x3Bu;
table[63] ^= 0x99u;
table[64] ^= 0x84u;
for (int i = 32; i < 50; i++) {
printf("%02X ", enc[i]);
}
cout << endl;
for (int i = 0; i < 24; i++) {
printf("%c", enc[i]);
}
cout << endl;
for (int i = 0; i < 65; i++) {
printf("%c", table[i]);
}
}

image-20250201171129084

1
Section3 = _Anti_Cheat_Expert

最终结果

最终得到flag

1
flag{8939008_Anti_Cheat_Expert}

全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
var moduleBase;
var GWorld;
var GWorld_Ptr_Offset = 0xB32D8A8;
var GName;
var GName_Offset = 0xB171CC0;
var GObjects;
var GObjects_Offset = 0xB1B5F98;

var offset_UObject_InternalIndex = 0xC;
var offset_UObject_ClassPrivate = 0x10;
var offset_UObject_FNameIndex = 0x18;
var offset_UObject_OuterPrivate = 0x20;

var GUObject = {
getClass: function (obj) {
return ptr(obj).add(offset_UObject_ClassPrivate).readPointer();
},
getNameId: function (obj) {
try {
return ptr(obj).add(offset_UObject_FNameIndex).readU32();
}
catch (e) {
return 0;
}
},
getName: function(obj) {
if (this.isValid(obj)){
return getFNameFromID(this.getNameId(obj));
} else {
return "None";
}
},
getClassName: function(obj) {
if (this.isValid(obj)) {
var classPrivate = this.getClass(obj);
return this.getName(classPrivate);
} else {
return "None";
}
},
isValid: function(obj) {
return (ptr(obj) > 0 && this.getNameId(obj) > 0 && this.getClass(obj) > 0);
}
}

function getFNameFromID(index) {
var FNameStride = 0x2
var offset_GName_FNamePool = 0x30;
var offset_FNamePool_Blocks = 0x10;

var offset_FNameEntry_Info = 0;
var FNameEntry_LenBit = 6;
var offset_FNameEntry_String = 0x2;

var Block = index >> 16;
var Offset = index & 65535;

var FNamePool = GName.add(offset_GName_FNamePool);
var NamePoolChunk = FNamePool.add(offset_FNamePool_Blocks + Block * 8).readPointer();
var FNameEntry = NamePoolChunk.add(FNameStride * Offset);

try {
if (offset_FNameEntry_Info !== 0) {
var FNameEntryHeader = FNameEntry.add(offset_FNameEntry_Info).readU16();
} else {
var FNameEntryHeader = FNameEntry.readU16();
}
} catch(e) {
return "";
}

var str_addr = FNameEntry.add(offset_FNameEntry_String);
var str_length = FNameEntryHeader >> FNameEntry_LenBit;
var wide = FNameEntryHeader & 1;
if (wide) return "widestr";

if (str_length > 0 && str_length < 250) {
var str = str_addr.readUtf8String(str_length);
return str;
} else {
return "None";
}
}

function set(modulename) {
moduleBase = Module.findBaseAddress(modulename);
GWorld = moduleBase.add(GWorld_Ptr_Offset).readPointer();
GName = moduleBase.add(GName_Offset);
GObjects = moduleBase.add(GObjects_Offset);
}

function getActorsAddr(){
var Level_Offset = 0x30
var Actors_Offset = 0x98

var Level = GWorld.add(Level_Offset).readPointer()
var Actors = Level.add(Actors_Offset).readPointer()
var Actors_Num = Level.add(Actors_Offset).add(8).readU32()
var actorsAddr = {};
for(var index = 0; index < Actors_Num; index++){
var actor_addr = Actors.add(index * 8).readPointer()
var actorName = GUObject.getName(actor_addr)
actorsAddr[actorName] = actor_addr;
}
return actorsAddr;
}

function getPlayerAddr(){
var actorsAddr = getActorsAddr();
var playerAddr = actorsAddr["FirstPersonCharacter_C"];
return playerAddr;
}

function setPlayerHP(hp){
var playerAddr = getPlayerAddr();
var playerHP = playerAddr.add(0x510);
playerHP.writeFloat(hp);
}

function setActorCollisionEnabled(actorAddr, bEnabled){
var functionAddr = moduleBase.add(0x8C21320);
var setActorCollisionEnabledFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'char']);
setActorCollisionEnabledFunc(ptr(actorAddr), bEnabled);
}

function cheatWall(){
var actorsAddr = getActorsAddr();
for (var actorName in actorsAddr){
if (actorName.includes("Wall")){
setActorCollisionEnabled(actorsAddr[actorName], 0);
}
}
}

function setPlayerLocation(x, y, z){
var playerAddr = getPlayerAddr();
var functionAddr = moduleBase.add(0x8C3181C);
var setActorLocationFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'bool', 'pointer', 'bool', 'float', 'float', 'float']);
setActorLocationFunc(ptr(playerAddr), 0, ptr(0), 0, x, y, z);
}

function setStaticMeshActorCollisionEnabled(actorAddr, bEnabled){
var functionAddr = actorAddr.add(0x220).readPointer().readPointer().add(0x660).readPointer();
var setStaticMeshActorCollisionEnabledFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'char']);
setStaticMeshActorCollisionEnabledFunc(ptr(actorAddr).add(0x220).readPointer(), bEnabled);
}

function cheatCube(){
var actorsAddr = getActorsAddr();
for (var actorName in actorsAddr){
if (actorName.includes("Cube")){
setStaticMeshActorCollisionEnabled(actorsAddr[actorName], 3);
}
}
}

function setVisibility(actorAddr, bVisible, bForce){
var functionAddr = moduleBase.add(0x8E619BC);
var setVisibilityFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'int', 'int']);
setVisibilityFunc(ptr(actorAddr).add(0x130).readPointer(), bVisible, bForce);
}

class Vector {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
toString() {
return `(${this.x}, ${this.y}, ${this.z})`;
}
}

function dumpVector(vectorAddr){
const values = Memory.readByteArray(vectorAddr, 3 * 4);
const vec = new Vector(
new Float32Array(values, 0, 1)[0],
new Float32Array(values, 4, 1)[0],
new Float32Array(values, 8, 1)[0]
);
console.log(vec);
}

function getActorLocation(actorAddr){
var functionAddr = moduleBase.add(0x965ddf8);
var getActorLocationFunc = new NativeFunction(functionAddr, 'void', ['pointer', 'pointer', 'pointer']);
var location = Memory.alloc(0x100);
try{
getActorLocationFunc(ptr(actorAddr), location, location);
dumpVector(location);
return location;
}
catch(e){
}
}

function cheatflag(){
var Level_Offset = 0x30
var Actors_Offset = 0x98

var Level = GWorld.add(Level_Offset).readPointer()
var Actors = Level.add(Actors_Offset).readPointer()
var Actors_Num = Level.add(Actors_Offset).add(8).readU32()
var actorsAddr = {};
for(var index = 0; index < Actors_Num; index++){
var actor_addr = Actors.add(index * 8).readPointer()
var vec = getActorLocation(actor_addr);
const values = Memory.readByteArray(vec, 3 * 4);
var z = new Float32Array(values, 8, 1)[0];
if (z > 2000){
try{
setVisibility(actor_addr, 1, 0);
}
catch(e){
}
}
}
}

function main(){
Java.perform(function(){
set("libUE4.so");
setPlayerHP(10000000);
cheatWall();
// setPlayerLocation(-1000, 100, 270);
cheatCube();
cheatflag();
}
);
}

setImmediate(main);

参考文章:

腾讯游戏安全2024安卓初赛复现-Android安全-看雪-安全社区|安全招聘|kanxue.com

CTF对抗-腾讯游戏安全技术竞赛2024初赛 安卓方向复盘_游戏逆向|游戏安全|yxfzedu.com

Author:IzayoiShiki
Link:http://izayoishiki.github.io/2025/02/01/2024腾讯游戏安全安卓初赛复现/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可