至個人年久失修的git博客搬運https://yusakul.github.io/
掃雷遊戲插件的目標
- 當鼠標放在掃雷的方格中時,會顯示是否有雷。
- 一鍵掃雷,快捷鍵是F5
掃雷遊戲的分析
- 需要的技能
① 會編寫DLL
②會寫注入讀取或者寫內存的代碼
③能夠分析出掃雷程序中的信息
- 需要分析的數據
① 掃雷數組的大小
② 掃雷數組的寬度 、高度
③ 掃雷數組中的數量
- 需要分析的代碼
① 找到遍歷掃雷數組的代碼,或者分析出如何遍歷數組
② 找到屏幕座標轉換數組下的代碼,或者分析出這個過程
③ 找到數組下標轉換屏幕坐的代碼,或者分析出這個過程
- 代碼框架
接管掃雷窗口的回調函數,處理F5的按鍵消息
修改窗口屬性SetWindowLong
掃雷遊戲-分析數據
① 掃雷數組的大小
② 掃雷數組的寬度 、高度
③ 掃雷數組中的數量
根據寬度或者高的值變化,使用 CE 在內存中搜索數值,從而能找到寬度的地址
CE使用
-
修改程序數據的值,在CE中搜索
-
每次修改之後,再使用CE搜索,最後確定可能的值
注意:綠色的地址就是可以映射到文件偏移的地址,綠色地址稱爲基址
- 在可能的值上查找此地址數據的代碼
再次修改數據,會有代碼被記錄
代碼處:10036AC
高度:1005338
寬度:1005334
雷數:1005330
掃雷遊戲-分析代碼
- 從數值變化的代碼處開始分析
從代碼處: 10036 AC 開始跟蹤分析
- 敏感 API 下斷點,再進一步分析 下斷點,再進一步分析
定時器創建和銷燬: SetTimer,KillTimer
隨機函數: rand
-
從窗口回調函數開始分析 窗口回調函數開始分析 ,分析鼠標按下消息
使用 spy++ 查找程序的窗口回調
地址:1001BC9
- 從數值變化的代碼處開始分析
從代碼處:10036AC開始跟蹤分析
修改完數據之後,應該初始化掃雷數組、隨機生成雷。
隨機生成雷的代碼中,是一個循環隨機生成雷的x,y 座標,然後寫入到對應緩衝區
基地址:0x1005340
X座標計算:隨機數+1
Y座標計算:(隨機數+1)*32
根據內存情況可以分析出一些標記:
0F :初始化的值
8F :雷的標記
4X :周圍有幾顆雷就是4X, 41,42,43,44……
10 :邊界
有了隨機雷的生成,那麼在雷生成前面的CALL,應該就是初始化雷區數組的代碼,經過調試查看內存,發現真的是。
分析初始化雷區數組代碼,發現爲三步
-
初始化全部緩衝區爲 0x0 F
-
初始化行標記 爲 0x10
-
初始化列標記 位 0x 10
搭建代碼框架
第一步 ,定義一些值和函數
WNDPROC g_OldProc = NULL;
HWND g_hWnd = NULL;
LRESULT WINAPI WindowProc (
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
if (Msg == WM_KEYDOWN && wParam == VK_F5)
{
CString strString;
strString.Format(L"wParam = %p", wParam);
OutputDebugString(strString.GetBuffer());
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return CallWindowProc(g_OldProc, hWnd, Msg, wParam, lParam);
}
第二步,修改回調函數
// 修改掃雷窗口的回調函數
// 1. 找到掃雷窗口
g_hWnd = FindWindow(NULL, L"掃雷");
// 2. 修改窗口回調
g_OldProc = (WNDPROC)SetWindowLong(g_hWnd, GWL_WNDPROC, (LONG)WindowProc);
// 恢復窗口回調
SetWindowLong(g_hWnd, GWL_WNDPROC, (LONG)g_OldProc);
注意:寫代碼要記得釋放和恢復,寫代碼要做好debug的準備
經過調整編寫之後,遍歷掃雷的數組
// 基地址:0x1005340
// 高度:1005338
// 寬度:1005334
// 雷數:1005330
byte** g_MineArray = (byte**)0x1005340;
int* g_nHeight = (int*)0x1005338;
int* g_nWidth = (int*)0x1005334;
int* g_nMineCount = (int*)0x1005330;
LRESULT WINAPI WindowProc (
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
if (Msg == WM_KEYDOWN && wParam == VK_F5)
{
CString strString;
strString.Format(L"saolei wParam = %p", wParam);
OutputDebugString(strString.GetBuffer());
// 遍歷掃雷數組
int nHeight = *g_nHeight; // 高度y
int nWidth = *g_nWidth; // 寬度x
int nCount = *g_nMineCount;
int nCurrentCount = 0;
for (int j = 1; j < nHeight+1; j++)
{
CString strString1;
strString1.Format(L"saolei 行:%d ", j);
// 行遍歷時,需要注意去掉邊界
for (int i = 1; i < nWidth + 2 - 1; i++)
{
byte byCode = *(byte*)((int)g_MineArray+i+j*32);
if (byCode == (byte)0x8F)
{
nCurrentCount += 1;
}
CString strCode;
strCode.Format(L" %02x ", byCode);
strString1 += strCode;
}
strString1 += L"\r\n";
OutputDebugString(strString1.GetBuffer());
}
CString strString2;
strString2.Format(L"saolei 雷數 = %d", nCurrentCount);
OutputDebugString(strString2.GetBuffer());
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return CallWindowProc(g_OldProc, hWnd, Msg, wParam, lParam);
}
分析 -屏幕座標轉數組下標
分析思路:
① 窗口回調下消息斷點,分析鼠標按下彈起消息的流程
② 靜態分析窗口回調,使用IDA找到對應的消息處理流程
窗口回調地址:1001BC9
在地址處下消息斷點WM_LBUTTONDOWN
在這個消息的lParam參數裏是x,y座標
將彙編代碼翻譯爲C代碼
WORD y = HIWORD(lParam);
y = (y - 0x27) >> 4;
WORD x = LOWORD(lParam);
x = (x + 4) >> 4;
byte byCode = *(byte*)((int)g_MineArray + x + y * 32);
if (byCode == (byte)0x8F)
{
SetWindowText(hWnd,L"掃雷 - 友情提示:你踩到雷了!");
}
else {
SetWindowText(hWnd, L"掃雷");
}
分析-數組下標轉屏幕座標
將上面的代碼反轉
// 發送一個鼠標按下彈起的消息
WORD y = j;
y = (y << 4) + 0x27; //y 高16位
WORD x = i;
x = (x << 4) - 4; // x 低16位
SendMessage(hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM(x,y));
SendMessage(hWnd, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
DLL調試
-
如果有源碼,可以使用VS進行調試,只需要設置啓動exe即可
-
如果沒有源碼,使用OD調試,一般選擇在調試的代碼處加入特徵彙編指令
比如:
Mov eax, eax
Mov eax, eax
在注入 DLL 之後,可以搜索指令 之後,可以搜索指令
選擇 DLL 模塊 (在模塊列表中找到對應,右鍵跟隨入口)
選擇 DLL 模塊的代碼基址 (進入模塊代碼之後,拖到最前面)
使用 Ctrl +S, 搜索
- 在OD中注入DLL