IDA - The Interactive Disassembler - part 2

Dans la première partie nous avons vu les bases de IDA, nous allons maintenant l'illustrer par l'analyse de A à Z en dead-listing d'un crackme très simple.
Nous partirons de la source pure tout juste désassemblé pour finir par une source complétement modifiée et adaptée à notre compréhension, je vais y allez doucement en rappelant quelques fondement sur l'ASM et j'essairai de commenter chaque manoeuvre et de détailler l'utilisation des commandes de IDA, en espérant ne rien oublier.

Pour la suite il est nécéssaire d'avoir quelques bases en assembleur et quelques notions de la programmation system en Win32, et surtout d'être muni d'une référence des fonctions Windows comme Win32.hlp ou le MSDN afin de pouvoir se référer aux prototypes des fonctions, comprendre la fonction en elle même et les argument dont elle se sert. Vous verrez que ces documents sont indispensables.
Les blocs d'exemples seront de 2 couleurs, bleu pour original, rouge une fois modifié.
Voici le programmeOn l'ouvre sous IDA, on laisse les options de désassemblage par défaut. Une fois le désassemblage fini, on adpate les options personnelles et au saute directement à l'entry point par le menu jump / jump to entry point ou (ctrl-E)
CODE:00401000 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦s¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
CODE:00401000
CODE:00401000
CODE:00401000 public start
CODE:00401000 start proc near
CODE:00401000 push 0 ; lpModuleName
CODE:00401002 call GetModuleHandleA
CODE:00401007 mov ds:hInstance, eax
CODE:0040100C push 0 ; dwInitParam
CODE:0040100E push offset sub_40102B ; lpDialogFunc
CODE:00401013 push 0 ; hWndParent
CODE:00401015 push offset aMydialog ; lpTemplateName
CODE:0040101A push ds:hInstance ; hInstance
CODE:00401020 call DialogBoxParamA
CODE:00401025 push eax ; uExitCode
CODE:00401026 call ExitProcess
CODE:00401026 start endp
Nous voici donc là où le programme commence. Cet endroit est defini par le label start comme dans une source assembleur. IDA a ici interprété ce bloc comme une procedure à l'image de la fonction main() ou winmain() dans un programme en C/C++, puique ces fonctions représentent le point d'entrée d'un programme dans une source en C (à savoir que IDA analyse le code comme si il venait d'une source en C/C++).

Le 1er call que l'on rencontre est GetModuleHandleA cette fonction sert à recupérer l'Hinstance handle du programme, qui est en fait un numero d'identification du programme qui est souvent utilisé pour appeler d'autres fonctions, on rencontre cette fonction vers l'entry point sur la quasi totalité des programmes. Si l'on regarde en amont d'un call on verra ses argument pushé dans la pile.

D'après le prototype de la fonction GetModuleHandle celle-ci necessite 1 seul argument, que l'on retrouve ici par push 0.
On voit que IDA a ajouté un commentaire qui identifie l'argument de la fonction avec son nom tel que déclaré dans son prototype :
lpModuleName.
Pour comprendre pourquoi sa valeur est 0 il suffit de lire le prototype qui nous dit que si
ce paramètre est NULL (0), GetModuleHandle retourne un handle représentant le programme. Suit une instruction qui place dans une variable (nommé automatiquement par IDA) la valeur de retour de GetModuleHandle (le retour d'une fonction se faisant par le registre eax, j'espere que je ne vous apprend rien) soit l'Hinstance handle.
Si on double clique sur le nom de cette variable (ds:hInstance) on se retrouve dans la partie .data ou elle est declaré comme suit :
DATA:00402000 ; HINSTANCE hInstance
DATA:00402000 hInstance dd 0 ; DATA XREF: start+7w
DATA:00402000 ; start+1Ar
On voit que cette variable est de type double-word ( dd : declare double-word ) et qu'elle est référencée deux fois (note: le nombre maximum de XREF affiché est par defaut de 2, si il y a plus de référence vous pourrez voir ... à la fin de la ligne, a moins que vous n'ayer modifié le nbr de XREF à afficher dans les options). Le nom par défaut de cette variable est tres bien représentatif nous ne la renommerons donc pas. On fait 'echap' pour revenir en arriere et continuer notre analyse. Commence ensuite une serie de push avant l'appel de DialogBoxParamA.

Pareil on regarde dans le manuel à quoi sert cette fonction : elle sert à créer une boite de dialogue à partir d'un fichier ressource.
Et on analyse les arguments que l'on vient de lui passer par la pile. (n'oubliez pas que les argument sont pushés en ordre inverse par rapport au prototype)

On voit ainsi qu'on lui passe l'Hinstance tout juste recupéré précedement, et parmi les autres arguments un particulièrement interressant est l'adresse d'une procedure qui va servir à gérer les évenements qui se produisent sur cette boite de dialog. Cet argument est lpDialogFunc // dialog box procedure.

Nous reviendrons sur ce point dans quelques instants, finissons par le dernier call que l'on rencontre ensuite qui est ExitProcess et qui va se servir du retour de la fonction precedente (eax) comme unique argument. Cette fonction sert à terminer un processus en l'ocurrence notre programme. Nous voyons bien ensuite la fin de la procedure start par le label start endp qui pourrait représenter la fin de la fonction main() en C/C++.

Mais que devient le programme ? En realité il ne va pas tout de suite sur le call ExitProcess, lors de l'appel de la fonction DialogBoxParamA celle-ci va creer et afficher une boite de dialog qui entrera dans une sorte de boucle sans fin. Le dialog sera alors gérer par une procedure et le seul moyen de quitter le dialog et de revenir après l'appel de DialogBoxParamA sera d'envoyer un message à ce dialogue que l'on recuperera via sa procedure de gestion des messages et en specifiant de mettre fin à ce dialog et le detruire.
On reviens donc sur l'argument de DialogBoxParamA qui specifie l'adresse de cette procedure de gestion des message:
CODE:0040100E         push    offset sub_40102B       ; lpDialogFunc
On double-click sur sub_40102B et l'on se retouve au debut de cette procedure:
CODE:0040102B ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
CODE:0040102B
CODE:0040102B
CODE:0040102B ; int __cdecl sub_40102B(HWND hWnd,int,int)
CODE:0040102B ; Attributes: bp-based frame
CODE:0040102B
CODE:0040102B sub_40102B proc near ; DATA XREF: start+Eo
CODE:0040102B
CODE:0040102B hWnd = dword ptr 8
CODE:0040102B arg_4 = dword ptr 0Ch
CODE:0040102B arg_8 = dword ptr 10h
Si on regarde dans notre manuel pour DialogBoxParamA et le parametre lpDialogFunc.
On nous dit de nous référer, pour plus d'informations sur la procedure de la dialog-box, à la déclaration DialogProc dont voici le prototype :
BOOL CALLBACK DialogProc(
HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter );

Si nous comparons avec celui qu'IDA nous donne on voit qu'il n'est pas tout à fait bon, ou plutôt qu'il n'a mentionné en argument que ceux que le code qui suit exploite. De plus mis à part hWnd qui est bien nommé les argument suivants n'ont pas de noms. Comme nous sommes surs à 100% que cette procédure est bien la procédure de DialogBoxParam nous allons réctifier le prototype ( nom de la fonction, et argument ). Pour cela il nous faut placer notre curseur sur sub_40102B proc near et cliquer droit pour choisir 'edit fuction' à partir du menu contextuel.

Dans la case 'Name of function' on tape : DialogProc puis OK, IDA a renommé la fonction. Maintenant on va editer les arguments selon le prototype, pareil clic droit et on chosit 'Set fuction type'. On tape le bon prototype (sans le CALLBACK que IDA ne supporte pas) :

BOOL DialogProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

Nous devrions obtenir ca :
CODE:0040102B ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
CODE:0040102B
CODE:0040102B
CODE:0040102B ; BOOL DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
CODE:0040102B ; Attributes: bp-based frame
CODE:0040102B
CODE:0040102B DialogProc proc near ; DATA XREF: start+Eo
CODE:0040102B
CODE:0040102B hWnd = dword ptr 8
CODE:0040102B uMsg = dword ptr 0Ch
CODE:0040102B wParam = dword ptr 10h
CODE:0040102B lParam = dword ptr 14h
Tous les arguments ont été renommés et l'un a même été ajouté (lParam). Il n'était pas présent avant car il n'etait sans doute pas utilisé par le code. Faisons un petit saut en arriere histoire de voir que le re-nommage s'est effectué dans toutes les parties référencées, pour l'entry-point nous obtenons désormais ca:

CODE:00401000 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
CODE:00401000
CODE:00401000
CODE:00401000 public start
CODE:00401000 start proc near
CODE:00401000 push 0 ; lpModuleName
CODE:00401002 call GetModuleHandleA
CODE:00401007 mov ds:hInstance, eax
CODE:0040100C push 0 ; dwInitParam
CODE:0040100E push offset DialogProc ; lpDialogFunc
CODE:00401013 push 0 ; hWndParent
CODE:00401015 push offset aMydialog ; lpTemplateName
CODE:0040101A push ds:hInstance ; hInstance
CODE:00401020 call DialogBoxParamA
CODE:00401025 push eax ; uExitCode
CODE:00401026 call ExitProcess
CODE:00401026 start endp


On voit que l'argument lpDialogFunc est push offset DialogProc et on comprend clairement que l'on push l'adress de la procedure DialogProc plutot qu'un simple push offset sub_40102B. Continuons dans cette procédure qui est le moteur de notre boite de dialogue.


CODE:0040102B ; BOOL DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
CODE:0040102B ; Attributes: bp-based frame
CODE:0040102B
CODE:0040102B DialogProc proc near ; DATA XREF: start+Eo
CODE:0040102B
CODE:0040102B hWnd = dword ptr 8
CODE:0040102B uMsg = dword ptr 0Ch
CODE:0040102B wParam = dword ptr 10h
CODE:0040102B lParam = dword ptr 14h
CODE:0040102B
CODE:0040102B enter 0, 0
CODE:0040102F cmp [ebp+uMsg], 10h
CODE:00401033 jz short loc_40104E
CODE:00401035 cmp [ebp+uMsg], 110h
CODE:0040103C jz short loc_40105F
CODE:0040103E cmp [ebp+uMsg], 111h
CODE:00401045 jz short loc_401079
CODE:00401047 mov eax, 0
CODE:0040104C jmp short locret_4010A5
CODE:0040104E ; ---------------------------------------------------------------------------


Analysons cette premiere partie très importante :
enter 0, 0 va permettre de préparer la pile, pour un accès conventionnel,
cette instruction revient à :
  • sauvegarder la valeur de ebp en la poussant dans la pile => push ebp
  • placer dans ebp le pointeur de pile esp => mov ebp, esp
Une fois cela accompli on peux accéder aux arguments de la procédure comme ceci:
  • ebp = valeur du pointeur de pile qui pointe sur le dernier element pushé qui est ebp
  • ebp+04h = adresse de retour du call qui appel cette procedure
  • ebp+08h = 1er argument
  • ebp+0Ch = 2eme argument
  • ebp+10h = 3eme argument
  • ebp+14h = 4eme argument
L'instruction qui permet de réequilibrer l'état de la pile une fois la procedure terminé est l'instruction leave qui est quelque sorte l'inverse de enter et qui va effectuer:
  • mov esp,ebp => replace dans esp la valeur de ebp
  • pop ebp ; désempile en reprenant l'ancienne valeur de ebp
En continuant à la suite on trouve :
CODE:0040102F         cmp     [ebp+uMsg], 10h
CODE:00401033 jz short loc_40104E
Le programme compare [ebp+uMsg] qui correspond donc au 2eme argument de cette procédure (soit uMsg).
Regardons à quoi correspond cet argument. Il représente le message que la procedure est chargé d'intercepter et de lier à un evenement si l'on souhaite le traiter. En gros, tout sous Windows est lié à la gestion de message, chaque evenement (clic sur un bouton...) declenche un message, et si l'on souhaite traiter cet evenement on va intercepter ce message et le lier à une procedure.
Là nous voyons donc que nous comparons uMsg avec la valeur 10h. Cette valeur represente un message, chaque message correspond à un nombre entier representé par un nom
pour faciliter la vie des programmeurs. Quel est le message qui à la valeur 10h ? WM_CLOSE.

IDA va encore nous aider ici car il possede toute la liste des messages avec leur valeur numerique et pour faire la correspondance entre la valeur numerique et le nom du message il suffit de clicker droit et du menu chosir 'use standart symbol content'. Une liste assez immense apparaît et indique tous les noms symboliques qui representent la valeur 10h. En se situant dans le context on sait que l'on cherche un message, ceux-ci commence par WM_, alors il suffit de commencer à taper au clavier WM pour placer la recherche sur ce que nous voulons.

On se trouve sur WM_CLOSE et on clique OK. IDA a remplacé 10h par le nom du message qu'il symbolise. Faites pareil pour les deux autres comparaisons avec uMsg.
Suivi du cmp [ebp+uMsg], WM_CLOSE on trouve un saut conditinnel qui saute vers un label si la comparaison est égale.
C'est la que sera defini le code à executer si notre dialog a bien recu le message WM_CLOSE. On peut donc renommer les 3 labels suivant les comparaisons avec des noms plus explicites et en rapport avec les messages qu'ils traitent et pourquoi pas mettre quelques commentaires, tel que mon exemple :
CODE:0040102B enter 0, 0
CODE:0040102F cmp [ebp+uMsg], WM_CLOSE
CODE:00401033 jz short _CloseDlg ; Ferme le dialog
CODE:00401035 cmp [ebp+uMsg], WM_INITDIALOG
CODE:0040103C jz short _InitDlg ; Initialisation du dialog
CODE:0040103E cmp [ebp+uMsg], WM_COMMAND
CODE:00401045 jz short _Command ; Commande actionné sur le dialog
CODE:00401047 mov eax, 0
CODE:0040104C jmp short locret_4010A5
CODE:0040104E ; ---------------------------------------------------------------------------

Apres la comparaison des messages on trouve mov eax, 0 et jmp short locret_4010A5, si on double-clique sur le jmp on voit que l'on se retrouve sur la fin de la procédure symbolisée par DialogProc endp. Toujours dans le manuel, il est dit que la procedure DialogProc doit retourner non-zero si elle a repondu à un message et zero dans le cas contraire, une exception se faisant sur le message WM_INITDIALOG si dans le code qui lui correspond on appel la fonction SetFocus auquel cas nous devrions retourner zero aussi. Ici on voit que l'on respecte bien cette condition et on mettra 0 dans eax, si aucun message n'a été traité. Avant de sauter sur un label qui nous envois vers la fin de la procedure, renommons ce label en _LeaveProc.

CODE:004010A5 _LeaveProc:                                  ; CODE XREF: DialogProc+21j
CODE:004010A5 ; DialogProc+32j ...
CODE:004010A5 leave
CODE:004010A6 retn 10h
CODE:004010A6 DialogProc endp

On voit ici l'instruction leave qui va permettre de rétablir la pile qui a été modifié lors de enter 0,0. Suivi de retn 10h qui marque le retour de la fonction et qui precise en specifiant à 10h de désempiler les 4 argument de DialogProc. Pourquoi 10h ? DialogProc se sert de 4 arguments sur la pile tous sont des DWORD ( DWORD = 4 BYTES ) 4 bytes * 4 argument = 16 soit 10 en hexa.

Analysons les routines des 2 premiers messages traités : WM_CLOSE et WM_INITDIALOG

CODE:0040104E ; ---------------------------------------------------------------------------
CODE:0040104E
CODE:0040104E _CloseDlg: ; CODE XREF: DialogProc+8j
CODE:0040104E push 0 ; nResult
CODE:00401050 push [ebp+hWnd] ; hDlg
CODE:00401053 call EndDialog
CODE:00401058 mov eax, 1
CODE:0040105D jmp short _LeaveProc
CODE:0040105F ; ---------------------------------------------------------------------------
CODE:0040105F
CODE:0040105F _InitDlg: ; CODE XREF: DialogProc+11j
CODE:0040105F push 3E8h ; nIDDlgItem
CODE:00401064 push [ebp+hWnd] ; hDlg
CODE:00401067 call GetDlgItem
CODE:0040106C push eax ; hWnd
CODE:0040106D call SetFocus
CODE:00401072 mov eax, 0
CODE:00401077 jmp short _LeaveProc
CODE:00401079 ; ---------------------------------------------------------------------------

Pour le code executé si l'on recoit le message WM_CLOSE (message qui est declenché lors de l'appuie sur la croix de fermeture de la fenetre) on voit que l'on appel l'API EndDialog en lui passant l'handle de notre boite boite de dialog que l'on met eax à 1 pour etre conforme à la valeur de retour de DialogProc puisqu'on à traité le message et que l'on saute vers le label defini il y a quelques instants vers la fin de la procedure.

Pour WM_INITDIALOG (message qui est déclenché une seule fois au moment de la creation de la fenetre) on appel GetDlgItem, cette fonction permet de recuperer l'handle d'un control, pour ensuite appeler la fonction SetFocus en lui passant cet handle. SetFocus sert à 'donner la main' à un controle ou encore sur un edit-box de placer le curseur tout de suite sur ce controle permettant de pouvoir écrire directement dans celui-ci sans avoir besoin de le selectionner avant. Si on lance le programme on s'apercoit que c'est le champ 'Name' qui va recevoir le focus puisque le curseur est placé dans celui-ci au démarrage. On peux en conclure du n° d'identification du controle qui est celui presicé par dans le 1er argument pushé de GetDlgItem correspond au controle Edit-box 'Name'. On va placer cette info en commentaire. Pareil la suite se conforme à la condition de retour de DialogProc laquelle precise l'exeption de retourné 0 si le message WM_INITDIALOG est traité mais que celui-ci appel dans sa routine fera appel à SetFocus.

Passons au traitement de WM_COMMAND

CODE:00401079 _Command:                                    ; CODE XREF: DialogProc+1Aj
CODE:00401079 mov eax, [ebp+wParam]
CODE:0040107C cmp ax, 3EAh
CODE:00401080 jnz short loc_40108C
CODE:00401082 push [ebp+hWnd] ; hDlg
CODE:00401085 call sub_4010A9
CODE:0040108A jmp short loc_4010A0
CODE:0040108C ; ---------------------------------------------------------------------------
CODE:0040108C
CODE:0040108C loc_40108C: ; CODE XREF: DialogProc+55j
CODE:0040108C cmp ax, 3EBh
CODE:00401090 jnz short loc_4010A0
CODE:00401092 push 0 ; lParam
CODE:00401094 push 0 ; wParam
CODE:00401096 push 10h ; Msg
CODE:00401098 push [ebp+hWnd] ; hWnd
CODE:0040109B call SendMessageA
CODE:004010A0
CODE:004010A0 loc_4010A0: ; CODE XREF: DialogProc+5Fj
CODE:004010A0 ; DialogProc+65j
CODE:004010A0 mov eax, 1

On commence par placer dans eax le 3eme argument wParam on compare ensuite seulement la valeur contenu dans ax - explication : Ce message est declenché quand on actionne un boutton, ou un element d'un menu, d'apres le prototype de WM_COMMAND on voit que wParam contient alors 2 infos :

wNotifyCode = HIWORD(wParam); // notification code 
wID = LOWORD(wParam); // item, control, or accelerator identifier

wParam etant un DWORD on peux le diviser en 2 WORD, le word de partie haute contient le notification code, et le word de la partie basse l'identifiant du controle, en asm pour separer les parties on va se servir d'un registre 32 bits comme eax, si l'on place wParam dans eax, ax de capacité 16 bit ( WORD ) contiendra naturellement le WORD de la partie basse, et si l'on souhaite acceder au WORD de la partie haute il suffit de decaler les bits dans eax afin de faire passer ceux qui se trouvait en partie haute dans la partie basse avce l'instruction shr eax,16, qui decale de 16 bits (1 WORD) vers la partie basse.

Ici donc on compare l'identificateur d'un controle avce la valeur 3EAh, si vous cliqker droit sur cette valeur vous pourrez voir la valeur exprmié sous d'autre base numerique comme décimal, octal et binaire. Si l'identifiant n'est pas egal à cette valeur on saute sur loc_40108C. Ou la on retrouve le meme shema de comparaison de l'identifiant avce la valeur 3EBh cette fois. Si l'identificateur n'est toujours pas egal à cette nouvelle valeur on saute sur un nouveau label qui place 1 dans eax et qui se poursuit par la fin de la procédure. On peux renommer les 2 labels que nous venons de voir le premier nous faisant sauter sur la comparaison avce une autre valeur (qui represente un autre boutton) et le 2eme qui nous envoie vers la fin de la procedure. on renomme le 1er en _TestNextId ( pour test next identifiant ) et le 2eme en _LeaveProc2 (un label _LeaveProc existant deja). Analysons maintenant le cas ou l'identificateur correspond au valeur commencons par la 2eme ( ouias c pas chronologiquement logique mais c plus simple pour le tut enfin je pense)

CODE:0040108C _TestNextId:                                 ; CODE XREF: DialogProc+55j
CODE:0040108C cmp ax, 3EBh
CODE:00401090 jnz short _LeaveProc2
CODE:00401092 push 0 ; lParam
CODE:00401094 push 0 ; wParam
CODE:00401096 push 10h ; Msg
CODE:00401098 push [ebp+hWnd] ; hWnd
CODE:0040109B call SendMessageA

Si l'identificateur du controle qui à été actionné correspond à 3EBh, on va appeler la fonction SendMessageA. Cette fonction sert à envoyer un windows message vers une fenetre, en analysant les argument pushé on peux definir que le message est destiné à cette mem boite de dialog (hWnd) et que le message est WM_CLOSE toujours en cliquant droit pour acceder aux 'use standard symbol constant' et en selectionnant un WM_. Donc la tout deviens clair, si l'identificateur du boutton est 3EBh a va envoyer le Message WM_CLOSE sur notre propre boite de dialog ce qui aura pour effet de la fermer. Si on lance le programme on voit qu'il y un boutton qui permet de fermer le programme, on comprend dès lors que le code que nous venons de voir est celui qui est rataché à ce boutton. Comme sur cette fenetre il n'y a que 2 bouttons on en deduis que l'autre identifiant represente le boutton qui reste : le boutton 'Check', on peux arranger cette partie du code sous IDA en precisant en commentaire les bouttons qui sont actionnés. Si on reviens sur le controle de boutton check on voit qu'on va appeler une nouvelle fonction nommé par defaut sub_4010A9 la on peux en déduire directement que c'est dans cette nouvelle procedure que va se passer la verification du serial, puisque nous avons reussis à analyser tout les traitement des messages et leur evenements, on peux donc renommer aussi cette nouvelle procedure en _CheckProc ce qui au final nous donne :

CODE:00401079 _Command:                                    ; CODE XREF: DialogProc+1Aj
CODE:00401079 mov eax, [ebp+wParam]
CODE:0040107C cmp ax, 3EAh ; boutton 'Check'
CODE:00401080 jnz short _TestNextId
CODE:00401082 push [ebp+hWnd] ; hDlg
CODE:00401085 call _CheckProc ; appel procedure de check serial
CODE:0040108A jmp short _LeaveProc2
CODE:0040108C ; ---------------------------------------------------------------------------
CODE:0040108C
CODE:0040108C _TestNextId: ; CODE XREF: DialogProc+55j
CODE:0040108C cmp ax, 3EBh ; boutton 'Exit'
CODE:00401090 jnz short _LeaveProc2
CODE:00401092 push 0 ; lParam
CODE:00401094 push 0 ; wParam
CODE:00401096 push WM_CLOSE ; Msg
CODE:00401098 push [ebp+hWnd] ; hWnd
CODE:0040109B call SendMessageA ; envoie le message close pour fermer le prog
CODE:004010A0
CODE:004010A0 _LeaveProc2: ; CODE XREF: DialogProc+5Fj
CODE:004010A0 ; DialogProc+65j
CODE:004010A0 mov eax, 1
CODE:004010A5
CODE:004010A5 _LeaveProc: ; CODE XREF: DialogProc+21j
CODE:004010A5 ; DialogProc+32j ...
CODE:004010A5 leave
CODE:004010A6 retn 10h
CODE:004010A6 DialogProc endp

On peux passer à présent à l'analyse de la procedure de check du serial:

Voici la 1ere partie :

CODE:004010A9 ; int __cdecl CheckProc(HWND hDlg)
CODE:004010A9 ; Attributes: bp-based frame
CODE:004010A9
CODE:004010A9 _CheckProc proc near ; CODE XREF: DialogProc+5Ap
CODE:004010A9
CODE:004010A9 hDlg = dword ptr 8
CODE:004010A9
CODE:004010A9 enter 0, 0
CODE:004010AD push esi
CODE:004010AE push edi
CODE:004010AF push edx
CODE:004010B0 push 10h ; nMaxCount
CODE:004010B2 push offset unk_40200D ; lpString
CODE:004010B7 push 3E8h ; nIDDlgItem
CODE:004010BC push [ebp+hDlg] ; hDlg
CODE:004010BF call GetDlgItemTextA
CODE:004010C4 cmp eax, 5
CODE:004010C7 jl short loc_401142
CODE:004010C9 mov esi, offset unk_40200D
CODE:004010CE push esi

Pareil ici on retrouve enter 0, 0 qui va nous permettra d'acceder aux argument de cette fonction via le registre ebp+argument. On trouve aussi une serie de 3 push de registre, généralement on push des registres en debut de procedure lorque celle-ci est succeptible de les modifier alors en les sauvgardant dans la pile on pourra les restituer en fin de procedure en les rappelant par des pop.

Ensuite on entamme les push de la fonction GetDlgItemTextA. (attention de ne pas confondre des simple push avce des push passage d'argument d'une fonction, pour cela il suffit de regarder le prototype et de compter le nombre d'argument qu'elle prend et de re-compter en remontant a partir du call pour determiner quel est le premier push de la fonction), GetDlgItemText permet de recuperer le text d'une fenetre ou d'un controle vers un buffer (variable de type string). La on apprend pleins de chose en examinant ses arguments, nMaxCount specifie le nbr max de Byte à copier dans le buffer, lpString pointe vers l'adresse de notre buffer qui recevra le text du controle, et 3E8h determine la valeur le controle en lui meme. Si on se rappelle plus tot on s'est servi de SetFocus en specifiant cette meme valeur on en deduit que c'est donc le meme controle càd l'edit-box 'Name', on arrange ca à notre sauce :

CODE:004010A9         enter   0, 0
CODE:004010AD push esi
CODE:004010AE push edi
CODE:004010AF push edx
CODE:004010B0 push 10h ; nMaxCount = nbr de char à copier
CODE:004010B2 push offset NameBuffer ; lpString = pointeur vers le buffer qui recupe le Nom
CODE:004010B7 push 3E8h ; nIDDlgItem = 'Name'
CODE:004010BC push [ebp+hDlg] ; hDlg
CODE:004010BF call GetDlgItemTextA ; Recupere texte dans Edit-box 'Name'

La fonction GetDlgItemTextA retourne le nbr de char copié dans le buffer. Suite du code :

CODE:004010C4         cmp     eax, 5                       
CODE:004010C7 jl short loc_401142
CODE:004010C9 mov esi, offset NameBuffer
CODE:004010CE push esi

On compare donc le nbr de char copié dans le buffer avce 5, si c'est moins au saute vers un label ou l'on va appeler la fonction SetDlgItemTextA et si on regarde dans les argument on voit que l'on pousse une string declaré comme suit :

aLeNomDoitCompo db 'LE NOM DOIT COMPORTER 5 LETTRES MINIMUM',0

On renomme cette variable en 'Mess_5char' et le label qui nous envoie vers cette routine '_5charMin' ce qui donne:

CODE:004010C4         cmp     eax, 5                       ; cmp nbr de char copié, 5
CODE:004010C7 jl short _5charMin ; jump si inferieur
CODE:004010C9 mov esi, offset NameBuffer ; esi = offset NameBuffer
CODE:004010CE push esi ; sauvgarde esi

On continue la procedure Check:

CODE:004010CF loc_4010CF:                                  ; CODE XREF: _CheckProc+32j
CODE:004010CF mov dl, [esi]
CODE:004010D1 test dl, dl
CODE:004010D3 jz short loc_4010DD
CODE:004010D5 add dl, 2
CODE:004010D8 mov [esi], dl
CODE:004010DA inc esi
CODE:004010DB jmp short loc_4010CF
CODE:004010DD ; ---------------------------------------------------------------------------
CODE:004010DD
CODE:004010DD loc_4010DD: ; CODE XREF: _CheckProc+2Aj

APres analyse de ce bloc on comprend que l'on place dans dl , le byte qui se touve à l'adresse de esi qui est NameBuffer, donc le 1er caractere du Nom on test si ce byte egal 0 soit la valeur de fin d'une strings , c'est pas le cas on continue en ajoutant 2 à ce byte et on remplace l'ancien byte par le nouveau dans la variable NameBuffer on incremente esi qui pointe alors sur le caractere suivant et on jump au debut de cette boucle tant que la chaine n'est pas fini, On commente tout ca :

CODE:004010CF _Boucle1:                                    ; CODE XREF: _CheckProc+32j
CODE:004010CF mov dl, [esi] ; place dans dl, 1er char de 'Name'
CODE:004010D1 test dl, dl ; test si le char est le char de fin de string
CODE:004010D3 jz short _endBoucle1 ; aukel cas saute en _endBoucle1
CODE:004010D5 add dl, 2 ; augment la valeur du byte de 2
CODE:004010D8 mov [esi], dl ; ecrase l'ancien byte avce le nouveau
CODE:004010DA inc esi ; char suivant de Name
CODE:004010DB jmp short _Boucle1 ; boucle1
CODE:004010DD ; ---------------------------------------------------------------------------
CODE:004010DD
CODE:004010DD _endBoucle1: ; CODE XREF: _CheckProc+2Aj

Et on continue avce le reste de l'algo maintenant que vous avez compris (j'espere) la technique d'analyse et de renommage petit à petit des label et des variables:

CODE:004010DD _endBoucle1:                                 ; CODE XREF: _CheckProc+2Aj
CODE:004010DD push 10h ; nMaxCount
CODE:004010DF push offset unk_40204D ; lpString
CODE:004010E4 push 3E9h ; nIDDlgItem
CODE:004010E9 push [ebp+hDlg] ; hDlg
CODE:004010EC call GetDlgItemTextA
CODE:004010F1 cmp eax, 5
CODE:004010F4 jl short loc_401127
CODE:004010F6 mov edi, offset unk_40204D
CODE:004010FB pop esi
CODE:004010FC
CODE:004010FC loc_4010FC: ; CODE XREF: _CheckProc+61j
CODE:004010FC mov dl, [edi]
CODE:004010FE mov dh, [esi]
CODE:00401100 test dh, dh
CODE:00401102 jz short loc_40110C
CODE:00401104 cmp dh, dl
CODE:00401106 jnz short loc_401127
CODE:00401108 inc esi
CODE:00401109 inc edi
CODE:0040110A jmp short loc_4010FC
CODE:0040110C ; ---------------------------------------------------------------------------
CODE:0040110C
CODE:0040110C loc_40110C: ; CODE XREF: _CheckProc+59j
CODE:0040110C push 0 ; uType
CODE:0040110E push offset aBravoGoodSeria ; lpCaption
CODE:00401113 push offset aBravoGoodSeria ; lpText
CODE:00401118 push [ebp+hDlg] ; hWnd
CODE:0040111B call MessageBoxA
CODE:00401120 pop edx
CODE:00401121 pop edi
CODE:00401122 pop esi
CODE:00401123 leave
CODE:00401124 retn 4
CODE:00401127 ; ---------------------------------------------------------------------------
CODE:00401127
CODE:00401127 loc_401127: ; CODE XREF: _CheckProc+4Bj
CODE:00401127 ; _CheckProc+5Dj
CODE:00401127 push 0 ; uType
CODE:00401129 push offset aErrorBadSerial ; lpCaption
CODE:0040112E push offset aErrorBadSerial ; lpText
CODE:00401133 push [ebp+hDlg] ; hWnd
CODE:00401136 call MessageBoxA
CODE:0040113B pop edx
CODE:0040113C pop edi
CODE:0040113D pop esi
CODE:0040113E leave
CODE:0040113F retn 4
CODE:00401142 ; ---------------------------------------------------------------------------
CODE:00401142
CODE:00401142 _5charMin: ; CODE XREF: _CheckProc+1Ej
CODE:00401142 push offset Mess_5char ; lpString
CODE:00401147 push 3E9h ; nIDDlgItem
CODE:0040114C push [ebp+hDlg] ; hDlg
CODE:0040114F call SetDlgItemTextA
CODE:00401154 pop edx
CODE:00401155 pop edi
CODE:00401156 pop esi
CODE:00401157 leave
CODE:00401158 retn 4
CODE:00401158 _CheckProc endp

Une fois travaillé cette partie on obtient :

CODE:004010DD _endBoucle1:                                 ; CODE XREF: _CheckProc+2Aj
CODE:004010DD push 10h ; nMaxCount
CODE:004010DF push offset SerialBuffer ; lpString
CODE:004010E4 push 3E9h ; nIDDlgItem = Edit-Box Serial
CODE:004010E9 push [ebp+hDlg] ; hDlg
CODE:004010EC call GetDlgItemTextA ; Recupere Text de l'edit Serial
CODE:004010F1 cmp eax, 5 ; compare nbr de char copié avec 5
CODE:004010F4 jl short _BadBoy ; si inferieur à 5 saute vers messagebox bad boy
CODE:004010F6 mov edi, offset SerialBuffer ; place dans edi, adresse de SerialBUffer
CODE:004010FB pop esi ; recupere esi => esi = offset NameBuffer
CODE:004010FC
CODE:004010FC _Boucle2: ; CODE XREF: _CheckProc+61j
CODE:004010FC mov dl, [edi] ; dl = char de SerialBuffer
CODE:004010FE mov dh, [esi] ; dh = char de NameBuffer
CODE:00401100 test dh, dh ; test fin de NameBuffer
CODE:00401102 jz short _GoodBoy ; saute vers Good boy fin de string (tout bon)
CODE:00401104 cmp dh, dl ; cmp les 2 char
CODE:00401106 jnz short _BadBoy ; si pas egal saute vers badboy
CODE:00401108 inc esi ; char suivant de NameBuffer
CODE:00401109 inc edi ; char suivant de Serial Buffer
CODE:0040110A jmp short _Boucle2
CODE:0040110C ; ---------------------------------------------------------------------------
CODE:0040110C
CODE:0040110C _GoodBoy: ; CODE XREF: _CheckProc+59j
CODE:0040110C push 0 ; uType
CODE:0040110E push offset aBravoGoodSeria ; lpCaption
CODE:00401113 push offset aBravoGoodSeria ; lpText
CODE:00401118 push [ebp+hDlg] ; hWnd
CODE:0040111B call MessageBoxA ; Good Boy
CODE:00401120 pop edx
CODE:00401121 pop edi
CODE:00401122 pop esi
CODE:00401123 leave
CODE:00401124 retn 4
CODE:00401127 ; ---------------------------------------------------------------------------
CODE:00401127
CODE:00401127 _BadBoy: ; CODE XREF: _CheckProc+4Bj
CODE:00401127 ; _CheckProc+5Dj
CODE:00401127 push 0 ; uType
CODE:00401129 push offset aErrorBadSerial ; lpCaption
CODE:0040112E push offset aErrorBadSerial ; lpText
CODE:00401133 push [ebp+hDlg] ; hWnd
CODE:00401136 call MessageBoxA ; Bad Boy
CODE:0040113B pop edx
CODE:0040113C pop edi
CODE:0040113D pop esi
CODE:0040113E leave
CODE:0040113F retn 4
CODE:00401142 ; ---------------------------------------------------------------------------
CODE:00401142
CODE:00401142 _5charMin: ; CODE XREF: _CheckProc+1Ej
CODE:00401142 push offset Mess_5char ; lpString
CODE:00401147 push 3E9h ; nIDDlgItem
CODE:0040114C push [ebp+hDlg] ; hDlg
CODE:0040114F call SetDlgItemTextA ; Name Min 5 lettres
CODE:00401154 pop edx
CODE:00401155 pop edi
CODE:00401156 pop esi
CODE:00401157 leave
CODE:00401158 retn 4
CODE:00401158 _CheckProc endp

Et voila l'analyse complete de ce crackme, l'algo du serial est on ne peux plus simple il ajoute tout simplement 2 à la valeur de chaque caractere du Nom et le serial doit correspondre à ce Nom modifié.

Pour ceux que ca interresse ou qui veulent comparer ce listing de IDA avce la vrai source voici la source complete du crackme en asm - Download me
Note: Si le lien ne marche pas, visitez notre site: shmeitcorp.tk