C++实现一秒扫雷

调试分析阶段:

  1. 在内存中寻找行、列、雷区数据(具体可看参考文章2)
  2. 分析雷区数据格式

雷区一行为32byte,正常区域由0x0f(无雷)、0x8f(雷)组成,不可点击区域由0x10围成

雷区大小为3*16*16byte,处理时要把不可点击区域删去。

 

编程阶段:

代码流程和关键API函数:

1.根据pid打开进程

OpenProcess

2.检测内存是否可读

VirtualQueryEx

3.根据调试出的行、列、雷数基址,读取内存,获取数据

ReadProcessMemory

4.读取雷区数据

5.处理雷区数据,得到雷的坐标

6.根据雷的坐标,模拟点击无雷位置(此处偷懒使用spy++获取窗口坐标baseX、baseY,完成品应该自动获取扫雷窗口坐标)

mouse_event

 

效果如下:

C++实现一秒扫雷

详情请见代码:

#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <cstdlib>
#include "conio.h"
using namespace std;

const unsigned char MINE = 0x8f;//地雷
const unsigned char SAFE_AREA = 0x0f;//安全位置
const unsigned char WALL = 0x10;//墙壁
const unsigned int row_addr = 0x1005338;
const unsigned int col_addr = 0x1005334;
const unsigned int num_addr = 0x1005330;
const unsigned int area_addr = 0x1005361;//雷区最大就是5361-5661,3*16*16

static int getValue(HANDLE hProcess, int pos) {
	byte pvBufferLocal;
	DWORD dwNumberOfBytesRead;
	if (ReadProcessMemory(hProcess, (LPCVOID)pos, &pvBufferLocal, sizeof(byte), &dwNumberOfBytesRead) != 0) {
		return (byte)pvBufferLocal;
	}
	return 0;
}

BOOL searchMem(DWORD pid) {
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //以最大的权限打开进程句柄
	if (NULL == hProcess)
		return FALSE;
	PBYTE pAddress = NULL;
	MEMORY_BASIC_INFORMATION mbi;
	if (VirtualQueryEx(hProcess, pAddress, &mbi, sizeof(mbi)) != sizeof(mbi)) {
		return FALSE;
	}
	int row, col, num;
	row = getValue(hProcess, row_addr);
	col = getValue(hProcess, col_addr);
	num = getValue(hProcess, num_addr);
	printf("行数:%d\n", row);
	printf("列数:%d\n", col);
	printf("个数:%d\n", num);
	if ((row > 0) && (col > 0) && (num > 0)) {
		byte* pvBufferLocal = new byte[3 * 16 * 16];
		DWORD dwNumberOfBytesRead;
		char* temp = new char[row*col];
		if (ReadProcessMemory(hProcess, (LPCVOID)area_addr, pvBufferLocal, sizeof(byte) * 3 * 16 * 16, &dwNumberOfBytesRead) != 0) {
		}
		else {
			cout << "read error" << endl;
		}
		int index = 0;
		int offset = 32 - col;
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				temp[i*col + j] = pvBufferLocal[index];
				if (j == col - 1) {
					index += offset;
				}
				index++;
			}
		}
		printf("\n");
		int baseX = 39;
		int baseY = 2;
		int firstX = baseX + 30;
		int firstY = baseY + 160;
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				int xloc = firstX + j * 24;
				int yloc = firstY + i * 24;
				if ((i == 0) && (j == 0)) {
					mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (xloc) * 65536 / 1920, (yloc) * 65536 / 1080, 0, 0);
				}
				if ((unsigned char)temp[i*col + j] != 0x8f) {
					mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (xloc) * 65536 / 1920, (yloc) * 65536 / 1080, 0, 0);
				}
			}
		}
		delete[]pvBufferLocal, temp;
	}
	CloseHandle(hProcess);
	return TRUE;
}

int main() {
	unsigned int pid = 0;
	printf("你的扫雷游戏pid是?:");
	scanf_s("%d", &pid);
	if (NULL == pid || 0 == pid) {
		printf("错误的pid\n");
		return 1;
	}
	searchMem(pid);
	return 0;
}

参考感谢:

代码框架:

https://www.52pojie.cn/thread-536250-1-1.html

动态调试和分析:

https://www.cnblogs.com/iBinary/p/7533292.html

鼠标坐标:

https://blog.****.net/qq_35409640/article/details/75212236

像素点定位:

http://www.cppblog.com/ArthasLee/archive/2011/07/24/151762.html