Reversing mIRC

Introduction:


Voilà c'est mon premier cours pour la team TipiaK, je vais m'occuper de la partie reversing et maybe cracking et maybe coding asm aussi je sais pas encore :o)

Pour l'instant ce cours va s'attaquer à une cible connue mais néanmoins facile : mIRC ...

Peu importe la version que vous utiliserez , le but ici n'est pas de cracker une protection (yen a pas :)) mais plutôt de vous apprendre comment réfléchir à partir d'une source d'un programme que vous n'avez plus et d'optimiser cette merde de mIRC pour qu'il réponde à vos attentes... tel est le but du reversing ...

Je ne vous cacherais pas que pour reverser il faut déjà avoir de solides connaissances en asm, et puisque je ne m'occupe que de Windows pour l'instant, il vous faut connaître ce système d'exploitation un maximum ... ne vous inquiétez pas, vous apprendrez vite du moment que vous êtes un petit peu curieux ...

Bon pour vous y aider, vous devrez avoir les logiciels suivants à portée de main ... ne me DEMANDEZ PAS où on peut se les procurer, faites une recherche sur internet point.
 


Voilà c'est bon vous avez tout ? alors c'est parti ! :-)



 

Deep into gnisreveR !


Le but de ce tutorial sera de pouvoir changer le CTCP version REPLY .

Note : j'utilise la version 5.7 de mIRC ! Certains offsets peuvent changer d'une version à l'autre ...

Evidemment vous pouvez changer tout ce que vous voulez , ou presque , du moment que vous connaissez bien votre cible ...

Donc on va d'abord désassembler. Utilisez IDA pour ce faire, bon je ne suis pas là pour faire un tutorial sur IDA donc j'assume que vous savez cliquer sur file/open et sélectionner un fichier n'est ce pas ? :o)

A l'aide d'IDA recherchez les strings ref intéressantes ...
Note: d'après la RFC 1459 sur le protocole IRC, le CTCP est envoyé sous forme de NOTICE nick. Vous pouvez chercher soit NOTICE soit la version reply "normale" de mIRC à savoir "mIRC32 ..."

Vous le trouverez normalement en 4F3EDB (pour la version 5.7 du moins ...). Et que voyez vous avant ? un NOTICE %s : !!!! donc nous sommes tombés sur la réponse directement :-)
A l'aide des xref, remontez à la source ... DATA XREF reference offset 445DD2. Bien :-)

Bon maintenant essayons de changer directement la version reply de mIRC en 4F3EDB. Pour ce faire vous avez deux solutions, ou bien utiliser un hexéditeur et faire une recherche sur tout le fichier ... c'est bourrin mais bon ca peut marcher ou alors utiliser le PE format pour déterminer le raw offset de la data et modifier ensuite directement celle ci . Bon pour la première méthode je ne vous ferais pas de dessin sur comment faire par contre pour la deuxième je vais vous expliquer un chtit peu ... :o)

Utilisez un PE editor genre ProcDump mais bon beaucoup d'autres sont valables aussi ...

Utilisez le PE viewer de ProcDump et choisissez mIRC32.EXE. Maintenant vous devez trouver l'espace mémoire dans lequel se trouve la data XREF ... N'oubliez pas que l'EIP = imagebase+virtualoffset !
Donc soustrayez 400000 à 4F3EDB pour obtenir le virtual offset ...

Virtual offset = F3EDB donc :)

La section data à pour vitual offset F1000 et pour taille 35000 ce qui nous fait : 126000 à la fin de la section ... or F3EDB appartient bien à l'interval F1000-126000 non ? :)
Donc nous avons trouvé la section . Maintenant trouvons le raw offset ... le raw offset de la section = F0600.

Pour obtenir l'offset de la data il nous suffit simplement de faire un "produit en croix" ...

Raw offset section (F0600) = Virtual offset section (F1000)
Raw offset data (?) = Virtual offset data (F3EDB)

Donc : en admettant que le fichier n'est pas packed , Raw offset data = Raw offset section + (Virtual offset data - Virtual offset section) ... ROD = F0600+(F3EDB-F1000) = F34DB ... voilà ... allez à cet offset et admirez la magie :)

Bon maintenant changez le version reply (en fait tout ce qui est après le mot VERSION c'est à dire mIRC connerie ... :o))
N'oubliez pas que votre chaine doit être ASCIIZ donc terminée par un caractère ASCII 0 (null) sinon ca va pas donner ce que vous voudrez ... :o)

Bon redémarrez maintenant ... n'oubliez pas de faire une sauvegarde de l'original au cas où tout partirait en couille ... En outre votre nouvelle chaîne ne doit pas dépasser la chaîne initiale ...

Bon on essaye ... et hop merde ca ne marche pas !!! :-(

Quand un client modifié essaye de faire un version echo, il n'y a pas de version reply mais lorsque quelqu'un nous fait un version echo on ne sort rien non plus :)
En ce qui concerne les ping echo et ping reply ca ne marche plus du tout non plus :'(

Bon il doit y avoir forcément des vérifications des fois qu'on serait des bad boyz :)

En recherchant d'autres occurences de la version reply on ne trouve rien de bien intéressant ... donc il faut trouver le maillon faible de cette "protection" ...

Pour éviter d'avoir à chercher pendant des heures LA ligne de code qui change tout , on va passer à la phase II: le live approach :)

Le principe est simple, utiliser un débogueur genre SoftICE pour savoir ce que le code fait exactement et quand il le fait ... il n'y a pas d'anti SI donc ca sera un jeu d'enfant ... enfin encore faut il savoir comment on procède :)

Tapez sous SoftICE:

:bpx wsprintfa

Cette API est utilisée très régulièrement dans le programme et va nous permettre d'accéder directement au VM du programme (un VM c'est grosso modo le process). Donc quand vous essayerez une nouvelle fois le CTCP version, le formatage de la ligne étant fait grâce à wsprintfa, vous serez averti directement, faites alors F12 pour revenir au programme lui même et là faites un:

:d 4F3EDB

Vous retrouvez notre super phrase modifiée :)
Maintenant tapez:

:bpm 4F3EDB r

Ce qui va poser un breakpoint système sur cette adresse, le R veut dire READ donc comme cette string va être vérifiée par le programme, elle sera lue et donc à chaque tentative de lecture nous serons là pour voir ce qu'il en est ... sympa non ? :o)

F5 pour continuer l'exécution et voilà où nous tombons :


CODE:00445DD2 064                 mov     esi, offset aNoticeS_1 ; "NOTICE %s :" ; le fameux message
CODE:00445DD7 064                 mov     edi, offset byte_513C45 ; la destination pour la copie

CODE:00445DDC 064                 mov     eax, edi ; sauvegarde du registre

CODE:00445DDE 064                 mov     ecx, 0Bh ; 11 dword = 44 bytes

CODE:00445DE3 064                 repe movsd ; copie ...

CODE:00445DE5 064                 movsb ; copie le dernier byte donc 45 caractères max

CODE:00445DE6 064                 pop     edi

CODE:00445DE7 060                 pop     esi

CODE:00445DE8 05C                 push    173Eh    ; c'est une valeur à vérifier dans le call

CODE:00445DED 060                 push    offset byte_513C45 ; la chaine copiée

CODE:00445DF2 064                 call    sub_4206C4     ; et voilà la superbe routine qui vérifie :)

CODE:00445DF7 05C                 mov     [ebp+var_C], eax ; EAX=1 tout va bien

CODE:00445DFA 05C                 cmp     [ebp+var_C], 0 ; EAX=0 => bad boy on arrête ...

CODE:00445DFE 05C                 jz      short loc_445E3A


C'est bien tout ca :)
Allons voir ce qui se cache dans ce call 4206C4 ...


CODE:004206C4     sub_4206C4      proc near               ; CODE XREF: sub_445D18+DAp
CODE:004206C4 000                                         ; sub_464594+B0p ...

CODE:004206C4

CODE:004206C4     arg_0           = dword ptr  8

CODE:004206C4     arg_4           = dword ptr  0Ch

CODE:004206C4

CODE:004206C4                     push    ebp

CODE:004206C5 004                 mov     ebp, esp

CODE:004206C7 004                 push    ebx

CODE:004206C8 008                 xor     ecx, ecx

CODE:004206CA 008                 mov     eax, [ebp+arg_0] ; premier argument : l'offset de la chaine a lire

CODE:004206CD 008                 jmp     short loc_4206DF

CODE:004206CF     ; ---------------------------------------------------------------------------

CODE:004206CF

CODE:004206CF     loc_4206CF:                             ; CODE XREF: sub_4206C4+1Fj

CODE:004206CF 008                 and     edx, 0FFh    ; on isole le caractère

CODE:004206D5 008                 xor     ebx, ebx       ; EBX=0

CODE:004206D7 008                 mov     bl, [eax+1]    ; on prend le caractère d'après

CODE:004206DA 008                 add     edx, ebx       ; on ajoute le caractère à edx

CODE:004206DC 008                 add     ecx, edx       ; puis à ecx ...

CODE:004206DE 008                 inc     eax               ; on passe au caractère suivant

CODE:004206DF

CODE:004206DF     loc_4206DF:                             ; CODE XREF: sub_4206C4+9j

CODE:004206DF 008                 mov     dl, [eax]       ; dl = caractère

CODE:004206E1 008                 test    dl, dl               ; est ce 0 ?

CODE:004206E3 008                 jnz     short loc_4206CF    ; non alors on jump

CODE:004206E5 008                 cmp     ecx, [ebp+arg_4]    ; on compare ECX à la valeur à trouver ...

CODE:004206E8 008                 jz      short loc_4206EE    ; c'est bon ? => good boy

CODE:004206EA 008                 xor     eax, eax

CODE:004206EC 008                 jmp     short loc_4206F3    ; EAX=0 bad boy

CODE:004206EE     ; ---------------------------------------------------------------------------

CODE:004206EE

CODE:004206EE     loc_4206EE:                             ; CODE XREF: sub_4206C4+24j

CODE:004206EE 008                 mov     eax, 1       ; la bonne valeur : GOOD BOY !

CODE:004206F3

CODE:004206F3     loc_4206F3:                             ; CODE XREF: sub_4206C4+28j

CODE:004206F3 008                 pop     ebx

CODE:004206F4 004                 pop     ebp

CODE:004206F5 000                 retn    8

CODE:004206F5     sub_4206C4      endp


Voilà ... c'est plutôt ridicule comme protection ... un petit algo qu'il nous est facile de contourner juste en traçant le programme et ensuite de changer la valeur du PUSH 173E normal en 445DE8.

Là plusieurs façons de patcher ... en noppant le jmp en 4206EC par exemple ... EAX=0 puis 1 donc c'est parfait :)

Voilà, vous testez et tout rentre dans l'ordre ... bon ben c'est fini :)



 

Is it the end ?


He ben oui pour ceux qui veulent encore s'emmerder la prochaine fois qu'ils voudront changer leur CTCP version REPLY directement sur le fichier ... :)

Et c'est là la magie du reversing lorsqu'on s'y connait un peu ... jusque là on pouvait considérer cette opération comme un "crack", d'ailleurs il serait possible de faire un programme qui cherche pour chaque version l'offset dans le fichier, change en ce que vous voulez et recalcule à l'aide de l'algorithme et puis hop c'est fini :)
Mais bon on va faire encore mieux , on va modifier le programme lui même pour pouvoir changer le ctcp reply directement dans le menu des options !!! :o)

Attention ici on va avoir besoin d'un cerveau 8o)

Le problème va être de trouver un emplacement suffisamment grand pour mettre notre ressource ...
Un simple CTCP VERSION reply en static et une editbox feront l'affaire. Par contre nous aurons besoin d'un peu d'espace dans le code ... puisque la routine en 4206C4 devient inutile, nous allons la remplacer par notre propre routine :o)

Mais ne croyez pas que ce soit aussi facile que ca !
Si on veut un patch parfait , il faudrait que l'information soit enregistrée sur le disque dur !

Dans la base de registres ce serait parfait ou au pire dans un fichier .INI :-(

Je me suis emballé trop vite :'-(, mIRC utilise toujours un fichier .INI pour stocker ses informations ce qui est plus long à extraire et en ce qui concerne les ressources ce serait inapplicable !

Nous voilà donc confronté à plusieurs possibilités qui ne nous arrange pas vraiment ... :-/
Dans ces cas là, le mieux est de choisir la "moins pire" solution , c'est à dire d'appeller un programme via /run, par exemple, qui changerait en mémoire les informations nécessaires, c'est à dire la data et patcherait le call pour qu'il renvoie toujours la bonne valeur .

Quelque soit la méthode utilisée, ce ne pourra rester qu'une méthode provisoire, puisqu'il n'est pas dit que ce genre de patch soit valable sur tous les mIRC antérieurs et surtout postérieurs, même par une recherche de chaîne et non par une recherche d'offset raw ou virtual, il sera toujours possible de changer l'algorithme ou le système de vérification pour que tout cela devienne inutile.

C'est pour cette raison que ce tutorial ne contient pas de "solution miracle" pour pouvoir vous la péter avec votre nouveau versoin reply malheureusement, mIRC est trop merdique pour faire quoique ce soit de valable donc xchat roulaize et si le coeur vous en dit vous pouvez toujours coder votre propre client irc :o)

Il reste néanmoins une solution provisoire pour appliquer vos CTCP reply directement depuis la base de registres ... elle pourrait être portable sur tous les mIRC puisqu'elle utilise la base de registre et elle pourrait rentrer dans notre routine ... C'est pour ne pas vous laisser sur le carreau sans code source que je vais développer cette méthode maintenant ...



 

The Coding Part


Bon aller en route ... après une petite dépression tout à l'heure sur le fait que l'on ne puisse pas faire un reverse vraiment "l33t" 8o) , il est néanmoins possible avec vos skillz de faire un joli truc tout mignon tout plein :-)

Nous avons plusieurs manières d'arriver à nos fins comme je l'ai expliqué dans la partie précédente, j'en ai choisi deux en particulier ...
 


Je vais vous fournir l'explication de ces deux programmes ...

Je ne fournis ici aucun code source sinon le temps de vous expliquer comment ca fonctionne et tout vous serez dégouté de la lecture ! :o)

Je ne programme qu'en assembleur en outre ce qui ne semble pas être le langage privilégié de nos jours (malheureusement!) donc cela risquerait encore plus d'accroitre l'ennui ...

Je vais simplement vous expliquer la démarche "intellectuelle" à avoir ...
 

I. Patcher à l'aide d'un programme extérieur


Voilà ce que vous devrez faire ...

 

  1. Afficher une boite de dialogue très simple (en partant d'une ressource par exemple) et l'afficher (une EDITBOX suffit). Il faut juste demander le nouveau ctcp version reply ...
  2. La chaîne à prendre ne doit pas dépasser la chaîne initiale de la version reply de mIRC à savoir 1E . Vous pouvez par exemple appeller GetDlgItemTextA avec 1Eh en size de votre buffer ... ce qui est la méthode la plus simple en assembleur ... maintenant si vous le faites en delphi jpeux pas vous dire ! :o)
  3. Ripper l'algorithme du call précédent en sachant que [arg_0] = chaine copiée ... Je ne vous ferais pas de dessin sur comment faire là débrouillez vous un peu !
  4. Patcher mIRC32.exe : évidemment cela enlève la possibilité de changer le ctcp version reply quand on est déjà connecté mais je pense que ce ne sera pas un trop gros inconvenient ! 8=) Pour le patcher, il vous faut connaitre les offsets donc ca dépend de chaque versioni de mIRC ... Vous n'aurez besoin de patcher qu'à deux endroits, l'offset de la data du ctcp version reply et le push juste avant l'appel du call (enfin le 2ème ...) . C'est juste la valeur à changer (n'oubliez pas que nous avons à faire aux processeurs intel avec le little endian format !).


Voilà pour cette première méthode ...

 

II. Utiliser la base de registres pour trouver la nouvelle version reply ...


La deuxième méthode consiste à utiliser la base de registres pour crééer votre clé (Version par exemple) qui sera checkée à chaque fois que le programme devra renvoyer un ctcp version reply ... l'avantage c'est que vous pouvez modifier la réponse sans avoir besoin de fermer mIRC et de le relancer !

C'est assez simple là, plus besoin de calculer le nouveau code pour que la version reply soit acceptée puisque cette routine remplacera la précédente !

D'autre part elle peut tenir sur les 35h bytes de la procédure ...

C'est la méthode que je préfère ...

Si vous avez besoin de plus amples informations sur ces programmes vous pouvez toujours me demander par mail ou via IRC ... :-)



 

Conclusion


C'est toujours mieux que rien mais bon il y a bien d'autres méthodes d'arriver à vos fins ...

Si vous voulez toujours vous accrocher a mIRC ... je vous donne quelques idées de programmes :

 


Le reversing c'est aussi apprendre... Si jamais vous vouliez faire votre propre client IRC, prenez mIRC comme base pour toutes vos opérations internet, par exemple le CTCP version REPLY que l'on doit envoyer en réponse à un CTCP version REQUEST ... Vous pouvez toujours regarder certaines parties de code, elles vous éviteront pour sûr la fastidieuse lecture de la RFC 1459 !

Voilà c'est fini pour ce premier tutorial, je ne sais pas si vous avez tout compris, j'ai essayé d'être le plus clair possible mais il n'est pas dit que tous mes lecteurs auront le même niveau alors toutes les suggestions sont welcome évidemment !
 



mail : james_bond_coding@yahoo.com

IRC : #tipiak sur undernet


-- James Bond 007
-- Ecrit le 1/08/2000