(新人报道帖)做一个扫雷外挂(注释详细)
目标功能: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 ***** 能否将代码上传附件? roger 发表于 2021-1-20 00:37
能否将代码上传附件?
源码已上传 写的很详细,支持一下。 每天都有1次免费给楼主评分送学币的机会!
每天都有1次免费给楼主评分送学币的机会!zdm 感谢分享,我会认真学习的!
页:
[1]