Protection
   ASProtect  

Ready Made Protection

  Outils
   SoftIce    
  Cible
   Advanced Registry Tracer 1.1 b  

By Christal

Auto-Psy d'une Ready Made Protection

Avant Propos

Depuis une quinzaine, le hasard veut que dès que je m'intéresse à un programme, celui ci soit protégé par la même Ready Made. Je n'ai pas encore identifié avec certutude cette joyeuseté, mais il s'agit très certainement d'ASProtect, le dernier bébé d'Alexey Solodovnik, et vous trouverez ci dessous tout ce qui m'a semblé nécessaire à mieux la connaître. La première fois que j'ai rencontré cette protection, pour Alchemy Laucher 1.0, l'essay que j'ai écrit dans la foulée laissait des zones d'ombres, et surtout n'indiquait pas comment j'avais pu trouver les différentes adresses m'ayant permis d'écrire un patch. Je vais y remédier dans le chapitre Step by Step…
Cette " étude " porte sur 5 programmes : Alchémy Laucher 1.0, le premier de la série, Icon Grabber 2.18, dont je vous engage à lire l'essay qu'a écrit Metallica, Cool Desk 99, Batch Converter, et Assembly Registry Tracer 1.1 b.


Reconnaître cette Ready Made Protection : les Symptômes

Au lancement de la protection vous avez une MessageBox qui surgit annonçant :

Debugger detected !
Please remove it before
start of application.

Si vous lancez FrogIce, vous apprendrez que cette Ready Made cherche à détecter SoftIce pour les versions W98 et NT, par la méthode MetlIce (Code 0B), soit deux beaux écrans bleu qui apparaîtront, pilotés par FrogIce.

=> Art
** SOFTICE DETECTION ** code 0B, at cs:00C8F5F0
Attempting to load: NTICE

=> Art
** SOFTICE DETECTION ** code 0B, at cs:00C8F5FC
Attempting to load: SICE

Allons voir, Merci Frog's Print !

:00C8F65E  A12C34C900          MOV     EAX,[00C9342C]
:00C8F663  50                  PUSH    EAX
:00C8F664  E8734DFFFF          CALL    KERNEL32!_lopen  > ici
:00C8F669  40                  INC     EAX
:00C8F66A  7510                JNZ     00C8F67C
:00C8F66C  6A00                PUSH    00
:00C8F66E  A13034C900          MOV     EAX,[00C93430]
:00C8F673  50                  PUSH    EAX
:00C8F674  E8634DFFFF          CALL    KERNEL32!_lopen  > et ici
:00C8F679  40                  INC     EAX
:00C8F67A  7425                JZ      00C8F6A1

L'écran bleu de FrogIce va apparaître en traçant sur le Kernel32 ! _lopen
Habituellement un Code OB (MeltIce) est écrit ainsi :

00401067:  push      00402025      > \\.\SICE
0040106C:  call      CreateFileA
00401071:  cmp       eax,-001
00401074:  je        00401091

Dans le cas de cette protection, j'aurais tendance à y voir une variante :

00C8F65E   MOV     EAX,[00C9342C]
00C8F663   PUSH    EAX
00C8F664   CALL    KERNEL32!_lopen
00C8F669   INC     EAX
00C8F66A   JNZ     00C8F67C

Au retour de l'API, EAX est incrémenté, et s'il vaut 00, SICE est détecté. Or si SI est présent, la valeur retournée sera -01 !
Une petite interrogation de EAX avant l'appel à l'API va donner :

Pour le premier appel : détection NTICE

:00C8F5EC 5C 5C 2E 5C 4E 54 49 43-45 00 00 00 5C 5C 2E 5C  \\.\NTICE...\\.\
:00C8F5FC 53 49 43 45 00 00 00 00-55 8B EC 83 C4 E4 53 56  SICE....U.....SV

Et pour le second : détection SICE

:00C8F5F8 5C 5C 2E 5C 53 49 43 45-00 00 00 00 55 8B EC 83  \\.\SICE....U...
:00C8F608 C4 E4 53 56 57 33 D2 89-55 E8 89 55 E4 33 D2 55  ..SVW3..U..U.3.U

A la fin du fichier exécutable, un éditeur hexadécimal vous permet de voir :

0.......File  cor
rupted  !...=..De
bugger  detected
!...n...$......_c

Pour finir, une tentative de désassemblage ne vous donnera aucun listing, mais vous obtiendrez quelques informations sur le PE Header, et l'Entry Point de l'application :

+++++++++++++++++++  ASSEMBLY  C0DE  LISTING  ++++++++++++++++++
//**********************  Start  of  Code  in  0bject     **************
Program  Entry  Point  =  004F5000  (ART.exe  Fi1e  0ffset:000F5000)

Une modification des attributs de la première section du PeHeader, couplé avec la présence active de FrogIce, va être détectée et le programme balancera une nouvelle MessageBox " File corrupted ".
Heureusement, cette Ready Made se contente de vérifier l'intégrité des informations du PE Header. Par la suite vous verrez qu'une modification des codes de l'exécutable sera sans conséquence.
La solution qui apparaît la meilleur est alors de placer un CC à la place du Pushad sur l'Entry Point de l'application, et de provoquer un break par un BPINT 3.

Regardons un peu les caractéristiques des sections :

Alchemy Laucher 1.0

.text  00023000 00001000 00012A00 00001000 C0000040 
.rdata 00009000 00024000 00002A00 00013A00 C0000040 
.data  00007000 0002D000 00000E00 00016400 C0000040 
.rsrc  0000E000 00034000 00006200 00017200 C0000040 
.aspr  00013000 00042000 00013000 0001D400 C0000040 > EP
.rsrc  00001000 00055000 00000000 00030400 C0000040

Pour les cinq programmes étudiés, la section .rsrc est en doublon, et pour quatre d'entre eux, la section .aspr est présente. Dans le cas d'Advanced Registry Tracer 1.1b cette section est appelée .tls

Advanced Registry Tracer 1.1b

CODE   00094000 00001000 00040E00 00000400 C0000040
DATA   00003000 00095000 00001000 00041200 C0000040
BSS    00035000 00098000 00000000 00042200 C0000040
.idata 00003000 000CD000 00001000 00042200 C0000040
.tls   00001000 000D0000 00000000 00043200 C0000040
.rdata 00001000 000D1000 00000200 00043200 C0000040
.reloc 0000B000 000D2000 00000000 00043400 C0000040
.rsrc  00018000 000DD000 00005600 00043400 C0000040
.tls   00010000 000F5000 0000F200 00048A00 C0000040 > EP
.rsrc  00001000 00105000 00000000 00057C00 C0000040

Mais dans tous les cas, l'Entry Point de l'application se situe dans l'avant dernière section (.aspr ou .tls suivant le cas), avec une Image Base à 00400000.

Si vous modifiez les caractéristiques de la première section en remplaçant le C0000040 par un E0000020 (l'explication des effets de ce changement se trouvant dans de nombreux essay's, je ne rentrerai pas plus dans les détails), vous aurez un break dans SI.

:0043F001  60                  PUSHAD                > Entry Point
:0043F002  E801000000          CALL      0043F008
:0043F007  90                  NOP
:0043F008  5D                  POP       EBP
:0043F009  81EDE3E54400        SUB       EBP,0044E5E3

Comme il s'agit d'une Ready Made, vous retrouverez toujours ces mêmes codes pour les 5 programmes.

Si vous attaquer un traçage normal (F10), la MessageBox va surgir dès que vous passerez sur le call 0043F008. A la seconde tentative, un Step Into (F8) va vous permettre de découvrir les joies des Codes Changeant d'Apparences (CCA ou codes polymorphes, ou encore OverLapping).

Dans ce type de codage, dont Pulsar en a donné d'excellentes explications, vous devez a priori tracer Step Into tous les call 004XXXXX, et Step Over les call [EBP+00000XXX] qui sont des appels aux API.
Avec un peu de patience et beaucoup de temps, vous allez repérer, au cours du tracing, les différents points de passage du crypteur/compresseur. Nous verrons au fur et à mesure que ces adresses sont toujours placées à la même distance les unes des autres, quelques soit l'application concernée :

Cool Desk 99 :

:0043F4D1  E8C3EB019B          CALL      9B45E099
:0043F4D6  58                  POP       EAX
:0043F4D7  EB01                JMP       0043F4DA
:0043F4D9  E8EB01E968          CALL      692CF6C9
:0043F4DD  6801E06D00          PUSH      006DE001
:0043F4E2  C3                  RET

Hop ! VU !
Un push adresse/ ret n'est ni plus ni moins qu'une simulation de retour sur Call appelant, et en l'occurrence celle ci va faire passer l'application des adresses 004XXXXX à 006XXXXX.
Pour certains programmes, comme Alchemy, l'adresse poussée sera la même, mais dans le cas d'Icon Grabber celle ci vaudra 00C8XXXX. 0043F4DD va donc être un point de passage obligé pour venir y relever la valeur poussée sur la pile.
On notera donc :
EP + 0x04DC = 0043F001 + 0x04DC = 0043F4DD > passe la main aux adresses 6D ou C8.
Et le 4DC se vérifie pour les cinq programmes ciblés…
006DE001 va devenir mon EP2.

Continuons le tracing :

:006DE5C5  61                  POPAD
:006DE5C6  7508                JNZ       006DE5D0
:006DE5C8  B801000000          MOV       EAX,00000001
:006DE5CD  C20C00              RET       000C
:006DE5D0  68600B6D00          PUSH      006D0B60
:006DE5D5  C3                  RET

De nouveau une simulation de retour sur call appelant :
EP2 + 0x0
5D4 = 006DE001 + 0x05D4 = 006DE5D5

Ce calcul n'est plus vrai que pour 4 programmes et il en sera ainsi jusqu'à la fin, ART n'est plus concerné par cette base de calcul, par contre on retrouvera les mêmes codes à une longueur de saut différente.

C'est reparti :

:006DE0FA  50                  PUSH      EAX
:006DE0FB  FF9541294400        CALL      [EBP+00442941]
:006DE101  8D85152C4400        LEA       EAX,[EBP+00442C15]
:006DE107  50                  PUSH      EAX
:006DE108  C3                  RET

Yep !
Cette fois ci c'est un Push Eax/Ret.
Il se trouve à EP2 + 0x0
106.

On continue :
En traçant sur un call 006CFFF0 en 006D058C, la MessageBox " Debugger Detected " va surgir.
Nous sommes à EP2 - 0xDA75.

Entrons dans ce call :

:006CFFF0  55                  PUSH      EBP       > à remplacer par un C3
:006CFFF1  8BEC                MOV       EBP,ESP
:006CFFF3  83C4F8              ADD       ESP,-08
:006CFFF6  8955F8              MOV       [EBP-08],EDX
:006CFFF9  8945FC              MOV       [EBP-04],EAX
:006CFFFC  81C4C8000000        ADD       ESP,000000C8
:006D0002  B8EB436C00          MOV       EAX,006C43EB
:006D0007  40                  INC       EAX
:006D0008  6AFF                PUSH      FF
:006D000A  BB7D436C00          MOV       EBX,006C437D
:006D000F  4B                  DEC       EBX
:006D0010  6830200000          PUSH      00002030
:006D0015  FF75FC              PUSH      DWORD PTR [EBP-04]
:006D0018  FF75F8              PUSH      DWORD PTR [EBP-08]
:006D001B  6A00                PUSH      00
:006D001D  53                  PUSH      EBX
:006D001E  50                  PUSH      EAX
:006D001F  C3                  RET

Une fois de plus on trouve un Push Eax/Ret, et c'est dans cette routine que les informations nécessaires à l'affichage de la MessageBox vont être initialisées, avant de renvoyer vers un Exit Process.
Suivant vos préférences, vous pouvez aussi bien ajouter 5 à l'EIP en EP2 + DA75, pour " sauter " le call Sice_détecté, ou placer un Ret au tout début de la routine embarrassante.

Dernière ligne droite, le passage de relais de la Ready Made Protection au programme d'origine :

:006D04C6  8BC0                MOV       EAX,EAX
:006D04C8  89C4                MOV       ESP,EAX
:006D04CA  89D0                MOV       EAX,EDX
:006D04CC  8B1D34566D00        MOV       EBX,[006D5634]
:006D04D2  89041C              MOV       [EBX+ESP],EAX
:006D04D5  61                  POPAD
:006D04D6  50                  PUSH      EAX
:006D04D7  C3                  RET

Il va de soit qu'à chaque fois que vous rencontrez un Push Eax/Ret, vous relevez la valeur de EAX. Dans ce cas précis, cette valeur renvoi vers une adresse 004XXXXX et vous avez un POPAD (caractéristique !, on restaure les registres…) au dessus. Si vous continuez le traçage, vous aurez un classique démarrage de programme, avec l'abandon du codage CCA. EAX contient donc l'OEP (Original Entry Point), à EP2 - 0x34D5.

Avec ces informations, vous avez de quoi réaliser un Dump Memory de l'application, en remplaçant le push eax/ret par un jmp EIP. Resteque le dump obtenu ne va pas être excutable. Nous verrons comment faire dans un instant.
Mieux, j'estime disposer de suffisamment d'éléments pour envisager la réalisation d'un Unpacker, sachant que celui ci devra :

- Relever l'adresse de l'Entry Point du programme crypté en cherchant dans les codes de l'exe les informations sur celui ci qui sont situées de l'offset 128 à l'offset 12B

Advanced Back Converter   Entry Point: 004DB001
00000100 5045 0000 4C01 0A00 195E 422A 0000 0000 PE..L....^B*....
00000110 0000 0000 E000 8E81 0B01 0219 0024 0A00 .............$..
00000120 0030 0300 0000 0000 01B0 0D00 0010 0000 .0..............

Quick Icon Grabber        Entry Point: 004ED001 
00000100 5045 0000 4C01 0A00 195E 422A 0000 0000 PE..L....^B*....
00000110 0000 0000 E000 8E81 0B01 0219 0092 0900 ................
00000120 00D6 0400 0000 0000 01D0 0E00 0010 0000 ................

Advanced Registry Tracer  Entry Point: 004F5000
00000100 5045 0000 4C01 0A00 195E 422A 0000 0000 PE..L....^B*....
00000110 0000 0000 E000 8E81 0B01 0219 0038 0900 .............8..
00000120 0072 0200 0000 0000 0050 0F00 0010 0000 .r.......P...... 

- Lui ajouter 0x04DC pour pourvoir récupérer l'adresse de la seconde partie du décryptage (EP2) poussé sur la pile
- Retrancher 0xDA75 à EP2 pour arriver sur le call Sice_détecté, ou poser un bpx _lopen, puis tracer pour trouver le call
- Ajouter 5 à l'EIP pour sauter ce call
- Retrancher 0x34D6 à EP2 pour relever la valeur de l'OEP dans EAX, ou rechercher la signature 89 04 1C 61 50 C3
- Réaliser un dump mémory de l'application (voir les indications ci dessous)
- Placer la nouvelle valeur de l'OEP

Mais c'est une autre histoire, et j'espère que j'aurais l'occasion de vous en reparler.
A ce propos, j'encourage les codeurs à se pencher sur cet exercice, en vue de la confrontation des techniques utilisées, et qui pourraient être développées dans un prochain essay.
Eventuellement vous pouvez vous inspirer de l'UN-Peshield codé par Pulsar.

Decompression advanced registry tracer 1.11. (courtesy Of SV)

Le but est, encore une fois, d'avoir un dump correct de l'exe et donc d'avoir une section import valide, ce qui n'est pas le cas pour le moment.

Voyons les infos contenues dans les sections de art.exe:
On a une section .idata en CD000 (virtual offset) et donc en 4CD000 theoriquement.

En general c'est une section import qui a ce nom la.
Par un break sur GetProcAddress on peut d'ailleurs verifier que c'est ici que ca se passe.

bpx getprocaddress

015F:00C8F3C7  FF5518              CALL      [EBP+18] <-----------GetProcAddress
015F:00C8F3CA  8945F4              MOV       [EBP-0C],EAX 
015F:00C8F3CD  837DF400            CMP       DWORD PTR [EBP-0C],00              
015F:00C8F3D1  7559                JNZ       00C8F42C                           
015F:00C8F3D3  84DB                TEST      BL,BL                              
015F:00C8F3D5  743F                JZ        00C8F416                           
015F:00C8F3D7  52                  PUSH      EDX                                
015F:00C8F3D8  53                  PUSH      EBX                                
015F:00C8F3D9  57                  PUSH      EDI

d 4cd000 et on vois une table d'import nickel :-)
Plus loin en tracant on peut voir :

015F:00C8F4A2  2BCA                SUB       ECX,EDX
015F:00C8F4A4  03052434C900        ADD       EAX,[00C93424]
015F:00C8F4AA  8908                MOV       [EAX],ECX                          
015F:00C8F4AC  8B45D8              MOV       EAX,[EBP-28]                       
015F:00C8F4AF  8B55E8              MOV       EDX,[EBP-18]                       
015F:00C8F4B2  8910                MOV       [EAX],EDX <--------- a noper
015F:00C8F4B4  EB08                JMP       00C8F4BE                           
015F:00C8F4B6  8B45D8              MOV       EAX,[EBP-28]                       
015F:00C8F4B9  8B55F4              MOV       EDX,[EBP-0C]                       
015F:00C8F4BC  8910                MOV       [EAX],EDX <--------- a noper
015F:00C8F4BE  83C604              ADD       ESI,04                             
015F:00C8F4C1  8345D804            ADD       DWORD PTR [EBP-28],04 

En 00C8F4B2 et 00C8F4BC, le programme ecrase la table et donc c'est ici qu'il faut intervenir pour noper ces 2 instructions. Il ne reste plus ensuite qu'à se caller sur le Push eax/Ret pour le remplacer par un jmp Eip et réaliser un dump avec ProcDump (ATTENTION : Option 'Don't Rebuild Import' puisque on a laisse l'import tel quel) et le résultat obtenu sera xécutable!

Une autre solution, proposée par el.CaRaCoL est de Partir du principe que le loader d'Asprotect doit décompresser les données (qu'il a lui-même compressé) et restituer la section idata telle qu'elle était à l'origine à un moment donné ou un autre, il va bien être obligé d'écrire les bytes à leur emplacement d'origine !
c.a.d à partir de l'adresse Virtual Offset +lmagebase (appelons le 0044XXXX)

Avec un peu de chance, et surtout un BPM 44XXXX W on devrait pouvoir être averti de la restauration de la section idata.
Par expérience, et pour avoir déjà étudié les précédente versions d'Aspack , (et si le principe n'a pas changé depuis), je sais qu'il va lire les données compressées, les décompresser dans une zone mémoire tampon, puis copier la zone décompressée vers sa destination d'origine (c'est à dire 0044XXXX)
Et cela, il va le faire autant de fois qu'il y a eu de couches de compression.
Il ne nous restera plus qu'à observer le contenu de la zone en 0044XXXX à chaque break, jusqu'à ce qu'elle soit propre.
Chaque break devrait donc correspondre à une couche de compression.
On peut ainsi savoir en quelques secondes combien de couches ont été appliquées sur un programme.

Phase 1 : dump manuel de l'Import Table lorsque celle-ci est " clean "

Il nous faut placer un bpm write à l'adresse ds : 0044XXXX, on va s'arranger pour qu'il y ait un break sous softIce avant que le loader n'ait commencé à décompresser les données, et en profiter pour poser le bpm.
Voici la procédure en pas à pas. (c'est long à lire, mais dans la réalité çà va très vite !)

CRTL+D pour basculer dans Sice
BPX getprocaddress ou BPX getversionexa (bpx getprocaddress est suffisant ici)
X ou F5 pour revenir à Winxx et lancer la cible
POP ! on a un break !
F12 jusqu'à arriver dans le code d'Asprotect.
On désactive le bpx par BD * et on pose notre collet :
BPM ds : 0044XXXX W
(on peut vérifier que la section idata n'a pas encore été touchée en faisant un D 0044XXXX : il n'y a que des ? ? ? ?)
X ou F5 pour relancer le travail du loader Aspack
POP ! on a un 1er break sur un accès write dans idata en 0044XXXX
On est ici dans le code :

Break due to BPMB #017F:00446000 W DR3 
0177:00C0268F  F3A5                REPZ MOVS        <-- ICI
0177:00C02691  89C1                MOV     ECX,EAX
0177:00C02693  83E103              AND     ECX,03
0177:00C02696  F3A4                REPZ MOVSB       <-- copie le reste de la zone
0177:00C02698  5F                  POP     EDI
0177:00C02699  5E                  POP     ESI
0177:00C0269A  C3                  RET

On trace par F10 jusqu'à l' exécution du 2ème movsb pour terminer la copie de tous les bytes vers la section idata

Un rapide coup d'œil sur la fenêtre des datas et l'on voit une image import descriptor qui ressemble à quelque chose de correct.

On fait quand même défiler la fenêtre des datas pour parcourir l'import table jusqu'à la fin des noms de fonctions, et notre œil maintenant surentraîné nous confirme en quelques secondes que tout baigne !

Il n'y aurait donc qu'une seule couche de compression sur la section idata. (la version 108.3 n'avait également qu'une seule couche).

Maintenant on va faire un dump de la mémoire de 0044XXXX à 0044XXXX+2000 ( 2000 bytes de Virtual Size )
Soit vous faites le dump à l'aide d'IceDump et vous passez ensuite à la phase 2 (dump manuel du programme) soit vous utilisez Procdump et il faut pour cela " geler " Asprotect de la manière suivante :

A  EIP                                                assemble à partir de l'adresse courante	
0177:00C02698  jmp eip             pour boucler sur la même instruction

puis X ou F5

ensuite à l'aide de Procdump, sélectionner la cible dans la liste des tâches, clic droit, et faire un dump partiel de 2000 bytes à partir de 0044XXXX. Sauvegarder sous " ImpData_Asp.dmp " (par exemple). On n'oublie pas le Kill du task Cible.
(Le fichier ainsi sauvegardé correspond à celui qui a servi de support pour les exemples dans ce tutoriel : détail d'une import table de type Borland et exercice de reconnaissance visuelle)

Il est temps de passer à la phase 2.

Phase 2 : dump manuel du programme décompressé avant son lancement


Bien entendu, pour pouvoir effectuer cela, il faut savoir à quel endroit asprotect a terminé son travail et passe le relais au programme décompressé.
La technique de recherche rapide que j'ai utilisé dans ce cas précis : une technique " visuelle " (un peu " bestiale " je dois le reconnaître) :
On relance la cible et comme le " bpm 0044XXXX w " est resté actif, on va avoir un break sur l'accès à la section idata. C'est notre point de départ de la recherche.
On trace rapidement avec F10 sans chercher à comprendre le code qui s'exécute, mais en comptant le nombre d'appuis et en regardant " l'image " formée par le code, jusqu'au lancement d'Asprotect.
(Cela arrive relativement vite, car il ne reste plus beaucoup de sections à décompresser)
Il ne reste plus qu'à relancer Asprotect, break, et tracer rapidement nfois avec F10 pour arriver sur le dernier call qui lance l'application.


Une fois le call repéré ( en 0177:00C1151F pour moi ),

0177:00C1151F  E874F9FFFF          CALL    00C10E98 <-- lance la cible
0177:00C11524  5F                  POP     EDI
0177:00C11525  5E                  POP     ESI
0177:00C11526  5B                  POP     EBX
0177:00C11527  59                  POP     ECX
0177:00C11528  59                  POP     ECX
0177:00C11529  5D                  POP     EBP
0177:00C1152A  C20400              RET     0004

il suffit de tracer avec F8 pour arriver ici :

0177:00C10E98  89C4                MOV     ESP,EAX
0177:00C10E9A  89D0                MOV     EAX,EDX
0177:00C10E9C  8B1D6C66C100        MOV     EBX,[00C1666C]
0177:00C10EA2  89041C              MOV     [EBX+ESP],EAX
0177:00C10EA5  61                  POPAD		<-- restaure registres
0177:00C10EA6  50                  PUSH    EAX	<-- push entry point
0177:00C10EA7  C3                  RET			<-- call entry point

Je ne détaille pas davantage, c'est du déjà vu.
Dérouler le programme jusqu'au RET, notez l'adresse indiquée dans le registre EAX qui correspond à l'Entry Point du programme (0044295C pour moi)
Et comme précédemment :

A EIP assemble à partir de l'adresse courante
0177:00C10EA7 jmp eip pour boucler sur la même instruction

Désactiver tous les breakpoints par 'BD*'
puis X ou F5

ensuite à l'aide de Procdump ( toujours lui ), sélectionner la cible dans la liste des process, clic droit et faire un dump Full.

Attention :
avant d'effectuer le Dump, je vous conseille de modifier les options de la manière suivante :

- recompute object 	(oui)
- optimize                      (non) afin d'obtenir les raw offset = aux virtual offset (c'est plus facile à lire par la suite)
- rebuild Import table ou  don't rebuild import table ? Cela n'a pas d'importance 

Sauvegarder le dump full sous " cible_upk.exe" puis kill du process.

On peut passer à la phase 3

Phase 3 : insertion du fichier dump de l'Import Table dans le programme décompressé

Nous avons donc un exécutable décompressé, mais avec une section idata pas good !
Maintenant que vous êtes familier de l'Import Table vous pouvez jeter un œil sur l'exécutable avec Hexworks30 pour vous apercevoir rapidement qu'il y a quelque chose qui cloche…
En effet ! Si la table à l'air correcte au premier abord, dès que l'on parcourt visuellement l'import address table on s'aperçoit vite qu'il y a des adresses différentes des autres : il y a des pointeurs non pas vers les noms de fonctions, mais vers 00C3xxxx (c'est à dire en dehors de notre zone).
Et cela concerne les fonctions de USER32.DLL et KERNEL32.DLL
(Si vous avez choisi l'option " rebuid import table ", une partie des pointeurs est correcte)
Si vous voulez savoir qui, quand, où et comment cela a été fait, il suffit de placer un BPR 0044XXXX 447FFF W juste après le break sur accès à la section idata. Relancer puis attendre le break révélateur. Il n'y a plus qu'à tracer pour comprendre ce qu'il se passe…

Dans Hexworks30 :
- Charger le fichier dump " ImpData_Aspk.dmp " ainsi que le programme décompressé " Cible_upk.exe".
- Faire un edit / goto 4XXXX pour vous positionner sur le 1er byte de la section idata.
0044XXXX : parce que le raw offset est identique au virtual offset (grâce au choix de nos options lors du dump)
- Sélectionner le block , size = 2000 (en hexa) puis delete
- Dans le fichier " ImpData_Asp.dmp " faites la même chose pour sélectionner le bloc de 2000 bytes puis 'copy'
- Activer la fenêtre " Cible_upk.exe" puis 'paste'.

Et voilà le programme Asprotect décompressé, avec une import table tout propre, comme à son origine.
Il ne reste plus qu'à effectuer les finitions, c'est à dire mettre les bonnes valeurs dans le PE header et PE directory.

Phase 4 : mise à jour de l'entry point et des adresses dans la directory Import table

Avec Procdump :
· Mettre la valeur de l'entry point du programme décompressé.
pour moi : 0044295C - 00400000 = 0004295C (valeur relevée dans eax - image de base)
· Modifier les caractéristiques de la section .CODE en " E0…..20 " afin d'obtenir le listing désassemblé avec Wdasm

· Modifier dans Directory Import Table la valeur pour pointer vers le début de l'image import descriptor = 4XXXX Modifier la size = 012C (pour connaître la valeur, il suffit de regarder le fichier dump et repérer la fin de l'image import descriptor)

Si vous ne vous êtes pas trompés en route, vous avez maintenant un dump de programme non seulement désassemblable avec les import functions, mais en plus il est exécutable.
Il n'y a plus de détection de SoftIce, puisque celle-ci était prise en charge par le loader Asprotect…
Vous pouvez maintenant corriger d'éventuels bugs sur l'application…



Seconde Partie :

Advanced Registry Tracer 1.1 b

Réalisation d'un patch Step by Step :


Dans le cas de programmes protégés de cette façon, la réalisation d'un script est assez inefficace (bien que certainement faisable), et un Mémory Dumper comme R !sc Process Patcher semble ne pas se retrouver dans les méandres d'un code polymorphe.

Reste le patch manuel.
Celui ci n'est pas facilité par le fait que la Ready Made Protection va décompresser/décrypter l'application par petits morceaux, et qu'il va falloir isoler les uns après les autres.

Pour commencer, il faut connaître l'adresse à patcher dans le programme d'origine. Il serait trivial ici (j'adore quand les Ricains utilisent cette expression…) de détailler la façon dont je m'y suis pris pour trouver l'octet que je souhaite modifier dans ART en 00489D0C, mais quoi qu'il en soit, il va falloir commencer par trouver dans quelle routine et à quel moment l'adresse 00489D0C est décompressée.

:00489CFA 8B45F4                  mov eax, dword ptr [ebp-0C]
:00489CFD 8B14857C5E4900          mov edx, dword ptr [4*eax+00495E7C]
:00489D04 8B45F0                  mov eax, dword ptr [ebp-10]
:00489D07 E82CEAF7FF              call 00408738
:00489D0C 85C0                    test eax, eax  à remplacer par 33 C0
:00489D0E 7506                    jne 00489D16
:00489D10 C645FB01                mov [ebp-05], 01
:00489D14 EB0C                    jmp 00489D22

Pour ce genre de question, la réponse est un BPM 00489D0C…
Dés que vous obtenez un break, il va falloir chercher ensuite, en traçant ave F10 si besoin est, un espace de 5 octets minimum (longueur nécessaire pour écrire un Jmp XXXXXXXX). En sortant de deux petits calls, j'ai opté pour créer un branchement en 00C8F2E7 à la place d'un Lea Eax, [Ebp-0C] (3 octets) et du Mov Ebx [Esi+04] (3 octets) suivant.
A nouveau j'ai placé un Bpm, sur 00C8F2E7 cette fois ci, pour trouver la routine décompressant cette adresse. PoP !
Break en 00C9C3C3 sur un classique de la décompression, l'instruction Repz Movsb. Quelques lignes plus bas un Push 00008000 va parfaitement me convenir…

:00C9C3C3  F3A5                REPZ MOVSD  > décompression
:00C9C3C5  8BC8                MOV     ECX,EAX
:00C9C3C7  83E103              AND     ECX,03
:00C9C3CA  F3A4                REPZ MOVSB
:00C9C3CC  5E                  POP     ESI
:00C9C3CD  8B8535194400        MOV     EAX,[EBP+00441935]
:00C9C3D3  6800800000          PUSH 00008000  > 5 octets. Parfait !
:00C9C3D8  6A00                PUSH    00
:00C9C3DA  50                  PUSH    EAX
:00C9C3DB  FF953D194400        CALL    [EBP+0044193D]

Et ainsi de suite. 00C9C3DA est décompressé en 00C9C0E7, avec de nouveau un Push 00008000 pas loin. Cette procédure est elle-même décompressée en 004F530F avec un JMP 004F51F8 quelques lignes plus basses, et finalement cette dernière adresse est décompressée en 004F50AC (et encore une fois avec un Push 00008000 à proximité) dans une zone clean, c'est à dire non-compressée et accessible par un éditeur hexadécimal. OUF !

Reste à trouver de la place pour loger ces 5 Patchs…
Une solution assez empirique, mais qui marche souvent, consiste à rechercher de la place disponible (des séries de Add Byte Ptr) avant que le crypteur ne passe le relais au programme d'origine (bien qu'il me soit arrivé de voir des décompresseurs utiliser deux fois le même espace…), de poser un BPM adresse W ( ou BPR adresse adresse+lg présumée du patch RW) pour vérifier que le programme n'écrira rien dans la zone convoitée, et de se l'attribuer…
Entre les patchs d'Alchemy (EP + 0x0F40), de Icon Grabber (EP + 0x10A0 -> mais sur l'entête de l'icône de l'application) ou de ART (EP + 0xA6), vous avez une idée des adresses où vous seriez susceptible de pouvoir implanter un patch…

Une fois que vous aurez en tête, ou sur le papier, la " gueule " du patch à écrire, vous pouvez commencer par l'écrire en mémoire à l'adresse repérée. C'est la phase test….
En général, à ce stade, je me contente de réécrire les octets que j'envisage de remplacer, juste histoire de voire si le principe va fonctionner. Si c'est OK en mémoire (c'est à dire si le programme ne plante pas, et que visiblement rien n'a bougé), je peux passer à la phase suivante, et détourner le programme !

:004F50A6  8BB5A8214500        MOV     ESI,[EBP+004521A8]
:004F50AC  F3A4                REPZ MOVSB
:004F50AE  8B85A8214500        MOV     EAX,[EBP+004521A8]
:004F50B4  E9DB110000          JMP     004F6294  > saut vers patch 1
:004F50B9  6A00                PUSH    00

Puis je vérifie que l'adresse que je vise est bien décompressée (par un d adresse_visée), et je trace le patch écrit précédemment, qui modifie un Push 00008000 en JMP 004F62AF (patch 2)

patch 1
:004F6294  C70514534F00E9960F00MOV     DWORD PTR [004F5314],000F96E9 > implantation
:004F629E  C60518534F0000      MOV     BYTE PTR [004F5318],00        > du Jump Patch 2
:004F62A5  6800800000          PUSH    00008000   > restauration des octets modifiés
:004F62AA  E90AEEFFFF          JMP     004F50B9   > le programme continue sa route

Un BPM 004F5314 X va me permettre de m'assurer que tout va bien :

Result of ptach 1
:004F52FF  58                  POP     EAX
:004F5300  EB01                JMP     004F5303
:004F5302  E8EB01E98B          CALL    8C3854F2
:004F5307  741A                JZ      004F5323
:004F5309  1403                ADC     AL,03
:004F530B  F2C1F902            REPNZ SAR ECX,02
:004F530F  F3A5                REPZ MOVSD
:004F5311  83C328              ADD     EBX,28
:004F5314  E9960F0000          JMP     004F62AF  > jump patch 2
:004F5319  8B85A8214500        MOV     EAX,[EBP+004521A8]
:004F531F  50                  PUSH    EAX

Un d C9C0EF va permettre de se rendre compte que l'adresse du jump_vers_patch_3 n'est pas encore décompressée. Il y a donc plusieurs passages dans la routine ci dessus avant que l'adresse convoitée ne soit disponible. Pour éviter un crash fatal, il va falloir tester la présence en 00C90EF de l'octet-test qui nous garantira que nous pouvons procéder à la modification :

patch 2
:004F62AF  803DEFC0C90068      CMP     BYTE PTR [00C9C0EF],68 > zone décompressée
:004F62B6  7511                JNZ     004F62C9               > Non -> saute
:004F62B8  C705EFC0C900E9DAA185MOV     DWORD PTR [00C9C0EF],85A1DAE9 > oui
:004F62C2  C605F3C0C900FF      MOV     BYTE PTR [00C9C0F3],FF > modification 
:004F62C9  E92AEFFFFF          JMP     004F51F8 > reprend le cours normal des choses

Ici, il n'y a pas lieu de restaurer d'octets écrasés puisque j'ai remplacé un jump par un autre. Celui qui clôture la fin du patch_2 est le même que celui qui se trouvait en 00C90EF.
Après avoir tracé ces lignes et posé un BPM 00C90EF X, allez contrôler la validité de vos modifications :

Result of patch 2
:00C9C0DB  8DBD051A4400        LEA     EDI,[EBP+00441A05]
:00C9C0E1  8BB535194400        MOV     ESI,[EBP+00441935]
:00C9C0E7  F3A4                REPZ MOVSB
:00C9C0E9  8B8535194400        MOV     EAX,[EBP+00441935]
:00C9C0EF  E9DAA185FF          JMP     004F62CE  > jump patch 3
:00C9C0F4  6A00                PUSH    00
:00C9C0F6  50                  PUSH    EAX

et ainsi de suite pour chacun des différents patchs à écrire :

patch 3
:004F62CE  C705D3C3C900E9119F85MOV     DWORD PTR [00C9C3D3],859F11E9
:004F62D8  C605D7C3C900FF      MOV     BYTE PTR [00C9C3D7],FF
:004F62DF  6800800000          PUSH    00008000
:004F62E4  E90B5E7A00          JMP     00C9C0F4

Result of patch 3
:00C9C3C3  F3A5                REPZ MOVSD
:00C9C3C5  8BC8                MOV     ECX,EAX
:00C9C3C7  83E103              AND     ECX,03
:00C9C3CA  F3A4                REPZ MOVSB
:00C9C3CC  5E                  POP     ESI
:00C9C3CD  8B8535194400        MOV     EAX,[EBP+00441935]
:00C9C3D3  E9119F85FF          JMP     004F62E9  > jump patch 4
:00C9C3D8  6A00                PUSH    00
:00C9C3DA  50                  PUSH    EAX
:00C9C3DB  FF953D194400        CALL    [EBP+0044193D]

Arrivé au patch 4, vous allez commencer par vous débarrasser du call Sice_détecté, en remplaçant le 5A par un C3 (opcode pour Ret), restaurer les octets écrasés, et préparer la saut_vers_patch_5

patch 4
:004F62E9  C605E82CC800C3      MOV     BYTE PTR [00C82CE8],C3   > anti-anti-Sice
:004F62F0  6800800000          PUSH    00008000                 > restauration
:004F62F5  C705E7F2C800E9217086MOV     DWORD PTR [00C8F2E7],867021E9
:004F62FF  66C705EBF2C800FF90  MOV     WORD PTR [00C8F2EB],90FF > jump patch 5
:004F6308  E9CB607A00          JMP     00C9C3D8                 > retour

Si vous vous en rappelez, la modification que j'envisage de faire en 00C8F2E7 n'est que de 5 octets (E9 21 70 86 FF), mais que je prends la place d'une combinaison de 6 octets. Pour éviter de déséquilibrer les codes de l'application, j'ai rajouté 90 (opcode pour NOP) comme 6ème octet.

Result of patch 4
:00C8F2E4  FF550C              CALL    [EBP+0C]
:00C8F2E7  E9217086FF          JMP     004F630D > jump to patch 5
:00C8F2EC  90                  NOP              > octet de rééquilibrage
:00C8F2ED  FF5518              CALL    [EBP+18]
:00C8F2F0  83C60C              ADD     ESI,0C
:00C8F2F3  837E0400            CMP     DWORD PTR [ESI+04],00
:00C8F2F7  0F8535FFFFFF        JNZ     00C8F232
:00C8F2FD  837DF000            CMP     DWORD PTR [EBP-10],00
:00C8F301  7408                JZ      00C8F30B

Et enfin, nous arrivons au patch qui va permettre de faire de nous des utilisateurs enregistrés en remplaçant un 85 par un 33

patch 5
:004F630D  C6050C9D480033      MOV     BYTE PTR [00489D0C],33 > patch
:004F6314  8D45F4              LEA     EAX,[EBP-0C] > restauration des octets
:004F6317  8B5604              MOV     EDX,[ESI+04] > modifié par le jmp to patch5
:004F631A  E9CE8F7900          JMP     00C8F2ED     > et on continue …

Vous voici avec une version d'Advanced Registry Tracer agréablement relookée…

Il reste que cette Ready Made Protection, qui semble se généraliser actuellement, mériterait qu'on lui crée un Unpacker. J'espère que nous n'en resterons pas là…

Bonne Journée
Christal