━━━━ ◇ ━━━━
IT/기술 정리

[보안] APK 코드 분석하는 방법

📌 개요

 

 

오늘은 우리가 만든 Unity 게임의 APK 파일이 어떻게 만들어지고, 또 그 안의 코드를 어떻게 엿볼 수 있는지에 대해 이야기해 보려고 합니다. 누군가의 APK를 몰래 뜯어보는 건 좋지 않지만, 내 게임의 빌드 구조를 이해하고 보안 취약점을 점검하는 데는 큰 도움이 되겠죠?

 

 

 

 

 


1. Unity APK 빌드 원리

 

우리가 C# 스크립트를 작성하고 Unity 에디터에서 'Build' 버튼을 누르면, 내부적으로 아주 복잡한 과정이 일어납니다. 이 과정을 빌드 파이프라인이라고 하는데요, 핵심적인 단계는 다음과 같습니다.

 

 

 

1. C# 소스 코드 -> .NET Assembly (.dll)

 

여러분이 작성한 C# 스크립트는 가장 먼저 .NET Assembly라고 불리는 .dll 파일로 변환됩니다. 이 파일 안에는 사람이 읽을 수 없는 IL(Intermediate Language) 코드가 담겨 있습니다. 대표적인 파일이 바로 Assembly-CSharp.dll이죠.

 

 

 

2. 스크립팅 백엔드 (Scripting Backend)

 

.dll 파일을 최종 실행 가능한 코드로 변환하는 작업입니다. Unity는 두 가지 방법을 제공합니다.

 

Mono: JIT(Just-In-Time) 컴파일 방식을 사용합니다. 게임이 실행될 때 기기에서 실시간으로 IL 코드를 네이티브 코드로 변환합니다. 변환 속도가 빠르지만, 런타임 성능 최적화에 한계가 있고, .dll 파일이 그대로 남아있어 디컴파일이 매우 쉽습니다.

 

IL2CPP: AOT(Ahead-Of-Time) 컴파일 방식입니다. IL 코드를 C++ 코드로 먼저 변환한 다음, 각 플랫폼에 맞는 컴파일러를 통해 **네이티브 코드(.so 파일)**로 만듭니다. 빌드 시간이 오래 걸리지만, 런타임 성능이 뛰어나고 코드가 네이티브 파일로 바뀌어 디컴파일이 훨씬 어렵습니다. 모바일 게임에선 거의 필수적으로 사용됩니다.

 

 

 

3. Android 처리 및 APK 생성

 

Unity는 Android SDK와 NDK를 활용하여 최종 APK를 만듭니다. Gradle 빌드 시스템을 사용하며, 이 과정에서 Unity의 자바 플레이어 코드는 DEX 파일로, IL2CPP로 생성된 네이티브 코드는 .so 파일로 변환됩니다. 에셋 번들 같은 리소스 파일들과 함께 이 모든 파일이 하나의 ZIP 압축 파일 형태로 묶이게 되는데, 이것이 바로 우리가 아는 .apk 파일입니다.

 

 

 

 

 


2. JADX : APK 디컴파일을 위한 파일 추출

 

 

 

먼저 APK 파일을 준비합니다. 그리고 JADX 툴을 통해 APK 파일을 열어줍니다. 

 

※ JADX : classes.dex 파일을 자바 코드로 변환하고 보여주는 최고의 도구입니다. Android 프로젝트를 분석할 때 주로 사용됩니다.

 

 

 

JADX로 APK을 열어보면 위와 같습니다.

 

 

트리 구조를 살펴보면 위 사진처럼 .so 파일을 확인할 수 있습니다. 여기서 libil2cpp.so 파일이 바로 Unity에서 작성한 여러분의 C# 스크립트가 컴파일되어 나온 C++ 네이티브 코드입니다. 여기있는 .so 파일들을 모두 받아주세요.

 

 

 

그리고 위 사진처럼 global-metadata.dat 파일도 확인할 수 있습니다. 이 파일도 디컴파일에 필요하니 받아주세요.

 

 

 

 

 


3. IL2CPPDumper : .so파일을 C# 코드로 변환

 

IL2CPPDumper 툴을 다운받아 실행해줍니다. 그러면 검은색 프롬프트 창이 뜰겁니다.

 

 

 

 

IL2CPP 바이너리 파일을 선택하라고 뜹니다. libil2cpp.so 파일을 선택해줍니다.

 

 

 

 

그 후 global-metadata 파일을 선택합니다. 

 

 

 

 

위 사진처럼 출력되면 성공입니다. IL2CPPDumper 툴 위치에 DummyDll 폴더, 그리고 dump.cs 라는 파일이 생성된 것을 확인할 수 있습니다. dump.cs 파일을 한번 열어보세요.

 

 

 

 

그러면 실제 코드를 확인할 수 있습니다. 하지만 실제 코드 구현 로직은 확인할 수 없습니다. .so 파일의 네이티브 코드를 C#의 더미(dummy) 코드로 변환했지만, 내부 구현 로직은 복원하지 못합니다. 하지만 내부 구현 로직은 여전히 C++ 네이티브 코드 형태로 남아있기 때문에 복원할 수 있습니다. 이제 마지막 단계입니다.

 



 


4. Ghidra : .so파일을 어셈블리어로 변환 + 구현 로직 확인

 

 

 

Download the Latest Java LTS Free

 

Download the Latest Java LTS Free

Subscribe to Java SE and get the most comprehensive Java support available, with 24/7 global access to the experts.

www.oracle.com

 

 

Ghidra 툴을 사용하기 위해선 JDX 21 이상의 버전이 필요하니 참고해주세요.

 

※ 시스템 변수에 JAVA_HOME, C:\Program Files\Java\jdk-21 추가해주세요.

 

 

 

먼저, il2cpp_header_to_ghidra.py 으로 히드라 헤더 파일이 필요합니다.

 

 

il2cpp_ghidra.h 헤더를 추가합니다.

 

※ 헤더는 마지막 위치에 반드시 존재해야 합니다.

 

 

 

 

il2cppDumper 폴더를 추가하고 번들을 적용합니다.

 

 

 

 

ghidra_with_struct.py 실행 후 script.json을 선택합니다.

 

 

 

 

그러면 위 사진처럼 데이터 타입과 함수 이름이 매핑된 것을 확인할 수 있습니다.

 

 

 

 

모든 세팅이 끝났다면 분석을 시작합니다.

 

 

 

분석을 마치면 위 사진처럼 사람이 읽을 수 있는 수준으로 디컴파일할 수 있습니다. 물론 수도 코드(Pseudo-Code)에 가깝지만 이정도면 충분합니다. 자세히 살펴보면 위 코드는 게임 골드를 지급하는 메서드라는 것을 확인할 수 있습니다.

 

사실 여기서 어셈블리어를 수정하고 APKTool을 사용하여 APK을 생성하면 버그판을 만들 수 있습니다. 하지만 이 방법에 대해서는 다루지 않도록 하겠습니다.

 

물론 인디 게임사가 아닌, 대형 게임사라면 보안/난독화 시스템으로 분석이 어려울 수 있습니다. 혹시라도 직접 개발한 APK가 위처럼 코드를 분석할 수 있는 구조라면 보안에 신경을 쓰는 편이 좋겠습니다.

 

※ 보안 관련된 문제이기 때문에 과정을 간략화했습니다.



 

 

 

'IT > 기술 정리' 카테고리의 다른 글

[보안] C# AES-256 암복호화 알고리즘  (0) 2024.12.09
[보안] C# XOR 암복호화 알고리즘  (0) 2024.12.06
COMMENT