這篇細說會花很久時間,這裡不打算針對架構做任何說明,以放碼做 note 為主。不保證它是最有效的搜尋方式。預設是搜尋目標為 "int" 型別。

 

受測程式

 

建立之受測程式功能為,可以選擇「輸入、顯示」變數 a 之內容。

 

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a = 47;
    int Sel=0;


    while(Sel != 3){
        printf("\nAddress of A = %p\n", &a);
        puts("1. Display A context.");
        puts("2. Change A context.");
        puts("3. Exit.");

        printf("\nPlease input your sel : ");
        if( scanf("%d", &Sel)!=1) {
            puts("Input Error, Ending..");
        }

        switch(Sel){
            case 1:
                printf(">> A = %d\n", a);
                break;
            case 2:
                printf(">> Input New A = ");
                scanf("%d", &a);
                break;
            case 3:
                puts("Goodbye !!");
                break;
            default:
                puts("Input Error !!");
        }
    }
    system("pause");
    return 0;
}

 

生成為 "game2.exe",完整路徑為 "D:\Setting\win7_desktop2\game2.exe"

 

搜尋片段

 

主要先抓 game2.exe 不同之 a-value 出來。

只有第一次搜尋時用 VirtualQueryEx ,找到 key-value 加入 array,第二次以後搜尋都是根據 array 做刪除 (標記為0代表被刪除,加快速度) 動作。較佳之資料結構選用為 list,唯此處以 c 示之,資料結構不再多撰。

 

xxx

#include <windows.h>
#include <stdio.h>
DWORD AddCnt = 0; DWORD Address[2000]; BOOL RaisePrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if(OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hToken)) if(LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid)) AdjustTokenPrivileges(hToken,FALSE,&tp,0,NULL,0); if(hToken) {// success CloseHandle(hToken); return TRUE; } else { printf("last error : %d\n", GetLastError()); return FALSE; } } void SearchValue(HANDLE hProcess, int Val) { char *p = NULL; int *q; int v; DWORD dwStart = 0, i; SIZE_T lpRead; SYSTEM_INFO si; MEMORY_BASIC_INFORMATION mbi; DWORD MinAddr, MaxAddr; DWORD lpBase; DWORD Size = sizeof(int); // Get Application Min And Max Value GetSystemInfo(&si); MinAddr = (DWORD)si.lpMinimumApplicationAddress; MaxAddr = (DWORD)si.lpMaximumApplicationAddress; // printf("MinAddr : %p\n", MinAddr); // printf("MaxAddr : %p\n", MaxAddr); dwStart = MinAddr; puts("Begin"); while(1){ VirtualQueryEx( hProcess,(void *)dwStart,&mbi,sizeof(MEMORY_BASIC_INFORMATION)); if(dwStart + mbi.RegionSize < dwStart) break; if(mbi.State != MEM_COMMIT) { dwStart+=mbi.RegionSize; continue; } for(i=dwStart; i<dwStart+mbi.RegionSize; i+=Size) if(ReadProcessMemory(hProcess, (LPVOID)i, &v, Size, &lpRead) && v==Val) { Address[AddCnt++] = i; printf("%p\n", i); } dwStart+=mbi.RegionSize; } puts("End"); } void SearchValue2(HANDLE hProcess, int Val) { char *p = NULL; int *q; int v; DWORD dwStart = 0x1000, i; SIZE_T lpRead; SYSTEM_INFO si; MEMORY_BASIC_INFORMATION mbi; DWORD MinAddr, MaxAddr; DWORD lpBase; DWORD Size = sizeof(int); puts("Begin"); for(i=0 ; i<AddCnt; ++i){ ReadProcessMemory(hProcess, (LPVOID)Address[i], &v, Size, &lpRead); if(Address[i]!=0 && v==Val) printf("%p\n", Address[i]); else Address[i] = 0; } puts("End"); } int main() { const char *title = "D:\\Setting\\win7_desktop2\\game2.exe"; HWND hwnd = FindWindow(NULL, title); DWORD proc_id; HANDLE hProcess; int Val; if(hwnd==NULL) { printf("can't find %s\n", title); getchar(); return 1; } // raise privilege RaisePrivilege(); // Open Process GetWindowThreadProcessId(hwnd, &proc_id); hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proc_id); if(!hProcess) { printf("open process fail at code : %d.\n", GetLastError()); getchar(); return 1; } scanf("%d", &Val); SearchValue(hProcess, Val); while(scanf("%d", &Val)==1) SearchValue2(hProcess, Val); // // close handle // CloseHandle(hProcess); // puts("Write All Zero OK"); system("pause"); return 0; }

 

header 自己再加 stdlib.h 上去,懶得再修了。 關鍵在副函式 SearchValue、SearchValue2,試過,的確可找出 a 之位置。

另外還拿三套遊戲測試過,可抓到、寫入無誤。

由於筆者經驗不算足,上法雖不是完全暴力法( 由 GetSystemInfo 得到的 minaddress~maxaddress 逐一查看才是真正的暴力) ,但對 Windows Page Management 特性還不完全熟悉,MEMORY_BASIC_INFORMATION 裡之參數意義讀得不夠透徹,搜尋上面小的程式,速度是可接受,不過拿去搜尋遊戲裡面的話...速度有待加強 。另game2.exe 不論是用 stack 或 heap 存放 a 值,都可順利找到。

 

最後,這份程式碼其實還藏一手,關鍵是當用 VirtualQueryEx 得知 RegionSize 時,做一次 malloc,接下來的 ProcessMemoryRead 也只需呼叫一次,剩下的再自己處理,一直用 ProcessMemoryRead 速度當理會被拖下來。有興趣可自己改。

 

Others

 

接下來繼續接連續技 -  [野戰外掛]  Read / Write Process Memory ,一份簡易的 Game Master/ Cheat Engine 便完成三分之一,剩下的三分之二便是 GUI 設計。GUI 設計就真的不建議還用 C 硬砍了,這個真的是太累人了。

 

初步的 note,幾個實作重點大概如下。

 

1. GUI - 如何設計一份好的 Excel 分頁 <excel 分頁程度偏中,考慮「好的」設計模式程式中偏難>。

2. kernel - 如何在 comboBox 裡列舉所有視窗 <偏易>

3. GUI - 如何在 comboBox 裡列舉之視窗文字前,加上 icon <中偏易 >

4. kernel - 如何響應 list-view 之 double-click,呼叫出獨佔式 Dialog <中>

5. kernel - 鎖定某個 memory address 後,如何定時對它寫入某值 <易>

6. kernel - 當欲截取之 process 關閉時,若再搜尋其 memory 有機會引發錯誤。程式怎麼獲取 process 關閉訊息 <難>

7. 穩定性測試 - 這類型程式容易產生程式死當現象 ( 用過 Game Master 大概都知道 )

 

其他要加什麼,可能有在玩遊戲的人會比較想得到,這裡就點到為止吧。

edisonx 發表在 痞客邦 PIXNET 留言(0) 人氣()