| I_Introduction |
Christal m'a demandé de refaire ma copie sur le dump de pleditor
qui n'était, il est vrai, pas très expliqué. Mais
cette fois il m'a demandé si je pouvais le faire sur un autre programme,
histoire de vérifier si elle marche toujours. Donc cette essai visera
à expliquer comment obtenir un dump fonctionnel d'un programme protégé
avec asprotect 1.0 (ou plus). En fait il y a plusieurs versions d'asprotect
et les deux progs présentés ici n'utilisent pas la même
version (enfin c'est quasiment la même). Je vais en profiter pour
parler un peu plus d'asprotect lui-même. TeeJi m'ayant proposé
son aide il vous expliquera une partie du fonctionnement d'asprotect (voir
son essai). Enfin trois méthodes de dump seront présentés
ici: tout d'abord celle que j'ai utilisé sur pleditor mais revu
et corrigé et celle mise au point sur commview par Tsehp . Une autre
méthode (enfin deux pour être exact) expliquera un moyen de
reconstruire une table d'import.
En fin de ce tutoriel je vous indiquerait des tuts à lire qui
pourrait vous aider à mieux comprendre (certain sur aspr et d'autre
sur cdilla et d'autres sur le format pe).
| II_Tools |
Softice 4.01
Icedump 6.01
Procdump 1.6.2 (ou Procinfos)
Ida 4.04 (pas nécessaire)
Winhex 9.4 (ou autre)
Pe-rebuilder (ou un autre rebuilder)
| III_1ère méthode: pleditor |
Dans le dossier sur Aspack/Asprotect j'avais proposé une méthode pour obtenir un dump fonctionnel de PerL Editor 3.0 build 15.06.2000. J'avais dit que cette méthode ne marchait pas mais en fait si. Je vais la refaire ici mais d'une autre facon. De plus je vais essayer le l'expliqué plus clairement.
Asprotect qu'est-ce que c'est? et bien c'est une protection. Elle permet
de mettre sur son programme des limitations (temps, utilisation ...), de
mettre des anti-debuggers, de mettre du code polymorphe ou overlaping du
code,de multi compressé/cryptés le prog et d'empêcher
le dump du programme. Il y a également d'autres fonctions mais elles
ne nous préoccupe pas ici (on peut notamment cryptés certaine
partie du prog qui se décrytpe avec une clef).
Comment peut-on s'en sortir? et bien c'est plus ou moins simple. Une
fois que l'on sait faire ca devient simple :).
Bon tout d'abord qu'est ce qui empêche le dump du programme?
et bien en fait asprotect ne recré pas une table d'import et une
iat (import address table) correct lors de la décompression en mémoire
du programme (une partie de ce tut est consacré à ceci).
1) Récolte d'informations et dump.
Bon tout d'abord on va regarder les sections de pleditor. Ceci v nous permettre de savoir où asprotect devrait normallement mettre la table d'import du programme. Donc on prend Procdump et on va dans Pe-Editor et sections et on a ceci :
Name: CODE VS: 000B3000 VA:
00001000 RS: 00042800 RA: 00000400 C: C0000040
Name: DATA VS: 00002000 VA:
000B4000 RS: 00000C00 RA: 00042C00 C: C0000040
Name: BSS VS: 00002000
VA: 000B6000 RS: 00000000 RA: 00043800 C: C0000040
Name: .idata VS: 00003000 VA: 000B8000
RS: 00000200 RA: 00043800 C: C0000040
Name: .tls VS: 00001000 VA:
000BB000 RS: 00000000 RA: 00043A00 C: C0000040
Name: .rdata VS: 00001000 VA: 000BC000
RS: 00000200 RA: 00043A00 C: C0000040
Name: .reloc VS: 0000D000 VA: 000BD000
RS: 00000000 RA: 00043C00 C: C0000040
Name: .rsrc VS: 00036000 VA: 000CA000
RS: 0000A200 RA: 00043C00 C: C0000040
Name: .data VS: 00015000 VA: 00100000
RS: 00014800 RA: 0004DE00 C: C0000040
Name: .data VS: 00001000 VA: 00115000
RS: 00000000 RA: 00062600 C: C0000040
On trouve une section .idata et c'est ici que doit se trouvé une table d'import (pour les exe avec sections .idata). Donc l'import address table correspond au virtual offset de la section .idata (b80000). Donc on sait qu'il va mettre sa table d'import en 4b8000 (virtual address + image base). Il faudra donc mettre un point d'arrêt quand il écrira dans cette partie : bpm 4b8000 w .
Maintenant que l'on sait où asprotect va mettre la table d'import on peut commencer à tracer le programme. Le point d'entrée du programme est en 500001, c'est la que se trouve la première partie d'asprotect (et oui il y a une multi-compression). Mais cette version 1.0 d'asprotect permet de décompressé ces parties en mémoires à des addresses différentes. Il va donc falloir enlever cela (thx sword). Pour ce faire on pose un bpx gettickcount et on lance le programme. Softice apparait et on appuie sur F12 pour revenir dans le code du prog, on doit normallement arriver là :
015F:00500223 C3
RET
015F:00500224 25FFFF0100
AND EAX,0001FFFF
<-- on arrive là
015F:00500229 EB04
JMP 0050022F
015F:0050022B E8EB04E9EB
CALL EC39071B
015F:00500230 FB
STI
015F:00500231 E950EB04E8
JMP E854ED86
015F:00500236 EB04
JMP 0050023C
015F:00500238 E9EBFBE9E8
JMP E939FE28
015F:0050023D 0300
ADD EAX,[EAX]
015F:0050023F 0000
ADD [EAX],AL
Donc pour ce débarraser du changement d'adresse il faut mettre eax à 0 (ou autre chose mais tjs pareil), ensuite on trouve la fin de la première zone en 500c28 :
015F:00500C17 E8EB04E9EB
CALL EC391107
015F:00500C1C FB
STI
015F:00500C1D E95BEB04E8
JMP E854F77D
015F:00500C22 EB04
JMP 00500C28
015F:00500C24 E9EBFBE9C3
JMP C43A0814
015F:00500C28 C3
RET
<-- fin de la 1ère zone
015F:00500C29 B0DD
MOV AL,DD
015F:00500C2B 54
PUSH ESP
015F:00500C2C 004765
ADD [EDI+65],AL
015F:00500C2F 7454
JZ 00500C85
Et là il passe à la deuxième zone qu'il a décompressé en mémoire. On arrive là :
015F:0056407C 90
NOP
015F:0056407D 60
PUSHAD
<-- on arrive là
015F:0056407E E844060000
CALL 005646C7
015F:00564083 EB44
JMP 005640C9
015F:00564085 0000
ADD [EAX],AL
015F:00564087 0000
ADD [EAX],AL
015F:00564089 0000
ADD [EAX],AL
015F:0056408B 0000
ADD [EAX],AL
015F:0056408D 87DB
XCHG EBX,EBX
015F:0056408F 90
NOP
Puis il faut tracer jusqu'à la fin de cette deuxième zone. Pour y arriver plus rapidement on peut poser un bpm eip+5bb x . On arrive ici:
015F:00564632 0385A0304400
ADD EAX,[EBP+004430A0]
015F:00564638 5B
POP EBX
<-- on tombe là
015F:00564639 0BDB
OR EBX,EBX
015F:0056463B 8985D92E4400
MOV [EBP+00442ED9],EAX
015F:00564641 61
POPAD
015F:00564642 7508
JNZ 0056464C
015F:00564644 B801000000
MOV EAX,00000001
015F:00564649 C20C00
RET 000C
015F:0056464C 68B8665500
PUSH 005566B8
015F:00564651 C3
RET
<-- fin 2ème zone
Donc en 564651 il va vers la troisième zone mémoire. On y va nous aussi :
015F:005566B5 8D4000
LEA EAX,[EAX+00]
015F:005566B8 55
PUSH EBP
<-- on est là
015F:005566B9 8BEC
MOV EBP,ESP
015F:005566BB 83C4F4
ADD ESP,-0C
015F:005566BE E86DFBFEFF
CALL 00546230
015F:005566C3 0F858F09FFFF
JNZ 00547058
015F:005566C9 E8460EFFFF
CALL 00547514
015F:005566CE E84537FFFF
CALL 00549E18
015F:005566D3 E81C55FFFF
CALL 0054BBF4
015F:005566D8 E8A3AAFFFF
CALL 00551180
015F:005566D8 E8A3AAFFFF
CALL 00551180
015F:005566DD E87609FFFF
CALL 00547058
<-- on rentre dans ce call
015F:005566E2 8BE5
MOV ESP,EBP
015F:005566E4 5D
POP EBP
015F:005566E5 C20C00
RET 000C
015F:005566E8 0000
ADD [EAX],AL
015F:005566EA 0000
ADD [EAX],AL
015F:005566EC 0000
ADD [EAX],AL
015F:005566EE 0000
ADD [EAX],AL
015F:005566F0 0000
ADD [EAX],AL
Donc voici le début de la troisième zone mémoire d'asprotect, il faut toujours rentrer dans le dernier call pour s'occuper de l'anti debugger. Une fois que l'on est rentré dans ce call on rentre dans le 2ème que l'on rencontre et on trace pour arriver là :
015F:00555674 8B1574C75500
MOV EDX,[0055C774]
015F:0055567A 8B45F8
MOV EAX,[EBP-08]
015F:0055567D E832E6FFFF
CALL 00553CB4
015F:00555682 8A15DCB65500
MOV DL,[0055B6DC]
015F:00555688 8B45F8
MOV EAX,[EBP-08]
015F:0055568B E890E6FFFF
CALL 00553D20
<-- call anti-debugger
015F:00555690 8945F4
MOV [EBP-0C],EAX
015F:00555693 837DF400
CMP DWORD PTR [EBP-0C],00
015F:00555697 7428
JZ 005556C1
015F:00555699 8B45F4
MOV EAX,[EBP-0C]
Le call en 55568b est l'anti-debugger ensuite il met eax dans ebp-0c et regarde si c'est 0. Donc je remplace le call par mov eax, 0 (on peut simplement mettre 0 dans eax à la sortie du call). Ensuite on appuie sur F5 et softice devrait réapparaitre. En effet j'ai dit au tout début de mettre un bpm 4b8000 w pour savoir quand il écrira dans la zone de l'import table. On doit arriver ici :
015F:00545709 782A
JS 00545735
015F:0054570B F3A5
REPZ MOVSD
<-- on arrive là
015F:0054570D 89C1
MOV ECX,EAX
015F:0054570F 83E103
AND ECX,03
015F:00545712 F3A4
REPZ MOVSB
015F:00545714 5F
POP EDI
015F:00545715 5E
POP ESI
015F:00545716 C3
RET
015F:00545717 8D740EFC
LEA ESI,[ECX+ESI-04]
015F:0054571B 8D7C0FFC
LEA EDI,[ECX+EDI-04]
Donc il copie ce qu'il y a en esi vers edi. Or edi == 4b8004 et esi == C72824. Ici il copie vers la zone où devrait être la table d'import des 00. Mais comment ce fait-il qu'il utilise une zone en c70000. On va regarder avec softice ce qu'il y a :
0167:00C72500 97 1F FB 4D B4 B2 65 77-1B 59 3D 6C ED E4 00 D0
...M..ew.Y=l....
0167:00C72510 88 0B 00 01 69 6D 6D 33-32 2E 64 6C 6C 00 01 18
....imm32.dll...
0167:00C72520 8C 90 8F 6D 2A 4B F0 E9-95 CE F8 38 EC 02 E5 C9
...m*K.....8....
0167:00C72530 CB 82 44 A9 C1 4D E5 EA-01 17 8C 90 8F 6D 2A 4B
..D..M.......m*K
Bizare on trouve des références à des dlls. Il faut garder cela en tête. On peut enlever le point d'arrêt en écriture car il va encore y écrire par la suite mais dans cette partie nous n'iront pas plus loin. Donc on continue de tracer (f10) pour arriver au moment ou asprotect passe la main au prog décompressé en mémoire. On arrive normalement là :
015F:00555DE9 8B4508
MOV EAX,[EBP+08]
015F:00555DEC 8B10
MOV EDX,[EAX]
015F:00555DEE 8B4508
MOV EAX,[EBP+08]
015F:00555DF1 8B401C
MOV EAX,[EAX+1C]
015F:00555DF4 E887F6FFFF
CALL 00555480
<-- on rentre là
015F:00555DF9 5F
POP EDI
015F:00555DFA 5E
POP ESI
015F:00555DFB 5B
POP EBX
015F:00555DFC 5D
POP EBP
015F:00555DFD C20400
RET 0004
Bon on reconnait facilement ce call grace au fait qu'il récupère les registres juste après. Il faut donc y rentrer, sinon le prog se lance. Ensuite on arrive là :
015F:005554A4 B854775400
MOV EAX,00547754
015F:005554A9 E8AE3CFFFF
CALL 0054915C
015F:005554AE E86D0AFFFF
CALL 00545F20
<-- on évite ce call
015F:005554B3 33C0
XOR EAX,EAX
015F:005554B5 5A
POP EDX
015F:005554B6 59
POP ECX
015F:005554B7 59
POP ECX
015F:005554B8 648910
MOV FS:[EAX],EDX
015F:005554BB EB0A
JMP 005554C7
015F:005554BD E93208FFFF
JMP 00545CF4
Et là il faut eviter de passer ce deuxième call sinon il utilise des sehs pour lancer le prog et on s'y pert. Don soit on le nop soit on fait r eip eip+5 quand on est dessus. Ensuite on à la même chose plus loin :
015F:005554D9 B854775400
MOV EAX,00547754
015F:005554DE E8793CFFFF
CALL 0054915C
015F:005554E3 E8380AFFFF
CALL 00545F20
<-- on évite
015F:005554E8 33C0
XOR EAX,EAX
015F:005554EA 5A
POP EDX
015F:005554EB 59
POP ECX
015F:005554EC 59
POP ECX
015F:005554ED 648910
MOV FS:[EAX],EDX
015F:005554F0 EB0A
JMP 005554FC
015F:005554F2 E9FD07FFFF
JMP 00545CF4
On fait pareil r eip eip+5 ou on nop. Il reste encore un double call, ou on évite le deuxième :
015F:00555534 648920
MOV FS:[EAX],ESP
015F:00555537 33C9
XOR ECX,ECX
015F:00555539 B201
MOV DL,01
015F:0055553B B854775400
MOV EAX,00547754
015F:00555540 E8173CFFFF
CALL 0054915C
015F:00555545 E8D609FFFF
CALL 00545F20
<-- on évite
015F:0055554A 33C0
XOR EAX,EAX
015F:0055554C 5A
POP EDX
015F:0055554D 59
POP ECX
015F:0055554E 59
POP ECX
De même on fair r eip eip+5 quand on est sur le call. Maintenant on continue de tracer pour arriver ici :
015F:00555590 EBE8
JMP 0055557A
015F:00555592 61
POPAD
<-- eax a l'eip
015F:00555593 EB01
JMP 00555596
015F:00555595 E850EB02E9
CALL E95840EA
015F:0055559A 17
POP SS
015F:0055559B E802000000
CALL 005555A2
015F:005555A0 E91758803D
JMP 3DD5ADBC
015F:005555A5 88C7
MOV BH,AL
015F:005555A7 55
PUSH EBP
015F:005555A8 0000
ADD [EAX],AL
Ici on retrouve le popad qui est caractéristique d'une fin de décompression . Et en effet à ce moment eax contient l'eip du prog décompressé en mémoire et plus loin un ret nous y envoie. Mais avant de faire quelquechose regardons donc l'état de la zone en 4b8000 ou devrait être la table d'import :
015F:004B8180 F4 49 C7 00 00 4A C7 00-0C 4A C7 00 18 4A C7 00
.I...J...J...J..
015F:004B8190 24 4A C7 00 30 4A C7 00-3C 4A C7 00 48 4A C7 00
$J..0J..<J..HJ..
015F:004B81A0 54 4A C7 00 60 4A C7 00-6C 4A C7 00 78 4A C7 00
TJ..`J..lJ..xJ..
015F:004B81B0 84 4A C7 00 90 4A C7 00-9C 4A C7 00 A8 4A C7 00
.J...J...J...J..
Et qu'est-ce qu'on y retrouve et bien des références vers
la zone c7xxxx. Et oui il utilise le même principe (bien que différent)
que cdilla. Pour appeler une api il fait call [4b8180], qui appel ce qu'il
y a en c749f4 et là on a un truc du genre jmp api. En fait c'est
plus complexe (voir la partie du tut qui en parle), il décrypte
certaines choses pour avoir l'api. Mais cela il le fait quand le programme
a besoin de cette api. Donc l'idée est de rajouter cette partie
au programme donc il faut dumper ce qu'il y a en c7xxxx. Pour ce faire
il faut savoir où commence cette zone et où elle se termine.
On regarde sous softice et on voit (entre des zones mémoires invalides)
qu'elle commence en c70000 et qu'elle se termine en c78000. Donc On peut
dumper cette zone en utilisant icedump (extension pour softice) et en faisant
: pagein d c70000 8000 c:\import.dat . On obtiendra ainsi un dump
de cette zone dans le fichier import.dat.
Cependant il ne faut pas ce précipiter et dumper le prog ensuite
directement. En effet si on analyse cela de plus près et qu'on laisse
le programme ce dérouler on constate que quand il fait appel à
la zone en c7xxxx il fait également appel à une autre zone.
Voici un exemple :
:00C74C32 0000
ADD [EAX],AL
:00C74C34 1E
PUSH DS
:00C74C35 0000
ADD [EAX],AL
:00C74C37 006842
ADD [EAX+42],CH
:00C74C38 68424CC700
PUSH 00C74C42
:00C74C3D E84A048EFF
CALL 0055508C
<-- il appel une zone mémoire en 55508c
:00C74C42 7A2C
JP 00C74C70
:00C74C44 C7009A2CC700
MOV DWORD PTR [EAX],00C72C9A
:00C74C4A C7002A560000
MOV DWORD PTR [EAX],0000562A
:00C74C50 1E
PUSH
On voit qu'il se sert d'une zone mémoire situé en 55508c.
Or ceci correspond au endroit ou asprotect se décompresse. Donc
pour que le programme marche la zone d'asprotect doit aussi être
présente. Il faut donc la dumper aussi. Comme tout à l'heure
on repère toute la zone entre des zones mémoires invalides,
mais attention ici certaine partie sont encore invalide, donc il faut bien
regarder. On trouve que cette zone s'étend de 520000 à 567000,
il faut donc la dumper comme ceci : pagein d 520000 44000 c:\asprotect.dat
. Maintenant on a la zone d'asprotect dans le fichier asprotect.dat .
On peut maintenant passer au dump du programme par lui même.
Au niveau du popad on fait a eip pour modifier l'instruction et on met
jmp eip pour qu'il boucle sur lui-même. Ensuite f5 pour sortir du
programme et il reste a dumper le programme avec procdump en faisant un
dump full sur la cible en mémoire.
2) Reconstruction du dump
Maintenant avec tout cela il faut reconstituer un exe valide. J'avais utilisé une première méthode très peu détaillé et assez compliqué. Un jour TeeJi m'a demandé pourquoi j'avais fait cela et pourquoi je n'avais pas utilisé les sections ? heu oui pourquoi ... heu ... il faut le temps que ca monte au cerveau ... lol :). En fait je n'y est pas pensé une seule seconde. Donc la je vais le faire ca va être nettement plus simple.
Bon tout d'abord il faut faire une petite manipulaiton. Il faut copier la section d'asprotect (celle qui est déjà dans le fichier) du prog d'origine (le protégé) et le mettre sur celle de notre dump. On fait ca car sinon la table d'import (celle utilisé par asprotect pas celle dont nous parlions) est abimé et on ne peut pas utiliser le programme dumpé. Pour savoir ou est le début de cette zone on cherche avec un héditeur hexa 90 60 E8 01 00 00 00 90 5D c'est le début de la zone d'asprotect (toujours) et on copi de la à la fin du prog, et on le remet à la place de celle du programme dumpé (on cherche la même chaine pour être au début).
Ensuite les deux autres dumps que l'on a fait il faut les rajoutés au programme. Cependant il faut qu'il soit remis dans les zones mémoires où ils se trouvaient. Pour ce faire on va utiliser procdump, puis peeditor et on ouvre notre programme dumpé. On va dans section et on fait add section. On rajoute une section avec ces caractéristiques :
Name: .aspr VS: 00047000 VA: 00120000 RS: 00047000 RA: 00115400 C: E0000020
C'est la section pour le fichier asprotect.dat. On met une virtual adresse
de 120000 (plus l'image base == 520000) pour qu'il se trouve au bon endroit
en mémoire. La taille du dump était de 47000h donc on met
47000h. La RA est géré par procdump (ca de moins à
faire :) ) et on peut changer les caractéristiques de la section
mais ce n'est pas obligatoire.
Ensuite On rajoute la section pour import.dat :
Name: .import VS: 00008000 VA: 00870000 RS: 00008000 RA: 0015C400 C: E0000020
Même explication que précédement. Ensuite il ne faut pas oublié de changer la taille de l'image. Je l'avoue je n'e m'en préoccupe pas car j'utilise procinfo (de TeeJi) qui le fait à ma place. Mais vous il faut le faire :) en utilisant la taille rajouté dans les sections que l'on rajoute à la taille de l'image.
Bon maintenant que ceci est fait, on prend son éditeur hexa et on ouvre le fichier asprotect.dat et on copi tout. On insère cela à la fin du dump de pleditor. On fait ensuite la même chose avec le fichier import.dat. On sauvegarde le dump et on change l'eip (celui contenu dans eax au niveau du popad), ici on fait 4b327c - 400000 (image base) = b327c. Donc le nouvel eip est b327c, on le change soit avec l'éditeur hexa soit avec pe-editor de procdump.
Maintenant on lance le dump et il se lance. CooL :). On a enfin notre
dump qui est executable. Il ne nous reste plus qu'à cracker le dump
...
| III_2ème méthode: Advanced mail list verify 2.0 |
N'ayant pas réussi à utiliser la méthode précédente sur amv, je vais vous présenter une méthode que Tsehp a utilisé sur commview. Remarquez que cette méthode ne marche pas sur pleditor (enfin Tsehp l'a changé: voir partie4). Cette méthode est notamment utilisé avec cdilla: c'est ce qu'on appel un "call fixer". Il permet de mettre les adresses des apis (enfin des fonctions) dans la table d'import (enfin ici dans l'iat). Donc en fait on aura toujours pas une iat et une table d'import correct au vrai sens du terme mais on n'aura plus besoin de dumper le chunk de l'it et la partie d'asprotect.
Regardons d'abord un peu comment de présentent les sections du programmes :
Name: .text VS: 00028000 VA: 00001000
RS: 00012200 RA: 00001000 C: C0000040
Name: .rdata VS: 00003000 VA: 00029000
RS: 00000C00 RA: 00013200 C: C0000040
Name: .data VS: 0000D000 VA: 0002C000
RS: 00001400 RA: 00013E00 C: C0000040
Name: .rsrc VS: 00029000 VA: 00039000
RS: 00019000 RA: 00015200 C: C0000040
Name: .data VS: 00015000 VA: 00062000
RS: 00014E00 RA: 0002E200 C: C0000040
Name: .data VS: 00001000 VA: 00077000
RS: 00000000 RA: 00043000 C: C0000040
Ici point de section .idata mais une section .rdata donc la table d'import
du prog décompressé aurait du (si elle existait) se trouvé
dans la section .rdata. On va donc mettre un point d'arrêt sur le
début de la section (lorsqu'il va y écrire) : bpm 429000
w . Ensuite il faut tracer comme tout à l'heure (je ne réexplique
pas c'est identique) jusqu'au début de la zone trois d'asprotect.
là on vire de la même facon le debugger, et softice réagit
de même pour le point d'arrêt en écriture sur le début
de la section .rdata. On trouve cette fois ci que le chunk de "l'import
table" est compris entre de0000 et de4000.
Bon on enlève notre point d'arrêt et on trace jusqu'à
la fin (identique à tout à l'heure à l'exception des
doubles calls qui n'y sont pas). On passe ensuite au début du programme
:
015F:00421BFB C3
RET
015F:00421BFC 55
PUSH EBP
<-- début du prog
015F:00421BFD 8BEC
MOV EBP,ESP
015F:00421BFF 6AFF
PUSH FF
015F:00421C01 6860944200
PUSH 00429460
015F:00421C06 68985B4200
PUSH 00425B98
015F:00421C0B 64A100000000
MOV EAX,FS:[00000000]
015F:00421C11 50
PUSH EAX
015F:00421C12 64892500000000
MOV FS:[00000000],ESP
015F:00421C19 83EC58
SUB ESP,58
Au passage on en profite pour noté que l'eip est 421bfc. On continue de tracer pour arriver là :
015F:00421C19 83EC58
SUB ESP,58
015F:00421C1C 53
PUSH EBX
015F:00421C1D 56
PUSH ESI
015F:00421C1E 57
PUSH EDI
015F:00421C1F 8965E8
MOV [EBP-18],ESP
015F:00421C22 FF1574914200
CALL [00429174]
<-- intérressant
015F:00421C28 33D2
XOR EDX,EDX
015F:00421C2A 8AD4
MOV DL,AH
015F:00421C2C 8915B86B4300
MOV [00436BB8],EDX
015F:00421C32 8BC8
MOV ECX,EAX
Là on retrouve ce que j'avais expliqué pour pleditor. Il appel ce qu'il y a normalement dans la table d'import (ici en 429174 au début de la section .rdata) et on trouve en 429174, de2464. Donc il va appelé ce qu'il y a en de2464 :
015F:00DE2464 E993091BBF
JMP KERNEL32!GetVersion
015F:00DE2469 1B00
SBB EAX,[EAX]
015F:00DE246B 000E
ADD [ESI],CL
015F:00DE246D 0000
ADD [EAX],AL
015F:00DE246F 00E9
ADD CL,CH
015F:00DE2471 37
AAA
015F:00DE2472 A11ABF1B00
MOV EAX,[001BBF1A]
Et en de2464 c'est ce jump api. Voilà comment il retrouve ces fonctions. Mais ce n'est pas tout je vous ai dit qu'il en décrypté au runtime. On continue de tracé le programme et voici un endroit intérressant (on cherche tjs un call [429xxxx] ) :
015F:0040B8DB 51
PUSH ECX
015F:0040B8DC 52
PUSH EDX
015F:0040B8DD FF1508904200
CALL [00429008]
<-- on a un call intérressant
015F:0040B8E3 85C0
TEST EAX,EAX
015F:0040B8E5 7408
JZ 0040B8EF
015F:0040B8E7 8B44241C
MOV EAX,[ESP+1C]
015F:0040B8EB 33F6
XOR ESI,ESI
015F:0040B8ED 8907
MOV [EDI],EAX
Pourquoi ce call [00429xxxx] est-il plus intérressant pour nous que d'autres? et bien voici ce qu'il fait par la suite :
015F:00DE2A14 681E2ADE00
PUSH 00DE2A1E
<-- on arrive là si on rentre dans le call
015F:00DE2A19 E816246DFF
CALL 004B4E34
<-- il appel une partie d'asprotect
015F:00DE2A1E 851F
TEST [EDI],EBX
015F:00DE2A20 DE00
FIADD WORD PTR [EAX]
015F:00DE2A22 AE
SCASB
015F:00DE2A23 1F
POP DS
015F:00DE2A24 DE00
FIADD WORD PTR [EAX]
015F:00DE2A26 DE00
FIADD WORD PTR [EAX]
Et bien il va vers la même zone mémoire que tout à l'heure mais là il ne fait pas jmp api, mais call 004b4e34. C'est une partie qui est dans la zone d'asprotect. Voilà on a trouvé le call qui sert à décrypter les api. Il est très important, il faut le noter.
Bon maintenant on recommence on lance le programme, on vire le changement d'adresse, on passe les décompressions on arrive dans la troisième zone, on vire l'anti-debugger et on oubli pas de remmettre notre bpm 429000 w. Normalement softice devrait s'arrêter sur le repz mosb de tout à l'heure, là où il copiait des zéro. Mais cette fois ci on laisse ce point d'arrêt et on appuis sur f5. Là softice devrait s'arrêté ici :
015F:004B5138 87FE
XCHG EDI,ESI
015F:004B513A AC
LODSB
015F:004B513B 08C0
OR AL,AL
015F:004B513D 74E4
JZ 004B5123
015F:004B513F 4E
DEC ESI
015F:004B5140 56
PUSH ESI
015F:004B5141 53
PUSH EBX
015F:004B5142 80F802
CMP AL,02
015F:004B5145 7407
JZ 004B514E
015F:004B5147 0FB64E01
MOVZX ECX,BYTE PTR [ESI+01]
015F:004B514B 41
INC ECX
015F:004B514C EB05
JMP 004B5153
015F:004B514E B904000000
MOV ECX,00000004
015F:004B5153 41
INC ECX
015F:004B5154 01CE
ADD ESI,ECX
015F:004B5156 E8B5FDFFFF
CALL 004B4F10
<-- cherche les apis
015F:004B515B AB
STOSD
015F:004B515C EBDC
JMP 004B513A
<-- retourne au début
015F:004B515E 61
POPAD
Et là on tombe dans une boucle. Si on regarde on trouve dans
edi des trucs du genre 429004. En gros cette routine sert à écrire
dans la table d'import normal (en 429000) les références
à la table d'import en de0000. Donc l'idée ici est qu'il
n'écrive pas en 429000 des références à la
zone en de0000, mais qu'il mette directement les adresses des apis qui
seront utilisés. Et c'est pour cela que tout à l'heure on
a cherché le call 4b4e34 qui sert à décrypter les
adresses des apis.
C'est donc la qu'on va mettre le call fixer. Remarquez que si vous
avez la flemme Tsehp propose de trouver le call 4b4e34 en faisant S 0 L
FFFFFFFF 55 8b ec c4 f8 fe ff. Pour cette boucle on peut la trouver avec
S 0 L FFFFFFFF AC 08 c0 74 E4. Mais bon il est utile de savoir d'où
cela provient. Maintenant il faut avant que cette boucle ne s'éxécute
écrire un call fixer. Et c'est là que l'on dit merci Tsehp
:). En effet il proposait cette méthode sur le programme commview.
Donc voici le principe :
015F:004B514E B904000000
MOV ECX,00000004
015F:004B5153 41
INC ECX
015F:004B5154 01CE
ADD ESI,ECX
015F:004B5156 E8B5FDFFFF
CALL 004B4F10
<-- on dévit là
015F:004B515B AB
STOSD
015F:004B515C EBDC
JMP 004B513A
<-- saut pour la boucle
Ce qu'il faut faire c'est dévié au niveau du call et chercher un espace libre en mémoire (avec des 0000) pour y dévié le call et y rajouté notre code. Je l'ai dévié en 4b6500 :
015F:004B5147 0FB64E01
MOVZX ECX,BYTE PTR [ESI+01]
015F:004B514B 41
INC ECX
015F:004B514C EB05
JMP 004B5153
015F:004B514E B904000000
MOV ECX,00000004
015F:004B5153 41
INC ECX
015F:004B5154 01CE
ADD ESI,ECX
015F:004B5156 E9A5130000
JMP 004B6500
<-- on dévit
015F:004B515B AB
STOSD
015F:004B515C EBDC
JMP 004B513A
015F:004B515E 61
POPAD
Et en 4b6500 on met le call fixer que Tsehp nous proposait (thx man :) ). Voici ce qu'il proposait comme call fixer :
190000 call 18a2f4
; we use the legal call
190005 cmp ecx,40000000
; ecx contains an api address ?
19000b jle 19001c
; if not
19000d add ecx,eax
; if yes convert the address***case 2
19000f add ecx,5
; to the absolute api address for a normal import table
190012 mov dword ptr [edi],ecx ; put it in import table,
pointed by edi
190014 add edi,4
; updates edi to the next import
190017 jmp 18a51e
; back to normal
19001c cmp ecx,0
; is it case 4 ?
19001f jz 19002e
190021 cmp eax,40000000
; eax contains an api address ?
190026 jle 19003c
; if not go to 19003c
190028 stosd
; we're in case 1, direct copy of valid api address in import table
190029 jmp 18a51e
; back to normal
19002e push dword ptr [eax+1] ; we're in case
3, pushes the encrypted api address
190031 call 16df80
;IMPORTANT
you have to locate this aspack address by doing a S 0 L FFFFFFFF
55 8b ec 81 c4 f8 fe ff ff 53 56
so we found as example 16df80, you MUST modify the code at offset
16df80+95 with three nops (90), if you don't
this call will generate an error 13 in aspack (the call doesn't
return properly)
this call is used to decypher the encrypted api address, normally
decrypted at run time
190036 stosd
; the api's address is then decrypted, copy it to import table
190037 jmp 18a51e
; back to normal, don't forget that this is the loop back jump
19003c mov eax,KERNEL32!GetProcAddress
; use softice to have your system's getprocaddress. this is case 4
190041 stosd
; copy to import table
190042 jmp 18a51e
; back to normal
Bon mais ici ce call fixer ne fonctionne pas. Il va falloir le modifer, notamment au niveau du cmp ecx, 0 qui empêche de passer par le cmp eax, 40000000. Mais avant cela il reste quelquechose qui l'empécherait de toute facon de marcher. Ne nous préoccupons pas de la table d'import pour le moment et faisons le dump du prog (comme tout à l'heure mais cette fois juste le programme). Analysons donc un peut le dump que nous avons obtenu et regardons notamment le début de la section .rdata :
00029000 DC29 DE00 F829 DE00 142A DE00 302A DE00 .)...)...*..0*..
00029010 4C2A DE00 682A DE00 842A DE00 A02A DE00 L*..h*...*...*..
00029020 BC2A DE00 0000 0000 482B DE00 542B DE00 .*......H+..T+..
00029030 602B DE00 6C2B DE00 782B DE00 842B DE00 `+..l+..x+...+..
00029040 0000 0000 4228 F2BF FE24 F2BF D824 F2BF ....B(...$...$..
00029050 3A28 F2BF 8A28 F2BF 9322 F2BF 3551 F2BF :(...(..."..5Q..
00029060 144A F2BF 7B50 F2BF 8D14 F2BF 7F26 F2BF .J..{P.......&..
00029070 0000 0000 7021 DE00 7C21 DE00 8821 DE00 ....p!..|!...!..
00029080 9421 DE00 A021 DE00 AC21 DE00 B821 DE00 .!...!...!...!..
00029090 C421 DE00 D021 DE00 DC21 DE00 E821 DE00 .!...!...!...!..
000290A0 F421 DE00 0022 DE00 0C22 DE00 1822 DE00 .!..."..."..."..
000290B0 2422 DE00 3022 DE00 3C22 DE00 4822 DE00 $"..0"..<"..H"..
000290C0 5422 DE00 6022 DE00 203D 4B00 6C22 DE00 T"..`"..
=K.l"..
000290D0 7822 DE00 8422 DE00 9022 DE00 9C22 DE00 x"..."..."..."..
000290E0 A822 DE00 B422 DE00 C022 DE00 CC22 DE00 ."..."..."..."..
On retrouve bien évidemment des références à la zone dexxxx. Mais en 290c8 (4290c8 en mémoire) on trouve 4b3d20, or ceci fait partie de la section d'asprotect. Comment ce fait-il qu'il mette un lien vers cette zone ici ? Pour le savoir on va relancer le programme (on enlève le chgt d'adresse + anti-debugger) et on met un point d'arrêt sur 4b3d20: bpm 4b3d20 x . Normallement softice apparait là :
015F:004B3D1F C3
RET
015F:004B3D20 55
PUSH EBP
015F:004B3D21 8BEC
MOV EBP,ESP
015F:004B3D23 8B550C
MOV EDX,[EBP+0C]
015F:004B3D26 8B4508
MOV EAX,[EBP+08]
015F:004B3D29 3B0584B64B00
CMP EAX,[004BB684]
<-- eax == -1 ?
015F:004B3D2F 7509
JNZ 004B3D3A
<-- non on saute
015F:004B3D31 8B0495E0B64B00
MOV EAX,[EDX*4+004BB6E0]
<-- oui on met 4b3da8 dans eax
015F:004B3D38 EB07
JMP 004B3D41
<-- et on retourne dans le prog
015F:004B3D3A 52
PUSH EDX
015F:004B3D3B 50
PUSH EAX
015F:004B3D3C E83739FFFF
CALL KERNEL32!GetProcAddress
<-- sinon on récupère l'adresse qu'il veut
015F:004B3D41 5D
POP EBP
015F:004B3D42 C20800
RET 0008
<-- et on retourne dans le programme
015F:004B3D45 8D4000
LEA EAX,[EAX+00]
015F:004B3D48 55
PUSH EBP
Intérressant non ? Donc dans notre call fixer il va falloir tenir
compte du fait que eax peut être égal à 4b3d20. Le
programme fait appel plusieurs fois à cette routine mais il ne met
qu'une seule fois 4b3da8 dans eax. De plus En testant le programme, à
aucun moment le programme n'a fait appel a ce qu'il y a en 4b3da8 (ca sert
peut-être à décrypté une région du programme,
mais la une clef est nécessaire, enfin je ne sait à quoi
sert cette partie). Ensuite dans notre dump il faudra également
remettre cette routine, et vu que le prog ne c'est pas servi de ce qu'il
y avait en 4b3da8 je ne l'ai pas remis (de toutes facon ca aurait été
beaucoup plus compliqué, mais je pense qu'il s'en sert pour décrypté
une partie du programme si on a une clef).
Bon maintenant on peut refaire le call fixer pour qu'il tienne compte
de cela :
015F:004B6500 E80BEAFFFF
CALL 004B4F10
<-- on remet le call
015F:004B6505 81F900000040
CMP ECX,40000000
<-- ecx contient une adresse d'api
015F:004B650B 760F
JBE 004B651C
<-- non on saute
015F:004B650D 03C8
ADD ECX,EAX
<-- oui on calcule l'adresse
015F:004B650F 83C105
ADD ECX,05
015F:004B6512 890F
MOV [EDI],ECX
<-- et on la met dans la table d'import
015F:004B6514 83C704
ADD EDI,04
<-- on augment edi pour l'import suivant
015F:004B6517 E91EECFFFF
JMP 004B513A
<-- et on boucle
015F:004B651C 3D00000040
CMP EAX,40000000
<-- eax contient une adresse d'api
015F:004B6521 7606
JBE 004B6529
<-- non on saute
015F:004B6523 AB
STOSD
<-- oui on copi l'adresse dans l'import table
015F:004B6524 E911ECFFFF
JMP 004B513A
<-- et on boucle
015F:004B6529 3D203D4B00
CMP EAX,004B3D20
<-- eax = 4b3d20
015F:004B652E 7506
JNZ 004B6536
<-- non on saute
015F:004B6530 AB
STOSD
<-- oui on le copi dans l'import table
015F:004B6531 E904ECFFFF
JMP 004B513A
<-- et on boucle
015F:004B6536 83F900
CMP ECX,00
<-- ecx = 0
015F:004B6539 750E
JNZ 004B6549
<-- non on saute
015F:004B653B FF7001
PUSH DWORD PTR [EAX+01]
<-- oui on met sur la pile la ref à
l'adresse de l'api
015F:004B653E E8F1E8FFFF
CALL 004B4E34
<-- et on appel le call de décryptage
015F:004B6543 AB
STOSD
<-- puis on copi eax dans la table d'import
015F:004B6544 E9F1EBFFFF
JMP 004B513A
<-- et on boucle
015F:004B6549 B8AC6DF7BF
MOV EAX,KERNEL32!GetProcAddress
<-- sinon on met l'adresse
015F:004B654E AB
STOSD
<-- de getprocaddress dans la table
d'import
015F:004B654F E9E6EBFFFF
JMP 004B513A
<-- et on boucle
015F:004B6554 0000
ADD [EAX],AL
015F:004B6556 0000
ADD [EAX],AL
Voilà le call fixer qui va mettre directement les adresses des apis dans la table d'import. Mais pour s'en servir il faut faire quelquechose sur le call 4b4e34 que l'on a trouvé précedement et qui sert à décrypter ces adresses :
004B4E34 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
004B4E34
004B4E34 ; Attributes: bp-based frame
004B4E34
004B4E34 sub_4B4E34 proc near
; CODE XREF: .import:00DE29FDp
004B4E34
; .import:00DE2A19p ...
004B4E34
004B4E34 var_108
= byte ptr -108h
004B4E34 var_8
= dword ptr -8
004B4E34 var_4
= dword ptr -4
004B4E34 arg_0
= dword ptr 8
004B4E34
004B4E34
push ebp
004B4E35
mov ebp, esp
004B4E37
add esp, 0FFFFFEF8h
004B4E3D
push ebx
004B4E3E
push esi
004B4E3F
mov ebx, [ebp+arg_0]
004B4E42
mov eax, [ebx]
004B4E44
mov [ebp+var_8], eax
004B4E47
lea eax, [ebp+var_108]
004B4E4D
xor ecx, ecx
004B4E4F
mov edx, 100h
004B4E54
call sub_4A5880
004B4E59
mov eax, ebx
004B4E5B
mov edx, [eax+4]
004B4E5E
mov dl, [edx]
004B4E60
cmp dl, ds:byte_4BB74C <--
je sais c'est mal désassemblé mais bon ...
004B4E66
jz short loc_4B4EB5
004B4E68
mov edx, [eax+4]
004B4E6B
mov ecx, edx
004B4E6D
inc ecx
004B4E6E
mov bl, [ecx]
004B4E70
add edx, 2
004B4E73
mov [ebp+var_4], edx
004B4E76
mov esi, ebx
004B4E78
and esi, 0FFh
004B4E7E
mov ecx, esi
004B4E80
lea eax, [ebp+var_108]
004B4E86
mov edx, [ebp+var_4]
004B4E89
call sub_4A76F0
004B4E8E
mov eax, [ebp+var_8]
004B4E91
call sub_4A817C
004B4E96
sub eax, 2
004B4E99
push eax
004B4E9A
mov edx, esi
004B4E9C
lea eax, [ebp+var_108]
004B4EA2
mov ecx, [ebp+var_8]
004B4EA5
call sub_4B3564
004B4EAA
lea eax, [ebp+var_108]
004B4EB0
mov [ebp+var_4], eax
004B4EB3
jmp short loc_4B4EBE
004B4EB5 ; ---------------------------------------------------------------------------
004B4EB5
004B4EB5 loc_4B4EB5:
; CODE XREF: sub_4B4E34+32j
004B4EB5
mov eax, [eax+4]
004B4EB8
inc eax
004B4EB9
mov eax, [eax]
004B4EBB
mov [ebp+var_4], eax
004B4EBE
004B4EBE loc_4B4EBE:
; CODE XREF: sub_4B4E34+7Fj
004B4EBE
push [ebp+var_4]
004B4EC1
push [ebp+var_8]
004B4EC4
call sub_4B4C8C
004B4EC9
mov [ebp+4], eax
<-- il faut nopper cette instruction
004B4ECC
pop esi
004B4ECD
pop ebx
004B4ECE
mov esp, ebp
004B4ED0
pop ebp
004B4ED1
retn 4
004B4ED1 sub_4B4E34 endp
004B4ED1
004B4ED4
Il faut nopper l'instruction en 4b4ec9 sinon on aura droit à
une erreur 13 et le prog s'arrêtera. Voilà maintenant on continu
de tracer le programme (on laisse notre "call fixer" décrypté
les adresses) et on va jusqu'à l'endroit où le programme
passe la main à celui décompressé en mémoire.
On retrouve le call suivi de la sauvegarde des registre, donc on rentre
dedans. Mais ensuite on trouve presque tout de suite le popad. Là
on note le nouvel eip : 421bfc, et on fait a eip et on met jmp eip. On
quitte softice (f5) et on dump la cible avec procdump. Attention utilisez
l'option don't rebuild import table ou use actual import, pour le dump.
Maintenant comme pour pleditor il faut changer l'eip avec le nouveau
soit 21bfc (421bfc-400000). Il faut également copié la section
.aspr (ici c'est l'avant dernière section la .data) de l'original
sur celle du dump. Ensuite on édite avec un éditeur hexa
ce qu'il y a en 4290c8. Et oui il y a la référence en 4b3d20,
donc on va mettre une référence vers une zone libre du programme.
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00029090 0D 0B F8 BF 56 F6 F9 BF 15 0A F8 BF 24
2E F9 BF ..ø¿Vöù¿..ø¿$.ù¿
000290A0 C4 64 F7 BF 93 7B F7 BF AD 73 F7 BF
2B 0B FA BF Äd÷¿“{÷¿s÷¿+.ú¿
000290B0 D5 6F F7 BF B4 5C F9 BF 2C 62 F9 BF
FE D5 F9 BF Õo÷¿´\ù¿,bù¿þÕù¿
000290C0 B4 20 F8 BF EF 62 F9 BF 00 70 47
00 A0 E0 F8 BF ´ ø¿ïbù¿..w.
àø¿
000290D0 B3 CA F8 BF 85 7D F7 BF 65 43 F7 BF
3C 43 F7 BF ³Êø¿…}÷¿eC÷¿<C÷¿
000290E0 B8 48 F7 BF DB 6D F7 BF 1F 6E F7 BF
41 6E F7 BF ¸H÷¿Ûm÷¿.n÷¿An÷¿
J'ai trouvé de la place libre en 477000. Donc il va falloir rajouté la routine qui se trouvait normalement en 4b3d20 :
015F:00477000 55
PUSH EBP
015F:00477001 8BEC
MOV EBP,ESP
015F:00477003 8B550C
MOV EDX,[EBP+0C]
015F:00477006 8B4508
MOV EAX,[EBP+08]
015F:00477009 3DFFFFFFFF
CMP EAX,FFFFFFFF
015F:0047700E 90
NOP
015F:0047700F 7509
JNZ 0047701A
015F:00477011 B8A83D4B00
MOV EAX,004B3DA8
015F:00477016 90
NOP
015F:00477017 90
NOP
015F:00477018 EB07
JMP 00477021
015F:0047701A 52
PUSH EDX
015F:0047701B 50
PUSH EAX
015F:0047701C E88BFDAFBF
CALL KERNEL32!GetProcAddress
015F:00477021 5D
POP EBP
015F:00477022 C20800
RET 0008
Voilà normalement maintenant le dump est complètement
fonctionnel :). Il ne reste plus qu'à le cracké...
Enfin il existe une méthode plus simple pour obtenir les addresses:
vous devez d'abord dumpé le programme. Ensuite l'exe d'origine doit
être lancé et vous executé le programme import_list
(désolé j'ai oublié le nom de l'auteur) qui vous donnera
un fichier avec les bonnes adresses, il ne reste plus qu'à faire
du copier/coller (vous aurez même un txt ave les changements :) ).
Enfin sur ce programme il y a toujours le problème du 4b3d20 à
régler ...
En fait je n'est pas pris le meilleur programme pour vous montrer cette exemple. En effet si vous relancé votre machine il y a peu de chance que le dump fonctionne, pourquoi ? et bien parce que certaine librairie de windows se charge à des endroits différents en mémoires. Voici ou se trouve le problème pour ce dump :
00029000 AA19 EABF 4416 EABF 3415 EABF D114 EABF ....D...4.......
00029010 EA15 EABF 7D16 EABF 2B18 EABF 8717 EABF ....}...+.......
00029020 2A17 EABF 0000 0000 97D3 7282 FE43 7082 *.........r..Cp.
00029030 F205 7582 F4DB 7182 77D8 7182 FD4E 7282 ..u...q.w.q..Nr.
00029040 0000 0000 4228 F2BF FE24 F2BF D824 F2BF ....B(...$...$..
00029050 3A28 F2BF 8A28 F2BF 9322 F2BF 3551 F2BF :(...(..."..5Q..
00029060 144A F2BF 7B50 F2BF 8D14 F2BF 7F26 F2BF .J..{P.......&..
00029070 0000 0000 DF7A F7BF E250 F8BF A570 F7BF .....z...P...p..
Il y a 6 adresses sélectionnées et voici à quoi elle correspondent :
00DE2B48->8272D397[2]=(00001028)COMCTL32.DLL!CreateToolbarEx:0017
00DE2B54->827043FE[2]=(0000102C)COMCTL32.DLL!InitCommonControls:0011
00DE2B60->827505F2[2]=(00001030)COMCTL32.DLL!CreateStatusWindow:0015
00DE2B6C->8271DBF4[2]=(00001034)COMCTL32.DLL!ImageList_ReplaceIcon:0046
00DE2B78->8271D877[2]=(00001038)COMCTL32.DLL!ImageList_Create:002D
00DE2B84->82724EFD[2]=(0000103C)COMCTL32.DLL!PropertySheet:0056
Donc pour ces 6 adresses il va falloir faire une modification. Je vais
simplement vous donner le principe car la méthode qui suit fait
pareil mais en nettement mieux (enfin la méthode de Tsehp celle
qui suit a changé et n'y fait pas référence). Ces
6 adresses nous savons à quelles fonctions elles correspondent,
donc le principe est de modifé l'eip du prog vers un endroit où
on peut mettre notre code . Là il faut avoir préalablement
noté le nom de la dll et des fonctions utilisées. Encuite
en utilisant GetModuleHandle puis GetProcAddress on répurère
l'adresse mémoire et on va l'écrire à son emplacement
dans la section .rdata. Comme cela il n'y aura plus de problème
d'adresse pour ces fonctions. Je n'est volontairement pas expliqué
complétement cette partie car entre temps Tsehp a mis au point une
nouvelle méthode de dump ... et dans celle ci vous comprendrez de
quoi je parles ...
| IV_3ème méthode: amv vu différement |
Maintenant je vais essayer de décrire une méthode qui
permet d'obtenir un dump qui marche sur toutes les machines (enfin sur
toutes les version de win 9x si le dump est fait sous win 9x et sous les
version de NT/2000 si le dump ... enfin voilà :) ). Je vais vous
présenté une méthode qui a été
mise au point par Tsehp (very good work man): vous pouver trouver son essai
original sur Commview à cette adresse : http://tsehp.cjb.net
en allant dans la section news, c'est l'essai sur asprotect1.05 . Je vous
la présente car le début de sa méthode (comment obtenir
les noms m'a permis d'élaborer ensuite la mienne).
Je vais essayer de vous expliquer cela clairement mais ca vas être
difficile, car la méthode par elle même n'est pas simple (enfin
quand on sait le faire ca devient plus simple :)). Tout d'abord le principe
est le suivant : au lieu de faire un dump qui a les adresses vers les fonctions
utilisées déjà ecrites dans la section .rdata (dans
le cas de amv sinon ca peut-être la .idata comme pleditor) il faudrait
plutôt que l'on récupère le nom de cette fonction (comme
CreateFileA) leur module (comme kernel32.dll) et l'endroit où elle
doivent être dans la section .rdata (ou .idata). Comme cela dans
le dump il suffirait de dévier le début du programme vers
un bout de code qui se chargerait de récupérer l'adresse
de ces fonctions et de les écrires à leurs places dans la
section .rdata, comme cela le dump marcherai sur toutes les versions de
win9x (ou nt/2000 si le dump est fait avec).
Où pourrait-on faire ca? Et bien au même endroit que précédement,
au niveau du call fixer. Pourquoi là? et bien c'est très
simple : tout d'abord on a chaque adresse vers la section .rdata où
il écrit. Mais en plus pour faire ces liens il est bien obligés
de connaitre de quelles fonctions il s'agit (en fait c'est plus compliqué
que cela).
Bon je ne vous réexplique pas comment arrivé là, lisez la partie précédente :
015F:004B5138 87FE
XCHG EDI,ESI
015F:004B513A AC
LODSB
015F:004B513B 08C0
OR AL,AL
015F:004B513D 74E4
JZ 004B5123
015F:004B513F 4E
DEC ESI
015F:004B5140 56
PUSH ESI
015F:004B5141 53
PUSH EBX
015F:004B5142 80F802
CMP AL,02
015F:004B5145 7407
JZ 004B514E
015F:004B5147 0FB64E01
MOVZX ECX,BYTE PTR [ESI+01]
015F:004B514B 41
INC ECX
015F:004B514C EB05
JMP 004B5153
015F:004B514E B904000000
MOV ECX,00000004
015F:004B5153 41
INC ECX
015F:004B5154 01CE
ADD ESI,ECX
015F:004B5156 E8B5FDFFFF
CALL 004B4F10
<-- cherche les apis
015F:004B515B AB
STOSD
015F:004B515C EBDC
JMP 004B513A
<-- retourne au début
015F:004B515E 61
POPAD
Au niveau du call 4b4f10, on remarque que edi contient l'adresse de destination dans la section .rdata et certaine fois eax ou edx on l'adresse d'une fonction. Il serait donc intérressant de regarder un peu ce call 4b4f10. Je ne vais pas l'analyser, je vous conseil plutôt de lire l'essai de TeeJi qui a fait cela très bien. Voici qd même une petite partie :
004B4F10 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
004B4F10
004B4F10 ; Attributes: bp-based frame
004B4F10
004B4F10 sub_4B4F10 proc near
; CODE XREF: sub_4B5094+C2p
004B4F10
004B4F10 var_11D
= byte ptr -11Dh
004B4F10 var_1D
= byte ptr -1Dh
004B4F10 var_1C
= dword ptr -1Ch
004B4F10 var_18
= byte ptr -18h
004B4F10 var_17
= dword ptr -17h
004B4F10 var_13
= byte ptr -13h
004B4F10 var_12
= dword ptr -12h
004B4F10 var_E
= dword ptr -0Eh
004B4F10 var_A
= dword ptr -0Ah
004B4F10 arg_0
= dword ptr 8
004B4F10 arg_4
= dword ptr 0Ch
004B4F10
004B4F10
push ebp
004B4F11
mov ebp, esp
004B4F13
add esp, 0FFFFFEE0h
004B4F19
push ebx
004B4F1A
push esi
004B4F1B
push edi
004B4F1C
mov edi, [ebp+arg_4]
004B4F1F
mov esi, [ebp+arg_0]
004B4F22
mov bl, [esi]
004B4F24
lea eax, [ebp+var_11D]
<-- notez l'adresse ebp-11d : pour moi 6afc6b
004B4F2A
xor ecx, ecx
004B4F2C
mov edx, 100h
004B4F31
call sub_4A5880
004B4F36
sub bl, 2
004B4F39
jz short loc_4B4F49
004B4F3B
sub bl, 2
004B4F3E
jz loc_4B4FE1
004B4F44
jmp loc_4B5035
004B4F49
............................................................................. <-- j'ai coupé
004B508A
004B508A loc_4B508A:
; CODE XREF: sub_4B4F10+9Aj
004B508A
; sub_4B4F10+B9j ...
004B508A
pop edi
004B508B
pop esi
004B508C
pop ebx
004B508D
mov esp, ebp
004B508F
pop ebp
004B5090
retn 8
004B5090 sub_4B4F10 endp
004B5090
Bon là j'ai juste mis le début du call et la fin (il est long), mais en tracant et en ayant repéré l'adresse indiqué vous verez apparaitre le nom de la fonction (la première c'est CreateFileA). Et on resort du call (pour comprendre son utilité voir l'essai de TeeJi): là le nom de la fonction est toujours en 6afc6b. On peut repérer cette adresse à la sortit du call en faisant ebp-15dh mais ceci reste encore à vérifié. Donc obtient donc bien le nom de la fonction désirées, de plus le nom de la dll associé apparait en faisant d ebx, et dans edi on a l'adresse de destination dans la section .rdata. Donc on a tout, sauf pour les fonctions cryptées alors là comme dans la partie précédente on fait appel à la fonction de décryptage (voir la partie précédente pour savoir comment la localiser) et on obtient le nom de la fonction. Il ne reste plus qu'un cas a traité c'est le cas où les fonctions sont appelées par leurs ordinal ... et bien vous verez que contrairement à Tsehp je ne m'en préoccupe pas ... vous comprendrez pourquoi.
Bon maintenant attaquons nous à la bête :). Donc en 4b5156 on dévit le call en mettant un jmp 4b6500 (voir partie 3), où on a de la place libre. Ensuite il faut trouver un espace mémoire libre pour pouvoir aller écrire le noms des fonctions, des dlls et leur adresses dans la section .rdata. Pour ma part je rajoute le début de code suivant en 4b6500 que j'enlève après pour mettre le call fixer :
pushad
<-- sauve les registres
push 40
<-- rempli avec des 00
push 00003000
<-- readable writable and executable
push 00008000
<-- taille de la mémoire allouée (on prend ses précautions)
push 01000000
<-- adresse où l'on veut allouée de la mémoire
(j'ai mis 1000000 car il n'y avait rien mais ceci est aléatoire)
call KERNEL32!VirtualAlloc
<-- on alloue la mémoire
popad
<-- on restaure les registres
Maintenant que l'on a notre zone alloué en 1000000 et se remet au début de notre code en 4b6500 (r eip 4b6500). Maintenant là on place le call fixer qui va se charger de copier le noms des fonctions avec dlls et adresses de la section .rdata en 1000000. Voici le call fixer de Tsehp légèrement modifé :
015F:004B6500 E80BEAFFFF
CALL 004B4F10
<-- on restaure le call
015F:004B6505 60
PUSHAD
<-- on sauve les registres
015F:004B6506 833D6BFC6A0000
CMP DWORD PTR [006AFC6B],00
<-- on regarde s'il y a un nom de fonction
015F:004B650D 7456
JZ 004B6565
<-- si il n'y en a pas alors on va vers la
décrytion / ordinal
015F:004B650F 6855FE6A00
PUSH 006AFE55
<-- on met le nom de la foncttion
015F:004B6514 FF35F0644B00
PUSH DWORD PTR [004B64F0]
<-- l'adresse ou l'on veut copier (avant
d'éxécuter la boucle pensé à mettre
00000001 en 4b64f0
015F:004B651A E8DD0DACBF
CALL KERNEL32!lstrcpy
<-- on copie
015F:004B651F FF35F0644B00
PUSH DWORD PTR [004B64F0]
<-- on met l'adresse ou se trouve le nom
015F:004B6525 E8830EACBF
CALL KERNEL32!lstrlen
<-- on calcul sa longueur
015F:004B652A 40
INC EAX
<-- pour rajouté un 0
015F:004B652B 43
INC EBX
<-- pour être au début du nom de la dll
015F:004B652C 0105F0644B00
ADD [004B64F0],EAX
<-- on augmente l'adresse de destination
015F:004B6532 53
PUSH EBX
<-- on met le nom de la dll
015F:004B6533 FF35F0644B00
PUSH DWORD PTR [004B64F0]
<-- l'adresse de destination
015F:004B6539 E8BE0DACBF
CALL KERNEL32!lstrcpy
<-- on copie
015F:004B653E FF35F0644B00
PUSH DWORD PTR [004B64F0]
<-- on met l'adresse ou se trouve le nom
015F:004B6544 E8640EACBF
CALL KERNEL32!lstrlen
<-- on calcul sa longueur
015F:004B6549 40
INC EAX
<-- pour rajouté un 0
015F:004B654A 0105F0644B00
ADD [004B64F0],EAX
<-- on augmente l'adresse de destination
015F:004B6550 8B0DF0644B00
MOV ECX,[004B64F0]
<-- on met l'adresse dans ecx
015F:004B6556 8939
MOV [ECX],EDI
<-- et on y copie l'adresse de .rdata qui
correspond à la fonction
015F:004B6558 8305F0644B0005
ADD DWORD PTR [004B64F0],05
<-- on augmente l'adresse de destination
015F:004B655F 61
POPAD
<-- on restaure les registres
015F:004B6560 E9D5EBFFFF
JMP 004B513A
<-- et on retourne dans la boucle d'asprotect
au niveau du stosd
015F:004B6565 3D00000040
CMP EAX,40000000
<-- eax contient l'adresse d'une api
015F:004B656A 7D14
JGE 004B6580
<-- oui alors on va vers le traitement par
ordinal
015F:004B656C 83F900
CMP ECX,00
<-- ecx == 0
015F:004B656F 750F
JNZ 004B6580
<-- oui alors ordinal, sinon crypté
015F:004B6571 FF7001
PUSH DWORD PTR [EAX+01]
<-- on met le paramêtre à la proc de
décryption
015F:004B6574 E8BBE8FFFF
CALL 004B4E34
<-- on décrypte (il faut mettre trois nop en
4b4e34+95 pour éviter les pb
015F:004B6579 686BFC6A00
PUSH 006AFC6B
<-- on met l'adresse du nom
015F:004B657E EB94
JMP 004B6514
<-- et on retourne à la copi
015F:004B6580 83EE04
SUB ESI,04
<-- ordinal alors on enlève 4 à esi
015F:004B6583 56
PUSH ESI
<-- on le met sur la pile
015F:004B6584 EB8E
JMP 004B6514
<-- et on retourne à la copie
015F:004B6586 0000
ADD [EAX],AL
015F:004B6588 0000
ADD [EAX],AL
Voilà je pense que cette fonction est assez bien détaillée.
Cependant il faut mettre un bpm 4b6579 après le call de décryption
pour vérifié si le nom de la proc est toujours à la
même adresse. Si elle ne l'est pas et bien elle n'est pas très
loin (un petit coup d'oeil sous softice) et on remplace l'adresse et après
on peut boucler tranquilement. (note : un point d'arrêt après
le call que l'on a dévié fait passer la boucle).
Une fois que la boucle est finie il faut copier tout ce qui est en
1000000 avec icedump, pagein d 1000000 2000 c:\noms.dat. Ensuite Tsehp
propose de rajouté une section au dump et d'y mettre les noms (avec
les adresses) puis d'écrire une procédure qui se chargerait
de récupérer chaque nom (ou ordinal) et de récupérer
l'adresse de la fonction avec getmodulehandle et getprocaddress (voir son
tut), puis de mettre cette adresse en .rdata correspondand. Comme cela
on aurait nous aussi reconstruit une fausse table d'import et le programme
marcherait.
Mais là description de sa méthode s'arrête là. En effet j'ai changé tout ca de manière non pas à obtenir une fausse table d'import pour que le programme marche mais une vraie comme si on n'avait jamais touvhé au programme (aucun rajout de section ou de code après). Ceci est possible car pour le moment asprotect ne détruit pas encore tout ... enfin sur la version d'amv, vous verez après que la dernière version (pleditor, commview ...) est plus compliqué .... Enfin repartons du début :
015F:004B5138 87FE
XCHG EDI,ESI
015F:004B513A AC
LODSB
<-- on est là
015F:004B513B 08C0
OR AL,AL
015F:004B513D 74E4
JZ 004B5123
015F:004B513F 4E
DEC ESI
015F:004B5140 56
PUSH ESI
015F:004B5141 53
PUSH EBX
015F:004B5142 80F802
CMP AL,02
015F:004B5145 7407
JZ 004B514E
015F:004B5147 0FB64E01
MOVZX ECX,BYTE PTR [ESI+01]
015F:004B514B 41
INC ECX
015F:004B514C EB05
JMP 004B5153
015F:004B514E B904000000
MOV ECX,00000004
015F:004B5153 41
INC ECX
015F:004B5154 01CE
ADD ESI,ECX
015F:004B5156 E8B5FDFFFF
CALL 004B4F10
<-- cherche les apis
015F:004B515B AB
STOSD
015F:004B515C EBDC
JMP 004B513A
<-- retourne au début
015F:004B515E 61
POPAD
Donc on se trouve en 4b513a et rien n'a encore été fait. Et bien regardons comment est le début de la section .rdata a ce moment là :
0030:00429000 72 B3 02 00 FC B2 02 00-0A B3 02 00 1E B3 02
00 r...............
0030:00429010 2E B3 02 00 40 B3 02 00-52 B3 02 00 62 B3 02 00
....@...R...b...
0030:00429020 80 B3 02 00 00 00 00 00-2C B4 02 00 11 00 00 80
........,.......
0030:00429030 06 00 00 80 3E B4 02 00-56 B4 02 00 6A B4 02 00
....>...V...j...
0030:00429040 00 00 00 00 12 B2 02 00-44 B2 02 00 54 B2 02 00
........D...T...
0030:00429050 38 B2 02 00 7A B2 02 00-86 B2 02 00 6A B2 02 00
8...z.......j...
0030:00429060 9E B2 02 00 AC B2 02 00-90 B2 02 00 22 B2 02 00
............"...
0030:00429070 00 00 00 00 58 AB 02 00-66 AB 02 00 76 AB 02 00
....X...f...v...
0030:00429080 4C AB 02 00 3E AB 02 00-88 AB 02 00 A8 AB 02 00
L...>...........
0030:00429090 B8 AB 02 00 C8 AB 02 00-DA AB 02 00 98 AB 02 00
................
0030:004290A0 F6 AB 02 00 02 AC 02 00-EA AB 02 00 24 AC 02 00
............$...
0030:004290B0 3E AC 02 00 4A AC 02 00-5E AC 02 00 72 AC 02 00
>...J...^...r...
Et oui on trouve des références du style 2b372, or si on ajoute l'image base on a 42b372 ... est ce que ceci ne ressemblerait pas à une import address table (lien vers les noms des fonctions en gros)? et bien oui, asprotect n'a pas détrui l'iat elle est comme neuve. Regardons un peu plus loin :
0030:00429300 E2 AF 02 00 CE AF 02 00-C2 AF 02 00 B4 AF 02 00
................
0030:00429310 A0 AF 02 00 92 AF 02 00-84 AF 02 00 76 AF 02 00
............v...
0030:00429320 60 AF 02 00 4A AF 02 00-36 AF 02 00 26 AF 02 00
`...J...6...&...
0030:00429330 12 AE 02 00 04 AE 02 00-8C B0 02 00 A6 B1 02 00
................
0030:00429340 00 00 00 00 D2 B3 02 00-E4 B3 02 00 FA B3 02 00
................
0030:00429350 00 00 00 00 14 00 00 80-03 00 00 80 13 00
00 80 ................
0030:00429360 10 00 00 80 02 00 00 80-12 00 00 80 16 00 00 80
................
0030:00429370 06 00 00 80 74 00 00 80-73 00 00 80 33 00 00 80
....t...s...3...
0030:00429380 0A 00 00 80 11 00 00 80-04 00 00 80 17 00 00 80
................
0030:00429390 34 00 00 80 00 00 00 00-C6 B2 02 00 DA B2
02 00 4...............
0030:004293A0 00 00 00 00 00 00 00 00-53 6F 66 74 77 61 72 65
........Software
0030:004293B0 5C 4D 69 63 72 6F 73 6F-66 74 5C 57 41 42 5C 44
\Microsoft\WAB\D
Pourquoi j'ai mis 14 00 00 80 en plus gros ? et bien simplement parceque là aussi c'est l'iat mais ce n'est pas un lien vers le nom de la fonction -2, c'est l'ordinal de la fonction utilisé. Bon maintenant allons voir la suite : en 429000 on a 2b372, donc normallement en 42b372 on devrait avoir l'import_by_name (celle avec les noms et l'ordinal de chaque fonctions, en fait c'est un tableau d'import_by_name mais il faut bien lui donner un nom :)) :
0030:0042B2F0 00 00 00 00 00 00 00 00-00 00 00 00 5B 01 00 00
............[...
0030:0042B300 00 00 00 00 00 00 00 00-00 00 7B 01 00 00 00 00
..........{.....
0030:0042B310 00 00 00 00 00 00 00 00-00 00 00 00 00 00 72 01
..............r.
0030:0042B320 00 00 00 00 00 00 00 00-00 00 00 00 00 00 86 01
................
0030:0042B330 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
................
0030:0042B340 5F 01 00 00 00 00 00 00-00 00 00 00 00 00 00 00
_...............
0030:0042B350 00 00 67 01 00 00 00 00-00 00 00 00 00 00 00 00
..g.............
0030:0042B360 00 00 62 01 00 00 00 00-00 00 00 00 00 00 00 00
..b.............
0030:0042B370 00 00 71 01 00 00 00 00-00 00 00 00 00 00 00 00
..q.............
0030:0042B380 5E 01 00 00 00 00 00 00-00 00 00 00 00 00 00 00
^...............
0030:0042B390 00 00 00 00 00 00 00 00-00 00 00 00 00 00 72 00
..............r.
0030:0042B3A0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
................
Et là il n'y a pas de nom de fonction :(. Mais en 42b372 on a
pourtant 7101 puis des 00 00. Et bien le 7101 c'est l'ordinal de la fonction
et juste après on devrait avoir le nom de la fonction. Car l'import_by_name
est fait comme cela : un word pour l'ordinal de la fonction et juste après
le nom de la fonction. Donc en fait dans l'iat les références
pointe vers le nom de la fonction-2.
C'est interressant tout cela on a l'iat correct et l'import_by_name
(supposé) contient des espaces vides avec des words suposés
être l'ordinal de la fonction. Il faut donc le vérifier. On
va faire un premier passage dans la boucle jusqu'au call inclu. Là
si on regarde en 6afc3b on a la fonction CreateFileA et en edi 429074.
Donc on va voir en 429074 et on a 58 AB 02 00 , donc si on ajoute l'image
base (400000 pour amv) et bien on obtient 42ab58. Allons voir en 42ab58
:
0042AB58 3400 0000 0000 0000 0000 0000 0000 1A01 4...............
Et bien en 42AB58 on a 3400 (lordinal de createfilea) et juste la place
de mettre CreateFileA (avec un 0 caractère de fin) avant d'arriver
sur l'ordinal suivant. Donc c'est bien cela : c'est l'import_by_name sans
les noms.
Donc l'idée est de faire un call fixer qui va se charger de
remettre les noms à leurs places. Et oui comme edi pointe vers ce
qu'il y a en 429xxx, il suffit de récupérer ce qu'il y a
et de rajouter l'image base comme cela on sera au début de l'endroit
où doit être le nom de la fonction-2. Pour ce qui est du cas
des fonctions appelés par ordinal il n'y a rien à faire car
l'ordinal est déjà dans l'iat. Ensuite on dumpera cette partie
que l'on remettre dans le dump.
Bon maintenant il faut le faire :). Donc on recommence tout et on est de nouveau au début de la boucle en 4b513a. Là on va jusqu'au call qu'on dévit de nouveau :
015F:004B5147 0FB64E01
MOVZX ECX,BYTE PTR [ESI+01]
015F:004B514B 41
INC ECX
015F:004B514C EB05
JMP 004B5153
015F:004B514E B904000000
MOV ECX,00000004
015F:004B5153 41
INC ECX
015F:004B5154 01CE
ADD ESI,ECX
015F:004B5156 E9A5130000
JMP 004B6500
<-- on dévit
015F:004B515B AB
STOSD
015F:004B515C EBDC
JMP 004B513A
015F:004B515E 61
POPAD
Maintenant on peut mettre en 4b6500 notre fixer :
015F:004B6500 E80BEAFFFF
CALL 004B4F10
<-- le call que l'on a détruit
015F:004B6505 60
PUSHAD
<-- on sauve les registres
015F:004B6506 833D6BFC6A0000
CMP DWORD PTR [006AFC6B],00
<-- est que le nom est là si non on saute
015F:004B650D 741C
JZ 004B652B
<-- et on va vers crypté/ordinal
015F:004B650F 686BFC6A00
PUSH 006AFC6B
<-- on met le nom
015F:004B6514 8B0F
MOV ECX,[EDI]
<-- on met la VA pointé par edi dans .rdata
015F:004B6516 81C102004000
ADD ECX,00400002
<-- on rajoute l'imagebase + 2 pour passer
l'ordinal
015F:004B651C 51
PUSH ECX
<-- on met l'adresse de destination
015F:004B651D E8DA0DACBF
CALL KERNEL32!lstrcpy
<-- et on copie
015F:004B6522 61
POPAD
<-- on restaure les registres
015F:004B6523 83C704
ADD EDI,04
<-- on passe au prochain traitement
015F:004B6526 E90FECFFFF
JMP 004B513A
<-- et on boucle
015F:004B652B 3D00000040
CMP EAX,40000000
<-- eax contient l'adresse d'une api
015F:004B6530 7DF0
JGE 004B6522
<-- oui alors c'est par ordinal et on
passe au suivant car l'ordinal est déjà
dans l'iat
015F:004B6532 83F900
CMP ECX,00
<-- ecx = 0 si non alors ordinal et on
015F:004B6535 75EB
JNZ 004B6522
<-- passe au suivant
015F:004B6537 FF7001
PUSH DWORD PTR [EAX+01]
<-- sinon c crypté et on passe le paramètre
015F:004B653A E8F5E8FFFF
CALL 004B4E34
<-- à la fonction de décryption
015F:004B653F 6864FC6A00
PUSH 006AFC64
<-- on recherche l'adresse du nom que l'on
met sur la pile
015F:004B6544 EBCE
JMP 004B6514
<-- et on va l'écrire dans l'import_by_name
Voilà cette proc est bien commentée je pense. Toutefois
il faut également mettre 3 nops en 4b4e34+95 (la proc de décryption)
et faire attention à l'adresse du nom en retour de cette procédure
(voir partie précédente).
Avant de laisser tournée la boucle il faut mettre un bpm 4b515e
x pour s'arrêter après cette boucle. Une fois qu'elle est
finie il faut dumper toute la partie qui a changé : pagein d 429000
2c00 c:\import.dat .
Maintenant on prend le dump du prog (obtenu en allant à l'endroit
où il passe la main à celui décompressé et
en ayant mis jmp eip, puis en le dumpant avec procdump ou procinfos). Si
jamais vous avez dumper le prog avec icedump pensé à le reconstruire
avec perebuilder (avec l'option fix raw offset) ou alors vous mettez directement
le raw offset de chaque section égal à la virtual address
.
Maintenant une chose très pratique est que la virtual adress
est égal au raw ofset, comme cela on se repère facilement.
Il va donc faloir coller l'import que l'on a dumpé dans le dump
en 29000. Une fois cela fait le dump n'est pas encore fonctionnel : en
effet on a bien l'import_by_name (les noms des fonctions avec l'ordinal),
l'iat (liens vers le noms des fonctions) mais il manque encore l'import
descriptor qui dit quelles dlls sont utilisées.
Et bien il nous reste encore à le reconstruire.
Pour ce faire un petit coup d'oeil sur le tut de TeeJi sur comment rajouter une dll dans l'iat et on comprend tout de suite mieux de quoi ca parle :). Bon on va commencer par le début : comment se présente d'import descriptor :
IMAGE_IMPORT_DESCRIPTOR struct
OriginalFirstThunk dd
0 ;RVA to original unbound IAT
TimeDateStamp
dd 0 ;not used here
ForwarderChain
dd 0 ;not used here
Name
dd 0 ;RVA to DLL name sring
FirstThunk
dd 0 ;RVA to IAT array
IMAGE_IMPORT_DESCRIPTOR ends
Donc pour une dll il y a 4 dword : le premier correspond au début
de la zone de l'iat dorrespondante, le deuxième on s'en fou, le
troisième ne sert pas ici donc on met FFFFFFFF, le quatrième
pointe vers le nom de la dll, et le cinquième vers un tableau d'adresse
(comme ici il n'y en a pas on le mettra égal à l'OriginalFirstThunk).
Bon, commencons : tout d'abord allons au début de la section
.rdata en 429000 :
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00029000 72 B3 02 00 FC B2 02 00 0A B3 02 00
1E B3 02 00 r³..ü²...³...³..
00029010 2E B3 02 00 40 B3 02 00 52 B3 02
00 62 B3 02 00 .³..@³..R³..b³..
00029020 80 B3 02 00 00 00 00 00 2C B4
02 00 11 00 00 80 €³......,´.....€
00029030 06 00 00 80 3E B4 02 00 56 B4 02 00
6A B4 02 00 ...€>´..V´..j´..
00029040 00 00 00 00 12 B2 02 00 44 B2 02 00
54 B2 02 00 .....²..D²..T²..
00029050 38 B2 02 00 7A B2 02 00 86 B2 02 00
6A B2 02 00 8²..z²..†²..j²..
00029060 9E B2 02 00 AC B2 02 00 90 B2 02 00
22 B2 02 00 ž²..¬²..².."²..
Voilà comment on repaire les dlls utilisés: au début de la section en 29000 et bien on a des liens vers les noms-2. J'en ai sélectionné 17 car juste après il y a 00 00 00 00. Ceci permet de dire que le prog fait appel à 17 fonctions de cette dlls. On sait déjà que L'originalFirstThunk est 29000. Pour le nom de la dll on pourrait le mettre n'importe où mais on peut faire mieux ici : Le premier liens est 2b372, on y va :
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
0002B350 41 00 67 01 52 65 67 45 6E 75 6D 4B 65
79 45 78 A.g.RegEnumKeyEx
0002B360 41 00 62 01 52 65 67 44 65 6C 65 74
65 4B 65 79 A.b.RegDeleteKey
0002B370 41 00 71 01 52 65 67 4F 70 65 6E 4B
65 79 41 00 A.q.RegOpenKeyA.
0002B380 5E 01 52 65 67 43 72 65 61 74 65 4B
65 79 41 00 ^.RegCreateKeyA.
0002B390 00 00 00 00 00 00 00 00 00 00 00
00 00 00 72 00 ..............r.
0002B3A0 53 68 65 6C 6C 45 78 65 63 75 74 65
41 00 00 00 ShellExecuteA...
En 2b390 on trouve plein de 00, et au dessus les noms des fonctions de la dlls, pointés par la partie de l'iat sélectionné. Or c'est fonctions font partis de la dll advapi32.dll. Donc en 2b390 on met advapi32.dll et le nom de la dll est en 2b390. Si vous ne vous y retrouvé pas au niveau des noms des dlls utilisé le prog import list il vous donnera dans l'ordre les fonctions avec leur dll associée. Maintenant on passe en 29020 et on recommence avec la dll suivante . Pour ce qui est des Ordinals il se trouve que dans ce prog c'est pour la dll wsock32.dll (vous pouvez le repérer soit avec le prog russe, soit au niveau de la boucle que l'on a fait et en mettant un point d'arrêt au niveau du traitement de l'ordinal et vous notez la/les dll(s) correspondantes). Pour écrire l'import descriptor on choisi une place libre, voici le mien reconstruit :
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
0002A800 00 90 02 00 00 00 00 00 FF FF FF FF
90 B3 02 00 .......ÿÿÿÿ³..
0002A810 00 90 02 00 28 90 02 00 00 00
00 00 FF FF FF FF ...(......ÿÿÿÿ
0002A820 7B B4 02 00 28 90 02 00 44 90 02
00 00 00 00 00 {´..(..D......
0002A830 FF FF FF FF BC B2 02 00 44 90 02
00 74 90 02 00 ÿÿÿÿ¼²..D..t..
0002A840 00 00 00 00 FF FF FF FF 2F AD 02 00
74 90 02 00 ....ÿÿÿÿ/..t..
0002A850 04 92 02 00 00 00 00 00 FF FF FF
FF AE B3 02 00 .’......ÿÿÿÿ®³..
0002A860 04 92 02 00 0C 92 02 00 00 00
00 00 FF FF FF FF .’...’......ÿÿÿÿ
0002A870 05 B2 02 00 0C 92 02 00 44 93 02
00 00 00 00 00 .²...’..D“......
0002A880 FF FF FF FF 14 B4 02 00 44 93 02
00 54 93 02 00 ÿÿÿÿ.´..D“..T“..
0002A890 00 00 00 00 FF FF FF FF D0 A8 02 00
54 93 02 00 ....ÿÿÿÿШ..T“..
0002A8A0 98 93 02 00 00 00 00 00 FF FF FF
FF ED B2 02 00 ˜“......ÿÿÿÿí²..
0002A8B0 98 93 02 00 00 00 00 00 00 00
00 00 00 00 00 00 ˜“..............
0002A8C0 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 ................
0002A8D0 57 53 4F 43 4B 33 32 2E 44 4C 4C 00
00 00 00 00 WSOCK32.DLL.....
.
Voilà, j'ai mis en evidente les différentes parties, ici le prog utilise 8 dlls. Maintenant pour que le dump soit opérationnel, il faut encore changé l'import adress RVA (avec peeditor) en 2a800, et également mettre l'eip original, celui trouvé au moment où le prog passe la main à celui décompressé (ici 421bfc).
Bon mais là amv ne marchera pas, pourquoi ? et bien si vous vous souvenez dans une partie précédent je vous ai dit qu'il mettait une adresse d'asprotect dans la table d'import qui servira pour une décryption si on a la clef. Et bien en fait il fait appel à GetProcAddress sauf pour un passage où il met cette valeur. Donc notre fixer a mis comme valeur pour ca une référence à GetProcAddress, et si le dump ne marche pas c'est que pour le passage pour la décryption et bien il fait appel à getprocaddress alors qu'il devrait mettre une valeur. Regardons le message d'erreur qui apparait au lancement du dump :
Crypt API not found. Please re-install
Donc on désassemble et on recherche ca et on arrive là :
00417620 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
00417620
00417620
00417620 sub_417620 proc near
; CODE XREF: _WinMain@16+EBp
00417620
push 4
00417622
push 0FFFFFFFFh
00417624
call ds:off_4290C8
0041762A
test eax, eax
0041762C
mov ds:dword_433758, eax
00417631
jnz short loc_417649
<-- on force
00417633
push 10h
00417635
push offset aAdvancedMailLi ; "Advanced Mail List Verify"
<-- pas bon
0041763A
push offset aCryptApiNotFou ; "Crypt API not found. Please
re-install "...
0041763F
push eax
00417640
call ds:dword_4292E8
00417646
xor eax, eax
00417648
retn
00417649 ; ---------------------------------------------------------------------------
00417649
00417649 loc_417649:
; CODE XREF: sub_417620+11j
00417649
mov eax, 1
0041764E
retn
Pour eviter ce message d'erreur et la fin du prog il suffit de forcer
le saut en 417631 et le tours est joué. Maintenant le dump est complètement
fonctionnel et comme neuf :) . Biensur il y a encore une partie crypté
nécessitant la clef ... mais bon on a comme obtenu un dump d'amv
avec une véritable table d'import.
Bon si je vous est décrit le principe ce la méthode à
Tsehp c'est parce que c'est grace à cela que j'ai pu élaborer
celle là, et oui il fallait savoir où était le nom
des fonctions ....
| V_4ème méthode: pleditor |
Ce qui serait intérressant c'est que la méthode précédente
marche également sur pleditor (étant donné qu'il est
protégé avec une version plus récente d'asprotect).
Et bien malheuresement elle ne marche pas car l'iat et l'it n'éxiste
plus, il n'y a que des 00. Cependant est il impossible pour autant d'arriver
au même objectif que précédement? et bien non mais
il va falloir arriver à mettre une méthode au point qui va
créer une iat, un tableau d'import_by_name et pourquoi pas en même
temps l'import descriptor. Mais comment peut on y arriver? et bien c'est
simple on ne va plus maintenant écrire un call fixer mais un import
table rebuilder. Comment est-ce possible ? et bien tout simplement parce
que asprotect se trahi lui même... Il est obligé de mettre
c'est liens vers les fonctions (qu'il y ai du cryptage ou juste un jmp
ceci n'a pas d'importance) au même endroit qu'il devait se trouvé
originellement. En effet suposons qu'en 429000 on est un lien vers l'api
CreateFileA, et bien le programme appelera ce qu'il y a en 429000 : call
[429000] et comme ceci fait partie de l'import table, windows se chargera
de remplacer ca par l'adresse de CreateFileA. Donc si Asprotect veut reconstruire
une import table, même une crypté ou n'importe quoi d'autre,
il est obligé que ce qui liera le prog à CreateFileA se trouve
en 429000. Et bien c'est comme cela que nous pourrons arriver à
reconstruire une iat, le tableau d'import_by_name, et l'import descriptor.
Je tiens à précisez que je n'aurais jamais pu mettre
cette méthode au point si Tsehp n'avait pas expliqué comment
récupérer le noms des fonctions.
Bon maintenant mettons les choses en pratique. On lance pleditor, on trace, on enlève l'anti-debug et on arrive à cette fameuse boucle (voir toutes les parties précédentes pour y arriver) :
015F:00555392 AC
LODSB
015F:00555393 08C0
OR AL,AL
015F:00555395 74E4
JZ 0055537B
015F:00555397 4E
DEC ESI
015F:00555398 56
PUSH ESI
015F:00555399 53
PUSH EBX
015F:0055539A 80F802
CMP AL,02
015F:0055539D 7407
JZ 005553A6
015F:0055539F 0FB64E01
MOVZX ECX,BYTE PTR [ESI+01]
015F:005553A3 41
INC ECX
015F:005553A4 EB05
JMP 005553AB
015F:005553A6 B904000000
MOV ECX,00000004
015F:005553AB 41
INC ECX
015F:005553AC 01CE
ADD ESI,ECX
015F:005553AE E8B5FDFFFF
CALL 00555168
<-- on redévi le call en jmp 556700
015F:005553B3 AB
STOSD
015F:005553B4 EBDC
JMP 00555392
015F:005553B6 61
POPAD
015F:005553B7 837D0800
CMP DWORD PTR [EBP+08],00
015F:005553BB 7509
JNZ 005553C6
Bon comme précédement on redévi le call vers un endroit libre, pour moi en 556700. Maintenant je vais essayer de vous expliquer le principe de de cette import rebuilder. Tout d'abord supposons que asprotect désire mettre son lien en 4b8000 et bien on prendra une place pour mettre notre tableau import_by_name (les noms) en 4b8950 (par exemple) et on écrit le nom en 4b8950+2 (pour gérer l'ordinal mais qui ici n'y est pas) et on met en 4b8000 un lien vers 4b8950-image base (ici b8950 car l'image base est de 400000). Ensuite pour le cas où la fonction est appelé par ordinal on mettra directement l'ordinal en 4b8000 (enfin l'adresse à vers laquelle aspr pointe), on le met directement dans l'iat, mais pour que windows le reconnaisse il faudra ajouter 80000000 pour qu'il sache que c'est un ordinal et non l'adresse du nom de la fonction-2. Pour ce qui est de l'import descriptor on le reconstruira comme ceci ebx, contient une référence vers le nom de la dll en cours, il suffira de comparé ebx à chaque passage à ca valeur précendente pour savoir si on est toujours dans la même partie de l'import descriptor. Si non alors on reconstruit une structure comme ceci:
IMAGE_IMPORT_DESCRIPTOR struct
OriginalFirstThunk dd
0 ;début de la zone où l'on va écrire les noms
pour cette structure
TimeDateStamp
dd 0 ;on met 00000000
ForwarderChain
dd 0 ;on met FFFFFFFF
Name
dd 0 ;on écrit le nom de la dll avec le nom des fonctions
et on met un lien vers ce nom
FirstThunk
dd 0 ;on le met égal à OriginalFirstThunk
IMAGE_IMPORT_DESCRIPTOR ends
Je sais tout ceci n'est expliqué que très grossièrement mais je ne sais pas trop comment le dire ... Bon maintenant on peut mettre en 556700 l'import rebuilder (il n'est en rien optimisé mais il fonctionne bien :) ) :
015F:00556700 E863EAFFFF
CALL 00555168
<-- on restaure le call
015F:00556705 60
PUSHAD
<-- on sauve les registres
015F:00556706 391DFC665500
CMP [005566FC],EBX
<-- est-on tjs dans la même structure de
l'import descriptor
015F:0055670C 7470
JZ 0055677E
<-- oui on passe à la suite
015F:0055670E 60
PUSHAD
<-- non en en créér une nouvelle
015F:0055670F 891DFC665500
MOV [005566FC],EBX
<-- on met ebx (pointeur vers le nom de la
dll) dans une variable
015F:00556715 8B15F4665500
MOV EDX,[005566F4]
<-- on met l'adresse courante de l'id
dans edx
015F:0055671B 893A
MOV [EDX],EDI
<-- et on y met le contenu de edi (c'est à
dire le début de l'iat correspondant
015F:0055671D 812A00004000
SUB DWORD PTR [EDX],00400000
<-- on enlève l'image base
015F:00556723 8305F466550008
ADD DWORD PTR [005566F4],08
<-- on actualise le pointeur vers l'id
015F:0055672A 8B15F4665500
MOV EDX,[005566F4]
<-- et on le met dans edx
015F:00556730 C702FFFFFFFF
MOV DWORD PTR [EDX],FFFFFFFF
<-- pour mettre ff.. dans la structure
015F:00556736 43
INC EBX
<-- on fait pointer ebx sur la dll
015F:00556737 53
PUSH EBX
<-- on met sur la pile
015F:00556738 FF35F8665500
PUSH DWORD PTR [005566F8]
<-- ainsi que sa destination (adresse
courante de du tableau d'import_by_name)
015F:0055673E E8B90BA2BF
CALL KERNEL32!lstrcpy
<-- et on copi
015F:00556743 FF35F8665500
PUSH DWORD PTR [005566F8]
<-- on calcule la longueur du nom
015F:00556749 E85F0CA2BF
CALL KERNEL32!lstrlen
<-- dela dll
015F:0055674E 40
INC EAX
<-- on incrémente pour le 0 de fin
015F:0055674F 8B15F8665500
MOV EDX,[005566F8]
<-- on met l'adresse courante du tableau
d'import_by_name (contient
le nom de la dll)
015F:00556755 81EA00004000
SUB EDX,00400000
<-- on enlève l'image base
015F:0055675B 8305F466550004
ADD DWORD PTR [005566F4],04
<-- on actualise l'id
015F:00556762 8B0DF4665500
MOV ECX,[005566F4]
<-- et on met son adresse dans ecx
015F:00556768 8911
MOV [ECX],EDX
<-- pour y mettre le pointeur vers le nom de la
dll
015F:0055676A 8B59F4
MOV EBX,[ECX-0C]
<-- on récupère l'OriginalFirstThunk
015F:0055676D 895904
MOV [ECX+04],EBX
<-- que l'on met dans le FirstThunk
015F:00556770 8305F466550008
ADD DWORD PTR [005566F4],08
<-- on actualise l'id
015F:00556777 0105F8665500
ADD [005566F8],EAX
<-- ainsi que le tableau d'import_by_name
015F:0055677D 61
POPAD
<-- on a finit pour l'id on passe à la
suite
015F:0055677E 833D6BFC740000
CMP DWORD PTR [0074FC6B],00
<-- on a un nom de focntion
015F:00556785 743C
JZ 005567C3
<-- non alors on va vers crypté/ordinal
015F:00556787 8B15F8665500
MOV EDX,[005566F8]
<-- oui on récupère l'adresse courante du
tableau d'import_by_name
015F:0055678D 81EA00004000
SUB EDX,00400000
<-- on enlève l'image base
015F:00556793 8917
MOV [EDI],EDX
<-- et on met un lien dans l'iat
015F:00556795 686BFC7400
PUSH 0074FC6B
<-- on met l'adresse du nom de fonction
015F:0055679A 8B15F8665500
MOV EDX,[005566F8]
<-- on met l'adresse courante dun tableau
d'import_by_name
015F:005567A0 83C202
ADD EDX,02
<-- on ajoute deux pour l'ordinal
015F:005567A3 52
PUSH EDX
<-- on la met sur la pile
015F:005567A4 E8530BA2BF
CALL KERNEL32!lstrcpy
<-- et on copie la procname
015F:005567A9 52
PUSH EDX
<-- on met le nom de la fonction
015F:005567AA E8FE0BA2BF
CALL KERNEL32!lstrlen
<-- on calcule sa taille
015F:005567AF 0503000000
ADD EAX,00000003
<-- on ajoute trois pour le zero de fin et
l'ordinal
015F:005567B4 0105F8665500
ADD [005566F8],EAX
<-- on actualise le tableau d'import_by_name
015F:005567BA 61
POPAD
<-- on récupère les registres
015F:005567BB 83C704
ADD EDI,04
<-- on passe au suivant
015F:005567BE E9CFEBFFFF
JMP 00555392
<-- et on boucle
015F:005567C3 3D00000040
CMP EAX,40000000
<-- eax a l'adresse d'une api
015F:005567C8 7D22
JGE 005567EC
<-- oui alors ordinal
015F:005567CA 83F900
CMP ECX,00
<-- ecx = 0
015F:005567CD 751D
JNZ 005567EC
<-- oui alors ordinal
015F:005567CF FF7001
PUSH DWORD PTR [EAX+01]
<-- on passe le paramaètre pour la fonction
015F:005567D2 E8B5E8FFFF
CALL 0055508C
<-- de décryption
015F:005567D7 8B15F8665500
MOV EDX,[005566F8]
<-- on met l'adresse courante du tableau
d'import_by_name dans edx
015F:005567DD 81EA00004000
SUB EDX,00400000
<-- on enlève l'image base
015F:005567E3 8917
MOV [EDI],EDX
<-- et on met le lien dans l'iat
015F:005567E5 6864FC7400
PUSH 0074FC64
<-- on met l'adresse du nom de la fonction
015F:005567EA EBAE
JMP 0055679A
<-- et on retourne la copié dans le tableau
d'import_by_name
015F:005567EC 83EE04
SUB ESI,04
<-- ordinal alors on le récupère
015f:005567EF 8B16
MOV EDX,[ESI]
<-- dans edx
015f:005567F1 81C200000080
ADD EDX, 80000000
<-- on prépare l'ordinal pour windows
015F:005667F7 8917
MOV [EDI],EDX
<-- on le met dans l'iat
015F:005667F9 61
POPAD
<-- on récupère les registres
015F:005567F2 83C704
ADD EDI,04
<-- on passe au suivant
015F:005567F5 E998EBFFFF
JMP 00555392
<-- on boucle
015F:005567FA 0000
ADD [EAX],AL
015F:005567FC 0000
ADD [EAX],AL
Voilà , je pense avoir commenté au maximum, mais de toute
facon c'est assez dur à expliquer. Avant d'éxécuter
la boucle il faudra mettre en 5566f4 le début de l'endroit où
vous voulez mettre l'id ( j'ai mis 4b8950 car quand il installe la fausse
table d'import il met des liens jusque là, donc l'iat s'arrêtera
là). Ensuite en 5566f8 on met l'endroit où vous voulez mettre
le tableau d'import_by_name (j'ai mis 5566fc en pensant que 200h
octets pour l'id était sufisant. Il faut également mettre
les trois nops dans le call de décryptages (voir avant), mais pour
le repérer il faut étendre la recherche : s 0 l ffffffff
55 8b ec 81 c4 f8 fe ff ff 53 56 8b 5d 08 8b 03 89 45 f8 8d.
Maintenant on laisse tranquilement la boucle s'éxécuter
et le rebuilder nous reconstruire l'import table. Donc on aura pris soin
de mettre un point d'arrêt à la sortie de la boucle au niveau
du popad.
Bon quand même un petit complément, une réétude du rebuilder en 2 parties :
Tout d'abord redéfinissons bien les termes pour être d'accord
(je finis par m'embrouiller à force):
import_by_name c'est pour les noms des fonctions (on y mettras également
le nom des dlls)
iat c'est les liens vers les noms des fonctions
et l'id c'est pour décrire toutes les strctures utilisées
(quelles dlls).
Bon donc au départ edi contient un pointeur vers l'iat (supposons
4e31b8) donc nous on a décidé de mettre les noms (l'it) en
4e3b00 pour commencer . Donc ce qu'on va faire ces mettres le noms en 4e3b02
et dans l'iat (pointé par edi) on met le liens vers 4e3b00-image
base. Après il reste encore à actualiser le pointeur de le
tableau d'import_by_name (les noms) mais on ne gère pas l'iat, c'est
asprotect qui s'en occupe tout seul (enfin on incrémente qd même
edi vu qu'il n'y a plus de stosd).
Bon maitenant je recommente ma routine :
015F:00556700 E863EAFFFF
CALL 00555168
<-- on restaure le call
015F:00556705 60
PUSHAD
<-- on sauve les registres
................................................................... <-- j'ai coupé toutes une routine c'est en fait l'import descriptor rebuilder
015F:0055677E 833D6BFC740000
CMP DWORD PTR [0074FC6B],00
<-- on a un nom?
015F:00556785 743C
JZ 005567C3
<-- non on saute crypté/ordinal
015F:00556787 8B15F8665500
MOV EDX,[lesnoms]
<-- on met le pointeur vers l'import
descriptor dans edx il évoluera au fil des
boucles
015F:0055678D 81EA00004000
SUB EDX,00400000
<-- on enlève l'image base
015F:00556793 8917
MOV [EDI],EDX
<-- comme edi pointe vers l'iat on peut y mettre
un lien vers le nom de la fonctions
015F:00556795 686BFC7400
PUSH 0074FC6B
<-- on met le nom
015F:0055679A 8B15F8665500
MOV EDX,[lesnoms]
on met son adresse de destination (le
tableau d'import_by_name)
015F:005567A0 83C202
ADD EDX,02
<-- on passe l'ordinal
015F:005567A3 52
PUSH EDX
<-- adresse ou le nom va être copié (dans le
tableau d'import_by_name)
015F:005567A4 E8530BA2BF
CALL KERNEL32!lstrcpy
<-- on le copie
015F:005567A9 52
PUSH EDX
<-- on calcul sa longueur
015F:005567AA E8FE0BA2BF
CALL KERNEL32!lstrlen
015F:005567AF 0503000000
ADD EAX,00000003
<-- on y rajoutes 3 (ordinal + 0 de fin) pour
015F:005567B4 0105F8665500
ADD [lesnoms],EAX
<-- pour actualiser le pointeur vers le tableau
d'import_by_name
015F:005567BA 61
POPAD
<-- on récupère les registres
015F:005567BB 83C704
ADD EDI,04
<-- on actualise le pointeur vers l'iat
015F:005567BE E9CFEBFFFF
JMP 00555392
<-- pour le prochain passage
015F:005567C3 3D00000040
CMP EAX,40000000
<-- eax == api
015F:005567C8 7D22
JGE 005567EC
<-- oui alors on va vers ordinal
015F:005567CA 83F900
CMP ECX,00
<-- ecx == 0
015F:005567CD 751D
JNZ 005567EC
<-- oui alors on va vers ordinal
015F:005567CF FF7001
PUSH DWORD PTR [EAX+01]
<-- sinon crypté
015F:005567D2 E8B5E8FFFF
CALL 0055508C
<-- et on décrypte pour le nom
015F:005567D7 8B15F8665500
MOV EDX,[lesnoms]
<-- on met le pointeur courant vers le tableau
d'import_by_name
015F:005567DD 81EA00004000
SUB EDX,00400000
<-- on enlève l'image base
015F:005567E3 8917
MOV [EDI],EDX
<-- et on le copie dans l'iat qui est pointé
par edi
015F:005567E5 6864FC7400
PUSH 0074FC64
<-- on met le nom
015F:005567EA EBAE
JMP 0055679A
<-- et on va le copier dans le tableau
d'import_by_name
015F:005567EC 83EE04
SUB ESI,04
<-- là on est au ordinal
015f:005567EF 8B16
MOV EDX,[ESI]
<-- on le récupère
015f:005567F1 81C200000080
ADD EDX, 80000000
<-- on l'adapte pour le mettre dans l'iat
015F:005667F7 8917
MOV [EDI],EDX
<-- on le met car edi pointe vers l'iat
015F:005667F9 61
POPAD
<-- on récupère les registres
015F:005567F2 83C704
ADD EDI,04
<-- on actualise le pointeur vers l'iat car il
n'y a plus de stosd pour le faire
015F:005567F5 E998EBFFFF
JMP 00555392
<-- et on boucle
015F:005567FA 0000
ADD [EAX],AL
015F:005567FC 0000
ADD [EAX],AL
Donc avant que cette routine ne s'execute il faut avoir mis dans le
pointeur [lesnoms] et bien le début du tableau d'import_by_name.
Maintenant je recommente l'import descriptor rebuilder (au fait les noms des dlls sont mise dans l'it):
015F:00556706 391DFC665500
CMP [nomdllencours],EBX
<-- est ce le même pointeur si oui alors
c la même dlls et c'est la même
structure pour l'id
015F:0055670C 7470
JZ 0055677E
<-- si c différent alors on saute pas
015F:0055670E 60
PUSHAD
<-- et on créé une nouvelle structure
015F:0055670F 891DFC665500
MOV [nomdllencours],EBX
<-- et on met le pointeur de ebx pour
vérifier au prochain passage si on
est tjs dans la même structure
015F:00556715 8B15F4665500
MOV EDX,[importdescriptor]
<-- on récupère le pointeur vers l'import
descriptor
015F:0055671B 893A
MOV [EDX],EDI
<-- et on y met le début de l'iat (les
liens) correspondant
015F:0055671D 812A00004000
SUB DWORD PTR [EDX],00400000
<-- on enlève l'image base
015F:00556723 8305F466550008
ADD DWORD PTR [importdescriptor],08
<-- et on actualise le pointeur de
l'import descriptor
015F:0055672A 8B15F4665500
MOV EDX,[importdescriptor]
<-- que l'on récupère
015F:00556730 C702FFFFFFFF
MOV DWORD PTR [EDX],FFFFFFFF
<-- pour mettre ffffffff
015F:00556736 43
INC EBX
<-- on met ebx au début du nom de la dll
015F:00556737 53
PUSH EBX
<-- et on met sur la pile
015F:00556738 FF35F8665500
PUSH DWORD PTR [lesnoms]
<-- on met le nom de la dll dans le
tableau d'import_by_name (les noms)
015F:0055673E E8B90BA2BF
CALL KERNEL32!lstrcpy
<-- on copie
015F:00556743 FF35F8665500
PUSH DWORD PTR [lesnoms]
<-- on calcul la longueur du nom de la
015F:00556749 E85F0CA2BF
CALL KERNEL32!lstrlen
<-- dll et on ajoute le
015F:0055674E 40
INC EAX
<-- zero de fin pour actualise tout à
l'heure le pointeur vers le tableau
d'import_by_name
015F:0055674F 8B15F8665500
MOV EDX,[lesnoms]
<-- on récupère le pointeur vers le
tableau d'import_by_name (non
actualisé)
015F:00556755 81EA00004000
SUB EDX,00400000
<-- on enlève l'image base
015F:0055675B 8305F466550004
ADD DWORD PTR [importdescriptor],04
<-- on actualise le pointeur vers
l'import descriptor
015F:00556762 8B0DF4665500
MOV ECX,[importdescriptor]
<-- on le récupère
015F:00556768 8911
MOV [ECX],EDX
<-- et on y met le liens vers le noms de
la dll
015F:0055676A 8B59F4
MOV EBX,[ECX-0C]
<-- et on met dans l'id le firstthunk ==
à l'originalfirstthunk
015F:0055676D 895904
MOV [ECX+04],EBX
<-- enfin c'est là qu'on les met ==
015F:00556770 8305F466550008
ADD DWORD PTR [importdescriptor],08
<-- et on actualise le pointeur pour
l'import descriptor qd une nouvelle
strucure se présentera
015F:00556777 0105F8665500
ADD [lesnoms],EAX
<-- et on actualise aussi le pointeur
vers le tableau d'import_by_name
015F:0055677D 61
POPAD
Bien sur au début de la routine il ne faut rien mettre dans [nomdllsencours],
pk? et bien parce qu'au début il n'y a pas encore de structure et
on va comparé ebx à 00 ce qui va obligatoirement faire le
passage dans l'import descriptor builder, donc comme cela on aura une structure
de crée au départ .
Bon maintenant on dump tout ca : pagein d 4b8000 3000 c:\import.dat. On a pris soin de faire le dump auparavant du programme et on colle notre nouvelle table d'import dans le dump (voir précédement). Attention ici je n'avais pas une VA égal au raw offset donc faites attention pour vous repérer dans le programme. Bon une fois que cela est fait il reste encore a changé l'eip (bf27c) et l'import table rva (b8950) qui correspond au début de l'import descriptor.
Maintenant on lance le programme et ..... un beau message d'erreur :
Asprotect API not found! Running in unregistered mode. Hehe, le même
problème que sur amv, une partie du programme est crypté
par clef. Bon pour enlever cette écran un petit bpx messageboxa
et on force le saut qui la précède .
Enfin voilà, maintenant il ne reste plus qu'à cracker
le prog. Maintenant on a un dump avec une vrai table d'import. Bon j'avoue
que cette import rebuilder n'est pas spécialement optimisé
mais bon ... De plus j'ai essayé de vous en expliquez le principe
mais ce n'est pas spécialement facile ...
| VI_Conclusion |
Voilà, cet essai sur le manual unpacking d'asprotect 1.0+ est
fini. Je vous ai présenté différente manière
d'y parvenir, mais à mon avis l'import rebuilder final est le plus
intérressant car il reconstruit véritablement une table d'import.
Bon voici quelques lectures supplèmentaires si vous en avez
le courage :) :
http://assembly.citeweb.net/zip/IAT.htm
Texte très interressant sur le rajout d'une dll dans l'iat
http://christal.citeweb.net/tutor/cdilla.htm
Etude de cdilla por mieux comprendre le call fixer
http://tamambolo.free.fr
Etude de cdilla por mieux comprendre le call fixer
http://www.woodmann.com/tsehp_asprotect105.htm
Le texte de Tsehp sur le dump d'asprotect (dernière version)
http://tsehp.cjb.net/
Pour les textes sur asprotect et cdilla
http://assembly.citeweb.net
Pour les textes sur le patching d'asprotect ...
Je tenais à remercier plusieurs personnes : Christal pour m'avoir
relancé dans cette protection, TeeJi pour avoir accepté d'étudier
cette protection et pour les discussions dessus sur irc, et +Tsehp pour
avoir pris le temps de me répondre et surtout pour avoir trouvé
et mis au point (avec un très bon tut) une première méthode
de dump qui m'a finalement permis d'aboutir à l'import table rebuilder.
Un special greetz à Alexey Solodovnikov qui nous prépare
surement de bien belle surprise pour l'avenir ...
Amicalement,
LuTiN NoIR
(c) 2000 . LuTiN NoIR - city_of_bitch@caramail.com