본문 바로가기

Android/Tech

[안드로이드] 유니티 게임 엔진 IL2CPP 해킹

 


 이전 글에서는 mono로 빌드된 유니티 게임 변조방법에 대해서 살펴보았습니다. mono로 빌드된 게임의 경우 쉽게 디컴파일되어 게임코드를 볼 수 있을 뿐만 아니라 아주 간단하게 코드 삽입 및 변조가 가능하여 보안에 매우 취약하다는 점입니다. 당장 구글에 모드앱과 관련된 단어를 통해 검색만 하더라도 악성 광고 삽입과 동시에 여러 핵들이 포함되어 있는 변조앱들을 손 쉽게 발견할 수 있습니다. 물론 해킹 이슈를 방어하기 위해 코드난독화 또는 보안솔루션 도입과 같은 방법이 있긴하나, 이것 또한 비용에 따른 문제등으로 손쉽게 해킹을 차단하기가 만만치 않습니다. 그래서 이번편에서는 해킹 이슈가 조금이라도 덜한 il2cpp로 빌드된 유니티 게임에 대해서 설명하고자 합니다.  왜 해킹 이슈를 줄일 수 있는가에 대해서 차근차근 설명드리도록 하겠습니다.

il2cpp AOT 컴파일
 

 우선 mono빌드와 달리 il2cpp 빌드는 mono 가상머신이 IL코드를 읽는 방식이 아닙니다. mono는 빌드를 수행할 때 C# 코드를 msc.exe가 IL코드로 변환하고, 이 IL코드를 mono가상 머신이 읽게되는 방식입니다.  IL2CPP의 경우 만들어진 IL코드를 il2cpp.exe 에 의해 C++ 소스코드로 변환하게 되고 c++ 컴파일러에 전달되어 네이티브 라이브러리(.so)파일이 생성되는 구조로 AOT컴파일 방식을 사용합니다. 그렇기때문에 실행도중 IL코드를 읽고 실제 기계어를 수행하는 JIT 컴파일 방식이 아니라 미리 기계어를 만들어놓고 바로 수행하는 방식이기 때문에 속도 또한 향상이 되었다고 합니다. 또한 이전에 발생하던 잦은 크래쉬 현상도 많이 해결되어 최근에 출시되는 유니티 게임들이 해당 방식을 많이 사용하고 있다고 합니다.

APK 내부 네이티브 라이브러리

 

 그렇다면 il2cpp로 빌드된 게임앱을 어떻게 변조하는지에 대한 방법을 살펴보겠습니다. apk 파일을 열어보면 lib폴더내에 여러 아키텍처에 맞는 폴더가 존재하며 여러 폴더에는 위 그림과 같이 아키텍처에 맞는 세개의 네이티브 라이브러리 파일이 포함되어 있습니다. libunity.so는 초기에 먼저 로드되어 il2cpp 실행에 대한 여러 초기설정을 한 뒤 libil2cpp.so를 로드하여 작동하게 됩니다. 실제 기계어로 변환된 실제 게임코드들은 libil2cpp.so 파일에 존재하며, 해당 파일을 변조하여 게임코드들을 수정할 수 있게됩니다. 

libil2cpp.so

 

이때 게임코드들은 아키텍처에 맞는 기계어로 되어있기 때문에 mono와 달리 어셈블리어를 모르면 수정을 할 수 없습니다. 어셈블리어를 안다는 기준하에 간단한 리턴값 수정이나 nop 패치등 간단한 변조들은 간단하게 할 수 있습니다. 다만, mono처럼 악성광고를 삽입, 특정 함수 호출코드들을 삽입등의 까다로운 방법들은 쉽게 패치가 불가능할 것입니다. 이러한 이유로 해킹에 대한 이슈를 조금이라도 줄일 수 있다고 하지만 역시나 간단한 변조는 막을 수 없을것입니다ㅠ. 
 다음으로 실제 해킹에 사용되는 툴과 사용법에 대해서 알아보겠습니다.

il2cpp 로 만들어진 바이너리에 대한 필드, 메소드등의 정보와 실제 위치를 알려주는 "il2cppDumper" 라는 툴입니다. 깃허브 내 사용법처럼 apk 파일내에 "assets\bin\Data\Managed\Metadata\global-metadata.dat" 를 추출하고 "libil2cpp.so" 파일을 통해 출력파일을 생성할 수 있습니다. 여기서 "DummyDll" 폴더에는 "Assembly-CSharp.dll" 파일이 생성되는데 이 파일을 DnSpy를 통해 확인할 수 있습니다. 단, 게임내 코드를 확인할 수 있는게 아니라 특정 메소드의 위치만 확인할 수 있습니다. 즉, 위치만 확인하되 코드 패치 또는 후킹을 이용하여 변조를 하게되는것이지요.

dump.cs
 

또한 "DummyDll" 폴더만 생성되는것이 아니라 dump.cs를 통해서도 쉽게 확인할 수 있습니다. 위 그림과 같이 클래스와 메소드 이름의 주소를 확인할 수 있습니다.
 il2cppDumper라는 툴이 동작을 하지 않는다면 메타데이터 파일, 네이티브 라이브러리가 암호화 되어 있을 확률이 큽니다. 이 경우 mono와 마찬가지로 덤프를 이용하여 복호화된 파일을 쉽게 얻을 수 있으며, il2cpp가 동작하는 내부 코드들은 공개되어 있기때문에 유니티 내부 특정 함수들을 후킹하여 게임에 대한 정보들을 Dumper 툴을 사용하지 않고도 쉽게 얻어올 수 있습니다. 단, 후킹 기술을 사용할 수 있으며 아키텍처에 맞는 어셈블리어 작성 및 분석 능력도 요구되겠죠? 
다음편에서는 메모리 덤프 방법에 대해서 다뤄보겠습니다.

 


http://linforum.kr

 

린포럼 (Lin Forum) - 안드로이드 커뮤니티

안드로이드 보안/개발 커뮤니티입니다.

linforum.kr