2024 腾讯游戏安全大赛 mobile 初赛 wp

news/2025/3/26 12:55:56/文章来源:https://www.cnblogs.com/lordtianqiyi/p/18789809

找关键结构体

https://www.cnblogs.com/revercc/p/17641855.html

找GWORLD

https://bbs.kanxue.com/thread-280042.htm

image-20250322111902706

可以发现是 TEXT包裹的,utf-16编码,ida alt + b搜索 53 00 65 00 61 00 6D 00 6C 00 65 00 73 00 73 00 54 00 72 00即可

image-20250324102128421

网上翻即可找到 GWorld

image-20250324102208236

对应地址: 0x00000000B32D8A8

找GNAME

直接搜 ByteProperty 字符串即可,函数的第一个参数即为GNAME,交叉应用一下即可拿到GNAME

image-20250324102327051

对应地址: 0x00000000B171CC0

找GUObjectArray

直接搜 CloseDisregardForGC字符串即可,下面就是GUObjectArray

image-20250324102242125

0x00000000B1B5F98

dump关键信息

查看ue4版本

参考: https://www.52pojie.cn/thread-1838396-1-1.html

image-20250324105327482

然鹅并没有查到版本,仿佛题目作者专门抹去了ue4的版本号,不过经过测试可以坑底的是一个较高版本的ue4


事后看了别人的wp,原来得搜utf16编码的字符串才能看到

image-20250324170343593

点击setup后勾选这个:

image-20250324170314353

就能看到了

image-20250324170236418

设置之后,前面定位关键信息都好定位了

dump 相关信息

使用这个工具: https://github.com/revercc/UE4Dumper

要dump出以下三种信息才好逆向:

# dump sdk:
# ./ue4dumper64 --package com.tencent.ace.match2024  --newue+ --sdkw  --gworld 0x00000000B32D8A8 --gname 0x00000000B171CC0 --output /data/local/tmp/111# dump objs:  # 获取类地址、对象地址
# ./ue4dumper64 --package com.tencent.ace.match2024  --newue+ --objs  --gname 0x00000000B171CC0 --guobj 0x00000000B1B5F98 --output /data/local/tmp/111# dump actors
# ./ue4dumper64 --package com.tencent.ace.match2024  --newue+ --actors  --gworld 0x00000000B32D8A8 --gname 0x00000000B171CC0 --output /data/local/tmp/111

SDK.txt就是dump出的SDK结构体信息,Object.txt是dump的游戏中所有的对象名以及所属类的信息,actor.txt是游戏中所有Actor的相关信息。

相关重要操作

frida遍历 64位 ue4的Actor

参考:

https://github.com/revercc/UE4Dumper

https://www.cnblogs.com/revercc/p/17629584.html

function pt_all_actor(){var libUE4_module = Module.findBaseAddress("libUE4.so")// console.log("libUE4_module is :", libUE4_module)var GWorld_Offset = 0x00000000B32D8A8var GName_Offset = 0x00000000B171CC0var GName = libUE4_module.add(GName_Offset);var GWorld = libUE4_module.add(GWorld_Offset).readPointer()var Level_Offset = 0x30var Level = GWorld.add(Level_Offset).readPointer()// console.log("Level :", Level)var Actors_Offset = 0x98var Actors = Level.add(Actors_Offset).readPointer()// console.log("Actors Array :", Actors)var Actors_Num = Level.add(Actors_Offset).add(8).readU32()// console.log("Actors_num :", Actors_Num)var ct = 0for(var index = 0; index < Actors_Num; index++){var actor = Actors.add(index * 8).readPointer()//通过角色actor获取其成员变量FNamevar FName_Offset = 0x18var FName = actor.add(FName_Offset);var FNameEntryAllocator = GNamevar FNamePool = FNameEntryAllocator.add(0x38)  // FNamePool//手动解析FNamePoolvar ComparisonIndex = FName.add(0).readU32()var FNameBlockOffsetBits = 16var FNameBlockOffsets = 65536var Block = ComparisonIndex >> FNameBlockOffsetBitsvar Offset = ComparisonIndex & (FNameBlockOffsets - 1)var NamePoolChunk = FNamePool.add(0x8 + Block * 8).readPointer()var FNameEntry = NamePoolChunk.add(Offset * 2)var FNameEntryHeader = FNameEntry.readU16()var isWide = FNameEntryHeader & 1var Len = FNameEntryHeader >> 6// console.log("actor : ", actor, " ", FNameEntry.add(2).readCString(Len))if(0 == isWide){var Name =  FNameEntry.add(2).readCString(Len)var actor_addr = actor;console.log("actor : ", actor, " ", FNameEntry.add(2).readCString(Len))}}
}

exec包裹的关键函数

我在hook / 主动调用的时候发现不管是函数参数个数还是参数类型都对不上,一度没有思路,甚至想下载ue4编译个demo研究研究。

后来想到2022年final中有一个32位的有符号的 libUE4.so文件,于是拿出来研究了一下

发现SDK中指向的函数一般都是 exec开头的函数,而这种函数一般有两种实现: 带exec头与不带exec头,而且exec开头的函数,其参数都是 AActor *this, FFrame *a2, void* a3 这种的形式,于是我猜测,可能execSetActorHiddenInGame函数只是在 SetActorHiddenInGame 上套了个壳,并猜测下图的 *this+0x160指针指向的就是 SetActorHiddenInGame 函数,实际上也确实如此。

image-20250324110324555

image-20250324110927126

实际上我们直接调用 内部的不包裹exec的函数即可。

参考: https://www.cnblogs.com/wodehao0808/p/6163673.html

section0

只要碰到障碍我,人物就会死亡。正常情况下不可能走出房屋。

way1

经过我的尝试,发现了 0x5e93094 偏移处的ReceiveHit函数,只要与障碍物解除就会调用这个函数,那我直接动态重写这个函数,把函数开头patch成 ret指令,即可逃脱房间

function section0_way1(){var base_addr = get_so_base("libUE4.so")var hook_list = [//0x6fcd294,0x5e93094,  // ReceiveHit,当碰到墙壁时就会触发这个函数]Memory.protect(base_addr.add(0x5e93094), 0x1000, 'rwx');base_addr.add(0x5e93094).writeByteArray([0xC0, 0x03, 0x5F, 0xD6])
}

way2

修改血量 FirstPersonCharacter Actor的生命值

image-20250324111300908

原本的生命值是100.0,那我们直接给一个极大的值:

function hook_find_actor( find_actor_name,cnt=0){var libUE4_module = Module.findBaseAddress("libUE4.so")// console.log("libUE4_module is :", libUE4_module)var GWorld_Offset = 0x00000000B32D8A8var GName_Offset = 0x00000000B171CC0var GName = libUE4_module.add(GName_Offset);var GWorld = libUE4_module.add(GWorld_Offset).readPointer()var Level_Offset = 0x30var Level = GWorld.add(Level_Offset).readPointer()// console.log("Level :", Level)var Actors_Offset = 0x98var Actors = Level.add(Actors_Offset).readPointer()// console.log("Actors Array :", Actors)var Actors_Num = Level.add(Actors_Offset).add(8).readU32()// console.log("Actors_num :", Actors_Num)var ct = 0for(var index = 0; index < Actors_Num; index++){var actor = Actors.add(index * 8).readPointer()//通过角色actor获取其成员变量FNamevar FName_Offset = 0x18var FName = actor.add(FName_Offset);var FNameEntryAllocator = GNamevar FNamePool = FNameEntryAllocator.add(0x38)  // FNamePool//手动解析FNamePoolvar ComparisonIndex = FName.add(0).readU32()var FNameBlockOffsetBits = 16var FNameBlockOffsets = 65536var Block = ComparisonIndex >> FNameBlockOffsetBitsvar Offset = ComparisonIndex & (FNameBlockOffsets - 1)var NamePoolChunk = FNamePool.add(0x8 + Block * 8).readPointer()var FNameEntry = NamePoolChunk.add(Offset * 2)var FNameEntryHeader = FNameEntry.readU16()var isWide = FNameEntryHeader & 1var Len = FNameEntryHeader >> 6// console.log("actor : ", actor, " ", FNameEntry.add(2).readCString(Len))if(0 == isWide){var Name =  FNameEntry.add(2).readCString(Len)var actor_addr = actor;if (Name === find_actor_name){if(ct == cnt){// console.log("actor : ", actor, " ", FNameEntry.add(2).readCString(Len))return actor_addr}else{ct++}}}}
}function section0_way2(){var TriggerBox_addr = hook_find_actor("FirstPersonCharacter_C")TriggerBox_addr.add(0x510).writeFloat(1000000.0)
}

way3

后续找到了生命值碰墙的扣血函数:

ReceiveHit(offset: 0x5e93094)	---> no_exec_ReceiveHit(offset: 0x5E843D4)  ---> Hp_sub(offset: 0x5E840C8)

image-20250324111729118

直接nop掉0x5E840F8处的指令即可:

function section0_way3(){var base_addr = get_so_base("libUE4.so")Memory.protect(base_addr.add(0x000000005E840F8), 0x1000, 'rwx');base_addr.add(0x000000005E840F8).writeByteArray([0x1F, 0x20, 0x03, 0xD5])
}

way4

锁定生命值,类似于CE的锁定值功能,还不会实现。。。

section1

感觉这一section最难,很难猜到作者让干什么,我想了很多情况,试了好多次才成功。

走的弯路

感觉主要的难点是猜不到对应的对象以及对应的类,那就不好解析字段,只能对Actor对象进行一个个尝试。到最后才发现,既然都继承了Actor字段,那直接从Actor字段就可以设置大部分的属性。(也并不是绝对的,比如section2设置了Actor全局的碰撞属性并没影响到组件的相关属性)

一开始我猜测被隐藏的字符串是TextRenderActor类的对象,因此我开始我走向弯路:

  • 猜测文字渲染被透明化了,因此我通过调用Set_Color函数来吧字体变得透明。这里让所有的Actor都主动调用了这个方法(实际上让 只上所有TextRenderActor 类的对象调用即可,其他的类的对象调用巧不巧还会出错,得设置好异常处理函数)

    代码实现如下:

    function set_Color(Actor_Name,color){   try{var base_addr = get_so_base()var Actor_addr = hook_find_actor(Actor_Name)var TextRenderComponent = Actor_addr.add(0x220).readPointer()// 需要注意的是,如果直接修改颜色字段而不调用相关函数,则需要刷新一下才能实现,刷新方法可以通过下面这种方法实现,我的评价是不如直接调用SetTextRenderColor()函数//  => set_Color()//  => call_SetActorHiddenInGame("TextRenderActor",1,0)//  => Thread.sleep(1); // 暂停 3 秒//  => call_SetActorHiddenInGame("TextRenderActor",0,0)// var old_color = TextRenderComponent.add(0x474).readU32(color)// console.log(hex(old_color))// TextRenderComponent.add(0x474).writeU32(color)var no_exec_SetTextRenderColor = base_addr.add(0x000000008EAC0F0)const SetTextRenderColor = new NativeFunction(no_exec_SetTextRenderColor, 'void', ['pointer','uint']);SetTextRenderColor(TextRenderComponent,color)}catch(e){console.log(e)}}

    事实上,就算让所有的Actor都主动调用了 SetTextRenderColor函数也是没有作用。

  • 获取相关文字,到这里我还是不信邪,因此主动调用了 TextRenderActor类的所有文字,并手动解析了FText结构体

    // class FTextData {
    //     public:
    //         char pad_0x0000[0x28];  //0x0000
    //         wchar_t* Name;              //0x0028 
    //         __int32 Length;               //0x0030 
    //     };
    //     struct FText {
    //         FTextData* Data;
    //         char UnknownData[0x10];
    //         wchar_t* Get() const {
    //             if (Data) 
    //                 return Data->Name;
    //             return nullptr;
    //         }
    //     };
    function get_FText_mess(Actor_Name){            // 确实能找到所有字符串,但没啥用try{var base_addr = get_so_base()var Actor_addr = hook_find_actor(Actor_Name)var TextRenderComponent = Actor_addr.add(0x220).readPointer()var FText = TextRenderComponent.add(0x448)// console.log(`FText: ${hexdump(FText.readByteArray(0x20))}`)var FTextData = FText.add(0).readPointer()// console.log(`FTextData: ${hexdump(FTextData.readByteArray(0x50))}`)var FText_Name_pointer = FTextData.add(0x28).readPointer()var FText_len = FTextData.add(0x30).readU32()var real_string = FText_Name_pointer.readUtf16String()console.log(`FText_Name: ${hexdump(FText_Name_pointer.readByteArray(0x50))}`)console.log(`FText_len: ${FText_len}`)console.log(`=> ${real_string}`)console.log("\n")}catch(e){console.log(e)}
    }
    

    事实上,读取了所有字符串,根本没有有用的。

  • 于是决定转变思路。"被隐藏的可能并不是TextRenderActor类的对象"。

    后来分析了Actor.txt与Objects.txt,发现绝大部分对象都属于StaticMeshActor类或者继承了这个类。因此我打算从这个类入手

    image-20250324145753332

    如下方代码所示,我在 StaticMeshComponent类中找到了两个api: SetHiddenInGameSetActorHiddenInGame,我让所有的Actor都call了这两个api,然鹅没什么用,后来检查了 behidden 字段,发现基本所有的对象基本上 hidden字段都为0,也就是都没被隐藏。既然都没被隐藏,那字符串是怎么没的呢?一时间我陷入了迷茫。

    function check_behidden(Actor_Name){var Actor_addr = hook_find_actor(Actor_Name)var behidden = Actor_addr.add(0x58).readU8()console.log(`${Actor_Name}: hidden: ${(behidden >> 5) & 1}`)
    }function call_SetActorHiddenInGame(Actor_Name,behidden,sel = 0){        // 确实能隐藏物体,比如说Cubetry{var Actor_addr = hook_find_actor(Actor_Name,sel)var a1_0 = Actor_addr.add(0).readPointer()var no_exec_SetActorHiddenInGame = a1_0.add(0x310).readPointer()const SetActorHiddenInGame = new NativeFunction(no_exec_SetActorHiddenInGame, 'void', ['pointer','bool']);SetActorHiddenInGame(Actor_addr,behidden)}catch(e){console.log(`${Actor_Name} error:  ${e}`)}}
    function StaticMeshActor_call_SetHiddenInGame(Actor_Name,NewHidden,bPropagateToChildren,sel = 0){  // 并没用try{var base_addr = get_so_base()var Actor_addr = hook_find_actor(Actor_Name,sel)var StaticMeshComponent = Actor_addr.add(0x220).readPointer() // StaticMeshComponent.MeshComponent.PrimitiveComponent.SceneComponent.ActorComponent.Objectvar no_exec_SetHiddenInGame =  base_addr.add(0x000000008E61C70)const SetHiddenInGame = new NativeFunction(no_exec_SetHiddenInGame, 'void', ['pointer','bool','bool']);SetHiddenInGame(StaticMeshComponent,NewHidden,bPropagateToChildren)console.log(`${Actor_Name} call_SetHiddenInGame`)}catch(e){console.log(`${Actor_Name} error:  ${e}`)}}function section1(){try{// StaticMeshActor.Actor.Object// call_SetActorHiddenInGame("Cube",1)// var list = ["TemplateLabel","SkySphereBlueprint","AtmosphericFog","SphereReflectionCapture","NetworkPlayerStart","LightSource","PostProcessVolume","SkyLight","EditorCube8","EditorCube9","EditorCube10","EditorCube11","EditorCube12","EditorCube13","EditorCube14","EditorCube15","EditorCube16","EditorCube17","EditorCube18","Floor","Wall1","Wall2","Wall3","Wall4","BigWall","BigWall2","Wall_400x400","Wall_400x401","Wall_400x402","Wall_400x403","Wall_400x404","PointLight","Floor_400x400","Floor_400x401","TextRenderActor","TextRenderActor2","TextRenderActor3","Wall_Door_400x400","SM_Door","TriggerBox","TextRenderActor4","Wall_400x405","Wall_400x406","SM_MERGED_Shape_Pipe_Flag","Plane_Blueprint","Cube","Cube2","Cube3","Wall_400x407","Wall_400x408","TextRenderActor6","TextRenderActor10","Wall_400x409","Wall_400x410","TextRenderActor12","TextRenderActor13","TextRenderActor14","TextRenderActor15","Actor","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","SM_MERGED_Shape_Pipe_Flag_1_Blueprint2","SM_MERGED_Shape_Pipe_Flag_1_Blueprint3","SM_MERGED_Shape_Pipe_Flag_1_Blueprint","EditorCube19","Shape_Pipe_Flag","TextRenderActor8","TextRenderActor9","TextRenderActor11","TextRenderActor16","Wall_400x411","TextRenderActor17","DefaultPhysicsVolume","FirstPersonGameMode_C","GameSession","ParticleEventManager","GameNetworkManager","FirstPersonExampleMap_C","FirstPersonCharacter_C","GameStateBase","PlayerController","PlayerState","PlayerCameraManager","CameraActor","FirstPersonHUD_C"]// var cnt = -1// for(var i in list){//     const name = list[i].trim()//     if(name === "Shape_Pipe_Flag"){//         cnt += 1//         call_SetActorHiddenInGame(name,0,cnt)//     }else{//         call_SetActorHiddenInGame(name,0)//     }// }// call_ StaticMeshActor_call_SetHiddenInGame ,不可以// var list = ["TemplateLabel","SkySphereBlueprint","AtmosphericFog","SphereReflectionCapture","NetworkPlayerStart","LightSource","PostProcessVolume","SkyLight","EditorCube8","EditorCube9","EditorCube10","EditorCube11","EditorCube12","EditorCube13","EditorCube14","EditorCube15","EditorCube16","EditorCube17","EditorCube18","Floor","Wall1","Wall2","Wall3","Wall4","BigWall","BigWall2","Wall_400x400","Wall_400x401","Wall_400x402","Wall_400x403","Wall_400x404","PointLight","Floor_400x400","Floor_400x401","TextRenderActor","TextRenderActor2","TextRenderActor3","Wall_Door_400x400","SM_Door","TriggerBox","TextRenderActor4","Wall_400x405","Wall_400x406","SM_MERGED_Shape_Pipe_Flag","Plane_Blueprint","Cube","Cube2","Cube3","Wall_400x407","Wall_400x408","TextRenderActor6","TextRenderActor10","Wall_400x409","Wall_400x410","TextRenderActor12","TextRenderActor13","TextRenderActor14","TextRenderActor15","Actor","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","SM_MERGED_Shape_Pipe_Flag_1_Blueprint2","SM_MERGED_Shape_Pipe_Flag_1_Blueprint3","SM_MERGED_Shape_Pipe_Flag_1_Blueprint","EditorCube19","Shape_Pipe_Flag","TextRenderActor8","TextRenderActor9","TextRenderActor11","TextRenderActor16","Wall_400x411","TextRenderActor17","DefaultPhysicsVolume","FirstPersonGameMode_C","GameSession","ParticleEventManager","GameNetworkManager","FirstPersonExampleMap_C","FirstPersonCharacter_C","GameStateBase","PlayerController","PlayerState","PlayerCameraManager","CameraActor","FirstPersonHUD_C"]// var cnt = -1// for(var i in list){//     const name = list[i].trim()//     if(name === "Shape_Pipe_Flag"){//         cnt += 1//         StaticMeshActor_call_SetHiddenInGame(name,0,1,cnt)//     }else{//         StaticMeshActor_call_SetHiddenInGame(name,0,1)//     }// }// 检测是否被隐藏,不是这样的作法,不过通过这个我发现triggerbox就是本就存在的东西// var list = ["TemplateLabel","SkySphereBlueprint","AtmosphericFog","SphereReflectionCapture","NetworkPlayerStart","LightSource","PostProcessVolume","SkyLight","EditorCube8","EditorCube9","EditorCube10","EditorCube11","EditorCube12","EditorCube13","EditorCube14","EditorCube15","EditorCube16","EditorCube17","EditorCube18","Floor","Wall1","Wall2","Wall3","Wall4","BigWall","BigWall2","Wall_400x400","Wall_400x401","Wall_400x402","Wall_400x403","Wall_400x404","PointLight","Floor_400x400","Floor_400x401","TextRenderActor","TextRenderActor2","TextRenderActor3","Wall_Door_400x400","SM_Door","TriggerBox","TextRenderActor4","Wall_400x405","Wall_400x406","SM_MERGED_Shape_Pipe_Flag","Plane_Blueprint","Cube","Cube2","Cube3","Wall_400x407","Wall_400x408","TextRenderActor6","TextRenderActor10","Wall_400x409","Wall_400x410","TextRenderActor12","TextRenderActor13","TextRenderActor14","TextRenderActor15","Actor","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","SM_MERGED_Shape_Pipe_Flag_1_Blueprint2","SM_MERGED_Shape_Pipe_Flag_1_Blueprint3","SM_MERGED_Shape_Pipe_Flag_1_Blueprint","EditorCube19","Shape_Pipe_Flag","TextRenderActor8","TextRenderActor9","TextRenderActor11","TextRenderActor16","Wall_400x411","TextRenderActor17","DefaultPhysicsVolume","FirstPersonGameMode_C","GameSession","ParticleEventManager","GameNetworkManager","FirstPersonExampleMap_C","FirstPersonCharacter_C","GameStateBase","PlayerController","PlayerState","PlayerCameraManager","CameraActor","FirstPersonHUD_C"]// for(var i in list){//     const name = list[i].trim()//     // console.log(name)//     check_behidden(name)// }}catch(e){console.log(e)}}
    

正解

后来找到了 SetVisibility API。并通过询问deepseek得到了这样的信息

image-20250324150355600

通过Actor主动调用SetVisibility函数或者直接设置 bVisible属性并刷新,即可显示出flag

function set_bVisible(Actor_Name,sel=0){try{var base_addr = get_so_base()var Actor_addr = hook_find_actor(Actor_Name,sel)var StaticMeshComponent = Actor_addr.add(0x220).readPointer()var old_val = StaticMeshComponent.add(0x14c).readU8()StaticMeshComponent.add(0x14c).writeU8(old_val | 0x10)console.log(`${Actor_Name} set_bVisible`)}catch(e){console.log(`${Actor_Name} error:  ${e}`)}}function StaticMeshActor_call_SetVisibility(Actor_Name,bNewVisibility,bPropagateToChildren,sel=0){try{var base_addr = get_so_base()var Actor_addr = hook_find_actor(Actor_Name,sel)var StaticMeshComponent = Actor_addr.add(0x220).readPointer()var no_exec_SetVisibility = base_addr.add(0x000000008E619BC)const SetVisibility = new NativeFunction(no_exec_SetVisibility, 'void', ['pointer','bool','bool']);SetVisibility(StaticMeshComponent,bNewVisibility,bPropagateToChildren)console.log(`${Actor_Name} call_SetVisibility`)}catch(e){console.log(`${Actor_Name} error:  ${e}`)}}function section1(){try{// call_SetVisibility// var list = ["TemplateLabel","SkySphereBlueprint","AtmosphericFog","SphereReflectionCapture","NetworkPlayerStart","LightSource","PostProcessVolume","SkyLight","EditorCube8","EditorCube9","EditorCube10","EditorCube11","EditorCube12","EditorCube13","EditorCube14","EditorCube15","EditorCube16","EditorCube17","EditorCube18","Floor","Wall1","Wall2","Wall3","Wall4","BigWall","BigWall2","Wall_400x400","Wall_400x401","Wall_400x402","Wall_400x403","Wall_400x404","PointLight","Floor_400x400","Floor_400x401","TextRenderActor","TextRenderActor2","TextRenderActor3","Wall_Door_400x400","SM_Door","TriggerBox","TextRenderActor4","Wall_400x405","Wall_400x406","SM_MERGED_Shape_Pipe_Flag","Plane_Blueprint","Cube","Cube2","Cube3","Wall_400x407","Wall_400x408","TextRenderActor6","TextRenderActor10","Wall_400x409","Wall_400x410","TextRenderActor12","TextRenderActor13","TextRenderActor14","TextRenderActor15","Actor","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","SM_MERGED_Shape_Pipe_Flag_1_Blueprint2","SM_MERGED_Shape_Pipe_Flag_1_Blueprint3","SM_MERGED_Shape_Pipe_Flag_1_Blueprint","EditorCube19","Shape_Pipe_Flag","TextRenderActor8","TextRenderActor9","TextRenderActor11","TextRenderActor16","Wall_400x411","TextRenderActor17","DefaultPhysicsVolume","FirstPersonGameMode_C","GameSession","ParticleEventManager","GameNetworkManager","FirstPersonExampleMap_C","FirstPersonCharacter_C","GameStateBase","PlayerController","PlayerState","PlayerCameraManager","CameraActor","FirstPersonHUD_C"]// var cnt = -1// for(var i in list){//     const name = list[i].trim()//     if(name === "Shape_Pipe_Flag"){//         cnt += 1;//         StaticMeshActor_call_SetVisibility(name,1,1,cnt)//     }else{//         StaticMeshActor_call_SetVisibility(name,1,1)//     }// }// set_Visible(),这个需要刷新一下才可以,与上面的call_SetHiddenInGame 连用就ok// var list = ["TemplateLabel","SkySphereBlueprint","AtmosphericFog","SphereReflectionCapture","NetworkPlayerStart","LightSource","PostProcessVolume","SkyLight","EditorCube8","EditorCube9","EditorCube10","EditorCube11","EditorCube12","EditorCube13","EditorCube14","EditorCube15","EditorCube16","EditorCube17","EditorCube18","Floor","Wall1","Wall2","Wall3","Wall4","BigWall","BigWall2","Wall_400x400","Wall_400x401","Wall_400x402","Wall_400x403","Wall_400x404","PointLight","Floor_400x400","Floor_400x401","TextRenderActor","TextRenderActor2","TextRenderActor3","Wall_Door_400x400","SM_Door","TriggerBox","TextRenderActor4","Wall_400x405","Wall_400x406","SM_MERGED_Shape_Pipe_Flag","Plane_Blueprint","Cube","Cube2","Cube3","Wall_400x407","Wall_400x408","TextRenderActor6","TextRenderActor10","Wall_400x409","Wall_400x410","TextRenderActor12","TextRenderActor13","TextRenderActor14","TextRenderActor15","Actor","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","Shape_Pipe_Flag","SM_MERGED_Shape_Pipe_Flag_1_Blueprint2","SM_MERGED_Shape_Pipe_Flag_1_Blueprint3","SM_MERGED_Shape_Pipe_Flag_1_Blueprint","EditorCube19","Shape_Pipe_Flag","TextRenderActor8","TextRenderActor9","TextRenderActor11","TextRenderActor16","Wall_400x411","TextRenderActor17","DefaultPhysicsVolume","FirstPersonGameMode_C","GameSession","ParticleEventManager","GameNetworkManager","FirstPersonExampleMap_C","FirstPersonCharacter_C","GameStateBase","PlayerController","PlayerState","PlayerCameraManager","CameraActor","FirstPersonHUD_C"]// var cnt = -1// for(var i in list){//     const name = list[i].trim()//     if(name === "Shape_Pipe_Flag"){//     console.log(name)//         cnt += 1//         set_bVisible(name,cnt)//     }else{//         set_bVisible(name)//     }// }}catch(e){console.log(e)}}
image-20250324151004760

我这里的初衷是让 所有StaticMeshActor类的对象主动调用 SetVisibility函数,事后,读别人wp发现,似乎直接让Actor类的 RootComponent成员直接调用 SetVisibility函数更好,只要设置好 bPropagateToChildren属性,其所有子组件都能显现出来。用法更宽泛一些。

image-20250324152101081

image-20250324151948748

得到section1: 8939

section2

题目提示了物体为立方体。那我们就直接定位对象名与对应的类。

image-20250324152457291

image-20250324152509648

关注这个类StaticMeshActor即可,查询大模型,如何设置物体不可穿越属性,让三个Cube(Cube、Cube2、Cube3)一块主动调用SetCollisionResponseToAllChannelsSetCollisionEnabled函数即可(实际上只调用SetCollisionEnabled就ok )。代码实现如下:


function hook_find_actor( find_actor_name,cnt=0){var libUE4_module = Module.findBaseAddress("libUE4.so")// console.log("libUE4_module is :", libUE4_module)var GWorld_Offset = 0x00000000B32D8A8var GName_Offset = 0x00000000B171CC0var GName = libUE4_module.add(GName_Offset);var GWorld = libUE4_module.add(GWorld_Offset).readPointer()var Level_Offset = 0x30var Level = GWorld.add(Level_Offset).readPointer()// console.log("Level :", Level)var Actors_Offset = 0x98var Actors = Level.add(Actors_Offset).readPointer()// console.log("Actors Array :", Actors)var Actors_Num = Level.add(Actors_Offset).add(8).readU32()// console.log("Actors_num :", Actors_Num)var ct = 0for(var index = 0; index < Actors_Num; index++){var actor = Actors.add(index * 8).readPointer()//通过角色actor获取其成员变量FNamevar FName_Offset = 0x18var FName = actor.add(FName_Offset);var FNameEntryAllocator = GNamevar FNamePool = FNameEntryAllocator.add(0x38)  // FNamePool//手动解析FNamePoolvar ComparisonIndex = FName.add(0).readU32()var FNameBlockOffsetBits = 16var FNameBlockOffsets = 65536var Block = ComparisonIndex >> FNameBlockOffsetBitsvar Offset = ComparisonIndex & (FNameBlockOffsets - 1)var NamePoolChunk = FNamePool.add(0x8 + Block * 8).readPointer()var FNameEntry = NamePoolChunk.add(Offset * 2)var FNameEntryHeader = FNameEntry.readU16()var isWide = FNameEntryHeader & 1var Len = FNameEntryHeader >> 6// console.log("actor : ", actor, " ", FNameEntry.add(2).readCString(Len))if(0 == isWide){var Name =  FNameEntry.add(2).readCString(Len)var actor_addr = actor;if (Name === find_actor_name){if(ct == cnt){// console.log("actor : ", actor, " ", FNameEntry.add(2).readCString(Len))return actor_addr}else{ct++}}}}
}function set_Collision(Actor_Name){try{var base_addr = get_so_base("libUE4.so")var TriggerBox_addr = hook_find_actor(Actor_Name)var ShapeComponent = TriggerBox_addr.add(0x220).readPointer()var a1_0 =  ShapeComponent.add(0).readPointer()var no_exec_SetCollisionResponseToAllChannels = a1_0.add(0x850).readPointer()// console.log(`no_exec_SetCollisionResponseToAllChannels ${no_exec_SetCollisionResponseToAllChannels.sub(base_addr)}`)       const SetCollisionResponseToAllChannels_pointer = new NativeFunction(no_exec_SetCollisionResponseToAllChannels, 'void', ['pointer','uint8']);const ECR_Block = 2SetCollisionResponseToAllChannels_pointer(ShapeComponent,ECR_Block);    // 0x98eb590const QueryAndPhysics = 3const no_exec_SetCollisionEnabled = a1_0.add(0x660).readPointer()const SetCollisionEnabled_pointer = new NativeFunction(no_exec_SetCollisionEnabled,"void",['pointer','uint8'])SetCollisionEnabled_pointer(ShapeComponent,QueryAndPhysics)// console.log(`no_exec_SetCollisionEnabled ${no_exec_SetCollisionEnabled.sub(base_addr)}`)       var no_exec_K2_IsQueryCollisionEnabled = a1_0.add(0x510).readPointer()const K2_IsQueryCollisionEnabled = new NativeFunction(no_exec_K2_IsQueryCollisionEnabled,"bool",['pointer'])var ret1 = K2_IsQueryCollisionEnabled(ShapeComponent)// console.log(`K2_IsQueryCollisionEnabled & K2_IsPhysicsCollisionEnabled & K2_IsCollisionEnabled : ${ret1}`)}catch(e){console.log(e)}}
function section2(){try{set_Collision("Cube")set_Collision("Cube2")set_Collision("Cube3")console.log("section2 ~")}catch(e){console.log(e)}
}
image-20250324152757946

得到section2: 008

另外,直接调用SetActorEnableCollision并没有实现,看来全局Actor的碰撞属性影响不到局部的碰撞属性

section3

image-20250324155657518

image-20250324155707746

image-20250324155604018

image-20250324155818362

image-20250324164601190

函数采用了字符串混淆 + br混淆。

unidbg trace一下后手动patch即可恢复控制流

package com.Tencent_game;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.debugger.DebuggerType;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import java.io.*;public class pri_2024 {public final AndroidEmulator emulator;public final VM vm;public final Memory memory;public final Module module;public pri_2024(){emulator = AndroidEmulatorBuilder.for64Bit().build();memory =  emulator.getMemory();memory.setLibraryResolver(new AndroidResolver(23));emulator.getSyscallHandler().setEnableThreadDispatcher(true);vm = emulator.createDalvikVM();new AndroidModule(emulator,vm).register(memory);DalvikModule dalvikModule = vm.loadLibrary(new File("C:\\Users\\27236\\Desktop\\2024_tencent\\mobile_pri\\gamesec2024_signed\\lib\\arm64-v8a\\libplay.so"), true);module = dalvikModule.getModule();
//        vm.callJNI_OnLoad(emulator,module);}public  static  void main(String[] args){pri_2024 mainActivity = new pri_2024();mainActivity.debugger();}public void debugger(){
//        emulator.traceCode(0x40000000,0x40010000);emulator.attach(DebuggerType.CONSOLE).addBreakPoint(module.base+ 0x000000000001710);module.callFunction(emulator,0x000000000001458,"tlsn22334455667788");}
}

image-20250324164707815

image-20250324164718296

image-20250324164738575

实际上就一换表后的魔改base64 + xor

给出解密脚本:

def enc():N_str = bytes.fromhex(" 0A 0C 0E 00 51 16 27 38 49 1A 3B 5C 2D 4E 6F FA FC FE")inp = b"tlsn22334455667788"cip1 = []for i in range(len(N_str)):cip1.append(inp[i] ^ N_str[i])cip1 = bytes(cip1)base64en = b"ACE0BDFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789+/"inp = cip1enc = bytearray(b"\x00" * 0x18)idx = 0v10 = 0cnt = 0lenn = len(inp)while v10+1 < lenn:v10 = idx + 2;v8 = (inp[idx ] << 16) |  (inp[idx + 1] << 8) | inp[idx + 2];v9 = base64en[(v8 >> 12) & 0x3F];enc[cnt*4+0] = base64en[v8 >> 0x12];enc[cnt*4+1] = v9;enc[cnt*4+2] = base64en[(v8 >> 6) & 0x3F];enc[cnt*4+3] = base64en[v8 & 0x3F];idx += 3cnt += 1assert enc == bytearray(b'elC9alLjDAs9Kf5oF3gXybSF')def dec(cip):base64en = b"ACE0BDFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789+/"debase64 = []for i in range(0,len(cip),4):x1 = base64en.find(cip[i+0])x2 = base64en.find(cip[i+1])x3 = base64en.find(cip[i+2])x4 = base64en.find(cip[i+3])v8 = (x1 << 0x12) | (x2 << 12) | (x3 << 6) | x4debase64.append( (v8 >> 16) & 0xff )debase64.append( (v8 >> 8) & 0xff )debase64.append( v8 & 0xff )N_str = bytes.fromhex(" 0A 0C 0E 00 51 16 27 38 49 1A 3B 5C 2D 4E 6F FA FC FE")inp = debase64cip1 = []for i in range(len(N_str)):cip1.append(inp[i] ^ N_str[i])cip1 = bytes(cip1)return cip1enc()
assert dec(b"elC9alLjDAs9Kf5oF3gXybSF") == b"tlsn22334455667788"m = dec(b"UT1fc0gIYDArdz80Z0Xem46J")
print(m)
print(len(m))

得到section3: _Anti_Cheat_Expert

最后的flag: FLAG{8939008_Anti_Cheat_Expert}

我的问题

  • frida UE4反射,如果能反射的话,主动调用就很简单了。

  • 自动化去br混淆

  • 如何用frida锁定某内存值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/904876.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

制造业订单处理烦恼多,日事清 OTD 管理为您排忧解难

你是不是经常因为接单和交货时间差太大而焦头烂额?今天我们就结合制造业OTD管理,带您了解如何应用日事清进行订单交付全周期管理。日事清可以帮你设定精细的流程,从接单到发货,清晰可控地帮你解决以上烦心事。在制造业里打拼,每天都得面对各种烦心事,比如订单处理慢、生产…

如何在SSD1306上显示动态表情符号位图

解锁您的SSD1306上充满活力的视觉效果!学习毫不费力地显示动态表情符号位图,并以风格增强您的项目。 在本教程中,我们将通过使用PCBX在线模拟环境在SSD1306 OLED显示器上显示位图图像的过程。本教程将介绍设置PCBX模拟,格式化位图数据,配置显示大小和管理图像延迟。步骤1:…

redis基础数据结构——ZipList

ZipList 基于特殊写法实现的双端链表,由一系列特殊编码的连续内存块组成,可以像deque一样在双端压入/弹出,并且时间复杂度在O(1) 整体ZL结构如下zlbytes(uint32):当前zl总的byte数。 zltail(uint32):尾结点的offset,指向的是最后一个entry的起始地址。 zllen(uint16):记…

day:28 postman——环境变量(依赖,关联接口)

一.接口的环境变量 (1)定义变量 可以将需要填写的值设为变量 变量设置:{{}}(2)添加环境变量 方法一:方法二:(3)查看环境变量(4)选择环境,执行二.依赖接口 先登录接口成功,生成cookie值,才能让后面接口依赖 cookie值是保持会话 查看cookie值方法 方法一:方法二:…

L1.1 技术和产品准备度

L1.1 技术和产品准备度 技术和产品准备度 技术与产品的演进 ​ 上面这张图展示了如何在技术尚未完全成熟时,启动产品开发,以及技术如何随着新需求或洞察逐步演进,并支持产品的更新换代。产品1.0:由先前研发的的技术3支撑,加上“产品开发可以在预期的技术开发成果的基础上提…

从故障响应到客户信赖:华为ITR流程的五大核心步骤与实战案例

华为究竟是如何在与西方巨头的激烈竞争中崭露头角、脱颖而出的呢?答案是:凭借卓越的服务。今天我们来探讨一下华为是如何通过卓越的服务赢得全球市场的。 一、华为的三件大事 华为前高管费敏曾经总结过,华为的业务可以分成三件大事:1. 开发产品:这就是 IPD 流程,负责从有…

提升生产效率的关键: ethercat转TCPIP智能通信

大家好。最近在数据互联互通方面,我们迎来了一个重要的突破。作为生产管理系统的核心组成部分,数据互联互通一直是一个亟待解决的挑战。我们知道,EtherCAT和TCP/IP是两种不同的通信协议,它们之间的互通性一直存在问题。不过,现在有一款新产品值得关注,这款产品能够实现Et…

Trae初体验

Trae(国际版)的Ai搭载Claude-3.7-Sonnet(完全免费且速度很快)和DeepSeek-R1以及V3(不存在服务器繁忙)以及GPT-4o Trae国服的Ai搭载DouBao和DeepSeek。用Claude-3.7-Sonnet 写一个简易的贪吃蛇小游戏:这个贪吃蛇游戏包含以下功能:使用方向键控制蛇的移动 吃到食物会增加长度和…

C# 从零开始使用Layui.Wpf库开发WPF客户端

一、简介最近需要开发一个桌面版的工具软件,之前用得更多的是Winform,作为一个全干工程师,我们也要兼顾下WPF,趁此机会再研究下开源控件库。MaQaQ:Winform真好用(有个HZHControls控件库,值得一看)。 二、准备工作找了下开源控件库,诸如MaterialDesignInXAML、HandyCon…

聚点和闭包中点的等价条件

聚点有以下等价描述: 闭包中点有以下等价描述:这些等价描述在与导集和闭包的证明中能起到很大的作用。下面是一个例子。

Itext5生成高质量、易识别、适合小尺寸标签打印的二维码

高质量、易识别、小尺寸二维码生成 1.增大二维码的原始尺寸(例如 1000 x 1000 或更大),再缩放为 PDF 所需的大小。这样可以保留更多像素细节,提高识别率。 2.降低容错级别到 L 或 M,如果你的内容不是特别长或复杂的话,这样能减少密集度。 3.优化缩放方式: • 使用 Buffe…