WinRar 2.80

By Christal

WinRar peut être considéré comme un programme paradoxal!
D'un coté il est assez simple à cracker, de l'autre il me semble qu'à ce jour personne n'a encore trouvé comment générer un serial valide.

Il ne s'agit pas ici de proposer un nouveau tut sur cette protection. TeeJi avait déjà étudié la
version 2.50 en mettant en évidence le flag "is_registered" et la manière de le mettre à 01, et Alien, pour la version 2.60 s'était attaqué à la key File (rarreg.*).
Pour ces trois versions, le principe de cette protection est restée identique.
Je ne développerai donc pas le "How To crack", mais plutôt la partie qui m'intéressait, à savoir apprendre comment gérer en ASM des fichiers Key, et pouvoir créer un patch.

La solution que j'ai donc retenue est la création d'un fichier rarreg.key et sa validation par le programme. Sans le KeyFile le patch du programme ne donnera rien de mieux que la version d'évaluation, l'adresse patchée n'étant lue par Winrar que si un fichier rarreg.* a été détecté.

L'approche de TeeJi:

Recherche de la string "evaluation copy" du bandeau d'écran:

:00434E95 803D083B470000          cmp byte ptr [00473B08], 00
:00434E9C 752A                    jne 00434EC8

* Possible Reference to String Resource ID=00873: "evaluation copy

Une seule occurrence, où l'on retrouve le flag "Is_registered" comme dans la version 2.50: [00473B08]

Une recherche rapide va permettre de cibler 4 manipulations sur cette adresse

:0042415C E88FD1FFFF              call 004212F0
:00424161 803D083B470000          cmp byte ptr [00473B08], 00  is_registered?
:00424168 7410                    je 0042417A
:0042416A 803D0435470000          cmp byte ptr [00473504], 00  is_name_present (dans la keyFile)
:00424171 7507                    jne 0042417A
:00424173 C605083B470000          mov byte ptr [00473B08], 00  user not registered

Cette première manipulation (avec un call 004212F0 qui est sollicité par 35 appels) va principalement définir si un fichier rarreg.* est présent et non vide (ce fichier peut avoir n'importe quelle extension, .txt marche tout aussi bien que .key).

* Reference To: KERNEL32.GetLocalTime, Ord:0000h
                                  |
:0042B1BB E8F0CF0300              Call 004681B0
:0042B1C0 33C0                    xor eax, eax
:0042B1C2 E8D1A3FEFF              call 00415598
:0042B1C7 A2083B4700              mov byte ptr [00473B08], al

Cette seconde manipulation fait suite au contrôle de la fin de la période d'essai:

- saisie de la date
- vérification du statut utilisateur

Les deux dernières manipulations sont liées à la validation du KeyFile.
Vous aurez remarqué que ces trois derniers extraits du Dead Listing font tous référence au call 00415598.
Dans la mesure ou la première manipulation est liée à l'absence d'un fichier rarreg.*, seul le call repéré va nous intéresser.

Affichage, ou non, de "evaluation copy" dans le bandeau d'écran

:00433C8A B001                    mov al, 01                   > mise à 01 du flag
:00433C8C E80719FEFF              call 00415598                > contrôle du contenu du KeyFile
:00433C91 A2083B4700              mov byte ptr [00473B08], al  > AL=01 -> User registered

Affichage, ou non, de "40 Day trial" dans l'écran About

:0043460A B001                    mov al, 01
:0043460C E8870FFEFF              call 00415598
:00434611 A2083B4700              mov byte ptr [00473B08], al

C'est donc le Call 00415598 qui est LE call où le KeyFile est vérifié:

- Présence d'un name et serial
- Génération de la clé
- Vérification de la clé
- AL = 0 ou 1 en fonction du résultat

A la sortie de ce call, en présence d'un faux serial, vous arrivez ici:

:00415890 E8033B0000              call 00419398
:00415895 8BD8                    mov ebx, eax
:00415897 3B9E00060000            cmp ebx, dword ptr [esi+00000600]
:0041589D 7424                    je 004158C3
:0041589F 33C0                    xor eax, eax   > Bad_KeyFile

Pour que le serial soit reconnu comme valide, il faudrait que le programme passe ici:

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00415965(C), :00415A07(C)
|
:00415A2A B001                    mov al, 01     > Flag KeyFile_OK

via deux Strings Data ref de ce type:

* Possible StringData Ref from Data Obj ->"70c2441db366d92ea7be1342b3bf629026ba92bb675f06"
                                        ->"e684bdd34511097434"

Qui laissent préfigurer de la forme que pourrait avoir le serial.
Tout se joue entre le "je 004158C3" en 0041589D et l'adresse 00415A2A mov al, 01.
Une petite portion de code...

En définitive, c'est le Xor EAX,EAX en 0041589F que j'ai décidé de modifier.
Dans la mesure ou le Flag Is_registered ne porte que sur l'état du registre AL, un simple Mov AL,01 (quelque soit la valeur contenue dans EAX) va suffire à valider le KeyFile et votre statut.

Mon petit programme devra donc:

- vérifier la présence du 33C0 et le remplacer par un B001
- créer un keyFile dont la forme sera reconnue pour pouvoir au moins arriver en 004158F.

Reste à trouver l'allure que devra avoir ce KeyFile.
Dans son tut Alien avait proposé de remplir ce fichier avec un Name et un serial (dans cette ordre), mais au premier essai, seul le serial apparaissait. Dans une seconde tentative, il avait inversé cet ordre (Serial + Name), et seul son Nom était apparu.
j'en ai déduis qu'en fait ce fichier devait être composé de trois lignes (j'ai essayé avec quatre lignes...), et que la première d'entre elles ne serait pas affichée.

Bien joli toutes ces suppositions, mais un peu trop empiriques quand même....

En regardant dans la liste des APIs utilisées, deux d'entre elles pourraient nous donner de plus amples informations:

- ReadFile (utilisé pour la lecture de datas dans un fichier)
- GetPrivateProfileStringA (Utilisé pour récupéré une string d'une section spécifiée dans un fichier d'initialisation).

Commençons par la première API:

:004066DB   push 00000000                > adresse de la structure pour les datas 
:004066DD   lea eax, dword ptr [esp+04]  > adresse du nombre d'octets lus,
:004066E1   push eax                     > poussée sur la pile
:004066E2   push esi                     > nombre d'octets à lire (53h dans notre cas)
:004066E3   push edi                     > adresse du buffer qui recevra les datas
:004066E4   push [ebx+04]                > handle du fichier à lire           
:004066E7   Call KERNEL32.ReadFile

Le programme devant récupérer le handle du fichier à lire, ce handle est retourné par CreateFileA:

:0040629B  push [ebp-04]                 > pointe vers le nom du fichier
:0040629E  Call CreateFileA

Ce nom du fichier va être C:\Program Files\WinRar\rareg.key
Et ce fichier doit contenir un maximum de 83 caractères (53h).
La taille du Name étant aléatoire, d'un utilisateur à l'autre (disons 10 caractères), la première ligne du fichier pouvant en consommer autant, il resterait environ 63 caractères pour composer le serial. Or les strings ressemblant à des serials rencontrées dans Wdasm sont de 64 caractères, ce qui semblerait confirmer que le serial pourrait être composé d'autant de digits.

ReadFile pouvant être utilisé pour lire n'importe quel type de données dans un fichier, son utilisation ne nous apprend pas grand chose.

GetPrivateProfileStringA pourrait se révéler intéressant dans la mesure ou ce type de foncion est principalement utilisée pour des fichiers d'initialisation dans lequel on trouve un nom de section (ici [registration]) et un nom de clé (informated=) suivi des données à saisir par l'API.

[registration]
regname=Christal
regcode=12345678
informed=0

En fait, cette API est utilisée pour lire un fichier RAR.INI (inexistant) et dans lequel le programme cherche à trouver des données pour:

:0042AC70   push "regname"                 > nom de la clé
:0042AC75   push "registration"            > nom de la section
:0042AC7A   Call GetPrivateProfileStringA

puis

:0042AC8F   push "regcode"
:0042AC94   push "registration"
:0042AC99   Call GetPrivateProfileStringA
  
et enfin 

:0042ACA1   push "informed"
:0042ACA6   push "registration"
:0042ACAB   Call GetPrivateProfileIntA    > attend un nombre entier


Mais en jouant avec ces adresses, on aboutit à un écran qui nous apprends que depuis la version 2.60, le principe d'enregistrement a été renforcé, puis WritePrivateProfileStringA met la clé "informed" à 01 pour se souvenir qu'il est inutile d'afficher cet écran par la suite (comme vous pourrez vous en rendre compte, à condition que vous n'ayez pas un fichier rareg.* déjà créé):

[registration]
regname=Christal
regcode=12345678
informed=1

Nous voilà guère plus avancé pour la création de notre KeyFile.
A défaut, j'ai donc choisi un KeyFile qui afficherait le Nom qui vous aurez entré dans le champ de mon générateur de Key suivi d'un petit message, et qui serait créé sur ce modèle:

[registration]
Your name
WinRar 2.80 KeyFile

N'étant ni codeur, ni cracker ou quoi que ce soit d'autre, je me suis inspiré de sources déjà existant, dont ceux de papow, SyntaxError et Iczelion, pour réaliser mon générateur/patcheur.
Un merci tout particulier à SyntaxError et à TaxerZ qui m'ont bien briefé lors d'un passage sur le channel #crack.fr (undernet)

;=========================================================================
;     Ce source ASM est dérivé de celui de:
;     Papaow pour la partie graphique et la trame de ce source
;     (développé lors d'un précedent texte sur les KeyGenerator)
;     SyntaxError pour la partie Patch de Winrar.exe
;     Iczelion (traduit par Morgatte) pour la partie KeyFile
;     Un grand merci à tous!
;=========================================================================

      .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 "WinRar 2.80"  
Auteur 	equ "Christal"
;=========================================================================

    IDC_Gen      equ 3001 
    IDC_Exit     equ 3002 
    IDC_About    equ 3003 

.data

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

    Erreur          db "Mettez au moins une lettre...",0
    Already         db "Un fichier Key existe déjà !",0
    no_here         db "Le Fichier WinRar.exe est absent !",0 
    badversion      db "Ce n'est pas la bonne version de WinRar !",0
    PasGlop         db "Le patch a échoué !",0
    ClassName       db "WinClass",0 
    NameApp         db Prog," KeyFile by [Your name]",0
    EditClass       db "edit",0 
    StaticClass     db "static",0 
    ButtonClass     db "button",0 
    DlgName         db "Dialog",0 
    IconName        db "Icon2",0 
    TextAbout       db "KeyFile Maker pour Win Rar 2.80",0Dh,0Ah,0Ah,0Ah 
                    db "Coded by Christal",0 
    Caption         db Prog,0 
    TextNom         db "Enter your name here:",0 
    TextCoded       db "cODED bY [Your name]",0 
    ButtonAbout     db "&About",0 
    ButtonExit      db "&Exit",0 
    ButtonGen       db "&Generate",0 
    FontString      db "Arial", 0 
    cle_serial      db "RegName",0
    Well_done       db "Key File créée",0
    prg_name        db "\WinRar.exe",0
    Path_Key_File   db "\rarreg.key",0
    dont_touch      db "[registration]",0Dh,0Ah
    fmap            dd ?                   ; handle du fichier mappé
    faddr           dd ?                   ; adresse du début du fichier mappé
    Lg_path         DWORD 0

.data? 

;=========================================================================  
;                   Variables dont vous aurez besoin  
;=========================================================================	
    
      hInstance    HINSTANCE ? 
      CommandLine  LPSTR ? 
      CommandLine2 LPSTR ?
      hwnd         HWND ? 
      hwndButton   HWND ? 
      hwndEdit1    HWND ? 
      hwndEdit2    HWND ? 
      hwndStatic1  HWND ? 
      hwndStatic3  HWND ? 
      hwndButton1  HWND ? 
      hwndButton2  HWND ? 
      hwndButton3  HWND ? 
      
      buffer db 512h dup (?)
      
      lg_name       DWORD ?
      Font1         DWORD ? 
      Directory     DWORD ?
      SizeReadWrite DWORD ?    ; Nombre d'octets à écrire 
      hFile         HANDLE ?   ; Handle du fichier. 

    .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 
;=========================================================================
; Tracage de la DialogBox et Gestion des événements (voir précedent texte)
;=========================================================================

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 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, 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 

;========================================================================= 
;                        Key File generator et Patch
;=========================================================================  	

      cmp eax,00                                     ; champ Name rempli?
      je Trop_court                                  ; Goto msg error
      mov word ptr [eax+buffer],0A0Dh                ; rajoute un RTN chariot
      add eax,2                                      ; et modifie la lg du buffer Name
      mov lg_name, eax                               ; enregistre la lg du name
      
      invoke GetCurrentDirectory,128,ADDR Directory  ; récupère le Path
      mov Lg_path,eax                                ; enregistre sa longueur
      mov edi, offset Directory                      ; et y ajoute le nom
      Add edi,eax
      mov esi, offset Path_Key_File                  ; de la KeyFile
      mov ecx, 11
      rep movsb

      ; création du fichier rarreg.key 

      invoke CreateFile,ADDR Directory,GENERIC_READ or GENERIC_WRITE ,\ 
      FILE_SHARE_READ or FILE_SHARE_WRITE,NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,NULL 
      
      cmp eax,-1                                    ; eax renvoi le Handle du fichier créé
      jne @@1                                       ; si un fichier identique existe
      jmp reg_exist                                 ; la valeur de retour sera égal à -1
      @@1:
      mov hFile,eax                                 ; hFile = handle du fichier créé

      ; le fichier rareg.key contient trois lignes:
      ; [Registration] , le nom saisie dans la DlgBox , et ce qui correspondrait au serial

      mov eax, 14      ; = longueur de [Registration]
      invoke WriteFile,hFile,ADDR dont_touch,EAX,ADDR SizeReadWrite,NULL 
      mov eax, lg_name ; taille du Name entré
      invoke WriteFile,hFile,ADDR buffer,eax,ADDR SizeReadWrite,NULL 
      mov eax,33       ; = taille du "serial" à écrire
      invoke WriteFile,hFile,ADDR NameApp,EAX,ADDR SizeReadWrite,NULL 

;BOOL WriteFile

;  HANDLE hFile,                       // handle du fichier à remplir
;  LPCVOID lpBuffer,                  // pointeur vers les data à écrire
;  DWORD nNumberOfBytesToWrite,      // nombre d'octets à écrire 
;  LPDWORD lpNumberOfBytesWritten,  // pointe vers "l'adresse" où doit être écrit les Data
;  LPOVERLAPPED lpOverlapped       // pointe vers la structure nécessaire pour overlapped I/O
  
; lpNumberOfBytesWritten est géré par la fonction WriteFile et pointe vers le nombre d'octets
; écrits par la fonction. WriteFile met cette valeur à zero avant de faire quoi que ce soit.
; lpNumberOfBytesWritten est incrémenté par WriteFile à chaque octet écrit.
; Si vous devez écrire dans un endroit particulier du fichier, il vous faudra connaître le nombre
; d'octets ("l'adresse" du pointeur) à partir duquel vous voudrez écrire

      ; Patch de WinRar.exe: recherche de la présence de l'exécutable
      
      invoke GetCurrentDirectory,128,ADDR Directory  ; récupère le Path
      mov Lg_path,eax                                ; enregistre sa longueur
      mov edi, offset Directory                      ; et y ajoute le nom
      Add edi,eax
      mov esi, offset prg_name                       ; de l'exe à trouver
      mov ecx, 11                                    ; (11 caractères de long)
      rep movsb
      
      ; vérifie si le fichier Winrar.exe est bien dans le même répertoire que notre prog
      ; et récupère le handle de la cible à modifier (patcher).
      ; et appel du  fichier à Mapper. 
     
      invoke  CreateFileA,addr Directory,GENERIC_READ+GENERIC_WRITE,\
      FILE_SHARE_READ+FILE_SHARE_WRITE,0, OPEN_EXISTING,\
      FILE_ATTRIBUTE_NORMAL,0
      cmp     eax,-1                                 ; si EAX=-1
      jz      absent                                 ; WinRar.exe est absent
      mov     hFile,eax                              ; hFile = handle du fichier trouvé

; Les (Files Mapping) configurations de fichiers vont permettre de lire et écrire dans un fichier.
; Pour utiliser la configuration de fichier, vous pouvez vous imaginer que c'est un fichier entier,
; déjà chargé en mémoire, et dans lequel vous pouvez employer un pointeur de mémoire 
; pour lire ou écrire des données . 
      
      invoke  CreateFileMapping,hFile,0,PAGE_READWRITE,0,0,addr prg_name
      test    eax,eax
      jz      umap               ; la fonction a échouée, sinon
      mov     fmap,eax           ; la valeur de retour est le handle du fichier mappé
      
; HANDLE CreateFileMapping

;    HANDLE hFile,                                     // handle du fichier à mapper 
;    LPSECURITY_ATTRIBUTES lpFileMappingAttributes,   // optional security attributes 
;    DWORD flProtect,                                // protection for mapping object 
;    DWORD dwMaximumSizeHigh,                       // high-order 32 bits of object size  
;    DWORD dwMaximumSizeLow,                       // low-order 32 bits of object size  
;    LPCTSTR lpName                               // Nom du fichier à mapper 

; MapViewOfFile va permettre de Mapper une région du fichier choisie ou bien le fichier entier en mémoire.
; Cette fonction renvoie le pointeur du premier octet de la zone où commence le fichier configuré (Mappé). 

      invoke  MapViewOfFile,fmap,FILE_MAP_WRITE,0,0,0
      test    eax,eax      ; la valeur de retour est l'adresse de début du mapped view
      jz      umap         ; cad l'adresse à laquelle le fichier commence en mémoire

;LPVOID MapViewOfFile

;    HANDLE hFileMappingObject,      // file-mapping object to map into address space  
;    DWORD dwDesiredAccess,         // access mode 
;    DWORD dwFileOffsetHigh,       // high-order 32 bits of file offset 
;    DWORD dwFileOffsetLow,       // low-order 32 bits of file offset 
;    DWORD dwNumberOfBytesToMap  // number of bytes to map 
      
      mov     faddr,eax                       ; On a notre pointeur !!!
      mov     edx,eax
      cmp     word ptr ds:[edx],'ZM'          ; c'est un .exe ?
      jnz     Pas_Glop
            
      mov     eax,dword ptr ds:[edx+3Ch]      
      add     eax,faddr
      cmp     word ptr ds:[eax],'EP'          ; au bon format ?
      jnz     Pas_Glop
      
      mov     eax,edx                         ; on rajoute la valeur de l'offset à patcher
      add     eax, 00014E9Fh                  ; au pointeur de début du fichier mappé
      cmp     word ptr [EAX],49203            ; vérifie que l'on va modifier la bonne version
      jne     bad_version
      mov     word ptr [EAX],01B0h            ; et on patch

; Dans l'exemple d'Iczelion (traduit par Morgatte), la modification d'un fichier peut être
; fait avec la fonction WriteFile, à condition de correctement paramètrer l'adresse à patcher

     jmp GlopGlop
      
;========================================================================= 
;                         Affichage des messages  
;=========================================================================

Trop_court: 
    invoke 	SetWindowTextA,hwndEdit2,ADDR Erreur 
jmp 	Fin 

reg_exist:
    invoke 	SetWindowTextA,hwndEdit2,ADDR Already
jmp 	Fin 

absent:
    invoke 	SetWindowTextA,hwndEdit2,ADDR no_here 
jmp 	Fin 

bad_version:
    invoke 	SetWindowTextA,hwndEdit2,ADDR badversion
jmp Umapping

Pas_Glop: 
    invoke 	SetWindowTextA,hwndEdit2,ADDR PasGlop
jmp Umapping

umap:
    invoke 	SetWindowTextA,hwndEdit2,ADDR Already
jmp 	Umapping 

GlopGlop: 
    invoke SetWindowTextA,hwndEdit2,ADDR Well_done

Umapping:
    ; UnmapViewOfFile va Unmapper le fichier
    invoke UnmapViewOfFile,faddr

    ; CloseHandle avec l'Handle renvoyé par CreateFile va fermer le fichier réel. 
    invoke CloseHandle,hFile
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
;=========================================================================

;* Fin de KeyGen.asm *  
;* KeyGen.rc *  
Icon2 ICON "icon2.ico" 
;* Fin de KeyGen.rc *  

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

Bonne Journée

Christal