Protection
   Compression/Cryption  

Les Anti SOftIce

  Outils
   SoftIce Wdasm ProcDump    
  Cible
   Alchemy Laucher 1.0  

By Christal

Une fois de plus, le texte que vous allez lire ne va vous expliquer que la solution pour contourner une protection, sans apporter une réelle méthode, ou approfondir les différentes sources des infections rencontrées. En fait, je me rends compte qu'il n'est pas toujours facile d'être à la fois suffisamment généraliste, tout en restant concret. Cet essai est hautement critiquable, et j'accepterai avec plaisir tous commentaires que vous pourriez faire, la manière dont je m'y suis pris relevant beaucoup de l'empirisme.

Infections:

Programme compressé
Overlapping du code (ce que Pulsar appelle un CCA)
2 Anti SoftIce de code 0B, signalés au lancement de l'application par une Message Box
Période d'essai limité à 30 Jours, mais sans test de fin de période
UNREGISTERED affiché en face de "Registered to" dans la boite About
Un nag "Please Register" s'affichant à chaque ouverture d'un fichier


How To:

Le premier Bug que j'ai cherché à traiter est l'Anti SoftIce.
A l'aide de FrogIce version 4, j'ai pu savoir immédiatement qu'il s'agissait de Codes 0B, en 006D04E0 et 006D04EC. Voici les grandes lignes du Code 0B: (extrait de la doc de Frog's Print)

Cette méthode de détection est mieux connue sous le nom de 'MeltICE', et peut s'écrire ainsi:

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

FrogsICE retournera "detection at cs:00402025" (dans ce cas de figure)

Et le breakpoint à poser est le suivant:
BPX CreateFileA if *(esp->4+4)=='SICE' || *(esp->4+4)=='SIWV' || *(esp->4+4)=='NTIC'

Sans avoir insisté longtemps, je n'ai pas mis la main sur une routine de ce type, ni sur les adresses retournées par FrogIce (perdues dans le code polymorphe de l'application).

Changement de méthode, pour chercher où était logé le message signalant la détection AntiSice.

L' overlapping:

Pour commencer, SoftIce n'a pas marqué de break sur l'entry point de l'application, et il a fallu changer les caractéristiques de la première section,

.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
.rsrc        00001000   00055000   00000000   00030400  C0000040

en modifiant C0000040 par E0000020 (pour rendre le code Readable, Writeable et Executable).

A partir de là, SoftIce va pouvoir prendre la main, et le traçage dans le code va commencer. En regardant les premières lignes du code, il est facile de deviner que celui ci est compressé ou crypté:

:00442001  60                  PUSHAD                 > entry point
:00442002  E801000000          CALL      00442008
:00442007  90                  NOP
:00442008  5D                  POP       EBP
:00442009  81EDE3E54400        SUB       EBP,0044E5E3

Une application qui commence par PUSHAD (sauvegarde des registres) est en général cryptée/compressée (toujours ?).
En commençant le tracing F10, le message AntiSice va apparaître immédiatement après avoir vu le code se modifier au fur et à mesure de la lecture des codes. Voici un extrait d'un texte de Pulsar, disponible sur http://perso.libertysurf.fr/cets/antideb.zip (avec le code source d'un petit exemple, également fourni, qui vous permettra de mieux comprendre le fonctionnement de ce type de Codage).


" Disons qu'on veuille que le code dans softice se modifie lorsque vous allez tracer (certains appelle ca du code polymorphe mais je préfère garder ce terme pour une encryption à chaque fois différente avec des ajouts de codes bidons comme les virus polymorphes, et que je vais l'appeler Code Changeant d'Apparence ou CCA :)

Softice désassemble une instructions d'offset EIP (disons que le nombre de bytes qu'elle fait est appellé NBI), puis l'instructions positionnée a EIP+NBI....
Il est donc assez facile a faire du CCA.
Imaginons la séquence suivante:

jmp @1
db E8h
@1:
mov eax,[eax]
inc eax
inc edx

En mémoire vous aurez quelque chose comme

EB01
E8
8B004042

mais E8 est le byte qui commence un opcode du style "call offset" donc ce code, s'il est désassemblé par adresse croissante donnera

EB01 jmp @1
E88B004042 call addresse

Lorsque vous tracerez en sautant le jump, vous tombez au milieu de l'opcode du call, et softice re-désassemblera cette instruction ce qui donnera l'impression que le code change d'apparence -> CCA

Vous avez sûrement compris qu'il est donc très facile de faire ce style de code en ajoutant différents types de bytes. Au lieu de E8h on peut ajouter 0CDh,020h qui correspond à une int 20h -> VMMJump qui utilise le DWORD suivant l'opcode... et SoftIce affichera donc un truc du style:

int 20 VXDJump XXXX,XXXX
avec XXXX,XXXX la valeur contenue par le DWORD suivant l'int 20.... "


Au cas ou vous ne l'auriez pas compris, ce type de codage est particulièrement long, pénible, et fatiguant à tracer (pour ne pas dire ….), et demande à être tracé TRES doucement, puisque vous ne voyez pratiquement jamais quelle va être la VRAIE instruction suivante.
Pour avoir déjà rencontré ce type de codage, a peu prés TOUS les Call adresse doivent être tracé Step Into (F8). Pour mieux comprendre, je vous recommande de tracer le fichier/exemple que Pulsar a écrit, et mis dans son ZIP.

Un autre inconvénient majeur des CCA, et la difficulté de poser des breakpoints de type BPX, SoftIce n'acceptant pas de poser ses INT3 au milieu d'un code. Les BPM adresse X donnent de bons résultats, mais sont limités à quatre .

En 33 BPM X et une bonne heure de traçage, j'ai fini par mettre la main sur la routine envoyant vers la message box:

:006CFFF0  55            PUSH      EBP     > début de la routine
: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    > pousse une adresse sur la pile
:006D001F  C3            RET              > pour simuler un retour sur call appelant

En fait, cette routine va contenir le message de la boite "Debuggeur detected", pousser l'adresse d'affichage de la message box, qui après un clic sur le bouton "OK" va provoquer un Exit Process.

Dans la mesure ou le programme agit par PUSH EAX/RET, pour aller se promener ailleurs et fermer l'application, il y avait fort à parier qu'un RET en début de routine (qui alors retournerait au VRAI call appelant) permettrait de se débarrasser de la nuisance apportée par ce Bug…

Vous l'ayant dit, Sice n'acceptant pas plus de quatre BPMs, j'ai cherché à réduire à 3 le nombre de breaks indispensables pour me rendre à la routine ci dessus. En effet, elle n'est décryptée que dans un cycle de 3 autres décryptions préalables, et pour pouvoir obtenir un break avec les BPM, il faut que cette routine soit clean.

Sur le principe du Switch, et par élimination, j'ai isolé les trois adresses maximum que je m'étais fixées:


004420BC ->
décryptage de la zone allant jusqu'en 0044233B
0044233B ->
décryptage de la zone allant jusqu'en 006DE78B
006DE78B ->
décryptage de la routine provoquant l'Exit Process

Le Crypteur utilisé:

La majorité des utilitaires de Cryptage "ready made" laisse un copyright quelque part dans les codes de l'exécutable (et souvent au début ou à la fin). Cette fois ci, je n'en ai trouvé aucun, en dehors de l'hypothèse que la section "aspr" pouvait être une abréviation pour ASPack. Ayant déjà joué avec ASPack, et n'ayant pas reconnu le principe de cryptage des versions que je connaissais, EL Raiser a supposé qu'il pouvait s'agir de la dernière version de ce crypteur. Des informations concernant la version 1.08.04 ont été développées par TaMaMBoLo dans son Tutor8, et dans le script de la version 1.6 de ProcDump, en indiquant la signature de cette version d'ASPack:

ProcDump
L1=OBJR
L2=LOOK ?,C3
L3=JZ 5
L4=QUIT
L5=BP
L6=OBJR
L7=LOOK 5B,0B,DB
L8=BP
L9=OBJR
LA=LOOK C3
LB=BP
TaMaMBoLo
L1=LOOK 6A,00,50
L2=BP
L3=LOOK 50,03,85
L4=ADD 20
L5=BP
L6=WALK
L7=EIP
L8=STEP

Le script de ProcDump étant assez particulier, celui de TaMaBoLo m'a semblé plus évident à suivre.
De toute façon dans un cas comme dans l'autre, avant ou après la "désactivation" de l'anti Sice, aucun de ces deux scripts n'ont permis de créer une exécutable décrypté.
Pourtant, en cherchant dans le code avec un éditeur hexadécimal, on retrouve bien la première signature d'ASPack en 004420BA (6A0050), et en cours de décryptage, par une recherche S cs:00400000 l FFFFFFFF 50 03 85 la seconde signature, donnée par TaMaMBoLo, en 00429A0.
Par contre si j'ai bien obtenu un break sur la première adresse, il n'y a rien eu à faire (quelle que soit la méthode utilisée) pour en obtenir un en 00429A0.

Le désassemblage:

Il est toujours plus facile de pouvoir se référer à un dead listing, quand c'est possible. Dans le cas d'Alchemy Laucher, Wdasm ne désassemble rien tant que les caractéristique de la première section n'ont pas été modifiées, et ne désassemble qu'un listing crypté ensuite. A partir de là, il va falloir réaliser un Dump manuel de l'application.

Normalement, la seconde signature de (la supposée) encryption ASPack, aurait permis de réaliser un Dump exécutable, mais comme je n'ai jamais réussi à arriver à l'adresse convoitée, j'ai repris le traçage F10/F8 de l'exécutable, jusqu'à arriver à une adresse qui me laissait supposer que l'ensemble de l'exécutable d'origine (comprenez avant encryption) soit redevenu Clean. En procédant ainsi, j'ai trouvé plusieurs Push Eax/Ret (habituel avec ASPack -> il y en a trois dans les versions que je connaissais), pour finir à l'adresse 006D04D7, la seule ou le registre EAX contenait une adresse 0040000 (exactement 00409C27) au lieu de 0060XXXX.
En remplaçant le PUSH EAX/RET par un JMP EIP, j'ai lancé ProcDump pour obtenir un dump full (non exécutable même en ayant replacé l'entry point noté dans EAX), et Wdasm a pu sortir un Listing avec ressources. A noter, au passage, que le Dump obtenu est de 312 ko, contre 193 ko normallement, ce qui devrait permettre de dire que le programme est crypté/compressé. Two in One. Le pied!

Par contre dans ce listing, rien qui puisse se rapporter à un Anti Sice, aucune occurrence trouvée qui puisse être en lien avec une routine de Code 0B. De la à conclure que l'Anti-Sice "appartient" au schéma d'encryption, et à renforcer mon idée qu'il s'agit bien d'une Ready Made Protection…

Le Dead Listing n'est pas inutile pour autant, il servira à trouver où patcher l'application, dans le programme original.
L'objectif de ce texte n'étant pas de cracker à tout prix le programme, mais bel est bien à trouver une solution face à une application encryptée avec Anti Sice intégré, la proposition suivante permet de faire croire au programme qu'il est enregistré. Il reste que le Unregistered dans la boite About fait toujours face à "registered to". A d'autre de finir…

:00406927 8901                    mov dword ptr [ecx], eax
:00406929 8B15E4D84200            mov edx, dword ptr [0042D8E4]
:0040692F 8D7E30                  lea edi, dword ptr [esi+30]
:00406932 33C0                    xor eax, eax               > notre cible
:00406934 89442418                mov dword ptr [esp+18], eax
:00406938 8917                    mov dword ptr [edi], edx
:0040693A 8B15E4D84200            mov edx, dword ptr [0042D8E4]
:00406940 8D5E34                  lea ebx, dword ptr [esi+34]
:00406943 8913                    mov dword ptr [ebx], edx
:00406945 C644241802              mov [esp+18], 02
:0040694A C706844E4200            mov dword ptr [esi], 00424E84
:00406950 894604                  mov dword ptr [esi+04], eax
:00406953 894608                  mov dword ptr [esi+08], eax
:00406956 89460C                  mov dword ptr [esi+0C], eax
:00406959 894610                  mov dword ptr [esi+10], eax
:0040695C 894614                  mov dword ptr [esi+14], eax
:0040695F 894618                  mov dword ptr [esi+18], eax
:00406962 89461C                  mov dword ptr [esi+1C], eax
:00406965 894620                  mov dword ptr [esi+20], eax
:00406968 894624                  mov dword ptr [esi+24], eax
:0040696B 894628                  mov dword ptr [esi+28], eax

EAX sert à initialiser l'Etat Utilisateur. En le mettant à 1, vous ferrez croire au programme qu'il est effectivement Registered, en remplaçant le XOR EAX, EAX par deux INC EAX, soit remplacer 33CO par 4040.

Ce "désagrément" peut être résolu, pour faire très simple, de la manière suivante:

* Possible StringData Ref from Data Obj ->"UNREGISTERED"
                                  |
:00406F4E 684CD64200              push 0042D64C
:00406F53 8D4DF0                  lea ecx, dword ptr [ebp-10]

En arrivant dans la routine "Unregistered", le Push 0042D64C va pousser sur la pile le contenu de cette adresse, soit l'expression que l'on voudrait voir disparaître….
A ce niveau, si vous remplacer le contenu de l'adresse 0042D64C par votre nom, c'est celui ci qui sera affiché dans l'écran About. Par contre le patch à réaliser risque d'être assez long à écrire, et ASPack (s'il s'agit bien de lui) n'a pas la réputation de laisser beaucoup de place, sachant que le ratio de compression est quand même de 37%… (c'est pas le meilleur!)

En posant un BPM sur 0042D64C, vous aurez différents breaks (entre autre au moment où le programme "charge" UNREGISTERED, pour finir par ceci:

017F:00409F82  8FE4                POP       ESP
017F:00409F84  8B448EE8            MOV       EAX,[ECX*4+ESI-18]
017F:00409F88  89448FE8            MOV       [ECX*4+EDI-18],EAX
017F:00409F8C  8B448EEC            MOV       EAX,[ECX*4+ESI-14]
017F:00409F90  89448FEC            MOV       [ECX*4+EDI-14],EAX
017F:00409F94  8B448EF0            MOV       EAX,[ECX*4+ESI-10]
017F:00409F98  89448FF0            MOV       [ECX*4+EDI-10],EAX
017F:00409F9C  8B448EF4            MOV       EAX,[ECX*4+ESI-0C]  > ici
017F:00409FA0  89448FF4            MOV       [ECX*4+EDI-0C],EAX
017F:00409FA4  8B448EF8            MOV       EAX,[ECX*4+ESI-08]
017F:00409FA8  89448FF8            MOV       [ECX*4+EDI-08],EAX
017F:00409FAC  8B448EFC            MOV       EAX,[ECX*4+ESI-04]
017F:00409FB0  89448FFC            MOV       [ECX*4+EDI-04],EAX
017F:00409FB4  8D048D00000000      LEA       EAX,[ECX*4+00000000]

En faisant un d ECX*4+ESI-0C, vous verrez qi'il pointe vers le vilain texte:

:d ecx*4+esi-18
0187:0042D640 52 45 47 49 53 54 45 52-45 44 00 00 55 4E 52 45  REGISTERED..UNRE
0187:0042D650 47 49 53 54 45 52 45 44-00 00 00 00 2E 48 4C 50  GISTERED.....HLP
0187:0042D660 00 00 00 00 53 61 76 65-20 73 68 6F 72 74 63 75  ....Save shortcu
0187:0042D670 74 20 66 61 69 6C 65 64-21 00 00 00 5C 2A 2E 6C  t failed!...\*.l

Et que juste au dessus, vous avez un texte nettement plus sympathique...
Il suffirait donc de modifier légèrement les pointeurs pour voir "Registered to: REGISTERED" dans l'écran About (ou virer le UN de unregistered, les variantes sont nombreuses…).
Mais vous le ferriez pour vous faire plaisir, c'est du folklore!

Revenons à notre programme crypté/compressé, et à la modification à faire en 00406932.

Résumons:

- L'application se décrypte/décompresse en trois fois pour atteindre le RET à placer au début de la routine d'affichage du nag "Debuggeur Detected"
- Le XOR EAX, EAX est lui aussi (contrôlé par un "d 00406932") décompressé dans les mêmes temps

Il ne reste plus qu'à:
- Ecrire un patch qui permettrait de détourner le programme après chaque décompression vers celui ci, et modifier les codes de l'application en 006CFFF0 et 00406932 (on aura besoin de connaître la taille qu'aura le patch pour la suite)
- Trouver de la place pour loger le patch, soit en trouvant suffisamment d'espaces dans l'une des sections, soit en agrandissant une section, soit en en rajoutant une.
- Détourner pour finir le programme de façon à ce qu'il se branche sur le patch à partir d'une zone non cryptée/compressée.

Ya+Ka!

Glop, Glop, c'est partie!

Le patch

Les adresses que j'envisage de modifier sont les suivantes:

Patch 1: En 00442315 vérifier que la zone suivante est bien devenue clean, et forcer le programme à revenir au patch2.
Patch2: En 006DE78B vérifier que la zone suivante est bien devenue clean, et forcer le programme à revenir au patch3.

017F:006DE78A  5D                  POP       EBP
017F:006DE78B  C20800              RET       0008

Patch3; En 006DE77D vérifier que la zone suivante est bien devenue clean, éradiquer l'anti Sice , et forcer le programme à revenir au patch4.

017F:006DE77A  F3A4                REPZ MOVSB
017F:006DE77C  5E                  POP       ESI
017F:006DE77D  E9E7FEFFFF          JMP       006DE669

Patch4: En 406932 vérifier que la zone XOR EAX, EAX est bien devenue clean, et changer en 4040

Et mon point de départ va être l'adresse 004420B, dans une zone clean, d'où je vais me brancher sur le patch 1

017F:004420A7  8BB574F24400   MOV       ESI,[EBP+0044F274]
017F:004420AD  F3A4           REPZ MOVSB
017F:004420AF  8B8574F24400   MOV       EAX,[EBP+0044F274]
017F:004420B5  6800800000     PUSH      00008000            > d'ici et à modifier en JMP XXXXXXXX
017F:004420BA  6A00           PUSH      00
017F:004420BC  50             PUSH      EAX

Le Patch aura une bouille de ce genre:

Patch 1
C70515234400E9XXXXXXMOV       DWORD PTR [00442315],XXXXXXE9 > Force le saut vers le patch2
C60519234400XX      MOV       BYTE PTR [00442319],XX
6800800000          PUSH      00008000                      > restaure les octets écrasé
E9XXXXXXXX          JMP       004420BA                      > et continue

Patch2
803D8BE76D00C2      CMP       BYTE PTR [006DE78B],C2 >       >la zone est elle décompressée
7511                JNZ       00442F76                       > si non-> saute, si oui:
C7057DE76D00E9XXXXXXMOV       DWORD PTR [006DE77D],XXXXXXE9  > prépare le saut suivant vers patch3
C60581E76D00XX      MOV       BYTE PTR [006DE781],XX
E9XXXXXXXX          JMP       004421F9                       > et continue

Patch3
803DF0FF6C0055      CMP       BYTE PTR [006CFFF0],55        > la zone est elle décompressé
7518                JNZ       00442F9C                      > si non-> saute, si oui:
C605F0FF6C00C3      MOV       BYTE PTR [006CFFF0],C3        > remplace 55 par C3
C70593266C00E9XXXXXXMOV       DWORD PTR [006C2693],XXXXXXE9 > prépare le saut suivant vers patch4
C60597266C00XX      MOV       BYTE PTR [006C2697],XX
E9XXXXXXXX          JMP       006DE669                      > et reprend le boulot

Patch4
803D3269400033      CMP       BYTE PTR [00406932],33   > la zone est elle décompressée
750A                JNZ       00442FB4                 > si non-> saute, si oui:
66C705326940004040  MOV       WORD PTR [00406932],4040 > remplace 33C0 par 4040
90                  NOP                > erreur de calcul de longeur du saut
83E103              AND       ECX,03   > restaure les octets modifiés
F3A4                REPZ MOVSB         > idem
E9XXXXXXXXX         JMP       006C2698 > retour à la normale.

Maintenant que la taille du patch est connue, il ne reste plus qu'à trouver la place pour le loger.
Les solutions sont assez variables suivants les cas. La place à prendre peut être:

- A la fin d'une section
- Dans un coin de l'Icône (il paraît que c'est pas propre! Et puis les éditeurs de ressources qui pourraient nous dire où est codée l'icône ne fonctionnent pas quand celle ci est cryptée/compressée)
- A la place des informations de versions, de copyright, etc…
- Dans un endroit ou bizarrement il y a de la place disponible (très empirique, mais la chance aidant...) avec plein de 00 00 00 00.

Et bien sur, cette zone ne doit ni être compressée, ni être utilisée lors de la décompression du Soft.

Glop, Glop!
Du premier coup, l'info de version fonctionne. Ca c'est un morceau de chance! La place est prenable...
Mon patch va donc commencer en 00442F41, et en 004420B5 je vais remplacer le PUSH 00008000 par un JMP 00442F41 (même nombre d'octets).
Voici le bébé:

017F:00442F41  C70515234400E9420C00MOV       DWORD PTR [00442315],000C42E9
017F:00442F4B  C6051923440000      MOV       BYTE PTR [00442319],00
017F:00442F52  6800800000          PUSH      00008000
017F:00442F57  E95EF1FFFF          JMP       004420BA
017F:00442F5C  803D8BE76D00C2      CMP       BYTE PTR [006DE78B],C2
017F:00442F63  7511                JNZ       00442F76
017F:00442F65  C7057DE76D00E9F947D6MOV       DWORD PTR [006DE77D],D647F9E9
017F:00442F6F  C60581E76D00FF      MOV       BYTE PTR [006DE781],FF
017F:00442F76  E97EF2FFFF          JMP       004421F9
017F:00442F7B  803DF0FF6C0055      CMP       BYTE PTR [006CFFF0],55
017F:00442F82  7518                JNZ       00442F9C
017F:00442F84  C605F0FF6C00C3      MOV       BYTE PTR [006CFFF0],C3
017F:00442F8B  C70593266C00E90909D8MOV       DWORD PTR [006C2693],D80909E9
017F:00442F95  C60597266C00FF      MOV       BYTE PTR [006C2697],FF
017F:00442F9C  E9C8B62900          JMP       006DE669
017F:00442FA1  803D3269400033      CMP       BYTE PTR [00406932],33
017F:00442FA8  750A                JNZ       00442FB4
017F:00442FAA  66C705326940004040  MOV       WORD PTR [00406932],4040
017F:00442FB3  90                  NOP
017F:00442FB4  83E103              AND       ECX,03
017F:00442FB7  F3A4                REPZ MOVSB
017F:00442FB9  E9DAF62700          JMP       006C2698

A partir de l'adresse 00442FAA vous avez moyen d'écrire le patch (le vrai, celui qui va cracker le prog) de votre choix, à condition de finir votre modification par les "AND ECX,3", "REPZ MOVSB" et bien sur le JMP 006C698.

Glop, Glop!

Bonne journée
Christal