Protection
   Pe Compact 1.25  

Les Protections Renforcées

  Outils
   SoftIce ProcDump    
  Cible
   FCF Crackme  

By Christal

FCF Trial - Crackme de RD 116

Ce crackme propose 4 options : supprimer un nag, trouver un sérial, un name & sérial, et comment s'enregistrer grâce à un Key file.

Problème n°1 : le nag à virer

Commençons par le nag.
Pour pouvoir bosser dans de " bonnes conditions ", j'ai commencé par créer un dump exécutable de ce crackme. En effet, celui ci est crypté/compressé avec un codage polymorphe.
En traçant, sans se presser, les codes de l'exécutable, vous finirez par arriver ici :

:0047BAA7  FFA52E744000        JMP     [EBP+0040742E]
:0047BAAD  8BB51E744000        MOV     ESI,[EBP+0040741E]
:0047BAB3  8BBD22744000        MOV     EDI,[EBP+00407422]
:0047BAB9  E85E040000          CALL    0047BF1C
:0047BABE  61                  POPAD
:0047BABF  9D                  POPFD
:0047BAC0  50                  PUSH    EAX
:0047BAC1  6820B74400          PUSH    0044B720
:0047BAC6  C20400              RET     0004

Un jmp EIP en 0047BAC1 va permettre de créer un Dump Full (option Rebuilt Import Table) avec ProcDump. En remplaçant l'EP d'origine par 0044B720, le dump obtenu sera exécutable, et Wdasm donnera les strings Data nécessaires...
Inutile de modifier les caractéristiques de la première section, RD-116 l'a déjà fait pour vous en la mettant à E0000020.

Regardons un peu plus en détail ce compresseur, et à commencer par les sections du PE Header :

pec1   00057000     00001000     00022C00     00000400     E0000020
pec2   00024A52     00058000     0000CA00     00023000     C0000040
.rsrc  00001000     0007D000     00000400     0002FA00     50000040

RD-116 a eu la gentillesse de m'indiquer que le compresseur en question était PE Compact 1.25.
Voyons si il ne serait pas possible d'écrire un script pour ProcDump afin d'automatiser tout ça...

Première chose, il faudrait savoir quand les adresses 0047BAC6 sont décompressées et l'Original Entry Point poussé sur la pile. Un BPM va vite nous renseigner :

:0047B8E6  C1F902              SAR     ECX,02
:0047B8E9  F3A5                REPZ MOVSD
:0047B8EB  03C8                ADD     ECX,EAX
:0047B8ED  83E103              AND     ECX,03
:0047B8F0  F3A4                REPZ MOVSB
:0047B8F2  EB14                JMP     0047B908

En 0047B8F2, c'est good. Le passage du relais entre le compresseur et le programme d'origine est devenu clean.

Voyons voir si la routine trouvée est directement accessible depuis l'Entry Point du compresseur. Evidemment non ! Ca aurait été trop facile…

Recommençons un chtit coup de tracing depuis l'Entry Point, histoire de repérer les points de passage intéressants :

:004647B8  9C                  PUSHFD > Entry Point
:004647B9  60                  PUSHAD
:004647BA  E802000000          CALL    004647C1
:004647BF  33C0                XOR     EAX,EAX
:004647C1  8BC4                MOV     EAX,ESP
:004647C3  83C004              ADD     EAX,04
:004647C6  93                  XCHG    EAX,EBX
:004647C7  8BE3                MOV     ESP,EBX
:004647C9  8B5BFC              MOV     EBX,[EBX-04]
:004647CC  81EB0F704000        SUB     EBX,0040700F
:004647D2  87DD                XCHG    EBX,EBP
:004647D4  8B85A6704000        MOV     EAX,[EBP+004070A6]
:004647DA  018503704000        ADD     [EBP+00407003],EAX
:004647E0  66C785007040009090  MOV     WORD PTR [EBP+00407000],9090
:004647E9  01859E704000        ADD     [EBP+0040709E],EAX
:004647EF  BBC6080000          MOV     EBX,000008C6
:004647F4  039DAA704000        ADD     EBX,[EBP+004070AA]
:004647FA  039DA6704000        ADD     EBX,[EBP+004070A6]
:00464800  53                  PUSH    EBX
:00464801  53                  PUSH    EBX
:00464802  53                  PUSH    EBX
:00464803  53                  PUSH    EBX
:00464804  58                  POP     EAX
:00464805  2D70704000          SUB     EAX,00407070
:0046480A  898571704000        MOV     [EBP+00407071],EAX
:00464810  5F                  POP     EDI
:00464811  8DB570704000        LEA     ESI,[EBP+00407070]
:00464817  B916020000          MOV     ECX,00000216
:0046481C  F3A5                REPZ MOVSD
:0046481E  5F                  POP     EDI
:0046481F  C3                  RET     > premier passage de relais

Un " d 0047B8F2 " a ce moment va permettre de vérifier que la routine de décompression du passage de relais vers l'OEP est devenue clean.

Pour pouvoir lire tranquillement les codes en 0047BAC6 (qui passe la main à l'OEP), il faut donc s'arrêter une première fois en 0046481F, une seconde fois en 0047B8F2, avant de pouvoir coincer le Push OEP/Ret.

On va commencer par s'occuper du premier BreakPoint à poser :

:0046481C  F3A5                REPZ MOVSD
:0046481E  5F                  POP     EDI
:0046481F  C3                  RET 

et le script ProcDump va commencer comme ceci :

[PE Compact 1.25]        > titre
L1=LOOK 5F,C3            > recherche la chaîne 5F C3
L2=BP                    > pose un BreakPoint

Le BreakPoint de ProcDump fonctionne comme celui de SoftIce, et il va maintenant être possible de trouver la seconde chaîne. Une recherche dans SI sur

83E103              AND     ECX,03
F3A4                REPZ MOVSB
EB14                JMP     0047B908

Par un s cs :EIP l 500000 F3 A4 EB 14 va donner 2 occurrences. Aie !
Et en plus la première trouvée ne fait pas notre affaire, et se trouve située juste en dessous du Pop EDI /Ret. Par comparaison avec celle qui m'intéresse, elles font toutes les deux 46 octets de long. Pour pouvoir chopper la seconde, et ne pas faire planter le dump automatisé, j'ai du aller chercher une différence 46 octets plus haut :

La mauvaise procédure (la première)

:0046481E  5F                  POP     EDI
:0046481F  C3                  RET            > premier passage de relais
:00464820  BD56480700          MOV     EBP,00074856 
:00464825  57                  PUSH    EDI
:00464826  5E                  POP     ESI

La bonne procédure (la seconde)

:0047B8C5  00BD56480700        ADD     [EBP+00074856],BH
:0047B8CB  57                  PUSH    EDI
:0047B8CC  5E                  POP     ESI

Vous remarquerez que la différence se fait en 0046481F (C3) et en 0047B8C5 (00). C'est à ce niveau que j'ai choisi de chercher la chaîne qui me permettra de placer mon BreakPoint suivant. Par contre, ce n'est pas en 0047B8C5 qu'il doit être posé, mais 0x2D octets plus bas. Voici ma proposition de script :

L3=LOOK 00,BD,56  > recherche la chaîne " ADD [EBP+00074856],BH "
L4=ADD 2D         > ajoute 45 (décimal) à l'adresse trouvée
L5=BP             > pose un Breakpoint 

Et le breakpoint va être posé ici :

:0047B8ED  83E103              AND     ECX,03
:0047B8F0  F3A4                REPZ MOVSB
:0047B8F2  EB14                JMP     0047B908 > ici

Dernière ligne droite, viser le passage du relais au programme d'origine, noter l'adresse de l'OEP, réaliser le Dump avec l'option Rebuilt Import Table :

:0047BAC1  6820B74400          PUSH    0044B720
:0047BAC6  C20400              RET     0004     > adresse visée pour le BP

Une recherche en 0047B8F2 donne deux occurrences pour C2 04 00. La première correspondant à celle qu'il me faut, je vais faire simple :

L6=LOOK C2,04,00  > recherche de la chaîne 
L7=BP             > pose du Breakpoint
L8=STEP           > analyse pas à pas avec enregistrement de l'adresse de l'OEP
OPTL1=00000000    > options par défaut
OPTL2=01010001    > comme " Rebuilt Import Table "
OPTL3=01010001    > etc...
OPTL4=00030000    > (cf le script.txt livré avec ProcDump)
OPTL5=00000000

Et voilà à quoi va ressembler le script au complet pour Pe Compact 1.25 :

[PE Compact 1.25]
L1=LOOK 5F,C3
L2=BP
L3=LOOK 00,BD,56
L4=ADD 2D
L5=BP
L6=LOOK C2,04,00
L7=BP
L8=STEP
OPTL1=00000000
OPTL2=01010001
OPTL3=01010001
OPTL4=00030000
OPTL5=00000000

Par contre, chose étonnante, il fonctionne très bien avec les versions antérieures à la 1.60 (c'est à dire le moment ou G-Rom en a abandonné le développement), et très mal avec les versions postérieures (y compris la 1.62)


Mais revenons au Crackme de RD-116 :

Pour localiser un nag, il y a plusieurs possibilités, et entre autre :

Tracer le crackme depuis l'Entry Point jusqu'à trouver le call qui affichera le nag, puis tracer dans ce call, dans le suivant et ainsi de suite jusqu'à mettre la main sur le dernier call qui appellera l'API affichant le nag.
Disons que c'est une méthode en descendant...

Chercher dans Wdasm une fonction importée susceptible d'afficher un tel écran, à défaut d'en trouver une référence directe.

USER32.ShowScrollBar
USER32.ShowWindow                      > these one seams to be good !
USER32.SystemParameterslnfoA
USER32.TrackPopupMenu

Puis après l'affichage du nag, remonter le listing en sortant des différents call par F12.
L'un des endroits qui m'a paru le plus propice à une recherche approfondie se trouve dans les lignes suivantes, car plus vous descendrez dans les call, plus ceux ci seront sollicités. RD-116 a écrit son crackme en le programmant de telle sorte que les call qui servent à l'affichage du nag soient à usages multiples :

* Referenced by a CALL at Addresses:
|:0043D522   , :0043D7E1   , :0043EB4E   , :004411E2   , :004411EF   
|:00441AF2   , :00441D13   , :0044431D   > 8 appels sur cette routine.
|
:0043E0D0 53                      push ebx
:0043E0D1 56                      push esi
:0043E0D2 8BDA                    mov ebx, edx
:0043E0D4 8BF0                    mov esi, eax
:0043E0D6 F686CC02000001          test byte ptr [esi+000002CC], 01
:0043E0DD 7417                    je 0043E0F6
:0043E0DF 84DB                    test bl, bl
:0043E0E1 7409                    je 0043E0EC
:0043E0E3 808ECC02000002          or byte ptr [esi+000002CC], 02
:0043E0EA EB23                    jmp 0043E10F

En fonction de la valeur contenue dans [esi+2cc] (03 pour ce qui concerne le nag), il y a moyen de supprimer son affichage...

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043E0E1(C)
|
:0043E0EC 80A6CC020000FD          and byte ptr [esi+000002CC], FD
:0043E0F3 5E                      pop esi
:0043E0F4 5B                      pop ebx
:0043E0F5 C3                      ret

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043E0DD(C)
|
:0043E0F6 84DB                    test bl, bl
:0043E0F8 740C                    je 0043E106
:0043E0FA 3A5E47                  cmp bl, byte ptr [esi+47]
:0043E0FD 7407                    je 0043E106
:0043E0FF 8BC6                    mov eax, esi
:0043E101 E87E0E0000              call 0043EF84

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0043E0F8(C), :0043E0FD(C)
|
:0043E106 8BD3                    mov edx, ebx
:0043E108 8BC6                    mov eax, esi
:0043E10A E8D57FFEFF              call 004260E4   > le nag apparaît ici

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043E0EA(U)
|
:0043E10F 5E                      pop esi
:0043E110 5B                      pop ebx
:0043E111 C3                      ret

Par contre, cette routine étant commune au nag, à l'affichage du Crackme, et surtout à la fermeture du prog, une modification des branchements, ou une suppression du call, ne résoudra qu'une partie des problèmes. Si le nag ne s'affichera plus, la fermeture de l'application devra se faire par un CTRL-ALT-Suppr et un kill de la tache...

Pas Glop !

Comme il n'y a que 8 calls à tester, j'ai mis un bpm adresse X sur chacun d'entres eux. Le seul à n'être sollicité que par le nag est en 004411EF :

* Referenced by a CALL at Addresses:
|:004413A8   , :00443D62   , :0044B74E   
|
:004411E8 53                      push ebx
:004411E9 8BD8                    mov ebx, eax
:004411EB B201                    mov dl, 01
:004411ED 8BC3                    mov eax, ebx
:004411EF E8DCCEFFFF              call 0043E0D0  > vers la routine ci dessus
:004411F4 8BC3                    mov eax, ebx
:004411F6 E81552FEFF              call 00426410
:004411FB 5B                      pop ebx
:004411FC C3                      ret

Et en noppant purement et simplement ce call (ou en le re-adressant en 004411FC), le nag disparaît sans plus de façon.

Glop Glop !

Reste à patcher le crackme. Le faire sur le dump est évidemment très facile. Un memory patcher type R!SC Process Patcher merdouille avec le codage polymorphe si vous ne lui accordez pas un temps de recherche suffisant :

T=100000:
F=fcftrial.exe:
O=fcfcrk.exe:
P=004411EF/E8,DC,CE,FF,FF/90,90,90,90,90:
$

Plus de nag. Pour autant ce n'est pas encore suffisant.
Reste donc le hard patching du crackme. Dans ce cas précis, il faudra 4 patchs différents pour arriver à modifier le crackme. Mais pour commencer, il faut trouver, ou créer, de la place pour loger ce patch.

La Méthode de Nody : Chérie, j'ai agrandie l'exe !

Particulièrement difficile à mettre en œuvre, elle peut être d'une efficacité redoutable. Le principe consiste à rajouter une section à la suite des autres.

pec1    00057000   00001000   00022800   00000400   E0000020
pec2    00024A52   00058000   0000CC00   00022C00   C0000040
.rsrc   00001000   0007D000   00000400   0002F800   50000040
.Nody   00000000   0007E000   00001000   0002FC00   C0000040

Et pour y arriver, il n'y a qu'à lancer le programme de Nody : PE1.exe.
C'est tout !

Le programme de Nody se charge de lui-même de modifier le PE Header, et de re-router l'Entry Point vers la nouvelle section. Certains pourront voir un inconvénient dans la message box rappelant le passage de Nody, d'autre pourront nopper le call eax...

Vous voici à la tête de prés de 1000 octets disponibles. Largement de quoi s'exprimer, non ?

Passons au Patch. En partant des mêmes adresses que celles repérées pour écrire le script de ProcDump, il va falloir créer un premier branchement vers le patch en 0047E1DC à partir de l'adresse 00464817, car il ne faut pas oublier q'un jump long occupe 5 octets, et qu'il ne faut pas déborder après le RET :

:00464811  8DB570704000        LEA     ESI,[EBP+00407070]
:00464817  B916020000          MOV     ECX,00000216  > Ceci
:0046481C  F3A5                REPZ MOVSD
:0046481E  5F                  POP     EDI
:0046481F  C3                  RET

Première modification : le saut vers la patch 1

:00464811  8DB570704000        LEA     ESI,[EBP+00407070]
:00464817  E9A6990100          JMP     0047E1C2      > devient cela
:0046481C  F3A5                REPZ MOVSD
:0046481E  5F                  POP     EDI
:0046481F  C3                  RET

Patch 1 :

:0047E1BF  8BDA                MOV     EBX,EDX
:0047E1C1  C3                  RET     > fin de l'intervention de Nody
:0047E1C2  B916020000          MOV     ECX,00000216
:0047E1C7  F3A5                REPZ MOVSD
:0047E1C9  C705EDB84700E9EA2800MOV     DWORD PTR [0047B8ED],0028EAE9
:0047E1D3  C605F1B8470000      MOV     BYTE PTR [0047B8F1],00
:0047E1DA  5F                  POP     EDI
:0047E1DB  C3                  RET

A partir de l'adresse 0047E1C2, la place est pour nous. Le patch 1 réécrit les octets écrasés, et implante en 0047B8ED le saut vers le Patch 2.

Résultat du Patch 1 :

:0047B8E4  8BC1                MOV     EAX,ECX
:0047B8E6  C1F902              SAR     ECX,02
:0047B8E9  F3A5                REPZ MOVSD
:0047B8EB  03C8                ADD     ECX,EAX
:0047B8ED  E9EA280000          JMP     0047E1DC  > saut vers patch 2
:0047B8F2  EB14                JMP     0047B908
:0047B8F4  C0D147              RCL     CL,47

Arrivé dans la routine ci dessus, le jump va brancher sur la patch 2 :

Patch 2 :

:0047E1DC  83E103              AND     ECX,03 > réécrit les
:0047E1DF  F3A4                REPZ MOVSB     > octets écrasés
:0047E1E1  C705BFBA4700E9332700MOV     DWORD PTR [0047BABF],002733E9
:0047E1EB  C605C3BA470000      MOV     BYTE PTR [0047BAC3],00
:0047E1F2  E911D7FFFF          JMP     0047B908 > continue sa route

Pour qu'il implante le saut vers patch 3 juste avant de passer la main à l'OEP.

Résultat du Patch 2 :

:0047BAAD  8BB51E744000        MOV     ESI,[EBP+0040741E]
:0047BAB3  8BBD22744000        MOV     EDI,[EBP+00407422]
:0047BAB9  E85E040000          CALL    0047BF1C
:0047BABE  61                  POPAD
:0047BABF  E933270000          JMP     0047E1F7  > saut vers patch 3
:0047BAC4  44                  INC     ESP
:0047BAC5  00C2                ADD     DL,AL
:0047BAC7  0400                ADD     AL,00

Le Patch 3 :

:0047E1F7  C705EF11440090909090MOV     DWORD PTR [004411EF],90909090
:0047E201  C605F311440090      MOV     BYTE PTR [004411F3],90
:0047E208  9D                  POPFD            > réécrit les 
:0047E209  50                  PUSH    EAX      > octets écrasés
:0047E20A  6820B74400          PUSH    0044B720 > et retour à la
:0047E20F  C20400              RET     0004     > normale
:0047E212  0000                ADD     [EAX],AL > le rab de place
:0047E214  0000                ADD     [EAX],AL

Son job va être de virer le call appelant le nag.
Esta Finito !

Le prog est patché, et tout fonctionne…
No more Nag Screen !


Problème n°2 : le sérial à entrer

Je ne pense pas que cet exercice soit solvable dans la mesure ou RD-116 saisi le sérial entré, le code, et compare ensuite ce code avec le sérial entré. A partir de là, je ne vois pas comment il y aurait moyen d'obtenir une comparaison de sérial positive.

Voici la routine de comparaison :

:00403C70 53                      push ebx
:00403C71 56                      push esi
:00403C72 57                      push edi
:00403C73 89C6                    mov esi, eax  > eax le code généré
:00403C75 89D7                    mov edi, edx  > edx notre code
:00403C77 39D0                    cmp eax, edx
:00403C79 0F848F000000            je 00403D0E

Et à la sortie de ce call :

:0044B302  8B55E4              MOV     EDX,[EBP-1C]
:0044B305  58                  POP     EAX
:0044B306  E86589FBFF          CALL    00403C70  > call de comparaison
:0044B30B  751A                JNZ     0044B327  > saut good boy/ bad boy
:0044B30D  6A00                PUSH    00
:0044B30F  B988B34400          MOV     ECX,0044B388

Il suffit d'inverser le saut en 0044B30B pour valider tout sérial entré, mais ce n'était pas la " commande " de RD-116

Pour moi, le problème n'est donc pas possible à résoudre, mais pour avoir passé du temps sur la recherche du bon sérial, j'ai trouvé que RD-116 avait fait fort pour le codage de celui ci :

Dans la routine de comparaison vous verrez le sérial tel qu'il est comparé avec le notre : -754140928 par exemple.

En tapant un d EAX, vous calerez la fenêtre des datas sur l'affichage du code calculé. En repartant de la saisie du sérial entré, et en traçant avec F10 vous allez attendre le moment ou celui ci réapparaîtra dans la fenêtre. Puis en descendant de call en call, vous finirez ici :

Génération du code :

:0040847A B90A000000       mov ecx, 0000000A   > le code fera 10 caract de long
:0040847F 8D759F           lea esi, dword ptr [ebp-61] > et sera placé à cette
                                                       > adresse en partant du
                                                       > dernier caractère
:00408482 31D2             xor edx, edx   > met EDX à 0, début de la boucle
:00408484 F7F1             div ecx        > divise EAX par 10. 
                                          > Le reste va dans edx
:00408486 80C230           add dl, 30     > ajoute 30 (30 = 0 ascii) à EDX
:00408489 80FA3A           cmp dl, 3A     > compare si le résultat est : ascii
:0040848C 7203             jb 00408491    > et sinon rajoute 7 
:0040848E 80C207           add dl, 07     > au résultat obtenu 
:00408491 4E               dec esi        > prochain caract à écrire
:00408492 8816             mov byte ptr [esi], dl > en mémoire
:00408494 09C0             or eax, eax    > les 10 caract sont calculés ?
:00408496 75EA             jne 00408482   > sinon Loop

et à la sortie du call

:0040846E E807000000       call 0040847A  > génération du sérial
:00408473 B02D             mov al, 2D     > place 2D (-) dans AL
:00408475 41               inc ecx        > ajoute 1 à ECX (?)
:00408476 4E               dec esi        > décrémente de 1 l'adresse du code
:00408477 8806             mov byte ptr [esi], al > pour placer devant celui ci
:00408479 C3               ret            > un signe -


En se calant dans la fenêtre des datas sur l'adresse de ESI, vous verrez le code apparaître au fur et à mesure des Loop.
C'est donc EAX (et le résultat de sa division par 10) qui va être à la base du calcul de ce code. Reste à trouver comment EAX a bien pu recevoir une valeur, qui varie suivant le sérial que vous avez entré.

En procédant de la même façon que pour le code calculé, j'ai cherché en traçant, un œil sur le fenêtre des Datas, à quel moment EAX recevait la valeur qui m'intéressait. Après quelques changements de registres, je suis arrivé ici :

La valeur de EAX va être calculée à partir des 4 premiers caractères du sérial entré. En prenant l'hypothèse d'un sérial égale à 4893555. Le premier caractère va être le 4, soit 34 en ASCII.

:0044B24F 8D55F8         lea edx, dword ptr [ebp-08] 
:0044B252 8B83D8020000   mov eax, dword ptr [ebx+000002D8]
:0044B258 E86FAFFDFF     call 004261CC
:0044B25D 8B45F8         mov eax, dword ptr [ebp-08]      > sérial entré 
:0044B260 0FB600         movzx eax, byte ptr [eax]        > 1er caract dans EAX 
:0044B263 8BF0           mov esi, eax                     > esi = 34
:0044B265 C1E604         shl esi, 04                      > rotation à droite ESI = 340
:0044B268 8D55F4         lea edx, dword ptr [ebp-0C]      > ? ? ?
:0044B26B 8B83D8020000   mov eax, dword ptr [ebx+000002D8]> ? ? ?
:0044B271 E856AFFDFF     call 004261CC                    > ? ? ?
:0044B276 8B45F4         mov eax, dword ptr [ebp-0C]      > sérial entré
:0044B279 0FB64001       movzx eax, byte ptr [eax+01]     > 2d caractère -> 38
:0044B27D 6BC016         imul eax, 00000016               > mult sur 16 bits -> 4D0
:0044B280 03F0           add esi, eax                     > + ESI = 810
:0044B282 8D55F0         lea edx, dword ptr [ebp-10]      > ? ? ?
:0044B285 8B83D8020000   mov eax, dword ptr [ebx+000002D8]> ? ? ?
:0044B28B E83CAFFDFF     call 004261CC                    > ? ? ?
:0044B290 8B45F0         mov eax, dword ptr [ebp-10]      > sérial entré
:0044B293 0FB64002       movzx eax, byte ptr [eax+02]     > 3 eme caract -> 39
:0044B297 C1E002         shl eax, 02                      > rotation à gauche -> E4
:0044B29A 03F0           add esi, eax                     > ESI + EAX = 8F4
:0044B29C 8D55EC         lea edx, dword ptr [ebp-14]      > ? ? ?
:0044B29F 8B83D8020000   mov eax, dword ptr [ebx+000002D8]> ? ? ?
:0044B2A5 E822AFFDFF     call 004261CC                    > ? ? ?
:0044B2AA 8B45EC         mov eax, dword ptr [ebp-14]      > sérial entré
:0044B2AD 0FB64003       movzx eax, byte ptr [eax+03]     > 4eme caract -> 33
:0044B2B1 03C0           add eax, eax                     > 33 * 2 = 66
:0044B2B3 8D0480         lea eax, dword ptr [eax+4*eax]   > (66*4)+66 = 1FE
:0044B2B6 03F0           add esi, eax                     > 1FE + 8F4 = AF2
:0044B2B8 893574D84400   mov dword ptr [0044D874], esi    > ESI en mémoire
:0044B2BE A178D84400     mov eax, dword ptr [0044D878]    > EAX = lg du sérial entré
:0044B2C3 E884CCFBFF     call 00407F4C                    > ? ? ?
:0044B2C8 8B1574D84400   mov edx,  dword ptr [0044D874]   > EDX = AF2
:0044B2CE 0FAF1574D84400 imul edx, dword ptr [0044D874]   > AF2 * AF2 = 77CCC4
:0044B2D5 0FAF1574D84400 imul edx, dword ptr [0044D874]   > 77CCC4 * AFD = 1F3F3948
:0044B2DC F7EA           imul edx               > 1F3F3948 * 6(ld du sérial)= BB7B57B0
:0044B2DE A374D84400     mov dword ptr [0044D874], eax    > met cette valeur en mémoire

EAX va donc valoir BB7B57B0, et c'est cette base qui va servir à la génération du code.

3eme Problème : trouver le sérial correspondant au nom entré

L'étude de calcul du code ci dessus n'aura pas été inutile.
Comme c'est souvent le cas, un programmeur réutilise facilement une routine déjà faite (comme c'est la cas pour la génération du code) ou en fait un coupé/collé, comme ça va être à peu de choses prés le cas pour le calcul de la base placée dans EAX
Pour trouver les adresses qui suivent, il y a plusieurs solutions :
Soit celle de tout à l'heure, soit en recherchant une séquence de codes du même genre...
Ca ne marche évidemment pas à tous les coups, mais la nature humaine étant ce qu'elle est :

:0044B078  8D55F8              LEA     EDX,[EBP-08]
:0044B07B  8B83E0020000        MOV     EAX,[EBX+000002E0]
:0044B081  E846B1FDFF          CALL    004261CC
:0044B086  8B45F8              MOV     EAX,[EBP-08]
:0044B089  0FB600              MOVZX   EAX,BYTE PTR [EAX]
:0044B08C  6BF07A              IMUL    ESI,EAX,7A
:0044B08F  8D55F4              LEA     EDX,[EBP-0C]
:0044B092  8B83E0020000        MOV     EAX,[EBX+000002E0]
:0044B098  E82FB1FDFF          CALL    004261CC
:0044B09D  8B45F4              MOV     EAX,[EBP-0C]
:0044B0A0  0FB64001            MOVZX   EAX,BYTE PTR [EAX+01]
:0044B0A4  C1E003              SHL     EAX,03
:0044B0A7  8D0440              LEA     EAX,[EAX*2+EAX]
:0044B0AA  03F0                ADD     ESI,EAX
:0044B0AC  8D55F0              LEA     EDX,[EBP-10]
:0044B0AF  8B83E0020000        MOV     EAX,[EBX+000002E0]
:0044B0B5  E812B1FDFF          CALL    004261CC
:0044B0BA  8B45F0              MOV     EAX,[EBP-10]
:0044B0BD  0FB64002            MOVZX   EAX,BYTE PTR [EAX+02]
:0044B0C1  03F0                ADD     ESI,EAX
:0044B0C3  8D55EC              LEA     EDX,[EBP-14]
:0044B0C6  8B83E0020000        MOV     EAX,[EBX+000002E0]
:0044B0CC  E8FBB0FDFF          CALL    004261CC
:0044B0D1  8B45EC              MOV     EAX,[EBP-14]
:0044B0D4  0FB64003            MOVZX   EAX,BYTE PTR [EAX+03]
:0044B0D8  03C0                ADD     EAX,EAX
:0044B0DA  8D0440              LEA     EAX,[EAX*2+EAX]
:0044B0DD  03F0                ADD     ESI,EAX
:0044B0DF  893574D84400        MOV     [0044D874],ESI
:0044B0E5  A178D84400          MOV     EAX,[0044D878]
:0044B0EA  E85DCEFBFF          CALL    00407F4C
:0044B0EF  8B1574D84400        MOV     EDX,[0044D874]
:0044B0F5  0FAF1574D84400      IMUL    EDX,[0044D874]
:0044B0FC  F7EA                IMUL    EDX
:0044B0FE  A374D84400          MOV     [0044D874],EAX -> 6FE2B720

Pour le mon que j'ai entré, j'obtiens une base de 6FE2B720, et un sérial calculé de 1877128992, comme je pourrais le voir en faisant un d eax dans la même routine de comparaison que pour le sérial précédent :

016F:00C3C262 00 00 63 68 72 69 73 74-61 6C 00 6C 3A 00 1A 00  ..christal.l:...
016F:00C3C272 00 00 01 00 00 00 08 00-00 00 63 68 72 69 73 74  ..........christ
016F:00C3C282 61 6C 00 6C 3A 00 1A 00-00 00 01 00 00 00 0A 00  al.l:...........
016F:00C3C292 00 00 31 38 37 37 31 32-38 39 39 32 00 00 74 D4  ..1877128992..t.
016F:00C3C2A2 44 00 74 D4 44 00 5C 3D-00 00 26 56 E9 72 69 66  D.t.D.\=..&V.rif
016F:00C3C2B2 69 65 72 00 00 00 74 D4-44 00 74 D4 44 00 44 3D  ier...t.D.t.D.D=
016F:00C3C2C2 00 00 1B 00 00 00 00 00-00 00 0B 00 00 00 4E 6F  ..............No
016F:00C3C2D2 6D 2F 53 65 72 69 61 6C-3A 00 74 D4 44 00 74 D4  m/Serial:.t.D.t.

Au passage vous remarquerez qu'une recherche sur le nom entré aurait probablement permis de trouver l'écho du bon sérial...

Contrairement au premier sérial, pas de tiret devant le code généré :

:0040828B  E85A000000          CALL    004082EA
:00408290  895DE0              MOV     [EBP-20],EBX
:00408293  8975DC              MOV     [EBP-24],ESI
:00408296  51                  PUSH    ECX
:00408297  52                  PUSH    EDX
:00408298  E896000000          CALL    00408333  call calcul de code
:0040829D  5A                  POP     EDX
:0040829E  8B5DE4              MOV     EBX,[EBP-1C]
:004082A1  29CB                SUB     EBX,ECX

Pour ce conformer à l'exercice proposé par RD-116, voilà qui me semble suffisant pour écrire un KeyGen.
Il n'y a pratiquement qu'à faire un coupé/collé...

4ème exercice : le KeyFile

Au choix, RD-116 propose de créer un fichier ou de patcher le prog. Regardons du coté du dead Listing :

* Possible StringData Ref from Code Obj ->"c:\windows\system\"
                                  |
:0044B40C 68A4B44400              push 0044B4A4
:0044B411 8D55F8                  lea edx, dword ptr [ebp-08]
:0044B414 8B8304030000            mov eax, dword ptr [ebx+00000304]
:0044B41A E8ADADFDFF              call 004261CC
:0044B41F FF75F8                  push [ebp-08]

* Possible StringData Ref from Code Obj ->"crackme.key"
                                  |
:0044B422 68C0B44400              push 0044B4C0
:0044B427 8D45FC                  lea eax, dword ptr [ebp-04]
:0044B42A BA03000000              mov edx, 00000003
:0044B42F E8EC87FBFF              call 00403C20
:0044B434 8B45FC                  mov eax, dword ptr [ebp-04]
:0044B437 E88CC9FBFF              call 00407DC8
:0044B43C 84C0                    test al, al
:0044B43E 741A                    je 0044B45A
:0044B440 6A00                    push 00000000

* Possible StringData Ref from Code Obj ->"???Bien Jou"
                                  |
:0044B442 B9CCB44400              mov ecx, 0044B4CC

* Possible StringData Ref from Code Obj ->"Vous avez r"

Intéressant non ?
Il est assez facile d'imaginer que le KeyFile en question pourrait s'appeler crackme.key, et se trouver dans le répertoire c:\window\system.
Aussitôt pensé, aussitôt fait, et voilà un fichier crackme.key de mis dans le fichier system !

Aie !, ce n'est pas encore ça...
Allons jeter un coup d'œil en Live approach.

Un d eax en 0044B434 va être très instructif :

016F:00C3C264 63 3A 5C 77 69 6E 64 6F-77 73 5C 73 79 73 74 65  c:\windows\syste
016F:00C3C274 6D 5C 66 63 66 74 72 69-61 6C 5C 63 72 61 63 6B  m\fcftrial\crack
016F:00C3C284 6D 65 2E 6B 65 79 00 00-74 D4 44 00 74 D4 44 00  me.key..t.D.t.D.
016F:00C3C294 70 3D 00 00 31 32 38 39-39 32 00 00 74 D4 44 00  p=..128992..t.D.
016F:00C3C2A4 74 D4 44 00 5C 3D 00 00-31 38 37 37 31 32 38 39  t.D.\=..18771289
016F:00C3C2B4 39 32 00 00 74 D4 44 00-74 D4 44 00 44 3D 00 00  92..t.D.t.D.D=..
016F:00C3C2C4 26 56 E9 72 69 66 69 65-72 00 00 00 74 D4 44 00  &V.rifier...t.D.

Je doute qu'il vous failles plus d'explications !
Ensuite, que le fichier .key soit vide ou plein ne change rien. Dans tous les cas la présence seule du fichier au bon endroit suffit.

Quand au patch, il n'y a qu'à inverser le saut pour faire croire au Crackme que tout est bon.
Il va de soit que dans un Shareware, le fait de ne détourner que l'affichage ne serait pas suffisant, et qu'il faudrait chercher un peu plus en profondeur, et dans le call précèdent le test par exemple :

:00407DC8  53                  PUSH    EBX
:00407DC9  8BD8                MOV     EBX,EAX
:00407DCB  8BC3                MOV     EAX,EBX
:00407DCD  E88EFFFFFF          CALL    00407D60 > call déterminant
:00407DD2  40                  INC     EAX
:00407DD3  0F95C0              SETNZ   AL  > la valeur de AL est manipulée
:00407DD6  5B                  POP     EBX
:00407DD7  C3                  RET

En 00407DD3, le setnz al ne fait que manipuler ce qui a été trouvé par le call 00407D60. Un chtit coup d'œil :

:00407D60  55                  PUSH    EBP
:00407D61  8BEC                MOV     EBP,ESP
:00407D63  81C4B4FEFFFF        ADD     ESP,FFFFFEB4
:00407D69  53                  PUSH    EBX
:00407D6A  8BD8                MOV     EBX,EAX
:00407D6C  8D85B4FEFFFF        LEA     EAX,[EBP-014C]
:00407D72  50                  PUSH    EAX
:00407D73  8BC3                MOV     EAX,EBX
:00407D75  E8AABFFFFF          CALL    00403D24
:00407D7A  50                  PUSH    EAX           > c:\windows\system etc…
:00407D7B  E870E2FFFF          CALL    KERNEL32!FindFirstFileA
:00407D80  83F8FF              CMP     EAX,-01         Oh Oh !
:00407D83  7434                JZ      00407DB9
:00407D85  50                  PUSH    EAX
:00407D86  E85DE2FFFF          CALL    KERNEL32!FindClose
:00407D8B  F685B4FEFFFF10      TEST    BYTE PTR [EBP-014C],10
:00407D92  7525                JNZ     00407DB9
:00407D94  8D45F4              LEA     EAX,[EBP-0C]
:00407D97  50                  PUSH    EAX
:00407D98  8D85C8FEFFFF        LEA     EAX,[EBP-0138]
:00407D9E  50                  PUSH    EAX
:00407D9F  E83CE2FFFF          CALL    KERNEL32!FileTimeToLocalFileTime
:00407DA4  8D45FC              LEA     EAX,[EBP-04]
:00407DA7  50                  PUSH    EAX
:00407DA8  8D45FE              LEA     EAX,[EBP-02]
:00407DAB  50                  PUSH    EAX
:00407DAC  8D45F4              LEA     EAX,[EBP-0C]
:00407DAF  50                  PUSH    EAX
:00407DB0  E823E2FFFF          CALL    KERNEL32!FileTimeToDosDateTime
:00407DB5  85C0                TEST    EAX,EAX
:00407DB7  7507                JNZ     00407DC0
:00407DB9  C745FCFFFFFFFF      MOV     DWORD PTR [EBP-04],FFFFFFFF
:00407DC0  8B45FC              MOV     EAX,[EBP-04]
:00407DC3  5B                  POP     EBX
:00407DC4  8BE5                MOV     ESP,EBP
:00407DC6  5D                  POP     EBP
:00407DC7  C3                  RET

Bingo ! Si au retour du CALL KERNEL32!FindFirstFileA EAX est différent de -1, le fichier recherché (crackme.key) est bien là, et à mon avis cette routine me semble être la meilleurs pour y glisser une petite modification...

:00407DB5  85C0                TEST    EAX,EAX
:00407DB7  7507                JNZ     00407DC0
:00407DB9  B810000000          MOV     EAX,00000010 > par exemple
:00407DBE  90                  NOP     > pas élégant
:00407DBF  90                  NOP     > mais suffisant !
:00407DC0  90                  NOP
:00407DC1  90                  NOP
:00407DC2  90                  NOP
:00407DC3  5B                  POP     EBX
:00407DC4  8BE5                MOV     ESP,EBP
:00407DC6  5D                  POP     EBP
:00407DC7  C3                  RET

Ce qui pourrait donner pour le script RPP :

T=100000:
F=fcftrial.exe:
O=fcfcrk.exe:
P=004411EF/E8,DC,CE,FF,FF/90,90,90,90,90:
P=00407DB9/C7,45,FC,FF,FF,FF,FF,8B,45,FC/B8,10,00,00,00,90,90,90,90,90:
$

That's all

Bonne Journée
Christal