Wesnoth 해킹하기 (8) DLL Memory Hack
Wesnoth 해킹하기 (7) 편에서 external program을 통해 Wesnoth의 가상메모리를 read, write하였고 이를 통해 게임의 재화인 골드를 바꿀 수 있었다.
External Hacking의 한계
아무래도 external program을 통해 게임 메모리를 해킹하는 것이다보니 제약사항이 많다.
예를 들어, ReadProcessMemory API를 통해 API 공식 문서에 의해 지정된 자료형으로만 받아올 수 있다.
게임 골드처럼 간단한 값을 받아올 때는 상관없지만 복잡한 데이터나 클래스를 받아올 때는 굉장히 어려움을 겪을 수 있다.
이러한 한계점을 극복하기 위해 DLL injection이 활용된다.
그래서 Wesnoth 해킹하기 (8) 편에서는 DLL injection하는 방법을 소개한다.
DLL이란?
그 전에 DLL이 무엇이고 어떤 기능을 하는지 살펴보자.
DLL은 우선 독자적으로 실행될 수는 없다. 다른 executable에 로드되어야만 한다. 그래서 DLL을 통해 개발자는 하여금 동적으로 로드될 수 있는 라이브러리를 만들 수 있다. 이렇게 DLL로 만든 라이브러리는 다양한 executable에 로드될 수 있어 코드의 재사용성을 올릴 수 있다.
예를 들어, user32.dll은 Windows UI 요소를 조작(띄워거나, 닫는 등)하는 라이브러리다. Windows를 타겟으로 하는 거의 모든 application은 user32.dll을 이용한다.
그리고 DLL은 일반 executable과 차이점이 크게 2가지가 있다.
- DLL은 부모 프로세스 안에서 실행된다. DLL에서 선언된 변수도 부모 프로세스 메모리에 생성된다.
- DLL은 main function 대신 DllMain function에서 시작한다. DllMain 함수의 구성은 아래와 같다.
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved
);
DLL 개발 방법
이제 이 DLL을 만드는 방법을 알아보자.
1. 우선 Visual Studio에서 빈 프로젝트를 만들어주자. 프로젝트의 이름은 InternalMemoryHack이라고 지어주자.
2. 프로젝트 생성 후 Source Files에 main.cpp를 만들어주자.
이제 프로젝트 빌드 시 executable이 아니라 DLL로 빌드하기 위해서는 몇가지 설정을 건드려줘야 한다.
프로젝트를 우클릭하고 Properties에 진입하자.
3. Configuration Properties - General 탭에서 Configuration Type을 Application(.exe)에서 Dynamic Library(.dll)로 바꿔주자.
4. main.cpp에 다음과 같은 코드를 작성해보자.
#include <Windows.h>
void injected_thread() {
while (true) {
if (GetAsyncKeyState('M')) {
// *(*(0x17EECB8 + 0x60) + 0xA90)+0x4
DWORD* player_base = (DWORD*)(0x17EECB8 + 0x60);
DWORD* game_base = (DWORD*)(*player_base + 0xA90);
DWORD* gold = (DWORD*)(*game_base + 4);
*gold = 999;
}
Sleep(1);
}
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
//MessageBox(0, 0, 0, 0);
if (fdwReason == DLL_PROCESS_ATTACH) {
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)injected_thread, NULL, 0, NULL);
}
return true;
}
MessageBox는 메시지박스를 출력하는 Windows API다.
int MessageBox(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType
);
메시지박스를 띄움으로써 우리는 DLL injection이 정상적으로 이루어졌음을 알 수 있을 것이다.
5. F5를 눌러 솔루션 빌드하자.
아래 경로에 dll이 생성된다.
C:\Users\사용자명\source\repos\InternalMemoryHack\Debug\InternalMemoryHack.dll
DLL Injection
이제 DLL을 로드할 차례다.
일반적인 경우라면 LoadLibrary API를 통해 DLL을 주입한다.
하지만 우리는 Wesnoth의 소스코드를 수정할 수 없는데 2가지 대안이 있다.
- AppInit_DLLs를 이용한다.
- AppInit_DLL은 Windows의 기능 중 하나로 사용자 정의 DLL을 모든 executable에 주입하는 기능이다.
- DLL Injector를 사용한다.
- DLL Injector는 executable의 메모리 안에 thread를 생성하는 프로그램이다.
- CreateRemoteThread API를 통해 쓰레드를 생성하고 생성된 쓰레드가 LoadLibrary API를 호출하는 방식으로 DLL을 주입할 수 있다.
본 포스트에서는 AppInit_DLLs 기능을 사용하는 방법을 살펴볼 것이다.
AppInit_DLLs
AppInit_DLLs 기능을 사용하기 위해서는 BIOS 세팅에서 Secure Boot을 disable시켜주자.
다음, 레지스트리를 수정해주자.
윈도우 + R을 눌러 실행창을 띄우고 regedit을 입력하자.
그리고 다음 경로로 이동하자:
컴퓨터\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows
AppInit_DLLs 레지스트리를 수정하기 위해 더블클릭.
레지스트리 값이 빈칸으로 되어 있을텐데 InternalMemoryHack.dll의 경로를 넣어주자.
다음으로 LoadAppInit_DLLs 레지스트리도 1로 바꿔주자.
이제 Wesnoth를 실행하고 DLL이 성공적으로 로드되었는지 확인해보자.
'오류' 메시지박스가 뜬다면 성공적으로 로드된 것이다.