Continue reverse engineering notes

This commit is contained in:
Yorick Barbanneau 2023-10-10 22:18:14 +02:00
parent 0d1c90448c
commit d4d633a1bc

View file

@ -124,12 +124,12 @@ monde de l'ingénierie inverse. Cette syntaxe est plus simple :
`[eax]` et `3(%eax)` devient `[eax + 3]` voir le cours [accès à la mémoire de `[eax]` et `3(%eax)` devient `[eax + 3]` voir le cours [accès à la mémoire de
sécurité logicielle]({{<ref "secu_logicielle/4_acces_memoire/index.md#accès-indirects-à-la-mémoire">}}) sécurité logicielle]({{<ref "secu_logicielle/4_acces_memoire/index.md#accès-indirects-à-la-mémoire">}})
Le vocablulaire reste le même que celui vu lors des cours [d'introduction de Le vocabulaire reste le même que celui vu lors des cours [d'introduction de
sécurité logicielle]({{ref "secu_logicielle/1_introduction/index.md"}}) sécurité logicielle]({{ref "secu_logicielle/1_introduction/index.md"}})
### L'assembleur x86 : les registres ### L'assembleur x86 : les registres
LEs registres sont de petits espace memoire directement intégrés au processeur. Les registres sont de petits espace mémoire directement intégrés au processeur.
Ce sont les espace mémoires les plus rapides disponible sur un ordinateur, mais 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. aussi les plus petits. Il sont dédiés entre autres au stockage de données.
@ -147,7 +147,7 @@ Certains autres ont des spécificité, mais il est tout à fait possible pour le
programmeur de les utiliser à sa guise : programmeur de les utiliser à sa guise :
* `ecx` par exemple est utilisé comme compteur dans certaines instructions de * `ecx` par exemple est utilisé comme compteur dans certaines instructions de
repétition(typiquement les boucles); répétition (typiquement les boucles);
* `esi` et `edi` utilisés comme source (*extended source index*) et destination * `esi` et `edi` utilisés comme source (*extended source index*) et destination
(*extended destination index*) pour certaines instructions de copie. (*extended destination index*) pour certaines instructions de copie.
@ -155,15 +155,15 @@ Les registres peuvent être découpés en sous registres :
> `a` 8 bits -> `ax` 16 bits -> `eax` 32 bits -> `rax` 64 bits > `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` Il est d'ailleurs possible de découper `ax` en deux registres de 8bits : `al`
(bits 0 à 7) et `ah` (bits 8 à 15). Ces découpes permettent certaines (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 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 registre 32bits ou encore pour les instruction se basant sur **l'interprétation
du contenu** des registres. du contenu** des registres.
Il existe aussi des registes d'états, mais nous les avons vu [en sécurité Il existe aussi des registres d'états, mais nous les avons vu [en sécurité
logicielle]({{<ref "secu_logicielle/3_assembleur_approfondissement/index.md#flags-de-résultats">}}) logicielle]({{<ref "secu_logicielle/3_assembleur_approfondissement/index.md#flags-de-résultats">}})
DAs le cadre de ce cours, le *Zero Flag*, *Sign Flag* et *Carry flag* sont Dans 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 importants. Le carry flag correspond à la présence d'une retenue [voir sur
Wikipedia][carry_flag]. Wikipedia][carry_flag].
@ -194,7 +194,7 @@ hell:
C'est un outils gratuit et multi plate-forme d'analyse de binaire. Il existe 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 aussi plusieurs versions payantes (pro, home, corporate ...). La version free
comporte un desassembleur, un décompilateur "cloud" pour x86_64 seulement. Il comporte un désassembler, un décompilateur "cloud" pour x86_64 seulement. Il
permet la manipulation de binaire au formats différents (ELF, PE Mach-O). Il permet la manipulation de binaire au formats différents (ELF, PE Mach-O). Il
s'interface aussi avec des débogueras notamment GDB. s'interface aussi avec des débogueras notamment GDB.
@ -276,7 +276,7 @@ binaire.
#### Question 3-a #### Question 3-a
Dasn le code assembleur, il semble y avoir 3 arguments. IDA nous les donne en Dans le code assembleur, il semble y avoir 3 arguments. IDA nous les donne en
commentaire : commentaire :
```asm ```asm
@ -305,7 +305,6 @@ main() {
Ce qui donnera en assembleur : Ce qui donnera en assembleur :
```asm ```asm
main: main:
push ebp ; backup current base pointer push ebp ; backup current base pointer
mov ebp, esp ; get the new base pointer mov ebp, esp ; get the new base pointer
@ -325,5 +324,139 @@ retour de notre fonction `secrets`.
#### Question 3-d #### Question 3-d
La décente de `0x18` dans la pile permet à la fonction apellée de mettre en La décente de `0x18` dans la pile permet à la fonction appelée de mettre en
place un espace pour les variables locales. place un espace pour les variables locales.
## Les conventions d'appel
Nous avons vu dans le cas pratique (question 3-b) la convention d'appel
`__cdecl`. Une convention d'appel définie les règles d'appel d'une fonction
dont :
* Comment sont transmis les paramètres à la *fonction appelée*;
* L'ordre dans lesquels ces paramètres sont passés;
* Quels registres doivent être préservés par la *fonction appelantes*;
* Comment la pile est nettoyée lors du retour à la *fonction appelante*.
Une convention d'appels peut dépendre de l'architecture, du système
d'exploitation, du compilateur, du langage (`__cdecd` est issue du *C*). Il
existe aussi :
* `stdcall` : variation de la convention `pascal`, utilisée par *Open Watcom
C++* et l'API *Win32*. Comme pour `cdecl` les éléments sont poussés de droite
à gauche et les registres `ebx`, `ecx` et `edx` dont préservés pour
l'appelant;
* `Microsoft fastcall` : les deux premiers arguments ( à partir de la gauche)
sont passés par les registres `ecx` et `edx` (s'ils rentrent) puis les autres
sont poussés sur la pile de droite à gauche.
### Deux convention principales pour x86_64
Pour ce qui concerne ce cours, nous parlerons de deux conventions principalement
utilisées.
#### Convention de Microsoft
Les arguments sont passés -- dans l'ordre -- par les registres `rcx`, `rdx`,
`r8`, `r9` [^microsoft] et le reste sur *la pile* de droite à gauche. Un espace
mémoire de 32 octets est réservés sur la pile avec les arguments par la fonction
appelante. Le retour se fait dans `rax` pour les entiers jusqu'à
64bits[^retour_ms] mais en vas de retour plus importants, alors `rax` contient
un pointeur.
[^microsoft]: pour les entiers, struct et pointeurs. Pour les flottants sur les
registres `xmm0` à `xmm3`.
[^retour_ms]: et `xmms0` pour un flottant.
#### Convention Unix (SYS-V)
Les arguments sont passés par `rdi`, `rsi`, `rdx`, `rcx`, `r8` et `r9`[^sysv] et
le reste sur la pile. La valeur de retour est positionnée dans `rax` pour les
valeurs jusqu'à 64 bits et `rax:rdx` entre 64 et 128 bits[^retour_sysv].
[^sysv]: de `xmm0` à `xmm7` pour les flottants
[^retour_sysv] `xmm0` et `xmm1` pour les flottants
### Pratique
Afin de découvrir ce que fait le programme `secrets2` il faut commencer par
l'exécuter si c'est possible. Ensuite nous passons dans IDA.
Un petit tours par les chaînes de caractères via le raccourci *shift + F12*
permet d'en apprendre plus. Nous pouvons ainsi voir le message d'aide avec ce
qui semble être l'utilisation de la commande. Nous voyons aussi ce qui semble
être un message qui **invite l'utilisateur à la saisie**.
En affichant les *cross-references* dans IDA, il est alors possible de voir ou
est utilisé cette chaîne et de trouver le registre ou est stocké cette saisie
(enfin l'adresse mémoire contenant la saisie). En retraçant sont utilisation,
nous pouvons voir qu'elle est envoyée dans la fonction `secret`.
Il est primordial d;annoter le code assembleur dans IDA mais aussi de renommer
les variables afin de rendre le code le plus lisible possible. Ainsi nous
pouvons renommer la variable `s` en `buffer`.
Une fois toutes ces étapes réalisées nous pouvons en déduire que la fonction
échappe les caractères qui pourraient être interprétés d'une chaîne résultant
d'une commande `ls`.
## Shellcode
Nous en avons déjà vu lors du cours de sécurité logicielle notamment [le TD
5]({{ <ref ../../secu_logicielle/td5-stackoverflow_shellcode/index.md }}), un
shellcode est une **représentation hexadécimale** d'un **code assembleur**
utilisé comme **charge utile** lors de **l'exploitation d'une vulnerabilité**.
Si nous trouvons un shellcode lors d'une opération de reverse-engineering sur un
binaire, alors nous pouvons en déduire qu'il a été compromis.
Il existe des bibliothèques pour la création de shellcode comme
[pwbtools](https://docs.pwntools.com/en/latest/).
## Protection de binaires
Certain développeurs cherche à protéger leurs binaires contre la
retro-ingénieurie que se soit pour des questions de propriétés intellectuelles ou
de licences. Ils cherchent alors à rendre l'opération plus difficile :
* En ajoutant des *anti-debug* par l'ajout de code pour la détection
d'environnement, de machine virtuelle. Certains code peuvent pas exemple
faire planter *gdb*;
* En obfusquant le code par l'ajout de couche de chiffrement, complexifiant les
opération, ajoutant du code inutile;
* En créant un *bytecode* spécifique et ajoutant une machine virtuelle pour
l'interprétant;
* En compressant le code binaire (packer) via plusieurs technique dont la
suppression de certains éléments (sections etc.).
On considère qu'une protection qui tient 6 mois est de très bonne conception.
La plupart de ces protection on des impact négatifs sur les performances.
### Pratique: analyse statique `SimpleKeyGen`
La première chose à faire ici c'est d'identifier l'architecture et le système.
Comme vu plus haut nous pouvons en déduire la convention d'appel.
Ensuite nous pouvons nous concentrer sur le nom des fonctions connue comme
`printf`, `strlen` etc. Nous pouvons en déduite certains nom de variables.
Dans le cadre de branchement, nous pouvons aussi éliminer les parties inutiles
pour notre analyse comme l'affichage de message d'aide / d'erreurs. Dans IDA,
l'utilisation de couleurs pour les bloc inutiles se révèle pratique.
### Pratique: analyse statique de `CrackMe2`
L'identification nous permet de déduire que nous sommes en présence d'un binaire
*PE* pour architecture *x86_64*. Beaucoup d'éléments sont superflus pour notre
analyse et il est important de les ignorer comme par exemple la partie sur les
`secure_cookie` ou le `password_features`.
Comme nous sommes en présence d'un exécutable Windows, l'exécuter nous permettra
d'en apprendre plus sur ce qu'il faut. Nous pouvons aussi trouver des chaînes de
caractères afficher pour les chercher avec IDA. Avec les *cross-references* nous
pouvons identifier rapidement les parties ou elles sont utilisées.
Une fois les variables identifiées, le code commenté nous déduisons que nous
somme en présence d'une [s-box](https://fr.wikipedia.org/wiki/S-Box) ou boite de
substitution.