逆向扫雷(2)

本文介绍了如何通过编程实现扫雷游戏的自动化,包括获取游戏窗口句柄,读取进程内存,计算雷区坐标,遍历并模拟鼠标点击。详细步骤包括查找内存中的宽度和高度,使用CE搜索立即数地址,以及通过PostMessage发送点击消息。

紧接着 扫雷(1)博客,我们接着说,接下来我们需要找到存放宽和高的内存地址,一般肯定也是立即数地址,这种小游戏不会存在那种太难的偏移。

第一步:拿到游戏的窗口句柄:

HWND hwnd = ::FindWindowA("扫雷", "扫雷");//获取游戏的窗口句柄

第二步:通过窗口句柄拿到进程ID:

GetWindowThreadProcessId(hwnd, &pid);

这个pid是上面提前定义好的DWORD类型

第三步:通过进程ID拿到进程句柄:

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)

第四步:随便找个基地址读取一下,观察进程是否存在(打开):

这里的0x1005361是通过CE观察出来的,大家可以自行尝试

if (!ReadProcessMemory(hProcess, (LPCVOID)0x1005361, &gamedata, 32 * 24, &pid))
	{
		::MessageBoxA(NULL, "扫雷游戏未打开", "错误", MB_OK);
		return 0;
	}

第五步(综合讲解所需要的一些值计算):把每格的大小,以及坐标利用VS自带的SPY++计算一下:

a.这里我算出来是(20,60);

short gamex = 20;
short gamey = 60;

每格大小的话,利用两个点计算一下就好,这里用图表示一下:
在这里插入图片描述
这是点击了雷区的左上角

在这里插入图片描述
这是点击了雷区的右上角

然后两个相减,最后再来除以格子数量,就可以得到长度了,既可以找到鼠标点击的位置坐标。

b.至于雷区的宽度和高度利用CE立即数搜索就可以搜索到它们的立即数地址,即不变的地址,就可以找到

c.通过观察,高度最大可以24,长度最大可为32,如何判断终止的地方呢?
在这里插入图片描述
通过观察,遇到0x10后它就停止了一行的有效区,这就是扫雷模式的改变,雷区所放的地方是不变的,它只需要改变一下一行结尾。每行的起始位置是不变的,它只改变终止位置。这就改变了扫雷模式的宽度和高度。所以一行的长度是不变的,如何控制呢?我们只需要找到0x10地点,高度呢?高度只需要读取一下CE里面找出来的立即数不就行喽,思路就是这样

根据上述我们即可 初始化一个雷区数组unsigned char gamedata[24][32] = { 0 };

第六步:遍历之后判断并用鼠标点击:

	unsigned short xypos[2] = { 0 };

	for (int i = 0; i < dwHight;++i) {
		for (int j = 0; j < 32;++j) {
			if (0x10 == gamedata[i][j])
				break;

			xypos[0] = gamex + j * 16;
			xypos[1] = gamey + i * 16;
			if (0x8F != gamedata[i][j]) {
				::PostMessage(hwnd, WM_LBUTTONDOWN,MK_LBUTTON,*(int *)xypos);
				::PostMessage(hwnd, WM_LBUTTONUP,0, *(int*)xypos);
			}
		}
	}

完整代码如下

阅读下面的c++代码需要一点点的windows核心编程基础,扫雷程序的话可以参照模仿一下就行

#include<iostream>
#include<windows.h>
using namespace std;
int main() {
	DWORD pid;
	HWND hwnd = ::FindWindowA("扫雷", "扫雷");//获取游戏的窗口句柄
	if (NULL == hwnd)
	{
		MessageBoxA(NULL, "扫雷游戏未打开", "错误", MB_OK);
		return 0;
	}

	GetWindowThreadProcessId(hwnd, &pid);//通过窗口句柄拿到进程ID
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);//通过进程ID拿到进程句柄
	if (NULL == hProcess) {

		::MessageBoxA(NULL, "扫雷游戏未打开","错误", MB_OK);
		return 0;
	}

//扫雷基址:0x1005361  炸弹:0x8F
//扫雷的高  0x01005338
//扫雷的宽  0x01005334

	unsigned char gamedata[24][32] = { 0 };
	if (!ReadProcessMemory(hProcess, (LPCVOID)0x1005361, &gamedata, 32 * 24, &pid))
	{
		::MessageBoxA(NULL, "扫雷游戏未打开", "错误", MB_OK);
		return 0;
	}

	DWORD dwHight = 0;
	if (!ReadProcessMemory(hProcess, (LPCVOID)0x01005338, &dwHight, sizeof(dwHight), &pid))
	{
		::MessageBoxA(NULL, "读取扫雷进程未打开", "错误", MB_OK);
		return 0;
	}
	short gamex = 20;
	short gamey = 60;
	unsigned short xypos[2] = { 0 };

	for (int i = 0; i < dwHight;++i) {
		for (int j = 0; j < 32;++j) {
			if (0x10 == gamedata[i][j])
				break;

			xypos[0] = gamex + j * 16;
			xypos[1] = gamey + i * 16;
			if (0x8F != gamedata[i][j]) {
				::PostMessage(hwnd, WM_LBUTTONDOWN,MK_LBUTTON,*(int *)xypos);
				::PostMessage(hwnd, WM_LBUTTONUP,0, *(int*)xypos);
			}
		}
	}

	cout << "彪哥出品,必为废品";
	CloseHandle(hProcess);
}

运行结果图:

在这里插入图片描述
在这里插入图片描述
前三步总结也就是
通过窗口的标题拿到窗口的句柄,FindWindowA
然后通过窗口的句柄拿到进程的IDGetWindowThreadProcessId
最后通过进程的ID拿到进程的句柄
OpenProcess

插入一个提示(vs如何打开MSDN帮助文档)

比如要查看float的特征。
在vs中输入float,然后按F1键,会在默认浏览器上打开帮助文档。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻梦&之璐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值