###############################################################################
##                    Meloquynthe - Yolejedi & ++Meat                        ##
##                       Tutorial  By Pulsar (2006)                          ##
###############################################################################

I Introduction
Voila on se retrouve donc en face de cette bête codée par les soins de Meat
& Yolejedi... Il se trouve que ca le truc est packé par upx...C'est vraiment
pas un obstacle, mais c'est sans doute pas fait pour, la suite sera plus sympa :)


Donc let's go pour l'unpacking...

>upx -d Meloquynthe.exe

                     Ultimate Packer for eXecutables
  Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
UPX 1.93 beta    Markus F.X.J. Oberhumer & Laszlo Molnar         Feb 7th 2005

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
   4979200 <-   3631104   72.93%    win32/pe     Meloquynthe.exe

Unpacked 1 file.
>

Voila c'est prêt on peux y aller!

Les choses serieuses commencent maintenant :) La suite des opérations est la suivante
	- Suppression du junk
	- Analyse de la protection
	
II JUNK
II.1 First thought
On arrive au premier obstacle, le junk. Lorsque vous avez déjà reversé une dizaine de programme,
vous commencez à vraiment comprendre ce que fait une fonction si vous l'avez sous les yeux. En
effet il suffit de connaitre une quizaine d'instructions et le tour est joué vous n'aurez pas
de difficultés à lire 90% des programmes et a les comprendre si vous avez le temps :)
Un très bon moyen de ralentir un reverser est de changer l'aspect "normal" du code. Il y a eu
plusieurs méthodes employées au fil des temps (utiliser les SEH qd ce n'était pas connu, crypter
les zones sensibles du programmes et les decrypter/encrypter à la volée en mettant le Trap Flag à 1,
sauter "dans" une instruction pour qu'un désassembleur linéaire affiche des choses bizzares (CCA).

La grande mode actuellement c'est les "packers" , 90% des programmes vont etre packé et il sera plus
difficile de trouver le code original, cependant la majorité des packers n'ont pas accès au code
original de l'application et donc doivent avoir des algos très poussés pour permettre à 99% des
programmes de fonctionner correctement. Cependant à un moment ou à un autre le code original sera
présent en mémoire et pourra etre volé :)

Le junk présent dans ce crackme est nettement plus efficace et beaucoup plus simple à mettre en place!

code:

push eax
push ebx
push ecx
call 0xXXXXXXXX

rien de bien compliqué pour le reverser.

Maintenant on applique une fonction à ce fichier source qui le modifie comme tel:

-------------------------------
ins1:
			push eax
ins2:	
			push ebx
ins3:
			push ecx
ins4:
			call 0xXXXXXXXX
ins5
-------------------------------
puis on ajoute des jumps entre chaque instruction

-------------------------------
ins1:
			push eax
			jmp ins2
ins2:	
			push ebx
			jmp ins3
ins3:
			push ecx
			jmp ins4
ins4:
			call 0xXXXXXXXX
			jmp ins5
ins5:
-------------------------------

et on mélange le tout

-------------------------------
ins1:
			push eax
			jmp ins2
ins4:
			call 0xXXXXXXXX
			jmp ins5
ins3:
			push ecx
			jmp ins4
ins2:	
			push ebx
			jmp ins3

ins5:
-------------------------------

C'est nettement moins lisible maintenant non? pourtant les fonctions à code pour faire cela 
sont relativement simples! Il suffit d'inserer avant chaque ligne de code un label qu'on incrémente
puis après la ligne un jmp vers ce label et ensuite déplacer des "blocs" composés de

insX
		....
		jmp insX

Le compilateur fait tout le boulot en convertissant chacun des label en des offsets et en calculant
les déplacement à effectuer pour les jxx et call.

Cependant le travail pour le reverser est nettement plus important, comment faire pour récuperer le
code original? Le codeur gagne donc il peut coder rapidement cette protection et ennuyer BCP le reverser.


II.2 MyWay:
Je suis partit sur une idée assez simple pour réaliser cela. Je vais désassembler le programme en partant
de l'EOP, créer un arbre d'instructions du style


instruction
{
	information info; 								//Contient toutes les infos sur l'instruction courante
	instruction *next;								//pointeur vers l'instruction suivante
	instruction *next_flow;						//pointeur vers l'autre instruction suivante possible (voir ci-dessous)
}

Le next_flow est utilisé lorsque l'instruction peut changer le "flow" de l'execution, comme un jnz, call, jmp...

par exemple
-------------------------------
XX mov eax,0 (1)
YY call VV   (2)
ZZ
.....
VV pusha
-------------------------------

(1)
instruction XX
{
	info;
	next=YY;
	next_flow=0;
}

(2)
instruction YY
{
	info;
	next=ZZ;
	next_flow=VV;
}

Un jmp aura un next=0 et un next_flow valorisé.

Bon maintenant il faut coder un outil permettant de créer cette liste d'instruction!
J'ai utilisé la librairie Libdisasm ( http://bastard.sourceforge.net/libdisasm.html)
pour décompiler chaque instruction, en extraire les infos necessaires, et créer notre
arbre.

II.3 Nettoyer l'arbre
Maintenant je vais nettoyer cet arbre d'instruction en supprimant tout les jumps. Il suffit de parcourir
l'arbre et si on rencontre un jmp le virer de l'arbre. Il faut également supprimer d'autres instructions
en effet on trouve dans ce crackme un ensemble de:

jxx
jnxx

par exemple
jz  XXXX
jnz YYYY
ZZZZ:

Dans ce cas je supprime de l'arbre la seconde instruction et la remplace par l'instruction à YYYY. En effet
il ne sera jamais possible d'arriver à ZZZZ.

II.4 Ecrire l'arbre
Bon durant cette étape on va écrire nos instructions dans une nouvelle section, et fixer certaines offset.

J'extrait la section Melo_5te et je la rajoute au programme puis je mets toute cette nouvelle section à 0x20.

->Section Header Table
   Name
   												VirtualSize	VirtualAddress	SizeOfRawData	PointerToRawData	Characteristics
    .text				           0x00001EF6	0x00001000			0x00002000		0x00000400				0x60000020 (CODE, EXECUTE, READ)
    .rdata				         0x000006D4	0x00003000			0x00000800		0x00002400				0x40000040 (INITIALIZED_DATA, READ)
    .data				           0x00000220	0x00004000			0x00000400		0x00002C00				0xC0000040 (INITIALIZED_DATA, READ, WRITE)
     Melo_5te				       0x00454149	0x00005000			0x00454200		0x00003000				0xE0000040 (INITIALIZED_DATA, EXECUTE, READ, WRITE)
    .rsrc				           0x000687E8	0x0045A000			0x00068800		0x00457200				0xC0000040 (INITIALIZED_DATA, READ, WRITE)
--> Pulsar				      	 0x00455000	0x004C3000			0x00454200		0x004BFA00				0xE00000E0  (CODE, INITIALIZED_DATA, UNINITIALIZED_DATA, EXECUTE, READ, WRITE)

Voila on va pouvoir écrire tranquillement dedans.
Bon maintenant on écrit notre arbre en commencant à 0x8C3000 (début de la section), on s'apercoit
rapidement que les jmp / call / jnz ... En effet les jumps par exemple contiennent le déplacement à effectuer
pour arriver au nouvel offset.

Exemple:
E9 00 00 00 00 		jmp $+5
	------------
			|
			v
	Déplacement

On s'apercoit que le déplacement correspond au nombre à ajouter pour obtenir le nouvel offset. Si par exemple
l'offset de cette opérande est de XXX et que le déplacement est de YYY alors le nouvel offset ou l'on ira
est : XXX + 5 + YYY. le 5 correpond à la taille de l'opérande.

Si l'on écrit l'arbre tel quel dans la nouvelle section, ces déplacements sont faux! ils correspondaient en effet
aux déplacement par rapports aux vieux offset et vu que l'on a supprimé des opérandes cela n'a plus aucun sens.
Il faut donc corriger toutes les opérations qui modifient le flux (new_flow !=0). Pour qu'elles pointent vers le bon
endroit.

Pour ce faire, on ajoute à notre instruction une nouvelle information (new_offset)

instruction
{
	information info; 			
	instruction *next;			
	instruction *next_flow;	
	long new_offset;									//Correspond au nouvel offset que l'operand aura.
}

On parcours l'arbre et on assigne un nouvel offset à chaque instruction en commencant par 0x8C3000 et en ajoutant à chaque fois
la taille de la précédente opérande pour trouver le nouvel offset. On fait attention à assigner un et un seul offset pour une instruction.

Maintenant grace à cette information on peut connaitre le déplacement entre 2 opérandes

déplacement (ins, ins2) = ins2->new_offset - (ins->new_offset + ins->info->size)
Une fois l'arbre écrit on change tout ces offset de déplacement pour leur mettre la nouvelle valeur.

Maintenant on a un arbre épuré avec de bon offsets pour les opérands. Il reste quelques petites opérations à effectuer
mais je les passerai sous silence. (Elles ne sont pas importantes).


II.5
Une fois tout ceci effectué, on lance la création d'arbre depuis l'EOP. Bon ca ne suffit pas car notre désassembleur
simple ne peut pas savoir qu'il faut désassembler aussi certains offset passé en push comme pour un CreateTread, pour
une WindowProc ...
Donc il faut lancer la création d'arbre depuis plusieurs endroits :

0x07FE013 -> EOP
0x06162BE	-> MainWindowProc
0x04F2E6D -> ButtonProc
0x05BFE14	-> Thread1
0x05BFE39 -> Thread2
0x081C0CA -> Thread3
0x07A3DEC -> Thread4
0x05BFF04 -> Thread5

Et hop voila on a une nouvelle section avec un code clair. On modifie l'EOP du programme, 
ainsi que les offset pushé (Thread & Proc) pour correspondre à nos nouvelles valeurs.
Le nouveau crackme est pret à etre analysé :)


Exemple:

Pulsar:008C3000 start           proc near
Pulsar:008C3000                 push    0               ; lpModuleName
Pulsar:008C3002                 call    GetModuleHandleA
Pulsar:008C3007                 mov     ds:hInstance, eax
Pulsar:008C300C                 call    GetCommandLineA
Pulsar:008C3011                 mov     ds:dword_7FF353, eax
Pulsar:008C3016                 push    0Ah             ; lpIconName
Pulsar:008C3018                 push    ds:hInstance    ; hInstance
Pulsar:008C301E                 call    LoadIconA
Pulsar:008C3023                 mov     ds:lpMsg, eax
Pulsar:008C3028                 push    7F00h           ; lpCursorName
Pulsar:008C302D                 push    0               ; hInstance
Pulsar:008C302F                 call    LoadCursorA
Pulsar:008C3034                 mov     ds:dword_406D7A, eax
Pulsar:008C3039                 push    0               ; nIndex
Pulsar:008C303B                 call    GetSystemMetrics
Pulsar:008C3040                 mov     ds:dword_77B492, eax
Pulsar:008C3045                 push    0               ; nIndex
Pulsar:008C3047                 call    GetSystemMetrics
Pulsar:008C304C                 mov     ds:dwNewLong, eax
Pulsar:008C3051                 call    sub_8C305D
Pulsar:008C3056                 push    eax             ; uExitCode
Pulsar:008C3057                 call    ExitProcess



III. Analyse
Le crackme initialise la fenetre principale, ainsi que les 5 boutons.
Il crée 5 threads différents, 

(2-3-4) 3 qui gèrent à mon avis les clignotements de l'interface.
(1)			1 thread qui recevra les messages relatif à l'avancement du son
(5)			1 qui controle un DWORD...


Le thread(5) initialise le dword que j'appellerai 'magic' . Ce dword donne la suite
des boutons sur lequel l'utilisateur doit cliquer, au travers de la fonction meat_good_button.


Le thread(1) ajoute de manière aléatoire des samples dans un des 2 buffers sons. 2 buffers sont
utilisés pour permettre de switcher sans coupure de son, un peu comme ce qui etait utilisé à 
l'époque pour les buffers vidéos :) Il ajoute de manière aléatoire un des samples suivants ou aucun.
L'adresse de ce sample est accolée au wave_header (ou vide si aucun n'est sélectionné).


Chacun des samples correspond à un boutton de l'interface
       case 0x83C0F1:   // Crackme
               return 0x532;
       case 0x5DCB78:   // Tendinite
               return 0x533;
       case 0x461DD4:		// Accept
               return 0x534;
       case 0x4F559B:		// Yo
               return 0x535;
       case 0x5F9746:		// Meat (? ou Me)
               return 0x536;

Il faut ensuite cliquer sur le bouton correspondant au dword magic au bon moment.
Le bon moment est en fait lorsque les paroles sont prononcées. La fenetre est relativement courte
Il faut etre rapide :) Les positions suivantes correspondent à l'index dans le buffer audio

       case 0x83C0F1:
               return 0x483b; // -> dbaf
       case 0x5DCB78:
               return 0xb111; // -> 11dbd
       case 0x461DD4:
               return 0xfa99; // -> 1ae85
       case 0x4F559B:
               return 0x8061; // -> 1c3d5
       case 0x5F9746:
               return 0xa05f; // -> 1c79d

ce qui donne un temps en seconde de la (position /AVERAGE_BIT_S*1000)
AVERAGE_BIT_S c'est une constante utilisée lorsque tu ouvre le device
audio qui donne comme son nom l'indique le nombre moyen de bit par second
égal dans ce cas à 0x0AC44.

En cliquant sur le bon boutton pendant le sample correspondant, au bon moment (ca fait
beaucoup de bon hein? :) ) on passe à l'étape suivante(un nouveau bon boutton à cliquer...
Une fois arrivé à l'étape 5, c'est gagné.