学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

450

积分

0

好友

15

主题
楼主
发表于 2021-1-19 21:54:13 | 查看: 29627| 回复: 8
目标功能:
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
能否将代码上传附件?
Print动 发表于 2021-1-20 08:49 详情  回复
源码已上传
板凳
发表于 2021-1-20 08:49:13
roger 发表于 2021-1-20 00:37
能否将代码上传附件?

源码已上传

回复 只看该作者 道具 举报 打印

    地板
    发表于 2021-1-22 09:58:32
    写的很详细,支持一下。

    回复 只看该作者 道具 举报 打印

      5#
      发表于 2021-3-27 12:21:02
      每天都有1次免费给楼主评分送学币的机会!

      回复 只看该作者 道具 举报 打印

        6#
        发表于 2021-4-23 11:42:51

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

        回复 只看该作者 道具 举报 打印

        7#
        发表于 2021-4-23 14:43:37
        感谢分享,我会认真学习的!

        回复 只看该作者 道具 举报 打印

          8#
          发表于 2025-8-15 00:09:12
          tql大佬

          回复 只看该作者 道具 举报 打印

            9#
            发表于 3 天前
            高手这很适合小白学习很有乐趣

            回复 只看该作者 道具 举报 打印

            小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

            GMT+8, 2025-9-15 23:41 , Processed in 7.525861 second(s), 80 queries .

            Powered by Discuz! X3.4

            Copyright © 2001-2021, Tencent Cloud.

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