逆向之制作掃雷外掛
本次掃雷外掛制作 涉及工具,VS2017、OD、Dbgview、CE, 至于掃雷程式,網上下的117kb 的那個,
外掛功能:一鍵通關、左上角視窗標題處顯示雷區,
效果展示:

下面開始記錄從頭到尾的逆向與撰寫外掛程序
1.尋找關鍵資料基址
首先用CE打開掃雷行程,然后點擊掃雷的自定義設定,找到寬、高、雷數量的界面,
然后用CE依次找到寬、高、雷的基址

發現有0x100533? 、0x10053A? 兩組資料
分析思路是記憶體肯定要讀取 寬、高、雷 的值,所以可以用OD打開掃雷,其次輸入寬的地址,其次下硬體訪問斷點,找到訪問 寬度 的代碼塊,
也可以利用CE直接查找訪問地址,選中地址,點擊訪問此資料的代碼,

點擊笑臉,重繪一下掃雷,發現出現好幾個訪問地址,
優先選擇匯編代碼為 mov xxx,[0x???]的 代碼,因為該變數將會被使用,
可以用OD一組一組的動態除錯,我除錯后發現0x100533?,相當于臨時資料,0x10053A?相當于初始資料,都可以進行利用!

2.分析地圖邊界
根據上圖,用OD跳轉到0x1002EE4 地址
然后單步慢慢跟蹤,發現一個0x1005340為首地址的陣列,更改標簽為基址Addr,這實際上是一個二維陣列,一般的平面游戲地圖用的都是二維陣列,

開頭就是一個回圈,初始化基地址為0x1005340,大小為0x360個位元組的空間為0F,查看資料基址記憶體如圖

為了方便與OD的記憶體界面對齊,設定掃雷的寬度為14,高度為9,
繼續單步跟蹤,不難發現,下邊的代碼就是初始化掃雷區域的邊界,其記憶體分布如下圖:

如圖,可以得知邊界被賦值為0x10,因為邊界額外占了兩行和兩列,所以操作這個陣列的時候要特別注意,
該記憶體分布有點奇怪的是,每隔一行,都會有一行全是0F ,這個不用管它,根據你之前的代碼,我們只要能逆向出遍歷陣列的代碼就行了,遍歷匯編重要代碼如下:

3.分析掃雷區域各種資料對應的陣列元素
同樣的方法,通過CE找到地雷數量的基址,然后下訪問斷點,進入單步跟蹤代碼,
跟蹤不了幾步,就會發現一個 or [eax],0x80的地方,它會將eax邏輯或為 8F,

通過隨時觀看地圖基址0x1005340的記憶體資料,發現他會在地圖陣列里面填充最大的地雷數量個8F, 猜測8F在地圖陣列里面就代表地雷,

為了驗證猜想,先把OD中的斷點都取消,然后讓掃雷程式跑起來,之后對比雷區地圖和記憶體資料,
只對比第一排,發現被點擊后的地雷變為了8A, 有數字的地圖被ASCII碼填充了,至少說明沒有找錯方向,我們只需要關心8F代表沒有被點擊之前的地雷就行了,

3.訊息回應與視窗回呼分析
通過上面的分析,已經完全能寫代碼遍歷地圖陣列,找到地雷都在什么位置了,
但是還有一個重點是,這是滑鼠游戲,所以需要找到滑鼠訊息,以便用滑鼠開掛,
這里需要才做滑鼠的點擊訊息,LBUTTONDOWN和LBUTTONUP,
用spy++ 打開掃雷視窗,找到視窗行程(視窗回呼)函式的地址,

既然是視窗回呼函式,那在OD中就可以假設該地址的WndProc(1,2,3,4), 然后下LBUTTONDOWN的訊息條件斷點,直接用圖片演示依次的操作,




下好訊息條件斷點,直接跑起來,然后滑鼠點擊一下雷區,就會斷下,在這里我們主要的關注的該視窗的滑鼠坐標,也就是WndProc()的第四個引數 lParam

可以看到傳進來的坐標是50,62,實際上我們不用關心它的值是多少,只需要關心第四個引數,或者被第四個引數賦值的暫存器在哪被利用了,OD已經用arg.4 代表了這是第四個引數,

接下來F8單步跟蹤,會發現有一個call呼叫了第四個引數,結果里面全是和界面繪圖相關的API,因此跳過這個函式,繼續往下走,就會找到真正的關鍵函式,

從上圖知道,傳進來的坐標,被進行了一系列的操作,然后計算出坐標所在雷區的那一個位置,我之前點擊的是第一排的第三個格子,所以被程式轉化成了(1,3)

找到了滑鼠坐標是如何轉化陣列的序號索引,接下來就可以開始寫外掛的,
在匯編代碼我寫的注釋都是根據你單步跟蹤慢慢分析出來的結果,x代表寬或x軸坐標,y代表高或y軸坐標,
有一點要注意的是,計算機上的y軸方向是向下的,所以最左上角的坐標應該是(0,0)
4.外掛撰寫
寫外掛用MFC的動態庫DLL ,創建一個MFC的DLL專案后,更改視窗回呼,這類似于鉤子,最難的逆向那一步都熬過來,DLL的代碼就輕松多了,原始碼再此貼上,
// 唯一的 C掃雷DLLApp 物件
C掃雷DLLApp theApp;
HWND g_hWnd; //掃雷視窗的句柄
PDWORD g_Width = (PDWORD)0x10056ac; //寬
PDWORD g_High = (PDWORD)0x10056a8; //高
PDWORD g_Mine = (PDWORD)0x10056a4; //雷
PBYTE g_Addr = (PBYTE)0x1005340; //地圖陣列基址
WNDPROC g_WndProc;//原來的視窗回呼
// 自己的視窗回呼
LRESULT CALLBACK MyDefWindowProcW(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
// 按下F5鍵,一鍵通關
if (Msg == WM_KEYDOWN && wParam == VK_F5)
{
size_t nWidth = *g_Width;
size_t nHigh = *g_High;
size_t nMine = *g_Mine;
CString strShow;
strShow.Format(L"寬度:%d 高度:%d 雷數:%d", nWidth, nHigh, nMine);
OutputDebugString(strShow);
// 遍歷每列
for (size_t j = 1; j < nHigh + 1; j++)
{
CString strAddr;
// 遍歷每行
for (size_t i = 1; i < nWidth + 1; i++)
{
// (y + 2) * 32 + x + 1 + addr
BYTE bValue = *(PBYTE)((DWORD)g_Addr + j * 0x20 + i);
// 輸出地圖位元組
CString strByte;
strByte.Format(L"%02X ", bValue);
strAddr += strByte;
// 如果不是雷區,就模擬點擊
if (bValue != 0x8F)
{
int x, y;
x = (i << 4) - 4;
y = (j << 4) + 0x27;
SendMessage(hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
SendMessage(hWnd, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
}
}
OutputDebugString(strAddr);
}
}
// 標題欄上提示雷區
if (Msg == WM_MOUSEMOVE)
{
//OutputDebugStringA("移動了滑鼠");
int x, y;
// 獲取視窗中滑鼠的x y 坐標
x = LOWORD(lParam);
y = HIWORD(lParam);
x = (x + 4) >> 4;
y = (y - 0x27) >> 4;
if (*(PBYTE)((DWORD)g_Addr + y * 0x20 + x) == 0x8F)
{
SetWindowText(hWnd, L"*** 此處有雷!***");
}
else
{
SetWindowText(hWnd, L"九陽道人");
}
}
// 將訊息資訊傳遞給掃雷視窗程序
return CallWindowProc(g_WndProc, g_hWnd, Msg, wParam, lParam);
}
BOOL C掃雷DLLApp::InitInstance()
{
CWinApp::InitInstance();
// 找到視窗句柄
g_hWnd = ::FindWindowA("掃雷", "掃雷");
if (!g_hWnd)
{
AfxMessageBox(L"FindWindowA!");
return FALSE;
}
// 設定視窗回呼
g_WndProc = (WNDPROC)SetWindowLong(g_hWnd, GWL_WNDPROC, (LONG)MyDefWindowProcW);
if (!g_WndProc)
{
AfxMessageBox(L"SetWindowLong!");
return FALSE;
}
return TRUE;
}
5.掃雷程式及DLL和注入工具鏈接
可以自己撰寫注入代碼,也可以用我的,我自己寫了一個注入工具,點擊注入,然后找到掃雷,然后前面四個注入方式都可以使用,
鏈接:https://pan.baidu.com/s/1fChp7lOVNCYtdqE1U5Q2FA
提取碼:71fb

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/198807.html
標籤:其他
