自制 war3 map hack
http://blog.csdn.net/aj3423/article/details/6670529
0. 工具
OD(ollydbg), CE (cheat engine),war3 1.24, win7
1. 目標功能
去除戰爭迷霧,只是走過的地方永久可見。
3. 猜測
如果你來寫 war3,你是否會用一個 bool 變量比如 hasFog, 界面每次重畫時候都判斷該變量,是 true 就加迷霧,false 就去迷霧。至于你會不會這樣寫,我反正會這樣寫。
4. 用 CE 找當前游戲 hasFog 內存地址
用 OD 加載 war3.exe(加參數-window),建一單機游戲,此時是有迷霧的(hasFog == true)。
操作:用CE打開war3.exe,搜索內存為1的值(true==1),列出無數個地址。然后輸入全圖指令 iseedeadpeople,此時 hasFog == false,到CE中,在上次搜索結果里繼續搜索0的值。
重復上述操作步驟,直到剩下少數幾個地址
范圍被縮小到了7個地址,一個個試。先雙擊第一個,CE下方會出現該地址,雙擊value那一列,修改為0,war3迷霧還在,說明不是這個地址。
重復上述步驟, 直到 這個地址
0A8D009C 就是 hasFog,到此 CE 的工作結束
寫段代碼直接改這個地址為 0 就ok了?no
上面找到的 hasFog 地址,在每次建游戲時候都會變(從CE中也可以看出,靜態地址是綠色,黑色的是動態地址)
5. OD 分析
如何動態找這個 hasFog 地址?war3 是怎么找的我們也怎么找,如果在 hasFog 處加寫斷點,然后打全屏指令,war3 就會找到該地址,并且寫入值。
ok,換到 OD,在 0A8D009C 處加內存寫入斷點,然后到 war3 輸入 iseedeadpeople, 斷在 6F408906
(ctrl+A 分析該函數可以看到整個函數范圍)
分析: esi 可能是指向這樣一個struct
[cpp] view plaincopy
struct MapInfo { BYTE unknown[0x14]; bool hasFog; .... }
看這個esi是怎么來的,往上幾行可以看到
注釋1:war3是vc編譯的,vc處理類成員函數時候就是把 this 指針放到 ecx 中,然后 call 函數,比如
[cpp] view plaincopy
class A { void hello() {} }; A a; a.hello(); // 這句的匯編代碼為
[cpp] view plaincopy
mov ecx, a call hello
猜測: 全局變量 [6FACD44C] 里放的是 GAME 指針,包含各種游戲信息,
GAME + 0x34 處,放的是 MapInfo 指針,里面放的map信息,包括 hasFog, 大致是
[cpp] view plaincopy
struct GAME { BYTE unknown[0x34]; MapInfo* map_info; ... }
在 od 中,按工具條的 M 按鈕,查看內存鏡像
因為 dll 每次加載到內存的位置可能不同,所以 dll 的內存地址采用 基址+偏移 組成,基址可能會變,但偏移不變。
對于全局變量 6FACD44C,是由基址 6F000000 + ACD44C組成,
整個過程就是
1. 找 game.dll 基址
2. 用 基址+ACD44C 找全局變量 GAME 指針
3. 從GAME 的 0x34 偏移處找到 MapInfo
4. 修改 MapInfo 0x14 偏移處的 hasFog
以下是代碼:
[cpp] view plaincopy
// main.cpp #include <windows.h> #include <iostream> using namespace std; #define W3_CLASS "Warcraft III" #include "mem_manip.h" typedef struct process_info_ { HWND hwnd; DWORD pid; HANDLE hProcess; } process_info; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { process_info ps; ps.hwnd = ::FindWindow(W3_CLASS, NULL); if(!ps.hwnd) { throw("no war3 found"); } ::GetWindowThreadProcessId(ps.hwnd, &ps.pid); EnableDebugPrivilege(); ps.hProcess = ::OpenProcess(0x1FFFFF, FALSE, ps.pid); //0x1FFFFF from dll_injector if (ps.hProcess == NULL) { throw("OpenProcess error"); } DWORD GAME_DLL_BASE = GetBaseAddress(ps.pid, "game.dll"); cout << "base of game.dll: " << hex << GAME_DLL_BASE << dec << endl; DWORD GAME = GAME_DLL_BASE + 0x00ACD44C; DWORD addr; cout << "try to read: " << hex << GAME << dec << endl; ReadProcessBYTES(ps.hProcess, GAME, &addr, 4); cout << "1. addr = " << hex << addr << dec << endl; addr += 0x34; ReadProcessBYTES(ps.hProcess, addr, &addr, 4); cout << "2. addr = " << hex << addr << dec << endl; addr += 0x14; DWORD zero = 0; WriteProcessBYTES(ps.hProcess, addr, &zero, 4); cout << "done!" << endl; return 0; }
[cpp] view plaincopy
// mem_manip.h #include <windows.h> #include <stdio.h> #include <tlhelp32.h> void EnableDebugPrivilege() { HANDLE hToken; LUID seDebugNameValue; TOKEN_PRIVILEGES tkp; if ( !OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return; if ( !LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &seDebugNameValue) ) { CloseHandle( hToken ); return; } tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = seDebugNameValue; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if ( !AdjustTokenPrivileges( hToken, false, &tkp, sizeof(tkp), NULL, NULL)) { CloseHandle( hToken ); return; } } void WriteProcessBYTES(HANDLE hProcess, DWORD lpAddress, void* buf, int len) { DWORD oldprot,dummy = 0; VirtualProtectEx(hProcess, (void*) lpAddress, len, PAGE_READWRITE, &oldprot); WriteProcessMemory(hProcess, (void*) lpAddress, buf, len, 0); ::FlushInstructionCache(hProcess, (void*)lpAddress, len); VirtualProtectEx(hProcess, (void*) lpAddress, len, oldprot, &dummy); } void ReadProcessBYTES(HANDLE hProcess, DWORD lpAddress, void* buf, int len) { DWORD oldprot, dummy = 0; VirtualProtectEx(hProcess, (void*) lpAddress, len, PAGE_READWRITE, &oldprot); ReadProcessMemory(hProcess, (void*) lpAddress, buf, len, 0); VirtualProtectEx(hProcess, (void*) lpAddress, len, oldprot, &dummy); } DWORD GetBaseAddress(DWORD pid, const char* ModuleName) { // Typedefs for toolhelp32 typedef BOOL (WINAPI *fnModule32First)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); typedef BOOL (WINAPI *fnModule32Next)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); typedef HANDLE (WINAPI *fnCreateToolhelp32Snapshot)(DWORD dwFlags, DWORD th32ProcessID); // Find out if the toolhelp API exists in kernel32 HMODULE k32=GetModuleHandle("kernel32.dll"); if (!k32) { throw("Unable to get handle of KERNEL32.DLL."); } fnModule32First Module32First = (fnModule32First)GetProcAddress(k32, "Module32First"); fnModule32Next Module32Next = (fnModule32Next)GetProcAddress(k32, "Module32Next"); fnCreateToolhelp32Snapshot CreateToolhelp32Snapshot=(fnCreateToolhelp32Snapshot)GetProcAddress(k32,"CreateToolhelp32Snapshot"); // Verify that the ToolHelp32 API is available if (!(Module32First) || !(Module32Next) || !(CreateToolhelp32Snapshot)) { throw("Your operating system does not support the TOOLHELP32 API.\nCheck back for updates that use PSAPI instead."); } else { // toolhelp code HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); if ((int)hSnapshot == -1) { throw("Can't create toolhelp32 snapshot of game."); } MODULEENTRY32 lpme; lpme.dwSize=sizeof(MODULEENTRY32); // Get first module, this is needed for win9x/ME if (!Module32First(hSnapshot, &lpme)) { CloseHandle(hSnapshot); throw("Can't get first module of game."); }; // Loop through all other modules while (TRUE) { if (!stricmp(lpme.szModule, ModuleName)) { CloseHandle(hSnapshot); return (DWORD)lpme.modBaseAddr; } if (!Module32Next(hSnapshot, &lpme)) { CloseHandle(hSnapshot); return 0; }; } return 0; } }
[javascript] view plaincopy
<pre name="code" class="cpp"><pre name="code" class="javascript"><pre name="code" class="php"></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> </pre></pre>
總結
以上是生活随笔為你收集整理的自制 war3 map hack的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 尼泊尔人口(人口不足三千万,不惧印度,或
- 下一篇: Figma 学习笔记 – Compone