개요
게임 관련 애플리케이션을 대상으로 코드 분석 및 변조 시 가장 많이 활용되는 도구를 뽑으라고 한다면 치트 엔진(Cheat Engine)이 아닐까 싶다. 치트 엔진은 보통 게임의 메모리 주소를 조작하여 게임 데이터를 변경하거나, 게임 내부의 변수나 값을 조작하는 등의 간단한 행위부터 동작 중인 앱을 디버깅하여 맵 포인터 분석, 스크립트 인젝터 등 다양한 행위를 가능케 해주는 애플리케이션이다. 또한, 치트 엔진은 x86 프로세서 아키텍쳐 기반의 운영 체제인 Windows 뿐만 아니라 ARM 프로세서 기반의 안드로이드 애플리케이션 분석 시에도 활용이 가능하다.
해당 포스팅부터 "Assault Cube"라는 FPS 게임을 대상으로 치트 엔진의 사용 방법을 익히고 이후 안드로이드 게임 앱 분석 방법까지 시리즈 물로 작성해보고자 한다.
실습 준비
실습을 진행하기 앞서 먼저 "Cheat Engine(이하 치트엔진)"과 "Assault Cube(이하 어썰트 큐브)"의 설치가 필요하다. 치트 엔진은 아래의 링크에서 다운로드 가능하며, 버전 상관 없이 가장 최신 버전을 다운로드 받아 설치하면 된다.
어썰트 큐브는 아래의 링크에서 다운로드 받아 설치를 진행하면 된다.
실습
어썰트 큐브를 설치하고 실행하면 [그림 1]과 같이 조작을 연습할 수 있는 공간에서 게임이 시작된다.
[그림 1]과 같은 게임 화면에서 "ESC" 버튼을 누르면 게임 메뉴 팝업이 출력되는데 해당 메뉴에서 멀티 플레이부터 게임 설정 등 다양한 조작 선택이 가능하다. 해당 포스팅에서는 싱글 플레이 상태에서 실습이 진행되며, [그림 2]와 같이 설정해주면 된다.
게임 설정이 다 되었다면, 앞서 설치한 치트 엔진을 실행해준다. 그리고 [그림 3]과 같이 오른쪽 상단의 모니터 아이콘 버튼을 클릭하면 실행 중인 프로세스 목록이 출력되는데 여기서 "Applications" 탭에서 어썰트 큐브를 선택하고 "Open" 버튼을 클릭해준다. 참고로 치트 엔진은 일종의 디버깅 프로그램으로 [그림 3]과 같이 실행 중인 프로세스에 치트 엔진을 어태치하여 대상 프로세스에 대한 스택 추적, 실행 흐름 제어, 변수 및 메모리 확인 등의 프로그램 상태를 모니터링 할 수 있도록 해준다.
[그림 4]와 같이 프로세스 정보가 치트 엔진 프로그램 상단에 출력된다면 정상적으로 대상 프로세스에 어태치 된 것이다.
치트 엔진을 활용하여 게임 앱을 분석하는데 있어 우선시 봐야할 것은 포인터 맵이다. 게임에서 변수, 객체 또는 다른 데이터를 저장할 때 해당 데이터는 메모리 상의 특정 주소에 위치하게 된다. 그리고 포인터는 해당 메모리 주소를 가리키는 변수로, 실제 값을 포함하고 있는 주소를 가지고 있어 이를 분석한다면 분석 대상의 구조체 정보 등을 파악할 수 있게 된다. 다만, 포인터 맵을 제대로 분석하기 위해서는 C/C++ 프로그래밍 언어에 대한 이해와 역공학에 관한 지식이 필요하다. 따라서, 여기서는 바로 포인터 맵을 통한 분석을 진행하지 않고 기초적인 것 부터 시작하여 차근차근 단계를 높여가며 분석하고자 한다.
먼저 변조 포인트를 찾아야 한다. FPS 게임의 경우 캐릭터 체력(HP), 총알, 좌표(ESP), 총기 반동 등 다양한 변조 포인트들이 있는데 여기서는 "체력"을 변조 포인트로 잡아보겠다.
[그림 5]와 같이 현재 캐릭터의 체력은 100으로 이는 특정 위치에 존재하는 메모리 주소의 값이 100이라고 판단할 수 있으며, 이를 [그림 6]과 같이 치트 엔진을 통해 100이라는 정수 값을 가진 메모리를 검색한다.
참고로 [그림 6]에서 Scan Type을 "Exact Value"로 Value Type은 "4 Bytes"로 지정했는데 여기서 Exact Value는 검색하고자 하는 값과 정확히 일치하는 값을 가진 메모리 주소를 검색할 때 사용하고 [그림 7] 처럼 그 외에 옵션들은 따로 설명하지 않더라도 단어 뜻을 통해 알 수 있을 것이다. 그리고 Value Type을 "4 Bytes"로 설정한 이유는 C/C++ 프로그래밍 언어에서 정수(Int)는 4 Bytes(32 Bit)를 가지는데 지금 우리가 검색하려는 캐릭터 체력의 경우 일반적으로 게임에서 정수 타입으로 구현되기 때문에 [그림 6]과 같이 설정한 것이다.
데이터 타입 | 크기 |
int | 4 Bytes(32 Bit) |
short | 2 Bytes(16 Bit) |
long | 4 Bytes(32 Bit) or 8 Bytes(64 Bit) |
long long | 8 Bytes(64 Bit) |
[표 1] 데이터 타입
치트 엔진을 통해 "100"을 검색했다면 [그림 8]과 같이 "100"이라는 값을 가진 굉장히 많은 메모리 주소가 출력될 것이다. 보편적으로 한 번의 검색을 통해 값을 바로 찾는 경우는 드물다. 따라서, 체력 값을 줄여가며 재 검색을 통한 선별 과정이 필요하다.
[그림 9]와 같이 체력을 낮아진 상태에서 해당 값을 재 검색해준다. 이때, 재 검색 시에는 [그림 6]에서 처음 검색 시 클릭했던 "New Scan" 버튼이 아닌 "Next Scan" 버튼을 클릭해준다. 해당 버튼을 클릭하면, 처음 검색하여 출력된 결과를 대상으로 검색한다.
보통 1~2번 재 검색하면 체력 값을 가진 메모리 주소가 선별되며, [그림 10]과 같이 해당 메모리 주소를 더블 클릭해주면 테이블 뷰로 추가된다. 테이블 뷰에 추가된 메모리 주소는 새롭게 검색하거나 재 검색을 해도 사라지지 않는다.
[그림 11]과 같이 테이블 뷰에 추가된 메모리 주소의 "Value" 탭을 클릭하면 값 변조가 가능해지며, 여기서는 999999로 설정해준다.
모든 과정이 끝나면 [그림 12] 처럼 캐릭터 체력이 변조한 값으로 변경된 것을 볼 수 있다.
마무리
지금까지 어썰트 큐브라는 게임을 대상으로 치트 엔진을 이용하여 동적 메모리 값을 변조하는 방법을 살펴봤다.
다음 포스팅에서는 정적 메모리 값을 변조하는 내용을 다뤄보도록 하겠다.
'안드로이드 기타 > 게임 분석' 카테고리의 다른 글
치트 엔진을 이용한 코드 인젝션 (1) | 2023.08.21 |
---|---|
치트엔진을 안드로이드 디바이스에 연결시키는 방법 (1) | 2023.08.21 |
치트 엔진을 이용한 정적 메모리 값을 변조하는 방법 (0) | 2023.08.07 |
안드로이드 Assembly-CSharp.dll 덤프 모듈 frida-il2cpp-bridge (0) | 2023.07.18 |