-
涉及相关API函数
SendMessage
PostMessage
FindWindow
GetWindowThreadProcessId
OpenProcess
ReadProcessMemory
-
涉及相关工具
CE
Spy++
VC++ 6.0
-
步骤
1.用CE分析棋盘基址及其宽、高;
①基址: 通过CE以字节数值类型反复“未知初始化数值”、“更改过的数值”和“未更改过的数值”扫描内存,对扫描结果逐一进行过滤,查看滤后结果的内存与实际游戏界面显示情况是否对应,由此进行判定基址;
②宽、高:通过CE直接搜索对应宽度、高度,然后调整扫雷级别(即雷面的大小),再次用CE,通过“大于...”、“小于...”对宽高进行扫描,从而判定宽、高在内存中的地址;
- 附:CE扫描结果
2.用CE查看棋盘对应内存中数据的意义;
- 状态:查看非雷、未翻开的雷(标记、未标记的雷)、翻开的雷(失败后游戏自动翻开)等几种状态对应内存中的十六进制数值;
状态 | 十六进制数值 |
---|---|
未翻开的雷 | 0x8F |
翻开的雷 | 0x8A |
结束标识 | 0x10 |
- 棋盘坐标:通过Spy++ 监控winmine.exe的鼠标左键按下、松开消息,查看对应棋盘左上角坐标及每格的长、宽;
棋盘坐标 | 十进制数值 |
---|---|
横坐标X | 10 |
纵坐标Y | 53 |
PS:对于棋盘的左上角坐标大致即可,使用时,需要在此基础上加上一个偏移,以使鼠标模拟时能点击到小格内区域;
每小格 | 十进制数值 |
---|---|
长 | 16 |
宽 | 16 |
3.编制扫雷游戏辅助;
1.辅助源码
2.主要部分SweeperDlg.cpp
unsigned char gameData[24][32]; //ReadProcessMemory
#define gamex 10 + 8
#define gamey 53 + 8
void CSweeperDlg::OnButtonReaddata()
{
// TODO: Add your control notification handler code here
// TODO: Add your control notification handler code here
HWND hwnd = ::FindWindow(NULL,"扫雷");
if (hwnd == 0)
{
::MessageBox(0,"游戏未打开",0,MB_OK);
return;
}
//1005194
DWORD pid;
GetWindowThreadProcessId(hwnd,&pid);
//GetWindowThreadProcessId
//OpenProcss
HANDLE hp=OpenProcess(PROCESS_ALL_ACCESS,false,pid);
//
if (hp==NULL)
{
::MessageBox(0,"打开进程出错",0,MB_OK);
return;
}
ReadProcessMemory(hp,(LPCVOID)0x1005361,gameData,32*24,&pid);
int high = 0;
ReadProcessMemory(hp,(LPCVOID)0x1005338,&high,4,&pid);//字节大小可为1,2等,若不为int大小,应对high进行初始化置0;
m_gameData.Empty();
CString strGameData;
WORD yx[2] = {0,0};
for (int y = 0 ; y < high ; y++)
{
for (int x = 0 ; x <32 ; x++)
{
//对于后续无意义的字节直接略掉;
if (gameData[y][x] == 0x10)
break;
//格式化
strGameData.Format("%x,",gameData[y][x]);
//统一占两位显示
if (strlen(strGameData) == 2)
strGameData = "0" + strGameData;
m_gameData += strGameData;
}
//一排结束换行
m_gameData += "\r\n";
}
UpdateData(false);
}
void CSweeperDlg::OnButtonSweeper()
{
// TODO: Add your control notification handler code here
HWND hwnd = ::FindWindow(NULL,"扫雷");
if (hwnd == 0)
{
::MessageBox(0,"游戏未打开",0,MB_OK);
return;
}
//1005194
DWORD pid;
GetWindowThreadProcessId(hwnd,&pid);
//GetWindowThreadProcessId
//OpenProcss
HANDLE hp=OpenProcess(PROCESS_ALL_ACCESS,false,pid);
//
if (hp==NULL)
{
::MessageBox(0,"打开进程出错",0,MB_OK);
return;
}
ReadProcessMemory(hp,(LPCVOID)0x1005361,gameData,32*24,&pid);
int high = 0;
ReadProcessMemory(hp,(LPCVOID)0x1005338,&high,4,&pid);//字节大小可为1,2等,若不为int大小,应对high进行初始化置0;
CString strGameData;
for (int y = 0 ; y < high ; y++)
{
for (int x = 0 ; x <32 ; x++)
{
//对于后续无意义的字节直接略掉;
if (gameData[y][x] == 0x10)
break;
//不是雷就挖开
/*
point[0] = 14+6;
point[1] = 56+6;
*/
if (gameData[y][x] != 0x8f)
{
WORD yx[2];
yx[0] = gamex + x*16;
yx[1] = gamey + y*16;
::PostMessage(hwnd,WM_LBUTTONDOWN,1,*(int*)yx);
::PostMessage(hwnd,WM_LBUTTONUP,0,*(int*)yx);
}
}
}
}
void CSweeperDlg::OnButtonClick()
{
// TODO: Add your control notification handler code here
WORD point[2];
HWND hwnd = ::FindWindowA("扫雷",NULL);
if (hwnd == 0)
{
return ;
}
point[0] = 14+6;
point[1] = 56+6;
::SendMessage(hwnd,WM_LBUTTONDOWN,1,*(int*)point);
::SendMessage(hwnd,WM_LBUTTONUP ,0,*(int*)point);
}