Information Security/Android

Unreal Engine 4.27 Dump(Android)

hackcatml 2022. 2. 1. 01:02
반응형

※ frida ue4 dumper

https://github.com/hackcatml/frida-ue4dump

 

GitHub - hackcatml/frida-ue4dump: UE4 dump frida script

UE4 dump frida script. Contribute to hackcatml/frida-ue4dump development by creating an account on GitHub.

github.com

 

언리얼엔진 게임 개발 튜토리얼(https://www.youtube.com/watch?v=k7w66PGppaU&list=PLY9cHlxw3OgjuLmBX1rYPhRwcDfCza1_5&index=3)을 보고 Unreal Engine 4.27.2 버전 샘플게임을 직접 빌드하고 SDK 덤프 및 함수 후킹, 변조를 해보았습니다.

샘플게임은 구글드라이브에 올려놨으니 필요하신분은 다운받으셔서 연습해보시기 바랍니다.

 

- Android arm64 Unreal Engine Test Game
https://drive.google.com/file/d/1pflCQaUsyhdi274BQaU8lvTJOCCbIa2R/view?usp=sharing

 

■UE4Dumper Build

안드로이드 언리얼 엔진 덤퍼는 아래 깃헙 주소에서 다운받을 수 있습니다.

git clone https://github.com/kp7742/UE4Dumper.git
안드로이드 스튜디오에서 project open 합니다.
그냥 빌드할 경우 일반 UE4.27.2 버전의 경우 덤프가 되지 않기 때문에, Offset.h 파일 수정이 필요합니다.(initOffsets_64, patchUE423_64 함수 수정. offset 값이 정확하지 않을 수 있습니다.)

void initOffsets_64() {
        //Global
        PointerSize = 0x8;
        FUObjectItemPad = 0x0;
        FUObjectItemSize = 0x18;

        //---------SDK-----------
        //Class: FNameEntry
        FNameEntryToNameString = 0x10;  // Only Used if < UE4.23
        //Class: FUObjectArray
        FUObjectArrayToTUObjectArray = 0x10;
        //Class: TUObjectArray
        TUObjectArrayToNumElements = 0xC;
        //Class: UObject
        UObjectToInternalIndex = 0xC;
        UObjectToClassPrivate = 0x10;
        UObjectToFNameIndex = 0x18;
        UObjectToOuterPrivate = 0x20;
        //Class: UField
        UFieldToNext = 0x28;
        //Class: UStruct
        UStructToSuperStruct = 0x40;
        UStructToChildren = 0x48;
        //Class: UFunction
        UFunctionToFunctionFlags = 0xB0;
        UFunctionToFunc= 0xD8;
        //Class: UProperty
    	UPropertyToElementSize = 0x38;
    	UPropertyToPropertyFlags = 0x40;
    	UPropertyToOffsetInternal = 0x4C;
        //Class: UBoolProperty
        UBoolPropertyToFieldSize = 0x88;
        UBoolPropertyToByteOffset = 0x89;
        UBoolPropertyToByteMask = 0x8a;
        UBoolPropertyToFieldMask = 0x8b;
        //Class: UObjectProperty
        UObjectPropertyToPropertyClass = 0x78;
        //Class: UClassProperty
        UClassPropertyToMetaClass = 0x78;
        //Class: UInterfaceProperty
        UInterfacePropertyToInterfaceClass = 0x70;
        //Class: UArrayProperty
        UArrayPropertyToInnerProperty = 0x70;
        //Class: UMapProperty
        UMapPropertyToKeyProp = 0x70;
        UMapPropertyToValueProp = 0x78;
        //Class: USetProperty
        USetPropertyToElementProp = 0x70;
        //Class: UStructProperty
        UStructPropertyToStruct = 0x78;
        //Class: UWorld
        UWorldToPersistentLevel = 0x30;
        //Class: ULevel
        ULevelToAActors = 0x98;
        ULevelToAActorsCount = 0xA0;
    }

    void patchUE423_64() {
        //Class: FNamePool
        FNameStride = 0x2;
        GNamesToFNamePool = 0x30;
        FNamePoolToCurrentBlock = 0x8;
        FNamePoolToCurrentByteCursor = 0xC;
        FNamePoolToBlocks = 0x10;
        //Class: FNameEntry
        FNameEntryToLenBit = 0x6;
        FNameEntryToString = 0x2;
        //Class: TUObjectArray
        TUObjectArrayToNumElements = 0x14;
        //Class: UStruct
        UStructToChildProperties = 0x50;
        //Class: FField
        FFieldToClass = 0x8;
        FFieldToNext = 0x20;
        FFieldToName = 0x28;
    }

 

 

해당 프로젝트 디렉터리에서 ndk-build를 사용하여 빌드합니다.

 

 

adb push 로 단말기에 밀어넣고 실행해보면 SDK덤프를 위해서는 GNames, GUObject 포인터 주소값을 인자로 제공이 필요한 것을 알 수 있습니다.
여기서 말하는 GNames는 NamePoolData 라는 전역변수로서 게임내 모든 오브젝트에 할당된 이름이 들어있다고 합니다.
GUObject는 GUObjectArray라는 전역변수로서 게임내 모든 오브젝트들에 대한 정보가 들어있다고 합니다.

 

 

■Find NamePoolData, GUObjectArray
(1) Find NamePoolData Offset
UE4 소스코드에 의하면 UnrealName.cpp 파일의 FNamePool 클래스의 FNamePool 생성자에 "DuplicatedHardcodedName" 문자열이 하드코딩되어 있습니다.

 

 

해당 문자열을 IDA에서 검색합니다. 해당 문자열이 들어있는 곳이 FNamePool::FNamePool() 입니다.
샘플 빌드라서 함수명이 그대로 보이는 것 같습니다. 보통 sub_xxx 로 나타날 것입니다.

※ 언리얼 게임은 간단한 게임에도 불구하고 libUE4.so 파일의 용량이 수백메가입니다. 따라서, IDA가 자동분석을 완료하는데 많은 시간이 소요될 수 있습니다.

 

 

이제 FNamePool 생성자를 호출하는 곳을 역추적해보면 &unk_B07E0c0 가 전달되는데, 이것이 NamePoolData의 offset 주소값(0xB07E0c0) 입니다.

 

 

(2) Find GUObjectArray Offset
"uobject count is invalid" 문자열 검색합니다. 해당 문자열이 들어있는 곳은 FUObjectArray::AllocateObjectPool() 입니다.
역시 샘플 빌드라서 함수명이 그대로 보이는 것 같습니다. 보통 sub_xxx 로 나타날 것입니다.

 

 

 

FUObjectArray::AllocateObjectPool 함수를 호출하는 곳을 역추적해보면 GUObjectArray의 오프셋 주소값(0xB0C2398)이 전달되는 것을 확인할 수 있습니다.

 

 

GUObjectArray는 EXPORT되고 있으므로 Frida로도 Offset값을 확인 가능합니다.

 

 

■SDK 덤프
게임 앱 실행하고 터미널에서 ue4dumper64 실행합니다.
위에서 구한 NamePoolData 및 GUObjectArray의 offset 주소값을 인자로 전달해줍니다.

 

 

덤핑된 파일은 /sdcard/SDK.txt 파일로 저장 됩니다.
해당 파일을 열어보면 주요 함수명 및 offset 주소값을 확인 가능합니다. void AddCoin() 같은 함수의 Offset은 정확하게 추출되었는데, Offset.h 파일의 offset 설정값이 틀려서인지 Property들의 Offset 값이 그림과 같이 이상하게 나옵니다.

 

 

■Frida Hooking

EndlessRunnerGameModeBase 클래스의 AddCoin 함수를 후킹하여 획득하는 코인 갯수를 조작하려고 합니다.

AddCoin 함수의 오프셋 주소로 이동합니다. 빨간 박스안의 코드가 코인 갯수가 1씩 증가하도록 하는 로직으로 보입니다.

 

 

해당 부분의 어셈블리어를 보면 0x69a4478 오프셋에 있는 x9 레지스터 값을 후킹하면 획득하는 코인 갯수값을 조작할 수 있겠습니다.

 

 

따라서 다음과 같이 획득하는 코인 갯수가 10개씩 증가하도록 프리다 스크립트를 작성하였습니다.

function traceReg(reg_addr){
    var module_base = Module.findBaseAddress("libUE4.so");    // get base addr from module
    var reg_realaddr = module_base.add(reg_addr);   // add function offset

    console.log("Tracing " + ptr(reg_addr));

    Interceptor.attach(reg_realaddr, {   // set hook
        onEnter: function (args) {
            // console.warn("[+] " + ptr(reg_addr) + " called");
            if(reg_addr == "0x69A4478"){
                this.context.x9 = this.context.x9.add(0x9);
            }
        },
        // onLeave: function (retval) {
        //     console.warn("[-] " + ptr(reg_addr) + " Exiting");  // after call
        // }
    });
}

traceReg(0x69A4478)

 

 

프리다 attach 후 게임 플레이하게 되면 코인 갯수가 10개씩 증가하는것을 확인할 수 있습니다.

 

 

요 몇일 언리얼 게임을 만들어보고 분석해보니 유니티 게임보다 상당히 난이도가 높게 느껴졌지만, 재미도 있었습니다.

앞으로 메타버스니 뭐니 해서 게임엔진의 중요성이 커질듯하니, 언리얼이나 유니티 개발 및 분석을 취미로 갖고 가는것도 나쁘지 않을 듯합니다.

 

※ 출처

https://github.com/guttir14/UnrealDumper-4.25/blob/main/Dumper/engine.cpp(윈도우즈 언리얼 덤퍼)
https://github.com/kp7742/UE4Dumper(안드로이드 언리얼 덤퍼)
https://shhoya.github.io/ue_dumper.html(언리얼 분석)
https://github.com/Shhoya/Shh0yaUEDumper(윈도우즈 언리얼 덤퍼)
https://www.youtube.com/watch?v=P1OqJ3vZQeo(GName, GUObject 개념 설명 유튜브)

https://www.youtube.com/watch?v=hXu2BXIMiX0 (UnrealDumper-4.25 Windows를 사용하기 위해 NamePoolData, ObjObjects의 Offset 및 signature를 찾는법 설명하는 유튜브)

반응형