Découvrons EXESCOPE 5.12

By Christal

Commençons par le cracker :

Un bpx Hmemcpy posé sous SoftIce après avoir rempli les deux champs Name et serial, va vous permettre de tracer jusqu'ici :

:0047D15D  8B159C804800        MOV     EDX,[0048809C] 
:0047D163  8B12                MOV     EDX,[EDX]      > Name
:0047D165  A1947F4800          MOV     EAX,[00487F94] 
:0047D16A  8B00                MOV     EAX,[EAX]      > Serial
:0047D16C  E8DB780000          CALL    00484A4C
:0047D171  84C0                TEST    AL,AL
:0047D173  0F848D000000        JZ      0047D206       > jmp bad boy

Au sortir du Call 00484A4C, AL vaudra 00, et le test conditionnel va vous envoyer vers la routine d'affichage de la MessageBox " Bad Boy ".

Petite visite du call 00484A4C :

:00484A6F  E830F1F7FF  CALL    00403BA4              > taille du sérial entré
:00484A74  83F80A      CMP     EAX,0A                > égale à 10 ?
:00484A77  7527        JNZ     00484AA0              > jmp Bad Boy
:00484A79  8B45FC      MOV     EAX,[EBP-04]          > sérial entré dans EAX
:00484A7C  803841      CMP     BYTE PTR [EAX],41     > 1er caractère = " A " ?
:00484A7F  751F        JNZ     00484AA0              > jmp bad boy 
:00484A81  8B45FC      MOV     EAX,[EBP-04]          > serial dans EAX
:00484A84  0FB64008    MOVZX   EAX,BYTE PTR [EAX+08] > 9ème caractère dans EAX
:00484A88  8B55FC      MOV     EDX,[EBP-04]          > serial dans EDX
:00484A8B  0FB65209    MOVZX   EDX,BYTE PTR [EDX+09] > 10ème caractère dans EDX
:00484A8F  03C2        ADD     EAX,EDX               > EAX=EAX+EDX
:00484A91  B90A000000  MOV     ECX,0000000A          > ECX=10d
:00484A96  99          CDQ 
:00484A97  F7F9        IDIV    ECX                   > EAX/ECX -> reste dans EDX
:00484A99  83FA04      CMP     EDX,04                > si le reste <> de " 4 "
:00484A9C  7502        JNZ     00484AA0              > jmp bad boy
:00484A9E  B301        MOV     BL,01                 > flag Good Boy
:00484AA0  33C0        XOR     EAX,EAX               > Al = 00

Et voilà !
Rien de bien compliqué :

- le serial doit comporter 10 caractères
- il doit commencer par la lettre " A "
- les 9ème et 10ème caractères sont additionnés
- le résultat est divisé par 10
- le reste de la division est placé dans EDX
- si ce reste est égal à 4, vous êtes un Good boy, et BL=1

Par la suite, EBX sera placé dans EAX (BL -> AL).
Pour cracker le programme, il peut y avoir différentes solutions, mais l'une des plus simples est de provoquer un jump de l'adresse 00484A77 directement sur le flag Good Boy en 00484A9E. Ainsi, quel que soit le serial entré, il sera toujours validé, ExeEscope plaçant systématiquement le Name et le serial entré dans un fichier .INI

Faire un KeyGen n'est peut être pas indispensable,
mais après tout


Mais revenons à ce que peut nous apporter ce programme :

Son principal intérêt est de nous fournir des informations, et plus complètes qu'avec un outils comme ProcDump
:

- informations sur le Header
- informations sur les fonctions importées et exportées
- informations sur les ressources (menus, boites de dialogue, Strings…)

Même après enregistrement, le menu Help, et les différentes boites de dialogue restent inchangées, à vous faire douter que le bon password ait été accepté !
Dans l'idée d'apporter quelques modifications substancielles à EXESCOP, histoire de le débugger un peu, voyons ce qu'il va être possible de trouver :

+ Header

Import.

kernel32.dll
user32.dll

0000 GetKeyboardType
0000 LoadStringA
0000 MessageBoxA
etc…

advapi32.dll
oleaut32.dll
etc…

(l'API MessageBoxA se trouve importée dans le programme…)

Resource

+ Bitmap
+ String

RCData
DVCLAL
PACKAGEINFO
TFABOUT
TFCONF
TFEDITDLG
TFFIND
TFLANG
TFMAIN

object MenuReg: TMenuItem
Caption = '&Regist...'
OnClick = MenuRegClick
end

TFREG
TFVEREDIT

+ Cursor
+ Icon

Exescope est très pratique pour modifier ou supprimer des Items, ou des objets d'une application.
En supprimant dans TFMAIN la partie s'y rapportant vous enlevez le disgracieux " Regist… ".
Rien ne vous empêche d'y ajouter une touche personnelle :

object MenuReg: TMenuItem
Caption = '&KeyGen... '
OnClick = MenuRegClick
end

A supposer que vous souhaitiez modifier le programme pour y inclure un Key Générateur !

De la même façon, l'écran ABOUT, qui même dans sa version Registered continue à parler " Shareware " mérite d'être débuggé :

object Label4: TLabel
Left = 8      > position X du texte
Top = 80      > position Y du texte
Width = 515   > largeur X de la zone texte
Height = 12   > largeur Y de la zone texte
Caption =
'This software is a shareware. Please register if you'
end
object Label5: TLabel
Left = 8
Top = 96
Width = 416
Height = 12
Caption =
'continue to use after 2 weeks evaluation period'
end

Et va pouvoir devenir " registered to " + " Nom de_votre_belle_mère_adorée "

A chaque fois, EXESCOPE va se charger de recalculer et modifier les adresses des différentes ressources qui sont placées dans la section .rsrc
A titre d'exemple de ce que peut donner EXESCOPE comme informations sur le Header du programme (l'entête), voici quelques informations parmi les plus intéressantes pour la section .CODE :

Header

Exe Header
Coff Header
Optional Header
Section Header

CODE

000001F8  CODE      Section Name   > je vois pas...
00000200  00084254  Virtual Size   > espace mémoire occupé
00000204  00001000  RVA/Offset     > début de la section
00000208  00084400  Size Raw Data  > espace mémoire réservé
0000021C  60000020  Section Flags  > caractéristiques 

DATA
BSS
.idata
.tls
.rdata
.reloc
.rsrc

+ Import
+ Resource

Normalement la Virtual Size (mémoire allouée) est supérieure à la Raw Size (taille réelle). Il y a peut être ici une erreur dans EXESCOPE...

Header

Exe Header
Coff Header
Optional Header

Address           Value              Signification
00000118        010B           Magic: 010B=Normal Executable
0000011C        00084400       Taille de la section .CODE
00000128        000850B8       Entry Point RVA
0000012C        00001000       Base du Code (valeur très courante)
00000134        00400000       Image Base   (90% des cas…)
00000138        00001000       Section Alignment
00000178        00000000       Export Table Address
0000017C        00000000       & Size > pas de fonctions exportées
00000180        0008B000       Import Table Address
00000184        00002474       & Size > voir le tut de " El CaRaCol "
00000188        00098000       Resource Table Address
0000018C        00010200       & Size > modifié par EXESCOPE lors de bidouilles

Il y aurait donc moyen de trouver de la place à partir de l'adresse :
00400000 (image base) + 00001000 (début de la section CODE) + 00084254 (taille de la section), soit 00485254.
Par exemple pour ajouter une petite procédure qui s'ocuperait de créer un sérial valide à partir d'un Name que vous auriez entré.

La trame de ce serial pourrait être :

AXXXXXXXXB

- Ou " A " est un caractère attendu
- Ou
XXXXXXXX sont les lettres à placer dans le serial, et comme je vais partir du nom que vous aurez entré, il faut que celui ci comprenne 8 caractères.
- Ou "
B " est la clé qui sera utilisée pour pour le contrôle de la validité du serial entré, et fonction du dernier caractère du Name entré.

En tout premier lieu, il va falloir récupérer le Nom que vous aurez entré dans le champ " Your Name ". On a vu que celui ci est accessible dans EDX en 0047D163

:0047D15D  8B159C804800        MOV     EDX,[0048809C] 
:0047D163  8B12                MOV     EDX,[EDX]      > Name

Le programme à un mode se stockage du serial un peu inhabituel :

Il le charge dans une adresse, variable à chaque lancement de l'application.
Cette adresse est stockée dans [0048809C]. Le programme fait alors :

- place l'adresse mémoire dans edx (MOV EDX,[0048809C])
- place dans edx, le contenu de l'adresse mémoire (MOV EDX,[EDX])

La routine va devoir :

- Etre accessible avant le contrôle du serial (le CALL 00484A4C va devenir un " jump notre_routine)
- Récupérer le Name entré
- Vérifier qu'il est bien égal à 8 caractères (sinon envoyer vers une MessageBox " Name invalide " et embrayer vers la routine " Code invalide ")
- Remplacer le serial entré (ou le champ laissé vide) par la lettre " A " + Name (tronqué éventuellement à 8 caractères) + clé (calculée ) partir du 8ème caractère du Name)
- Replacer dans EDX le serial généré

:00485254  A1EC804800   MOV     EAX,[004880EC] > pousse @ Name dans eax
:00485259  8B00         MOV     EAX,[EAX]      > pousse Name dans eax
:0048525B  E844E9F7FF   CALL    00403BA4       > calcul longueur du Name
(je réutilise une routine présente dans le programme)
:00485260  3D08000000   CMP     EAX,00000008   > 8 caractères ?
:00485265  7C22         JL      00485289       > saut si inférieur
:00485267  8B35EC804800 MOV     ESI,[004880EC] > pousse @ Name dans esi
:0048526D  8B36         MOV     ESI,[ESI]      > pousse Name dans esi
:0048526F  8B3D9C804800 MOV     EDI,[0048809C] > pousse @ serial dans esi
:00485277  C60741       MOV     BYTE PTR [EDI],41 > place le caractère " A "
:0048527A  47           INC     EDI            > se cale au caractère suivant
:0048527B  B908000000   MOV     ECX,00000008   > taille de la chaîne à déplacer
:00485280  F3A4         REPZ MOVSB             > déplace la chaîne Name
:00485282  BF41000000   MOV     EDI,00000041   > place " A " dans EDI
:00485287  B90A000000   MOV     ECX,0000000A   > 10 va être le diviseur
:0048528C  8BD7         MOV     EDX,EDI        > EDX = dernière lettre possible du serial
:0048528E  A1EC804800   MOV     EAX,[004880EC] > Name dans EAX
:00485293  8B00         MOV     EAX,[EAX]
:00485295  0FB64007     MOVZX   EAX,BYTE PTR [EAX+07] > 8ème caractère dans EAX
:00485299  03C2         ADD     EAX,EDX         > ajoute val du compteur
:0048529B  99           CDQ
:0048529C  F7F9         IDIV    ECX             > EAX/10d. Le reste dans EDX
:0048529E  47           INC     EDI             > lettre suivante
:0048529F  83FA03       CMP     EDX,03          > Reste est égal à 3 ?
:004852A2  75E3         JNZ     00485387        > boucle
:004852A4  8BC7         MOV     EAX,EDI         > clé du serial dans EAX
:004852A6  8B3D9C804800 MOV     EDI,[0048809C]  > mise en place de la clé
:004852AC  8B3F         MOV     EDI,[EDI]       > à la fin du serial
:004852AE  884709       MOV     [EDI+09],AL     
:004852B1  61           POPAD                   > restaure les registres
:004852B2  8B159C804800 MOV     EDX,[0048809C]  > place le nouveau serial 
:004852B8  8B12         MOV     EDX,[EDX]       > dans EDX 
:004852BA  E88DF7FFFF   CALL    00484A4C        > test du serial
:004852BF  E9AD7EFFFF   JMP     0047D171        > retour à la normale

:004852C4  6A00         PUSH    00              > MB_OK (bouton OK)
:004852C6  68DD524800   PUSH    004852DD        > Attention ! (titre)
:004852CB  68E9524800   PUSH    004852E9        > Votre Name… (message)
:004852D0  6A00         PUSH    00              > pas de fenêtre propriétaire
:004852D2  FF150CB24800 CALL    [USER32!MessageBoxA] > affichage
:004852D8  E9297FFFFF   JMP     0047D206             > Code Invalide

:004852DD  41      INC     ECX                  > Début de la chaîne
:004852DE  7474    JZ      00485354             > " Attention ! "
:004852E0  656E    OUTSB   GS:
:004852E2  7469    JZ      0048534D
:004852E4  6F      OUTSD
:004852E5  6E      OUTSB
:004852E6  2100    AND     [EAX],EAX
:004852E8  00566F  ADD     [ESI+6F],DL
:004852EB  7472    JZ      0048535F
:004852ED  65204E61AND     GS:[ESI+61],CL
:004852F1  6D      INSD

Souvent il m'est arrivé, à mes débuts, de ne pas comprendre ce que pouvait bien faire tel ou tel partie du programme, les codes n'ayant aucun sens…
En fait c'était tout simplement la " traduction " que le débuggeur faisait d'une chaîne de texte :

0CB2 4800 E929 7FFF FF41 7474 656E 7469 6F6E 2100
0056 6F74 7265 204E 616D 6520 646F 6974 2063 6F6D
706F 7274 6572 2038 2063 6172 6163 74E8 7265 7320
6D69 6E69 6D75 6D00 0000 0000 0000 0000 0000 0000

correspond en fait à :

..H..)...Attention!.
.Votre Name doit com
porter 8 caract.res
minimum.............

Pour finir :

J'aurais pu provoquer l'affichage du serial généré dans le second champ (ID) de la DialogBox en utilisant par exemple l'API SetWindowTextA (présente dans les imports functions), mais il aurait fallu que je trouve Hwnd (le handle du champ en question), et très honnêtement, j'ai eu la flemme de le chercher…

Et une dernière chose, pour appeler la fonction " MessageBoxA ", j'ai utilisé un Call Dword ptr [adresse_trouvée_dans_Wdasm], afin d'être certain l'adresse de cette API sera trouvée dans n'importe quel Kernel32 d'un OS compatible avec EXESCOPE.


Les identificateurs des menus :

Dans le cas de EXESCOPE, il n'y a pas de menus POPUP tel qu'habituellement définis. En générale, dans une majorité de programmes, vous obtiendrez ce type d'informations :

0,&File
1000,&Open          > 1000d = ID
0,                  > séparateur
1001,&Save
0,                  > séparateur
1010,&Exit
0,&Window
1800,&Tile
1801,&Cascade
1802,&Arrange Icons
1803,&Next Window$09F6 > touche [F6]
1804,C&lose All
0,&Help
1900,&About

Et il vous sera facile d'y apporter les modifications que vous souhaitez (Dégriser un menu ou un Item de menu, le réactiver, le supprimer…)
De la même façon, vous pourrez rajouter un menu, mais dans ce cas je préfère Borland WorkShop Ressources 4.5 qui est un véritable éditeur de ressources, contrairement à EXESCOPE qui ne fait que permettre quelques modifications légères.

De la même façon, vous pourrez modifier à loisir les boites de Dialogues à supposer quelles n'aient pas été écrites comme le KeyMaker de Papaow (voir ci dessous). Un coup d'œil dans les Imports de EXESCOPE montre qu'un grand nombre de GDI ont été utilisées, et cette fois ci encore, comme pour les menus, les modifications qu'il sera possible d'apporter aux boites de dialogues seront beaucoup plus délicates et limitées :

Caption = 'Regist'     > Texte de la Barre de titre
object Label1: TLabel
Left = 10              > position X et Y du texte
Top = 12               > " Your Name "
Width = 56             > Taille des caractères
Height = 12
Caption = 'Your Name'  > texte à afficher
end

object IDEdit: TEdit
Left = 78
Top = 40
Width = 150
Height = 20
ImeName = 'MSIME95'
PasswordChar = '*'   > en supprimant cette ligne, les caractères de  ID ne seront plus les " * "
TabOrder = 1
end

Voilà !
C'était une petite approche de ce Tool qui devrait permettre à ceux d'entre vous qui ne le connaissaient pas de commencer à le découvrir...

Bonne journée
Christal

;=========================================================================
;     Ce source ASM est dérivé de celui de Win Zip codé par Papaow
;=========================================================================

      .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

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

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

      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib
      includelib \masm32\lib\gdi32.lib
      includelib \masm32\lib\masm32.lib

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

.const 

;=========================================================================
Prog 	equ "Exescope 5.12"  
Auteur 	equ "Christal"
;=========================================================================

    IDC_Gen      equ 3001 
    IDC_Exit     equ 3002 
    IDC_About    equ 3003 

.data

;=========================================================================
;                      Données dont vous aurez besoin 
;=========================================================================

    Erreur          db "Le Name doit être de 8 caractères",0
    ClassName       db "WinClass",0 
    NameApp         db Prog," KeyMaker by [Christal]",0 
    EditClass       db "edit",0 
    StaticClass     db "static",0 
    ButtonClass     db "button",0 
    DlgName         db "Dialog",0 
    IconName        db "Icon2",0 
    TextAbout       db "Keymaker pour Exescope 5.12",0Dh,0Ah,0Ah,0Ah 
                    db "Coded by Christal et Papaow",0 
    Caption         db Prog,0 
    TextNom         db "Enter your name here:",0 
    TextKey         db "Your serial number:",0 
    TextCoded       db "cODED bY [Christal et Papaow]",0 
    ButtonAbout     db "&About",0 
    ButtonExit      db "&Exit",0 
    ButtonGen       db "&Generate",0 
    FontString      db "Arial", 0 
    conversion      db '%d',0
    lzUSER32        db 'USER32',0
    lzWSPRINTFA     db 'wsprintfA',0
    
.data? 

;=========================================================================  
;                   Variables dont vous aurez besoin  
;=========================================================================	

      Buff db 512 dup(?)
      buffer2 db 512 dup (?) 
      
      hInstance    HINSTANCE ? 
      CommandLin   LPSTR ? 
      hwnd         HWND ? 
      hwndButton   HWND ? 
      hwndEdit1    HWND ? 
      hwndEdit2    HWND ? 
      hwndStatic1  HWND ? 
      hwndStatic2  HWND ? 
      hwndStatic3  HWND ? 
      hwndButton1  HWND ? 
      hwndButton2  HWND ? 
      hwndButton3  HWND ? 
      
      buffer db 512h dup (?)
      
      Key          DWORD ?
      Font1        DWORD ? 

    .code

start:
        invoke GetModuleHandle, NULL
        mov hInstance, eax

        invoke GetCommandLine
        mov CommandLine, eax

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

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD 

LOCAL 	wc:WNDCLASSEX 
LOCAL 	msg:MSG 

;=========================================================================
;                  Traçage de la boite de dialogue
;=========================================================================

      mov 	wc.cbSize,SIZEOF WNDCLASSEX 
      mov 	wc.style, CS_DBLCLKS 
      mov 	wc.lpfnWndProc, OFFSET WndProc 
      mov 	wc.cbClsExtra,NULL 
      mov 	wc.cbWndExtra,NULL 
      push 	hInstance 
      pop 	wc.hInstance 
      mov 	wc.hbrBackground,COLOR_BTNFACE+1 
      mov 	wc.lpszClassName,OFFSET ClassName 
      invoke 	LoadImage,hInstance,ADDR IconName,IMAGE_ICON,32,32,LR_LOADMAP3DCOLORS 
      mov 	wc.hIcon,eax 
      mov 	wc.hIconSm,NULL 
      invoke 	LoadCursor,NULL,IDC_ARROW 
      mov 	wc.hCursor,eax 
      invoke 	RegisterClassEx, addr wc 
      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 
      INVOKE 	ShowWindow, hwnd,SW_SHOWNORMAL 
      INVOKE 	UpdateWindow, hwnd 
      
      .WHILE 	TRUE 
      INVOKE 	GetMessage, ADDR msg,NULL,0,0 
      
      .BREAK 	.IF (!eax) 
      INVOKE 	TranslateMessage, ADDR msg 
      INVOKE 	DispatchMessage, ADDR msg 
      .ENDW 
      
      mov 	eax,msg.wParam 
      ret 
WinMain endp 

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

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 

      mov eax,uMsg 
      
      .IF eax==WM_DESTROY 
      	invoke PostQuitMessage,NULL 
      .ELSEIF eax==WM_CREATE 
      
      invoke 	CreateWindowExA,WS_EX_CLIENTEDGE, ADDR EditClass,NULL,\ 
      WS_CHILDWINDOW or WS_MAXIMIZEBOX or WS_VISIBLE or\ 
      ES_AUTOHSCROLL,12,30,261,23,hWnd,NULL,hInstance,NULL 
      
      mov 	hwndEdit1,eax 
      
      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 	hwndEdit2,eax 
      
      invoke 	CreateWindowExA,WS_EX_NOPARENTNOTIFY, ADDR StaticClass,ADDR TextNom,\ 
      WS_CHILD or WS_VISIBLE or ES_LEFT ,\ 
      12,15,180,15,hWnd,NULL,hInstance,NULL 
      mov 	hwndStatic1,eax 
      
      invoke 	CreateWindowExA,WS_EX_NOPARENTNOTIFY, ADDR StaticClass,ADDR TextKey,\ 
      WS_CHILD or WS_VISIBLE or ES_LEFT ,\ 
      12,65,180,15,hWnd,NULL,hInstance,NULL 
      mov 	hwndStatic2,eax 
      
      invoke 	CreateWindowExA,WS_EX_NOPARENTNOTIFY, ADDR StaticClass,ADDR TextCoded,\ 
      WS_CHILD or WS_VISIBLE or ES_LEFT ,\ 
      12,110,180,15,hWnd,NULL,hInstance,NULL 
      mov 	hwndStatic3,eax 
      
      invoke 	CreateWindowExA,NULL, ADDR ButtonClass,ADDR ButtonAbout,\ 
      WS_CHILDWINDOW or WS_MAXIMIZEBOX or WS_VISIBLE or WS_GROUP or WS_TABSTOP,\ 
      295,100,75,23,hWnd,IDC_About,hInstance,NULL 
      mov 	hwndButton1,eax 
      
      invoke 	CreateWindowExA,NULL, ADDR ButtonClass,ADDR ButtonExit,\ 
      WS_CHILDWINDOW or WS_VISIBLE or WS_CLIPSIBLINGS,\ 
      295,40,75,23,hWnd,IDC_Exit,hInstance,NULL 
      mov 	hwndButton2,eax 
      
      invoke 	CreateWindowExA,NULL, ADDR ButtonClass,ADDR ButtonGen,\ 
      WS_CHILDWINDOW or WS_VISIBLE or WS_CLIPSIBLINGS\ 
      or BS_DEFPUSHBUTTON,\ 
      295,10,75,23,hWnd,IDC_Gen,hInstance,NULL 
      mov 	hwndButton3,eax 
      
      invoke 	SetFocus, hwndEdit1 
      
      invoke 	CreateFontA, -11, -5, 0, 0, 150, FALSE, FALSE, FALSE,\ 
      DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,\ 
      DEFAULT_QUALITY, DEFAULT_PITCH,ADDR FontString 
      mov 	Font1, eax 
      
      invoke	PostMessageA, hwndStatic1, WM_SETFONT, Font1, TRUE 
      invoke 	PostMessageA, hwndStatic2, WM_SETFONT, Font1, TRUE 
      invoke 	PostMessageA, hwndButton1, WM_SETFONT, Font1, TRUE 
      invoke 	PostMessageA, hwndButton2, WM_SETFONT, Font1, TRUE 
      invoke	PostMessageA, hwndButton3, WM_SETFONT, Font1, TRUE 
      invoke 	PostMessageA, hwndStatic3, WM_SETFONT, Font1, TRUE 
      
      .ELSEIF eax==WM_COMMAND 
      
      mov 	eax,wParam 
      
      .IF ax==IDC_About 
      		shr eax,16 
      	.IF ax==BN_CLICKED 
      		invoke MessageBoxA, NULL,addr TextAbout, addr Caption,040h 
      	.ENDIF 
      .ENDIF 
      
      .IF ax==IDC_Gen 
      		shr eax,16 
      	.IF ax==BN_CLICKED 
      		invoke GetWindowTextA,hwndEdit1,ADDR buffer,512 

;========================================================================= 
;                            KeyGeneraor 
;        la bonne clé se trouve dans EDI à la fin de la routine   
;=========================================================================  	
sub 	esp,24h 

      cmp eax, 8                       ; Name doit faire au moins 8 caractères
      jl  Trop_court               
      je  suite_1                      ; si Name supérieur à 8 caractères

suite_0:

      mov byte ptr [buffer+eax],00     ; les caractères en trop sont effacés
      dec eax                              
      cmp eax,8
      jne suite_0

suite_1:

      MOV     EDI,"A"                  ; incrémente recherche 10ème caractère

boucle_2:

      MOV     ECX,0Ah                  ; ECX=10d
      MOV     EDX,EDI
      MOVZX   EAX,BYTE PTR [buffer+7]  ; 8ème caractère du Name dans EAX
      ADD     EAX,EDX                  ; EAX=EAX+EDX
      CDQ 
      IDIV    ECX                      ; EAX/ECX -> reste dans EDX
      INC     EDI                      ; passe à la lettre suivante
      CMP     EDX,03                   ; si le reste <> de " 3 "
      Jne     boucle_2                 ; boucle
      mov     eax,edi                  ; place le caractère trouvé dans AL

;=========================================================================
;                      création du sérial 
;=========================================================================
     
      lea   edi, buffer                ; Name dans EDI
      dec   edi                        ; se cale un espcae mémoire avant le name
      mov   byte ptr [edi],41h         ; met "A" devant la Name
      mov   byte ptr [edi+9], al       ; met le caractère trouvé après le Name
      
      jmp   Affiche 

;========================================================================= 
;                         Affichage du résultat  
;=========================================================================
Trop_court: 

    invoke 	SetWindowTextA,hwndEdit2,ADDR Erreur 

jmp 	Fin 

Affiche: 

    invoke SetWindowTextA,hwndEdit2,EDI

Fin: 
;=========================================================================
;                                 Fin  
;=========================================================================	
            .endif
         .endif

      .IF ax==IDC_Exit 
      invoke ExitProcess,eax       
      .ENDIF 
      
      .ELSE 
      	invoke DefWindowProc,hWnd,uMsg,wParam,lParam 
      	ret 
        .endif
      ret 
      WndProc endp 

end start
;=========================================================================

Bonne Journée

Christal