hacktricks/mobile-pentesting/android-app-pentesting/reversing-native-libraries.md

247 lines
21 KiB
Markdown
Raw Normal View History

2022-04-28 16:01:33 +00:00
<details>
2023-04-25 18:35:28 +00:00
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
2022-04-28 16:01:33 +00:00
2023-06-03 13:10:46 +00:00
- Travaillez-vous dans une entreprise de cybersécurité? Voulez-vous voir votre entreprise annoncée dans HackTricks? ou voulez-vous avoir accès à la dernière version de PEASS ou télécharger HackTricks en PDF? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop)!
2022-04-28 16:01:33 +00:00
2023-06-03 13:10:46 +00:00
- Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection d'[**NFTs**](https://opensea.io/collection/the-peass-family) exclusifs.
2022-04-28 16:01:33 +00:00
2023-06-03 13:10:46 +00:00
- Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
2022-04-28 16:01:33 +00:00
2023-06-03 13:10:46 +00:00
- **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live).
2022-04-28 16:01:33 +00:00
2023-06-03 13:10:46 +00:00
- **Partagez vos astuces de piratage en soumettant des PR au [repo hacktricks](https://github.com/carlospolop/hacktricks) et au [repo hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)**.
2022-04-28 16:01:33 +00:00
</details>
2023-06-03 13:10:46 +00:00
**Informations copiées de** [**https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html**](https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html)** (vous pouvez trouver des solutions là-bas)**
2023-06-03 13:10:46 +00:00
Les applications Android peuvent contenir des bibliothèques natives compilées. Les bibliothèques natives sont du code que le développeur a écrit puis compilé pour une architecture informatique spécifique. Le plus souvent, cela signifie du code écrit en C ou C++. Les raisons bénignes ou légitimes pour lesquelles un développeur peut le faire sont des opérations intensives en mathématiques ou sensibles au temps, telles que les bibliothèques graphiques. Les développeurs de logiciels malveillants ont commencé à passer au code natif car l'ingénierie inverse de binaires compilés tend à être un ensemble de compétences moins courant que l'analyse du bytecode DEX. Cela est largement dû au fait que le bytecode DEX peut être décompilé en Java, tandis que le code natif compilé doit souvent être analysé en tant qu'assemblage.
2023-06-03 13:10:46 +00:00
## Objectif
2023-06-03 13:10:46 +00:00
L'objectif de cette section n'est pas de vous apprendre l'assemblage (ASM) ou comment inverser le code compilé de manière plus générale, mais plutôt comment appliquer les compétences d'ingénierie inverse binaire plus générales, spécifiquement à Android. Parce que l'objectif de cet atelier n'est pas de vous enseigner les architectures ASM, tous les exercices incluront une version ARM _et_ une version x86 de la bibliothèque à analyser afin que chaque personne puisse choisir l'architecture avec laquelle elle est plus à l'aise.
2023-06-03 13:10:46 +00:00
### Apprentissage de l'assemblage ARM <a href="learning-arm-assembly" id="learning-arm-assembly"></a>
2023-06-03 13:10:46 +00:00
Si vous n'avez pas d'expérience préalable en ingénierie inverse binaire / assemblage, voici quelques ressources suggérées. La plupart des appareils Android fonctionnent sur ARM, mais tous les exercices de cet atelier incluent également une version x86 de la bibliothèque.
2023-06-03 13:10:46 +00:00
Pour apprendre et / ou réviser l'assemblage ARM, je suggère fortement les [bases de l'assemblage ARM](https://azeria-labs.com/writing-arm-assembly-part-1/) de [Azeria Labs](https://azeria-labs.com).
2023-06-03 13:10:46 +00:00
## Introduction à l'interface native Java (JNI) <a href="introduction-to-the-java-native-interface-jni" id="introduction-to-the-java-native-interface-jni"></a>
2023-06-03 13:10:46 +00:00
L'interface native Java (JNI) permet aux développeurs de déclarer des méthodes Java qui sont implémentées dans du code natif (généralement compilé en C/C++). L'interface JNI n'est pas spécifique à Android, mais est disponible plus généralement aux applications Java qui s'exécutent sur différentes plates-formes.
2023-06-03 13:10:46 +00:00
Le kit de développement natif Android (NDK) est l'ensemble d'outils spécifique à Android sur JNI. Selon les [docs](https://developer.android.com/ndk/guides):
2023-06-03 13:10:46 +00:00
> Dans Android, le kit de développement natif (NDK) est un ensemble d'outils qui permet aux développeurs d'écrire du code C et C++ pour leurs applications Android.
2023-06-03 13:10:46 +00:00
Ensemble, JNI et NDK permettent aux développeurs Android d'implémenter une partie de la fonctionnalité de leur application en code natif. Le code Java (ou Kotlin) appellera une méthode native déclarée en Java qui est implémentée dans la bibliothèque native compilée.
2023-06-03 13:10:46 +00:00
### Références <a href="references" id="references"></a>
**Oracle JNI Docs**
2023-06-03 13:10:46 +00:00
* [Spécification JNI](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html)
* [Fonctions JNI](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html) < J'ai toujours celui-ci ouvert et je m'y réfère lors de l'inversion de bibliothèques natives Android
2023-06-03 13:10:46 +00:00
**Références JNI & NDK Android**
2023-06-03 13:10:46 +00:00
* [Conseils JNI Android](https://developer.android.com/training/articles/perf-jni) < Je recommande vivement de lire la section "Bibliothèques natives" pour commencer
* [Commencer avec le NDK](https://developer.android.com/ndk/guides/) < Il s'agit d'un guide pour aider les développeurs à développer des bibliothèques natives et à comprendre comment les choses sont construites, ce qui facilite l'inversion.
2023-06-03 13:10:46 +00:00
## Cible d'analyse - Bibliothèques natives Android <a href="target-of-analysis---android-native-libraries" id="target-of-analysis---android-native-libraries"></a>
2023-06-03 13:10:46 +00:00
Pour cette section, nous nous concentrons sur la manière d'inverser la fonctionnalité de l'application qui a été implémentée dans des bibliothèques natives Android. Lorsque nous disons bibliothèques natives Android, qu'entendons-nous?
2023-06-03 13:10:46 +00:00
Les bibliothèques natives Android sont incluses dans les APK sous forme de bibliothèques d'objets partagés `.so`, dans le format de fichier ELF. Si vous avez déjà analysé des binaires Linux, c'est le même format.
2023-06-03 13:10:46 +00:00
Ces bibliothèques sont incluses par défaut dans l'APK au chemin de fichier `/lib/<cpu>/lib<name>.so`. C'est le chemin par défaut, mais les développeurs peuvent également choisir d'inclure la bibliothèque native dans `/assets/<custom_name>` s'ils le souhaitent. Plus souvent, nous voyons les développeurs de logiciels malveillants choisir d'inclure des bibliothèques natives dans des chemins autres que `/lib` et d'utiliser des extensions de fichier différentes pour tenter de "cacher" la présence de la bibliothèque native.
2023-06-03 13:10:46 +00:00
Étant donné que le code natif est compilé pour des CPU spécifiques, si un développeur veut que son application s'exécute sur plus d'un type de matériel, il doit inclure chacune de ces versions de la bibliothèque native compilée dans l'application. Le chemin par défaut mentionné ci-dessus inclut un répertoire pour chaque type de CPU officiellement pris en charge par Android.
2023-06-03 13:10:46 +00:00
| CPU | Chemin de la bibliothèque native |
| -------------------- | ---------------------------- |
2023-06-03 13:10:46 +00:00
| ARM 32 bits générique | `lib/armeabi/libcalc.so` |
| x86 | `lib/x86/libcalc.so` |
| x64 | `lib/x86_64/libcalc.so` |
| ARMv7 | `lib/armeabi-v7a/libcalc.so` |
| ARM64 | `lib/arm64-v8a/libcalc.so` |
2023-06-03 13:10:46 +00:00
## Chargement de la bibliothèque <a href="loading-the-library" id="loading-the-library"></a>
2023-06-03 13:10:46 +00:00
Avant qu'une application Android ne puisse appeler et exécuter un code implémenté dans une bibliothèque native, l'application (code Java) doit charger la bibliothèque en mémoire. Il existe deux appels d'API différents qui feront cela:
```
System.loadLibrary("calc")
```
2023-06-03 13:10:46 +00:00
Voici le contenu traduit en français :
# Rétro-ingénierie de bibliothèques natives
## Introduction
2023-06-03 13:10:46 +00:00
Les bibliothèques natives sont des fichiers binaires compilés qui contiennent du code machine pour une architecture spécifique. Les bibliothèques natives sont souvent utilisées pour fournir des fonctionnalités supplémentaires à une application, telles que l'accès à des fonctionnalités du système d'exploitation ou à des bibliothèques tierces. Les bibliothèques natives sont souvent utilisées dans les applications Android pour fournir des fonctionnalités telles que la prise en charge de la caméra, l'accès aux bases de données, etc.
## Extraire les bibliothèques natives
Les bibliothèques natives sont généralement stockées dans le répertoire `lib` de l'APK. Pour extraire les bibliothèques natives, vous pouvez utiliser l'outil `apktool`. L'outil `apktool` est un outil open source qui permet de décompiler et de recompiler des APK.
Pour extraire les bibliothèques natives, vous pouvez utiliser la commande suivante :
```
2023-06-03 13:10:46 +00:00
apktool d -s -o <output_directory> <apk_file>
```
2023-06-03 13:10:46 +00:00
Cette commande extraira les bibliothèques natives dans le répertoire de sortie spécifié.
## Analyse des bibliothèques natives
2023-06-03 13:10:46 +00:00
Les bibliothèques natives sont des fichiers binaires compilés, ce qui signifie qu'elles ne peuvent pas être lues directement. Pour analyser les bibliothèques natives, vous devez les désassembler en code assembleur.
2023-06-03 13:10:46 +00:00
Il existe plusieurs outils pour désassembler les bibliothèques natives, tels que `IDA Pro`, `Hopper`, `Ghidra`, etc. Ces outils permettent de désassembler les bibliothèques natives en code assembleur et de les analyser.
2023-06-03 13:10:46 +00:00
## Recherche de vulnérabilités
2023-06-03 13:10:46 +00:00
Une fois que vous avez désassemblé les bibliothèques natives, vous pouvez rechercher des vulnérabilités dans le code. Les vulnérabilités courantes dans les bibliothèques natives comprennent les dépassements de tampon, les fuites de mémoire, les erreurs de format de chaîne, etc.
2023-06-03 13:10:46 +00:00
Pour rechercher des vulnérabilités dans le code, vous pouvez utiliser des outils tels que `grep`, `strings`, `objdump`, etc. Ces outils permettent de rechercher des chaînes de caractères spécifiques dans le code, de rechercher des appels de fonctions dangereuses, etc.
2023-06-03 13:10:46 +00:00
## Conclusion
La rétro-ingénierie des bibliothèques natives est une compétence importante pour les testeurs de pénétration et les chercheurs en sécurité. En comprenant comment les bibliothèques natives sont utilisées dans les applications Android et comment les analyser, vous pouvez identifier les vulnérabilités et aider à sécuriser les applications.
```
2023-06-03 13:10:46 +00:00
System.load("lib/armeabi/libcalc.so")
```
2023-06-03 13:10:46 +00:00
La différence entre les deux appels d'API est que `loadLibrary` ne prend que le nom court de la bibliothèque en argument (c'est-à-dire libcalc.so = "calc" et libinit.so = "init") et le système détermine correctement l'architecture sur laquelle il s'exécute et donc le fichier correct à utiliser. D'autre part, `load` nécessite le chemin complet de la bibliothèque. Cela signifie que le développeur de l'application doit déterminer l'architecture et donc le fichier de bibliothèque correct à charger lui-même.
Lorsque l'un de ces deux API (`loadLibrary` ou `load`) est appelé par le code Java, la bibliothèque native qui est passée en argument exécute son `JNI_OnLoad` si elle a été implémentée dans la bibliothèque native.
Pour réitérer, avant d'exécuter des méthodes natives, la bibliothèque native doit être chargée en appelant `System.loadLibrary` ou `System.load` dans le code Java. Lorsque l'une de ces 2 API est exécutée, la fonction `JNI_OnLoad` dans la bibliothèque native est également exécutée.
2023-06-03 13:10:46 +00:00
## La connexion entre le code Java et le code natif <a href="the-java-to-native-code-connection" id="the-java-to-native-code-connection"></a>
2023-06-03 13:10:46 +00:00
Pour exécuter une fonction de la bibliothèque native, il doit y avoir une méthode native déclarée en Java que le code Java peut appeler. Lorsque cette méthode native déclarée en Java est appelée, la fonction native "associée" de la bibliothèque native (ELF/.so) est exécutée.
Une méthode native déclarée en Java apparaît dans le code Java comme ci-dessous. Elle apparaît comme n'importe quelle autre méthode Java, sauf qu'elle inclut le mot-clé `native` et n'a pas de code dans sa mise en œuvre, car son code est en fait dans la bibliothèque native compilée.
```
public native String doThingsInNativeLibrary(int var0);
```
Pour appeler cette méthode native, le code Java l'appellerait comme n'importe quelle autre méthode Java. Cependant, en arrière-plan, le JNI et le NDK exécuteraient plutôt la fonction correspondante dans la bibliothèque native. Pour ce faire, il doit connaître l'appariement entre une méthode native déclarée en Java et une fonction dans la bibliothèque native.
2023-06-03 13:10:46 +00:00
Il existe 2 façons différentes de faire cet appariement, ou de le lier :
2023-06-03 13:10:46 +00:00
1. Liaison dynamique en utilisant la résolution de nom de méthode native JNI, ou
2. Liaison statique en utilisant l'appel d'API `RegisterNatives`
2023-06-03 13:10:46 +00:00
### Liaison dynamique <a href="dynamic-linking" id="dynamic-linking"></a>
2023-06-03 13:10:46 +00:00
Pour lier, ou appairer, la méthode native déclarée en Java et la fonction dans la bibliothèque native de manière dynamique, le développeur nomme la méthode et la fonction selon les spécifications de sorte que le système JNI puisse faire la liaison dynamiquement.
2023-06-03 13:10:46 +00:00
Selon la spécification, le développeur nommerait la fonction comme suit pour que le système puisse lier dynamiquement la méthode native et la fonction. Un nom de méthode native est concaténé à partir des composants suivants :
2023-06-03 13:10:46 +00:00
1. le préfixe Java\_
2. un nom de classe qualifié entièrement manglé
3. un séparateur de soulignement ("\_")
4. un nom de méthode manglé
5. pour les méthodes natives surchargées, deux traits de soulignement ("\__") suivis de la signature d'argument manglée
2023-06-03 13:10:46 +00:00
Pour effectuer une liaison dynamique pour la méthode native déclarée en Java ci-dessous et disons qu'elle se trouve dans la classe `com.android.interesting.Stuff`
```
public native String doThingsInNativeLibrary(int var0);
```
2023-06-03 13:10:46 +00:00
La fonction dans la bibliothèque native devrait être nommée:
```
Java_com_android_interesting_Stuff_doThingsInNativeLibrary
```
2023-06-03 13:10:46 +00:00
Si une fonction avec ce nom n'existe pas dans la bibliothèque native, cela signifie que l'application doit effectuer une liaison statique.
2023-06-03 13:10:46 +00:00
### Liaison statique <a href="static-linking" id="static-linking"></a>
2023-06-03 13:10:46 +00:00
Si le développeur ne veut pas ou ne peut pas nommer les fonctions natives selon la spécification (par exemple, s'il veut supprimer les symboles de débogage), il doit utiliser la liaison statique avec l'API `RegisterNatives` ([doc](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp5833)) afin de faire correspondre la méthode native déclarée en Java avec la fonction dans la bibliothèque native. La fonction `RegisterNatives` est appelée à partir du code natif, pas du code Java, et est le plus souvent appelée dans la fonction `JNI_OnLoad`, car `RegisterNatives` doit être exécutée avant d'appeler la méthode native déclarée en Java.
```
jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
```
2023-06-03 13:10:46 +00:00
Lors de l'ingénierie inverse, si l'application utilise la méthode de liaison statique, nous, en tant qu'analystes, pouvons trouver la structure `JNINativeMethod` qui est transmise à `RegisterNatives` afin de déterminer quelle sous-routine de la bibliothèque native est exécutée lorsque la méthode native déclarée en Java est appelée.
2023-06-03 13:10:46 +00:00
La structure `JNINativeMethod` nécessite une chaîne de caractères pour le nom de la méthode native déclarée en Java et une chaîne de caractères pour la signature de la méthode, nous devrions donc être en mesure de les trouver dans notre bibliothèque native.
2023-06-03 13:10:46 +00:00
**Signature de méthode**
2023-06-03 13:10:46 +00:00
La structure `JNINativeMethod` nécessite la signature de la méthode. Une signature de méthode indique les types des arguments que la méthode prend et le type de ce qu'elle renvoie. Ce lien documente les [Signatures de type JNI](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html) dans la section "Signatures de type".
* Z: boolean
* B: byte
* C: char
* S: short
* I: int
* J: long
* F: float
* D: double
2023-06-03 13:10:46 +00:00
* L fully-qualified-class ; :classe entièrement qualifiée
* \[ type: type\[]
2023-06-03 13:10:46 +00:00
* ( arg-types ) ret-type: type de méthode
* V: void
2023-06-03 13:10:46 +00:00
Pour la méthode native
```
public native String doThingsInNativeLibrary(int var0);
```
2023-06-03 13:10:46 +00:00
La signature de type est
```
(I)Ljava/lang/String;
```
2023-06-03 13:10:46 +00:00
Voici un autre exemple de méthode native et de sa signature. Pour cela, voici la déclaration de la méthode :
```
public native long f (int n, String s, int[] arr);
```
2023-06-03 13:10:46 +00:00
Il a la signature de type :
```
(ILjava/lang/String;[I)J
```
2023-06-03 13:10:46 +00:00
### Exercice #5 - Trouver l'adresse de la fonction native <a href="exercise-5---find-the-address-of-the-native-function" id="exercise-5---find-the-address-of-the-native-function"></a>
2023-06-03 13:10:46 +00:00
Dans l'exercice #5, nous allons apprendre à charger des bibliothèques natives dans un désassembleur et à identifier la fonction native qui est exécutée lorsqu'une méthode native est appelée. Pour cet exercice particulier, le but n'est pas de rétro-ingénierie la méthode native, mais simplement de trouver le lien entre l'appel à la méthode native en Java et la fonction qui est exécutée dans la bibliothèque native. Pour cet exercice, nous utiliserons l'échantillon Mediacode.apk. Cet échantillon est disponible à `~/samples/Mediacode.apk` dans la VM. Son hachage SHA256 est a496b36cda66aaf24340941da8034bd53940d1b08d83a97f17a65ae144ebf91a.
2023-06-03 13:10:46 +00:00
**Objectif**
2023-06-03 13:10:46 +00:00
L'objectif de cet exercice est de :
2023-06-03 13:10:46 +00:00
1. Identifier les méthodes natives déclarées dans le bytecode DEX
2. Déterminer quelles bibliothèques natives sont chargées (et donc où les méthodes natives peuvent être implémentées)
3. Extraire la bibliothèque native de l'APK
4. Charger la bibliothèque native dans un désassembleur
5. Identifier l'adresse (ou le nom) de la fonction dans la bibliothèque native qui est exécutée lorsque la méthode native est appelée
**Instructions**
2023-06-03 13:10:46 +00:00
1. Ouvrez Mediacode.apk dans jadx. Référez-vous à l'[Exercice #1](https://maddiestone.github.io/AndroidAppRE/reversing_intro.html#exercise-1---beginning-re-with-jadx)
2. Cette fois, si vous développez l'onglet Ressources, vous verrez que cet APK a un répertoire `lib/`. Les bibliothèques natives pour cet APK sont dans les chemins CPU par défaut.
3. Maintenant, nous devons identifier toutes les méthodes natives déclarées. Dans jadx, recherchez et répertoriez toutes les méthodes natives déclarées. Il devrait y en avoir deux.
4. Autour de la méthode native déclarée, voyez s'il y a un endroit où une bibliothèque native est chargée. Cela fournira des indications sur la bibliothèque native dans laquelle chercher la fonction à implémenter.
5. Extrayez la bibliothèque native de l'APK en créant un nouveau répertoire et en copiant l'APK dans ce dossier. Ensuite, exécutez la commande `unzip Mediacode.APK`. Vous verrez tous les fichiers extraits de l'APK, y compris le répertoire `lib/`.
6. Sélectionnez l'architecture de la bibliothèque native que vous souhaitez analyser.
7. Démarrez ghidra en exécutant `ghidraRun`. Cela ouvrira Ghidra.
8. Pour ouvrir la bibliothèque native pour l'analyse, sélectionnez "Nouveau projet", "Projet non partagé", sélectionnez un chemin pour enregistrer le projet et donnez-lui un nom. Cela crée un projet dans lequel vous pouvez ensuite charger des fichiers binaires.
9. Une fois que vous avez créé votre projet, sélectionnez l'icône du dragon pour ouvrir le navigateur de code. Allez dans "Fichier" > "Importer un fichier" pour charger la bibliothèque native dans l'outil. Vous pouvez laisser tous les paramètres par défaut.
10. Vous verrez l'écran suivant. Sélectionnez "Analyser".
11. À l'aide des informations de liaison ci-dessus, identifiez la fonction dans la bibliothèque native qui est exécutée lorsque la méthode native déclarée en Java est appelée.
2023-06-03 13:10:46 +00:00
![Chargement du fichier dans le navigateur de code Ghidra](https://maddiestone.github.io/AndroidAppRE/images/loadingIntoGhidra.png)
2023-06-03 13:10:46 +00:00
![Capture d'écran de Mediacode ouvert dans jadx](https://maddiestone.github.io/AndroidAppRE/images/Mediacode.InJadx.png)
**Solution**
2023-06-03 13:10:46 +00:00
## Rétro-ingénierie du code des bibliothèques natives Android - JNIEnv <a href="reversing-android-native-libraries-code---jnienv" id="reversing-android-native-libraries-code---jnienv"></a>
2022-04-28 16:01:33 +00:00
2023-06-03 13:10:46 +00:00
Lorsque vous commencez à rétro-ingénierie des bibliothèques natives Android, l'une des choses que je ne savais pas que je devais savoir était à propos de `JNIEnv`. `JNIEnv` est une structure de pointeurs de fonction vers les [fonctions JNI](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html). Chaque fonction JNI dans les bibliothèques natives Android prend `JNIEnv*` comme premier argument.
2022-04-28 16:01:33 +00:00
2023-06-03 13:10:46 +00:00
De la documentation [JNI Tips](https://developer.android.com/training/articles/perf-jni)