sábado, 18 de octubre de 2008

Game Speed Hacking의 원리 그리고 Codes

Game Speed Hacking의 원리 그리고 Codes





Sammuel Dual [Dual5651@hotmail.com]

Homepage : http://dualpage.muz.ro



This article assumes you're familiar with WinNT
Level of Difficulty 1 2 3
Download the code for this article:GSH.zip


이번 글에서는 Game Speed Hacking에 대하여 다루어 봅니다.




1962년 3월, 미국의 MIT 공과대학 학생들이 만든 스페이스워(Spacewar)가

만들어 진지도 약 40년이 흘렀고, 그 세월동안 게임은 큰 발전을 거듭해 왔습니다.

사람들은 어떠한 일에서 다른 이보다 Advantage를 가지고 싶어합니다.

이러한 Advantage에 대한 욕망은 게임이 발달해온 40년간의 세월동안

Game Hacking을 Game의 발전속도에 맞추어 발전하게 해왔습니다.

약 1995년 이전까지만 하여도 Online게임이기 보다는 Single게임이

주류를 이뤘습니다. 이떄가지만 해도 Game Hacking은 단지 게임을

빨리 클리어 하고 싶다던지,변칙적인 게임을 즐겨 보고 싶은 욕구를

채우기 위한 수단이었습니다. 그러나 1990년대 후반부터 등장한

Online게임은 그동안 혼자서 즐기던, 혹은 친구 한명쯤과 같이 즐길수 있던

게임에서 벗어나 모르는 여러 사람들끼리도 게임을 즐길수 있게 되었고,

이는 게임의 중독성및 경쟁의식을 강하게 해주었습니다.

더 이상 게임은 혼자 엔딩을 보는것이 아니게 되었고,

다른 사람과의 경쟁이 되었습니다. 공평한 조건속에서의 게임보다,

Advantage를 가지고 있는 게임이 승산이 큰것은 당연한 일이었고,

사람들은 Online게임에 Game Hacking을 사용하기 시작했습니다.

Single게임에는 별로 큰 의미를 가지지 않던 Game Hacking기법 한개가

Online게임에는 커다란 의미를 가지게 되었으니,

그 기술이 바로 Game Speed Hacking이었습니다.

게임의 장르를 무시하고 Game Speed Hacking은 커다란 영향을 주었습니다.

때로는 다른 플레이어 보다 빠르게 움직이거나 공격함으로써,

또 때로는 다른 플레이어 보다 느리게 움직이거나 느리게 공격할수 있게 됨으로써,

엄청난 Advantage를 가지게 된 것입니다.

Game Speed Hacking은 직접적인 메모리 변수의 값을 바꾸거나,

Operation Code(기계어)를 조작하는 것이 아님으로,

다른 Game Hacking기법들 보다 방어하기가 까다롭습니다.

그 점이 Online게임이 등장한지도 약 10년이 지난 지금도

Game Developer들이 Game Speed Hacking하면 치를 떠는 이유인거 같습니다.

Game Speed Hacking?



Game Speed Hacking이란 대체 무엇일까요?

정의에 대해서 확실히 집고 넘어갈 필요성이 있다고 봅니다.

Game Speed Hacking이란 게임에서 다른 플레이어 보다 Advantage를

가지기 위해 속도를 빠르게 혹은 느리게 고의적으로 조작하는 기법 입니다.

그런데, 대체 게임의 속도를 어떻게 조작하는 걸까요?

크게 두가지 방법이 있습니다.

첫쨰로 시스템을 전체적으로 속도를 조작 버리는 기법이 있고,

둘쨰로 각 프로세스 개별적으로 속도를 조작할수 있는 기법이 있습니다.

첫쨰 방법은 시스템 전체가 가속되거나 감속됨으로 하드웨어에 안좋은

영향을 줄수도 있는 방법임으로 이 글에서는 다루지 않습니다.

두번쨰 방법으로 프로세스 개별적 속도 조작법이 있습니다.

이 기법은 원하는 특정 프로세스의 속도만을 개별적으로 조작해 줌으로써,

보다 하드웨어에 미치는 영향이 보다 적게 가는 기법 입니다.

본글에서는 이 기법에 대해서 다루어 보도록 하겠습니다.


Game Developer들은 게임에서 일정한 Delay Time을 주어야 할떄가 많습니다.

예를들면 공격 주기 라던지, 이동속도 라던지, 에너지 회복 시간 같은것을

하기 위해서 말입니다. Delay Time은 신뢰할수 있는 시간 이어야 함으로,

Local Time을 사용하기 보다는,System Time을 사용하는것이 일반적입니다.

이 비교적 신뢰할수 있는 시간을 얻기 위해서 Game Developer들은 아래의

3가지 API를 많이 사용 합니다.


DWORD GetTickCount(void);

DWORD timeGetTime(void);

BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount );


(DXUtil_Timer()는 내부적으로 QueryPerformanceCounter사용)

(위의 API들 외에 함수들을 더 알고 싶다면 여기를 클릭)


Game Developer들은 Delay Time을 계산할떄

위의 API들을 다음과 같이 코드에 응용하여 사용 합니다.


GetTickCount():




timeGetTime():




QueryPerformanceCounter():






보는 바와 같이 각각 사용하는 API는 다르지만,

전 시각 - 후시각의 차로 Delay Time을 만들어 내는걸 볼수 있습니다.

그렇다면 이 차를 빠르게 좁힘으로써 시간을 빠르게 흐른것 처럼

게임을 속여 줄수 있다는 말이 됩니다.

어떻게 하면 차를 빠르게 좁힐수 있을까요?

그건 누구나 쉽게 시간을 빠르게 흐르게 하면 되지요.

라고 말하겠지만 어떻게 빠르게 흐르게 할거냐는 질문엔 침묵을 금치 못합니다.

대답은 바로 API Hooking을 이용하라 입니다.

우리는 위의 API들이 각각 어딘가에 시간을 저장해두고,

우리가 해당 API를 호출할떄 이를 반환해줄것임을 예측해 볼수 있습니다.

즉 다음과 같은 구조가 될 것 입니다.



API Hooking을 이용하면 API 호출의 흐름으 도중에 가로채어 조작해줄수 있습니다.

즉 다음과 같은 구조로 조작하여 주면 될것입니다.



말로 표현하면, 시간 API가 호출될떄,

기존의 API의 코드가 실행 되게 하지 말고,

Hooking하여서, My Routine으로 넘어오게 하고,

My Routine은 가짜 변수에서 시간을 꺼내어서 반환해주면 됩니다.

물런 My Routine속에 있는 가짜 시간용 변수는 마치

진짜 시스템에 숨겨져 있는 '그' 변수 처럼

시간을 증가 시키면서 존재하고 있으되,

원하는 속도 만큼 지속적으로 빠르게 증가 시키면서 존재하고 있어야 합니다.

이제 이론적인 부분은 다 끝났으니 실제 Code를 작성하여 봅시다.



Fucking †he C0de



이번글의 내용을 진행한 컴퓨터의 사양은 아래와 같습니다.



CPU : Intel(R) Pentium(4) 4 CPU 1500MHz

RAM : 523,248KB

OS : Microsoft Windows XP

Tools : Microsoft Windows XP Driver Development Kit

Microsoft Visual Studio 2003

*-본글에서 등장하는 Code는 NT Platform을 위해 작성 되었습니다.-*


먼저 API Hooking기법중에 사용할 기법은 Dll Injection입니다.

int 3(0xCC)삽입 기법보다는 속도면이나, 시스템의 안정성에서 보다 좋기 떄문입니다.


Dll Injection을 하기위해 대상 Process의 핸들을 구해오는게 급선무 일것입니다.

필자는 대상의 Process를 구해오기위해 다음과 같은 Code를 작성했습니다.



위의 Code가 하는 기능은 ZwQuerySystemInformation()이라는

Native API를 이용하여, Process 목록을 구해 각각의 Pid를 배열에 저장해 두고,

프로세스 이름은 함수의 인자로 넘어온 hListBox안에 있는 핸들값을 갖는

리스트 박스에 문자열을 추가 시켜 주는 기능입니다.

특정 프로세스를 대상 프로세스 하고 싶다면 아래와 같이 수정하면 됩니다.




이제 대상의 Pid를 구했으니 Access Handle을 얻을 필요가 있습니다.

Access Handle을 구하는 부분은 아래와 같이 작성 하였습니다.



ZwOpenProcess()라는 Native API를 사용하여 Access Handle을 구해 온후,

현재 프로세스가 실행중인 경로를 구해 Directory라는 변수에 넣고,

DLL의 이름을 그 뒤에 붙여 넣은후 현재 경로 와 DLL이름이 합쳐진

이 문자열을 InjectDll()이라는 함수로 넘긴다.


AccessHandle도 구했으니 Dll Injection을 하기위해 다음과 같은 Code를 작성했습니다.



대상 프로세스에 현재경로 와 DLL이름을 합친 문자열의 길이만큼,

메모리를 할당한 후 그곳에 현재 경로 와 DLL이름을 합친 문자열을

적어 넣습니다. 그리고 원하는 프로세스에 Thread를 삽입할수 있는

API인 CreateRemoteThread()를 이용하여 함수의 주소로 LoadLibraryA()의

주소를, 함수 인자로 대상 프로세스에 현재경로와 DLL이름을 합친

문자열을 써넣은 주소를 주었습니다. 이렇게 하면, 대상 프로세스에 있는

LoadLibraryA()가 우리의 HackDll.dll을 대상 프로세스의 메모리에 로드 하여 줍니다.


이제 대상 프로세스에 Dll을 삽입 하는 것으로 이 프로세스의 임무는 끝났습니다.

지금 부터는 Dll의 임무가 시작됩니다.

삽입된 Dll은 이제 대상 프로세스의 메모리 위에 있는 것입니다.

즉 대상 프로세스 안에 얼마던지 우리가 원하는 Code를 널수 있다는 말이 됩니다.

먼저 DllMain은 아래와 같이 작성했습니다.




Dll이 대상 프로세스에 Attach될떄,

Start라는 함수로 부터 시작하는 새로운 Thread를 만듭니다.

이 Thread는 Hook을 설치하는 역할과 '가짜 시간 변수'의 시간을

지속적으로 업데이트 하여 주는 역활을 합니다.

'가짜 시간 변수'의 시간은 정확한 주기로 증가해 주어야 함으로,

SetThreadPriority()라는 API를 이용해 Thread의 Priority를 최상위로 맞춰 주었습니다.

두번쨰 Thread인 GetTime은 공유 메모리에서 지속적으로 가속도를 구해오는 역활을 합니다.

Dll이 Detach될떄는 Thread의 핸들들을 놓아주고, 공유메모리도 놓아주고, Hook도 해제합니다.


이제 중심부의 시작인 Start의 Code를 살펴 봅시다.



먼저 조종 프로세스와의 공유메모리를 만듭니다.

이 공유 메모리는 가속도를 조종 프로세스 -> Dll에 전달하기 위해 사용 됩니다.

현재 시간을 GetTickCount()로 구해 MyTick이라는 변수에 저장하여 둡니다.

그 다음 시간 API들에 Hook을 설치 하고, 이제 부터 '가짜 시간 변수'의 시간을

3ms마다 증가 시키어 줍니다. tCount에는 조종 프로세스로 부터 전달받은 가속도 값이

들어있고, Sleep(3)을 Sleep(몇)으로 바꿔 주느냐에 따라서도 속도에 차이가 나게 됩니다.


조종 프로세스와의 공유 메모리를 설치하는 Code는 아래와 같이 작성 하였습니다.



CreateFileMapping()함수로 공유 메모리를 만들떄, 주의할 점은 첫 인자로 핸들이 아닌,

INVALID_HANDLE_VALUE를 주어야 공유 메모리로써 쓸수 있다는 점과, CreateFileMapping()

의 마지막 인자로 공유 메모리의 이름을 주어야 한다는 겁니다. 본 글에서는 "DUALMEM"입니다.

그후에 공유 메모리의 가속도 값과 현재 Dll에 있는 가속도 변수의 값을 1로 초기화 시킵니다.


공유 메모리도 설치 하였으니,시간 API에 Hook을 설치하기 위해

아래와 같이 Code를 작성 하였습니다.



각 시간 API의 주소를 GetProcAddress()로 구해온후 그 구해온 주소의 메모리 영역은

Code Segment임으로 READONLY로 되있을 가능성이 큼으로 VirtualProtect()를 이용해

메모리 보호권한을 WRITE도 가능하게 변경하여 줍니다.

JmpByte라는 배열의 [1][0]에 장거리 점프 명령어의 Opreation Code인 0xE9를 넣어 줍니다.

JmpByte라는 배열의 [1][1]에 MytimeGetTime()의 주소 - timeGetTime()의 주소 - 5를

저장합니다. MyTimeGetTime()은 timeGetTime()이 대상 프로세스에서

호출될때 불려질 가짜 timeGetTime()입니다.

기존의 timeGetTime()의 처음 부터 5Bytes만큼 Bytebackup이라는 변수에 백업해두고,

그 자리에는 JmpByte배열의 값들을 써넣습니다.

위에처럼 다른 함수들에도 반복해줌으로써 Hook설치를 하여 주었습니다.


여기까지 핵심부인 Start()안에 있는 Code들을 살펴 보았습니다.

Hook설치도 했으니 Dll이 해제 될때 설치한 Hook을 해제 하는 Code는 다음과 같이 작성했습니다.



시간 API의 처음부터 5bytes를 백업해뒀던 Bytes로 복원하고,

메모리 보호 권한을 원래대로 되돌리는 작업을 하여 줍니다.


이제 설치,해제하는 것을 Code를 전부 보았음으로,

설치된후 대상 프로세스에서 시간 API가 호출 될떄 대신 호출될

'가짜 시간 함수들'의 Code는 다음과 같습니다.



간단하게 MyGetTickCount()와 MyTimeGetTime()은 MyTick의 값을 반환해 주고 있고,

MyQueryPerformanceCounter()는 *lpPerformanceCount에 MyPtick의 값을 넣어주고,

무조건 성공을 반환하는 Code로 작성되어 있는 것을 볼수 있습니다.


추가적으로 단축키를 설정하는 법에 대해서 간단히 Code를 작성해 보았습니다.



적당한 부분에서 CreateThread()로 새 Thread를 만들어 줍니다.

while을 돌며 GetAsyncKeyState()함수로 현재 눌린 키가

+이면 가속도를 증가 시켜주고, -이면 가속도를 감소 시켜 주는 기능입니다.


이로써 Game Speed Hacking에 필요한 모든 요소 & Code를 살펴 보았습니다.

잘못된 점이 있으면 메일로 알려 주세요.



For related articles see:

Sysinternals Freeware - Inside the Native API

Windows NT System Calls

Undocumented Windows NT

Programming the Microsoft Windows Driver Model

Win2K/XP SDT Restore 0.2 (Proof-Of-Concept)

No hay comentarios: