> For the complete documentation index, see [llms.txt](https://sansong.gitbook.io/cyber/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sansong.gitbook.io/cyber/rev/android.md).

# Android

Android Package

## Architecture d'une application Android

### Langage de programmation

Une application Android peut être développée en plusieurs langages:

* Kotlin (exécuté par la JVM, syntaxe plus concise que Java)
* Java
* C++ (pour les librairies natives, on en reparle plus loin)

{% hint style="success" %}
Exemple de Kotlin vs Java

```kotlin
// Kotlin
fun main() {
    println("Hello, World!")
}
```

```java
// Java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
```

{% endhint %}

Le code de l'application est compilé et embarqué avec toutes les ressources nécessaires à son fonctionnement (images, données, etc.) dans une archive **APK (Android Package)**.

### APK

Le contenu de l'archive peut être extrait avec `apktool` ou n'importe quel outil utilisable pour des archives (`unzip`, `7zip`...)

```shell
apktool d APPLICATION.apk -o OUTPUT_FOLDER
```

L'arborescence de l'archive ressemble à ça:

```
├── assets/
├── kotlin/
├── lib/
├── META-INF/
├── res/
├── AndroidManifest.xml
├── classes.dex
├── resources.arsc
├── [...]
```

Le tableau suivant récapitule le rôle des dossiers/fichiers.

{% tabs %}
{% tab title="assets" %}
Stocke des fichiers utilisés par l'application (images, audios, etc.), peut inclure du code
{% endtab %}

{% tab title="kotlin" %}
Dossier présent si l'application est développée en Kotlin, contient des données spécifiques au langage
{% endtab %}

{% tab title="lib" %}
Librairies natives compilées séparées dans des dossiers en fonction de l'architecture CPU (arm32, arm64, x86, x64)
{% endtab %}

{% tab title="META-INF" %}
Certificat utilisé pour signer l'APK
{% endtab %}

{% tab title="res" %}
Ressources non compilées dans `resources.arsc`
{% endtab %}

{% tab title="resources.arsc" %}
Ressources qui ne sont pas compilées dans `res`. Le fichier `strings.xml` contient les chaînes de caractères utilisées par l'application
{% endtab %}

{% tab title="classes.dex" %}
Bytecode Dalvik (code Java ou Kotlin compilé) exécuté par l'application
{% endtab %}

{% tab title="AndroidManifest.xml" %}
Fichier décrivant l'application (ses composants, permissions, etc.)
{% endtab %}
{% endtabs %}

### Modèle de sécurité d'Android

Chaque application se trouve dans sa propre *sandbox* isolée au niveau des processus et des fichiers.

* **Chaque application correspond à un utilisateur Linux unique**. Un UID est associé à chaque application et **le processus est exécuté en tant que cet utilisateur**.
* **Chaque application s'exécute dans son propre processus Linux et est indépendante des autres**. Le système Android démarre le processus lorsqu'un des composants de l'application doit être exécuté, puis arrête le processus lorsqu'il n'est plus utilisé ou quand le système doit récupérer de la mémoire pour d'autres applications.
* Chaque application a **un répertoire privé accessible par elle seule** en lecture/écriture.
* **Chaque application, par défaut, n'a accès qu'aux composants dont elle a besoin pour fonctionner, et rien de plus.** Une application ne peut pas accéder à des parties du système pour lesquelles elle n'a pas reçu d'autorisation.

Depuis Android 5.0, SELinux est en mode `enforced`: les actions non autorisées sont bloquées et toutes les tentatives de violation sont enregistrées par le noyau dans `dmesg` et `logcat`.

Chaque application est identifiée sur le système par un identifiant unique appelé nom de package (ça ressemble généralement à `com.example.app`).

{% hint style="success" %}
Pour plus d'infos sur le système d'exploitation et ses mécanismes de sécurité voir [Android](/cyber/os/android.md).
{% endhint %}

### Librairies natives

Android utilise une version personnalisée du noyau Linux. Du code natif C/C++ sous la forme de librairies (fichiers `.so`) peuvent être compilés dans l'APK pour différentes architectures de processeurs.

{% hint style="success" %}
Voir [Librairie native](/cyber/rev/android/librairie-native.md) pour de la rétro-ingénierie sur une librairie native.
{% endhint %}

### Composants d'une application

Un aspect unique d'Android est que **toute application peut démarrer un composant d'une autre application**.

Il existe 4 types de composants:

* **Activity**
* **Service**
* **Broadcast receiver**
* **Content provider**

#### Activity

Il faut voir chaque *Activité* comme un des écrans de l'application.

#### Service

Un *Service* s'exécute en arrière-plan sans interface utilisateur.

#### Broadcast receiver

Les *broadcasts* peuvent être considérées comme des messages envoyés à tout le système et les *receivers* comme des composants qui écoutent et attendent un message en particulier.&#x20;

Un *Broadcast receiver* attend un *broadcast* spécifique et est exécuté quand ce dernier est envoyé.

#### Content provider

Un C*ontent provider* permet d'accéder aux fichiers d'une application. Il sert à partager des données entre les applications. Une application peut aussi utiliser un *provider* pour accéder à ses propres données.&#x20;

Une application A peut utiliser le *Content provider* d'une application B pour accéder aux données A.

#### Composant exporté

**Chaque composant peut être exporté pour que les autres applications y aient accès.** Par défaut, un composant n'est pas exporté et n'est accessible que par les composants de sa propre application.

Une *Activité* et un *Service* exporté peuvent être démarrés par une autre application.

Un *Content provider* exporté permet à d'autres applications d'accéder aux ressources accessibles par celui-ci.

Un *Broadcast receiver* exporté peut écouter un broadcast venant d'une autre application.

**Un composant exporté peut être démarré par une autre application avec un `Intent`.**

#### Intent

Un `Intent` est un objet qui spécifie l'**intent**ion de démarrer un composant particulier (de la même app ou d'une autre app installée sur le téléphone). Ils sont utilisés pour :

* démarrer une *Activité* (un écran de l'application)
* démarrer un *Service* (fait des opérations en arrière plan sans interface utilisateur)
* envoyer un message en *broadcast* (à toutes les applications du système)

Il y a 2 types d'*intents*:

* **explicite**: le composant demandé est clairement identifié avec son nom, on sait quel composant de l'application doit être appelé.

{% hint style="info" %}
Pour démarrer un composant de la même application, un *intent* explicite est généralement utilisé comme le développeur connait le nom de la classe à utiliser.
{% endhint %}

* **implicite**: aucun composant spécifique n'est mentionné. Une action à effectuer est définie et un composant de la même ou d'une autre application est démarré si il déclare pouvoir s'occuper de cette action. Android choisi le composant à démarrer.

{% hint style="info" %}
Par exemple, pour montrer sa localisation à l'utilisateur, un *intent* implicite peut être utilisé pour demander à une autre application qui à cette fonctionnalité d'ouvrir une carte (google maps par exemple).
{% endhint %}

**Comment savoir si une autre application à les moyens de satisfaire un intent implicite ?**

Un `Intent` est un message qui contient plusieurs paramètres:

* **action**: l'action à effectuer (ex: afficher quelque chose)
* **data**: les données sur lesquelles agir
* **category**: du contexte
* **extras**: des données supplémentaires

Un composant qui se déclare comme capable de satisfaire un *intent* spécifique doit définir un `intent-filter` dans sa balise XML du fichier `AndroidManifest.xml`. Cet `intent-filter` décrit précisément comment démarrer le composants avec un `Intent`.&#x20;

Quand un *intent* implicite est utilisé, Android cherche le composant approprié en comparant le contenu de l'*intent* avec ceux des `intent-filters` déclarés dans le fichier `AndroidManifest.xml` des applications installée sur le téléphone.

Si une correspondance est trouvée, le système démarre le bon composant avec un `Intent`. Si plusieurs composants capables d'effectuer l'action demandée sont trouvés, une fenêtre demande à l'utilisateur de choisir une application.

Si aucun `intent-filter` n'est déclaré, l'*activité* ne peut être démarrée qu'avec un *intent* explicite.

**Un composant qui contient un `intent-filter` est forcément exporté.**

### Permissions

Les applications n'ont accès **qu'aux ressources auxquelles ont leur a donné explicitement accès**. Elles doivent donc demander des permissions. Certaines permissions sont données automatiquement à l'installation alors que d'autres sont demandées à l'exécution en fonction des besoins.

Toutes les permissions **demandées par l'application** sont déclarées dans le fichier `AndroidManifest.xml` sous la balise `<uses-permissions>`.

Il existe 3 types de permissions:

* **normale**: permission faible qui est donnée automatiquement à l'installation (*ex: accès internet*)
* **dangereuse**: permission plus sensible qui nécessite une confirmation de l'utilisateur (*ex: accéder à la localisation, aux photos...*)
* **spéciale**: permissions sensible qui doit être activée manuellement dans les options (*ex: modifier les paramètres du téléphone*)

A chaque permission est associée un niveau de protection parmi les suivants:

* `normal`: pour les permissions normales (une app qui demande une permission de ce niveau est garantie de l'avoir)
* `dangerous`: pour les permissions dangereuses (la permission est accordée si l'utilisateur accepte)
* `signature`: **ce niveau de protection est le plus fort**. Une app qui demande une permission dont le niveau de protection est `signature` ne l'aura que **si l'app est signée avec le même certificat que l'app qui a déclarée la permission**.

Des permissions personnalisées peuvent être crées avec la balise `<permission>`.

Le niveau de protection de chaque permission dépend de son type et est indiqué sur la page de référence de [l'API sur les permissions](https://developer.android.com/reference/android/Manifest.permission).

#### **Récapitulatif des permissions**

Une application **demande des permissions** avec la balise `<uses-permission>`. **En fonction du niveau de protection des permissions demandées, elles sont attribuées ou pas**.

Une application **peut protéger ses composants exportés** (donc accessibles aux autres app sur le téléphone) **avec des permissions**.

### AndroidManifest.xml

C'est le fichier le plus important pour commencer une analyse.

**Il contient plein d'informations sur les composants de l'application, les permissions**, les features logiciels et matériels nécessaires, la version minimale d'Android ciblée, etc.

Parmi les informations importantes il y a:

* le **numéro de version minimum et ciblé d'Android** avec la balise `<uses-sdk>`: voir `minSdkVersion` et `targetSdkVersion`
  * le lien entre le niveau d'API et la version d'Android peut être fait [ici](https://apilevels.com/)
* les **composants exportés et les protections associées**
  * activités
  * services
  * receivers
  * content providers
* les **permissions demandées** (balise `<uses-permission>`)
* les **permission personnalisées et leur niveau de protection** (balise `<permission>`)
* les *features* logiciels et matériels (balise `<uses-feature>`)
  * l'option `required` spécifie si le *feature* est indispensable au fonctionnement de l'appli
* le nom du `package` (utile pour écrire des scripts frida par exemple)
  * exemple: `package="com.example.myapp"`
* l'existence d'une sous classe de `Application` dans la balise `<application>` (champ `"android:name"`)

### Lancement d'une application

Contrairement aux applications de bureau, les applications mobiles ne démarrent pas toujours de la même manière, sur la même page.

{% hint style="info" %}
Par exemple, si on ouvre une application de Mails depuis l'écran d'accueil du téléphone on peut imaginer tomber sur une page qui affiche les mails reçus.

Mais si on utilise une autre application qui elle-même ouvre notre appli de Mails, on pourrait arriver directement sur une autre page (celle pour écrire des mails par exemple).
{% endhint %}

Quand on tape sur l'icône d'une application sur l'écran, un intent implicite particulier est envoyé. Il contient toujours la même action et catégorie:

* `android.intent.action.MAIN`
* `android.intent.category.LAUNCHER`

Android regarde les `intent-filters` des activités de l'application sélectionnée et démarre celle qui correspond.

{% hint style="info" %}

## Exemple d'activité de démarrage

```xml
// AndroidManifest.xml
<activity
    android:name="com.example.MainActivity"
    [...]
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>  <--- ici
        <category android:name="android.intent.category.LAUNCHER"/>  <--- ici
    </intent-filter>
</activity>
```

{% endhint %}

{% hint style="warning" %}
Cette activité n'est pas forcément la première exécutée quand le processus démarre. La classe `Application` est **la première instanciée lorsque le processus de l'application est lancé. Si l'application implémente une sous-classe de la classe `Application`, elle sera exécutée avant**. Cela permet une initialisation précoce avant que le reste de l'application ne démarre.

Si le champ `android:name` de la balise `<application>` dans `AndroidManifest.xml` est défini, celui-ci correspond au réel point d'entrée de l'application (avant que l'activité de démarrage ne soit lancée).
{% endhint %}

### Stockage

Pour stocker ses données sur le téléphone, une application dispose de plusieurs moyens:

* stockage privé (interne et externe)
* stockage partagé (public)

#### Stockage privé

* **Interne**

Le répertoire privé `/data/user/<id>/<package>` **n'est accessible qu'à l'application**.

`<id>` correspond à l'identifiant de l'utilisateur. Sur la plupart des téléphones, un seul utilisateur est installé (`id=0`).

Le répertoire `/data/data/<package>` est un lien vers `/data/user/0/<package>`. Sur les téléphones avec un seul utilisateur, il permet d'accéder au répertoire privé.

Tous les fichiers y sont supprimés quand l'application est désinstallée.

L'arborescence du répertoire est la suivante:

```
cache code_cache databases files shared_prefs
```

{% tabs %}
{% tab title="cache" %}
Fichiers temporaires de cache
{% endtab %}

{% tab title="code\_cache" %}
Fichiers temporaires de cache
{% endtab %}

{% tab title="databases" %}
Bases de données SQLite accessibles par l'application nativement
{% endtab %}

{% tab title="files" %}
Fichiers quelconques
{% endtab %}

{% tab title="shared\_prefs" %}
Fichiers XML sous la forme de clé/valeur pour stocker de la configuration généralement (par défaut en clair mais il est possible de les chiffrer)
{% endtab %}
{% endtabs %}

* **Externe**

L'application a aussi un répertoire privé dans le stockage externe du téléphone `/storage/emulated/<id>/Android/data/<package>/files/`. **Il n'est accessible qu'à l'application**.

Ce répertoire n'est **pas fait pour partager des données entre application** mais pour augmenter la capacité de stockage si le stockage privé interne ne suffit pas.

#### Stockage partagé

Par exemple:

* Carte SD
* Téléchargements (`/storage/emulated/<id>/Download`)
* Images (`/storage/emulated/<id>/Pictures`)
* Documents (`storage/emulated/<id>/Documents`)

Besoin de permissions pour accéder à des données écrits sur le stockage partagé par d'autres applications.

## Décompilation

### jadx

{% embed url="<https://github.com/skylot/jadx>" %}

## Émulation

Plusieurs émulateurs sont utilisables pour remplacer un vrai téléphone.

* **Android Studio**

{% embed url="<https://developer.android.com/studio?hl=fr>" %}

* **Genymotion**

{% embed url="<https://www.genymotion.com/>" %}

* **budtmo**

{% embed url="<https://github.com/budtmo/docker-android>" %}

```
docker run --privileged -d -p 6080:6080 -p 5554:5554 -p 5555:5555 -p 4723:4723 -e EMULATOR_DEVICE="Samsung Galaxy S10" -e WEB_VNC=true --device /dev/kvm --name android-container <image>
```

{% hint style="success" %}
Choisir le téléphone avec `-e EMULATOR_DEVICE` et l'image du conteneur en fonction de la version d'Android souhaitée: <https://hub.docker.com/r/budtmo/docker-android/tags>
{% endhint %}

{% hint style="danger" %}
Il faut vérifier que `vnc` fonctionne après avoir lancé le conteneur pour pouvoir avoir accès à l'interface du téléphone à `http://localhost:6080.`

```
docker logs <id du conteneur>
```

{% endhint %}

## Instrumentation

{% embed url="<https://frida.re/>" %}

## Debug

{% embed url="<https://developer.android.com/tools/releases/platform-tools?hl=fr>" %}
adb
{% endembed %}
