API Spy 32 version 2.4


API Spy 32 est un de ces petits programmes très utile à notre quète, mais qui malheureusement comprend certains Bugs, dont l'un des moindres est une compression de l'exécutable.

Je n'avais jamais utilisé GetType jusqu'à aujourd'hui...
Mais c'est qu'il est très bien cet utilitaire !

Packer: Petite 1.2
      Calculated entrypoint: 54272 / 0000D400h  (RVA: 0001A000h)
        File is executable
      Objects (object align = 00001000h):
        Name      Virt size     RVA     Phys size  Phys Ofs
        .text     0000F000h  00001000h  00003A00h  00000400h
        .idata    00001000h  00010000h  00000C00h  00003E00h
        .rsrc     00009000h  00011000h  00008A00h  00004A00h
        .madmat   0000D268h  0001A000h  0000161Ch  0000D400h

Il est capable de reconnaître un grand nombre de Packer, mais je le trouve un peu léger, au regard de mes "besoins" pour ce qui concerne les caractéristiques des sections, éventuellement l'imageBase (en général 00400000, mais ce n'est pas une règle absolue)
L'Entry Point est donc en RVA 0001A000 + les 00400000 de l'ImageBase , soit 0041A000 :

:0041A000  669C                PUSHF
:0041A002  60                  PUSHAD    > sauvegarde des registres
:0041A003  E8CA000000          CALL      0041A0D2
:0041A008  0300                ADD       EAX,[EAX]

Qui dit Pushad, dit Popad. Il suffit de tracer un peu le programme pour le trouver

:0041B5CF  61                  POPAD     > restitution des registres
:0041B5D0  669D                POPF
:0041B5D2  E9E99FFEFF          JMP       004055C0

Et le JMP 004055C0 va nous donner la valeur de l'Original Entry Point d'API Spy 32.

Reste à réaliser un Dump de cette cible, histoire d'obtenir les String Data References qui vont nous aider à débugger le programme.
Il y a différentes solutions pour y arriver.
L'une des plus facile est d'utiliser TRW 2000 :

:pedump
PE dump
VirtualSize RVA PhysicalSize PhysicalOffset
----------
    f000     1000     3a00      400
    1000    10000      c00     3e00
    9000    11000     8a00     4a00
    d268    1a000     161c     d400
Writing DOS head
Writing PE head, from 81630554, len f8+a0
Writing 400218 len 27050

(au passage, vous apprécierez la facilité d'utilisation de la commande PeDump de TRW 2000, qui se charge de lui même de nommer un Dump.exe dans le répertoire de l'application/cible).

Sur la version de SoftIce que j'utilise, je n'ai pas encore installé IceDump (bien pratique lui aussi), mais il est facile de créer un autre Dump en utilisant le couteau suisse de l'UnPackage : ProcDump

Process :

- BPX sur l'adresse du passage de relais en 0041B5D2
- " a " pour passer en mode assemblage
- JMP EIP pour forcer le programme à boucler sur lui-même
- F5 pour quitter SoftIce
- Ouverture de ProcDump, et sélection de "
Rebuilt import Table " dans " Options "
- Clic Droit sur la cible dans la fenêtre de ProcDump, et sélection de "
Dump Full "
- Sauvegarde du Dump obtenu, et réouverture de celui ci par "
PE Editor "
- Modification du RVA de l'Entry Point qui passe de 01A000 à 0055C0


Good ?
Le dump obtenu RUN sans problème, et se désassemble sans frémir.

Glop ! Glop !

Mais que de manipulations pour un malheureux Dump !
Et si nous automatisions tout cela, dans la mesure ou le Script de ProcDump ne le permet pas malgré une option "Petite < 1.3"?

Le problème, bien qu'il soit mineur, est que la signature du passage de relais (
61 66 9D)

:0041B5CF  61                  POPAD     
:0041B5D0  669D                POPF
:0041B5D2  E9E99FFEFF          JMP       004055C0 > vers OEP

n'est pas accessible dès l'Entry Point de la cible compressée.
En bloquant la fenêtre des Data de SoftIce sur 0041B5CF, les codes qui nous intéressent vont apparaître en traçant sur les lignes suivantes :

:0041A104  800424A1            ADD       BYTE PTR [ESP],A1
:0041A108  50                  PUSH      EAX
:0041A109  800424BF            ADD       BYTE PTR [ESP],BF
:0041A10D  833A00              CMP       DWORD PTR [EDX],00 > ici

Le script envisagé va devoir trouver en premier des codes "évidents" placés non loin des adresses ci dessus, avant de s'occuper de rechercher la signature de pETITE, et du passage de relais.
Les lignes qui suivent me semblent faire parfaitement l'affaire :

017F:0041A196  F3A4                REPZ MOVSB
017F:0041A198  5F                  POP       EDI
017F:0041A199  5E                  POP       ESI
017F:0041A19A  C3                  RET  

En recherchant un F3 A4 5F 5E C3 (accésible dès l'Entry point de pETITE en 0041A000), je vais pouvoir rebondir vers la recherche de ma seconde signature (61 66 9D). Voici le script que je vous propose :

[Petite 1.2]
L1=LOOK F3,A4,5F,5E,C3            ; 1er signature
L2=OBJR                           ; nouvelle recherche
L3=LOOK 61,66,9D                  ; seconde signature
L4=BP                             ; pose d'un BreakPoint
L5=STEP                           ; début du traçage
OPTL1=00000000                    ; les lignes suivantes
OPTL2=01000001                    ; correspondent aux options
OPTL3=01010001                    ; que vous pouvez habituellement
OPTL4=00030000                    ; sélectionner dans
OPTL5=00000000                    ; le menu " OPTIONS " de ProcDump

En réutilisant notre couteau suisse, mais cette fois ci via le bouton " UnPack ", vous verrez les messages suivant défiler :

String search in progress .....
Setting Memory Search Base to 0x0041A000   ; EntryPoint
String search in progress .....
Setting breakpoint at 0x0041B5CF        ; second signature trouvée
Breakpoint reached at 0x0041B5CF            
Step by step analyzis activated ...           ; commence le tracing

Glop ! Glop !
Ca marche !

C'est bien beau, un script, mais il arrive parfois qu'ils ne fonctionnent que pour un programme donné, et qu'en l'appliquant sur une autre cible, ProcDump n'arrive pas à retrouver ces petits...
Histoire de vérifier rapidement si notre script est un peu plus gnéral que pour API Spy 32 uniquement, le plus simple est de Packer un autre programme avec pETITE 1.2 est vérifier ainsi si le script a une chance d'être plus générique.
Une fois de plus, c'est Notepad.exe qui va jouer le rôle du programme test. A croire qu'on lui en veut, à ce petit truc... j'ai l'impression qu'aucun exécutable n'a été plus sollicité pour toutes les bidouilles possibles que ce programme de 56 Ko.

Une fois packé, le programme va gagner 9 Ko soit grosso modo un gain de 17%. Une fois ProcDumpé le programme va par contre passer à 116 Ko. La différence est essentiellement due à la présence des codes de pETITE qui bien que non utilisés restent présent dans le Dump obtenu.

A titre de comparaison, API Spy 32.exe fait 59 Ko (admirable ! il existe encore des programme qui ne fassent pas obligatoirement 140 Mo...). En utilisant le script, il passe à 137 Ko. 135 Ko en utilisant la technique du JMP EIP/Dump Full de ProcDump, et 157 Ko avec la fonction intégrée PeDump de TRW 2000.

Vainqueur de notre grand test (benchmarck Chr 1s Tal), le Dump Full de ProcDump !

Quelle que soit la méthode, wdasm va donner les String Data References qui vont nous simplifier la suite de la correction des Bugs dont est atteint API Spy 32 :

Infections :

- Splash Screen au lancement de l'application, avec la mention " UNREGISTERED "
- Immédiatement derrière, une boite de dialogue " this copy is UNREGISTERED " et "Registration info can be found..."
- A nouveau " UNREGISTERED " qui s'affiche dans le bandeau d'écran au lancement de la fenêtre principale de l'application (la même chaîne de cractères est elle utilisée pour tous les " UNREGISTERED " ?)
- Un bouton " Register " dans le bas de cette fenêtre qui donne accès à une boite d'enregistrement à deux champs (Name et serial), et un message d'erreur " the registration information you provided... "
- Une boite " About " avec une fois de plus le texte " UNREGISTERED " (soit 4 fois cette phrase qui va s'afficher).


Il y a peut être encore d'autres infections Sharewares, mais celles ci vont être suffisantes pour le moment.

Qu'avons nous comme Strings Data dans Wdasm :

"Registered to "
"Registration info can be found "
"Thanks for Registering APIS32!"
"The registration information you "
"This copy of APIS32 is"
"UNREGISTERED"
"UserKey"
"UserName"

En double-cliquant sur ces Strings, vous trouverez rapidement un point commun lié à la procédure Contrôle/vérifications/tests : le Call 004046A0.
Voici un "morceau choisi" des routines les plus intéressantes :

* Possible StringData Ref from Data 0bj ->" - "
I
:0040175A 6828934000              push 00409328  > " - "
:0040175F 68EOD14000              push 0040D1EO  > " API Spy 32 "
:00401764 FF15F8024100            call dword ptr [004102F8]
:0040176A E8312FOOOO              call 004046AO  > ? ? ?
:0040176F A374CE4000              mov dword ptr [0040CE74], eax -> Flag ?
:00401774 EB01                    jmp 00401777

:00401777 0ACO                    or al, al      > en fonction de EAX
:00401779 7402                    je 0040177D    > vous irez en Pas Glop!
:0040177B EB2C                    jmp 004017A9   > ou en Glop! Glop!

* Possible StringData Ref from Data 0bj ->"The registration information you provided is incorrect. P1ease"

:0040177D BFE8904000              mov edi, 004090E8
:00401782 BAEOD14000              mov edx, 0040D1EO
------------------------snips---------------------------------
* Referenced by a (U)nconditional or (C)onditiona1 Jump at Address:
|:0040177B(U)
I
* Possible StringData Ref from Data 0bj ->"Registered to "
I
:004017A9 BF40904000              mov edi, 00409040
:004017AE BAEOD14000              mov edx, 0040D1EO

* Possible StringData Ref from Data 0bj ->"Thanks for Registering APIS32!"
I
:0040181E BFA8904000              mov edi, 004090A8
:00401823 BAEOD14000              mov edx, 0040D1EO

Et va inscrire les informations Name et Serial dans la base de registre.

Une recherche sur une autre String va nous redonner le Call 004046A0 :

:004015CC E8CF300000              call 004046A0 > Contrôle
:004015D1 EB01                    jmp 004015D4

:004015D4 0ACO                    or al, al     > Registered ?
:004015D6 7402                    je 004015DA   > Pas Glop! pas Glop!
:004015D8 EB35                    jmp 0040160F  > Glop! Glop!

* Referenced by a (U)nconditional or (C)onditiona1 Jump at Address:
|:004015D6(C)

* Possible StringData Ref from Data 0bj ->"UNREGISTERED"

Vous retrouverez une fois de plus le même schéma pour la String Data suivante :

:004025E6 E8B5200000              call 004046AO
:004025EB A374CE4000              mov dword ptr [0040CE74], eax
:004025FO EB01                    jmp 004025F3

:004025F3 0ACO                    or al, al
:004025F5 7402                    je 004025F9
:004025F7 EB70                    jmp 00402669
* Referenced by a (U)nconditional or (C)onditiona1 Jump at Address:
|:004025F5(C)
I
* Possible StringData Ref from Data 0bj ->"This  copy  of  APIS32  is"


Le or al,al va exécuter un OU logique sur le registre EAX. Si celui ci est égal à zéro, vous êtes Bad Boy.
Vous ne pensez pas que le call 004046A0 mérite une petite visite ?

* Referenced by a CALL at Addresses:
|:004015CC   , :0040176A   , :00401D1A   , :00401F39   , :004025E6
I
:004046AO       push ecx
:004046A1       push ebx
:004046A2       push ebp
:004046A3       push esi
:004046A4       push edi
:004046A5       push 00000050
:004046A7       push 0040C6AO

* Possible StringData Ref from Data 0bj ->"UserKey"
I
:004046AC       push 00409608
:004046B1       call 004049DO
:004046B6       add esp, 0000000C
:004046B9       cmp eax, 00000010  > serial de 17 caractères ?
:004046BC       jge 004046C6       > Good Boy continue
:004046BE       xor eax, eax       > Bad Boy est Xoré

Pour un crack facile, inutile d'aller beaucoup plus loin. En forçant EAX=01 à la place du Xor, vous quittez la procédure, mais avec la valeur dans EAX qui va vous faire passer pour Registered. Il ne restera plus qu'à patcher le Dump, ou à utiliser un Memory Patcher, comme nous le verrons à la fin de cette bafouille.

Voyons quand même un peu la suite.
Qui dit Userkey, UserName, dit souvent base de registre. Qu'en est-il de ce coté, sachant qu'il y a 8 adresses qui se rapportent à ces Strings, ce qui permet d'émettre l'hypothèse que bon ou mauvais, le Name et le serial entré sont mémorisé quelque part ?

[HKEY_LOCAL_MACHINE\Software\APIS32]
"
UserKey"=hex(0):45,30,34,42,31,39,36,44,2d,35,30,37,44,36,43,35,39
"
UserName"=hex(0):63,68,72,69,73,74,61,6c

Première info qui va peut être pouvoir nous aider, le Name et le Serial sont sous leurs formes Hexa/Ascii : 63h est la correspondance ASCII du " c "...

* Possible StringData Ref from Data 0bj ->"UserName"
I
:004046CD       push 004095F8
:004046D2       cal1 004049DO
:004046D7       add esp, 0000000C
:004046DA       cmp eax, 00000005     > Name de 5 caractères ?
:004046DD       jge 004046E7          > Good Boy continue
:004046DF       xor eax, eax          > Bad boy est Xoré

Vous êtes au tout début de la routine de vérification du serial entré. Pour vous éviter une indigestion de Dead Listing, je ne vais vous donner que les lignes les plus représentatives de cette routine :

De 004046E7 à 00404730 le programme va déplacer le serial dans un autre espace mémoire. Vous aurez donc le serial entré en [0040C6B4] et en [0040C6A0]

:0040473A       mov esi, 0040C6A1     > 2d caractère du serial
:0040473F       mov edi, 0040C6B4     > 1er caractère du serial
:00404744       push edi              > serial sauvé sur la pile
:00404745       call 00404930         > visite obligatoire
:00404930 mov ecx, dword ptr [esp+04] > pointe sur [0040C6B4]
:00404934 mov al, byte ptr [ecx]      > 1er caractère du serial 
:00404936 cmp al, 39                  > est-ce un chiffre (<9) ?
:00404938 jle 0040493E                > si oui, saute    
:0040493A add al, C9                  > ajoute 201d (conversion)
:0040493C jmp 00404940                > saute
:0040493E add al, D0                  > ajoute 208d (conversion)
:00404940 mov cl, byte ptr [ecx+01]   > second caractère du serial
:00404943 cmp cl, 39                  > est-ce un chiffre (<9) ?
:00404946 jle 00404951                > si oui, saute
:00404948 shl al, 04                  > décalage d'un octet sur la gauche
:0040494B sub cl, 30                  > déduit 30 à CL                
:0040494E or al, cl                   > AH !
:00404950 ret

Dans ce Call, vous avez une manipulation de votre serial sur le 1er (0040C6B4) et le second caractère (0040C6A1) de celui ci.
Le sous-registre AL va recevoir la valeur hexa/ASCII de [0040C6B4], par exemple " 34 " si votre premier caractère est un " 4 ". Le Add Al,D0 va rajouter 208d à cette valeur ASCII " 34 " qui va se " convertir " en un " 4 " (le chiffre hexadécimal). Le sous-registre CL (la partie basse de ECX) va prendre quant à lui la valeur de [0040C6A1] que le programme va " convertir " en l'équivalent hexadécimal de sa valeur ASCII.

AL va être décalé d'un octet sur la gauche par le shl al,04 et forcer le registre EAX à prendre la valeur 40.
Puis Al va être ORé avec CL. On pourrait résumer cette routine par :

Al= 1er caractère du serial (par exemple 4)
Cl= 2d caractère du serial (par exemple 8)

(Al x 10) OR Cl Soit (4 x 10) + 8 = 48
-> le résultat est dans EAX

Le OR Al, CL va être la première manipulation intéressante de notre serial. A la sortie de cette routine, CL est remis à zéro par BL qui joue le rôle de compteur, puis CL prend la valeur 50h, et EDI va pointer sur le 3ème caractère du serial entré:

:0040474A       mov cl, bl
:0040474C       add esp, 00000004
:0040474F       add cl, 50
:00404752       add edi, 00000002

Nouvelle manipulation, le résultat obtenu est XORé avec 50h :

:00404755       xor al, cl                > 50 - 48  -> Al = 18
:00404757       inc bl                    > le compteur est incrémenté
:00404759       mov byte ptr [esi-01], al > le résultat est mémorisé
:0040475C       mov byte ptr [esi], 00    > et NULL Terminated
:0040475F       inc esi                   > pointe sur le caractère suivant
:00404760       cmp bl, 08                > 8 caractères ont été traités ?
:00404763       jb 00404744               > non -> boucle

Cette opération s'effectue uniquement sur 8 caractères, ce qui, avec le test d'un serial (en 004046B9) ayant une longueur de 11h caractères (17d), laisse à supposer que celui ci est composé de deux parties de 8 caractères chacune, probablement séparées par un " - "

Une fois le serial traité une première fois (vous obtiendrez au final une chaîne du type
18 C2 67 83 84 85 86 87). Le traitement suivant va se faire dans la foulée sur la chaîne crée, et sur ce principe :

EAX vaut 1 à l'entrée de cette routine.

:0040498C       mov dl, byte ptr [edi+esi] > 1er chaine crée (18 C2 67...)
:0040498F       mov edi, edx               > EDI =  DL = 1ere valeur
:00404991       mov edx, edi               > EDX = EDI = par ex:18h
:00404993       imul eax, edx              > EAX x EDX 
:00404996       cmp eax, 00008899          > EAX >= 8899h
:0040499B       jle 004049A7               > Non -> saute
:0040499D       cdq                        > Oui
:0040499E       mov ebx, 00008899          > EBX = 8899h
:004049A3       idiv ebx                   > EAX/8899h reste dans EDX
:004049A5       mov eax, edx               > EAX = reste de la division
:004049A7       mov edx, dword ptr [esp+14]> nb de valeurs à traiter (8)
:004049AB       dec edx                    > décrémentée
:004049AC       mov dword ptr [esp+14],edx > et remémorisée
:004049B0       jne 00404991               > si EDX >0 -> loop
:004049B2       cdq                        > EAX < 8899
:004049B3       mov edi, 000000BB          > EDI = BBh
:004049B8       idiv edi                   > EAX/BBh reste dans EDX
:004049BA       inc ecx                    > incrémente compteur 
:004049BB       cmp ecx, 00000008          > 8 valeurs traitées ?
:004049BE       mov byte ptr [esi], dl     > crée nouvelle chaîne (1D B6...)
:004049C0       mov byte ptr [ecx+ebp], 00 > NULL terminated
:004049C4       jl 00404976                > loop s'il reste des valeurs
:004049C6       pop edi
:004049C7       pop esi
:004049C8       pop ebp
:004049C9       pop ebx
:004049CA       ret

A la sortie de cette routine, une nouvelle chaîne a été crée, du type 1D B6 89 6D 37 17 A1 98.

C'est ensuite au tour du Name entré d'être bidouillé :

:00404774       mov edi, 0040CF00         > Name entré

:00404797       mov ecx, edx              > ECX = Name+1 (nb caractères du Name+1)

:004047AA       dec ecx                   > Nb caractère du Name
:004047AB       cmp cl, 08                > seul 8 premiers caractères traités
|
:004047EB       mov ecx, 0040C6B4         > ECX = 2d chaîne crée
:004047F0       mov esi, 00000008         > 8 valeurs à traiter dans cette chaîne
:004047F5       mov al, byte ptr [ecx]    > AL = 1ere valeur de la chaîne

:00404802       mov dl, byte ptr [ecx+0A] > DL = 1ère lettre du Name

:00404813       xor edx, eax              > DL XOR AL
:00404815       add ebp, edx              > résultat mémorisé dans EBP

C'est en 00404813 que tout ce joue. Les caractères du Name et les valeurs de la seconde chaîne sont manipulés par le XOR. Il faut, pour que votre serial soit valide, que les valeurs de la seconde chaîne générée soient identiques aux caractères composant votre Name (ce qui peut paraître surprenant dans la mesure ou le Name doit comprendre au moins 5 caractères). Bref, la seconde chaîne recrée votre Name !
EBP reçoit le résultat du Xor entre les caractères composant le Name et la seconde chaîne qui a été crée à partir de la première, elle-même crée à partir du serial entré. EBP est donc sensé être égal à zéro si votre serial est valide. Le TEST EBP,EBP va alors modifier le Zéro Flag qui par le SETE AL suivant va placer la valeur 01 dans le registre EAX.

:0040481E       test ebp, ebp
:00404820       pop esi
:00404821       pop ebp
:00404822       sete al
:00404825       pop ebx
:00404826       pop ecx
:00404827       ret

KeyGener API Spy 32:


Un keyMaker pourrait avoir cette allure :

;========================================================================= 
;                            KeyGenerator 
;        la bonne clé se trouve dans EDX à la fin de la routine   
;=========================================================================  	
pushad
        mov     [Key], 0                      ; mise à 0 de Key pour serials multiples
        mov     dword ptr [Name_Length],eax   ; lg Name récupérée par GetDlgItemtext
        mov     edx, offset Name_Length       ; mis dans edx
        movzx   ebx, byte ptr [edx+1]         ; ebx = Name+1
        cmp     eax, 05                       ; 5 caractères mini pour le name
        jl      Trop_court                    ; affichage "Name trop court"
        cmp     ebx, 8                        ; Name+1 > 8 ?
        jnl     no_movsb                      ; pas de déplacement
        lea     edi, Key                      ; charge adresse Key
        lea     esi, _Name                    ; charge adresse Name
        mov     ecx, 8                        ; ecx = taille du cache
        sub     ecx, ebx                      ; ecx = ecx - ebx (name+1)
        rep     movsb                         ; déplace Name ->Key de ECX caractères
        mov     dword ptr [edi],0             ; et termine la chaine par des 0000
        mov     dword ptr [edi+4],0           ; pour permettre serials multiples
        
no_movsb:
        xor     ecx, ecx                      ; compteur nb de caractère                  

loop1:
        call    generator                     ; Key2 va recevoir une valeur de la chaîne
        inc     ecx                           ; caractère suivant
        cmp     ecx, 8                        ; tous traités?
        jl      loop1                         ; non -> loop
        lea     edi, Key2                     ; oui -> charge adresse chaîne généré
        xor     ebx,ebx                       ; compteur caractères du serial à créer 
loop2:
        movzx   eax, byte ptr [edi+ebx]       ; 1 valeur de la chaîne dans EAX
        mov     cl, bl                        ; compteur dans CL
        add     cl, 50h                       ; ajoute 50h
        xor     al, cl                        ; valeur chaîne XOR cl
        mov     [edi+ebx], al                 ; stocké dans Key2
        call    ALtoAX                        ; et converti en chiffre
        shl     bx, 1                         
        mov     word ptr [ebx+Serial], ax     ; puis stocké dans @ Serial   
        shr     bx, 1
        inc     bl                            ; incrémente compteur
        cmp     bl, 8                         ; les 8 caractères sont générés?
        jb      loop2
        lea     edi, Key2                     ; EDI = @ où va être placé le serial 
        lea     esi, Serial+8                 ; ESI pointe sur adresse serial+8
        mov     cx, 8                         ; nb de caractères à déplacer
        rep     movsb                         ; déplacement
        lea     esi, Key2                     ; ESI = @ où va être placé le serial 
        lea     edi, Serial+9                 ; EDI pointe sur adresse serial+9
        mov     cx, 8                         ; 8 caractères à déplacer
        rep     movsb                         ; déplacement
        mov     byte ptr [Serial+8], 2dh      ; rajoute le tiret (-)
        mov     byte ptr [Serial+11h], 0      ; NULL Terminated String
  
        mov     edx, offset Serial            ; récupère le serial dans EDX

jmp 	Affiche 

; ----------------------------------------------------------------------------
; Le programme va tourner jusqu'à ce qu'une valeur corresponde 
; au caractère du Name en cours et la mémoriser dans Key2
; ----------------------------------------------------------------------------

generator proc
loop3:
        lea     esi, [Key2]           ; Key2 = Chaîne à créer
        xor     edx, edx              ; coef = 0
        mov     eax, 1                ; mémoire = 1
        mov     byte ptr counter, 7   ; compteur = 7
        mov     dl, [ecx+esi]         ; ECX = nb caractères
        mov     edi, edx              ; sauve coef dans EDI

loop5:
        mov     edx, edi              ; récupère coef
        imul    eax, edx              ; mémoire x coef
        cmp     eax, 8899h            ; le résultat dépasse 8899?
        jle     loop4                 ; va en caractère suivant
        cdq                           ; sinon
        mov     ebx, 8899h            ; ebx = 8899
        idiv    ebx                   ; mémoire/coef
        mov     eax, edx              ; reste dans EAX

loop4:
        dec     counter               ; les 8 boucles sont passés? 
        jnz     loop5                 ; non -> loop
        cdq
        mov     edi, 0BBh             ; EDI = BB
        idiv    edi                   ; EAX/EDI 
        cmp     byte ptr [ecx+Key],dl ; compare si le reste = caractère du Name
        jz      exit_proc             ; si egale alors c'est Good -> bye bye
        inc     byte ptr [Key2+ecx]   ; caractère suivant. Incrémente chaîne à créer
        jmp     loop3

exit_proc:
                 ret
generator endp


Patcher pETITE 1.2:

Du plus simple au plus compliqué, commençons avec un Memory Patcher.
R!SC Process Patcher, via un script, va permettre de créer un petit loader qui se chargera de lancer l'application, et de chercher les adresses à modifier. Comme la cible est compressée, il faudra lui laisser un peu de temps pour qu'il puisse effectuer la modification attendue :

T=30000:                  ; 30000 milisecondes
F=apis32.exe:             ; nom du programme à lancer  
P=4046BE/33,C0/B0,01:     ; modification à apporter à l'adresse 004046BE
$                         ; fin du script

Un peu plus compliqué, il y a le Hard Patching de l'application.
Dans la mesure où le passage de relais de pETITE n'est pas lisible dès l'EntryPoint, il va falloir agir en deux temps :

- Coincer une adresse " clean " dès l'Entry Point, un peu après que le passage de relais sera devenu accessible à son tour (donc après l'adresse 0041A10D). Les codes à cette adresse devront pouvoir être remplacés par les 5 octets nécessaires à un JMP long sans perturber le programme. Ce JUMP devra permettre de se rendre à l'adresse d'un premier patch.

- Modifier avec le patch1 le JMP OEP, avant que pETITE ne passe le relais à API Spy 32, pour forcer pETITE à se rendre à un Patch2 qui modifiera API Spy à l'adresse souhaitée (et désormais décompressée).

1- Trouver une adresse

En partant de 0041A10D, et en traçant un peu, vous verrez un test dword ptr [EDX], 80000000 qui devrait parfaitement convenir :

.0041A10D: 833A00                       cmp       d,[edx],000 
.0041A110: 0F84A7140000                 je       .00041B5BD   
.0041A116: F70200000080                 test      d,[edx],080000000 
.0041A11C: 741B                         je       .00041A139   


2- Trouver de la place pour les Patchs

Il existe de nombreuses variantes pour trouver de la place. L'une d'entre elles consiste à squatter un espace en fin d'une section, là où pour une question d'alignement sur un multiple du CODE, le compilateur a complété cette section par du PADDING. Le problème avec un (bon) compresseur, c'est qu'il va justement chercher à supprimer au maximum les 0000 qui ne servent à rien.

Une autre solution consiste à prendre le risque de modifier la section .rsrc, là où le programme va stocker ses icônes. Dans la mesure ou nos patchs vont être tout petit, l'altération éventuelle de ces icônes est en général invisible. Il faudra par contre s'assurer que la décompression du programme ne viendra pas interférer avec notre patch. Un BPR W (Write) sur les adresses convoitées permettra de s'assurer que rien ne viendra écraser les zolis codes que l'on va y placer.

Good ! je vais m'installer en 00411F90.

3- Détourner pETITE vers le patch 1

.0041A116: E9757EFFFF                 jmp 00411F90   > aiguillage
.0041A11B: 90                         nop            > équilibrage

4- 1er patch

Notre cible est celle ci :

:0041B5CF  61           POPAD     
:0041B5D0  669D         POPF
:0041B5D2  E9E99FFEFF   JMP       004055C0           > devra renvoyer vers patch 2

Et pour cela notre patch 1 va modifier le JMP en 0041B5D2. Je vous rappelle que les octets doivent s'écrire à l'envers.

:00411F90  C705D3B54100D569FFFF MOV       DWORD PTR [0041B5D3],FFFF69D5
:00411F9A  C605D2B54100E9       MOV       BYTE PTR [0041B5D2],E9
:00411FA1  F70200000080         TEST      DWORD PTR [EDX],80000000
:00411FA7  E96F810000           JMP       0041A11B

Le programme va remplacer le JMP 004055C0 par un JMP 00411FAC, soit remplacer E9E99FFEFF par E9D569FFFF
Puis les codes que notre JMP Aiguillage a écrasé (en 0041A116) vont être réécrit avant de renvoyer le programme continuer sa route comme si de rien n'était.


5- 2ème Patch

pETITE va donc continuer à décompresser API Spy 32, et arrivé en 0041B5D2, il va se rendre à l'adresse du Patch 2 :

:00411FAC  66C705BE464000B001  MOV       WORD PTR [004046BE],01B0
:00411FB5  E90636FFFF          JMP       004055C0

Qui va modifier le Xor Eax,Eax en 004046BE, pour le transformer en un Mov AL,01 avant de se rendre à l'Original Entry Point de la cible.


It's Cracked !