Reversing Native Libraries
Tip
Jifunze na fanya mazoezi ya AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.
For further information check: https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html
Android apps can use native libraries, typically written in C or C++, for performance-critical tasks. Malware creators also abuse these libraries because ELF shared objects are still harder to decompile than DEX/OAT byte-code.
Ukurasa huu unazingatia praktical workflows na recent tooling improvements (2023-2025) ambazo zinafanya reversing ya Android .so files kuwa rahisi.
Quick triage-workflow for a freshly pulled libfoo.so
- Extract the library
# 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/
- Identify architecture & protections
file libfoo.so # arm64 or arm32 / x86
readelf -h libfoo.so # OS ABI, PIE, NX, RELRO, etc.
checksec --file libfoo.so # (peda/pwntools)
- List exported symbols & JNI bindings
readelf -s libfoo.so | grep ' Java_' # dynamic-linked JNI
strings libfoo.so | grep -i "RegisterNatives" -n # static-registered JNI
- Load in a decompiler (Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper or Cutter/Rizin) and run auto-analysis. Newer Ghidra versions introduced an AArch64 decompiler that recognises PAC/BTI stubs and MTE tags, greatly improving analysis of libraries built with the Android 14 NDK.
- Decide on static vs dynamic reversing: stripped, obfuscated code often needs instrumentation (Frida, ptrace/gdbserver, LLDB).
Dynamic Instrumentation (Frida ≥ 16)
Mfululizo wa 16 wa Frida ulete maboresho kadhaa maalum kwa Android ambayo husaidia wakati lengo linapotumia optimisations za kisasa za Clang/LLD:
thumb-relocatorsasa inaweza hook tiny ARM/Thumb functions zinazotengenezwa na LLD’s aggressive alignment (--icf=all).- Enumerating and rebinding ELF import slots inafanya kazi kwenye Android, ikiruhusu per-module
dlopen()/dlsym()patching wakati inline hooks zinakataa. - Java hooking iliorekebishwa kwa ajili ya ART quick-entrypoint mpya inayotumika wakati apps zinapokomilishwa na
--enable-optimizationskwenye Android 14.
Example: enumerating all functions registered through RegisterNatives and dumping their addresses at runtime:
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 itafanya kazi bila setup kwenye vifaa vyenye PAC/BTI (Pixel 8/Android 14+) mradi utumie frida-server 16.2 au baadaye – matoleo ya awali yalishindwa kupata padding kwa inline hooks.
Dumping maktaba za native zilizofumbuliwa wakati wa runtime kutoka kwenye memory (Frida soSaver)
Wakati APK iliyo na ulinzi inahifadhi native code iliyofichwa au kuipanga tu wakati wa runtime (packers, downloaded payloads, generated libs), ungana na Frida na dump ELF iliyopangwa moja kwa moja kutoka kwenye process memory.
soSaver mtiririko (Python host + TS/JS Frida agent):
- Hooks
dlopennaandroid_dlopen_extili kugundua load-time library mapping na hufanya skana ya awali ya modules zilizopakuliwa tayari. - Hufanya skana mara kwa mara ya process memory mappings kutafuta ELF headers ili kunasa modules zilizopakiwa kupitia non-standard mappers ambazo hazikuwahi kupitia loader APIs.
- Inasoma kila module kwa bloksi kutoka memory na kutuma bytes kupitia Frida messages kwa host; ikiwa eneo halitoshwi kusomwa, inarudi kusoma kutoka kwenye on-disk path inapopatikana.
- Inahifadhi faili za
.sozilizojengwa upya na kuchapisha takwimu za uchimbaji kwa kila module, ikitoa artifacts kwa static RE.
Endesha (root + frida-server, Python ≥3.8, uv):
git clone https://github.com/TheQmaks/sosaver.git
cd sosaver && uv sync
source .venv/bin/activate # .venv\Scripts\activate on Windows
# target by package or PID; choose output/verbosity
sosaver com.example.app
sosaver 1234 -o /tmp/so-dumps --debug
Mbinu hii inavoidisha “only decrypted in RAM” protections kwa kurejesha live mapped image, ikiruhusu uchambuzi wa offline katika IDA/Ghidra hata kama nakala ya filesystem imefichwa au haipo.
Process-local JNI telemetry via preloaded .so (SoTap)
Wakati instrumentation yenye vipengele kamili inazidi mahitaji au imezuiwa, bado unaweza kupata uoni wa kiwango cha native kwa kupreload logger ndogo ndani ya mchakato lengwa. SoTap ni maktaba nyepesi ya Android native (.so) inayorekodi runtime behavior ya maktaba nyingine za JNI (.so) ndani ya mchakato sawa la app (hakuna root inahitajika).
Key properties:
- Inaanzishwa mapema na inaangalia JNI/native interactions ndani ya mchakato unaoiinua.
- Inahifadhi logs kwa kutumia multiple writable paths na fallback nzuri kwenda Logcat wakati storage imezuiwa.
- Source-customizable: hariri sotap.c ili kuongeza/kubadilisha kile kinachorekodiwa na ujenge tena per ABI.
Setup (repack the APK):
- Drop the proper ABI build into the APK so the loader can resolve libsotap.so:
- lib/arm64-v8a/libsotap.so (for arm64)
- lib/armeabi-v7a/libsotap.so (for arm32)
- Ensure SoTap loads before other JNI libs. Inject a call early (e.g., Application subclass static initializer or onCreate) so the logger is initialized first. Smali snippet example:
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
- Rebuild/sign/install, run the app, then collect 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:
- Ulinganifu wa ABI ni lazima. Kutokulingana kutasababisha UnsatisfiedLinkError na logger haitapakia.
- Vikwazo vya uhifadhi ni vya kawaida kwenye Android ya kisasa; ikiwa uandishi wa faili utakosa, SoTap bado itatoa kupitia Logcat.
- Tabia na kiwango cha taarifa vinakusudiwa kubadilishwa; jenga tena kutoka kwa chanzo baada ya kuhariri sotap.c.
Njia hii ni muhimu kwa malware triage na JNI debugging ambapo kuangalia mtiririko wa miito ya native tangu kuanza kwa mchakato ni muhimu lakini root/system-wide hooks hazipatikani.
Angalia pia: in‑memory native code execution via JNI
Mfano wa kawaida wa shambulio ni kupakua raw shellcode blob wakati wa runtime na kuiendesha moja kwa moja kutoka memory kupitia daraja la JNI (hakuna on‑disk ELF). Maelezo na snippet ya JNI tayari kwa matumizi hapa:
In Memory Jni Shellcode Execution
Udhaifu wa hivi karibuni zinazostahili kutafutwa ndani ya APKs
| Mwaka | CVE | Maktaba iliyoathirika | Maelezo |
|---|---|---|---|
| 2023 | CVE-2023-4863 | libwebp ≤ 1.3.1 | Heap buffer overflow reachable from native code that decodes WebP images. Several Android apps bundle vulnerable versions. When you see a libwebp.so inside an APK, check its version and attempt exploitation or patching. |
| 2024 | Multiple | OpenSSL 3.x series | Several memory-safety and padding-oracle issues. Many Flutter & ReactNative bundles ship their own libcrypto.so. |
Ukiona third-party .so files inside a APK, angalia hash yao dhidi ya upstream advisories. SCA (Software Composition Analysis) ni nadra kwenye mobile, hivyo builds zilizo kale na zilizo hatarishi ni nyingi.
Anti-Reversing & Hardening trends (Android 13-15)
- Pointer Authentication (PAC) & Branch Target Identification (BTI): Android 14 inaruhusu PAC/BTI katika system libraries kwenye silicon zinazounga mkono ARMv8.3+. Decompilers sasa zinaonyesha PAC‐related pseudo-instructions; kwa dynamic analysis Frida inaingiza trampolines baada ya kuondoa PAC, lakini trampolines zako za custom zinapaswa kuita
pacda/autibspinapohitajika. - MTE & Scudo hardened allocator: memory-tagging ni opt-in lakini apps nyingi zenye Play-Integrity zinajenga kwa
-fsanitize=memtag; tumiasetprop arm64.memtag.dump 1pamoja naadb shell am start ...ili kunasa tag faults. - LLVM Obfuscator (opaque predicates, control-flow flattening): packers za kibiashara (mfano, Bangcle, SecNeo) zinazuia zaidi native code, sio Java pekee; tarajia bogus control-flow na encrypted string blobs katika
.rodata.
Kukomesha early native initializers (.init_array) na JNI_OnLoad kwa instrumentation ya awali (ARM64 ELF)
Apps zilizo na ulinzi mkubwa mara nyingi huweka root/emulator/debug checks katika native constructors zinazotekelezwa mapema sana kupitia .init_array, kabla ya JNI_OnLoad na muda mrefu kabla ya code yoyote ya Java kuanza. Unaweza kufanya initializers hizo za kimya kuwa wazi na kurudisha udhibiti kwa:
- Kuondoa
INIT_ARRAY/INIT_ARRAYSZkutoka kwenye DYNAMIC table ili loader isitekeleze.init_arrayentries moja kwa moja. - Kutatua anwani ya constructor kutoka RELATIVE relocations na kuipeleka nje kama alama ya function ya kawaida (mfano,
INIT0). - Kubadilisha jina
JNI_OnLoadkuwaJNI_OnLoad0ili kumzuia ART kuiita kwa kimya.
Kwa nini hii inafanya kazi kwenye Android/arm64
- Katika AArch64, vipengele vya
.init_arraymara nyingi hujazwa wakati wa load kwa relocations zaR_AARCH64_RELATIVEambazo addend yake ni anwani ya function lengwa ndani ya.text. - Bytes za
.init_arrayzinaweza kuonekana tupu kwa static; dynamic linker huandika anwani iliyotatuliwa wakati wa usindikaji wa relocation.
Tambua lengo la constructor
- Tumia Android NDK toolchain kwa parsing sahihi ya ELF kwenye 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
- Tafuta relocation inayoweka ndani ya virtual address range ya
.init_array;addendyaR_AARCH64_RELATIVEhiyo ndiyo constructor (mfano,0xA34,0x954). - Fanya disassembly karibu na anwani hiyo ili kukagua kwa busara:
objdump -D ./libnativestaticinit.so --start-address=0xA34 | head -n 40
Mpango wa patch
- Ondoa tags za DYNAMIC
INIT_ARRAYnaINIT_ARRAYSZ. Usifute sections. - Ongeza alama ya GLOBAL DEFAULT FUNC
INIT0kwenye anwani ya constructor ili iweze kuitwa kwa mkono. - Badilisha jina
JNI_OnLoad→JNI_OnLoad0ili kumzuia ART kuiita kwa kimya.
Uthibitisho baada ya patch
readelf -W -d libnativestaticinit.so.patched | egrep -i 'init_array|fini_array|flags'
readelf -W -s libnativestaticinit.so.patched | egrep 'INIT0|JNI_OnLoad0'
Kurekebisha na LIEF (Python)
Skripti: ondoa INIT_ARRAY/INIT_ARRAYSZ, export INIT0, badilisha jina 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>
Maelezo na mbinu zilizoshindikana (kwa uhamaji)
- Kuweka byte za `.init_array` kuwa sifuri au kuweka urefu wa section kuwa 0 hakusaidi: the dynamic linker huzijaza tena kupitia relocations.
- Kuweka `INIT_ARRAY`/`INIT_ARRAYSZ` kuwa 0 kunaweza kuvunja loader kutokana na tags zisizolingana. Kuondoa kabisa yale entries za `DYNAMIC` ndilo njia ya kuaminika.
- Kuondoa kabisa sekta ya `.init_array` hupelekea loader kuanguka.
- Baada ya kupachika patch, anwani za function/layout zinaweza kubadilika; kila mara hesabu upya constructor kutoka kwa addends za `.rela.dyn` kwenye faili iliyopatchiwa ikiwa unahitaji kurudisha patch.
Kuanzisha ART/JNI ndogo ili kuita INIT0 na JNI_OnLoad0
- Tumia JNIInvocation kuanzisha muktadha mdogo wa ART VM katika binary huru. Kisha ita `INIT0()` na `JNI_OnLoad0(vm)` kwa mkono kabla ya Java code yoyote.
- Jumuisha APK/classes lengwa kwenye classpath ili `RegisterNatives` yoyote ipate Java classes zake.
<details>
<summary>Mfumo mdogo (CMake na C) wa kuita 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
Makosa ya Kawaida:
- Anwani za constructor hubadilika baada ya kuchomwa kwa sababu ya re-layout; hakikisha unahesabu tena kutoka
.rela.dynkwenye binary ya mwisho. - Hakikisha
-Djava.class.pathinajumuisha kila class inayotumiwa na wito zaRegisterNatives. - Tabia inaweza kutofautiana kulingana na toleo la NDK/loader; hatua iliyokuwa imethibitishwa mara kwa mara ilikuwa kuondoa DYNAMIC tags
INIT_ARRAY/INIT_ARRAYSZ.
Marejeo
- Kujifunza ARM Assembly: Azeria Labs – ARM Assembly Basics
- Nyaraka za JNI & NDK: Oracle JNI Spec · Android JNI Tips · NDK Guides
- Kukagua Native Libraries: Debug Android Native Libraries Using JEB Decompiler
- Frida 16.x change-log (Android hooking, tiny-function relocation) – frida.re/news
- Taarifa ya NVD kuhusu overflow ya
libwebpCVE-2023-4863 – nvd.nist.gov - SoTap: Lightweight in-app JNI (.so) behavior logger – github.com/RezaArbabBot/SoTap
- SoTap Releases – github.com/RezaArbabBot/SoTap/releases
- Jinsi ya kufanya kazi na SoTap? – 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
- soSaver — Frida-based live memory dumper for Android
.solibraries – github.com/TheQmaks/sosaver - soSaver Frida agent (TypeScript/JS) – github.com/TheQmaks/soSaver-frida
Tip
Jifunze na fanya mazoezi ya AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.


