(TAOMF) Les structures de données

L'ensemble des articles tagués TAOMF ont été écrit à partir d'un ensemble de notes prises suite à la lecture du livre "The Art Of Memory Forensic" écrit par Michael Hale Ligh, Andrew Case, Jamie Levy et Aaron Walters.

Tout les crédits de ces articles leur reviennent donc de droits. Par ailleurs je vous invite vraiment à lire ce livre qui est une mine colossale d'informations.


Pour bien appréhender l'art du forensic il est important de connaître un minimum les différents types de données ainsi que les structures de données. En C nous disposons nativement d'une dizaine de types de données répertoriés dans le tableau ci-dessous :

Type Taille Borne inférieure Borne inférieure (formule) Borne supérieure Borne supérieure (formule)
signed char ≥ 8 bits -127 -(27-1) +127 27-1
unsigned char ≥ 8 bits 0 0 +255 28-1
short ≥ 16 bits -32 767 -(215-1) +32 767 215-1
unsigned short ≥ 16 bits 0 0 +65 535 216-1
int ≥ 16 bits -32 767 -(215-1) +32 767 215-1
unsigned int ≥ 16 bits 0 0 +65 535 216-1
long ≥ 32 bits -2 147 483 647 -(231-1) +2 147 483 647 231-1
unsigned long ≥ 32 bits 0 0 +4 294 967 295 232-1
long long (C99) ≥ 64 bits -9 223 372 036 854 775 807 -(263-1) +9 223 372 036 854 775 807 263-1
unsigned long long (C99) ≥ 64 bits 0 0 +18 446 744 073 709 551 615 264-1

Merci Wikipédia 😜 !

En plus de ces types de données basique nous avons tout un ensemble de structures qui permettent de stocker nos données selon différentes méthodes.

Les tableaux :

Les tableaux sont une suite de variables de même type (int, char ...), situées dans un espace contigu en mémoire. En C on pourra déclarer un tableau de cette manière :

#include <stdio.h>
#include <stdlib.h>

int main(){
  int tab[5] = {1, 2, 3, 4, 5};
  return 0;
}

En mémoire cela se traduit comme ceci :

struc1.png

On pourra ensuite accéder à nos données via une boucle for par exemple :

#include <stdio.h>
#include <stdlib.h>

int main(){
  int tab[5] = {1, 2, 3, 4, 5};
  for (int i = 0; i < 5; i++){
  	printf("%d", tab[i]);
  }
  return 0;
}

Là où ça devient intéressant c'est que vu qu'un tableau est toujours typé, on peut assez aisément les détecter dans la mémoire. En effet un tableau de int sera toujours composé de plusieurs blocs de 2 octets.

Les bitmaps :

Les bitmaps sont des tableaux de tableaux qui ne contiennent qu'un seul type de valeur : des booléens ! Les bitmaps sont utilisés notamment pour stocker l'état des ports de notre machine.

 

Une cellule de notre bitmap a 1 indique que le port est ouvert et vis vers ça.

Les chaînes de caractères :

Grossièrement une chaîne de caractères n'est ni plus ni moins qu'un tableau de char. Attention cependant il y a une grosse différence entre une chaîne de caractères et un tableau : la chaîne de caractères se termine toujours par le caractère \00 (le null byte que l'on retrouve, entre autres, dans les techniques d'exploitation des Local File Include).

En C on déclarera une string de cette manière :

#include <stdio.h>
#include <stdlib.h>

int main(){
  char string[6] = "Hello\0";
  printf("%s", string);
  return 0;
}

Les listes chaînées :

Là on attaque un type de structure un peu plus complexe mais aussi extrêmement utile lorsqu'il s'agit de stocker des informations que l'on va consulter de manières régulières. Les listes chaînées se sont tout simplement un ensemble d'éléments qui sont liés les uns aux autres.

struct2.png

Ce lien étant symbolisé par un pointeur qui point sur la donnée suivante :

struct3.png

L'avantage avec cette structure c'est qu'il est assez simple de supprimer/ajouter un élément. Pour cela il suffit juste de modifier le pointeur sur la donnée suivante et vous verrez (plus tard) que c'est très souvent utilisé dans les rootkits !

L'exemple ci-dessus est une liste chaînée simple mais il existe aussi des doubles listes chaînées. Dans ces cas là, l’élément suivant ainsi que l'élément précédant sont liés :

struct4.png

Enfin il existe un dernier type de liste chaînée : les liste circulaires qui ne sont ni plus moins que des listes chaînés simples ou doubles avec le dernier élément qui est lié au premier :

struct5.png

Les arbres :

Dernière structure que nous verrons dans cet article : les arbres. Les arbres sont des structures très intéressantes car elles permettent de classifier des données assez rapidement.

Tout arbre est composé d'au moins une racine, de nœuds et de feuilles :

struct6.png

Si je vous disais qu'il permet de classifier et récupérer des données assez rapidement c'est parce qu'avec un arbre, il n'existe qu'un chemin permettant d'accéder à la valeur 6. Par conséquent en implémentant un arbre on va permettre d'économiser pas mal de temps de recherche.

Dans l'exemple précédent l'ensemble des données sont classifiées selon leurs valeurs. Plus la valeur est faible plus elle se trouve sur les noeuds situés à gauche de l'arbre et vis vers ça. Encore une fois les arbres sont très souvent utilisés et ça nous le verrons plus tard ;) !