Partie 22 - L'analyse dynamique - modifier les registres et la mémoire (4/4)
L’analyse dynamique : 📝 modifier les registres et la mémoire (4/4)
Comme à l’accoutumée, après avoir appris à lire, on apprend à écrire ! Si vous avez bien saisi la logique lors de la lecture en mémoire, vous ne devriez pas avoir trop de soucis à la modifier.
Astuce gdb : La commande
set
permet d’écrire dans des registres, variables et la mémoire.
Modifier les registres
Pour modifier la valeur d’un registre, rien de plus simple que de faire set $reg = value
où
reg
est le registre à modifiervalue
la valeur à affecter au registre (en décimal, hexadécimal …)
Ce qui est plutôt cool avec lorsque l’on utilise set
avec un registre est qu’il n’y a pas besoin de spécifier la taille de value
, il la détectera automatiquement.
Par exemple :
1
2
set $rdi = 0xdeadbeefcafebabe
set $eax = 0xcafebabe
Astuce gdb : Vous pouvez utiliser
rel
(pourreload
) afin de rafraîchir la GUI de pwndbg et voir les changements effectifs.
Si vous affectez à un registre une valeur plus petite que sa taille totale cela revient à le mettre à zéro d’abord (sans incidence sur les EFLAGS) puis à affecter la nouvelle valeur.
💫 Se téléporter n’importe où dans le code
On a parfois besoin d’exécuter assez rapidement une fonction ou un bout de code sans vouloir forcément exécuter tout ce qui le précède. Par exemple, si on souhaite analyser une fonction critique d’un malware mais qu’elle est précédée d’une fonction qui détecte le débogage, il vaut mieux éviter de l’exécuter.
Il existe deux manières de se téléporter déplacer dans le code :
- La commande
jump 0xdest
- Modifier
eip
/rip
avecset
. Exemple :set $rip = 0x55555555abcd
La différence entre les deux est la suivante :
- Avec
jump
, le processeur saute à l’adresse indiquée et poursuit l’exécution - En modifiant
eip
ourip
, le processeur saute à l’adresse indiquée mais ne poursuit pas l’exécution
Ainsi, à moins d’avoir une raison valable d’utiliser jump
, il vaut mieux modifier le pointeur d’instruction pour mieux contrôler le flot d’exécution.
Modifier une zone mémoire
Pour modifier une zone mémoire pointée par une adresse, nous allons également utiliser set
.
Compilons le code ci-dessous afin de voir comment nous allons réussir à modifier la mémoire :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
int main()
{
unsigned long long *identifiant = malloc(8);
memset(identifiant, 0xff,8);
if(*identifiant == 0xdeadbeefcafebabe)
puts("Connexion en tant qu'administrateur !");
else
puts("Connexion refusée.");
return 1;
}
Il s’agit d’une vérification à deux balles permettant d’autoriser la connexion à un administrateur et la refuser pour les autres.
Evidemment, en exécutant normalement le programme, il est impossible de se connecter en tant qu’admin car notre identifiant sera toujours 0xffffffffffffffff
. Mais en tant que reverser, nous n’allons pas nous laisser abattre par une aussi simple protection d’authentification 😈 !
Compilons le programme en 64 bits. Ouvrons-le dans gdb et mettons un point d’arrêt dans le main
puis exécutons le programme.
Maintenant que vous savez exécuter pas à pas un programme, allez jusqu’à l’instruction de comparaison avec 0xdeadbeefcafebabe
sans l’exécuter :
La variable identifiant
est contenue dans l’adresse pointée par le registre rax
.
Astuce gdb : Pour modifier une zone mémoire pointée par un registre, il est possible d’utiliser
set *$reg = value
.Pour modifier directement les données pointées par une adresse :
set *0xaddr = value
.
Modifions cette valeur avec la commande set *$rax = 0xdeadbeefcafebabe
.
Veillez à bien précéder le registre d’un astérisque
*
afin que ce soit la valeur pointée qui soit modifiée et non pas le contenu du registre, comme cela a été fait plus haut.
En exécutant rel
, on constate que la valeur de rax
est 0xffffffffcafebabe
et ce n’est pas ce que l’on voulait faire …
En fait, ce qui se passe est que gdb modifie au plus 4 octets dans la mémoire car il considère que ce qui est pointé est, par défaut, un int
. C’est pourquoi les 4 octets de poids fort pointés par rax
n’ont pas été modifiés.
Nous devons donc spécifier le type afin que gdb sache qu’il s’agit d’une variable de 8 octets à modifier set {unsigned long long}$rax = 0xdeadbeefcafebabe
.
Astuce gdb : De la même manière, si vous ne souhaitez modifier qu’un seul octet (au lieu de 4 par défaut) vous devez le spécifier. Exemple :
set {byte}0x401020 = 0xf5
.
Par ailleurs, nous aurions pu également manipuler directement l’adresse contenue dans rax
pour effectuer cette modification en mémoire set {unsigned long long}0x5555555592a0 = 0xdeadbeefcafebabe
Vous remarquerez que lorsque l’on spécifie le type de la zone mémoire, il n’y a plus besoin de mettre l’astérisque
*
.
A présent que nous avons modifié la mémoire pour y mettre l’identifiant de l’administrateur, nous pouvons poursuivre l’exécution du code :
Voilà voilà 😎 !
Savoir modifier la mémoire d’un processus dans un débogueur est une chose très importante. Cela permet notamment de contourner des détections basiques de débogage sans avoir à modifier le code du programme.
📋 Synthèse
En somme, l’analyse dynamique met à disposition d’un reverser des fonctionnalités lui permettant de manipuler un programme avec la granularité qu’il souhaite.
Cela permet notamment d’analyser des spécificités du code qui n’apparaissent pas forcément de prime abord lorsque l’on analyse statiquement un programme.
Encore une fois, il ne s’agit pas de choisir entre analyse statique et dynamique pour bien comprendre un programme : il faut savoir tirer partie des avantages des deux.
Ainsi, on ne s’attardera pas en analyse statique sur une fonction compliquée et qui semble peu intéressante alors qu’il est possible de l’exécuter plusieurs fois avec des arguments différents pour avoir une idée de ce qu’elle fait en fonction de la valeur de retour.
Egalement, nous n’avons pas vu toutes les fonctionnalités que propose gdb et pwndbg. Ce serait beaucoup trop long et pas vraiment pédagogique de voir dans ce cours tout ce qu’ils proposent. Ainsi, si vous souhaitez aller plus loin dans ces fonctionnalités, vous pouvez toujours lire leur documentation.
Vous trouverez facilement sur internet des fiches de synthèses (ou cheat sheets) résumant les principales commandes de gdb comme celle-ci. Encore une fois, les annexes de ce cours regroupent les différentes astuces et commandes gdb vues ensemble.
Enfin, nous avons parlé exclusivement de gdb car nous nous sommes focalisés sur l’analyse de programme ELF. En revanche, si vous souhaitez faire de l’analyse dynamique sous Windows, vous pouvez utiliser x64dbg qui est un débogueur très puissant et très utilisé sous Windows.