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?
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:
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.
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.
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
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.
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.
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
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
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.
"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)
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.
Ce site Web pourrait être plus utile:
http://www.subversity.net/reversing/hacking-minesweeper
La façon générale de procéder consiste à:
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.
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.
WinDBG prend un certain temps pour s'y habituer, mais il est aussi bon que n'importe quel autre débogueur.
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é.
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.
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.
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.