2006-02-11

About Game Hacking

원문: Game Hacking : An Overview by Sorin
정리: 이동우(leedw at ssrnet.snu.ac.kr), 2004.8.1

내용
Intro GameHacking이란 무엇인가 필요한 tool은 무엇인가 GameHacking의 유형 DMA란 무엇인가 Code Injection Saved game patching Value poking Memory patching Excutable/Dll patching Techniques 총알, 생명치, 마나등 증감되는 수치를 알고 있는 경우 에너지 바, 총알,
생명치, 마나등 정확한 값을 알 수 없는 경우 Instant Build Hacking Techniques map이 저장되는 방식

1 Intro어렸을 때 한창 오락실에서 죽치고 앉아 있던 시절, 아이들 사이에서는 갤러그나 제비우스에서, 혹은 마계촌같은 게임에서 적의 공격을 받아도 죽지 않는 '무적' 모드가 되는 비법이 회자되곤 했었다. 캐릭터를 특정 위치로 옮겨서 적 캐릭터가 어느 지점에 나타나는 시점에서 무슨 키를 동시에 누르면 무적 모드가 된다는 이야기들이 소곤 거리며 나돌았는데 무적 모드로 플레이를 하면 오락실 아저씨가 와서는 전원을 내려버렸다는 소문이 전설처럼 떠돌았다.
마지막 판은 고사하고 각 레벨의 끝에서 만나는 보스 캐릭터에게 번번히 죽임을 당해서 오락실 기계에 무수히 동전을 갖다 바쳐야 했던 필자같은 아이들에겐 솔깃한 이야기가 아닐 수 없었지만 실제로 무적 모드의 동작이 확인된 것은 없었다.
요즘식으로 말하자면 아마 그것이 게임 프로그램내의 '버그'를 가리키는 것이었을 텐데 그런 버그를 이용하는 것도 일종의 GameHacking의 한 기법이라 할 수 있을 것이다.
이 페이지에서는 바로 GameHacking이 무엇이고 어떻게 이루어지는지에 대한 개략적인 소개를 담게 된다.

2 GameHacking이란 무엇인가필자같이 게임의 끝판까지 클리어하지 못하는 '순발력이 떨어지는' 플레이어가 특정 레벨이나 게임을 클리어하는 방법은 다음의 두 가지중의 하나일 것이다.

게임내에 내장되어 있는 치트 코드(cheat code)를 쓴다. 물론 이것은 게임에서 지원해줘야 한다.
그리고 치트 코드를 쓰게 되면 일반적으로 게임에서 정상적인 플레이어가 아닌 cheater로 간주되기 때문에 고득점이 인정되지 않는등의 제약 사항이 있다.
게임에서 제공하는 치트 코드를 쓰지 않고 툴을 이용해서 게임의 법칙을 바꾼다.
(예를 들어 생명치, 에너지, 총알의 제한을 무제한으로 푸는 것.)
우리가 GameHacking이라고 부르는 것은 2번 방법이다.
주의: 당신이 정말 게임을 좋아한다면 치트 코드는 물론 GameHacking을 통해서 플레이 하는 것은 자제해야 한다. 정상적인 방법으로 게임을 클리어했을 때 게임의 진정한 묘미를 느낄 수 있다.
게임을 모두 클리어하고 난 이후 hacking을 연습하기 바란다.

3 필요한 tool은 무엇인가
Debugger (SoftIce, Olly등) : 메모리에서 실행되고 있는 게임 코드를 추적할 때 쓴다.
Memory Searcher (TSearch, ArtMoney, GameHack, Game Trainer등) : 게임에서 우리가 원하는 값(생명치, 총알, 에너지, 마나, money)이 저장된 주소를 찾아내려 할 때 쓴다.
Hex Editor (HexWorkshop, Hacker's View등) : 게임의 실행 파일(exe)이나 라이브러리(dll), 혹은 게임의 세이브 파일을 편집할 때 쓴다.
Disassembler (W32Dasm) : 게임의 dead-listing을 다룰 때 쓴다. dead-listing이란 메모리이 올려져 있지 않은, 파일로 저장된 게임의 asm 코드를 말한다.
Trainer Maker (Trainer Maker Kit) 혹은 프로그래밍 지식 : 선택 사항이다. trainer를 만들고자 할 때 필요하다.
위의 툴은 http://www.protools.cjb.net/ 이나 http://www.gamehacking.com/ 등 인터넷에서 쉽게 찾을 수 있다.
툴의 캡쳐 하면을 구경하려면 다음을 클릭하라.

4 GameHacking의 유형게임을 hacking하기 위한 방법을 요약하면 다음과 같다.

메모리 해킹(Memory Hacking) : 게임이 차지하고 있는 메모리 영역을 조작하는 기법이다.
1.1 Value poking : 게임에서 제한된 값으로 주어진 생명치, 에너지, mana, 총알 갯수등이 저장된 메모리 값을 변경시키거나 특정 값으로 freezing 하는 것. 1.2 Memory patching : 메모리에 로드된 게임 코드를 변경시키는 기법. 예를 들어 플레이어가 총을 쏠 때 총알을 감소시키는 코드가 다음과 같다고 하자.
어셈 코드 헥사 코드 dec ecx 49 이때 총을 쏴도 총알을 감소시키지 않게 하기 위해선 다음과 같이 instruction을 바꾸면 된다.
어셈 코드 헥사 코드 nop 90 "dec ecx"를 "nop"로 변경시킴으로 해서 총알 갯수는 줄어들지 않을 것이다. 이 방법은 DMA를 다루어야 할 때 가장 효율적인 방법이 된다.

실행파일 해킹(Executable/Dll Hacking) : Memory hacking 기법은 손쉬운 면이 있는 반면, 게임을 exit하고 다시 실행하면 기존에 메모리를 조작했던 것들이 모두 소용이 없게 된다. 실행 파일이나 라이브러리 자체를 조작해야 변경시켰던 값이나 코드가 영구적으로 지속될 수 있다.
2.1 EXE/DLL patching : Memory patching과 동일하나 조작 대상이 메모리가 아닌 파일이며 수정된 사항을 영구적으로 지속시킬 수 있다는 차이가 있다.

세이브 파일 해킹(Saved_Game Hacking)
3.1 .SAVED Patching : 해킹하고자 하는 게임이 "save game" 옵션이 있을 때 세이브 파일을 뒤져서 셋팅 값을 변경시키는 기법이다. 주로 헥사 에디터가 쓰인다.

5 DMA란 무엇인가DMA(Dynamic Memory Allocation)은 게임이 메모리를 다루는 방식의 일종이다. 이름이 의미하는 바와 같이 게임은 어떤 값을 저장하기 위해 메모리를 동적으로 할당하는 경우가 있다. 그것은 게임을 실행할 때마다 어떤 값이 저장되는 메모리 위치가 계속 바뀔 수 있다는 것이다. 예를 들어 어떤 게임에서 총알 갯수, 수류탄 갯수, 생명치를 저장하는 메모리 주소가 다음과 같다고 해보자.
bullets : 100 grenades : 120 life : 150
그런데 게임을 빠져나가서 다시 실행하면 메모리 주소가 다음과 같이 바뀐다.
bullets : 510 grenades : 530 life : 560
그 다음에 게임을 다시 실행하면 또 다음과 같이 바뀐다.
bullets : 610 grenades : 630 life : 660
여기서 무엇을 추론할 수 있는가? 데이타가 저장되는 메모리 주소는 매 실행시마다 바뀐다. 그러나 자세히 보면 3가지 경우 주소값이 100단위로 바뀌며 각 데이타가 저장되는 메모리 주소의 차이는 모두 동일함을 알 수 있다. 즉, 수류탄 갯수는 총알 갯수가 저장된 메모리 주소에서 20번지 다음에 있고 생명치는 수류탄 갯수에서 30번지 다음에 위치해 있는 것이다. DMA 문제는 이렇게 메모리 주소의 패턴pattern을 찾는 것부터 풀어나갈 수 있다. 메모리 주소 할당의 패턴을 알아낸다면 다른 데이타가 저장된 주소값을 쉽게 유추할 수 있다.

6 Code Injection위에서 DMA가 무엇인지 알아 보았다. 그럼 이렇게 매번 메모리 주소가 바뀌는 문제를 어떻게 우회할 수 있을까. 간단하다. 바로 code injection 기법을 이용하면 된다. code injection은 이름이 의미하는 것과 같이 게임에 code를 삽입시키는 것이다. 예를 들어 게임에서 다음과 같은 코드를 찾아냈다고 하자.
mov eax,[ecx] - ecx = 총알 갯수가 저장된 주소를 가리키는 포인터 dec byte ptr [eax] - eax = 총알 갯수가 저장된 주소 mov eax,[ecx+20] - ecx+20 = 수류탄 갯수가 저장된 주소를 가리키는 포인터 dec word ptr [eax] - eax = 수류탄 갯수가 저장된 주소 mov eax,[ecx+50] - ecx+50 = 마나 수치가 저장된 주소를 가리키는 포인터 dec dword ptr [eax] - eax = 마나 수치가 저장된 주소 여기서 처음 ecx에 저장된 주소로 수류탄, 마나 수치까지 알아 낼 수 있으므로 parent address라고 한다. 그런데 이 주소는 DMA로 결정되기 때문에 게임 실행시마다 변경되는 값이다. 그래서 이 ecx 값을 언제든 참고할 수 있도록 특정 장소에 저장해둘 필요가 있다. 특정 장소를 어떻게 찾을까.
게임 코드가 저장된 영역에서 0나 혹은 FF만 채워져 있는 영역을 찾아낸다. 그 부분이 바로 코드에서 쓰이지 않는 영역인데 거기에 우리의 코드를 삽입할 것이다. 코드를 삽입하는 절차는 간단하다.

게임 메모리에서 0이나 FF로 채워져 있는 빈 공간을 찾는다.
게임 코드내에 그 공간으로 "jmp"하거나 "call"하는 코드를 overwrite한다.
overwrite했던 코드 부분을 재구성한다.
나머지 빈 공간에 임의의 코드를 삽입한다. (parent address를 잡아내는 코드)
다시 원래 코드로 jmp back하거나 ret하도록 한다.
위에서 예로 든 코드를 다시 예로 들면,
[ORIGINAL[ [MODIFIED] mov eax,[ecx] jmp 4455 (place full of 0's) dec byte ptr [eax] dec byte ptr [eax] <-------<---------<----<--------- mov eax,[ecx+20] > mov eax,[ecx+20] ^ dec word ptr [eax] dec word ptr [eax] mov eax,[ecx+50] mov eax,[ecx+50] dec dword ptr [eax] dec dword ptr [eax] ^ ................... offset 4455: (원래 0으로 채워져 있던 빈 공간) mov eax,[ecx] - overwrite했던 instruction을 재구성 ^ mov dword ptr[5555],ecx - parent address를 static 영역에 저장 jump back to "dec byte ptr [eax]" ----->--->--->------>------> 이렇게 해서 parent 주소를 static 주소 영역에 저장함으로써 DMA 문제를 해결할 수 있다. trainer에서는 5555번지를 읽어 내서 총알, 수류탄, 생명치등의 수치를 조작할 수 있게 되는 것이다. 예를 들어 trainer에서 총알 갯수를 가져오고자 한다면 다음과 같이 코드를 짜면 된다.
... 5555번지 값을 읽어서 temp에 저장 push temp pop ecx ---> ecx = 총알 갯수가 저장된 주소의 포인터 mov ecx,[ecx] ---> ecx = 총알 갯수가 저장된 주소 mov eax,[ecx] ---> eax = 총알 갯수 (DAM에 관한 sheep의 tut들을 읽어보라.)

7 Saved game patching먼저 .SAVE file patching에 대해서 알아보자. 이는 게임의 세이브 파일을 뒤져서 우리가 원하는 수치가 저장된 데이타를 찾아내서 새로운 수치 값으로 변경시키는 기법을 말한다.
Warcraft2를 예로 들면, single player game을 플레이하다 게임을 저장한다. 이때 보유한 gold 값을 기억해 둔다. 이제 세이브 파일을 HexWorkshop으로 로딩해서 해당 gold 값을 search한다. 이때 search type은 32 Bit Unsigned long 으로 지정한다. 이는 10진수 값으로 찾기 위한 것인데 이 값이 65535 (WORD 타임의 최대값) 보다 크기 때문이다. "value" 에디트 박스에 money 수치를 입력하고 OK를 누르면 된다. 간단하지 않은가?
여기서 원하는 값을 찾기 위해선 세이브를 여러번 시도해야 할 때가 있다. 즉, 마나나 마법 수치등의 값을 찾으려 할 때 상태가 바뀌는 시점에서 게임을 세이브하고 HexWorkshop에서 compare 기능으로 원하는 값을 추적하는 것이다.
그러나 starcraft와 같은 많은 게임들이 세이브시키는 데이타를 암호화(encrytion)하는 경우가 많다. 이 경우에는 세이브 파일을 해킹하는 것보다는 value poking과 같은 게임의 메모리를 해킹하는 것이 더 수월하다.

8 Value poking이 방법은 GameHacking의 가장 일반적인 기법이다. Memory searcher(Tsearch, ArtMoney, 기타 등등)로 특정 수치 값을 검색한다. 물론 이때 같은 값을 가진 주소들이 우후죽순처럼 검색된다. 그럼 게임으로 돌아가서 해당 수치가 바뀌는 동작을 한다. 그리고 다시 memory searcher로 돌아가서 next search로 변경된 수치를 검색한다. 이와 같은 동작을 여러 차례 반복하다 보면 검색되는 주소가 몇 개의 주소로 좁혀지게 된다. 이제 남은 일은 의심되는 주소 값들을 바꾸어 보면서 게임내에서 어떤 영향을 미치는지 알아보는 것이다.
9 Memory patchingDMA, code injection과 함께 쓰이는 경우가 많으며 어셈 지식이 필요하다. 예를 들어 총알의 갯수를 저장하는 주소가 100번지라는 것을 알아냈다고 하자. SoftIce로 100번지에 메모리 브포를 건다. 메모리 브포는 bpm 커맨드를 쓰며 브포 플래그에 읽기(r)나 쓰기(w)를 지정할 수 있다. 이때 주의할 것은 브포를 거는 영역이 게임 모듈 내라는 것을 확인하도록 한다. 소아 화면에서 커맨드 윈도우 오른쪽 하단부에 현재 모듈 네임이 디스플레이 되니 확인하도록 한다.
100번지에 메모리 브포를 걸었으면 게임으로 돌아가서 총을 쏴보자. 그럼 소아 화면이 나타나게 된다.
............. 0001: mov eax, [44] 0002: dec eax ---> 브포는 여기서 걸리게 된다. 0003: mov dword ptr [44], eax ............ 44번지에서 총알의 갯수를 가져와서 총알 갯수를 1 감소시킨다. 그리고 감소된 총알 갯수를 다시 44번지에 저장하는 모습을 볼 수 있다. 따라서 총알 갯수를 감소시키지 않게 하기 위해 "dec eax" 부분에서 단지 nop만 삽입하면 된다. 이것은 SoftIce에서 다음과 같이 입력하면 된다. asm 0002 nop 이렇게 해서 memory patching이 이루어지게 된다.
그러나 매번 게임을 실행할 때마다 SoftIce에서 이런 작업을 할 수는 없는 노릇이다. 이때에는 2가지 방법이 있는데, 첫번째는 trainer를 만들든지 두번째로 게임의 실행 파일이나 라이브러리 자체를 patch하는 것이다.

10 Excutable/Dll patching이 방법은 memory paching과 거의 흡사한 방법이다. 단지 patch된 내용이 영구적으로 지속된다는 점이 다를 뿐이다. 여기서는 위의 총알 갯수를 patch하는 예로 해커의 비장의 무기라 불리는 Hiew를 이용해 보자. Hiew에서 게임의 실행 파일을 로딩한다. F4키를 눌러서 "Decode"를 선택한다. 그리고 F5 (goto address)를 눌러 소아에서 추적했던 총알 갯수를 감소시키는 0002 번지를 입력한다. F3 (Edit)를 누르고 F2 (asm)를 눌러서 "nop" instruction을 입력한다. 그리고 Enter와 ESC를 차례로 누르고 F9 (Save)를 누르면 작업이 모두 끝난다.
주의 : 때론 어떤 게임에서는 생명치나 money등을 감소시키는 instruction이 .exe 내에 없고 .dll에 있는 경우가 있다. 이때는 소아 화면에서 해당 instruction의 offset을 보면 쉽게 알아볼 수 있다. 해당 instruction의 offset이 1XXXXXXX 형태일 때는 현재 dll 안에 있는 instruction이라고 볼 수 있다. 이것은 소아 화면에서 커맨드 윈도우의 오른쪽 하단에 나타나는 모듈 이름에서도 확인할 수 있다.

11 Techniques게임을 해킹하는 여러 테크닉들을 소개하면 다음과 같다.
11.1 총알, 생명치, 마나등 증감되는 수치를 알고 있는 경우
메모리 검색기(memory searcher)로 해당 값을 검색한다.
게임내에서 수치(총알, 생명치, 마나등)가 변경되는 행동을 한다.
메모리 검색기에서 검색한 수치내에서 변경된 값으로 재검색 한다.
검색된 결과 값이 2 - 3개로 좁혀질 때까지 반복한다.
남겨진 주소 값의 내용을 변경시켜 보고 게임에서 어떤 영향을 미치는지 확인한다.

11.2 에너지 바, 총알, 생명치, 마나등 정확한 값을 알 수 없는 경우
메모리를 dump한다.
게임 내에서 수치(총알, 생명치, 마나등)가 변경되는 행동을 한다.
dump한 메모리 중에서 수치가 변경된 주소를 검색한다.
검색된 결과 값이 좁혀질 때까지 반복한다.
남겨진 주소 값의 내용을 변경시켜 보고 게임에서 어떤 영향을 미치는지 확인한다.
참고 : 보통 full로 찬 에너지 바의 수치 값은 250 근방일 경우가 많다.

11.3 Instant Build Hacking Techniques
메모리를 dump한다.
게임 내에서 에너지 수치를 변경시키는 행동을 한다.
dump한 메모리에서 값이 감소된 주소(일반적으로 감소지만 드믈게는 증가되는 게임도 있다.)를 검색한다.
몇 개의 주소로 좁혀질 때가지 반복한다.
남겨진 주소 값의 내용을 변경시켜 보고 게임에서 어떤 영향을 미치는지 확인한다.

11.4 map이 저장되는 방식2D 전략게임인 경우 일반적으로 맵은 matrix 형태로 저장된다.
___________________________ a[1,1] a[1,2] .... a[1,n] a[2,1] a[2,2] .... a[2,n] ...............X......... -> X are your units, and that purple color means u see .....................X... the map in that place a[m,1] a[m,2] .... a[m,n] ----------------------------- 예를 들어 플레이어의 유닛이 a[5, 6]으로 이동한다면, 게임 엔진은 그 장소를 나타내는 matrix를 안개없는 지역(place_without_fog)으로 coded-byte를 집어 넣게 된다. (즉, matrix내에 0은 보이지 않는 지역, 1은 보이는 지역으로 나타낸다면 플레이어의 위치 x 주위를 1로 상태가 바뀌게 된다.) 플레이어의 유닛이 그 장소를 떠난다면 안개 지역(place_with_flog)으로 coded-byte를 삽입한다. (즉, 해당 위치를 나타내는 matrix에 0을 넣게 될 것이다.) 따라서 게임은 다음 2가지의 옵션이 있게 된다.

타이머에 따라 map을 refresh
플레이어의 유닛이 이동함에 따라 map을 refresh
물론 이것은 일반적인 경우이며 게임에 따라 map 상태를 저장하는 방식이 다를 수 있다.

댓글 없음: