Reverse de PE SniFFer 1.06

par chopun

Avant de commencer cet essai, quelques precisions s'imposes: notre cible est donc PESniffer, ... pourquoi? et bien premierement je trouve assez penible cette habitude que ce programme (bien pratique!) a de me demander si je desire vraiment quitter, a croire qu'il me prend pour un ane! et en second lieu puisque je trouve que cette gestion d'un fichier log du nom de 'output.txt' est inutile je m'en vais la virer.

Ce texte s'adresse avant tout  aux reverseurs debutants,  mais necessite tout de meme un minimum de pratique et de connaissances, ceci etant dit on peut passer aux rejouissances:


I) MANUAL DUMPING

On commence par desassembler PESniff avec WinDasm et on s'apercoit tout de suite que le programme est compacte ( car RVA > Size ) et que l'on obtient pas de listing. On a cependant ceci:

Number of Objects = 0009 (dec), Imagebase = 00400000h

Object01: CODE   RVA: 00001000 Offset: 00000400 Size: 0001D000 Flags: C0000040
Object02: DATA   RVA: 00046000 Offset: 0001D400 Size: 00000600 Flags: C0000040
Object03: BSS    RVA: 00047000 Offset: 0001DA00 Size: 00000000 Flags: C0000040
Object04: .idata RVA: 00048000 Offset: 0001DA00 Size: 00000E00 Flags: C0000040
Object05: .tls   RVA: 0004A000 Offset: 0001E800 Size: 00000000 Flags: C0000040
Object06: .rdata RVA: 0004B000 Offset: 0001E800 Size: 00000200 Flags: C0000040
Object07: .reloc RVA: 0004C000 Offset: 0001EA00 Size: 00000000 Flags: C0000040
Object08: .rsrc  RVA: 00051000 Offset: 0001EA00 Size: 00002C00 Flags: C0000040
Object09: .Sky-M RVA: 00059000 Offset: 00021600 Size: 00001200 Flags: C0000040

Program Entry Point = 0041DDFB (pesnif106.exe File Offset:0001DDFB)

D'ailleurs si on utilise PESniff sur notre copie de PESniff on apprend qu'il est packe avec ASPack.. Donc  premiere chose a faire si on veut avoir un listing de l'exe ou si on veut utiliser le symbol loader, il faut un peu corriger le pe header et apres avoir constate que [Program_Entry_Point - ImageBase] etait compris entre l'Offset de la section CODE et celui de la section DATA, on peut se dire que l'EntryPoint du programme se situe dans la section CODE, on va donc changer le flag de cette section par E0000020 avec ProcDump (ce flag contrairement a C0000040 signifie qu'il y a du code). Ce qui donne ceci:

Object01: CODE RVA: 00001000 Offset: 00000400 Size: 0001D000 Flags: E0000020

on peut maintenant utiliser symbol loader, et on peut tracer le programme, c'est tres simple:

start proc near

    nop                   ; Entry Point
    jnz short [eip+2]
    jmp loc_459000        ; vers decompression

start endp

loc_459000:
    pusha                 ; on s'attend a voir un popa en fin de decompression
    call [eip+5]
    pop ebp
    sub ebp, 43D93Eh
    mov eax, 43D938h
    add eax, ebp
    sub eax, [ebp+43DE0Bh]
    mov [ebp+43DE17h], eax
    cmp byte ptr [ebp+43DE01h], 0
    jnz short loc_45903E
    inc byte ptr [ebp+43DE01h]
    call sub_459051
    call sub_4592B2
    call sub_459350

loc_45903E:
    mov eax, [ebp+43DE03h]
    add eax, [ebp+43DE17h]
    mov [esp+1Ch], eax
    popa                 ; restauration des registres: la decompression est terminee
    jmp eax              ; donne la main au programme depacke

donc arrive au jmp eax on fait ? eax et SICE nous donne ceci:

00445348 0004477768 "DSH"

L'entry point de notre futur dump est donc 45348 (445348-ImageBase), maintenant on tape r eax eip (equivalent a un mov eax, eip) et on appuie sur F5 pour lancer le programme. Rien ne se passe et c'est normal car on la glace juste avant que le programme depacke ne s'execute, on lance donc ProcDump, on choisit PESniff.exe et Dump (full), on oublie pas de killer PESniff (car il est bloque) ensuite on fait edit pe avec notre dump et on change l'entry point avec celle que l'on a calculee, et voila on a un beau dump! Enfin pas si beau que ca car le probleme avec ASPack c'est qu'il detruit l'Import Table donc sous WinDasm on n'a aucune references aux apis utilisee. Mais ce n'est pas si grave que ca car on toujours les Strings Reference et puis on peut mettre en parallele les informations de SICE avec celles de WinDasm.

 

II) VIRER LA CONFIRMATION

donc la premiere chose qu'on va modifier c'est la confirmation de sortie, on va voir les strings data  references et finalement on a ceci:

* Possible StringData Ref from Code Obj ->"Would you really want to quit "
|                                        ->"PE-Sniffer ?"
|
:0043D81B B848D84300 mov eax, 0043D848
:0043D820 E8175AFFFF call 0043323C      ; Affiche message
:0043D825 48         dec eax
:0043D826 750D       jne 0043D835       ; jmp annuler if eax=1

et ca aussi:

* Possible StringData Ref from Code Obj ->"Would you really want to quit "
|                                        ->"PE-Sniffer ?"
|
:0044415F B89C414400 mov eax, 0044419C
:00444164 E8D3F0FEFF call 0043323C      ; Affiche message
:00444169 48         dec eax
:0044416A 750E       jne 0044417A       ; jmp annuler if eax=1

on a donc deux fois la meme routine, (la premiere etant celle qui correspond au bouton Exit) pour cracker ca il suffit de modifier les 'call 0043323C' par des 'mov eax, 1' et ainsi les branchements ne se feront pas donc on quitteras directement et pas de confirmation..


III) VIRER LA GESTION DU FICHIER 'output.log'

on va encore une fois faire appel aux strings data references, on recherche cette fois les dernieres lignes que le programme est succeptible d'ecrire dans le output.txt et on trouve facilement ca:

"Encryptor : NONE"
"Encryptor : PE-Crypt v1.02"
"Encryptor : PESHiELD"
"Encryptor : Phrozen Crew PE enCryptor "

on va donc aller voir par exemple ce qu'il fait avec "Encryptor : NONE" :

* Possible StringData Ref from Code Obj ->"Compressor : Shrinker v3.4"
|
:0043E57B BAC4E84300 mov edx, 0043E8C4
:0043E580 B888774400 mov eax, 00447788
:0043E585 E8C656FCFF call 00403C50
:0043E58A E8E467FCFF call 00404D73
:0043E58F E89841FCFF call 0040272C

* Possible StringData Ref from Code Obj ->"Encryptor : NONE"
|
:0043E594 BAE8E84300 mov edx, 0043E8E8
:0043E599 B888774400 mov eax, 00447788
:0043E59E E8AD56FCFF call 00403C50
:0043E5A3 E8CB67FCFF call 00404D73
:0043E5A8 E87F41FCFF call 0040272C

vous avez bien remarque les trois call, comme d'habitude : vive le copier coller (dans ce cas c'est plutot vive les languages merdiques!)

explications: pour chaques lignes de texte succeptible d'etre ecrite dans output.txt on appelle 3 routines, et si on met un break sur la seconde (avec WinDasm), on remarque que le output.txt a ete ecrit...
donc la routine qui ecrit dans l'output.txt est en 403C50.

on va voir:

* Referenced by a CALL at Addresses:
|:0043DB8A , :0043DBA3 , :0043DBB9 , :0043DBFD , :0043DC16
|:0043DC2C , :0043DC70 , :0043DC89 , :0043DC9F , :0043DCB8
|:0043DCD1 , :0043DCEA , :0043DD00 , :0043DD19 , :0043DD32
|:0043DD4B , :0043DD61 , :0043E3F6 , :0043E40F , :0043E425
|:0043E469 , :0043E482 , :0043E498 , :0043E4DC , :0043E4F5
...snip...snip...snip...snip...snip...snip...snip...snip..
|:004429CD , :004429E3 , :004429FC , :00442A15 , :00442A2E
|:00442A44 , :00442A5D , :00442A76 , :00442A8F , :00442AA5
|:00443316 , :0044332F , :00443345 , :00443389 , :004433A2
|:004433B8 , :004433FC , :00443415 , :0044342B , :00443444
|:0044345D , :00443476 , :0044348C , :004434A5 , :004434BE
|:004434D7 , :004434ED
|
:00403C50 31C9       xor ecx, ecx
:00403C52 E901000000 jmp 00403C58
:00403C57 C3         ret

bon alors la, ma solution c'est de remplacer le xor ecx, ecx par un ret et a partir de maintenant on peut vraiment dire que ce satane fichier ne sert VRAIMENT a rien!

il n'empeche que ce fichier est tout de meme cree au demarage du prog, on va fixer ca et avec un bpx CreateFileA, on arrive ici:

:0040488B 6A00       push 00000000
:0040488D 6880000000 push 00000080
:00404892 51         push ecx                     ; OPEN_ALWAYS
:00404893 6A00       push 00000000
:00404895 52         push edx
:00404896 50         push eax
:00404897 8D4648     lea eax, dword ptr [esi+48]
:0040489A 50         push eax                     ; output.txt
:0040489B E83CC9FFFF Call kernel32.CreateFileA

etant donne que quand le fichier n'existe pas alors il est cree, on en deduit le sixieme parametre:  OPEN_ALWAYS (voir win32 Programmer's Ref) nous on va preferer OPEN_EXISTING qui ne cree pas de fichier s'il n'en existe pas.

on sait egalement que OPEN_ALWAYS=02h or quelques lignes plus haut dans le listing on trouve :

:00404856 B902000000 mov ecx, 00000002

on ne peut pas faire plus explicite et puisque OPEN_EXISTING=03h on va mettre mov ecx, 3 a la place...

il y a juste un tout petit probleme: quand on lance l'exe une message box nous informe: 'fichier introuvable' donc en une seconde (apres avoir pris le temps de faire un bpx sous sice) on se retrouve ici:

:0042C569 E8EE94FDFF Call user32.MessageBoxA

on va voir plus haut dans le listing et on trouve ca:

* Referenced by a CALL at Addresses:
|:0042C639 , :00436FB7
|
:0042C530 55 push ebp

encore une fois on remplace le push ebp par un ret et tout est ok!



IV) HARD PATCHING

on va donc maintenant patcher le fichier original avec nos modifications, pour cela on va modifier le loader d'aspack pour qu'une fois sa tache terminee (fin de decompression) il ne branche pas vers l'entry point mais vers notre routine qui va patcher le programme puis lui passer la main.. on s'amuse quoi!

a ce moment on a un probleme car il sagit de trouver de la place dans l'exe et ce n'est pas evident car les endroits seins sont ecrasees en cours de decompressions..
la solution que j'ai retenue est donc: puisqu'on ne trouve pas de place on en fait, on s'interesse a la section .Sky-M qui a une RawSize de 1200h on peut grapiller un peu de place par exemple en changeant la valeur de la RawSize de 1200h a 1264h (avec ProcDump) on a alors un gain de 100 octets ce qui est largement suffisant dans notre cas. Pour finaliser cette operation il faut utiliser un editeur hexa sur notre cible et ajouter 100 octets a la fin. voila on a donc de la place a partir de l'addresse 45A1F7 pour implanter tranquillement notre patch.

revoyons la fin du loader (OFFSET 21634h):

459034: E879020000   call 0004592B2
459039: E812030000   call 000459350
45903E: 8B8503DE4300 mov eax,[ebp+00043DE03]   ;\
459044: 038517DE4300 add eax,[ebp+00043DE17]   ; -calcul de l'ep
45904A: 8944241C     mov [esp+0001C],eax       ;/
45904E: 61           popad                     ; restauration des registres
45904F: FFE0         jmp eax

a partir de 45903E on peut passer la main a notre routine qui va patcher le programme et on pourra ensuite passer la main au programme, puisque de toute facons on connait l'EP, on modifie donc comme ca:

459034: E879020000   call 0004592B2
459039: E812030000   call 000459350
45903E: 61           popad                     ; restauration des registres..
45903F: E9B3110000   jmp 00045A1F7             ; vers notre patch
459044: 038517DE4300 add eax,[ebp+00043DE17]
45904A: 8944241C     mov [esp+0001C],eax
45904E: 61           popad
45904F: FFE0         jmp eax

il ne nous reste plus qu'a implanter notre patch en 45A1F7 (OFFSET 227F7h)

45A1F7: C70520D84300B8010000 mov dword ptr [0043D820],000001B8  ;\--place mov eax,1 en 43D820
45A201: 66C70524D843000048   mov  word ptr [0043D824],4800      ;/
45A20A: C70564414400B8010000 mov dword ptr [00444164],000001B8  ;\--place mov eax,1 en 444164
45A214: 66C705684144000048   mov  word ptr [00444168],4800      ;/
45A21D: 66C705503C4000C300   mov  word ptr [00403C50],00C3      ;place un ret en 403C50
45A226: C70556484000B9030000 mov dword ptr [00404856],000003B9  ;\--place mov ecx,3 en 404856
45A230: 66C7056048400000EB   mov  word ptr [00404860],EB00      ;/
45A239: 66C70530C54200C300   mov  word ptr [0042C530],00C3      ;place un ret en 42C530
45A242: E901B1FEFF           jmp   00445348                     ;saut vers EP

remarque: les instructions mov eax,1 et mov ecx,3 ont ete patche dans le fichier en deux fois car ce sont des instructions codees sur 5 octets or un dword est egal a 4 octets.. vous devriez egalement etre familie au fait d'ecrire a l'envers en asm car pour information mov eax,1 se traduit en fait par B801000000 quand au 48 qui est ecrit en 45A201, c'est un octet qui etait deja present dans le prog d'origine mais comme il falait encore que j'ecrive 00 et que je devais ecrire deux octets alors j'ai ecris 0048..

Voila c'est termine, j'espere que vous aurez au moins appris quelque chose. Je sais que ce n'est pas parfait et pour tout vous dire j'avais l'intention au debut de modifier le bouton 'Exit' par un bouton qui lancait tout les scans les uns apres les autres.

Aussi si un meilleur que moi veut me donner une lecon.. ou si quelqu'un a des commentaires qu'il n'hesite pas.

Il ne me reste plus qu'a remercier mes mentors: TiTi, dxp, Iczelion, +gthorne, tKC, +Frog's Print, et plus recement Christal, TeeJI et toute la french team en general.. Big kisses egalement a chopine et a m@dm@x.

chopun@lawyer.com

ps: je suis sur que SkymarShall ne m'en voudras pas!