IDA - The Interactive Disassembler - part 1

Voici une petite serie de 4 tutos pour ceux qui souhaiteraient s'initier à IDA, et je peux vous assurer que ca en vaut le détour (de se mettre à IDA, loin de moi de penser que mes tuts en vaillent le détour :p).

Quand vous aurez pris connaissances des puissantes capacités de cet outil il est fort à parier que vous allez changer votre vision du cracking et passerez à un état d'esprit où le cracking prend vraiment une autre dimension.
Car en effet ce n'est pas un simple désassembleur dans lequel vous pourrez tracer à la va vite les instructions d'un programme et casser la protection.

Pour etre employé à sa juste valeur, IDA nécessite du travail et du temps, il demande de s'investir dans le listing désassemblé et d'avoir une certaine dose de savoir en programmation, petit à petit c'est un peu comme si la source devenait la votre, vous allez l'adapter à votre comprehension, la travailler y ajouter des commentaires, renommer des fonctions, des variables etc.. si bien qu'à la fin la source original et la votre n'auront plus rien à voir. C'est pour ca aussi qu'il est difficile de faire des tutos de cracking avec IDA comme outils de reference pour le listing désassemblé puiqu'à la fin la source de 'l'auteur' (du tutorial) sera assez personnelle et moins comprehensible du fait que vous utiliserez des noms propre à votre analyse.

Dans cette série de tuts, plutôt que de vous balancer comme ca mon propre code modifié, je vais essayer de vous donner les bases pour que par vous même vous puissiez arriver à ce stade d'analyse et de modification de code.

Note: Certaines fonctionnalitées décrites par la suite peuvent ne pas etre presente dans IDA selon la version que vous utilisez. Tous les exemples ici sont accessibles à partir de la version 4.15.

IDA en lui même

Multi-processeur :
IDA supporte une multitude de processeur comme Texas Instrument, ARM, MIPS etc.. mais nous ne nous interresseront ici qu'à la famille des PC avec une architecture en 80x86 sous Windows.

Désassembleur intelligent :
IDA à la différence d'un simple désassembleur, analyse le code, avec notamment des méthodes comme
FLIRT et PIT.

FLIRT pour Fast Library Identification and Recognition Technology, permet de reconnaitre des appels de fonction sur des librairies standard de certains compilteurs. Exemple sur un programme en C si vous appelez la fonction strcpy(..) IDA pourra reconnaitre cet appel de fonction et au lieu de preciser un simple call xxxxxx lequel vous devrez explorer avant de comprendre que cette fonction sert à copier une string, vous obtiendrez directement call strcpy ce qui economise du temps et facilite bien la tache.

PIT pour Parameter Identification and Tracking permet à IDA de suivre et d'identifier les paramètres d'une fonction aux travers de la pile afin de reconnaitre et marquer les paramètres employés, ce qui là encore facilite grandement la tache. IDA permet aussi de désassembler proprement faisant la différence entre du code et des déclarations de variables ainsi que du code inexploité par le programme.

Interactif :
Pour vous donner la possibilité de faire des modifications, renommer des variables, des fonctions, faire des commentaires, definir les arguments d'une fonction, créer des fonctions etc...

Voila pour les fonctionnalités de base, bien sur il y en a beaucoup d'autres mais je vous laisse le soins de les découvrir par vous même.

De Wdasm à IDA

Wdasm étant le désassembleur le plus connu et le plus utilisé du fait de son extreme facilité, il s'agit surement du désassembleur par lequel vous avez commencé à pratiquer et quand on passe de wdasm à IDA on peux se trouver quelque peu dépaysé. D'abord je tient à dire que je ne déconsidere ou ne critique pas Wdasm qui est un très bon outil et pour des programmes simples qui ne necessitent pas une analyse poussée. Il convient amplement surtout avec le patch assez recent à l'heure où je parle qui ajoute des quantités de choses telles la colorisation syntaxique, ajout de commentaires etc..

La première difficulté se pose sans doute avec les Sting Datas References, puisque c'est la méthode la plus facile et par laquelle on apprend à cracker. Sous IDA on peux tout de meme lister les strings declarées et utilisées par le code via le menu / view / name ou voir directement en commentaire les strings reference par le code.

Vous obtenez une fenêtre listant les noms des fonctions et des noms de variables explicites nommées selon leur contenu. Exemple : une string qui contient "Merci de vous être enregistré" sera nommée par défaut comme ceci : aMerciDeVous. Sur ce point Wdasm est plus fort (surtout la version 8.5) car souvent il liste beaucoup plus de reference que IDA, mais il faut savoir que la technique des strings datas n'est pas la seule facon de cracker et qu'avec la pratique du cracking vous n'en avez presque plus l'utilité. Vous en viendrez même à l'éviter du fait des pièges que cette technique peut engendrer (crackmes tordus, etc...).

Les jump et les call références sont aussi un probleme, puisque definis différement.
Comparons deux mêmes portions d'un code sous Wdasm et IDA pour voir les differences.

Portion de code désassemblé sous Wdasm

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040105C(C)
|
:0040106D 817D0C11010000 	cmp dword ptr [ebp+0C], 00000111
:00401074 0F850B010000 		jne 00401185
:0040107A 8B4510 			mov eax, dword ptr [ebp+10]
:0040107D 837D1400 			cmp dword ptr [ebp+14], 00000000
:00401081 0F8407010000 		je 0040118E
:00401087 8B5510 			mov edx, dword ptr [ebp+10]
:0040108A C1EA10 			shr edx, 10
:0040108D 660BD2 			or dx, dx
:00401090 753B 				jne 004010CD
:00401092 6683F801 			cmp ax, 0001
:00401096 7518 				jne 004010B0
:00401098 6A30 				push 00000030
* Possible StringData Ref from Data Obj ->"Enregistrement..."
|
:0040109A 6816304000 		push 00403016
* Possible StringData Ref from Data Obj ->"Merci de vous êtes    enregistrés "
->"!"
|
:0040109F 6828304000 		push 00403028
:004010A4 6A00 				push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh
|
:004010A6 E817010000 		Call 004011C2
:004010AB E9D3000000 		jmp 00401183

Portion de code désassemblé sous IDA

0040106D loc_40106D:                             ; CODE XREF: sub_40102B+31j
0040106D cmp [ebp+arg_4], 111h
00401074 jnz loc_401185
0040107A mov eax, [ebp+arg_8]
0040107D cmp [ebp+arg_C], 0
00401081 jz loc_40118E
00401087 mov edx, [ebp+arg_8]
0040108A shr edx, 10h
0040108D or dx, dx
00401090 jnz short loc_4010CD
00401092 cmp ax, 1
00401096 jnz short loc_4010B0
00401098 push 30h
0040109A push offset aEnregistrement ; "Enregistrement..."
0040109F push offset aMerciDeVousTes ; "Merci de vous Ûtes enregistrÚs !"
004010A4 push 0
004010A6 call j_MessageBoxA
004010AB jmp loc_401183

Pour les jumps et calls références, sous Wdasm on a:

Referenced by a (U)nconditional or (C)onditional Jump at Address :0040105C(C) 

et sous IDA :

0040106D loc_40106D: ; CODE XREF: sub_40102B+31j

Cette petite ligne en dit beaucoup:

- On se situe à l'adresse 0040106D.
- loc_40106D: est defini en tant que label comme dans une source ASM ou l'on pourra se rendre via un jmp ou un call
- CODE XREF: nous dit que la référence provient de la section .CODE
- sub_40102B+31 ( = 0040105C ) est l'adresse de la référence
- sub_40102B étant l'adresse du debut de la procedure
- le  represente normalement une petite fleche (que je n'ai pas pas pu la reproduire sur cette page html) qui ..determine la direction vers le haut ou le bas ou se situe la référence
- j nous dit que cette reference est un jump
- et il suffit de double-cliquer sur sub_40102B+31j pour aller directement au jump

On voit que l'adresse 0040106D figure 2 fois, cela permet juste à IDA de rajouter des lignes interressantes ou plusieurs lignes de commentaires sur l'adresse. Comme ici la XREF.

On vois aussi que les strings data referencées par des adresses sous Wdasm ont été renommées sous IDA en nom de variables explicites suivi d'un commentaire indiquant leur contenu. On peut aussi double-clicker sur le nom de la variable pour aller directement là où celle-ci est déclarée suivi de sa XREF.

00403016 aEnregistrement db 'Enregistrement...',0 ; DATA XREF: sub_40102B+6Fo

Cette variable de type chaine de caracteres est déclarée comme dans une source asm avec la pseudo instruction db. Pour la référence de l'API MessageBox, Wdasm nous informe par
* Reference To: USER32.MessageBoxA,
Ord:01BBh, suivi du Call 004011C2 où 004011C2 représente l'adresse de l'appel MessageBox sous IDA. On voit alors directement le nom de la fonction que l'on appel :
call j_MessageBoxA.

On voit aussi que sur la première instruction cmp [ebp+arg_4], 111h , on utilise ebp+arg_4 alors que Wdasm emploie la valeur réel ebp+0C. arg_4 représente ici tout simplement l'un des arguments de cette procédure definie un peu plus haut:

0040102B sub_40102B proc near                    ; DATA XREF: start+Eo
0040102B
0040102B arg_0 = dword ptr 8
0040102B arg_4 = dword ptr 0Ch
0040102B arg_8 = dword ptr 10h
0040102B arg_C = dword ptr 14h

arg_4 représente bien la valeur 0C d'ailleur si on clique droit sur arg_4 dans cmp [ebp+arg_4], 111h, un petit menu va apparaitre nous donnant la valeur exact sous différentes bases (hexa, decimal, binaire...).
Nous verrons plus tard que l'on peux renommer arg_4 avec un nom plus representatif.

Les options

On peux afficher plus ou moins de renseignements et organiser selon nos souhaits le listing.
Le menu /options permet d'accéder à ces options. Depuis Options générales on pourra définir tout un tas de renseignements pour personnaliser l'affichage tel que les couleurs, la distance entre les differentes parties
( position ou le code commence, position ou les commentaires commence , ou les Xref commence etc..), caractéristique de nommage des strings, afficher les opcodes (valeur en hexa des instructions),
afficher le pointeur de pile suivant chaque instruction (très partiques parfois pour ne pas perdre de vue l'etat de la pile).

Le nombre d'options etant relativement important je ne peux pas toutes les décrires mais je vous conseille de prendre quelques minutes afin d'explorer chacune d'elle pour personaliser IDA selon vos désir.
Prenez du temps aussi pour découvrir l'interface, jouer avec les menus et tester un peu tout ce qui se présente.

Utilisation de IDA - les commandes et fonctions de base

Déja on commence par ouvrir le fichier que l'on souhaite via le menu File / Open file.. ou le bouton raccourci dans la barre d'outil. Une fenêtre apparaît nous permettant de spécifier quelques options, on ne touche à rien et on clique directement sur OK. On voit dans la petite fenêtre du bas l'évolution de l'analyse et on attend qu'il ait fini, ce qui peut etre très long pour des programmes conséquents.

Les zones

On obtient finalement un listing que l'on peux diviser en différentes zones comme suit :
- le code où l'on voit les instructions
- les datas on l'on voit les déclarations des variables comme en asm par les pseudos instruction db, dd, dw
- Inexploré que l'on reconnais car les segments d'offset sont grisés (partie de gauche), et ça ressemble a des déclaration de variables (ex : DB 82). Les zone inexplorées sont des zones qui ne sont ni référencées dans le code ni dans les datas. On peux modifier chaque zone en sélectionnant les lignes ou la ligne et en pressant :
'c' - pour code
'd' - pour data (si on se place seulement sur une ligne qui défini déjà une variable par db par ex on peut changer le ......type en pressant 'd' et en faisant défiler les types)
'u' - pour unexplored

Les jumps et le tracing

Sous IDA pour activer des options etc.. tout dépend de la position du curseur, il faut placer le curseur sur le nom d'une variable ou d'une fonction pour activer les options propres aux modifications de ces valeurs.
On peux tracer le code avec les fleches haut / bas et déplacer le curseur lateralement avec gauche / droite (ou la souris). Si on se place sur une instruction ou il y a un jump / un call il suffit de mettre le curseur sur le nom du label ou le nom de la fonction ou le jump / call sautera et appuyer sur 'Enter' pour effectuer le saut, on peux aussi double-cliquer directement.

On utilise la touche 'echap' pour revenir en arrière autant de fois que l'on a avancé (pratique pour revenir d'un jump ou d'un call). On peux aussi utiliser les 2 petites flèches latérales de la barre d'outils.

Depuis le menu / jump on a une foultitude de jump possibles, les plus utilisés etant 'jump to adress' accessibles directement via le raccourci clavier 'G', 'jump to entry-point' pour se rendre sur l'entry-point, 'Jump to function' etc... On a la possibilité aussi de définir des points clés que l'on nomme par 'Mark position'. On tape la description de ce point et l'on pourra s'y rendre directement en passant par "jump to marked position".

Le File Offset et adresse courante

Le File Offset nous permet de nous repérer dans le fichier avec un éditeur hexadecimal en vue de modification, on l'obtient en visualisant la barre d'etat muni de plusieurs cases tout en bas de la fenetre. C'est la 4eme case, si on laisse la souris dessus quelques instants nous verrons "current position in the input file".
La case suivante nous indique l'adresse courante suivi du nom de la procédure dans laquelle elle se trouve + le nombre à ajouter pour obtenir l'adresse courante.

Les commentaires

Il y a 2 types de commentaires : les commentaires uniques et les commentaires répétitifs :
Unique: en pressant ':' ce type de commentaire (unique) apparait là où on le place.
Répétitif : en pressant ';' ce type de commentaire permet de se repercuter dans le code, si par exemple on place ce type de commentaire sur une fonction nous le verrons à la suite de chaque call qui appelle cette fonction.

Les noms

C'est là la grande force de l'interactivité de IDA on peux renommer n'importe quel variable, fonction, valeur ou label et lui donner un nom bien plus explicite que celui donné par défaut.

Pour changer le nom d'un label ou en ajouter un, on se place sur le label déjà existant ou à l'adresse souhaité et on presse 'n'. Une boite apparaît permettant de spécifier le nom à utiliser. Le label sera changé et toutes les instructions qui y font references, ainsi si nous avons par exemple:

00401112 loc_401112: 		; CODE XREF: sub_40102B+116j
00401112 	mov al, [esi]
00401114 	test al, al
00401116 	jz short loc_401143

nous renommons loc_401112 en coucou. Si on se rend au jump ( par la ligne XREF ) qui référence ce label nous ne verrons plus jmp loc_401112 mais jmp coucou.

Pour changer le nom d'une fonction c'est un peu le meme procédé, la fonction etant déclaré comme suit :
call sub_4011A0 ( sub_ pour fonction loc_ pour label ) et pour renommer la fonction il suffit de se rendre là ou son 'label' est defini:

004011A0 sub_4011A0 proc near 		; CODE XREF: sub_401160+27p
004011A0 
004011A0 arg_0 = dword ptr 8h
004011A0 arg_4 = dword ptr 0Ch
004011A0 arg_8 = dword ptr 10h
004011A0 arg_C = dword ptr 14h
004011A0 
004011A0 	push ecx
004011A1 	push ebx

Et soit on presse 'n' pareil que pour un label et editer le nom soit on peux passer par un menu spécifique au fonction en pressant 'alt-p' ou clic droit pour y acceder via un petit menu contextuel.
Dans le Dialog Edit Function nous pourrons renommer la fonction ainsi que son adresse de base et de fin.
On peux aussi éditer le prototype de la fonction en choisissant 'set fonction type' depuis le menu contextuel, vous pourrez alors nommer les arguments et IDA modifiera le code pour renommer toutes les références de ces arguments. Voici la même fonction une fois son prototype modifié:

004011A0 ; int Calcul1(HWND hwnd,int var1,int var2,BOOL value)
004011A0 004011A0 Calcul1 proc near ; CODE XREF: sub_401160+27p
004011A0
004011A0 hwnd = dword ptr 8h
004011A0 var1 = dword ptr 0Ch
004011A0 var2 = dword ptr 10h
004011A0 value = dword ptr 14h
004011A0
004011A0 push ecx
004011A1 push ebx

On peux se servir du nom de constante symbolique pour renommer des valeurs. Exemple dans une procedure de gestion de message d'une fenetre, la constante WM_INITDIALOG une fois désassemblé sera remplacé par sa valeur numerique 110:

.code:0040102E         cmp     [ebp+arg_4], 110h
.code:00401035 jz short loc_401058

Si on clique droit sur 110h, on choisi 'use standard symbol constant' une liste apparait nous proposant les différentes constantes de Windows qui ont la valeur 110, apres il suffit de situer le contexte dans lequel nous sommes afin de choisir la bonne, Ici c'est dans une procedure de gestion des messages la constante que l'on recherche est donc un message celui qui nous est proposé est bien WM_INITDIALOG ( WM_ pour Windows Message ).
On le selectionne et on valide, 110 sera remplacé par WM_INITDIALOG. On peut alors en profiter aussi pour renommer le label du jump qui suit par _Initdialog par exemple puisque l'on sautera vers cette routine si c'est bien ce message.

Tant qu'on y est on va pousser un peu plus loin en montrant la technique de travail sous IDA,
mais avant tout un rappel sur la programmation assembleur:

Pour passer des arguments à une fonction on les push dans la pile avant le call de la fonction et en ordre inverse par rapport au prototype, la pile est accessible via le registre esp, ensuite dans la procédure on se sert de ebp pour recuperer les element de la pile selon ce shema :

push ebp ; sauvegarde valeur de ebp
mov ebp,esp ; place dans ebp, le pointeur de pile

ebp+00 = esp = old ebp => viens d'etre sauvgardé
ebp+04 = esp avant le call contient adresse retour de la fonction
ebp+08 = 1er argument
ebp+0C = 2eme argument
ebp+10 = 3eme argument
ebp+14 = 4eme argument
etc...

;fin de la procedure

mov esp,ebp ; ebp contient l'ancienne valeur d'ebp
pop ebp ; on desempile pour revenir à l'etat initial de la pile qui contient alors l'addresse de retour

ret ; retourne juste apres le call de la fonction

Revenons a notre exemple:

.code:0040102E 	cmp [ebp+arg_4], 110h
.code:00401035 	jz short loc_401058

Là on compare ebp+arg_4 qui represente ebp+0C qui lui même est le 2eme argument de cette procedure de gestion de message si on examine la prototype de la procedure de gestion des messages :

BOOL CALLBACK DialogProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);

Le 2eme argument est uMsg c'est une variable qui contient des valeurs numeriques (UINT - unsigned int) représentées par des constantes symboliques comme WM_INITDIALOG, c'est aussi en sachant que c'est ce 2eme argument que nous pouvons deduire que la valeur qui suit est un Windows Message.
Nous pouvons donc renommer ebp+arg_4 à sa juste valeur ce qui donne finalement:

.code:0040102E   cmp [uMsg], WM_INITDIALOG
.code:00401035 jz short _InitDialogRoutine

Pour ceux qui n'ont rien compris je reprendrais plus en détail dans la 2eme partie de cette serie de tutoriaux dont le thème sera de d'analyser et modifier un listing complet sur un petit programme très simple. Ce qui permettra de mettre en pratique et de pousser encore plus loin ce que nous venons de voir. Cet exemple étant là pour illustrer comment on travaille sous IDA et avoir un apercu du résultat qui ne ressemble plus trop à l'original, notez que l'exemple était sur 2 lignes de code. Imaginez sur une source complète ou même juste une fonction.

On peux aussi renommer tout ce que l'on souhaite en choisissant 'Manuel' depuis le menu contextuel ou 'alt-F1' et en cochant 'allow no matched operand' même si c'est completement illogique ou rien à voir donc prudence surtout si vous desirez plus tard exporter votre code (voir part 2 et 3).

Sauvegarder son travail et l'exporter

Une fois qu'on a bien bossé ou qu'on veux en laisser pour le lendemain, il suffit de sauvegarder le listing qui sera sauvegardé avce l'extension .idb lequel on pourra bien sur re-ouvrir et retrouver tout son boulot intact :)

Parmi les exports ( menu / produce ) on peux exporter le listing en .asm qui produira une source utilisable avec un assembleur, attention la source obtenue ne sera pas forcement compilable tout de suite voir même pas du tout, il faudra la retravailler et l'adapter. La 3eme partie expliquera la procedure à suivre.

On peux produire un fichier .map qui fait la correspondance entre les labels, fonctions variables et les adresses du programme, on s'en servira pour exporter le listing sous Soft-ice ce qui alors ouvre une voie dans un debugging plus clair.