> 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/librairie-native.md).

# Librairie native

Rétro-ingénierie d'une librairie native pour une app Android

Les applications Android peuvent utiliser des librairies C/C++ compilées. Ce sont des fichiers `.so` (format ELF) comme sur Linux (Android est basé sur un noyau Linux).

## Java Native Interface (JNI)

**La Java Native Interface (JNI)** permet d'**appeler des méthodes implémentées dans le code C/C++ depuis le code Java** de l'application.&#x20;

{% hint style="info" %}
Au lieu d'utiliser JNI, certaines applications peuvent utiliser [JNA](https://github.com/java-native-access/jna). C'est une librairie qui utilise JNI pour simplifier les appels à des fonctions natives.
{% endhint %}

### Chargement en mémoire

Avant d'être appelées, elles doivent être chargées en mémoire (avec `System.loadLibrary` ou `System.load`).

La différence entre les 2 API est que `System.loadLibrary` prend le nom raccourci de la librairie alors que `System.load` prend le chemin absolu.

{% hint style="success" %}
Si le dossier `lib` contient une librairie `libnative-android.so` elle sera importée dans le code Java avec `native-android` (caractères entre `lib` et `.so`) pour `System.loadLibrary`.

```java
static {
    System.loadLibrary("native-android");
    System.load("lib/armeabi/libnative-android.so")
}
```

{% endhint %}

Quand une de ces API est appelée, la fonction `JNI_OnLoad` de la librairie est exécutée si elle existe.

Une fois chargées, les fonctions sont déclarées avec le mot clé `native`.

```java
// Déclaration (le code de add est dans la lib)
public native int add(int a, int b);
```

Quand cette méthode Java est appelée, une fonction associée à celle-ci **dans la librairie native** est exécutée.

**Comment fonctionne l'association entre la fonction dans le code Java et celle dans la librairie ?**

Il existe 2 manières de faire:

* **Lien dynamique** (utilise la résolution de noms de la JNI)
* **Lien statique** (utilise l'API `RegisterNatives`)

### Lien Java/C

#### **Dynamique**

La méthode Java et la fonction C **suivent une convention de nommage** pour que la JNI fasse le lien.

Le nom de la fonction dans la librairie suit ces règles:

* préfixe `"Java_"`
* le nom de la classe
* séparateur `"_"`
* nom de la méthode Java

{% hint style="success" %}

## **Exemple de lien dynamique**

La classe `com.example.app.Math` contient cette méthode native:

```java
public native int add(int a, int b);
```

**Avec le lien dynamique**, la fonction dans la librairie doit s'appeler `Java_com_example_app_Math_add.`

Si la librairie ne contient pas de fonction avec ce nom, le lien statique est utilisé.
{% endhint %}

#### **Statique**

Si le développeur ne peut pas utiliser le *dynamic linking* (pas envie ou pas possible si la librairie est compilée sans symboles ou en cas d'obfuscation), il utilise l'API [RegisterNatives](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp5833).

Le code C appelle la fonction `RegisterNatives` (en général depuis sa fonction `JNI_OnLoad` qui est appelée quand le code Java charge la librairie).

{% hint style="info" %}

## **Pourquoi `RegisterNatives` est appelée dans `JNI_OnLoad` ?**

Parce que `RegisterNatives` doit être appelée avant la méthode native pour que le lien soit déjà établi, sinon ça ne fonctionne pas.
{% endhint %}

La fonction prend 4 arguments:

```c
jint RegisterNatives(
    JNIEnv* env,       // pointeur vers l'env JNI
    jclass clazz,      // classe Java qui contient des méthodes natives
    const JNINativeMethod* methods, // array qui décris le mapping
    jint nMethods      // nombre de méthodes dans l'array
);
```

La structure `JNINativeMethod` est la suivante:

```c
typedef struct {
    const char* name;       // Nom de la méthode Java
    const char* signature;  // Signature JNI de la méthode
    void* fnPtr;            // Pointeur vers la fonction C/C++
} JNINativeMethod;
```

**Qu'est-ce que la signature JNI ?**

Elle précise le **type des arguments** que prend la **méthode Java** et le **type qu'elle renvoie** ([doc](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html)).

| Signature JNI           | Type Java               |
| ----------------------- | ----------------------- |
| Z                       | `boolean`               |
| B                       | `byte`                  |
| C                       | `char`                  |
| S                       | `short`                 |
| I                       | `int`                   |
| J                       | `long`                  |
| F                       | `float`                 |
| D                       | `double`                |
| Lfully/qualified/Class; | `fully-qualified-class` |
| \[type                  | `type[]`                |
| (arg-types)ret-type     | `method type`           |

{% hint style="success" %}

## **Exemple de signature**

```c
long f (int n, String s, int[] arr);
```

Sa signature est `(ILjava/lang/String;[I)J`.

**Arguments**:

* `int -> I`
* `String -> Ljava/lang/String;` (le "fully qualified class" est dans [la doc du type](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) (String ici))
* `int[] -> [I`

**Type renvoyé par la fonction:**

* `long -> J`

**Signature:**

```
(arg-types)ret-type -> (ILjava/lang/String;[I)J
```

{% endhint %}

{% hint style="success" %}

## **Exemple de lien statique**

* Dans le code Java de l'application

```java
package com.example.app;

public class Math {
    static {
        System.loadLibrary("native-android");
    }

    public native int add(int a, int b);
}
```

* La fonction liée dans le code C de la librairie est:

```c
jint native_add(JNIEnv* env, jobject thiz, jint a, jint b) {
    return a + b;
}
```

La fonction `RegisterNatives` de la librairie fait le lien entre `add` et `native_add` grâce à une structure `JNINativeMethod.`

```c
static JNINativeMethod methods[] = {
    {
        "add",        // Nom de la méthode Java
        "(II)I",      // Signature: (int, int) -> int
        (void*)native_add // Pointeur
    }
};
```

La fonction `JNI_OnLoad` de la librairie est appelée lorsque la librairie est chargée par l'application Android et c'est elle qui exécute `RegisterNatives`.

```c
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;

    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }

    jclass clazz = (*env)->FindClass(env, "com/example/app/Math");

    if ((*env)->RegisterNatives(env, clazz, methods, 1) < 0) {
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}
```

{% endhint %}

## Rétro-ingénierie de la librairie avec Ghidra

* Chercher le mot clé `native` avec [jadx](https://github.com/skylot/jadx)
* Pour chaque déclaration de méthode native, identifier la librairie correspondante
  * Chercher les mots clés `System.loadLibrary` ou `System.load` autour de la déclaration
  * Identifier le nom de la librairie
* Extraire la librairie de l'APK

  ```
  unzip APPLICATION.apk
  OU
  apktool d APPLICATION.apk
  ```

  La librairie est dans souvent le dossier `lib`.
* Ouvrir la librairie avec [ghidra](https://github.com/NationalSecurityAgency/ghidra)

{% hint style="info" %}
Si le nom de la méthode native Java est **obfusquée**, c'est du lien statique **-> aller au point \[\*].**
{% endhint %}

* Chercher `Java_` dans la fenêtre des fonctions
  * Si une fonction matche -> `Java_<com.example.app.Class>_<name>`, si elle se termine par le nom de la méthode native dans le code Java c'est celle-ci qu'il faut analyser (les 2 sont liées)
  * Si rien ne match -> c'est du lien statique (voir **\[\*]**)
* **\[\*]** Si le nom de la méthode native Java est obfusqué, chercher la fonction C en utilisant la signature:
  * Chercher la signature JNI de la méthode Java dans les strings
  * Avec les cross-references, hop c'est bon !
* La fonction C liée à la méthode Java prend plusieurs arguments:

  * Un pointeur vers une structure `JNIEnv`
  * Un objet sur laquelle la fonction est exécutée (pas important ici)
  * Les arguments de la méthode Java

  Pour faciliter le reverse, **il faut changer le type du premier argument de la fonction en `JNIEnv*`**.

  **Avec ghidra:**

  * Télécharger le fichier [jni\_all.gdt](https://github.com/Ayrx/JNIAnalyzer/blob/master/JNIAnalyzer/data/jni_all.gdt)
  * Cliquer sur "Open File Archive" dans la fenêtre "Data Type Manager" et ajouter le fichier gdt
  * C'est bon pour changer le type

  Les fonctions de la JNI sont correctement affichées comme ça.

## Sources

{% embed url="<https://www.ragingrock.com/AndroidAppRE/reversing_native_libs.html>" %}
