Cheating

Wesnoth 해킹하기 (6) DMA 파훼하기

Bithub 2024. 2. 28. 08:36
728x90

게임을 해킹하다 보면 DMA로 인해 매번 원하는 정보가 있는 주소가 틀어진다.

예를 들어, 플레이어의 골드가 담겨있는 주소가 어제는 0x01CD0000 내일은 0x02CEABCD일 수 있다.

이를 파훼하기 위한 3가지 방법을 소개한다.


1번째 방법. Pointer Scan

아래 글에 방법이 자세히 소개되어 있다.

https://bithub.tistory.com/29

 

Wesnoth 해킹하기 (2) Memory Scan

본 포스트는 wesnoth의 메모리를 스캔하고 수정함으로써 게임의 재화인 골드를 올리는 방법을 설명한다. Wesnoth 시작 Wesnoth를 실행해보자. Multiplayer - Local Game을 만들어보자. 게임이 시작된다. 현재

bithub.tistory.com


2번째 방법. Code Cave

3편에서 병력 소집 시 골드 감소를 담당하는 코드를 찾았다:

sub dword ptr ds:[edx+4], ecx

 

이 코드 직후에 code cave를 파서 edx + 4를 참조한다면, gold가 담겨있는 주소를 찾아낼 수 있을 것이다.

Code cave는 다음과 같이 짜여져 있을 것이다.

 

pushad
mov dword ptr ds:[0x12345678], edx+4
popad
...대체된 original code...
jmp 0xredirect_location

 


3번째 방법. Reverse Engineering

sub dword ptr ds:[edx+4], ecx

위 코드가 실행되기 전 edx에 어떤 값이 담기는지 알고리즘 또는 규칙성을 찾아낸다.

예를 들어, EDX가 할당되는 코드를 쭉 따라가보니 EAX + 60의 값을 할당받는다는 것을 찾았다.

또, EAX가 할당되는 코드를 쭉 따라가보자.

결국 base value를 찾아낼 수 있을 것이다.

 

생각해보자.

플레이어의 골드가 담겨있는 주소를 찾아내고 싶다고 하자.

 

Player class는 대충 이런식으로 짜여져 있을 것이다.

class Player {
	string player_name = "hello user";
	Inventory inventory = null;    
}

 

 

Inventory class는 대충 이런식으로 짜여져 있을 것이다.

class Inventory {
	int gold;
	int items[];
}

 

 

플레이어가 게임을 켠다. 그럼 게임 프로그램은 이런식으로 메모리를 할당할 것이다.

Player player;
player.inventory = new Inventory(100, [103434, 23492, ...]);

 

만약 Player class 의 위치만 알아낼 수 있다면 gold의 위치를 찾는 것은 시간문제일 것이다.

그리고 Player class는 게임을 켤 때마다 항상 load되는 class이므로 consistent한 address offset을 가질 것이다.

자 백문이 불여일견. 바로 실습해보자.

 

먼저 Multiplayer game - Local Game을 생성한다.

 

Income을 10으로 설정하여 골드에 대한 디버깅이 용이하도록 하자.

 

 

게임이 시작되면 Cheat Engine을 이용해 gold address를 찾자.

0x09FA7DCC가 골드가 담긴 주소다.

그리고 디버거에서 0x09FA7DCC에 대한 중단점을 설정하자.

 

 

End Turn을 눌러 턴을 종료한다. 그럼 income이 들어올 것이다.

 

 

그럼 해당 라인에서 중단점이 발동되는 것을 볼 수 있다.

북마크해주고 주석도 달아주자 : 'add gold by income'

 

해당 코드를 해석해보자.

gold가 담긴 주소는 EAX+4라고 보여진다.

 

골드가 저장된 주소는 EAX + 4임을 염두해두고 EAX를 역추적(reversing)해보자.

근데 주변에 EAX에 write하는 코드가 없다.

그래서 0x009B4CE3에 저장된 다음 코드가 EAX에 write하는 것으로 의심해볼 수 있다.

call wesnoth.9AE7F0

 

 

0x9AE7F0을 따라가보자.

 

keypoint 1.

우선 0x009AE7F0에서 EAX=00000000으로 세팅되어 있다.

따라서 0x009AE7F0에서 시작되는 코드가 EAX를 세팅하는 코드임을 알 수 있다.

EAX에 세팅되는 값만 알 수 있다면 골드가 저장되는 위치를 알아낼 수 있을 것이다.

 

keypoint 2.

0x009AE7F0에서 코드가 시작되어 0x009AE887에 있는 return문에서 끝나는 것을 확인할 수 있다.

우리는 여기에서 eax가 어떻게 세팅되는지 확인해야 한다.

0x009AE887 return문에서 역으로 eax가 어떻게 세팅되는지 따라가보자.

0x009AE887 : ret // 여기서 reversing 시작. eax를 따라가보자.
0x009AE885 : add eax, edx // edx는 consistent함. reversing할 필요없다.
0x009AE85F : mov eax, dword ptr ds:[ecx+A90] // ecx는 inconsistent함. reversing해야함.
0x009AE853 : mov ecx, dword ptr ds:[ebx+60] // ebx는 consistent함. reversion할 필요없다.

Reversing Process

먼저, 0x009AE885다. eax가 edx의 영향을 받는 것을 알 수 있다.

Reversing을 할 때 꿀팁인데, reversing하고 싶은 값이 다른 값의 영향을 받는다면 우선 영향을 주는 값이 consistent한지부터 확인하자. 복잡도를 그나마 줄일 수 있다.

edx의 값은 constant 0이다. 게임을 껐다켜도 edx의 값은 계속 0으로 관찰된다.(참고 : 사실 edx는 적의 골드를 계산할 경우 0x270의 값을 갖는다.)

오케이. edx는 상수이므로 reversing할 필요없다.

 

다음 0x009AE85F다. 이번엔 ecx가 eax에 영향을 주는 것을 확인할 수 있다. 하지만 안타깝게도 ecx는 매 게임마다 값이 바뀐다. 그래서 reversing해야 한다.

 

ecx에 write하는 구문을 찾아보니 0x009AE853이 있다.

ebx가 ecx에 영향을 주는 것을 확인할 수 있다. 하지만 운이 좋겠도 ebx는 consistent하다. 게임을 껐다켜도 ebx는 0x17EECB8로 고정되어 있다!

 

이렇게 reversing이 끝났다.

골드가 저장되는 변수의 위치는 *(*(0x17EECB8 + 0x60) + 0xA90) + 0x4인 것을 알 수 있다.

이를 cheat engine에서 조회해보자.

 

 

 

Add Address Manually를 누르자.

여기서는 address의 값을 통해 address를 추가할 수 있다.

여기에  *(*(0x17EECB8 + 0x60) + 0xA90) + 0x4를 다음 사진과 같이 추가해주자.

 

 

현재 골드인 999,986골드를 정상적으로 참조하는 것을 볼 수 있다.

 

Dynamic address를 static address + static offset로 찾아내는데 성공했다.

 

The End.