Alchemy laucher 1.0 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 (Y aura t-il un Maître Reverser qui modifiera un jour Sice pour lui permettre d'en accepter plus?) 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 [Aspack108.4] 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 LC=STEP TaMaMBoLo L1=LOOK 6A,00,50 L2=BP L3=LOOK 50,03,85 L4=ADD 20 L5=BP L6=WALK L7=EIP L8=STEP Note de TaMaMBoLo : je remark que mon script fait 4 lignes de moins que celui de Procdump et en + j'aurai encore pu enlever une ligne (le L7=EIP n'est poa nécessaire ) . 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éristiques 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 '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! 1- 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