1 はじめに
マインスイーパは Windows の有名なゲームであり、古典的なゲームとして広く楽しまれていますが、地雷を自動的に除去し、光速でレベルをクリアする小さなプログラムを作成することは可能でしょうか? 答えは「はい」です。まず、上の写真では記録が -8 秒に設定されているため、誰もこの記録を破ることはできません。
2. 実施方法
1. ロジックの実装
1 ) WIN32空のプロジェクトを作成します。
2 ) マインスイーパプロセスを開き、地雷原の行と列のメモリアドレスに従って行と列の値を取得します。
3 ) 地雷原の最初のアドレスと地雷原の範囲に従って地雷原データを読み取ります。
4 ) マインスイーパーのゲームインターフェースを最上部に置き、地雷原の i 行 j 列の値を読み取り、地雷かどうかを判断します。地雷でない場合は、マウスの左クリック操作をシミュレートします。地雷の場合は、マウスの右クリック操作をシミュレートします。
5 ) マインスイーパ ゲーム インターフェイスの顧客座標系に従って、i 行目、j 列目の座標位置を計算し、この位置にマウスを移動し、手順3のマウス操作を実行します。
6 )すべての地雷原データがスキャンされるまで3 ~ 4を繰り返します。
2. 関連する API
メモリ アドレスの内容の読み取りおよび書き込みに使用されるWindows API関数は次のとおりです。
BOOL ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead);
BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten);
読み取りおよび書き込みメモリAPI関数には、パラメータとしてターゲット プロセスのハンドルが必要です。この目的のために、OpenProcess を呼び出してプロセス ハンドルを取得する必要があります。
HANDLE OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
OpenProcess にはパラメータとしてプロセスIDが必要で、これはGetWindowThreadProcessId関数を通じて取得できます。
番号は次のとおりです。
DWORD GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId);
GetWindowThreadProcessId関数には、 FindWindowを通じて取得できるウィンドウ ハンドルがパラメータとして必要です。このAPI関数で使用されるウィンドウ クラスとウィンドウ タイトル名の 2 つのパラメータは、手順7でSpy++を通じて取得されています。
HWND FindWindowA(LPCSTR lpClassName, LPCSTR lpWindowName);
マウス操作をシミュレートするAPI関数はmouse_eventであり、具体的な使用方法としては、SetCursorPos API関数を使用してマウスを指定位置に移動し、マウスのプレスやポップアップの動作をシミュレートします。
関数は次のように宣言されます。
void Mouse_event(DWORD dwFlags、DWORD dx、DWORD dy、DWORD dwData、ULONG_PTR dwExtraInfo);
BOOL SetCursorPos(int X, int Y);
この実験では、mouse_event関数は最初のパラメーターのみを設定する必要があります。これには、具体的には、左クリック押しとポップアップ ( MOUSEEVENTF_LEFTDOWN 、MOUSEEVENTF_LEFTUP )、右クリック押しとポップアップ ( MOUSEEVENTF_RIGHTDOWN 、MOUSEEVENTF_RIGHTUP ) が含まれます。
GUIプログラム インターフェイスを最上位に配置するAPI関数は次のとおりです。
BOOL SetForegroundWindow(HWND hWnd);
3. スクリプトの実装
マインスイーパー ゲームのグリッドのサイズを決定する
図8-4に示すように、 Spy++を開き、[監視--ログ メッセージ]をクリックし、[ Spy++オプションを非表示にする]をオンにして、[プログラム検索ツール]をマインスイーパ ゲームのメイン インターフェイスにドラッグします。表示されるコンテンツには、マインスイーパが含まれます。ウィンドウ クラス ( WNDCLASS ) 名とタイトル名。
Spy++の「メッセージ」ウィンドウに切り替え、最初にすべてのメッセージをクリアしてから、図8-5に示す2 つのメッセージ「WM_LBUTTONDOWNおよびWM_LBUTTONUP」を確認します。「OK」をクリックしてSpy++メイン インターフェイスに戻ります。
マインスイーパー ゲーム インターフェイスに戻り、左上隅、左上隅のすぐ右、左上隅のすぐ下の 3 つのグリッドをクリックし、マウスを押したときに Spy++ によって記録された xPos および yPos の値を確認します。をクリックして、各グリッドの幅と合計を計算します。
最初の地雷原の座標は次のとおりです。
xPos =14
yPos =58
Spy++ インターフェイスで、右クリック - [メッセージ ログのクリア] を選択し、上記の操作を繰り返します。また、いくつかのグリッドを離してクリックして、各グリッドの幅と高さの値として各グリッドの平均値を計算することもできます。グリッドの幅は16、グリッドの高さは16です。
#include <windows.h>
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
const char* text = "扫雷";
HWND hwnd = FindWindow(text, text);
if (hwnd == NULL)
{
MessageBox(NULL, TEXT("Winmine is not found!"), TEXT("AutoMineSweeper"), MB_ICONSTOP);
return 1;
}
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
int width, height;
width = -1;
height = -1;
ReadProcessMemory(handle, (LPVOID)0x01005334, &width, 4, NULL);
ReadProcessMemory(handle, (LPVOID)0x01005338, &height, 4, NULL);
/*BYTE **data;
data=(BYTE**)malloc(32 * height * sizeof(BYTE));*/
//BYTE data[] = new BYTE(32 * height);
BYTE data[32*32] = { 0 };
ReadProcessMemory(handle, (LPVOID)0x01005361, &data, 32 * height, NULL);
ShowWindow(hwnd, SW_RESTORE);
SetForegroundWindow(hwnd);
Sleep(300);
int width_num = 32;
int x_pos = 14; //雷区首个格子的位置
int y_pos = 58;
RECT rc;
BOOL bRet = GetClientRect(hwnd, &rc);
//更改扫雷时间,将其设置为-8
int time = -8;
WriteProcessMemory(handle, (LPVOID)0x0100579C, &time, 4, NULL);
for (int y = 1; y <= height; y++)
{
for (int x = 1; x <= width; x++)
{
UINT downMsg = x_pos, upMsg = y_pos;
if (data[width_num * (x - 1) + (y - 1)] == 0x10)
break;
else
{
if (data[(x - 1) + width_num * (y - 1)] == 0x0F)
{
downMsg = MOUSEEVENTF_LEFTDOWN;
upMsg = MOUSEEVENTF_LEFTUP;
}
else
{
downMsg = MOUSEEVENTF_RIGHTDOWN;
upMsg = MOUSEEVENTF_RIGHTUP;
}
}
POINT curPos = { rc.left + x_pos + (x - 1) * 16, rc.top + y_pos + (y - 1) * 16 };
ClientToScreen(hwnd, &curPos);
SetCursorPos(curPos.x, curPos.y);
mouse_event(downMsg, 0, 0, 0, 0);
mouse_event(upMsg, 0, 0, 0, 0);
Sleep(1);
}
}
CloseHandle(handle);
return 0;
}