|
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!
|