Saturday, 19 September 2009

Sử dụng Detours để tìm lua_State cho AutoPlay

Bài viết mô tả việc dùng Detours
Software package for re-routing Win32 APIs underneath applications. Under commercial release for over 10 years, Detours is licensed by over 100 ISVs and used within nearly every product team at Microsoft.
để thực hiện API hooking, cụ thể là ReAlloc nhằm lấy được base address của lua_State dùng cho AutoPlay. Lý do để thực hiện việc này là không thể dò lua_State bằng cách thông thường dùng CheatEngine (xem lại bài Sử dụng Cheat Engine để search base address dùng cho auto play).

Bài viết này không đi sâu chi tiết vào Detours, chỉ mô tả cách sử dụng Detours trong trường hợp ko dùng được CheatEngine. Cách thực hiện này có thể áp dụng trong các trường hợp có logic tương tự.

Do mấy bữa nay bận nên không thể ngồi dò base address của lua_State nên không sửa hoàn chỉnh cái auto Tru Tiên. Hôm nay cuối tuần mình sẽ ngồi viết tutorial cho phần này. Sau này nếu có ai muốn tìm giá trị của lua_State hay những địa chỉ trong trường hợp tương tự thì vẫn biết đường mà mò.

Build Lua library

Đầu tiên mình check phiên bản Lua mà game đang sử dụng (là 5.1.1), sau đó phải kiếm 1 bản lua về tại http://www.lua.org/download.html. Bản mới nhất hiện tại là 5.1.4, có thể dùng bản này hoặc 5.1.1. Sau khi download về và extract trong 1 folder Lua-5.1.1. Sau đó tạo một project static library Lua-5.1.1.vcproj, add các files trong thư mục src (nhớ bỏ 2 file lua.c và luac.c là 2 files build lua command line) vào project.

Mục đích là build static library Lua. Có thể tạo solution riêng để build lua library 1 lần thôi.















Sau khi build xong có lua-5.1.1d.lib và lua-5.1.1.lib, có thể copy lên thư mục Lib để dễ reference.

Cách thức cấp phát lua_State

Tạo một project đơn giản dạng Win32 application (dùng cái này khỏi dính dáng đến MFC) để biết lua_State được cấp phát như thế nào. Tạo Win32 application viết dạng Dialog

1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Test.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

// Header for Lua library
extern "C" {
#include "../Lib/Lua-5.1.1/src/lua.h"
#include "../Lib/Lua-5.1.1/src/lauxlib.h"
#include "../Lib/Lua-5.1.1/src/lualib.h"
};

//  21/Nov/2009 fixed link to Lua library
#if defined(_DEBUG)
// Debug libraries
#pragma comment(lib, "../Lib/lua-5.1.1d.lib")
#else
// Release libraries
#pragma comment(lib, "../Lib/lua-5.1.1.lib")
#endif

HINSTANCE g_hInstance = NULL;
BOOL CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPSTR lpCmdLine, int nCmdShow)
{
  // TODO: Place code here.
    g_hInstance = hInstance;
 
    HWND hDialog = 0; 
    hDialog = ::CreateDialog(hInstance, MAKEINTRESOURCE(IDD_TEST_DLG), 0, DialogProc); 
    if (!hDialog)
    {
  LPVOID lpBuffer;
  ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
   NULL, GetLastError(), 0, (LPTSTR)&lpBuffer, 0, NULL);

        ::MessageBox(NULL, (LPTSTR)lpBuffer, _T("CreateDialog"), MB_ICONEXCLAMATION | MB_OK);
  ::LocalFree(lpBuffer);

        return 1;
    } else {
  ::ShowWindow(hDialog, nCmdShow);
 }
 
    MSG  msg;
    int status;
    while ((status = ::GetMessage(&msg, 0, 0, 0)) != 0) {
        if (status == -1) {
            return -1;
  }

        if (!::IsDialogMessage(hDialog, & msg)) {
            ::TranslateMessage( & msg );
            ::DispatchMessage( & msg );
        }
    }
 
    return msg.wParam;
}

BOOL CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 WORD wId, wEvent;
 HICON hIcon = NULL;
 lua_State* state = NULL;
 TCHAR buffer[1024] = { '\0' };

    switch (message)
    {
    case WM_INITDIALOG:   
  hIcon = ::LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ICON));
  if(hIcon) {
   ::SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
  }
        return TRUE;

    case WM_COMMAND:
        wId    = LOWORD(wParam); 
  wEvent = HIWORD(wParam); 

  // Parse the menu selections:
  switch (wId)
  {
   case IDC_BUTTON_TEST:
    state = luaL_newstate();
    wsprintf(buffer, _T("[Process %p] "), ::GetCurrentProcessId(), state);
    ::MessageBox(NULL, buffer, _T("Test"), MB_OK);

    if (state != NULL) {
     lua_close(state);
    }
    
    break;

   case IDOK:
    ::DestroyWindow(hWnd);
    break;

   case IDCANCEL:
    ::DestroyWindow(hWnd);
    break;

   default:
    return ::DefWindowProc(hWnd, message, wParam, lParam);
  }
        return TRUE;

    case WM_DESTROY:
        ::PostQuitMessage(0);
        return TRUE;

    case WM_CLOSE:
        ::DestroyWindow(hWnd);
        return TRUE;
    }
    return FALSE;
}

Test project đơn giản chỉ là một dialog có một nút test.

// Header for Lua library
extern "C" {
#include "../Lib/Lua-5.1.1/src/lua.h"
#include "../Lib/Lua-5.1.1/src/lauxlib.h"
#include "../Lib/Lua-5.1.1/src/lualib.h"
};

//  21/Nov/2009 fixed link to Lua library
#if defined(_DEBUG)
// Debug libraries
#pragma comment(lib, "../Lib/lua-5.1.1d.lib")
#else
// Release libraries
#pragma comment(lib, "../Lib/lua-5.1.1.lib")
#endif

File lua-5.1.1d.lib là Lua đã build trong bước trước.














Khi thực hiện nhấn button Test sẽ tạo một lua_State, do đó để break point ngay đây xem có được thông tin gì không. Mình biết lua_State được tạo bởi luaL_newstate.

// Parse the menu selections:
switch (wId)
{
 case IDC_BUTTON_TEST:
  state = luaL_newstate();

Thực hiện F11 step into sẽ vào lua_newstate xem tiếp

1
2
3
4
5
LUALIB_API lua_State *luaL_newstate (void) {
  lua_State *L = lua_newstate(l_alloc, NULL);
  if (L) lua_atpanic(L, &panic);
  return L;
}

Tiếp tục step into lua_newstate với l_alloc
1
2
3
4
5
6
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
  int i;
  lua_State *L;
  global_State *g;
  void *l = (*f)(ud, NULL, 0, state_size(LG));
  if (l == NULL) return NULL;

Mình để ý tiếp lua_Alloc và step into lua_Alloc f (line 5), step into lua_Alloc tại bước này
1
 2
 3
 4
 5
 6
 7
 8
 9
10
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
  (void)ud;
  (void)osize;
  if (nsize == 0) {
    free(ptr);
    return NULL;
  }
  else
    return realloc(ptr, nsize);
}

Tới đây mình hầu như đã có thông tin mong muốn

  • Lua alloc sử dụng API realloc
  • Kích thước cấp phát là nzine

Có nghĩa là lua_State là giá trị trả về của realloc với size là 376. Con số này rất quan trọng mình sẽ ghi nhớ.










Hình: Kích thước cấp phát của lua_State

Tìm giá trị lua_State của game thông qua API hooking

Vấn đề tiếp theo là làm sao lấy được giá trị trả về của realloc trong game. Khi game bắt đầu khởi động thì lua_State đã được tạo và load các script, trước cả khi ra màn hình log in.

Khoảng thời gian này rất ngắn và không thể inject dll sau đó được. Nếu game build mode debug thì mình có thể dùng chính Visual Studio để open elementclient.exe với break point là realloc with condition (đó là cách làm lúc trước). Nhân đây mới nói chẳng hiểu Tru Tiên bản cũ sao nó dám đưa build debug làm bản thương mại ???.

Cách thực hiện theo suy nghĩ là lắng nghe tất cả các cấp phát có kích thước đúng bằng kích thước của lua_State mà bước trên mình đã có 376.

Mình sẽ dùng Detours một thư viện có sẵn để thực hiện system hook và thực hiện detour realloc. Trang chính của Detours là http://research.microsoft.com/en-us/projects/detours/ với version mới nhất là 2.1. Mình không biết nó có đổi gì hay có gì mới không nên dùng 1.5 bao gồm 2 file đơn giản detour.lib và detour.h (để trong thư mục Lib).



Libraries Lua và Detours

Bước 1: Chuẩn bị Hook và Installer

Tạo solution Detours có 2 project là Hook và Installer cùng cấp với thư mục Lib.

  1. Hook project là Win32 dll
  2. Installer project giống như test project ở trên ngoại trừ có 2 nút là Install và Uninstall.

Trong Hook project sẽ cần sử dụng detours.lib và detours.h.

Bước 2: Project Hook

Trong file Hook.h thực hiện include Detours, và tạo 2 function export install và uninstall.
Mình dùng file .def để export nên tạo file Hook.def và thêm vào project (có thể dùng __declspec(dllexport) cũng được)
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// -----------------------------------------------------------------------------
//  Document    :  Hook.h
//  Description :  Hook include file
//  Create date :  15/9/2009-13:40:07
//  Author      :  gponster (langxang)
// -----------------------------------------------------------------------------

#ifndef __HOOK_H_B10B0170_CAB8_4F19_8F66_830200F34112__
#define __HOOK_H_B10B0170_CAB8_4F19_8F66_830200F34112__

#include 
#include 
#include 

#if (_MSC_VER < 1310)
#else
#include 
#endif

#include "../Lib/detours.h"

#pragma warning(disable: 4099) 
#pragma comment(lib, "../Lib/detours.lib") 

#if defined(__cplusplus)
extern "C" {
#endif 
 void InstallHook();
 void UninstallHook();
#if defined(__cplusplus)
}
#endif

#endif // __HOOK_H_B10B0170_CAB8_4F19_8F66_830200F34112__

Nội dung Hook.def
1
2
3
4
5
6
7
8
; Hook.def : Declares the module parameters for the DLL.

LIBRARY "Hook"

EXPORTS
; Explicit exports can go here
InstallHook
UninstallHook

DLL này ko có gì đặc biệt, nếu bị lỗi SAFESEH thì tắt lý do là compiler mới bị lỗi khi link với lib build bằng compiler cũ (ở đây là Detours)
This happens when you link an .obj or .lib that contains code created by an earlier version of the compiler.
Tắt /SAFESEH:NO Project properties > Configuration Properties > Linker > Advanced










Mình sẽ thực hiện install và uninstall hook với ShellProc. Nếu bạn nào chưa hiểu rõ thì có thể tự tìm hiểu thêm (về API hooking và DLL Injection sẽ có thể ở một bài khác).

1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// -----------------------------------------------------------------------------
//  Document    :  Hook.cpp
//  Description :  Hook implementation
//  Create date :  15/9/2009-13:40:07
//  Author      :  gponster (langxang)
// -----------------------------------------------------------------------------

#include "Hook.h"

HHOOK g_hHook = NULL;
HANDLE g_hInst = NULL;

DETOUR_TRAMPOLINE(int WINAPI TrampolineMessageBoxA(HWND hWnd, LPCSTR lpText, 
 LPCSTR lpCaption, UINT uType), MessageBoxA);
DETOUR_TRAMPOLINE(int WINAPI TrampolineMessageBoxW(HWND hWnd, LPCWSTR lpText, 
 LPCWSTR lpCaption, UINT uType), MessageBoxW);

typedef void* (__cdecl *ReAllocFunc)(void* memptr, size_t newsize); 
ReAllocFunc g_lpTargetReAllocMSVCRT = NULL;   // Target realloc function pointer in MSVCRT
ReAllocFunc g_lpTrampolineReAllocMSVCRT = NULL;  // Trampoline realloc function pointer in MSVCRT

ReAllocFunc g_lpTargetReAllocMSVCR90D = NULL;  // Target realloc function pointer in MSVCR90D
ReAllocFunc g_lpTrampolineReAllocMSVCR90D = NULL; // Trampoline realloc function pointer in MSVCR90D

int WINAPI PatchedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
 CHAR buffer[1024] = { 0x00 };
 ::StringCbPrintfA(buffer, sizeof(buffer), "%s - patched by Detours", lpText);

 return TrampolineMessageBoxA(hWnd, buffer, lpCaption, uType);
}

int WINAPI PatchedMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) {
 WCHAR buffer[1024] = { 0x00 };
 ::StringCbPrintfW(buffer, sizeof(buffer), L"%s - patched by Detours", lpText);

 return TrampolineMessageBoxW(hWnd, buffer, lpCaption, uType);
}

#define EXPECTED_SIZE (376)
void* __cdecl PatchedReAllocMSVCRT(void* memblock, size_t newsize)
{ 
 void* result = NULL;
 result = g_lpTrampolineReAllocMSVCRT(memblock, newsize);

 if (newsize == EXPECTED_SIZE)
 {
  TCHAR buffer[1024] = { 0x00 };

  ::StringCbPrintf(buffer, sizeof(buffer), 
   _T("[Process %p] MSVCRT.realloc memptr %p size %d result %p"), 
   ::GetCurrentProcessId(), memblock, newsize, result);
  ::OutputDebugString(buffer);
 }

 return result;
}

void* __cdecl PatchedReAllocMSVCR90D(void* memblock, size_t newsize)
{ 
 void* result = NULL;
 result = g_lpTrampolineReAllocMSVCR90D(memblock, newsize);
 
 if (newsize == EXPECTED_SIZE)
 {
  TCHAR buffer[1024] = { 0x00 };

  ::StringCbPrintf(buffer, sizeof(buffer), 
   _T("[Process %p] MSVCR90D.realloc memptr %p size %d result %p"), 
   ::GetCurrentProcessId(), memblock, newsize, result);
  ::OutputDebugString(buffer);
 }

 return result;
}

void Intercept()
{
 DetourFunctionWithTrampoline((PBYTE)TrampolineMessageBoxA, (PBYTE)PatchedMessageBoxA);
 DetourFunctionWithTrampoline((PBYTE)TrampolineMessageBoxW, (PBYTE)PatchedMessageBoxW);

 g_lpTargetReAllocMSVCRT = (ReAllocFunc)DetourFindFunction("MSVCRT.DLL", "realloc");
 if(g_lpTargetReAllocMSVCRT) {
  g_lpTrampolineReAllocMSVCRT = (ReAllocFunc)DetourFunction((PBYTE)g_lpTargetReAllocMSVCRT, 
   (PBYTE)PatchedReAllocMSVCRT);
 }

 g_lpTargetReAllocMSVCR90D = (ReAllocFunc)DetourFindFunction("MSVCR90D.DLL", "realloc");
 if(g_lpTargetReAllocMSVCR90D) {
  g_lpTrampolineReAllocMSVCR90D = (ReAllocFunc)DetourFunction((PBYTE)g_lpTargetReAllocMSVCR90D, 
   (PBYTE)PatchedReAllocMSVCR90D);
 }
}

void UnIntercept()
{
 DetourRemove((PBYTE)TrampolineMessageBoxA, (PBYTE)PatchedMessageBoxA);
 DetourRemove((PBYTE)TrampolineMessageBoxW, (PBYTE)PatchedMessageBoxW);

 if(g_lpTrampolineReAllocMSVCRT) {
  DetourRemove((PBYTE)g_lpTrampolineReAllocMSVCRT, (PBYTE)PatchedReAllocMSVCRT);
 }

 if (g_lpTrampolineReAllocMSVCR90D)
 {
  DetourRemove((PBYTE)g_lpTrampolineReAllocMSVCR90D, (PBYTE)PatchedReAllocMSVCR90D);
 } 
}

BOOL APIENTRY DllMain(HANDLE hInstDLL, DWORD  dwReason, LPVOID lpReserved)
{
 g_hInst = hInstDLL;
 switch (dwReason)
 {
 case DLL_PROCESS_ATTACH:
  Intercept();
  break;

 case DLL_THREAD_ATTACH:
  break;

 case DLL_THREAD_DETACH:
  break;

 case DLL_PROCESS_DETACH:
  UnIntercept();
  break;
 }
 return TRUE;
}

LRESULT CALLBACK ShellProc(int nCode, WPARAM wParam, LPARAM lParam)
{
 return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

void InstallHook()
{
 if(g_hHook == NULL) {
  g_hHook = ::SetWindowsHookEx(WH_SHELL, ShellProc, (HINSTANCE)g_hInst, 0);
 }
}

void UninstallHook()
{
 if(::UnhookWindowsHookEx(g_hHook)) {
  g_hHook = NULL;
 }
}

Để ý hàm DllMain sẽ thực hiện Intercept khi DLL_PROCESS_ATTACH và UnIntercept khi DLL_PROCESS_DETACH.

Bước 3: làm quen với Detours

Đơn giản mình sẽ demo thử với MessageBox.
Mình patch 2 version của MessageBox là MessageBoxA và MessageBoxW.

Đầu tiên khai báo với macro của Detours DETOUR_TRAMPOLINE

DETOUR_TRAMPOLINE(int WINAPI TrampolineMessageBoxA(HWND hWnd, LPCSTR lpText, 
 LPCSTR lpCaption, UINT uType), MessageBoxA);
DETOUR_TRAMPOLINE(int WINAPI TrampolineMessageBoxW(HWND hWnd, LPCWSTR lpText, 
 LPCWSTR lpCaption, UINT uType), MessageBoxW);


Hai dòng này khai báo API sẽ bị patched cũng như prototype và calling convention của các API này.

Tạo 2 function detour sao cũng được, ví dụ thêm vào thông báo 1 dòng gì đó, để buffer là 1024 chắc cũng đủ. Lưu ý calling convention của các hàm này cũng phải tương ứng với khai báo Trampoline

int WINAPI PatchedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
 CHAR buffer[1024] = { 0x00 };
 ::StringCbPrintfA(buffer, sizeof(buffer), "%s - patched by Detours", lpText);

 return TrampolineMessageBoxA(hWnd, buffer, lpCaption, uType);
}

int WINAPI PatchedMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) {
 WCHAR buffer[1024] = { 0x00 };
 ::StringCbPrintfW(buffer, sizeof(buffer), L"%s - patched by Detours", lpText);

 return TrampolineMessageBoxW(hWnd, buffer, lpCaption, uType);
}

Ngoài ra trong Intercept và UnIntercept sẽ thực hiện detour hay remove (Line 76, 94).

Trong source code cũng có sẵn cài đặt detour cho realloc (xem phần sau).
Hai export function là InstallHook và UninstallHook dùng ShellProc.
Sau bước này mình đã có Hook.dll với 2 export function là Install và Uninstall

Bước 4: tạo installer để cài đặt hook vào hệ thống. 

Để inject Hook.dll vào hệ thống cần tạo Installer project và dùng lại test project sửa dialog tạo 2 button install và uninstall.
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Installer.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
HINSTANCE g_hInstance = NULL;
BOOL CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

typedef void (*InstallHookFunc)();
typedef void (*UninstallHookFunc)();

InstallHookFunc g_lpInstallHook = NULL;
UninstallHookFunc g_lpUninstallHook = NULL;

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPSTR lpCmdLine, int nCmdShow)
{
  // TODO: Place code here.
  ...
}

BOOL CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 WORD wId, wEvent;
 HMODULE hModule = NULL;
 HICON hIcon = NULL;

    switch (message)
    {
    case WM_INITDIALOG:  
  ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_UNINSTALL), FALSE);

  hModule = ::LoadLibraryA("Hook.dll");   
  if(hModule) {
   g_lpInstallHook = (InstallHookFunc)::GetProcAddress(hModule, "InstallHook");
   g_lpUninstallHook = (UninstallHookFunc)::GetProcAddress(hModule, "UninstallHook");    
  }
  
  hIcon = ::LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ICON));
  if(hIcon) {
   ::SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
  }

        return TRUE;

    case WM_COMMAND:
        wId    = LOWORD(wParam); 
  wEvent = HIWORD(wParam); 
  // Parse the menu selections:
  switch (wId)
  {
   case IDC_BUTTON_INSTALL:
       if(g_lpInstallHook) {
     g_lpInstallHook();
    }

    ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_INSTALL), FALSE);
    ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_UNINSTALL), TRUE);
    break;

   case IDC_BUTTON_UNINSTALL:
       if(g_lpUninstallHook) {
     g_lpUninstallHook();
     g_lpUninstallHook = NULL;
    }
    
    ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_INSTALL), TRUE);
    ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_UNINSTALL), FALSE);
    break;

   case IDOK:
    if(g_lpUninstallHook) {
     g_lpUninstallHook();
     g_lpUninstallHook = NULL;
    }
    
    ::DestroyWindow(hWnd);
    break;

   case IDCANCEL:
    if(g_lpUninstallHook) {
     g_lpUninstallHook();
     g_lpUninstallHook = NULL;
    }
    
    ::DestroyWindow(hWnd);
    break;

   default:
    return ::DefWindowProc(hWnd, message, wParam, lParam);
  }
        return TRUE;

 case WM_DESTROY:
  if (g_lpUninstallHook) {
   g_lpUninstallHook();
   g_lpUninstallHook = NULL;
  }

        ::PostQuitMessage(0);
        return TRUE;

    case WM_CLOSE:
  if(g_lpUninstallHook) {
   g_lpUninstallHook();
   g_lpUninstallHook = NULL;
  }

        ::DestroyWindow(hWnd);
        return TRUE;
    }

    return FALSE;
}

Mình khai báo prototype 2 function Install và Uninstall và 2 global function và thực hiện load từ Hook.dll, thực hiện test một cách đơn giản thôi.
typedef void (*InstallHookFunc)();
typedef void (*UninstallHookFunc)();

InstallHookFunc g_lpInstallHook = NULL;
UninstallHookFunc g_lpUninstallHook = NULL;

Thực hiện load Hook.dll ngay trong WM_INITDIALOG và lấy địa chỉ hàm luôn cho tiện. Tới đây mọi việc xong xuôi, để test mình chạy installer và install hook. 

Tới đây mình lấy cái Test.exe cũ ra xài, hoặc xài bất kỳ ứng dụng nào có MessageBox
Nếu thấy nội dung của MessageBox có thay đổi là OK.










OK vậy là đã hook API MessageBox rồi.

Bước 5: patch function realloc. realloc là function của CRT. 

CRT có version đi với OS và version của Visual Studio (để VS thực hiện debug).

API realloc trong game sẽ xài CRT ship với OS, mình có thể kiểm tra bằng cách nào cũng được, dùng Command Prompt trong Visual Studio Tools rồi xuất ra file imports.txt trong C chẳng hạn, gõ
dumpbin /imports elementclient.exe > C:\imports.txt

Trong danh sách sẽ thấy realloc trong MSVCRT.DLL.

Trong khi đó code thì mình sẽ dùng MSVCR9D.DLL của Visual Studio 2008 (bản đang dùng). Mình sẽ thực hiện patch 2 version này và test thử với Test.exe cũng tạo lua_State.

Cách làm hơi khác chút xíu, đầu tiên khai báo prototype của realloc.
typedef void* (__cdecl *ReAllocFunc)(void* memptr, size_t newsize);

Khai báo một cặp target và trampoline function
ReAllocFunc g_lpTargetReAllocMSVCRT = NULL;   // Target realloc function pointer in MSVCRT
ReAllocFunc g_lpTrampolineReAllocMSVCRT = NULL;  // Trampoline realloc function pointer in MSVCRT

ReAllocFunc g_lpTargetReAllocMSVCR90D = NULL;  // Target realloc function pointer in MSVCR90D
ReAllocFunc g_lpTrampolineReAllocMSVCR90D = NULL; // Trampoline realloc function pointer in MSVCR90D

Thực hiện tạo function detour đơn giản gọi function trampoline và log lại thông tin trả về vào debug output nếu size của realloc là 376 mà mình đã tìm ở trên.

Thêm trong Intercept và UnIntercept, mình dùng DetourFindFunction để tìm realloc, nếu tìm thấy thì thực hiện detour bằng DetourFunction
g_lpTargetReAllocMSVCRT = (ReAllocFunc)DetourFindFunction("MSVCRT.DLL", "realloc");
if(g_lpTargetReAllocMSVCRT) {
 g_lpTrampolineReAllocMSVCRT = (ReAllocFunc)DetourFunction((PBYTE)g_lpTargetReAllocMSVCRT, 
  (PBYTE)PatchedReAllocMSVCRT);
}

g_lpTargetReAllocMSVCR90D = (ReAllocFunc)DetourFindFunction("MSVCR90D.DLL", "realloc");
if(g_lpTargetReAllocMSVCR90D) {
 g_lpTrampolineReAllocMSVCR90D = (ReAllocFunc)DetourFunction((PBYTE)g_lpTargetReAllocMSVCR90D, 
  (PBYTE)PatchedReAllocMSVCR90D);
}

Trong các hàm này dùng OutputDebugString để output thông tin. Để coi được thông tin xuất bởi OutputDebugString mình dùng Dbgview có thể download tại http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx.

Đến đây chạy thử với Test, nhấn 2 lần button mình có 2 dòng debug output kết quả của patched MSVCR9D.realloc.

Tương tự vậy bật game mình cũng sẽ có được 2 kết quả tạo lua_State. Tuy nhiên không giống như test app của mình có thể nhấn nút test bất cứ lúc nào. Lua state của game chỉ thực hiện init ngay khi chạy game. Thế nên installer bắt buộc phải chạy trước tiên, thực hiện install xong xuôi rồi chúng ta mới bật game.

Chẳng hiểu sao thằng Tru Tiên này cũng debug ra đây lỗi từa lưa :D. Như vậy theo kết quả của debug viewer là mình tìm được lua_State rồi, tất nhiên đây là giá trị của lua state tại lần bật game này thôi. Nó là giá trị cấp phát động nên giá trị này chưa có ý nghĩa gì nhiều. Đến đây thì còn 1 bước nữa là tìm base address của 2 giá trị cấp phát này. Mình sẽ không tắt game client mà sẽ dùng Cheat Engine để tìm giá trị base address giống như bài trước, lưu ý giá trị hexa.


Như vậy là xong cách tìm lua_State.
Chúc mọi người cuối tuần vui vẻ

No comments:

Post a Comment