目标功能: 1.鼠标悬浮雷区时,显示是否有雷 2.一键扫雷
游戏通关条件: 没有雷的地方已经全部点出
需要分析的数据及代码: 1.鼠标位置 2.扫雷数组的高度,宽度,雷数 3.扫雷数据的基址 4.扫雷数组下标转为鼠标位置(便于发消息)
工具:OD(动态调试) CE(数据搜索) Spy++(寻找窗口回调函数) PEID/exeinfo(查壳,查编译环境)VS(程序开发)
搭建程序框架: 1.使用MFC DILL: 方便之处在于不需要自己写DllMain的case,直接写在initinstance中即可,且使用CString方便。 2.SetWindowlong: 修改窗口回调函数,在自己的窗口回调函数中修改快捷键响应。 3.CallWindowProc: 调用指定的窗口回调函数
边分析边测试: 1.找到必要的数据 扫雷数组的相关数据:高度,宽度,雷数 2.测试数据可靠性 写代码测试 3.找到扫雷数组的初始化代码: 将其转化为自己的代码,写在Dll中,进行测试 4.寻找屏幕坐标转为扫雷数组下标的代码 找到后,写在Dll中,测试 5.整合代码完成功能
数据寻找
(新人报道帖)做一个扫雷外挂(注释详细)
(新人报道帖)做一个扫雷外挂(注释详细)
再次扫描
(新人报道帖)做一个扫雷外挂(注释详细)
更改一次雷数,再次扫描
(新人报道帖)做一个扫雷外挂(注释详细)
再试几次,发现一直是这三个地址,那么就先全部点下来
(新人报道帖)做一个扫雷外挂(注释详细)
高度和宽度同理添加
(新人报道帖)做一个扫雷外挂(注释详细)
使用PEID查看以下程序的OEP,来确定查到的数据属于哪个区段。
(新人报道帖)做一个扫雷外挂(注释详细)
********************************************************************
(新人报道帖)做一个扫雷外挂(注释详细)
以上两张图说明我们的数据再(.data)段。
创建一个静态链接的MFC dll库 查找以下扫雷窗口的回调函数 编写测试代码,测试数据可靠性,测试注入代码。
(新人报道帖)做一个扫雷外挂(注释详细)
// MFCLibrary1.cpp: 定义 DLL 的初始化例程。 //
#include "pch.h" #include "framework.h" #include "MFCLibrary1.h" #include "windows.h" #define GWL_WNDPROC (-4)
#ifdef _DEBUG #define new DEBUG_NEW #endif
BEGIN_MESSAGE_MAP(CMFCLibrary1App, CWinApp) END_MESSAGE_MAP()
// CMFCLibrary1App 构造
CMFCLibrary1App::CMFCLibrary1App() { // TODO: 在此处添加构造代码, // 将所有重要的初始化放置在 InitInstance 中 }
// 唯一的 CMFCLibrary1App 对象
CMFCLibrary1App theApp; HWND g_Wnd; WNDPROC g_OldProc; PDWORD g_pWidth = (PDWORD)0x1005334; PDWORD g_pHeigh = (PDWORD)0x1005338; PDWORD g_pMineCount = (PDWORD)0x1005330;
// CMFCLibrary1App 初始化
LRESULT CALLBACK WindowProc( _In_ HWND hWnd, _In_ UINT Msg, _In_ WPA RAM wParam, _In_ LPARAM lParam) { if (Msg == WM_KEYDOWN && wParam == VK_F5) { //一键通关 OutputDebugString(L"F5"); int nHeight = *g_pHeigh; int nWidth = *g_pWidth; int nMineCount = *g_pMineCount;
CString strString; strString.Format(L"高度:%d 宽度:%d 雷数:%d", nHeight, nWidth, nMineCount); AfxOutputDebugString(strString.GetBuffer()); } else if (Msg == WM_MOUSEMOVE) { //鼠标移动透视 } return CallWindowProc(g_OldProc, hWnd, Msg, wParam, lParam); }
BOOL CMFCLibrary1App::InitInstance() { CWinApp::InitInstance(); //获取窗口句柄 g_Wnd = ::FindWindow(L"扫雷",L"扫雷"); if (NULL == g_Wnd) { OutputDebugString(L"找不到窗口句柄"); return FALSE; } //设置窗口回调函数 g_OldProc = (WNDPROC)SetWindowLong(g_Wnd,GWL_WNDPROC,(LONG)WindowProc); if (NULL == g_OldProc) { OutputDebugString(L"设置窗口回调失败"); return FALSE; } return TRUE; } ********************************************************************************************** 代码生成成功了
(新人报道帖)做一个扫雷外挂(注释详细)
使用注入工具将dll注入到扫雷内。
(新人报道帖)做一个扫雷外挂(注释详细)
注入成功了。
(新人报道帖)做一个扫雷外挂(注释详细)
F5 测试了一下,注入与数据是没有问题的。
寻找雷初始化的数组 将扫雷附加到OD中
(新人报道帖)做一个扫雷外挂(注释详细)
(新人报道帖)做一个扫雷外挂(注释详细)
将这个地址在OD中寻找
(新人报道帖)做一个扫雷外挂(注释详细)
F8将循环跑完,然后查看内存,猜测这里是一个边界
(新人报道帖)做一个扫雷外挂(注释详细)
这个循环走完,行边界初始化完成了
(新人报道帖)做一个扫雷外挂(注释详细)
(新人报道帖)做一个扫雷外挂(注释详细)
(新人报道帖)做一个扫雷外挂(注释详细)
判断这里就是一个初始化雷区的函数
(新人报道帖)做一个扫雷外挂(注释详细)
************************************************************************** 接下来捋顺一下,一键通关功能的思路:雷区数组,判断每个元素是不是雷,若不是雷,模拟点击。 按着思路,要遍历雷区数组,那么就得详细分析上图的汇编代码了。 首先观察以下内存窗口,为了方便,将扫雷设置一下。
(新人报道帖)做一个扫雷外挂(注释详细)
内存窗口观察更加清晰了
(新人报道帖)做一个扫雷外挂(注释详细)
玩一下游戏,分析一下内存中的数据 猜测过程就省略了雷区元素标志:大约 8F是雷 40是空 41是1 42是2 43是3 雷区基址:0x1005340
(新人报道帖)做一个扫雷外挂(注释详细)
汇编指令详细分析
(新人报道帖)做一个扫雷外挂(注释详细)
通过spy++来获取窗口回调函数的基址,然后在OD 中找到
(新人报道帖)做一个扫雷外挂(注释详细)
从这里开始分析 既然找到了回调函数,那么就可以假定参数
(新人报道帖)做一个扫雷外挂(注释详细)
(新人报道帖)做一个扫雷外挂(注释详细)
然后断点设置为消息断点
(新人报道帖)做一个扫雷外挂(注释详细)
消息回调函数分析
(新人报道帖)做一个扫雷外挂(注释详细)
// MFCLibrary1.cpp: 定义 DLL 的初始化例程。 //
#include "pch.h" #include "framework.h" #include "MFCLibrary1.h" #include "windows.h" #define GWL_WNDPROC (-4)
#ifdef _DEBUG #define new DEBUG_NEW #endif
BEGIN_MESSAGE_MAP(CMFCLibrary1App, CWinApp) END_MESSAGE_MAP()
// CMFCLibrary1App 构造
CMFCLibrary1App::CMFCLibrary1App() { // TODO: 在此处添加构造代码, // 将所有重要的初始化放置在 InitInstance 中 }
// 唯一的 CMFCLibrary1App 对象
CMFCLibrary1App theApp; HWND g_Wnd; WNDPROC g_OldProc; PDWORD g_pWidth = (PDWORD)0x1005334; PDWORD g_pHeigh = (PDWORD)0x1005338; PDWORD g_pMineCount = (PDWORD)0x1005330; PBYTE g_pBase = (PBYTE)0x1005340; #define MINE 0x8F
// CMFCLibrary1App 初始化
LRESULT CALLBACK WindowProc( _In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam) { if (Msg == WM_KEYDOWN && wParam == VK_F5) { //一键通关 OutputDebugString(L"F5"); int nHeight = *g_pHeigh; int nWidth = *g_pWidth; int nMineCount = *g_pMineCount;
CString strString; strString.Format(L"高度:%d 宽度:%d 雷数:%d", nHeight, nWidth, nMineCount); AfxOutputDebugString(strString.GetBuffer());
int nFindCount = 0; for (size_t y = 1; y < nHeight + 1; y++) { CString strLine; for (size_t x = 0; x < nWidth + 1; x++) { BYTE byCode = *(PBYTE)((DWORD)g_pBase + x + y * 32); if (byCode == MINE) { nFindCount++; } CString strCode; strCode.Format(L"%02x ", byCode); strLine += strCode; } AfxOutputDebugString(strLine.GetBuffer()); } } else if (Msg == WM_MOUSEMOVE) { int x, y; x = LOWORD(lParam); y = HIWORD(lParam); x = (x + 4) >> 4; y = (y - 0x27) >> 4; BYTE byCode = *(PBYTE)((DWORD)g_pBase + x + y * 32); if (byCode == MINE) { SetWindowText(hWnd,L"雷"); } else { SetWindowText(hWnd, L"无雷"); } } return CallWindowProc(g_OldProc, hWnd, Msg, wParam, lParam); }
BOOL CMFCLibrary1App::InitInstance() { CWinApp::InitInstance(); //获取窗口句柄 g_Wnd = ::FindWindow(L"扫雷",L"扫雷"); if (NULL == g_Wnd) { OutputDebugString(L"找不到窗口句柄"); return FALSE; } //设置窗口回调函数 g_OldProc = (WNDPROC)SetWindowLong(g_Wnd,GWL_WNDPROC,(LONG)WindowProc); if (NULL == g_OldProc) { OutputDebugString(L"设置窗口回调失败"); return FALSE; } return TRUE; }
注入代码测试一下
(新人报道帖)做一个扫雷外挂(注释详细)
(新人报道帖)做一个扫雷外挂(注释详细)
代码是没问题的
(新人报道帖)做一个扫雷外挂(注释详细)
下面开始研究一键扫雷
(新人报道帖)做一个扫雷外挂(注释详细)
// MFCLibrary1.cpp: 定义 DLL 的初始化例程。 //
#include "pch.h" #include "framework.h" #include "MFCLibrary1.h" #include "windows.h" #define GWL_WNDPROC (-4)
#ifdef _DEBUG #define new DEBUG_NEW #endif
BEGIN_MESSAGE_MAP(CMFCLibrary1App, CWinApp) END_MESSAGE_MAP()
// CMFCLibrary1App 构造
CMFCLibrary1App::CMFCLibrary1App() { // TODO: 在此处添加构造代码, // 将所有重要的初始化放置在 InitInstance 中 }
// 唯一的 CMFCLibrary1App 对象
CMFCLibrary1App theApp; HWND g_Wnd; WNDPROC g_OldProc; PDWORD g_pWidth = (PDWORD)0x1005334; PDWORD g_pHeigh = (PDWORD)0x1005338; PDWORD g_pMineCount = (PDWORD)0x1005330; PBYTE g_pBase = (PBYTE)0x1005340; #define MINE 0x8F
// CMFCLibrary1App 初始化
LRESULT CALLBACK WindowProc( _In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam) { if (Msg == WM_KEYDOWN && wParam == VK_F5) { //一键通关 OutputDebugString(L"F5"); int nHeight = *g_pHeigh; int nWidth = *g_pWidth; int nMineCount = *g_pMineCount;
CString strString; strString.Format(L"高度:%d 宽度:%d 雷数:%d", nHeight, nWidth, nMineCount); AfxOutputDebugString(strString.GetBuffer());
int nFindCount = 0; for (size_t y = 1; y < nHeight + 1; y++) { CString strLine; for (size_t x = 0; x < nWidth + 1; x++) { BYTE byCode = *(PBYTE)((DWORD)g_pBase + x + y * 32); if (byCode == MINE) { nFindCount++; } else { int xPos, yPos; xPos = (x << 4) - 4; yPos = (y << 4) + 0x27;
SendMessage(hWnd,WM_LBUTTONDOWN,0,MAKELPARAM(xPos, yPos)); SendMessage(hWnd, WM_LBUTTONUP, 0, MAKELPARAM(xPos, yPos)); } CString strCode; strCode.Format(L"%02x ", byCode); strLine += strCode; } AfxOutputDebugString(strLine.GetBuffer()); } } else if (Msg == WM_MOUSEMOVE) { int x, y; x = LOWORD(lParam); y = HIWORD(lParam); x = (x + 4) >> 4; y = (y - 0x27) >> 4; BYTE byCode = *(PBYTE)((DWORD)g_pBase + x + y * 32); if (byCode == MINE) { SetWindowText(hWnd,L"雷"); } else { SetWindowText(hWnd, L"无雷"); } } return CallWindowProc(g_OldProc, hWnd, Msg, wParam, lParam); }
BOOL CMFCLibrary1App::InitInstance() { CWinApp::InitInstance(); //获取窗口句柄 g_Wnd = ::FindWindow(L"扫雷",L"扫雷"); if (NULL == g_Wnd) { OutputDebugString(L"找不到窗口句柄"); return FALSE; } //设置窗口回调函数 g_OldProc = (WNDPROC)SetWindowLong(g_Wnd,GWL_WNDPROC,(LONG)WindowProc); if (NULL == g_OldProc) { OutputDebugString(L"设置窗口回调失败"); return FALSE; } return TRUE; }
测试一下也是没问题的
(新人报道帖)做一个扫雷外挂(注释详细)
|