난 정말 최고야 멋있어
[게임 해킹] 스피드핵 만들기 본문
스피드핵을 만들기 위해서는 우선 스피드핵의 원리를 짚고 넘어갈 필요가 있다
원리를 초보도 이해할수 있도록 위에 그림을 그려보았다
이 정도면 아무래도 모두가 한눈에 쏙 이해하지 않을까 싶다
우리의 대상 게임은 추억의 게임인 레바의 모험!!! 공식홈페이지에서 받아주자
시간 관련함수로는 크게 GetTickCount와 QueryPerformanceCounter , timeGetTime 등등이 있는데..
(설마 rdtsc를 사용하진 않을꺼라고 본다 ㅋㅋ)
브포를 걸고 확인해 보면 보다 싶이... GetTickCount 류에는 아무런 반응도 없지만 QueryPerformanceCounter와 timeGetTime에는 반응을 아주 잘하는것을 볼 수 있다.
일단 샘플로 작동원리를 확인하기 위해서 코드를 간단히 짜보았다..
후킹따위는 없지만, 자체적으로 후킹을 시뮬레이션한다
#include <iostream>
#include <Windows.h>
using namespace std;
int main()
{
float delta = 0.0001;
LARGE_INTEGER li;
static LARGE_INTEGER hook;
while (true)
{
system("cls");
QueryPerformanceCounter(&li);
if (hook.QuadPart == 0)
hook.QuadPart = li.QuadPart;
li.QuadPart = hook.QuadPart + (LONGLONG)(delta * (li.QuadPart - hook.QuadPart));
cout << li.QuadPart << endl;
}
return 0;
}
delta 변수를 바꿔줌에 따라서 속도가 오르는 속도가 확실히 차이 난다는것을 알 수 있다.
그럼 이제 본격적으로 DLL코드를 작성해보자
일단 detours를 인클루드 해주고..
원본 함수 주소를 GetProcAddress 함수를 이용해서 얻어와 전역변수로 저장을 했다
보통 typedef 함수포인터로 따로 만들고 그러던데 나는 귀찮아서 그냥 함수포인터만 썼다
auto _qfc = (BOOL (WINAPI*)(LARGE_INTEGER*))GetProcAddress(GetModuleHandle(L"kernel32.dll"), "QueryPerformanceCounter");
auto _tgt = (DWORD(WINAPI*)())GetProcAddress(GetModuleHandle(L"kernel32.dll"), "timeGetTime");
QueryPerformance와 timeGetTime 모두를 가져온건 절대로 처음에 QueryPerformanceCounter만 후킹했을때 스피드핵이 정상적으로 작동하지 않아서가 아니다
또 전역변수로 스피드핵 비율과, 예비 주황이들을 선언 해주었다
float delta = 10.0f;
static LARGE_INTEGER saved_qfc;
static DWORD saved_tgt;
이제 DllMain을 손대보자
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)&_qfc, qfchook);
DetourAttach((PVOID*)&_tgt, tgthook);
DetourTransactionCommit();
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
DLL_PROCESS_DETACH 에는 귀찮아서 아무것도 안썼다
tgthook 과 qfchook은 후킹할 함수다
BOOL WINAPI qfchook(LARGE_INTEGER* time)
{
if (saved_qfc.QuadPart == 0)
_qfc(&saved_qfc);
BOOL result = _qfc(time);
time->QuadPart = saved_qfc.QuadPart + delta * (time->QuadPart - saved_qfc.QuadPart);
return result;
}
DWORD WINAPI tgthook()
{
if (saved_tgt == 0)
saved_tgt = _tgt();
return saved_tgt + delta * (_tgt() - saved_tgt);
}
처음의 후킹 시뮬레이션 코드랑 비슷하게 짜보았다
내가 자주하는 실수인데 함수 호출 규약을 꼭 맞춰줘야 한다 안맞춰주면 무수한 에러의 요청이??
x86의 경우 WINAPI 는 웬만해선 __stdcall 이고 일반적인 함수는 __cdecl 인경우도 많다
대충 ret으로 끝나면 cdecl , ret 04 이런식으로 끝나면 stdcall (그래도 혹시 모르니 스택 정리를 누가 하는지 보는게 제일 확실하긴 하다)
음.. 이제 시연해보자
마지막에 안타깝게 죽었긴 한데 간단한 스피드핵 구현이 된 것을 알 수 있다