1. Introducción
Buscaminas es un juego de Windows muy conocido y ampliamente entretenido como un juego clásico. Entonces, ¿es posible crear un pequeño programa que elimine minas y niveles automáticamente a la velocidad de la luz? La respuesta es sí. Incluso puede llegar al punto de ser invencible. Primero, la imagen de arriba establece el récord en -8 segundos, por lo que nadie puede romper este récord.
2. Método de implementación
1. Implementar lógica
1 ) Cree un proyecto vacío WIN32 ;
2 ) Abra el proceso del buscaminas y obtenga los valores de filas y columnas de acuerdo con la dirección de memoria de las filas y columnas del campo minado;
3 ) Leer los datos del campo minado de acuerdo con la primera dirección del campo minado y el alcance del campo minado;
4 ) Coloque la interfaz del juego Buscaminas en la parte superior, lea el valor en la fila i y la columna j del campo minado y determine si es una mina. Si no, simule una operación de clic izquierdo del mouse. Si es una mina, simular una operación de clic derecho del mouse.
5 ) De acuerdo con el sistema de coordenadas del cliente de la interfaz del juego Buscaminas, calcule la posición de las coordenadas de la i-ésima fila y la j-ésima columna, luego mueva el mouse a esta posición y realice la operación del mouse en el paso 3 .
6 ) Repita 3-4 hasta que se escaneen todos los datos del campo minado.
2. API relacionadas
La función API de Windows utilizada para leer y escribir el contenido de la dirección de memoria es:
BOOL ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, TAMAÑO_T nTamaño, TAMAÑO_T *lpNumberOfBytesRead);
BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, TAMAÑO_T nTamaño, TAMAÑO_T *lpNumberOfBytesWritten);
La función API de memoria de lectura y escritura requiere el identificador del proceso de destino como parámetro, para ello es necesario llamar a OpenProcess para obtener el identificador del proceso:
HANDLE OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
OpenProcess requiere el ID del proceso como parámetro, que se puede obtener mediante la función GetWindowThreadProcessId .
El numero es:
DWORD GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId);
La función GetWindowThreadProcessId requiere el identificador de la ventana como parámetro, que se puede obtener a través de FindWindow . Los dos parámetros de la clase de ventana y el nombre del título de la ventana utilizados por esta función API se obtuvieron a través de Spy++ en el paso 7 .
HWND FindWindowA(LPCSTR lpClassName, LPCSTR lpWindowName);
La función API para simular las operaciones del mouse es mouse_event . El método de uso específico es usar la función API SetCursorPos para mover el mouse a la posición especificada y luego simular la presión del mouse y las operaciones emergentes.
La función se declara de la siguiente manera:
void mouse_event(DWORD dwFlags, DWORD dx, DWORD dy, DWORD dwData, ULONG_PTR dwExtraInfo);
BOOL SetCursorPos(int X, int Y);
En este experimento, la función mouse_event solo necesita configurar el primer parámetro, que incluye específicamente presionar con el botón izquierdo y la ventana emergente ( MOUSEEVENTF_LEFTDOWN , MOUSEEVENTF_LEFTUP ), presionar con el botón derecho y la ventana emergente ( MOUSEEVENTF_RIGHTDOWN , MOUSEEVENTF_RIGHTUP ).
La función API para colocar una interfaz de programa GUI en la parte superior es:
BOOL SetForegroundWindow(HWND hWnd);
3. Implementación del guión
Determinar el tamaño de la cuadrícula del juego Buscaminas.
Abra Spy++ , haga clic en " Monitor - Registrar mensaje " , marque " Ocultar opciones de Spy++ " y arrastre la " Herramienta de búsqueda de programa " a la interfaz principal del juego Buscaminas, como se muestra en la Figura 8-4 . El contenido mostrado incluye el Buscaminas. nombre de la clase de ventana ( WNDCLASS ) y nombre del título.
Cambie a la ventana " Mensaje " de Spy++ , primero borre todos los mensajes y luego verifique los dos mensajes "WM_LBUTTONDOWN y WM_LBUTTONUP" , como se muestra en la Figura 8-5 . Haga clic en Aceptar para regresar a la interfaz principal de Spy++ .
Regrese a la interfaz del juego Buscaminas, haga clic en las tres cuadrículas en la esquina superior izquierda, inmediatamente a la derecha de la esquina superior izquierda e inmediatamente debajo de la esquina superior izquierda. Verifique los valores xPos e yPos registrados por Spy++ cuando el mouse se hace clic y calcula el ancho y la suma de cada cuadrícula.
Las coordenadas del primer campo minado son:
xPos =14
yPos =58
En la interfaz de Spy++, haga clic con el botón derecho en Borrar registro de mensajes y luego repita la operación anterior. También puede hacer clic en varias cuadrículas para calcular el valor promedio de cada cuadrícula como el valor de ancho y alto de cada cuadrícula. El ancho de la cuadrícula es: 16 ; la altura de la cuadrícula es: 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;
}