reverse engineering - online - ¿Cómo puedo encontrar en la memoria la estructura de datos que representa el diseño de minas de Minesweeper?
como jugar buscaminas y ganar (10)
Estoy tratando de aprender sobre ingeniería inversa, usando Minesweeper como una aplicación de muestra. Encontré este artículo de MSDN sobre un simple comando de WinDbg que revela todas las minas pero es antiguo, no se explica en detalle y realmente no es lo que estoy buscando.
Tengo el desensamblador IDA Pro y el depurador WinDbg y he cargado winmine.exe en ambos. ¿Puede alguien proporcionar algunos consejos prácticos para cualquiera de estos programas en términos de encontrar la ubicación de la estructura de datos que representa el campo de minas?
En WinDbg puedo establecer puntos de interrupción, pero es difícil para mí imaginar en qué punto establecer un punto de interrupción y en qué ubicación de la memoria. De manera similar, cuando veo el código estático en IDA Pro, no estoy seguro de dónde comenzar a buscar la función o estructura de datos que representa el campo de la mina.
¿Hay algún ingeniero de reversa en Stackoverflow que pueda orientarme en la dirección correcta?
"En WinDbg puedo establecer puntos de interrupción, pero es difícil para mí imaginar en qué punto establecer un punto de interrupción y en qué ubicación de memoria. De manera similar, cuando veo el código estático en IDA Pro, no estoy seguro de dónde comenzar siquiera. para encontrar la función o estructura de datos que representa el campo de la mina ".
¡Exactamente!
Bueno, puedes buscar rutinas como random () que se llamarán durante la construcción de la tabla de minas. Este book me ayudó mucho cuando estaba experimentando con ingeniería inversa. :)
En general, los buenos lugares para establecer puntos de interrupción son llamadas a cuadros de mensajes, llamadas para reproducir un sonido, temporizadores y otras rutinas API de win32.
Por cierto, estoy escaneando dragaminas en este momento con OllyDbg .
Actualización: nemo me recordó una gran herramienta, Cheat Engine por Eric "Dark Byte" Heijnen.
Cheat Engine (CE) es una gran herramienta para observar y modificar el espacio de memoria de otros procesos. Más allá de esa facilidad básica , CE tiene características más especiales como ver la memoria desensamblada de un proceso e inyectar código en otros procesos.
(el verdadero valor de ese proyecto es que puedes descargar el código fuente -Delphi- y ver cómo se implementaron esos mecanismos; lo hice hace muchos años: o)
Parte 1 de 3
Si se toma en serio la ingeniería inversa, olvídese de los entrenadores y los motores de trucos.
Un buen ingeniero de ingeniería inversa debe conocer primero el sistema operativo, las funciones centrales del API, la estructura general del programa (qué es el ciclo de ejecución, las estructuras de Windows, las rutinas de manejo de eventos), el formato de archivo (PE). Los clásicos de Petzold, "Programación de Windows", pueden ser de ayuda (www.amazon.com/exec/obidos/ISBN=157231995X), así como MSDN en línea.
Primero debe pensar en dónde se puede llamar la rutina de inicialización del campo de minas. Pensé en seguir:
- Cuando lanzas el juego
- Cuando haces clic en la cara feliz
- Cuando haces clic en Juego-> Nuevo o presionas F2
- Cuando cambias la dificultad de nivel
Decidí verificar el comando acelerador F2.
Para encontrar el código de manejo del acelerador, debe encontrar el procedimiento de manejo de mensajes de ventana (WndProc). Puede rastrearse mediante llamadas CreateWindowEx y RegisterClass.
Leer:
- CreateWindowEx http://msdn.microsoft.com/en-us/library/ms632680%28VS.85%29.aspx
- RegisterClass http://msdn.microsoft.com/en-us/library/ms633586%28VS.85%29.aspx
- Capítulo 3 de Petzold "Windows y mensajes"
Abra IDA, Imports window, busque "CreateWindow *", salte a él y use el comando "Jump xref to operand (X)" para ver dónde se llama. Debería haber solo una llamada.
Ahora mire arriba para la función RegisterClass y su parámetro WndClass.lpfnWndProc. Ya he nombrado la función mainWndProc en mi caso.
.text:0100225D mov [ebp+WndClass.lpfnWndProc], offset mainWndProc
.text:01002264 mov [ebp+WndClass.cbClsExtra], edi
.text:01002267 mov [ebp+WndClass.cbWndExtra], edi
.text:0100226A mov [ebp+WndClass.hInstance], ecx
.text:0100226D mov [ebp+WndClass.hIcon], eax
.text:01002292 call ds:RegisterClassW
Presiona Enter en el nombre de la función (usa ''N'' para cambiarle el nombre a algo mejor)
Ahora eche un vistazo a
.text:01001BCF mov edx, [ebp+Msg]
Esta es la identificación del mensaje, que en el caso de presionar un botón F2 debe contener el valor WM_COMMAND. Debes encontrar dónde está comparado con 111h. Se puede hacer rastreando edx en IDA o estableciendo un punto de interrupción condicional en WinDbg y presionando F2 en el juego.
De cualquier manera conduce a algo así como
.text:01001D5B sub eax, 111h
.text:01001D60 jz short loc_1001DBC
Haga clic derecho en 111h y use "Constante simbólica" -> "Usar constante simbólica estándar", escriba WM_ y Enter. Ahora deberías tener
.text:01001D5B sub eax, WM_COMMAND
.text:01001D60 jz short loc_1001DBC
Es una manera fácil de descubrir los valores de identificación de mensaje.
Para comprender el manejo del acelerador, consulte:
- Usando los aceleradores de teclado
- Hacker de recursos ( http://angusj.com/resourcehacker/ )
Es un montón de texto para una sola respuesta. Si estás interesado, puedo escribir otro par de publicaciones. Larga historia corta campo de minas almacenado como una matriz de bytes [24x36], 0x0F muestra que el byte no se usa (jugando en un campo más pequeño), 0x10 - campo vacío, 0x80 - mío.
Parte 2 de 3
Ok, continuemos con el botón F2.
De acuerdo con el uso de los aceleradores de teclado cuando se presiona el botón F2 Función wndProc
... recibe un mensaje WM_COMMAND o WM_SYSCOMMAND. La palabra de orden bajo del parámetro wParam contiene el identificador del acelerador.
Bien, ya encontramos dónde se procesa WM_COMMAND, pero ¿cómo determinar el valor correspondiente del parámetro wParam? Aquí es donde entra en juego el http://angusj.com/resourcehacker/ . Aliméntalo con binario y te muestra todo. Me gusta la tabla de aceleradores.
texto alternativo http://files.getdropbox.com/u/1478671/2009-07-29_161532.jpg
Puede ver aquí que el botón F2 corresponde a 510 en wParam.
Ahora volvamos al código, que maneja WM_COMMAND. Compara wParam con diferentes constantes.
.text:01001DBC HandleWM_COMMAND: ; CODE XREF: mainWndProc+197j
.text:01001DBC movzx eax, word ptr [ebp+wParam]
.text:01001DC0 mov ecx, 210h
.text:01001DC5 cmp eax, ecx
.text:01001DC7 jg loc_1001EDC
.text:01001DC7
.text:01001DCD jz loc_1001ED2
.text:01001DCD
.text:01001DD3 cmp eax, 1FEh
.text:01001DD8 jz loc_1001EC8
Use el menú contextual o el atajo de teclado ''H'' para mostrar valores decimales y puede ver nuestro salto
.text:01001DBC HandleWM_COMMAND: ; CODE XREF: mainWndProc+197j
.text:01001DBC movzx eax, word ptr [ebp+wParam]
.text:01001DC0 mov ecx, 528
.text:01001DC5 cmp eax, ecx
.text:01001DC7 jg loc_1001EDC
.text:01001DC7
.text:01001DCD jz loc_1001ED2
.text:01001DCD
.text:01001DD3 cmp eax, 510
.text:01001DD8 jz loc_1001EC8 ; here is our jump
Lleva a un fragmento de código que llama a algún proceso y sale de wndProc.
.text:01001EC8 loc_1001EC8: ; CODE XREF: mainWndProc+20Fj
.text:01001EC8 call sub_100367A ; startNewGame ?
.text:01001EC8
.text:01001ECD jmp callDefAndExit ; default
¿Es esa la función que inicia un nuevo juego? ¡Descubre eso en la última parte! Manténganse al tanto.
Parte 3 de 3
Echemos un vistazo a la primera parte de esa función
.text:0100367A sub_100367A proc near ; CODE XREF: sub_100140C+CAp
.text:0100367A ; sub_1001B49+33j ...
.text:0100367A mov eax, dword_10056AC
.text:0100367F mov ecx, uValue
.text:01003685 push ebx
.text:01003686 push esi
.text:01003687 push edi
.text:01003688 xor edi, edi
.text:0100368A cmp eax, dword_1005334
.text:01003690 mov dword_1005164, edi
.text:01003696 jnz short loc_10036A4
.text:01003696
.text:01003698 cmp ecx, dword_1005338
.text:0100369E jnz short loc_10036A4
Hay dos valores (dword_10056AC, uValue) leídos en los registros eax y ecx y comparados con otros dos valores (dword_1005164, dword_1005338).
Eche un vistazo a los valores reales usando WinDBG (''bp 01003696''; en break ''p eax; p ecx'') - me parecieron dimensiones de campo minado. Jugando con un tamaño de campo de minas personalizado mostró que el primer par son nuevas dimensiones y el segundo - dimensiones actuales. Vamos a establecer nuevos nombres.
.text:0100367A startNewGame proc near ; CODE XREF: handleButtonPress+CAp
.text:0100367A ; sub_1001B49+33j ...
.text:0100367A mov eax, newMineFieldWidth
.text:0100367F mov ecx, newMineFieldHeight
.text:01003685 push ebx
.text:01003686 push esi
.text:01003687 push edi
.text:01003688 xor edi, edi
.text:0100368A cmp eax, currentMineFieldWidth
.text:01003690 mov dword_1005164, edi
.text:01003696 jnz short loc_10036A4
.text:01003696
.text:01003698 cmp ecx, currentMineFieldHeight
.text:0100369E jnz short loc_10036A4
Un poco más tarde, los nuevos valores sobrescriben la corriente y se llama a la subrutina
.text:010036A7 mov currentMineFieldWidth, eax
.text:010036AC mov currentMineFieldHeight, ecx
.text:010036B2 call sub_1002ED5
Y cuando lo vi
.text:01002ED5 sub_1002ED5 proc near ; CODE XREF: sub_1002B14:loc_1002B1Ep
.text:01002ED5 ; sub_100367A+38p
.text:01002ED5 mov eax, 360h
.text:01002ED5
.text:01002EDA
.text:01002EDA loc_1002EDA: ; CODE XREF: sub_1002ED5+Dj
.text:01002EDA dec eax
.text:01002EDB mov byte ptr dword_1005340[eax], 0Fh
.text:01002EE2 jnz short loc_1002EDA
Estaba completamente seguro de haber encontrado un campo de minas. Causa del ciclo que se encuentra en una matriz de longitud de 360h bytes (dword_1005340) con 0xF.
¿Por qué 360h = 864? Hay algunas pistas debajo de que la fila toma 32 bytes y 864 se puede dividir por 32, por lo que la matriz puede contener 27 * 32 celdas (aunque la IU permite un campo máximo de 24 * 30, hay un byte relleno alrededor de la matriz para las fronteras).
El siguiente código genera los bordes superior e inferior del campo de minas (0x10 byte). Espero que puedas ver la repetición de bucle en ese lío;) Tuve que usar papel y lápiz
.text:01002EE4 mov ecx, currentMineFieldWidth
.text:01002EEA mov edx, currentMineFieldHeight
.text:01002EF0 lea eax, [ecx+2]
.text:01002EF3 test eax, eax
.text:01002EF5 push esi
.text:01002EF6 jz short loc_1002F11 ;
.text:01002EF6
.text:01002EF8 mov esi, edx
.text:01002EFA shl esi, 5
.text:01002EFD lea esi, dword_1005360[esi]
.text:01002EFD
.text:01002F03 draws top and bottom borders
.text:01002F03
.text:01002F03 loc_1002F03: ; CODE XREF: sub_1002ED5+3Aj
.text:01002F03 dec eax
.text:01002F04 mov byte ptr MineField?[eax], 10h ; top border
.text:01002F0B mov byte ptr [esi+eax], 10h ; bottom border
.text:01002F0F jnz short loc_1002F03
.text:01002F0F
.text:01002F11
.text:01002F11 loc_1002F11: ; CODE XREF: sub_1002ED5+21j
.text:01002F11 lea esi, [edx+2]
.text:01002F14 test esi, esi
.text:01002F16 jz short loc_1002F39
Y el resto de la subrutina dibuja los bordes izquierdo y derecho
.text:01002F18 mov eax, esi
.text:01002F1A shl eax, 5
.text:01002F1D lea edx, MineField?[eax]
.text:01002F23 lea eax, (MineField?+1)[eax+ecx]
.text:01002F23
.text:01002F2A
.text:01002F2A loc_1002F2A: ; CODE XREF: sub_1002ED5+62j
.text:01002F2A sub edx, 20h
.text:01002F2D sub eax, 20h
.text:01002F30 dec esi
.text:01002F31 mov byte ptr [edx], 10h
.text:01002F34 mov byte ptr [eax], 10h
.text:01002F37 jnz short loc_1002F2A
.text:01002F37
.text:01002F39
.text:01002F39 loc_1002F39: ; CODE XREF: sub_1002ED5+41j
.text:01002F39 pop esi
.text:01002F3A retn
El uso inteligente de los comandos de WinDBG puede proporcionarle un excelente volcado de campos minados (tamaño personalizado 9x9). ¡Mira las fronteras!
0:000> db /c 20 01005340 L360
01005340 10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005360 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005380 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053a0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053c0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053e0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005400 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005420 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005440 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005460 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005480 10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054a0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054c0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054e0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
Hmm, parece que necesitaré otra publicación para cerrar el tema
Echa un vistazo a este artículo de proyecto de código, es un poco más profundo que la publicación de blog que mencionaste.
http://www.codeproject.com/KB/trace/minememoryreader.aspx
Editar
Y este artículo, aunque no sobre el buscaminas directamente, te brinda una buena guía paso a paso sobre cómo cazar a través de la memoria usando WinDbg:
http://www.codingthewheel.com/archives/extracting-hidden-text-with-windbg
Editar 2
Una vez más, esto no se trata de dragaminas, pero definitivamente me ha dado algo de reflexión para mi depuración de la memoria, hay una gran cantidad de tutoriales aquí:
http://memoryhacking.com/forums/index.php
Además, descargue CheatEngine (mencionado por Nick D.) y trabaje a través del tutorial que viene con.
Es bastante razonable suponer que la información sobre minas se distribuye contiguamente en la memoria al menos para las filas (es decir, es una matriz 2D o una matriz de matrices). Por lo tanto, trataría de abrir varias celdas adyacentes en la misma fila, haciendo volcados de memoria del proceso a medida que avanzo, y luego los diferencié y busco cualquier cambio repetitivo en la misma región de memoria (es decir, 1 byte cambiado en el primer paso, el siguiente el byte cambió a exactamente el mismo valor en el próximo paso, etc.).
También existe la posibilidad de que se trate de una matriz de bits empaquetada (3 bits por mina deberían ser suficientes para registrar todos los estados posibles: cerrado / abierto, mío / no mío, marcado / no marcado), así que también me preocuparía ( los patrones también serían repetibles, aunque más difíciles de detectar). Pero no es una estructura conveniente de manejar, y no creo que el uso de la memoria sea un cuello de botella para el Buscaminas, por lo que es poco probable que se use este tipo de cosas.
Este sitio web podría ser más útil:
http://www.subversity.net/reversing/hacking-minesweeper
La forma general de hacerlo es:
- De alguna manera obtener el código fuente.
- Desensambla y espera que los símbolos sobrantes te puedan ayudar.
- Adivina el tipo de datos e intenta manipularlo y utiliza un escáner de memoria para limitar las posibilidades.
En respuesta a Bounty
Bueno, en una segunda lectura, parece como si quisiera una guía sobre cómo utilizar un depurador como WinDBG en lugar de la cuestión habitual de cómo realizar ingeniería inversa. Ya te he mostrado el sitio web que te dice los valores que necesitas buscar, por lo que la pregunta es, ¿cómo lo buscas?
Estoy usando Notepad en este ejemplo porque no tengo instalado Minesweeper. Pero la idea es la misma.
Usted escribe
s <options> <memory start> <memory end> <pattern>
Presione "?" Y luego "s" para ver la ayuda.
Una vez que haya encontrado el patrón de memoria que desea, puede presionar alt + 5 para abrir el visor de memoria y obtener una buena pantalla.
A WinDBG le lleva algo de tiempo acostumbrarse, pero es tan bueno como cualquier otro depurador.
Las minas probablemente se almacenarán en algún tipo de matriz bidimensional. Esto significa que es una matriz de punteros o una única matriz de estilo C de booleanos.
Cada vez que el formulario recibe un evento de mouse, se hace referencia a esta estructura de datos. El índice se calculará utilizando la coordenada del mouse, probablemente utilizando la división de enteros. Eso significa que probablemente debería buscar una instrucción cmp
o similar, donde uno de los operandos se calcula utilizando un desplazamiento x
, donde x
es el resultado de un cálculo que involucra la división de enteros. El desplazamiento será entonces el puntero al comienzo de la estructura de datos.
Parece que estás intentando desensamblar la fuente, pero lo que tienes que hacer es mirar el espacio de la memoria del programa en ejecución. El editor hexadecimal HxD tiene una función que te permite eso.
Una vez que estás en el espacio de la memoria, es cuestión de tomar instantáneas de la memoria mientras juegas con la pizarra. Aislar qué cambios versus qué no. Cuando creas que tienes una idea de dónde está la estructura de datos en la memoria hexadecimal, intenta editarla mientras está en la memoria y ver si la placa cambia como resultado.
El proceso que desea no es diferente a la creación de un "entrenador" para un videojuego. Por lo general, se basan en encontrar dónde viven en la memoria valores como la salud y la munición y cambiarlos sobre la marcha. Es posible que pueda encontrar algunos buenos tutoriales sobre cómo construir entrenadores de juegos.
Si bien no es estrictamente una "herramienta de ingeniería inversa", y más de un juguete, incluso un idiota como yo podría usar, echa un vistazo a CheatEngine . Hace que sea más fácil rastrear qué partes de la memoria han cambiado, cuándo e incluso tiene disposiciones para rastrear las piezas de memoria modificadas a través de punteros (aunque probablemente no las necesite). Un buen tutorial interactivo está incluido.
Un buen artículo sobre este tema se puede encontrar en Uninformed . Cubre la inversión de Minesweeper (como una introducción a las aplicaciones de ingeniería inversa Win32) con bastante detalle y es un gran recurso.
Un buen punto para comenzar a rastrear en el depurador sería en el mouse hacia arriba. Así que busque el procedimiento de la ventana principal (creo que las herramientas como spyxx pueden inspeccionar las propiedades de Windows y la dirección del controlador de eventos es una de ellas). Acércate a él y encuentra dónde maneja los eventos del mouse: habrá un interruptor, si puedes reconocerlo en ensamblador (mira el valor de WM_XXX para el mouse hacia arriba en windows.h).
Ponga un punto de quiebre allí y comience a intervenir. En algún momento entre el momento en que suelte el botón del mouse y la pantalla se actualice, victum tendrá acceso a la estructura de datos que está buscando.
Sea paciente, intente identificar lo que se está haciendo en un momento dado, pero no se moleste en buscar demasiado en el código que sospeche que no es interesante para su objetivo actual. Podría tomar varias carreras en el depurador para clavarlo.
El conocimiento del flujo de trabajo de las aplicaciones win32 normales también ayuda.