所謂遊戲外掛,是一種遊戲輔助程序,可以協助玩家自動產生遊戲動作、修改遊戲網絡數據包以及修改遊戲內存數據等。能實現的功能一般包括:自動戰鬥、自動行走、自動練級、自動補血、加速、不遇敵、原地遇敵、快速增加經驗值等。
在看雪上看到很多人說他們第一個外掛就是掃雷,於是我也找來了掃雷遊戲練手,程序小,簡單,成功以後可以增加新手的自信心。
外掛掃雷,有兩個目標點。
第一,去除時間走動。
第二,自動找出所有的雷,一鍵掃雷。
一, 時間
這是一個windows窗體程序,我們需要對消息機制,窗體創建有一個了解。具體參見windows 核心編程。RegisterClassA(W), RegisterClassAEX(W), 會註冊窗體類,它接受一個_In_ const WNDCLASSEX *lpwcx 參數。
可以知道第三個參數是註冊的窗口消息處理函數的指針。用OD打開目標程序: 掃雷.exe,在RegisterClassAEX 系列函數下斷點,可以找到向系統註冊的自定義窗口回調函數。
看到這麼多case,猜測是對不同消息的不同處理。找到我們感興趣的:
猜測每次時間走動和這個消息有關係,下斷點,點擊一次窗體以後什麼也不幹,果然每一秒鐘就會斷一次。說明,確實是在這個消息裡面處理的時間。
斷點下來:
看時間是53秒,開打Cheat Engine,採用如圖參數搜尋。
可以看見我們exe模塊只有一個byte的值是53.為了確保這個地址確實就是我們存放時間的地址,讓OD繼續執行到時間為54。
我們發現這個內存地址發生了變化,從53變化到了54,確實就是存放時間的地址。為了找到修改這個地址的代碼,我們利用od下內存寫斷點。具體方法:在OD查看內存的地方,ctrl+G,輸入地址100579c
右鍵內存write point
然後f9,跑起來,會斷在修改這個byte內存的地方
可以看到對100579c這個地方的int變量每次增加1.有兩個辦法去掉時間增加:一個是修改pe文件,將這行指令用od nop掉,然後保存;一個是記錄下地址,在注入dll的時候把內存修改為nop。
二. 自動掃雷
從用戶行為開始分析,掃雷遊戲,右鍵是插旗幟,左鍵按下,抬起是踩下去。從上面的用戶註冊的消息回調開始,找到:
這是左鍵按下的消息。我們通過windows核心編程知道,按鍵發生的消息如下:
消息處理函數的第四個參數是按在窗體上距離原點(左上角)的x,y偏移,在1001fb1的地方調用了一個函數,用坐標作為第一個參數,我們猜測是邏輯處理,跟進去,結果發現裡面只是處理了一個繪製信息,好失望。出來,接著跟。一路f8,跟到:
發現圖標由微笑的臉變成了
哈哈,標註下來,找到一個系統call。說不定以後有用,可以直接調用這個系統call。接著f8,找到了:
傳入的兩個參數為格子的坐標(x,y),直接f8跳過這個函數,發現變成了灰色,猜測這裡做了邏輯處理。重新執行到這個函數,並且跟進去。
找到一個可疑函數跟進去。然後發現跟進去也沒看出個所以然來。
怎麼辦,這個時候思考一下,從哪個角度入手。無聊隨便亂按掃雷的雷區,發現左鍵按下保持,可以移動,當移動到一個位置,左鍵抬起,這個左鍵抬起的位置才會出現數字和判斷是否是雷。於是考慮從左鍵抬起信號地方入手。又回到註冊的消息回調函數,就調用了這一個函數,跟進去:
參數是坐標信息,猜測是用坐標信息做什麼處理,然後得到結論。
果然看到了row * 32 + col + 1005340 用來索引坐標。
打開遊戲自定義,查看高級的自定義,最多是30 * 16,猜測有邊界沒有使用。即使是初級9*9,也是使用的30 * 16的大的二維數組,其他的地方沒有使用。使用od,查看二維數組內存1005340
很有規律的10 8f 0f,還是一些42,什麼的。
突然我發現了一個42!!!!這不是ascii碼的2麼
測被點開的數字用ascii碼表示。
接下來的任務就是弄清這一堆數字表示的意思是什麼,10 8f 0f,怎麼辦呢,對比法。用簡單模式9*9,沒有點擊的時候內存如下:
根據這個公式row * 32 + col + 1005340,我們發現,內置的每一行有32個方塊(最高級模式為30*16),結合內存看,有11個10(簡單模式為9*9),我們猜測在9*9的數組外圍有一層牆,用0x10標識,那麼在第二行第一個,也就是(2-1)*32,應該是0x10,5340+0x20應該是0x10,好。我們知道了0x10是邊界,那麼就只剩下0xf 0x8f。
我們點開所有的雷:
運氣真好,點了兩下就中雷了。這個時候查看內存。
圖中多了幾個字符 0x8a 0x41 0xcc,結合兩張圖看
發現 5361 由由0f編程了8a 5366及還有得其他地方由8f變成了8a, 5384由8f變成了41(『1』),表示周圍有一個雷,當然就是這裡的5385的cc。
猜測,最原始的0x0f表示沒有被踩過的非雷方塊,0x8f表示沒有踩過的雷方塊。當點擊一個方塊,它周圍有雷,就計算出雷的個數,並顯示字符。當點擊一個雷,點中的這個雷由8f變成cc(int 3中斷了?哈哈),其他沒有點中的雷變成8a。由此可知,從1005340開始所有8f的地方就是雷。
三.注入
注入方法不再說了(採用遠程線程注入),直接上源碼。(壓縮包,下載)。這裡說Dll的寫法。
通過Cheat Engine 前後比較拿到遊戲設定的行列數,地址如下:
const int GameRow = *(int*)0x1005338;
const int GameCol = *(int*)0x1005334;
計算二維數組內存地址範圍:
unsigned char* start_base = (unsigned char*)0x1005340;
unsigned char* stop_base = (unsigned char*)(0x1005340 + GameRow * GameCol + 100); //多出100,偷懶+保險
static const unsigned char BOND_FLAG = 0x8f; //雷的標識,start_base stop_base範圍內找雷,並且存到vector<point> 裡面。
class point {
public:
int row;
int col;
};
找到所有的雷的行列坐標以後怎麼實現一鍵掃雷呢?
有一個思路是把所有不是雷的地方都點擊一下,這樣遊戲就勝利了。
SendMessage(hwnd, WM_LBUTTONUP, MK_LBUTTON, MAKELONG(x, y));
重點在於point.x point.y如何轉化為窗口的x,y。
我的做法是找來了QQ截圖軟體,觀察出像素點信息為,二維數組的棋盤起始偏移為(7,55) 方塊之間的空格為4,方塊為12*12.則 (row, col)對應的窗體偏移為:
X = 7 + 4 * row + 12 * (row - 1) + 6; // 6是點擊方塊的中間
Y = 50 + 4 * col + 12 * (col - 1) + 6
化簡:x = 1 + 16 * row; y = 44 + 16 * col; 窗體的x和y和正常坐標系是反過來的。所以模擬點擊代碼:
四.結束
截圖一張:
微信ID:ikanxue
看雪學院,致力於安全研究16年!
看雪眾測:http://ce.kanxue.com 第二期項目已發布,10萬獎金等你拿!
看雪閱讀:http://book.kanxue.com 付費閱讀新模式