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

I Introduction
Voila on se retrouve donc en face de cette bte code 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 prt on peux y aller!

Les choses serieuses commencent maintenant :) La suite des oprations 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 dj 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 difficults  lire 90% des programmes et a les comprendre si vous avez le temps :)
Un trs bon moyen de ralentir un reverser est de changer l'aspect "normal" du code. Il y a eu
plusieurs mthodes employes au fil des temps (utiliser les SEH qd ce n'tait pas connu, crypter
les zones sensibles du programmes et les decrypter/encrypter  la vole en mettant le Trap Flag  1,
sauter "dans" une instruction pour qu'un dsassembleur linaire 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 accs au code
original de l'application et donc doivent avoir des algos trs pousss pour permettre  99% des
programmes de fonctionner correctement. Cependant  un moment ou  un autre le code original sera
prsent en mmoire et pourra etre vol :)

Le junk prsent 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 mlange 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 incrmente
puis aprs la ligne un jmp vers ce label et ensuite dplacer des "blocs" composs de

insX
		....
		jmp insX

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

Cependant le travail pour le reverser est nettement plus important, comment faire pour rcuperer 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 ide assez simple pour raliser cela. Je vais dsassembler le programme en partant
de l'EOP, crer 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 crer cette liste d'instruction!
J'ai utilis la librairie Libdisasm ( http://bastard.sourceforge.net/libdisasm.html)
pour dcompiler chaque instruction, en extraire les infos necessaires, et crer 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 (dbut de la section), on s'apercoit
rapidement que les jmp / call / jnz ... En effet les jumps par exemple contiennent le dplacement  effectuer
pour arriver au nouvel offset.

Exemple:
E9 00 00 00 00 		jmp $+5
	------------
			|
			v
	Dplacement

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

Si l'on crit l'arbre tel quel dans la nouvelle section, ces dplacements sont faux! ils correspondaient en effet
aux dplacement par rapports aux vieux offset et vu que l'on a supprim des oprandes cela n'a plus aucun sens.
Il faut donc corriger toutes les oprations 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 prcdente oprande 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 dplacement entre 2 oprandes

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

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


II.5
Une fois tout ceci effectu, on lance la cration d'arbre depuis l'EOP. Bon ca ne suffit pas car notre dsassembleur
simple ne peut pas savoir qu'il faut dsassembler aussi certains offset pass en push comme pour un CreateTread, pour
une WindowProc ...
Donc il faut lancer la cration 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 cre 5 threads diffrents, 

(2-3-4) 3 qui grent  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 manire alatoire des samples dans un des 2 buffers sons. 2 buffers sont
utiliss pour permettre de switcher sans coupure de son, un peu comme ce qui etait utilis  
l'poque pour les buffers vidos :) Il ajoute de manire alatoire un des samples suivants ou aucun.
L'adresse de ce sample est accole au wave_header (ou vide si aucun n'est slectionn).


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 prononces. 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 utilise 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.