逆向分析-扫雷

例如:系统环境:win10 64位  

工具:吾爱专用OD、CE、代码注入器

1.分别以两种方式写辅助工具,MFC的辅助工具,DLL注入补丁模式的辅助工具。
2.功能:暂停时间、无限插旗数量、根据鼠标坐标得知是否有雷、一键获胜、一键镖旗、更改初级难度、中级难度、高级难度。

2.具体分析过程
2.1 暂停时间
2.1.1 CE找基址
先用CE找基址,点击一块区域发现时间会变动,用CE找精确的4字节数值,找到之后发现是绿色的基址。

时间基址:0x0100579C

CE右键->找出是什么改写了这个地址

->双击这条汇编指令

这个指令是让时间自增的,记住0x002FF5这个地址。
然后写代码把这里nop掉。

2.1.2 找到时间基址后写代码
以下是MFC内部button的代码

//改变时间的代码的基址  
2.	    DWORD dwTime = 0x01002FF5;  
3.	    DWORD dwRealSize = 0;  
4.	    DWORD pid = 0;  
5.	    //获取游戏窗口句柄  
6.	    HWND hWnd = ::FindWindow(NULL, L"扫雷");  
7.	    if (hWnd == NULL)  
8.	    {  
9.	        MessageBox(L"没有找到扫雷游戏进程", L"错误", 0);  
10.	        return;  
11.	    }  
12.	    //通过窗口句柄得到进程ID  
13.	    DWORD dwThreadPid = GetWindowThreadProcessId(hWnd, &pid);  
14.	    //通过进程ID得到进程句柄  
15.	    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);  
16.	    BYTE* nopCode = new BYTE[6];  
17.	    for (size_t i = 0; i < 6; i++)  
18.	    {  
19.	        nopCode[i] = 0x90;  
20.	    }  
21.	    WriteProcessMemory(hProcess, (LPVOID)dwTime, nopCode, 6, &dwRealSize);  
22.	    CloseHandle(hProcess);  

2.2 镖旗次数无限
跟修改时间一样,用CE找到基址,再把修改镖旗数的代码修改。

2.3 切换难度初级、中级、高级
首先把扫雷拖进OD,跑起来,点击上方一排H小按钮,右键刷新来到这里

选择父窗口是Topmost,标题是扫雷的这栏,右键跟随ClassProc
来到这里

这里就是窗口的回调函数
关键在于我知道这是SDK程序
把扫雷拖进PEID分析,而且这个程序很小,所以判断是一个SDK程序
窗口回调函数原型是

	//窗口消息回调函数  
2.	LRESULT CALLBACK WindowProc (  
3.	    HWND hwnd,  
4.	    UINT uMsg,  
5.	    WPARAM wParam,  
6.	    LPARAM IParam  
7.	);  

当窗口点击菜单的时候回发送
uMsg == WM_COMMAND消息,wParam是对应的菜单ID
图下,在这里下一个条件断点,
ebx == WM_COMMAND
右键->断点->条件断点 ebx== WM_COMMAND

跑起来,切换一下难度试一下
断下来了
ALT+K看一下堆栈的参数

哇哦,参数都被我看光光了哎
WM_COMMAND消息,窗口句柄也看到,菜单ID也看到了,但是这里显示的是10进制的
16进制应该是0x20B

我写了如下代码,尝试用注入器,注入一下试试,看能不能改变难度

//以下是汇编指令 扫雷调用窗口消息的函数  
2.	//菜单触发WM_COMMAND消息等会再回头看  
3.	push 0          //0          IParam  
4.	push 0x209      //菜单消息宏   wParam  
5.	push 0x111      //宏         uMsg  
6.	push 0x00D07AC  //窗口句柄    hwnd  
7.	call 0x01001BC9 //函数基址   LRESULT CALLBACK WindowProc  

成功~
写MFC代码吧,我就贴部分代码

1.	HWND hWnd = ::FindWindow(NULL, L"扫雷");  
2.	if (hWnd == NULL)  
3.	{  
4.	    MessageBox(L"没有找到扫雷游戏进程", L"错误", 0);  
5.	    return;  
6.	}  
7.	::SendMessage(hWnd, WM_COMMAND, 0x20B, 0);  
8.	return;  

2.4 一键获胜
得先找到雷区的基址,用CE找呗,也不知道是什么类型,先找字节类型呗
字节类型,为知的初始值,变动的数值,不变动的数值,一直搜索
找到结果如下

那我就去0x01005361这个地址去看一下,前两个都是栈里的值,先不管
而且0x01005361是个绿颜色的是基址,哟,在OD里果然是雷区数组的基址

什么OF、8F、10
测试得到
8F是雷、0F是可以点击的、10是墙壁

写代码之前先把雷区的高宽得到,切换难度然后用CE找
宽基地址:0x01005334
高基地址:0x01005338

我要发送鼠标点击,先用spy++获取到x、y的坐标
监视->日志消息->选中窗口->消息->WM_LBUTTONDOWN
点击第一个方块

X坐标:23
Y坐标:60

然后测量出方块的宽度为16

写代码:

1.		DWORD pid = 0;  
2.	    //获取游戏窗口句柄  
3.	    HWND hWnd = ::FindWindow(NULL, L"扫雷");  
4.	    if (hWnd == NULL)  
5.	    {  
6.	        MessageBox(L"没有找到扫雷游戏进程", L"错误", 0);  
7.	        return;  
8.	    }  
9.	    //通过窗口句柄得到进程ID  
10.	    DWORD dwThreadPid = GetWindowThreadProcessId(hWnd, &pid);  
11.	    //通过进程ID得到进程句柄  
12.	    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);  
13.	    //雷区数据基地址       0x01005361        
14.	    //宽       基地址       0x01005334  
15.	    //高       基地址       0x01005338  
16.	    //每个雷格距离 为16  
17.	    //0x8F是雷  
18.	    unsigned char gamedata[24][32] = { 0 };  
19.	  
20.	    BOOL bRead_If_Success =  
21.	        ReadProcessMemory(hProcess,  
22.	            (LPVOID)0x01005361,  
23.	            &gamedata,  
24.	            24 * 32,  
25.	            &pid  
26.	        );  
27.	  
28.	    if (bRead_If_Success == 0)  
29.	    {  
30.	        MessageBox(L"读取扫雷游戏内存失败", 0, 0);  
31.	        return;  
32.	    }  
33.	  
34.	    //数据处理  
35.	    DWORD dwHight = 0;  
36.	  
37.	    bRead_If_Success =  
38.	        ReadProcessMemory(hProcess,  
39.	            (LPVOID)0x01005338,  
40.	            &dwHight,  
41.	            sizeof(dwHight),  
42.	            &pid  
43.	        );  
44.	    //数据处理  
45.	    DWORD dwWidth = 0;  
46.	  
47.	    bRead_If_Success =  
48.	        ReadProcessMemory(hProcess,  
49.	            (LPVOID)0x01005334,  
50.	            &dwWidth,  
51.	            sizeof(dwWidth),  
52.	            &pid  
53.	        );  
54.	  
55.	  
56.	    if (bRead_If_Success == 0)  
57.	    {  
58.	        MessageBox(L"读取扫雷游戏内存失败", 0, 0);  
59.	        return;  
60.	    }  
61.	  
62.	  
63.	    short gamex = 20;  
64.	    short gamey = 60;  
65.	    unsigned short xypos[2] = { 0 };  
66.	  
67.	    for (size_t i = 0; i < dwHight; i++)  
68.	    {  
69.	        for (size_t j = 0; j < dwWidth; j++)  
70.	        {  
71.	            if (0x10 == gamedata[i][j])  
72.	            {  
73.	                break;  
74.	            }  
75.	            xypos[0] = gamex + j * 16;  
76.	            xypos[1] = gamey + i * 16  ;  
77.	            if (0x8F != gamedata[i][j])  
78.	            {  
79.	                ::PostMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, *(int*)xypos);  
80.	                ::PostMessage(hWnd, WM_LBUTTONUP, 0, *(int*)xypos);  
81.	            }  
82.	        }  
83.	    }  
84.	    CloseHandle(hProcess);  

2.5 一键镖旗
方法1:把一键获胜的代码改一下,是雷就发送右键按下、弹起消息,就好了。
方法2:把雷区的内存写入0x8E,但是写完之后要把扫雷最小化,最大化,才能看见。

2.6 根据鼠标坐标判断是不是雷
这里就必须要用到DLL注入的技术了
写一个MFC的静态DLL 在初始化函数BOOL CSaoLeiDLLApp::InitInstance()里面写东西

在我的回调函数里面写我的东西
比如 if (Msg == WM_MOUSEMOVE)
原理就是不断获取鼠标消息,得到鼠标坐标,计算变成雷区数组的下标,是雷就把标题改成有雷
如下图:

代码如下:

1.	扫雷新的回调函数  
2.	LRESULT CALLBACK MyWindowProc(  
3.	    _In_ HWND hWnd,  
4.	    _In_ UINT Msg,  
5.	    _In_ WPARAM wParam,  
6.	    _In_ LPARAM lParam)  
7.	{  
8.	    //一键秒杀  
9.	    if (Msg == WM_KEYDOWN && wParam == VK_F5)  
10.	    {  
11.	        DWORD width = *g_pWidth;  
12.	        DWORD height = *g_pHeight;  
13.	        for (size_t i = 0; i < height; i++)  
14.	        {  
15.	            for (size_t j = 0; j < width; j++)  
16.	            {  
17.	                BYTE code = *(BYTE*)(g_GameArray + i * 0x20 + j);  
18.	                if (code == 0x10)  
19.	                {  
20.	                    break;  
21.	                }  
22.	                if (code != 0x8F )  
23.	                {  
24.	                    WORD x = 20 + j * 16;  
25.	                    WORD y = 60 + i * 16 ;  
26.	                    DWORD dwPos = MAKELONG(x, y);  
27.	                    PostMessage(g_hWnd, WM_LBUTTONDOWN, MK_LBUTTON, (LPARAM)dwPos);  
28.	                    PostMessage(g_hWnd, WM_LBUTTONUP, MK_LBUTTON, (LPARAM)dwPos);  
29.	                }  
30.	            }  
31.	        }  
32.	    }  
33.	  
34.	    //鼠标坐标  
35.	    else if (Msg == WM_MOUSEMOVE)  
36.	    {  
37.	        //首先获取x和y轴的坐标  
38.	        WORD xPos = GET_X_LPARAM(lParam);  
39.	        WORD yPos = GET_Y_LPARAM(lParam);  
40.	        xPos = (xPos - 12) / 16;  
41.	        yPos = (yPos - 55) / 16;  
42.	        if (xPos + yPos <= width + height)  
43.	        {  
44.	            CString titleText;  
45.	            if (*(g_GameArray + xPos + yPos * 0x20) == 0x8F )  
46.	            {  
47.	                titleText.Format(L"扫雷 x:%d,y:%d,有雷   F5:一键秒杀", xPos + 1, yPos + 1);  
48.	            }  
49.	            else  
50.	            {  
51.	                titleText.Format(L"扫雷 x:%d,y:%d,没雷   F5:一键秒杀", xPos + 1, yPos + 1);  
52.	            }  
53.	            ::SetWindowText(g_hWnd, titleText);  
54.	        }  
55.	        else  
56.	        {  
57.	            CString titleText;  
58.	            titleText.Format(L"扫雷 辅助工具by CSDN博客:简单起个名字");  
59.	            ::SetWindowText(g_hWnd, titleText);  
60.	        }  
61.	    }  
62.	    return CallWindowProc(g_OdlProc, hWnd, Msg, wParam, lParam);  
63.	}  

3.总结
我自己写了一个MFC的辅助工具,然后看官方视频写了一个注入的DLL
总共是两个工程。
一个小小的扫雷游戏,麻雀虽小但五脏俱全,练手的第一个项目。要比较熟练的会使用OD、CE工具,还要懂得Windows编程的API,还要会注入技术,要会读写内存的技术。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值