web-dev-qa-db-fra.com

Comment puis-je trouver la structure de données qui représente la disposition des mines du démineur en mémoire?

J'essaie d'en apprendre davantage sur l'ingénierie inverse, en utilisant le démineur comme exemple d'application. J'ai trouvé cela article MSDN sur une simple commande WinDbg qui révèle toutes les mines mais elle est ancienne, n'est pas expliquée en détail et n'est vraiment pas ce que je recherche.

J'ai désassembleur IDA Pro et le débogueur WinDbg et j'ai chargé winmine.exe dans les deux. Quelqu'un peut-il fournir des conseils pratiques pour l'un ou l'autre de ces programmes pour trouver l'emplacement de la structure de données qui représente le champ de mines?

Dans WinDbg, je peux définir des points d'arrêt, mais il m'est difficile d'imaginer à quel moment définir un point d'arrêt et à quel emplacement de la mémoire. De même, lorsque je consulte le code statique dans IDA Pro, je ne sais même pas par où commencer pour trouver la fonction ou la structure de données qui représente le champ de mines.

Y a-t-il des ingénieurs inverses sur Stackoverflow qui peuvent m'orienter dans la bonne direction?

92
KingNestor

Partie 1 de 3


Si vous êtes sérieux dans l'ingénierie inverse - oubliez les entraîneurs et les moteurs de triche.

Un bon ingénieur inverse doit d'abord se familiariser avec le système d'exploitation, les fonctions principales de l'API, la structure générale du programme (qu'est-ce que la boucle d'exécution, les structures Windows, les routines de gestion des événements), le format de fichier (PE). Les classiques de Petzold "Programmation Windows" peuvent vous aider (www.Amazon.com/exec/obidos/ISBN=157231995X) ainsi que MSDN en ligne.

Vous devez d'abord penser à l'endroit où la routine d'initialisation du champ de mines peut être appelée. J'ai pensé à suivre:

  • Lorsque vous lancez le jeu
  • Lorsque vous cliquez sur le visage heureux
  • Lorsque vous cliquez sur Jeu-> Nouveau ou appuyez sur F2
  • Lorsque vous changez de niveau de difficulté

J'ai décidé de vérifier la commande d'accélérateur F2.

Pour trouver le code de gestion de l'accélérateur, vous devez trouver la procédure de gestion des messages de fenêtre (WndProc). Il peut être retracé par des appels CreateWindowEx et RegisterClass.

Lire:

Ouvrez IDA, fenêtre Imports, recherchez "CreateWindow *", accédez-y et utilisez la commande "Jump xref to operand (X)" pour voir où il est appelé. Il ne devrait y avoir qu'un seul appel.

Maintenant, regardez ci-dessus pour la fonction RegisterClass et son paramètre WndClass.lpfnWndProc. J'ai déjà nommé la fonction mainWndProc dans mon cas.

.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

Appuyez sur Entrée sur le nom de la fonction (utilisez 'N' pour la renommer en quelque chose de mieux)

Jetez maintenant un œil à

.text:01001BCF                 mov     edx, [ebp+Msg]

Il s'agit de l'ID du message qui, en cas de pression sur le bouton F2, doit contenir la valeur WM_COMMAND. Vous devez trouver où il est comparé à 111h. Cela peut être fait en traçant edx dans IDA ou en définissant le point d'arrêt conditionnel dans WinDbg et en appuyant sur F2 dans le jeu.

De toute façon, cela mène à quelque chose comme

.text:01001D5B                 sub     eax, 111h
.text:01001D60                 jz      short loc_1001DBC

Faites un clic droit sur 111h et utilisez "Constante symbolique" -> "Utiliser la constante symbolique standard", tapez WM_ et Entrée. Vous devriez maintenant avoir

.text:01001D5B                 sub     eax, WM_COMMAND
.text:01001D60                 jz      short loc_1001DBC

Il s'agit d'un moyen simple de trouver les valeurs d'ID de message.

Pour comprendre la gestion de l'accélérateur, consultez:

C'est beaucoup de texte pour une seule réponse. Si vous êtes intéressé, je peux écrire un autre couple de messages. Champ de mines court et long stocké sous forme d'un tableau d'octets [24x36], 0x0F montre que l'octet n'est pas utilisé (lecture d'un champ plus petit), 0x10 - champ vide, 0x80 - mien.

Partie 2 sur 3


Ok, continuons avec le bouton F2.

Selon tilisation des accélérateurs de clavier lorsque le bouton F2 est enfoncé Fonction wndProc

... reçoit un message WM_COMMAND ou WM_SYSCOMMAND. Le mot de poids faible du paramètre wParam contient l'identifiant de l'accélérateur.

Ok, nous avons déjà trouvé où WM_COMMAND est traité, mais comment déterminer la valeur de paramètre wParam correspondante? C'est là que pirate de ressources entre en jeu. Nourrissez-le avec binaire et il vous montre tout. Comme une table d'accélérateurs pour moi.

texte alternatif http://files.getdropbox.com/u/1478671/2009-07-29_161532.jpg

Vous pouvez voir ici que le bouton F2 correspond à 510 dans wParam.

Revenons maintenant au code qui gère WM_COMMAND. Il compare wParam avec différentes 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

Utilisez le menu contextuel ou le raccourci clavier "H" pour afficher les valeurs décimales et vous pouvez voir notre saut

.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

Cela conduit à un bloc de code qui appelle un proc et quitte wndProc.

.text:01001EC8 loc_1001EC8:                            ; CODE XREF: mainWndProc+20Fj
.text:01001EC8                 call    sub_100367A     ; startNewGame ?
.text:01001EC8
.text:01001ECD                 jmp     callDefAndExit  ; default

Est-ce la fonction qui lance un nouveau jeu? Découvrez-le dans la dernière partie! Restez à l'écoute.

Partie 3 de 3

Jetons un coup d'œil à la première partie de cette fonction

.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

Il y a deux valeurs (dword_10056AC, uValue) lues dans les registres eax et ecx et comparées à deux autres valeurs (dword_1005164, dword_1005338).

Jetez un oeil aux valeurs réelles en utilisant WinDBG ('bp 01003696'; en pause 'p eax; p ecx') - elles me semblaient être des dimensions de champ de mines pour moi. Jouer avec une taille de champ de mines personnalisée a montré que la première paire est de nouvelles dimensions et la seconde - les dimensions actuelles. Fixons de nouveaux noms.

.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 peu plus tard, de nouvelles valeurs écrasent le courant et le sous-programme est appelé

.text:010036A7                 mov     currentMineFieldWidth, eax
.text:010036AC                 mov     currentMineFieldHeight, ecx
.text:010036B2                 call    sub_1002ED5

Et quand je l'ai vu

.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

J'étais complètement sûr d'avoir trouvé un champ de mines. Cause du cycle qui contient un tableau de longueur de 360 ​​octets (dword_1005340) avec 0xF.

Pourquoi 360h = 864? Il y a quelques indices en dessous de cette ligne prend 32 octets et 864 peut être divisé par 32, donc le tableau peut contenir 27 * 32 cellules (bien que l'interface utilisateur permette un champ maximum de 24 * 30, il y a un octet de remplissage autour du tableau pour les bordures).

Le code suivant génère les bordures supérieure et inférieure du champ de mines (0x10 octets). J'espère que vous pouvez voir l'itération de boucle dans ce gâchis;) J'ai dû utiliser du papier et un stylo

.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

Et le reste du sous-programme dessine les bordures gauche et droite

.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

L'utilisation intelligente des commandes WinDBG peut vous fournir un vidage de champ de mines cool (taille personnalisée 9x9). Découvrez les frontières!

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, on dirait que j'aurai besoin d'un autre post pour clore le sujet

122
Stanislav

Il semble que vous essayez de démonter la source, mais ce que vous devez faire est de regarder l'espace mémoire du programme en cours d'exécution. L'éditeur hexadécimal HxD a une fonctionnalité qui vous permet de le faire.

http://www.freeimagehosting.net/uploads/fcc1991162.png

Une fois que vous êtes dans l'espace mémoire, il s'agit de prendre des instantanés de la mémoire pendant que vous jouez avec la carte. Isolez ce qui change par rapport à ce qui ne change pas. Lorsque vous pensez avoir une idée de l'emplacement de la structure de données dans la mémoire hexadécimale, essayez de la modifier lorsqu'elle est en mémoire et voyez si la carte change en conséquence.

Le processus que vous souhaitez n'est pas différent de la construction d'un "formateur" pour un jeu vidéo. Ceux-ci sont généralement basés sur la recherche de valeurs de la santé et des munitions et leur modification à la volée. Vous pourrez peut-être trouver de bons tutoriels sur la façon de créer des entraîneurs de jeux.

15
James McMahon

Consultez cet article de projet de code, il est un peu plus détaillé que le blog que vous avez mentionné.

http://www.codeproject.com/KB/trace/minememoryreader.aspx

Éditer

Et cet article, bien qu'il ne traite pas directement du dragueur de mines, vous donne un bon guide étape par étape sur la recherche dans la mémoire à l'aide de WinDbg:

http://www.codingthewheel.com/archives/extracting-hidden-text-with-windbg

Modifier 2

Encore une fois, il ne s'agit pas de dragueur de mines, mais cela m'a certainement donné matière à réflexion pour le débogage de ma mémoire, il y a une multitude de tutoriels ici:

http://memoryhacking.com/forums/index.php

Téléchargez également CheatEngine (mentionné par Nick D.) et suivez le didacticiel fourni.

11
Kirschstein

"Dans WinDbg, je peux définir des points d'arrêt, mais il m'est difficile d'imaginer à quel moment définir un point d'arrêt et à quel emplacement de la mémoire. De même, lorsque je consulte le code statique dans IDA Pro, je ne sais pas par où commencer pour trouver la fonction ou la structure de données qui représente le champ de mines. "

Exactement!

Eh bien, vous pouvez rechercher des routines comme random () qui seront appelées lors de la construction de la table des mines. Ce livre m'a beaucoup aidé lorsque j'expérimentais avec la rétro-ingénierie. :)

En général, les bons endroits pour définir des points d'arrêt sont les appels aux boîtes de message, les appels pour jouer un son, les minuteries et autres routines API win32.

BTW, je scanne le dragueur de mines en ce moment avec OllyDbg .

Mise à jour: nemo m'a rappelé un excellent outil, Cheat Engine par Eric "Dark Byte" Heijnen .

Cheat Engine (CE) est un excellent outil pour regarder et modifier l'espace mémoire d'autres processus. Au-delà de cette fonction basique, CE a des fonctionnalités plus spéciales comme l'affichage de la mémoire désassemblée d'un processus et l'injection de code dans d'autres processus.

(la valeur réelle de ce projet est que vous pouvez télécharger le code source -Delphi- et voir comment ces mécanismes ont été mis en œuvre - je l'ai fait il y a de nombreuses années: o)

9
Nick Dandoulakis

Un assez bon article sur ce sujet peut être trouvé sur ninformed . Il couvre le déminage inversé (comme une introduction aux applications Win32 de rétro-ingénierie) dans de très grands détails et est tout autour d'une très bonne ressource.

5
mrduclaw

Ce site Web pourrait être plus utile:

http://www.subversity.net/reversing/hacking-minesweeper

La façon générale de procéder consiste à:

  1. Obtenez en quelque sorte le code source.
  2. Démontez et espérez que les symboles restants peuvent vous aider.
  3. Devinez le type de données et essayez de le manipuler et utilisez un scanner de mémoire pour limiter les possibilités.

En réponse à Bounty

Eh bien, en deuxième lecture, il semble que vous vouliez un guide sur la façon d'utiliser un débogueur comme WinDBG plutôt que la question habituelle de la façon de procéder à la rétro-ingénierie. Je vous ai déjà montré le site Web qui vous indique les valeurs que vous devez rechercher, alors la question est de savoir comment le rechercher?

J'utilise le Bloc-notes dans cet exemple car je n'ai pas installé de démineur. Mais l'idée est la même.

alt text

Vous tapez

s <options> <memory start> <memory end> <pattern>

Appuyez sur "?" Puis sur "s" pour voir l'aide.

Une fois que vous avez trouvé le modèle de mémoire souhaité, vous pouvez ensuite appuyer sur alt + 5 pour afficher le visualiseur de mémoire pour un affichage agréable.

alt text

WinDBG prend un certain temps pour s'y habituer, mais il est aussi bon que n'importe quel autre débogueur.

4
Unknown

Il est assez raisonnable de supposer que les informations sur les mines sont disposées de manière contiguë en mémoire au moins pour les lignes (c'est-à-dire qu'il s'agit d'un tableau 2D ou d'un tableau de tableaux). Ainsi, j'essayerais d'ouvrir plusieurs cellules adjacentes dans la même ligne, de faire des vidages de mémoire du processus au fur et à mesure, puis de les différencier et de rechercher les changements répétitifs dans la même région de mémoire (c.-à-d. 1 octet changé à la première étape, la suivante octet changé à exactement la même valeur à l'étape suivante, etc.).

Il est également possible que ce soit un tableau de bits compressé (3 bits par mine devraient être suffisants pour enregistrer tous les états possibles - fermé/ouvert, mine/pas de mine, marqué/non marqué), donc je le surveillerais aussi ( les motifs seraient également reproductibles, bien que plus difficiles à repérer). Mais ce n'est pas une structure pratique à gérer, et je ne pense pas que l'utilisation de la mémoire était un goulot d'étranglement pour le démineur, il est donc peu probable que ce genre de chose soit utilisé.

0
Pavel Minaev

Les mines seront probablement stockées dans une sorte de réseau bidimensionnel. Cela signifie qu'il s'agit soit d'un tableau de pointeurs, soit d'un tableau unique de booléens de style C.

Chaque fois que le formulaire reçoit un événement souris vers le haut, cette structure de données est référencée. L'index sera calculé en utilisant les coordonnées de la souris, probablement en utilisant une division entière. Cela signifie que vous devriez probablement rechercher un cmp ou une instruction similaire, où l'un des opérandes est calculé à l'aide d'un décalage et x, où x est le résultat d'un calcul impliquant une division entière. Le décalage sera alors le pointeur vers le début de la structure de données.

0
Jørgen Fogh

Un bon point pour commencer le traçage dans le débogueur serait sur la souris. Donc, trouvez la procédure de la fenêtre principale (je pense que des outils comme spyxx peuvent inspecter les propriétés des fenêtres et l'adresse du gestionnaire d'événements est l'une d'entre elles). Entrez dedans et trouvez où il gère les événements de la souris - il y aura un commutateur, si vous pouvez le reconnaître dans l'assembleur (regardez la valeur de WM_XXX pour la souris vers le haut dans windows.h).

Mettez-y un point d'arrêt et commencez à intervenir. Quelque part entre le moment où vous avez relâché le bouton de la souris et la mise à jour de l'écran, le victum accédera à la structure de données que vous recherchez.

Soyez patient, essayez d'identifier ce qui se fait à un moment donné, mais ne vous embêtez pas à approfondir le code que vous soupçonnez d'être sans intérêt pour votre objectif actuel. Il peut falloir plusieurs exécutions dans le débogueur pour le résoudre.

La connaissance du flux de travail normal des applications win32 aide également.

0
Eugene

Bien que ce ne soit pas strictement un "outil d'ingénierie inverse", et plus d'un jouet, même un idiot comme moi pourrait utiliser, consultez Cheat Engine . Il facilite le suivi des parties de la mémoire qui ont changé, quand et même des dispositions pour suivre les parties de la mémoire modifiées via des pointeurs (bien que vous n'en ayez probablement pas besoin). Un joli didacticiel interactif est inclus.

0
Ivan Vučica