Ecrire un KeyGenerator-Patcher et faire de Zolies box en ASM

Inintiation à l'assembleur

For Newbies Only

Official CrackMe#4

By Christal avec des commentaires de X-or


Part I

Tripatouillage autour d'un serial:

Commençons par définir les éléments qui vont permettre de trouver comment le serial est généré.
Chaque programme est un cas particulier. Beaucoup d'entre eux peuvent se ranger dans tels ou tels catégories, d'autres sont atypiques...
Ce crackme est particulièrement simple à cerner, que ce soit en Live Approach ou en Dead listing.
Par contre, la cible étant compressée par UPX, pour obtenir les StringsData qui vont bien, le plus simple est d'utiliser UPX lui même pour décompresser l'application, avant d'utiliser Wdasm:

:00457E2A    mov edx, dword ptr [ebp-04]
:00457E2D    pop eax
:00457E2E    cal1 00403B84
:00457E33    jne 00457E4F
:00457E35    push 00000000

* Possible StringData Ref from Code 0bj ->"Congratz !"

:00457E37    mov ecx, 00457F38

* Possible StringData Ref from Code 0bj 
->"You cracked the CFF CrackMe #4 "
->"! P1ease send your solution to "
->"acidbytes@gmx.net !"

Une fois ciblé la string data "positive", l'ensemble Call/JNE va être l'aiguillage Good Boy/Bad Boy, et le générateur du code va se situer juste au dessus. Son étude manquant du moindre intéret, il n'y aurait pas là matière à une tut, si je ne voualais pas m'amuser à créer des boite de dialog un peu plus sympas que d'habitude...



PartII

Le générateur de code.

Le détails concernants l'utilisation des APIs sont de X-or (http://www.multimania.com/w32asm/ )

      .386             ; les fonctions utilisées sont compatibles 
                       ; avec l'architecture du 386 (vous avez 
                       ; aussi le 486 et le 586 pour les Pentium)
 .model flat, stdcall
 option casemap :none  ; case sensitive 
                       ; il différenciera majuscule et minuscule
;=========================================================================

Les includes (inc) et les library (lib) vont êtres choisis en fonctions des APIs que vous utiliserez.

Les fonctions de l'api Windows sont contenues dans des librairies chargées dynamiquement au démarrage du programme. Ces librairies (les DLLs) les plus couramment utilisées sont kernel32.dll pour la gestion des processus et de la mémoire, user32.dll pour l'interface et gdi32.dll pour les fonctions graphiques. Le problème est que le programme ne charge pas toutes les dlls au démarrage.
Il faut donc préciser à MASM que vous utilisez une fonction externe mais en plus qu'elle doit être importée d'une dll précise. Pour indiquer qu'une fonction externe est utilisée dans le module (le fichier source asm) il faut ajouter au début du module les lignes qui suivent :

      include \masm32\include\windows.inc
      include \masm32\include\user32.inc
      include \masm32\include\kernel32.inc
      include \masm32\include\gdi32.inc
      include \masm32\include\comdlg32.inc 
      include \masm32\include\masm32.inc
      include \masm32\include\shell32.inc 

      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib
      includelib \masm32\lib\gdi32.lib
      includelib \masm32\lib\comdlg32.lib 
      includelib \masm32\lib\masm32.lib
      includelib \MASM32\LIB\shell32.lib 

Il y a encore un point à préciser sur les fonctions de l'api win32. Dans la documentation officielle vous trouverez le nom standard des fonctions, mais dans les dlls vous aurez des fonctions du même nom succédées d'un A ou d'un W (par ex MessageBoxA ou MessageBoxW. Le A signifie ANSI et le W signifie UNICODE, ces deux lettres précisent donc le format des chaînes de caractères utilisées par les fonctions donc celles que vous passez en paramètre et celles que vous recevez en retour de fonction.

;=========================================================================

;déclaration des macros :

        szText MACRO Name, Text:VARARG    -> permet d'inclure dans le code une chaine de caractères
          LOCAL lbl
            jmp lbl
              Name db Text,0
            lbl:
          ENDM
          
      LoadSingle	MACRO	PieceOfVariable, bBrush 
	  invoke LoadImage ,hInstance, 200, IMAGE_BITMAP, 0, 0, 0 ; charge l'image de fond d'écran
	  mov    hSkin&PieceOfVariable , eax
	    IF bBrush
		invoke CreatePatternBrush, eax
		mov    hbSkin&PieceOfVariable, eax
	    ENDIF
      ENDM  
;=========================================================================
; Et pour commencer, il faut déclarer les constantes 
; et les variables dont on aura besoin :
.const
;=========================================================================
        IDC_Edit1    equ 103  ; Le premier champ d'édition (Name) se voit affecter la valeur 103
        IDC_Edit2    equ 104  ; Dans la mesure ou le fichier RSRC, dans lequel se 
        IDC_Edit3    equ 105  ; trouve les informations de traçage de la boite
        IDC_Exit     equ 109  ; de dialogue, vous n'êtes pas libre de choisir ici l'ID
        IDC_Gen      equ 110
        IDC_Copy     equ 111
        IDC_Static   equ 101
        IDC_Url      equ 115
        IDC_About    equ 112
        IDC_Brownse  equ 113
        IDC_Patch    equ 114
        ID_BACKUP    equ 116

        ICON_SMALL   equ 0	
        IDI_ICON     equ 500
;=========================================================================
 .data
;                       Données dont vous aurez besoin 
;=========================================================================

; chacunes de ces strings vont être stockées à une adresse précise, lors de
; la compilation. Elles doivent impérativement se terminer par un 0
; (NULL Terminated String)

        hIconImage  dd 0   -> réservation d'un espace mémoire
        hIcon       dd 0   -> pour le stockge des handles
        hEdit1      dd 0   -> qui seront nécessaires.
        hEdit2      dd 0
        hBACKUP     dd 0
        hStatImage  dd 0
        hBmp        dd 0
        
        dlgname     db "dialogbox",0
        StaticClass db "static",0
        error       db "Name supérieur à 6 digits, please...",0 
        url         db "www.christal.citeweb.net",0
        url_text    db "http://Christal.ctw.net",0 
        open_site   db "open",0

        TextAbout   db "         Official CrackMe #4",0Dh,0Ah,0Ah 
                    db "              - KeyGenarator",0Dh 
                    db "              - Memory Patch ",0Dh,0Ah,0Ah
                    db "         Coded by Christal        ",0 

; TextAbout s'affiche sur quatre lignes, dans la boite About, avec un CR (0D, 0A)

        caption_msg db "Official CrackMe#4",0
        running     db "Process en cours:",0Dh,0Ah
                    db "Veuillez fermer l'application",0
        
        bad_version db "Mauvaise version...",0 
        success     db "Cible patchée... ",0
        cible_abs   db "Cible Absente...",0  
        
        lg_first    DWORD 0    -> mémoires réservées
        lg_last     DWORD 0    -> initialisées à 0
        Lg_path     DWORD 0
        
        FilterString    db "CrackMe#4",0,"CrackMe#4.exe",0
                        db "Tous les Fichiers",0,"*.*",0,0
        cible_ok        db "Fichier chargée...",0         
        szBackupName    db MAX_PATH dup(0)
        szBackupExt     db ".bak",0                       

        Key             dd 40 dup (0)
        conversion      db '%d',0
        lzUSER32        db 'USER32',0
        lzWSPRINTFA     db 'wsprintfA',0

        offset1         dd  0044A6BCh
        patch1          dw  74h
.data? 
;==========================================================================  
;                    Variables dont vous aurez besoin 
;c'est variables, n'ayant pas par avance une taille définie, sont déclarées 
;    à part, pour que le compilateur leur réserve une place suffisante 
;==========================================================================
        hsmem           dd ?
        smem            dd ?
        nome            dd ?
        hDlg            dd ?
        cdc             dd ?
        fmap            dd ?
        faddr           dd ?

        lg_serial       DWORD ? 

        serial          db 10 dup (?)        ; place réservée pour serial : 10 octets
        buffer          db 90 dup (?) 
        buffer1         db 20 dup (?)

        Directory       db 512 dup (?)
        
        hFile           HANDLE     ?        ; Handle du fichier. 
        hInstance       HINSTANCE  ?
        hSkinMain_Back  HANDLE     ?
        hbSkinMain_Back HANDLE     ?

        startinfo      STARTUPINFO         <> ; déclaration des structures
        pi             PROCESS_INFORMATION <>
        DBEvent        DEBUG_EVENT         <>
        ofn            OPENFILENAME        <>        

    
    hInstance 	HINSTANCE ?  ; très important, c'est l'identifiant que votre 
                                                          ; application va se voir définir (en fonction
                                                          ; des autres tâches) et qui permettra à l'OS  
                                                          ; de savoir qui est "propriétaire" des 
                                                          ; événements qui se produiront 

Le paramètre hInstance est un nombre identifiant le programme en cours d'exécution. hInstance signifie handle Instance, sachez qu'un handle est une sorte d'index dans un tableau contenant des caractéristiques. Ici Windows tient un tableau d'info sur le programme et notre numéro d'handle lui permet de savoir ou regarder dans son tableau pour avoir le numéro qu'on lui donne (on peut connaître le handle d'un programme, sous SoftIce, en faisant TASK pour trouver le nom de la tâche recherchée, puis HWND pour obtenir le tableau des différents événements qui s'y rattache)

:task
TaskName   SS:SP      StackTop  StackBot  StackLow  TaskDB  hQueue  Events
Crackme#   0000:0000  006AB000  006B0000            39A6    39DF    0000
Spool32    0000:0000  0063E000  00640000            36D6    339F    0000

:hwnd Crackme#
Window Handle   hQueue  SZ  QOwner    Class Name            Window Procedure
 06BC(1)        39DF    32  CRACKME#4 TForm1  (delphi)      5557:000000FE
  0544(2)       39DF    32  CRACKME#4 Edit    (1er champ)   5557:0000016C
  0510(2)       39DF    32  CRACKME#4 Edit    (2d  champ)   5557:00000156
  0B40(2)       39DF    32  CRACKME#4 Edit                  5557:00000140
 052C(1)        39DF    32  CRACKME#4 TApplication          5557:00000064

La notion de fenêtre est extrêmement large pour le programmeur sous Windows. En effet une fenêtre peut très bien être la fenêtre d'une application, mais aussi tout composant de cette fenêtre : edit box, text box, bouton, etc...

    Lg_serial         DWORD ?       ; les DWORD ? sont des adresses réservées, 
                                    ; de taille non définies, mais dont j'aurais 
                                    ; besoin pour stocker mes informations,
                                    ; à la différence des DWORD de 8 bits.

La déclaration de structures permet de réserver un espace mémoire dans lequel les valeurs retrournées (au pluriel) par l'appel à certaines APIs pourront être stockées, et rappellées par des "." d'union comme mov ofn.lpstrFile, OFFSET Directory.

A titre d'exemple, la structure OPENFILENAME contient des information que les fonctions GetOpenFileName et GetSaveFileName utilisent pour initilaiser les boites de dialogues de "Open" ou "Save As". Après que l'utilisateur ait fermé la boite de dialogue, le system retourne les informations liées à son choix dans la structure définie:

-> initialisation de la structure OPENFILENAME ayant pour nom 'ofn'.
Elle définira les différentes propriétés de la boite permettant la récupération du fichier CrackMe#4.exe
(voir traduction par Morgatte du tut 30 d'Izcelion, ainsi que pour tous les autres commentaires s'y rapportant)

Chacunes de ces strings vont être stockées à une adresse précise, lors de la compilation. Elles doivent impérativement se terminer par un 0 (NULL Terminated String)

typedef struct tagOFN { // ofn 
    DWORD         lStructSize; 
    HWND          hwndOwner; 
    HINSTANCE     hInstance; 
    LPCTSTR       lpstrFilter; 
    LPTSTR        lpstrCustomFilter;
    DWORD         nMaxCustFilter; 
    DWORD         nFilterIndex; 
    LPTSTR        lpstrFile; 
    DWORD         nMaxFile; 
    LPTSTR        lpstrFileTitle; 
    DWORD         nMaxFileTitle; 
    LPCTSTR       lpstrInitialDir; 
    LPCTSTR       lpstrTitle; 
    DWORD         Flags; 

    WORD          nFileOffset; 
    WORD          nFileExtension; 
    LPCTSTR       lpstrDefExt; 
    DWORD         lCustData; 
    LPOFNHOOKPROC lpfnHook; 
    LPCTSTR       lpTemplateName; 
} OPENFILENAME; 
typedef struct _PROCESS_INFORMATION { // pi
    HANDLE hProcess; 
    HANDLE hThread; 
    DWORD dwProcessId; 
    DWORD dwThreadId; 
} PROCESS_INFORMATION; 
     ------------------------------------
typedef struct _DEBUG_EVENT { // de 
    DWORD dwDebugEventCode; 
    DWORD dwProcessId; 
    DWORD dwThreadId; 
    union { 
     EXCEPTION_DEBUG_INFO Exception; 
     CREATE_THREAD_DEBUG_INFO CreateThread; 
     CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
     EXIT_THREAD_DEBUG_INFO ExitThread; 
     EXIT_PROCESS_DEBUG_INFO ExitProcess; 
     LOAD_DLL_DEBUG_INFO LoadDll; 
     UNLOAD_DLL_DEBUG_INFO UnloadDll; 
     OUTPUT_DEBUG_STRING_INFO DebugString; 

        RIP_INFO RipInfo; 
    } u; 
} DEBUG_EVENT; 
typedef struct _STARTUPINFO { // si
    DWORD   cb; 
    LPTSTR  lpReserved; 
    LPTSTR  lpDesktop; 
    LPTSTR  lpTitle; 
    DWORD   dwX; 
    DWORD   dwY; 
    DWORD   dwXSize; 
    DWORD   dwYSize; 
    DWORD   dwXCountChars; 
    DWORD   dwYCountChars; 
    DWORD   dwFillAttribute; 
    DWORD   dwFlags; 
    WORD    wShowWindow; 
    WORD    cbReserved2; 
    LPBYTE  lpReserved2; 
    HANDLE  hStdInput; 
    HANDLE  hStdOutput; 
    HANDLE  hStdError; 

} STARTUPINFO, *LPSTARTUPINFO; 

;=========================================================================
; fin de la déclaration des constantes et des variables,
; et début du programme
;=========================================================================

.code

start:


; Le programme doit obligatoirement commencer par START.
; Il deviendra l'EntryPoint de l'application

invoke GetModuleHandle, NULL ; on récupère le handle de l'application
mov    hInstance, eax        ; différent à chaque nouveau lancement du prog

La première étape va être de récupérer le handle du programme. Pour cela on utilise la fonction GetModuleHandleA avec comme paramètre 0. Le handle correspondant au process principal.
Notez l'utilisation de la macro call GetModuleHandle, 0
Le handle est dans eax... mov hInstance, eax va le stocker.
Je rappelle qu'en retour d'une fonction win32, c'est dans eax que se trouve la valeur de retour.
En ce qui concerne les registres, eax, ecx et edx ne sont pas préservés lors des appels aux fonctions de l'api win32. Prenez donc vos précautions lorsque vous utilisez ces registres...
Pour les registres ebx, edx, edi, esi et ebp, c'est le contraire, ils sont toujours préservés et Windows attend de vous que vous en fassiez de même....

      invoke DialogBoxParam,hInstance,ADDR dlgname,0,ADDR WndProc,0 ; appel de la DialogBox dont les caractéristiques
                                                                  ; sont définies dans la section RSRC
   invoke ExitProcess,eax
;=========================================================================
; DialogBoxParam va lancer la procédure principale, WndProc, dont le nom
; est obligatoirement suivi de PROC et doit se terminer par ENDP
;=========================================================================
WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM , lpszSkinName:DWORD (est lié au fond d'écran de la box)

hWnd est l'handle de l'application, uMsg, wParam et lParam, sont des paramètres utilisés ou non par les messages:

Maintenant vous devez connaître les messages susceptibles de vous être envoyés par Windows. Pour cela cherchez dans la documentation officielle (le win32.hlp) la liste vous intéressant. Pour une fenêtre ce sera "Window messages", pour la souris "mouse input messages" pour une boite de dialogue "dialog box messages" etc...
Ensuite, vous devez, dans votre fonction WindowProc, redéfinir la gestion du message et ensuite renvoyer 0 ou bien appeler la fonction DefWindowProc, qui fera un traitement standard du message. Ce système est très pratique car il permet d'intercepter les messages du système et les redéfinir pour nous servir. Par exemple, lorsque vous clickez sur la croix pour fermer une appli, le msg WM_CLOSE est envoyé, qui par le traitement standard appelle la fonction DestroyWindow qui elle envoie le message WM_DESTROY à la fenêtre provoquant la fermeture de l'application. Si vous redéfinissez le traitement du msg WM_CLOSE dans votre appli en le prenant en charge dans WindowProc, vous pouvez empêcher la fermeture de l'appli ou demander une confirmation...
Mais quant on parle de messages, ca ne s'arrêtent pas à la seule réception de messages...
Vous pouvez en envoyer, à l'aide de la fonction SendMessage. Ceci a de l'intérêt par exemple lorsque vous ne voulez pas ré écrire un bout de code qui gérait un autre message il suffit de vous envoyer le message qui exécutera le code en question. Vous pouvez aussi interagir avec les fenêtres, par exemple, en envoyant le message WM_SETTEXT à une appli, avec en paramètre une chaîne de caractères, vous pourrez changer le titre de l'appli, en l'envoyant à une boite d'édition, vous modifierez le texte qu'elle contient

    local    r       :RECT                  ; wc et msg sont des variables locales. Elles ne
    local    p       :POINT                 ; seront reconnues qu'au sein de WinMain PROC

    local    hdc_db               :DWORD    ; les variables commencant 
    local    hdc_bmp              :DWORD    ; par h, seront des handles-filles
    local    hm_bmp               :DWORD 
	
    local    nSkinNum             :DWORD
    local    szNewSkin[MAX_PATH]  :BYTE

    local    szFullSkin[MAX_PATH] :BYTE 
      .IF uMsg == WM_INITDIALOG              ; initialisation des données de la DialogBox

               LoadSingle Main_Back, TRUE   ; appel à une des deux macros -> load le fond d'écran

dans eax se trouve l' "événement" associé à la dlgbox que Windows renvoi, ici 110 = WM_INITDIALOG, c-à-d la fenêtre est en train de se créer.

      ; load l'icon à partir des ressources (définie dans le fichier RSRC.RC)  
              invoke  LoadIcon,hInstance,IDI_ICON   ; = 500 : IDentificateur de l'icone
              mov     hIcon, eax
      
              invoke SendMessage,hWin,WM_SETICON,1,hIcon

      ; control handles
              invoke  GetDlgItem,hWin,IDC_Edit1       ; récupère le handle
              mov     hEdit1, eax                     ; des "fenêtres" à gérer
              invoke  GetDlgItem,hWin,IDC_Edit2       ; au sein d'une dialogBox
              mov     hEdit2, eax           
              invoke  GetDlgItem, hWin, ID_BACKUP
      	      mov    dword ptr [hBACKUP], eax
      
      	     invoke  SendMessage, hBACKUP, BM_SETCHECK, BST_CHECKED, 0   ; check la fenêtre Backup
      
      ; load le bitmap de la "vignette"
              invoke  StaticImage,NULL,hWin,178,17,121,44,IDC_Static
              mov     hStatImage, eax
              invoke  LoadBitmap,hInstance,100  ; (ID = 100 définie dans les ressources)
              mov     hBmp, eax  
              
      ; place l'image dans le static control (vigentte)
              invoke  SendMessage,hStatImage,STM_SETIMAGE,IMAGE_BITMAP,hBmp
              RET
      ; place l'image dans la Boite de dialogue (fond d'ecran)
	.elseif uMsg == WM_ERASEBKGND
		invoke GetClientRect ,hWin, addr r
		invoke CreateCompatibleDC ,wParam
		mov    hdc_db, eax
		invoke CreateCompatibleBitmap	,wParam, r.right, r.bottom
		mov    hm_bmp, eax
		invoke SelectObject, hdc_db, hm_bmp
		invoke FillRect, hdc_db, addr r, hbSkinMain_Back
		invoke GetClientRect, hWin, addr r
		invoke BitBlt,wParam, 0, 0, r.right, r.bottom, hdc_db, 0, 0, SRCCOPY
		
       ; traitement du texte (sans lui le texte apparaitrait dans de petits rectangles)
      .elseif uMsg==WM_CTLCOLORSTATIC 

            mov    eax, hEdit2                ; évite le traitement du champ edition 2
            cmp    lParam,eax
            je     no_color
            invoke SetTextColor,wParam,Yellow
            invoke SetBkMode, wParam,1        ; (1 = transparent, 2 = opaque)
            invoke GetStockObject, NULL_BRUSH ; (fond du texte transparent)
       no_color:     
            ret 
      .ENDIF  

      .IF uMsg == WM_COMMAND                  ; si un évenement survient à la boite de dialogue
          .if wParam == IDC_Exit              ; sa gestion commence ici, comme par exemple    
            invoke EndDialog,hWin,0           ; un clic sur le bouton "EXIT"
            invoke ExitProcess, NULL        

EAX va renvoyer la valeur: 111 = WM_COMMAND , et déterminer le type d'évenement dans wParam. Sous Wdasm, on le voit ainsi:

* Referenced by a (U)nconditional or (C)onditiona1 Jump at Addresses:
|:0040111E(U), :00401198(U), :004011A1(C)

:004011CF 817DOC11010000          cmp dword ptr [ebp+0C], 00000111
:004011D6 0F859DOOOOOO            jne 00401279 -> pas de message

; enregistre le contenu de Key dans le ClipBoard   
        .elseif wParam == IDC_Copy
              invoke GlobalAlloc,GMEM_MOVEABLE+GMEM_DDESHARE,addr lg_serial
              mov    hsmem,eax
              invoke GlobalLock,hsmem
              mov    smem,eax
              invoke lstrcpy,smem,addr Key
              invoke GlobalUnlock,hsmem
              invoke OpenClipboard,hWin
              invoke EmptyClipboard
              invoke SetClipboardData,CF_TEXT,hsmem
              invoke CloseClipboard
              invoke GetDlgItem,hDlg,hEdit1
              invoke SetFocus,eax

         .endif

      .elseif uMsg == WM_CLOSE
        invoke EndDialog,hWin,0

La fonction EndDialog détruit la fenêtre spécifiée. La fonction envoie un message pour que la fenêtre soit désactivée. La fonction détruit également le menu Window (s'il y en a), efface les messages dans la file d'attente, détruit les timers...

      .ENDIF
      
      .IF wParam==IDC_Gen

       push   20
       push   offset Key
       call   RtlZeroMemory                              ; remplit de 20 zéros les adresses de stockage des variables

       push   50
       push   offset serial
       call   RtlZeroMemory
          
       invoke GetDlgItemTextA,hWin,103, ADDR buffer1,10  ; saisie le texte se trouvant dans le 1er champ pour le placer  
       cmp    eax, 06                                    ; dans l'adresse pointée par BUFFER, limité à 10 caractères.
       jl     erreur
       mov    [lg_first],eax
       lea    edi,[offset buffer1] 

La fonction GetDlgItemTextA copie le texte du champ pointé par hEdit1 (ID = 103) dans un buffer (défini dans .const).

La valeur de retour de cette fonction sera placée dans EAX et contiendra le nombre de caractères qui auront été saisi, non compris le 00 final (NULL Terminated String) qui clôture la fin d'une chaîne de caractères (string). La fonction se présente ainsi:

UINT GetDlgItemText(

    HWND hDlg,           // handle de la DialogBox
    int nIDDlgItem,     // identificateur
    LPTSTR lpString,   // adresse du buffer 
    int nMaxCount     // taille maximale de la string à copier );

Au moment du break, la pile (stack) ressemblera à ceci:

[ESP+10h]    - contiendra nMaxCount
[ESP+0Ch]    - contiendra IpString
[ESP+08h]    - contiendra nIDDlgItem
[ESP+04h]    - contiendra hwnd
[ESP+00h]    - contiendra le Return EIP
;=========================================================================
;                      Key Generator pour le Crackme#4
;=========================================================================

      xor    ecx,ecx                   ; compteur
      movsx  eax, byte ptr [edi]       ; EDI pointe sur l'adresse du Name entré
      imul   esi,eax,2                 ; EAX = 1er caractère x 2, résultat dans ESI
@@:      
      inc    ecx                       ; incrémente caractère suivant
      cmp    ecx,5                     ; est ca que 6 caractères ont été traités ?
      jg     end_imul                  ; si oui -> quitte la boucle
      mov    al, byte ptr [edi+ecx]    ; charge caractère suivant
      imul   eax,eax,2                 ; multiplie sa valeur ASCII par 2
      add    esi,eax                   ; ajoute le résultat dans ESI
      jmp    @B                        ; boucle

end_imul:
              
      mov   eax, dword ptr [lg_first]  ; récupère la longeur du Name
      imul  eax,eax,2                  ; la multiplie par 2
      add   eax, esi                   ; ajoute le résultat dans ESI
      

      push eax                         ; transforme le résultat (DWORD)
      push offset conversion           ; en une chaine de caractères 
      push offset Key                  ; le résultat sera dans Key
      call wsprintfA 

;========================================================================= 
;                           Affichage divers  
;=========================================================================

; affiche le serial généré dans le second champ
invoke SetWindowTextA,hEdit2,ADDR Key

La fonction SetWindowText change le texte du champ spécifié (the specified window's title bar).

BOOL SetWindowText(

    HWND hWnd,          // handle de la fenêtre
    LPCTSTR lpString   // adresse de la string qui devra être affichée
   );
       ret
erreur:
       invoke  SetWindowTextA,hEdit2, ADDR error  ; si moins de 6 caractères entrés
       ret 
    .endif

;                           connection Internet     
     .IF wParam==IDC_Url
        invoke  ShellExecuteA,hWin,addr open_site,addr url,NULL,NULL,01
     .endif  

;                           Boite about
     .IF wParam==IDC_About
        invoke  MessageBoxA,hWin,addr TextAbout,addr caption_msg,MB_OK
(voir texte de Morgatte sur les MessageBox)
     .endif  

;                           Patch du process
     .IF wParam==IDC_Patch
         cmp    dword ptr [serial],0              ; pour éviter les plantages
         jne    processrunning                    ; et afin que le programme ne cherche
         mov    dword ptr [serial],offset retour  ; pas deux fois à ouvrir le même process
         call   memorypatch
processrunning: 
         invoke MessageBoxA, NULL, addr running,addr caption_msg, MB_OK         
retour:
     .endif

;                           Recherche d'un programme

     .IF wParam==IDC_Brownse
         call  OpenDialog
     .endif

 ret
WndProc endp            ; fin de la procédure principale
;=========================================================================
;                                                                      affichage BitMap
;=========================================================================
StaticImage proc lpText:DWORD,hParent:DWORD,
                 a:DWORD,b:DWORD,wd:DWORD,ht:DWORD,ID:DWORD

    invoke CreateWindowEx,WS_EX_STATICEDGE,\
    ADDR StaticClass ,lpText,\
    WS_CHILD or WS_VISIBLE or SS_BITMAP,\
    a,b,wd,ht,hParent,ID,\
    hInstance,NULL
    ret
StaticImage endp

;=========================================================================
;                                Brownse 
;=========================================================================
OpenDialog	PROC hWin:DWORD

       push   512                           ; place 500 octets Null
       push   offset Directory              ; dans l'espacé memoire 
       call   RtlZeroMemory                 ; réservé à Directory

       mov  ofn.lStructSize, SIZEOF ofn    ; initilaise la structure ofn
       mov  ofn.lpstrFilter, OFFSET FilterString
       mov  ofn.lpstrFile,   OFFSET Directory
       mov  ofn.nMaxFile,    512
       mov  ofn.Flags, OFN_FILEMUSTEXIST or \
                        OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
                        OFN_EXPLORER or OFN_HIDEREADONLY
; ouvre la boite de dialogue donnant accés aux fichiers du HDD
	  invoke    GetOpenFileName, offset ofn                  
	ret

OpenDialog	ENDP

;=========================================================================
;                             Memory Patch
;=========================================================================
memorypatch proc

       	invoke GetStartupInfo,addr startinfo
      	invoke CreateProcess, addr Directory, NULL, NULL, NULL, FALSE, \
      	DEBUG_PROCESS+ DEBUG_ONLY_THIS_PROCESS, NULL, NULL, \ 
      	addr  startinfo, addr pi
            cmp   eax,0
            jne   File_founded
            
            call   OpenDialog
            cmp	 eax, FALSE
	      jne    @F
            invoke MessageBoxA, NULL, addr absent, addr caption_msg, MB_OK
            jmp    close_all

@@:      	invoke GetStartupInfo,addr startinfo
      	invoke CreateProcess, addr Directory, NULL, NULL, NULL, FALSE, \
      	DEBUG_PROCESS+ DEBUG_ONLY_THIS_PROCESS, NULL, NULL, \ 
      	addr   startinfo, addr pi

File_founded:
      	 .while TRUE
      		 invoke WaitForDebugEvent, addr DBEvent, INFINITE
      		 .break .if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
      				
      		 .if DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT      
      
      			invoke WriteProcessMemory, DBEvent.u.CreateProcessInfo.hProcess, \
   modifications -> 	  offset1, addr patch1, 1, NULL      
      				
      		 .elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
      		   .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
                     invoke ContinueDebugEvent, DBEvent.dwProcessId, \
                                                DBEvent.dwThreadId, DBG_CONTINUE	
      			    .continue
            		   .endif
      		 .endif
      			invoke ContinueDebugEvent, DBEvent.dwProcessId, \
      			DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
      	 .endw
close_all:      
       invoke CloseHandle,pi.hProcess   
       invoke CloseHandle,pi.hThread
       
Go_out_memory:   
       push   dword ptr [serial]
ret
memorypatch endp   
;=========================================================================
;                                                                            Fin 
;=========================================================================
end start

Handle

Le terme 'handle' dans ce texte fait référence aux handles des objets gérés par le kernel32.
A la base, chaque handle peut être fermé par CloseHandle(). Ceci n'inclus PAS les objets graphiques (GDI) comme HPEN, HDC, HBITMAP etc, qui n'ont rien à voir avec le kernel32.
Un handle, dans le contexte d'un process, est supposé être un index dans une table dont l'entrée contiendrait les informations concernant différents objets.

DialogBox1

handle

 
 

Champ d’édition 1

handle

 

Champ d’édition 2

handle

Prenons l'exemple de ce CrackMe se présentant sous la forme d'une Boite de dialogue à deux champs, l'un pour le nom, l'autre pour le serial.
L'application, dés son démarrage, va se faire attribuer un handle (identificateur hInstance) :

invoke GetModuleHandle, NULL
       mov hInstance, eax

Puis la fenêtre-objet (ici notre boite de dialogue) se voit à son tour attribuer un handle (identificateur hWnd):

       INVOKE CreateWindowExA,WS_EX_WINDOWEDGE,\ 
       ADDR ClassName,ADDR NameApp,\ 
       WS_SYSMENU or WS_MINIMIZEBOX or WS_DLGFRAME or WS_BORDER\ 
       or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or WS_VISIBLE,\ 
       280,200,385,162,NULL,NULL,hInst,NULL 
       mov hWnd,eax   

Le handle de la DialogBox est en seconde position sur la pile, c'est la fenêtre_objet-propriétaire.
Si cette boite de Dialogue dépendait d'une autre fenêtre_objet, son handle serait celui d'une fenêtre_objet-fille.

Fenêtre 1

handle

       
   

DialogBox1

handle

   
       

Champ d’édition 1

handle

       

Champ d’édition 2

handle

Puis c'est au tour du premier champ d'édition (identificateur hwnedit1)

                Invoke CreateWindowExA,WS_EX_CLIENTEDGE, ADDR EditClass, NULL,\ 
       WS_CHILDWINDOW or ES_READONLY or WS_VISIBLE or\ 
       ES_AUTOHSCROLL,12,80,261,23,hWnd,NULL,hInstance,NULL 
       Mov hwndEdit1,eax
HWND CreateWindowEx
DWORD dwExStyle,                      // style de fenêtre étendue 
LPCTSTR lpClassName,                 // pointe vers le nom de la class enregistrée 
LPCTSTR lpWindowName,               // pointe vers le nom de la fenêtre 
DWORD dwStyle,                     // style de fenêtre 
int x,                            // cordonnée horizontale de la fenêtre 
int y,                           // cordonnée verticale de la fenêtre 
int nWidth,                     // largeur de la fenêtre 
int nHeight,                   // hauteur de la fenêtre 
HWND hWndParent,              // handle de la fenêtre parent ou propriétaire
HMENU hMenu,                 // handle du menu ou de l'identifiant de la 
                            //fenêtre fille (NULL dans ce cas précis)
HINSTANCE hInstance,       // handle de l'application 
LPVOID lpParam            // pointe vers les données de la fenêtre de création

Etc…

La table des handles est un DWORD contenant le nombre de handles différents, suivi par un tableau [1] contenant les identificateurs-handle des objets en question. Les identificateurs-handle sont des DWORD contenant le drapeau d'accès à un pointeur vers l'objet situé en mémoire.

typedef struct _HANDLE_TABLE
{
  DWORD   cEntries;               // Nombre de handles dans la table
  HANDLE_TABLE_ENTRY array[1];   // Tableau dont la taille est fixée par cEntries
} HANDLE_TABLE, *PHANDLE_TABLE;

typedef struct _HANDLE_TABLE_ENTRY
{
  DWORD   flags;           // flags dépend du type d'objet concerné
  PVOID   pObject;        // Pointeur vers l'objet auquel se réfère le handle
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
;=========================================================================
;          Fichier ressources: tracé de la DialogBox, ID des fenêtres, BitMap, Icon...
;=========================================================================
#include "\masm32\include\resource.h"

dialogbox DIALOGEX MOVEABLE IMPURE LOADONCALL DISCARDABLE	200,100, 245, 133, 0
STYLE DS_MODALFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_POPUP
CAPTION "DialogBox v 1.0"
FONT 8, "MS Sans Serif", 700, 0 /*FALSE*/
BEGIN
    GROUPBOX        "",        100, 	6,6,106,48,          0, , 0
    DEFPUSHBUTTON   "Exit",    109, 	174,69,51,14,        0, , 0
    CONTROL         "",        101, "Static", SS_BLACKFRAME, 	118,10,121,44, , 0
    GROUPBOX        "",        102, 	7,58,232,68, 0, , 0
    EDITTEXT                   103, 	52,70,108,12, ES_LEFT | WS_TABSTOP, , 0
    EDITTEXT                   104, 	52,89,108,12, ES_READONLY | ES_LEFT, , 0

    LTEXT           "Name",    106, 	13,72,32,9, SS_CENTER,  , 0
    LTEXT           "Serial",  107, 	13,91,32,9, SS_CENTER,  , 0
    LTEXT           "URL Site",108, 	13,110,32,9, SS_CENTER, , 0
    PUSHBUTTON      "Generate",110, 	174,88,51,14,        0, , 0
    PUSHBUTTON      "Copy",    111, 	174,107,51,14,       0, , 0
    PUSHBUTTON      "About",   112, 	18,18,31,11,         0, , 0
    PUSHBUTTON      "Browse",  113, 	18,35,31,11,         0, , 0
    PUSHBUTTON      "Patch",   114, 	70,18,31,11,         0, , 0
    PUSHBUTTON      "http://christal.ctw.net", 115,	52,108,108,12, , 0
    AUTOCHECKBOX    "Backup",  116,   65, 35, 40, 9
END
100 BITMAP MOVEABLE PURE LOADONCALL DISCARDABLE "chris2.bmp"
200 BITMAP MOVEABLE PURE LOADONCALL DISCARDABLE "main_b.bmp"
500 ICON   MOVEABLE PURE LOADONCALL DISCARDABLE "icon2.ICO"
Greets to Izcelion , X-or & Syntax Error
Bonne journée
Christal