diff --git a/content/secu_systeme/2_initiation_re/index.md b/content/secu_systeme/2_initiation_re/index.md new file mode 100644 index 0000000..192bf3d --- /dev/null +++ b/content/secu_systeme/2_initiation_re/index.md @@ -0,0 +1,329 @@ +--- +title: "Sécurité système : initiation au reverse engeneering" +date: 2023-09-21 +tags: ["ingénieurie inverse", "assembleur"] +categories: ["Sécurité système", "Cours", "TD"] +--- + +Nous avons revu dans le cours précédents les bases du fonctionnement d'un +binaire de la compilation à son exécution. Dans cette partie nous allons +appréhender les bases de l'ingénierie inverse. À l'issue de celle-ci nous +devrions être en mesure de réaliser de petits *crackme*. + +L'étape de compilation est en quelque sorte destructrice : Il n'est plus +possible à partir du binaire compilé de retrouver le code source correspondant. +Mais il est tout de même possible de **désassembler** : on reconstitue un code +assembleur. + +Ainsi afin de faire du reverse engineering, il est nécessaire de **comprendre la +compilation**, de savoir de quoi se **compose un binaire** et comment **il est +exécuté**. + +Une compréhension de l'assembleur, des type de structures qu'il manipule est +primordiale. Il est aussi nécessaire de maîtriser les outils à notre +disposition, de savoir coder un minimum (en *C*, *Python*). + +## La gestion de la mémoire + +Les sections `.rodata`, `.bss` et `.data` contiennent des données mémoires. Deux +structures existent pour les manipuler : la pile et le tas. + +### La pile + +Elle manipule des données statiques. C'est une structure de type LIFO (*Last In +First Out) qui croit vers le bas : plus on descend plus les adresse augmentent. +Sa taille est alignées sur un *int32* ou *int64* en fonction de l'architecture +(32 ou 64 bits). + +*Deux "fonctions"* permettent de la manipuler : `push()` afin de placer un +élément -- la pile descend alors d'une case -- et `pop()` pour reprendre un +élément -- la pile remonte d'un élément. + +Elle se situe **dans l'espace mémoire d'un processus**, ainsi chaque processus a +sa pile. Elle a une structure linéaire; son accès est **rapide** (pas +d'allocation de mémoire à effectuer) mais la **taille des éléments que l'on peut +y stockée est limitée**. + +### Le tas + +Il manipule des données dynamiques représentées par une structure de données +hiérarchisée. Le tas est lui aussi positionné dans l'espace mémoire d'un +processus. + +Il n'est pas limité en taille (enfin presque pas...) mais nécessite de coûteuses +allocations mémoire qui le rend plus lent. Le développeur peut lui même allouer +de la mémoire sur le tas avec la présence de fonctions comme `malloc()` (et ne +pas oublier de les libérer avec `free()`). L'implémentation de ces mécanismes +mémoire dépendent de **l'allocateur**. + +## Initiation au reverse Engineering + +### L'assembleur + +C'est un langage de bas niveau mais encore compréhensible par un humain +(initié...). Il est dépendant de l'architecture, prenons par exemple le code *C* +suivant: + +```c +int sum (int a, int b ){ + return a +b; +} +``` + +Voici ce code en assembleur *x86_64* : + +```asm +sum(int, int): + push rbp + mov rbp, rsp + mov DWORD PTR [rbp-4], edi + mov DWORD PTR [rbp-8], esi + mov edx, DWORD PTR [rbp-4] + mov eax, DWORD PTR [rbp-8] + add eax, edx + pop rbp + ret +``` + +Et en assembleur *ARM64*: + +```asm +sum(int, int): + sub sp, sp, #16 + str w0, [sp, 12] + str w1, [sp, 8] + ldr w1, [sp, 12] + ldr w0, [sp, 8] + add w0, w1, w0 + add sp, sp, 16 + ret +``` + +On parle alors d'ISA pour *Instruction Set Architecture*, cela représente le jeu +d'instruction disponible. Nous y trouvons les opérations élémentaires : +addition, soustraction, multiplication, division, et / ou (exclusifs ou non) +etc. Ici mon manipule les registres et la mémoire directement. Une *ISA* ne +contient pas d'opérateur avancés comme les structures de contrôles que nous +pouvons trouver dans les langages de haut niveau (`while ...`, `if ... +else ...`, `for ...`). + +### L'assembleur x86 (32 et 64 bits) + +Tout comme pour le cours de sécurité logicielle, nous utiliserons principalement +l'assembleur *x86_32* pour ce cours (et quelques fois sa version 64). Mais par +contre nous utiliserons **la syntaxe *Intel*** majoritairement utilisée dans le +monde de l'ingénierie inverse. Cette syntaxe est plus simple : + + * les suffixes de mnémoniques pour n'existes pas en syntaxe Intel, ainsi + `movl`, `movw` ou encore `movb` deviennent **`mov`**; + * les préfixes sur les registres et immédiats disparaissent : `%eax`, `$1`, + `$0x0ff` deviennent **`eax`, `1` et `0x0ff`**; + * l'ordre des opérandes est inversé : ce n'est plus `source, destination` mais + **`destination, source`**. `movl $1, %eax` devient **`mov eax, 1`** + * les accès indirect à la mémoire sont aussi plus simple : `(%eax)` devient + `[eax]` et `3(%eax)` devient `[eax + 3]` voir le cours [accès à la mémoire de + sécurité logicielle]({{}}) + +Le vocablulaire reste le même que celui vu lors des cours [d'introduction de +sécurité logicielle]({{ref "secu_logicielle/1_introduction/index.md"}}) + +### L'assembleur x86 : les registres + +LEs registres sont de petits espace memoire directement intégrés au processeur. +Ce sont les espace mémoires les plus rapides disponible sur un ordinateur, mais +aussi les plus petits. Il sont dédiés entre autres au stockage de données. + +Certains de ces registres ont des **rôles bien déterminés** par exemples : + + * `eip` pour *extended instruction pointer* pointe vers la prochaine + instruction à exécuter[^eip], il porte aussi le nom de *Compteur Ordinal*. + **il ne peut être modifié** contrairement à tous les autres registres; + * `ebp` pour `extended base pointeur` pointe vers le bas de la pile; + * `esp` pour `extented stack pointer` pointe lui vers le haut de la pile. + +Tous ces registres sont noté *extended* dans leurs versions **32 bits** + +Certains autres ont des spécificité, mais il est tout à fait possible pour le +programmeur de les utiliser à sa guise : + + * `ecx` par exemple est utilisé comme compteur dans certaines instructions de + repétition(typiquement les boucles); + * `esi` et `edi` utilisés comme source (*extended source index*) et destination + (*extended destination index*) pour certaines instructions de copie. + +Les registres peuvent être découpés en sous registres : + +> `a` 8 bits -> `ax` 16 bits -> `eax` 32 bits -> `rax` 64 bits + +Il est d'ailleurs possible de découper `ax` en deux registes de 8bits : `al` +(bits 0 à 7) et `ah` (bits 8 à 15). Ces découpes permettent certaines +optimisations comme par exemple le **stockage de deux entiers 16 bits** dans un +registre 32bits ou encore pour les instruction se basant sur **l'interprétation +du contenu** des registres. + +Il existe aussi des registes d'états, mais nous les avons vu [en sécurité +logicielle]({{}}) +DAs le cadre de ce cours, le *Zero Flag*, *Sign Flag* et *Carry flag* sont +importants. Le carry flag correspond à la présence d'une retenue [voir sur +Wikipedia][carry_flag]. + +Pour rappel, voici un exemple d'utilisation du *Zero flag* : + +```asm + +start: + mov eax, 1 + dec eax ; décrémente eax + + ; comme le résultat de la précédente instruction est 0 + ; alors notre programme va forcément brancher... + jz hell + +; [...] + +hell: +``` + +[^eip]: en x86, dans certaine ISA, il pointe l'instruction en cours). + +[carry_flag]:https://fr.wikipedia.org/wiki/Indicateur_de_retenue + +## Pratique + +### IDA Free + +C'est un outils gratuit et multi plate-forme d'analyse de binaire. Il existe +aussi plusieurs versions payantes (pro, home, corporate ...). La version free +comporte un desassembleur, un décompilateur "cloud" pour x86_64 seulement. Il +permet la manipulation de binaire au formats différents (ELF, PE Mach-O). Il +s'interface aussi avec des débogueras notamment GDB. + +C'est un outil puissant mais difficile à prendre en main. + +### Étudions `ls` (binaire ELF) + +Nous ouvrons notre binaire ls modifié avec `lift` lors du précédent TD pour +l'étudier avec *IDA Free*. + +#### question b + +`strcpy` se trouve dans la section `.dynsym`, section des symboles résolus +dynamiquement lors de son premier appel. + +#### question c + +Dans IDA, le code couleur utilisé entre autres dans la section `.dynsym` +correspond aux éléments fournis à l'extérieur de notre binaire, comme dans les +bibliothèques partagées. + +#### question d + +Les chiffres hexadécimaux dans ne nom de la fonction `sub_xxxxxx` correspondent +aus décalage (*offset*) de celle-ci par rapport à l'adresse de chargement de +notre binaire. + +### Les raccourcis clavier + +Ils sont une part importante de l'utilisation d'IDA, en voici quelques-uns: + +| raccourcis | fonction | commentaires | +|------------|----------|--------------| +| crtl + w | sauvegarder la base de données | très utile cat IDA est instable +| ctrl + shitf + w | effectuer un snapshot de la base de données | CF au dessus +| ctrl + z | annuler | | +| n | renommer un objet (variable, fonction, etc.) | hexrays | +| y | modifier le type d'un objet (variable, fonction, etc.) | asm / hexrays | +| alt + a | interpréter l'objet comme une chaine de caractères | | +| h | interpréter la selection comme un décimal / hexadécimal | asm / hexrays | +| r | interpréter la selection comme un caractère | asm / hexrays | +| c | interpréter la portion comme du code asm | asm | +| d | interpréter la portion comme data | asm | +| u | retirer l'interprétation | asm | +| : ou ;|ajouter un commentaire au niveau de l'instruction | visualiseur | +| insert | ajouter un commentaire avant l'instruction | asm / hexrays | +| espace | cycle entre la vue graph et linéaire | hexrays | +| F5 | décompiler (si possible) | | +| entrée ou double-clic | su un symbole -> aller où pointe le symbole | asm / hexrays | +| echap | revenir à la position précédente - en arrière | asm / hexrays | +| ctrl + entrée | revenir à la position précédente - en avant | asm / hexrays | +| x | afficher les cross références *xref* d'un objet | asm / hexrays | +| alt + t | recherche de texte | | +| alt + b | recherche de motif binaire | | +| alt + i | rechetche un *immédiat* | | +| tab | passer de la vue *asm* à la vue hexrays | | +| ctrl + e | afficher les points d'entrées du binaire | | +| maj + F1 | afficher la fenêtre des types locaux | | +| maj + F12 | afficher la fenêtre des chaîne de caractères | | + +Il faut voir IDA comme un bloc note accompagnant le travail d'ingénierie inverse +Il faut **absolument** documenter au fur et à mesure des investigations et faire +autant d'instantanés que possible (stabilité...). + +### Analyse du binaire `mysecrets` + +#### Question 1 + +Dans IDA, lorsque nous faisons `ctrl+e` nous atterrissons dans la liste des points +d'entrées du binaire chargé. Dans le cas de `mysecret`, nous atterrissons dans +la fonction `main:`. Ce qui est normal pour un binaire ELF. + +#### Question 2 + +Pou trouver la section `.rodata`, il suffit de trouver la liste des segments +(mais pas au sens ELF...) avec le raccourci clavier `ctrl+s`. Cette section +contient les données en lecture seule, ici toutes les *strings* de notre +binaire. + +#### Question 3-a + +Dasn le code assembleur, il semble y avoir 3 arguments. IDA nous les donne en +commentaire : + +```asm +ebp + arg0 +ebp + arg4 +ebp + arg8 +``` + +#### Question 3-b + +La convention d'appel utilisées ici est `__cdecl` (pour *C declaration*), c'est +la convention utilisés par le langage C et C++ -- la convention d'appel dépend +aussi de l'ABI système, du compilateur ). Ici les arguments sont placés sur la +pile par la fonction appelante (*caller*). Il sont placé dans l'ordre inverse, +prenons comme exemple le programme *C* suivant : + +```c +int add_3 ( int a, int b, int c){ + // [...] +} +main() { + add_3 (1, 2, 3); +} +``` + +Ce qui donnera en assembleur : + +```asm + +main: + push ebp ; backup current base pointer + mov ebp, esp ; get the new base pointer + + push 3 + push 2 + push 1 + call add_3 + ; [...] +``` + +#### Question 3-c + +Les instructions `push epb` et `mov epb, esp` permettent de mettre en place le +*base pointer* après l'avoir sauvegardé pour assurer sa remise en place lors du +retour de notre fonction `secrets`. + +#### Question 3-d + +La décente de `0x18` dans la pile permet à la fonction apellée de mettre en +place un espace pour les variables locales.