Continue reverse engineering notes
This commit is contained in:
parent
0d1c90448c
commit
d4d633a1bc
1 changed files with 144 additions and 11 deletions
|
@ -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
|
||||
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"}})
|
||||
|
||||
### 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
|
||||
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 :
|
||||
|
||||
* `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
|
||||
(*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
|
||||
|
||||
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
|
||||
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é
|
||||
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">}})
|
||||
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
|
||||
Wikipedia][carry_flag].
|
||||
|
||||
|
@ -194,7 +194,7 @@ hell:
|
|||
|
||||
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
|
||||
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
|
||||
s'interface aussi avec des débogueras notamment GDB.
|
||||
|
||||
|
@ -276,7 +276,7 @@ binaire.
|
|||
|
||||
#### 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 :
|
||||
|
||||
```asm
|
||||
|
@ -305,7 +305,6 @@ main() {
|
|||
Ce qui donnera en assembleur :
|
||||
|
||||
```asm
|
||||
|
||||
main:
|
||||
push ebp ; backup current base pointer
|
||||
mov ebp, esp ; get the new base pointer
|
||||
|
@ -325,5 +324,139 @@ 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.
|
||||
La décente de `0x18` dans la pile permet à la fonction appelée de mettre en
|
||||
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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue