C++实现一秒扫雷
调试分析阶段:
- 在内存中寻找行、列、雷区数据(具体可看参考文章2)
- 分析雷区数据格式
雷区一行为32byte,正常区域由0x0f(无雷)、0x8f(雷)组成,不可点击区域由0x10围成
雷区大小为3*16*16byte,处理时要把不可点击区域删去。
编程阶段:
代码流程和关键API函数:
1.根据pid打开进程
OpenProcess
2.检测内存是否可读
VirtualQueryEx
3.根据调试出的行、列、雷数基址,读取内存,获取数据
ReadProcessMemory
4.读取雷区数据
5.处理雷区数据,得到雷的坐标
6.根据雷的坐标,模拟点击无雷位置(此处偷懒使用spy++获取窗口坐标baseX、baseY,完成品应该自动获取扫雷窗口坐标)
mouse_event
效果如下:
详情请见代码:
#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