查看: 1290|回复: 6

[原创图文] (新人报道帖)做一个扫雷外挂(注释详细)

[复制链接]
发表于 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_ 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;
}

测试一下也是没问题的

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

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

游客,如果您要查看本帖隐藏内容请回复
游客,如果您要查看本帖隐藏内容请回复
温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的最好奖励,还可以获得学币奖励,请尊重作者的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
学逆向论坛-免费的逆向学习论坛
发表于 2021-1-20 00:37:57 | 显示全部楼层
能否将代码上传附件?

点评

源码已上传  详情 回复 发表于 2021-1-20 08:49
论坛交流群:672619046
 楼主| 发表于 2021-1-20 08:49:13 | 显示全部楼层
roger 发表于 2021-1-20 00:37
能否将代码上传附件?

源码已上传
学逆向论坛-免费的逆向学习论坛

0

主题

12

帖子

0

精华

初级会员

Rank: 4

学币
4
荣耀
0
rank
0
违规
0

    发表于 2021-1-22 09:58:32 | 显示全部楼层
    写的很详细,支持一下。
    学逆向论坛-免费的逆向学习论坛

    0

    主题

    31

    帖子

    0

    精华

    高级会员

    Rank: 12Rank: 12Rank: 12

    学币
    276
    荣耀
    0
    rank
    0
    违规
    0

      发表于 2021-3-27 12:21:02 | 显示全部楼层
      每天都有1次免费给楼主评分送学币的机会!
      学逆向论坛-免费的逆向学习论坛

      0

      主题

      1

      帖子

      0

      精华

      初级会员

      Rank: 4

      学币
      0
      荣耀
      0
      rank
      0
      违规
      0

        发表于 2021-4-23 11:42:51 | 显示全部楼层

        每天都有1次免费给楼主评分送学币的机会!zdm
        学逆向论坛-免费的逆向学习论坛

        1

        主题

        21

        帖子

        0

        精华

        初级会员

        Rank: 4

        学币
        5
        荣耀
        0
        rank
        0
        违规
        0

          发表于 2021-4-23 14:43:37 | 显示全部楼层
          感谢分享,我会认真学习的!
          学逆向论坛-免费的逆向学习论坛
          关闭

          论坛公告上一条 /1 下一条

          快速回复 返回顶部 返回列表