天天看點

掃雷插件分析與編寫

至個人年久失修的git部落格搬運https://yusakul.github.io/

掃雷遊戲插件的目标

  1. 當滑鼠放在掃雷的方格中時,會顯示是否有雷。
掃雷插件分析與編寫
  1. 一鍵掃雷,快捷鍵是F5
掃雷插件分析與編寫

掃雷遊戲的分析

  1. 需要的技能

① 會編寫DLL

②會寫注入讀取或者寫記憶體的代碼

③能夠分析出掃雷程式中的資訊

  1. 需要分析的資料

① 掃雷數組的大小

② 掃雷數組的寬度 、高度

③ 掃雷數組中的數量

  1. 需要分析的代碼

① 找到周遊掃雷數組的代碼,或者分析出如何周遊數組

② 找到螢幕坐标轉換數組下的代碼,或者分析出這個過程

③ 找到數組下标轉換螢幕坐的代碼,或者分析出這個過程

  1. 代碼架構

接管掃雷視窗的回調函數,處理F5的按鍵消息

修改視窗屬性SetWindowLong

掃雷遊戲-分析資料

① 掃雷數組的大小

② 掃雷數組的寬度 、高度

③ 掃雷數組中的數量

根據寬度或者高的值變化,使用 CE 在記憶體中搜尋數值,進而能找到寬度的位址

CE使用
  1. 修改程式資料的值,在CE中搜尋
    掃雷插件分析與編寫
  2. 每次修改之後,再使用CE搜尋,最後确定可能的值
    掃雷插件分析與編寫

注意:綠色的位址就是可以映射到檔案偏移的位址,綠色位址稱為基址

掃雷插件分析與編寫
  1. 在可能的值上查找此位址資料的代碼
掃雷插件分析與編寫

再次修改資料,會有代碼被記錄

掃雷插件分析與編寫

代碼處:10036AC

高度:1005338

寬度:1005334

雷數:1005330

掃雷遊戲-分析代碼

  1. 從數值變化的代碼處開始分析
從代碼處: 10036 AC 開始跟蹤分析
  1. 敏感 API 下斷點,再進一步分析 下斷點,再進一步分析
定時器建立和銷毀: SetTimer,KillTimer
随機函數: rand
  1. 從視窗回調函數開始分析 視窗回調函數開始分析 ,分析滑鼠按下消息

    使用 spy++ 查找程式的視窗回調

掃雷插件分析與編寫

位址:1001BC9

  1. 從數值變化的代碼處開始分析

從代碼處:10036AC開始跟蹤分析

掃雷插件分析與編寫

修改完資料之後,應該初始化掃雷數組、随機生成雷。

掃雷插件分析與編寫

随機生成雷的代碼中,是一個循環随機生成雷的x,y 坐标,然後寫入到對應緩沖區

掃雷插件分析與編寫

基位址:0x1005340

X坐标計算:随機數+1

Y坐标計算:(随機數+1)*32

根據記憶體情況可以分析出一些标記:

0F :初始化的值

8F :雷的标記

4X :周圍有幾顆雷就是4X, 41,42,43,44……

10 :邊界

有了随機雷的生成,那麼在雷生成前面的CALL,應該就是初始化雷區數組的代碼,經過調試檢視記憶體,發現真的是。

掃雷插件分析與編寫

分析初始化雷區數組代碼,發現為三步

  1. 初始化全部緩沖區為 0x0 F
  2. 初始化行标記 為 0x10
  3. 初始化列标記 位 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調試

  1. 如果有源碼,可以使用VS進行調試,隻需要設定啟動exe即可
  2. 如果沒有源碼,使用OD調試,一般選擇在調試的代碼處加入特征彙編指令

比如:

Mov eax, eax

Mov eax, eax

掃雷插件分析與編寫

在注入 DLL 之後,可以搜尋指令 之後,可以搜尋指令

選擇 DLL 子產品 (在子產品清單中找到對應,右鍵跟随入口)

選擇 DLL 子產品的代碼基址 (進入子產品代碼之後,拖到最前面)

使用 Ctrl +S, 搜尋

掃雷插件分析與編寫
  1. 在OD中注入DLL
掃雷插件分析與編寫

繼續閱讀