Print动 发表于 2021-1-19 21:54:13

(新人报道帖)做一个扫雷外挂(注释详细)

目标功能: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_ 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());      }      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;}
测试一下也是没问题的
**** Hidden Message ********* Hidden Message *****

admin 发表于 2021-1-20 00:37:57

能否将代码上传附件?

Print动 发表于 2021-1-20 08:49:13

roger 发表于 2021-1-20 00:37
能否将代码上传附件?

源码已上传

伯爵的信仰 发表于 2021-1-22 09:58:32

写的很详细,支持一下。

xuexi963 发表于 2021-3-27 12:21:02

每天都有1次免费给楼主评分送学币的机会!

打破了壳的蜗牛 发表于 2021-4-23 11:42:51


每天都有1次免费给楼主评分送学币的机会!zdm

keymark 发表于 2021-4-23 14:43:37

感谢分享,我会认真学习的!
页: [1]
查看完整版本: (新人报道帖)做一个扫雷外挂(注释详细)