Loading...

文章背景图

阶段九:游戏引擎逆向

a0yark a0yark
|
2025-12-03
|
0
|
-
|
- min
|

阶段九:游戏引擎逆向

📅 创建日期: 2025-01-XX
⏱️ 预计学时: 8-12 周
🎯 学习目标: 掌握 UE4/UE5 和 Unity 引擎逆向、SDK 生成、引擎特定技术
📋 前置要求: [[阶段五:核心逆向技术]], [[阶段三:进阶编程]]
🔗 返回: [[逆向与驱动开发学习路径(详细版)]]


📚 本阶段内容概览

  • [[#9.1 Unreal Engine逆向|9.1 Unreal Engine逆向]]
    • [[#9.1.1 UE架构概述|UE架构概述]]
    • [[#9.1.2 UObject与反射系统|UObject与反射系统]]
    • [[#9.1.3 GNames与GObjects|GNames与GObjects]]
    • [[#9.1.4 SDK生成|SDK生成]]
    • [[#9.1.5 常用逆向技术|常用逆向技术]]
  • [[#9.2 Unity逆向|9.2 Unity逆向]]
    • [[#9.2.1 Mono与IL2CPP|Mono与IL2CPP]]
    • [[#9.2.2 元数据恢复|元数据恢复]]
    • [[#9.2.3 Unity特定技术|Unity特定技术]]

9.1 Unreal Engine逆向

9.1.1 UE架构概述

📝 学习笔记

UE 核心概念

概念 说明
UObject 所有引擎对象的基类
AActor 可放置在关卡中的对象
UWorld 游戏世界,包含所有Actor
ULevel 关卡,World的子集
APlayerController 玩家控制器
APawn/ACharacter 可被控制的角色

类层次结构

UObject
├── AActor
│   ├── APawn
│   │   └── ACharacter
│   │       └── AMyCharacter
│   ├── AController
│   │   └── APlayerController
│   ├── AGameMode
│   └── APlayerState
├── UActorComponent
│   ├── USceneComponent
│   │   └── UPrimitiveComponent
│   │       └── UMeshComponent
│   └── UMovementComponent
└── UGameInstance

内存布局特点

// UObject基本布局 (简化)
class UObject {
    void** VTable;           // 虚表指针
    int32 ObjectFlags;       // 对象标志
    int32 InternalIndex;     // GObjects数组索引
    UClass* ClassPrivate;    // 类信息
    FName NamePrivate;       // 对象名称
    UObject* OuterPrivate;   // 外部对象
};

9.1.2 UObject与反射系统

📝 学习笔记

UClass 结构

class UClass : public UStruct {
    // 继承自UStruct
    UStruct* SuperStruct;           // 父类
    UField* Children;               // 子字段链表
    int32 PropertiesSize;           // 实例大小
    
    // UClass特有
    UObject* ClassDefaultObject;    // CDO默认对象
    UFunction* FuncLink;            // 函数链表
    // ...
};

// 属性结构
class UProperty : public UField {
    int32 ArrayDim;         // 数组维度
    int32 ElementSize;      // 元素大小
    int32 Offset_Internal;  // 在实例中的偏移
    // ...
};

遍历类属性

void DumpClass(UClass* Class) {
    printf("Class: %s\n", Class->GetName());
    printf("Size: %d\n", Class->PropertiesSize);
    
    // 遍历属性
    for (UProperty* Prop = Class->PropertyLink; Prop; Prop = Prop->PropertyLinkNext) {
        printf("  +0x%04X %s (%s)\n",
               Prop->Offset_Internal,
               Prop->GetName(),
               Prop->GetClass()->GetName());
    }
    
    // 遍历函数
    for (UFunction* Func = Class->FuncLink; Func; Func = Func->Next) {
        printf("  Function: %s\n", Func->GetName());
    }
}

9.1.3 GNames与GObjects

📝 学习笔记

GNames (FNamePool)

// FName是字符串的索引引用
struct FName {
    int32 ComparisonIndex;  // 在NamePool中的索引
    int32 Number;           // 实例编号
};

// 名称池结构 (UE4)
struct FNamePool {
    FNameEntry* Entries[...];  // 分块数组
};

// 获取字符串
const char* FName::GetName() {
    FNameEntry* Entry = GNames->Entries[ComparisonIndex];
    return Entry->AnsiName;  // 或 Entry->WideName
}

GObjects (FUObjectArray)

// 对象数组
struct FUObjectArray {
    FUObjectItem* Objects;     // 对象数组
    int32 MaxElements;         // 最大容量
    int32 NumElements;         // 当前数量
};

struct FUObjectItem {
    UObject* Object;           // 对象指针
    int32 Flags;               // 标志
    int32 ClusterIndex;        // 集群索引
    int32 SerialNumber;        // 序列号
};

// 遍历所有对象
void EnumerateObjects() {
    for (int i = 0; i < GObjects->NumElements; i++) {
        UObject* Obj = GObjects->Objects[i].Object;
        if (Obj) {
            printf("[%d] %s\n", i, Obj->GetFullName());
        }
    }
}

定位 GNames 和 GObjects

// 模式搜索 (示例)
// GNames通常在静态初始化时设置
// 搜索特征: 48 8B 05 ?? ?? ?? ?? 48 85 C0 75 ?? (lea rax, [GNames])

// GObjects
// 搜索特征: 48 8B 05 ?? ?? ?? ?? 48 8B 0C C8 (mov rax, [GObjects])

// 或通过已知函数定位
// UObject::GetFullName() 会访问GNames
// FindObject<>() 会访问GObjects

9.1.4 SDK生成

📝 学习笔记

SDK 生成原理

// 遍历所有UClass生成头文件
void GenerateSDK() {
    for (int i = 0; i < GObjects->NumElements; i++) {
        UObject* Obj = GObjects->Objects[i].Object;
        if (!Obj) continue;
        
        if (Obj->IsA(UClass::StaticClass())) {
            UClass* Class = (UClass*)Obj;
            GenerateClassHeader(Class);
        }
        else if (Obj->IsA(UEnum::StaticClass())) {
            GenerateEnumHeader((UEnum*)Obj);
        }
    }
}

// 生成类头文件
void GenerateClassHeader(UClass* Class) {
    FILE* f = fopen(GetClassFileName(Class), "w");
    
    // 输出类声明
    fprintf(f, "class %s", Class->GetName());
    if (Class->SuperStruct) {
        fprintf(f, " : public %s", Class->SuperStruct->GetName());
    }
    fprintf(f, " {\npublic:\n");
    
    // 输出属性
    for (UProperty* Prop = Class->PropertyLink; Prop; Prop = Prop->PropertyLinkNext) {
        fprintf(f, "    %s %s; // 0x%X\n",
                GetPropertyType(Prop),
                Prop->GetName(),
                Prop->Offset_Internal);
    }
    
    // 输出函数
    for (UFunction* Func = Class->FuncLink; Func; Func = Func->Next) {
        GenerateFunctionDecl(f, Func);
    }
    
    fprintf(f, "};\n");
    fclose(f);
}

常用 SDK 生成工具

工具 说明
UnrealDumper 基础SDK生成
Dumper-7 现代UE4/UE5支持
KN4CK3R/UnrealDumper 开源实现

9.1.5 常用逆向技术

📝 学习笔记

获取本地玩家

// 方法1: 通过GEngine
UEngine* GEngine = *(UEngine**)GEnginePtr;
UGameViewportClient* ViewportClient = GEngine->GameViewport;
UWorld* World = ViewportClient->World;
APlayerController* PC = World->OwningGameInstance->LocalPlayers[0]->PlayerController;

// 方法2: 通过UWorld
UWorld* World = *(UWorld**)GWorldPtr;
APlayerController* PC = World->OwningGameInstance->GetFirstLocalPlayerController(World);
APawn* LocalPawn = PC->AcknowledgedPawn;

Actor 遍历

void EnumerateActors(UWorld* World) {
    for (int i = 0; i < World->Levels.Num(); i++) {
        ULevel* Level = World->Levels[i];
        for (int j = 0; j < Level->Actors.Num(); j++) {
            AActor* Actor = Level->Actors[j];
            if (Actor && Actor->IsA(ACharacter::StaticClass())) {
                ProcessCharacter((ACharacter*)Actor);
            }
        }
    }
}

世界坐标转屏幕坐标

bool WorldToScreen(APlayerController* PC, FVector WorldLocation, FVector2D& ScreenLocation) {
    // 使用引擎函数
    return PC->ProjectWorldLocationToScreen(WorldLocation, ScreenLocation, false);
}

// 手动计算
bool WorldToScreenManual(FVector WorldPos, FVector2D& ScreenPos) {
    FMatrix ViewMatrix = GetViewMatrix();
    FMatrix ProjectionMatrix = GetProjectionMatrix();
    FMatrix ViewProjection = ViewMatrix * ProjectionMatrix;
    
    FVector4 Projected = ViewProjection.TransformFVector4(FVector4(WorldPos, 1.0f));
    
    if (Projected.W > 0.0f) {
        float X = (1.0f + Projected.X / Projected.W) * 0.5f * ScreenWidth;
        float Y = (1.0f - Projected.Y / Projected.W) * 0.5f * ScreenHeight;
        ScreenPos = FVector2D(X, Y);
        return true;
    }
    return false;
}

获取骨骼位置

FVector GetBoneLocation(ACharacter* Character, int BoneIndex) {
    USkeletalMeshComponent* Mesh = Character->Mesh;
    if (!Mesh) return FVector();
    
    // 方法1: 使用引擎函数
    return Mesh->GetBoneLocation(BoneIndex);
    
    // 方法2: 直接读取骨骼矩阵
    FTransform BoneTransform = Mesh->GetBoneTransform(BoneIndex);
    return BoneTransform.GetLocation();
}

// 常用骨骼索引
enum EBoneIndex {
    Head = 98,      // 因游戏而异
    Neck = 97,
    Chest = 66,
    // ...
};

9.2 Unity逆向

9.2.1 Mono与IL2CPP

📝 学习笔记

两种运行时对比

特性 Mono IL2CPP
代码形式 .NET IL字节码 编译为C++
性能 JIT编译 AOT编译,更快
逆向难度 简单 较难
主要文件 Assembly-CSharp.dll GameAssembly.dll
元数据 完整保留 需要global-metadata.dat

Mono 逆向

直接用dnSpy/ILSpy打开:
GameFolder/
├── Game_Data/
│   └── Managed/
│       ├── Assembly-CSharp.dll    <- 主要游戏逻辑
│       ├── Assembly-CSharp-firstpass.dll
│       └── UnityEngine.*.dll      <- Unity库

IL2CPP 结构

GameFolder/
├── Game_Data/
│   ├── il2cpp_data/
│   │   └── Metadata/
│   │       └── global-metadata.dat  <- 元数据
│   └── Native/
│       └── GameAssembly.dll         <- 编译后的代码

9.2.2 元数据恢复

📝 学习笔记

Il2CppDumper 使用

# 运行Il2CppDumper
Il2CppDumper.exe GameAssembly.dll global-metadata.dat output/

# 输出文件:
# - dump.cs          # C#类定义
# - script.json      # IDA/Ghidra脚本数据
# - stringliteral.json # 字符串信息

Il2Cpp 内部结构

// Il2CppClass (简化)
struct Il2CppClass {
    Il2CppImage* image;
    void* gc_desc;
    const char* name;
    const char* namespaze;
    Il2CppType byval_arg;
    Il2CppType this_arg;
    Il2CppClass* element_class;
    Il2CppClass* parent;
    MethodInfo** methods;
    FieldInfo* fields;
    // ...
    uint16_t method_count;
    uint16_t field_count;
    // ...
};

// MethodInfo
struct MethodInfo {
    void* methodPointer;     // 函数地址
    void* invoker_method;
    const char* name;
    Il2CppClass* klass;
    Il2CppType* return_type;
    ParameterInfo* parameters;
    // ...
};

运行时查找

// 查找类
Il2CppClass* FindClass(const char* namespaze, const char* name) {
    Il2CppDomain* domain = il2cpp_domain_get();
    Il2CppAssembly** assemblies;
    size_t count;
    assemblies = il2cpp_domain_get_assemblies(domain, &count);
    
    for (size_t i = 0; i < count; i++) {
        Il2CppImage* image = il2cpp_assembly_get_image(assemblies[i]);
        Il2CppClass* klass = il2cpp_class_from_name(image, namespaze, name);
        if (klass) return klass;
    }
    return nullptr;
}

// 查找方法
MethodInfo* FindMethod(Il2CppClass* klass, const char* name, int argCount) {
    return il2cpp_class_get_method_from_name(klass, name, argCount);
}

// 调用方法
void* CallMethod(MethodInfo* method, void* obj, void** params) {
    Il2CppException* exception = nullptr;
    void* result = il2cpp_runtime_invoke(method, obj, params, &exception);
    if (exception) {
        // 处理异常
    }
    return result;
}

9.2.3 Unity特定技术

📝 学习笔记

获取游戏对象

// 查找场景中的对象
Il2CppArray* FindObjectsOfType(Il2CppClass* type) {
    // Object.FindObjectsOfType(typeof(T))
    MethodInfo* method = FindMethod(
        FindClass("UnityEngine", "Object"),
        "FindObjectsOfType",
        1
    );
    
    Il2CppReflectionType* reflectionType = il2cpp_type_get_object(&type->byval_arg);
    void* params[] = { reflectionType };
    
    return (Il2CppArray*)CallMethod(method, nullptr, params);
}

// 通过名称查找
void* FindGameObject(const char* name) {
    // GameObject.Find(name)
    MethodInfo* method = FindMethod(
        FindClass("UnityEngine", "GameObject"),
        "Find",
        1
    );
    
    Il2CppString* nameStr = il2cpp_string_new(name);
    void* params[] = { nameStr };
    
    return CallMethod(method, nullptr, params);
}

访问 Transform

// 获取位置
Vector3 GetPosition(void* gameObject) {
    // 获取Transform组件
    MethodInfo* getTransform = FindMethod(
        FindClass("UnityEngine", "GameObject"),
        "get_transform",
        0
    );
    void* transform = CallMethod(getTransform, gameObject, nullptr);
    
    // 获取position
    MethodInfo* getPosition = FindMethod(
        FindClass("UnityEngine", "Transform"),
        "get_position",
        0
    );
    
    Vector3 pos;
    CallMethod(getPosition, transform, nullptr);
    // 返回值处理...
    return pos;
}

Camera 相关

// 获取主相机
void* GetMainCamera() {
    MethodInfo* method = FindMethod(
        FindClass("UnityEngine", "Camera"),
        "get_main",
        0
    );
    return CallMethod(method, nullptr, nullptr);
}

// 世界坐标转屏幕
Vector3 WorldToScreenPoint(void* camera, Vector3 worldPos) {
    MethodInfo* method = FindMethod(
        FindClass("UnityEngine", "Camera"),
        "WorldToScreenPoint",
        1
    );
    
    void* params[] = { &worldPos };
    Vector3 screenPos;
    // 调用并处理返回值...
    return screenPos;
}

字段读写

// 获取字段值
template<typename T>
T GetFieldValue(void* obj, Il2CppClass* klass, const char* fieldName) {
    FieldInfo* field = il2cpp_class_get_field_from_name(klass, fieldName);
    T value;
    il2cpp_field_get_value(obj, field, &value);
    return value;
}

// 设置字段值
template<typename T>
void SetFieldValue(void* obj, Il2CppClass* klass, const char* fieldName, T value) {
    FieldInfo* field = il2cpp_class_get_field_from_name(klass, fieldName);
    il2cpp_field_set_value(obj, field, &value);
}

// 静态字段
template<typename T>
T GetStaticFieldValue(Il2CppClass* klass, const char* fieldName) {
    FieldInfo* field = il2cpp_class_get_field_from_name(klass, fieldName);
    T value;
    il2cpp_field_static_get_value(field, &value);
    return value;
}

✅ 阶段检查点

Unreal Engine检查

  • [ ] 理解UObject/AActor/UWorld关系
  • [ ] 能定位GNames和GObjects
  • [ ] 能遍历游戏对象
  • [ ] 理解SDK生成原理
  • [ ] 实现基本的W2S转换

Unity检查

  • [ ] 区分Mono和IL2CPP游戏
  • [ ] 能使用dnSpy分析Mono游戏
  • [ ] 能使用Il2CppDumper恢复元数据
  • [ ] 理解Il2Cpp运行时结构
  • [ ] 能在运行时调用Unity方法

📖 学习资料

资源类型 名称 说明
📚 文档 UE4 Source 需Epic账号
📚 文档 Unity Scripting API 官方API参考
🔧 工具 Dumper-7 UE4/UE5 SDK生成
🔧 工具 Il2CppDumper IL2CPP逆向
🔧 工具 dnSpy .NET反编译
🔧 工具 Cpp2IL IL2CPP分析
📹 视频 GuidedHacking UE4教程 YouTube系列

🔗 导航

← [[阶段八:脚本框架]] | [[逆向与驱动开发学习路径(详细版)| 返回主目录]]

分享文章

未配置分享平台

请在主题设置中启用分享平台

评论