Omkeer van natiewe biblioteke
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die đŹ Discord groep of die telegram groep of volg ons op Twitter đŠ @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Vir verdere inligting sien: https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html
Android-apps kan natiewe biblioteke gebruik, tipies geskryf in C of C++, vir prestasie-kritieke take. Malware-skepper misbruik ook hierdie biblioteke omdat ELF shared objects steeds moeiliker te dekompileer is as DEX/OAT byte-code.
Hierdie bladsy fokus op praktiese werksvloeie en onlangse instrumentverbeterings (2023â2025) wat die omkeer van Android .so-lĂȘers vergemaklik.
Vinnige triage-werksvloeie vir ân pas-geĂ«xtraheerde libfoo.so
- Ekstraheer die biblioteek
# From an installed application
adb shell "run-as <pkg> cat lib/arm64-v8a/libfoo.so" > libfoo.so
# Or from the APK (zip)
unzip -j target.apk "lib/*/libfoo.so" -d extracted_libs/
- Identifiseer argitektuur & beskerming
file libfoo.so # arm64 or arm32 / x86
readelf -h libfoo.so # OS ABI, PIE, NX, RELRO, etc.
checksec --file libfoo.so # (peda/pwntools)
- Lys uitgevoerde symboles & JNI-bindings
readelf -s libfoo.so | grep ' Java_' # dynamic-linked JNI
strings libfoo.so | grep -i "RegisterNatives" -n # static-registered JNI
- Laai in ân dekompiler (Ghidra â„ 11.0, IDA Pro, Binary Ninja, Hopper or Cutter/Rizin) en voer auto-analise uit. Nuwe Ghidra-weergawes het ân AArch64 dekompiler wat PAC/BTI stubs en MTE tags herken, wat die analise van biblioteke wat met die Android 14 NDK gebou is baie verbeter.
- Bepaal statiese vs dinamiese omkering: gestrippte, obfuskated kode benodig dikwels instrumentation (Frida, ptrace/gdbserver, LLDB).
Dynamiese instrumentering (Frida â„ 16)
Die Frida 16-reeks het verskeie Android-spesifieke verbeterings gebring wat help wanneer die teiken moderne Clang/LLD optimalisasies gebruik:
thumb-relocatorkan nou klein ARM/Thumb funksies hook wat deur LLD se agressiewe alignment (--icf=all) gegenereer is.- Enumerering en herkoppeling van ELF import slots werk op Android, wat per-module
dlopen()/dlsym()patching moontlik maak wanneer inline hooks verwerp word. - Java hooking is reggemaak vir die nuwe ART quick-entrypoint wat gebruik word wanneer apps met
--enable-optimizationsop Android 14 gekompileer is.
Voorbeeld: die enumerering van alle funksies wat deur RegisterNatives geregistreer is en hul adresse tydens runtime dump:
Java.perform(function () {
var Runtime = Java.use('java.lang.Runtime');
var register = Module.findExportByName(null, 'RegisterNatives');
Interceptor.attach(register, {
onEnter(args) {
var envPtr = args[0];
var clazz = Java.cast(args[1], Java.use('java.lang.Class'));
var methods = args[2];
var count = args[3].toInt32();
console.log('[+] RegisterNatives on ' + clazz.getName() + ' -> ' + count + ' methods');
// iterate & dump (JNI nativeMethod struct: name, sig, fnPtr)
}
});
});
Frida sal sonder ekstra konfigurasie werk op PAC/BTI-geaktiveerde toestelle (Pixel 8/Android 14+) solank jy frida-server 16.2 of later gebruik â vroeĂ«r weergawes kon nie die padding for inline hooks lokaliseer nie.
Proses-lokaal JNI-telemetrie via voorafgelaaide .so (SoTap)
Wanneer full-featured instrumentation oordrewe is of geblokkeer word, kan jy steeds native-vlak sigbaarheid kry deur ân klein logger vooraf in die teikenproses te laai. SoTap is ân liggewig Android native (.so) biblioteek wat die runtime-gedrag van ander JNI (.so) libraries binne dieselfde app-proses log (no root required).
Key properties:
- Initialiseer vroeg en monitor JNI/native-interaksies binne die proses wat dit laai.
- Stoor logs op verskeie skryfbare paaie met ân elegante terugval na Logcat wanneer stoorplek beperk is.
- Bronkode-aanpasbaar: wysig sotap.c om uit te brei/aan te pas wat aangeteken word en herbou per ABI.
Setup (repack the APK):
- Plaas die korrekte ABI-bou in die APK sodat die loader libsotap.so kan oplos:
- lib/arm64-v8a/libsotap.so (vir arm64)
- lib/armeabi-v7a/libsotap.so (vir arm32)
- Verseker SoTap laai voor ander JNI libs. Injiseer ân oproep vroeg (bv., Application subclass static initializer of onCreate) sodat die logger eerste geĂŻnisialiseer word. Smali snippet example:
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
- Herbou/teken/installeer, begin die app, en versamel dan die logs.
Log paths (checked in order):
/data/user/0/%s/files/sotap.log
/data/data/%s/files/sotap.log
/sdcard/Android/data/%s/files/sotap.log
/sdcard/Download/sotap-%s.log
# If all fail: fallback to Logcat only
Notes and troubleshooting:
- ABI-belyning is verpligtend. ân Ongelykheid sal
UnsatisfiedLinkErrorveroorsaak en die logger sal nie laai nie. - Bergruimtelimiete is algemeen op moderne Android; as lĂȘerskryf misluk, sal SoTap steeds via Logcat uitstuur.
- Gedrag/uitvoerigheid is bedoel om aangepas te word; herbou die bron nadat jy sotap.c gewysig het.
Hierdie benadering is nuttig vir malware triage en JNI-foutopsporing waar dit krities is om native oproepvloei vanaf die prosesbegin te observeer, maar root/stelselwye hooks nie beskikbaar is nie.
See also: inâmemory native code execution via JNI
ân Algemene aanvalspatroon is om ân rou shellcode-blob tydens runtime af te laai en dit direk uit geheue via ân JNI-brug uit te voer (geen onâdisk ELF). Besonderhede en ân gereedâomâteâgebruik JNIâsnippet hier:
In Memory Jni Shellcode Execution
Onlangse kwesbaarhede wat die moeite werd is om in APKâs te jaag
| Jaar | CVE | BeĂŻnvloede biblioteek | Aantekeninge |
|---|---|---|---|
| 2023 | CVE-2023-4863 | libwebp †1.3.1 | Heap buffer overflow bereikbaar vanaf native kode wat WebP-beelde decodeer. Verskeie Android-apps bundel kwesbare weergawes. Wanneer jy ân libwebp.so binne ân APK sien, kontroleer die weergawe en probeer eksploitasie of patching. |
| 2024 | Multiple | OpenSSL 3.x series | Verskeie geheueâveiligheid en paddingâoracle probleme. Baie Flutter & ReactNative bundels bevat hul eie libcrypto.so. |
Wanneer jy derde partye .so lĂȘers binne ân APK sien, kruis-vergelyk altyd hul hash teen upstream advisories. SCA (Software Composition Analysis) is ongewoon op mobiel, sodat verouderde kwesbare boue wydverspreid is.
Anti-Reversing & Hardening trends (Android 13-15)
- Pointer Authentication (PAC) & Branch Target Identification (BTI): Android 14 aktiveer PAC/BTI in stelselbiblioteke op ondersteunende ARMv8.3+ silicon. Decompilers wys nou PACâverwante pseudo-instruksies; vir dinamiese ontleding inject Frida trampolines na die verwydering van PAC, maar jou eie trampolines moet
pacda/autibspaanroep waar nodig. - MTE & Scudo hardened allocator: memory-tagging is opsioneel maar baie Play-Integrity-bewuste apps bou met
-fsanitize=memtag; gebruiksetprop arm64.memtag.dump 1plusadb shell am start ...om tag-foute vas te lĂȘ. - LLVM Obfuscator (opaque predicates, control-flow flattening): kommersiĂ«le packers (bv., Bangcle, SecNeo) beskerm toenemend native kode, nie net Java nie; verwag vals control-flow en versleutelde stringâblobs in
.rodata.
Neutralizing early native initializers (.init_array) and JNI_OnLoad for early instrumentation (ARM64 ELF)
Sterk beveiligde apps plaas dikwels root/emulator/debug-kontroles in native constructors wat uiters vroeg via .init_array loop, voor JNI_OnLoad en baie vooraf voordat enige Java-kode uitgevoer word. Jy kan daardie implisiete initialiseerders eksplisiet maak en beheer terugkry deur:
- Verwydering van
INIT_ARRAY/INIT_ARRAYSZuit die DYNAMIC-tabel sodat die loader nie outomaties.init_arrayinskrywings uitvoer nie. - Die constructor-adres te ontsluit vanaf RELATIVE relocations en dit as ân gewone funksie-simbool te exporteer (bv.,
INIT0). - Hernoem
JNI_OnLoadnaJNI_OnLoad0om te verhoed dat ART dit implisiet aanroep.
Waarom dit op Android/arm64 werk
- Op AArch64 word
.init_arrayinskrywings dikwels gevul tydens laai deurR_AARCH64_RELATIVErelocations waarvan die addend die teikenfunksie-adres binne.textis. - Die bytes van
.init_arraykan staties leeg lyk; die dinamiese linker skryf die opgeloste adres tydens relocatieâverwerking.
Identifiseer die constructor teiken
- Gebruik die Android NDK toolchain vir akkurate ELFâparsing op AArch64:
# Adjust paths to your NDK; use the aarch64-linux-android-* variants
readelf -W -a ./libnativestaticinit.so | grep -n "INIT_ARRAY" -C 4
readelf -W --relocs ./libnativestaticinit.so
- Vind die relocatie wat binne die virtuele adresreeks van
.init_arrayland; dieaddendvan daardieR_AARCH64_RELATIVEis die constructor (bv.,0xA34,0x954). - Disassemble rondom daardie adres vir ân sanity check:
objdump -D ./libnativestaticinit.so --start-address=0xA34 | head -n 40
Patch plan
- Verwyder
INIT_ARRAYenINIT_ARRAYSZDYNAMIC tags. Moenie seksies verwyder nie. - Voeg ân GLOBAL DEFAULT FUNC-simbool
INIT0by die constructor-adres sodat dit handmatig aangeroep kan word. - Hernoem
JNI_OnLoadâJNI_OnLoad0om te keer dat ART dit implisiet aanroep.
Validasie na patch
readelf -W -d libnativestaticinit.so.patched | egrep -i 'init_array|fini_array|flags'
readelf -W -s libnativestaticinit.so.patched | egrep 'INIT0|JNI_OnLoad0'
Patching met LIEF (Python)
Skrip: verwyder INIT_ARRAY/INIT_ARRAYSZ, eksporteer INIT0, hernoem JNI_OnLoadâJNI_OnLoad0
```python import liefb = lief.parse(âlibnativestaticinit.soâ)
Locate .init_array VA range
init = b.get_section(â.init_arrayâ) va, sz = init.virtual_address, init.size
Compute constructor address from RELATIVE relocation landing in .init_array
ctor = None for r in b.dynamic_relocations: if va <= r.address < va + sz: ctor = r.addend break if ctor is None: raise RuntimeError(âNo R_*_RELATIVE relocation found inside .init_arrayâ)
Remove auto-run tags so loader skips .init_array
for tag in (lief.ELF.DYNAMIC_TAGS.INIT_ARRAYSZ, lief.ELF.DYNAMIC_TAGS.INIT_ARRAY): try: b.remove(b[tag]) except Exception: pass
Add exported FUNC symbol INIT0 at constructor address
sym = lief.ELF.Symbol() sym.name = âINIT0â sym.value = ctor sym.size = 0 sym.binding = lief.ELF.SYMBOL_BINDINGS.GLOBAL sym.type = lief.ELF.SYMBOL_TYPES.FUNC sym.visibility = lief.ELF.SYMBOL_VISIBILITY.DEFAULT
Place symbol in .text index
text = b.get_section(â.textâ) for idx, sec in enumerate(b.sections): if sec == text: sym.shndx = idx break b.add_dynamic_symbol(sym)
Rename JNI_OnLoad -> JNI_OnLoad0 to block implicit ART init
j = b.get_symbol(âJNI_OnLoadâ) if j: j.name = âJNI_OnLoad0â
b.write(âlibnativestaticinit.so.patchedâ)
</details>
Notas en mislukte benaderings (vir draagbaarheid)
- Om die bytes in `.init_array` op 0 te stel of die afdelinglengte op 0 te stel help nie: die dynamic linker vul dit weer in via relocations.
- Om `INIT_ARRAY`/`INIT_ARRAYSZ` op 0 te stel kan die loader breek weens onsamehangende tags. Skone verwydering van daardie DYNAMIC inskrywings is die betroubare hefboom.
- Om die `.init_array` afdeling heeltemal te verwyder neig om die loader te laat crash.
- Na patching kan funksie-/layout-adresse verskuif; rekalkuleer altyd die constructor vanaf die `.rela.dyn` addende op die gepatchte lĂȘer as jy die patch weer moet uitvoer.
Opbou van 'n minimale ART/JNI om INIT0 en JNI_OnLoad0 aan te roep
- Gebruik JNIInvocation om 'n klein ART VM-konteks op te stel in 'n standalone binary. Roep dan `INIT0()` en `JNI_OnLoad0(vm)` handmatig aan voor enige Java-kode.
- Sluit die teiken APK/classes in op die classpath sodat enige `RegisterNatives` sy Java-klasse kan vind.
<details>
<summary>Minimal harness (CMake and C) to call INIT0 â JNI_OnLoad0 â Java method</summary>
```cmake
# CMakeLists.txt
project(caller)
cmake_minimum_required(VERSION 3.8)
include_directories(AFTER ${CMAKE_SOURCE_DIR}/include)
link_directories(${CMAKE_SOURCE_DIR}/lib)
find_library(log-lib log REQUIRED)
add_executable(caller "caller.c")
add_library(jenv SHARED "jnihelper.c")
target_link_libraries(caller jenv nativestaticinit)
// caller.c
#include <jni.h>
#include "jenv.h"
JavaCTX ctx;
void INIT0();
void JNI_OnLoad0(JavaVM* vm);
int main(){
char *jvmopt = "-Djava.class.path=/data/local/tmp/base.apk"; // include app classes
if (initialize_java_environment(&ctx,&jvmopt,1)!=0) return -1;
INIT0(); // manual constructor
JNI_OnLoad0(ctx.vm); // manual JNI init
jclass c = (*ctx.env)->FindClass(ctx.env, "eu/nviso/nativestaticinit/MainActivity");
jmethodID m = (*ctx.env)->GetStaticMethodID(ctx.env,c,"stringFromJNI","()Ljava/lang/String;");
jstring s = (jstring)(*ctx.env)->CallStaticObjectMethod(ctx.env,c,m);
const char* p = (*ctx.env)->GetStringUTFChars(ctx.env,s,NULL);
printf("Native string: %s\n", p);
cleanup_java_env(&ctx);
}
# Build (adjust NDK/ABI)
cmake -DANDROID_PLATFORM=31 \
-DCMAKE_TOOLCHAIN_FILE=$HOME/Android/Sdk/ndk/26.1.10909125/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a ..
make
Algemene valkuils:
- Konstruktor-adresse verander na patching as gevolg van heruitleg; herbereken altyd vanaf
.rela.dynop die finale binĂȘre. - Verseker dat
-Djava.class.pathelke klas dek wat deurRegisterNatives-oproepe gebruik word. - Gedrag kan verskil met NDK/loader-weergawes; die konsekwent betroubare stap was om
INIT_ARRAY/INIT_ARRAYSZDYNAMIC tags te verwyder.
Verwysings
- Leer ARM Assembly: Azeria Labs â ARM Assembly Basics
- JNI & NDK Dokumentasie: Oracle JNI Spec · Android JNI Tips · NDK Guides
- Foutsporing van native biblioteke: Debug Android Native Libraries Using JEB Decompiler
- Frida 16.x veranderingslog (Android hooking, tiny-function relocation) â frida.re/news
- NVD advies vir
libwebpoverflow CVE-2023-4863 â nvd.nist.gov - SoTap: Liggewig in-app JNI (.so) gedraglogger â github.com/RezaArbabBot/SoTap
- SoTap vrystellings â github.com/RezaArbabBot/SoTap/releases
- Hoe om met SoTap te werk? â t.me/ForYouTillEnd/13
- CoRPhone â JNI memory-only execution pattern and packaging
- Patching Android ARM64 library initializers for easy Frida instrumentation and debugging
- LIEF Project
- JNIInvocation
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die đŹ Discord groep of die telegram groep of volg ons op Twitter đŠ @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
HackTricks

