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 reverse engineering doit d'abord apprendre à connaître 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 "Programming Windows" peuvent aider (www.amazon.com/exec/obidos/ISBN=157231995X) ainsi que MSDN en ligne.
Vous devez d'abord réfléchir à l'endroit où la routine d'initialisation du champ de mines peut être appelée. J'ai pensé à suivre:
- Lorsque vous lancez le jeu
- Quand tu cliques sur un 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 des accélérateurs, vous devez trouver la procédure de gestion des messages de fenêtre (WndProc). Il peut être retracé par les appels CreateWindowEx et RegisterClass.
Lire:
Ouvrez IDA, la fenêtre Importations, recherchez "CreateWindow *", accédez-y et utilisez la commande "Jump xref to operand (X)" pour voir où elle est appelée. Il ne devrait y avoir qu'un seul appel.
Regardez maintenant 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 le renommer en quelque chose de mieux)
Jetez maintenant un œil à
.text:01001BCF mov edx, [ebp+Msg]
Il s'agit de l'ID de message, qui en cas de pression sur le bouton F2, doit contenir la valeur WM_COMMAND. Vous devez trouver où c'est par rapport à 111h. Cela peut être fait soit en traçant edx dans IDA, soit en définissant un point d'arrêt conditionnel dans WinDbg et en appuyant sur F2 dans le jeu.
De toute façon 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. Tu devrais maintenant avoir
.text:01001D5B sub eax, WM_COMMAND
.text:01001D60 jz short loc_1001DBC
C'est un moyen simple de connaître les valeurs d'identifiant de message.
Pour comprendre la gestion des accélérateurs, consultez:
C'est beaucoup de texte pour une seule réponse. Si vous êtes intéressé, je peux écrire quelques autres articles. Le champ de mines court d'une longue histoire stocké sous forme de tableau d'octets [24x36], 0x0F montre que l'octet n'est pas utilisé (jouant un champ plus petit), 0x10 - champ vide, 0x80 - le mien.
Partie 2 sur 3
Ok, continuons avec le bouton F2.
Selon l' utilisation 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 du paramètre wParam correspondante? C'est là que Resource hacker entre en jeu. Nourrissez-le avec du 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 morceau de code qui appelle certains 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 sur 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 œil aux valeurs réelles en utilisant WinDBG ('bp 01003696'; on break 'p eax; p ecx') - elles me semblaient être des dimensions de champ de mines. Jouer avec la taille de champ de mines personnalisée a montré que la première paire correspond à de nouvelles dimensions et la seconde à des dimensions actuelles. Définissons 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 absolument sûr d'avoir trouvé un tableau de champ de mines. Cause du cycle qui comprend un tableau de longueur de 360h octets (dword_1005340) avec 0xF.
Pourquoi 360h = 864? Il y a quelques indices ci-dessous, cette ligne prend 32 octets et 864 peut être divisée par 32, donc le tableau peut contenir 27 * 32 cellules (bien que l'interface utilisateur autorise un champ maximum de 24 * 30, il y a un remplissage d'un octet autour du tableau pour les bordures).
Le code suivant génère des bordures supérieure et inférieure de champ de mines (0x10 octet). J'espère que vous pouvez voir une itération de boucle dans ce désordre;) 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 des 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
Une 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 fermer le sujet