안드로이드 기타/게임 분석

안드로이드 Assembly-CSharp.dll 덤프 모듈 frida-il2cpp-bridge

naroSEC 2023. 7. 18. 08:14

개요

Unity 기반의 엔진으로 제작된 안드로이드 게임 앱 분석 시 일반적으로 Assembly-CSharp.dll 파일을 통해 앱에서 사용되는 클래스와 함수 정보를 파악하게 된다. Assembly-CSharp.dll 파일에는 Unity의 게임 오브젝트들을 제어하고 게임의 동작과 기능을 구현하는데 이용되는 C# 스크립트가 포함되어 있으며, 컴파일 방식(Mono 및 IL2CPP)에 따라서 세부 코드를 파악하고 변조까지도 가능해진다. 때문에, 개발사들은 해당 파일을 통해 크래커들이 게임에서 사용되는 주요 클래스와 함수 코드를 분석하지 못하게 은닉 기법을 사용하여 숨긴다. 대표적인 은닉 기법으로는 PE구조와 CLR 메타데이터의 헤더를 손상시키거나 파일을 암호화하는 방법이 있으며, 이 경우 헤더 시그니처 복구 또는 메모리를 덤프하여 원본 Assebly-CSharp.dll 파일을 추출하는 방법으로 대응할 수 있다. 그리고 frida-il2cpp-bridge는 IL2CPP 컴파일 기반의 Unity 게임 앱이 런타임 중일 때, 메모리를 덤프하여 Assembly-CSharp.dll 파일 정보를 추출할 수 있게 도와준다.


다운로드

frida-il2cpp-bridge는 아래의 Github에서 다운로드 가능하다.

 

GitHub - vfsfitvnm/frida-il2cpp-bridge: A Frida module to dump, trace or hijack any Il2Cpp application at runtime, without needi

A Frida module to dump, trace or hijack any Il2Cpp application at runtime, without needing the global-metadata.dat file. - GitHub - vfsfitvnm/frida-il2cpp-bridge: A Frida module to dump, trace or h...

github.com


사용 방법

frida-il2cpp-bridge 모듈은 TypeScript 프로젝트로 Node Package Manager 기반에서 Frida 컴파일을 통해 사용할 수 있다. 따라서, 먼저 Node.js 환경 설치가 필요하다.

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

설치가 다 되었다면 작업 공간 디렉터리를 하나 생성하고(필자는 fridali2cpp로 만들었다.) 아래의 npm init 명령어를 입력하여 초기 환경 설정을 해준다. 이때, package name, version 등에 관한 정보를 입력하라고 하는데 엔터를 쳐서 모두 Default 설정으로 지정해준다.

npm init

[그림 1] npm 초기 설정

[그림 2]와 같이 Frida-Compile 및 frida-il2cpp-bridge 모듈을 설치해준다.

npm install frida-compile
npm install --save-dev frida-il2cpp-bridge

[그림 2] npm 모듈 설치

frida-il2cpp-bridge 모듈의 경우 TypeScript 기반의 프로젝트이다 보니 types와 호환되는 frida-gum 모듈도 필요하다. 따라서, 아래의 명령어를 통해 [그림 3]과 같이 frida-gum 모듈도 설치해준다.

npm install --save @types/frida-gum

[그림 3] frida-gum 모듈 설치

작업 공간(디렉터리) 내에 생성된 package.json에 아래의 데이터를 추가해준다.

{
  "name": "fridali2cpp", // 작업 공간 디레터리 이름
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "frida-compile -o _.js -w index.ts", // 작업 메인 파일 이름 지정
    "attach": "run() { frida -U \"$1\" -l _.js --runtime=v8; }; run",
    "spawn": "run() { frida -U -f \"$1\" -l _.js --no-pause --runtime=v8; }; run",
    "app0-spawn": "npm run spawn com.example.application0",
    "app1": "npm run \"Application1 Name\"",
    "app1-spawn": "npm run spawn com.example.application1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/frida-gum": "^18.4.0",
    "frida-compile": "^16.3.0"
  },
  "devDependencies": {
    "frida-il2cpp-bridge": "^0.8.5"
  }
}

 

타입 스크립트 환경 설정을 위해 tsconfig.json 파일을 만들고 아래의 데이터를 추가해준다.

{
    "compilerOptions": {
      "target": "esnext",
      "lib": [ "es2022" ],
      "experimentalDecorators": true,
      "module": "esnext",
      "allowJs": false,
      "noEmit": false,
      "esModuleInterop": false,
      "moduleResolution": "nodenext",
      "strict": true,
      "sourceMap": true
    },
    "files": [ "index.ts" ] // 작업 메인 파일 이름
  }

 

아래의 코드를 복사한 다음 [그림 4]와 같이 index.ts 파일을 만들어 복사한 코드를 붙여넣기 해준다.

import "frida-il2cpp-bridge"

Il2Cpp.perform(() => {
	Il2Cpp.dump();
	
  	Il2Cpp.trace()
    	  .assemblies(Il2Cpp.domain.assembly("Assembly-CSharp"))
    	  .and()
    	  .attach();
          
  	const AssmblyCSharp = Il2Cpp.domain.assembly("Assembly-CSharp").image;
    const Singleton = AssmblyCSharp.class("Singleton`1");

    Il2Cpp.trace()
     	  .classes(Singleton)
     	  .filterMethods(method => method.name.toLowerCase().includes("instance"))
     	  .and()
     	  .attach();

    const UnityCoreModule = Il2Cpp.domain.assembly("UnityEngine.CoreModule").image;
    const Component = UnityCoreModule.class("UnityEngine.Component");

    Il2Cpp.trace()
    	  .classes(Component)
    	  .filterMethods(method => method.name.toLowerCase().endsWith("component"))
    	  .and()
    	  .attach();
});

[그림 4] index.ts

 

index.ts 파일이 위치한 콘솔 단에서 아래의 명령어를 입력하여 스크립트를 컴파일 해준다.

npm run build

명령어 실행 후 산출물인 _.js 파일이 생성되었다면 컴파일이 성공한 것이다. 만약 아래 [그림 4]와 같이 TS2318, TS6053에러가 발생한다면 파일이 위치한 디렉터리 경로에 띄어쓰기, 한글 문자가 포함되어 있는지 확인한다. 해당 에러의 경우 모듈의 위치를 찾지 못해서 발생하는 오류인데 일반적으로 한글 경로 또는 띄어쓰기 때문에 발생한다.

[그림 5] 컴파일 에러

명령어가 정상 실행되었다면 아래 [그림 6]과 같이 Frida-Compile된 스크립트 파일이 생성된다.

[그림 6] frida-compiile된 스크립트 파일

frida를 통해 분석 앱을 대상으로 생성된 스크립트를 실행한다.

frida -U -f [패키지 이름] -l _.js

[그림 7] Frida 실행

스크립트 결과물은 디바이스 내에 외부 저장소에 저장된다.

 

파일을 확인해보면 Assembly-CSharp.dll 파일부터 Unity 엔진의 코어 모듈의 원본 코드까지 추출된 것을 확인할 수 있다.