hacktricks/src/mobile-pentesting/android-app-pentesting/reversing-native-libraries.md

8.5 KiB
Raw Blame History

Reversing Native Libraries

{{#include ../../banners/hacktricks-training.md}}

Więcej informacji: https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html

Aplikacje Android mogą używać natywnych bibliotek, zwykle napisanych w C lub C++, do zadań krytycznych pod względem wydajności. Malware creators również nadużywają tych bibliotek, ponieważ ELF shared objects wciąż trudniej zdekompilować niż DEX/OAT byte-code. Ta strona koncentruje się na praktycznych przepływach pracy i niedawnych usprawnieniach narzędzi (20232025), które ułatwiają reversing Android .so files.


Szybki triage — przepływ pracy dla świeżo pobranego libfoo.so

  1. Wyodrębnij bibliotekę
# 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/
  1. Zidentyfikuj architekturę i zabezpieczenia
file libfoo.so        # arm64 or arm32 / x86
readelf -h libfoo.so  # OS ABI, PIE, NX, RELRO, etc.
checksec --file libfoo.so  # (peda/pwntools)
  1. Wypisz eksportowane symbole i powiązania JNI
readelf -s libfoo.so | grep ' Java_'     # dynamic-linked JNI
strings libfoo.so   | grep -i "RegisterNatives" -n   # static-registered JNI
  1. Wczytaj do dekompilera (Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper or Cutter/Rizin) i uruchom automatyczną analizę. Nowsze wersje Ghidra wprowadziły dekompiler AArch64, który rozpoznaje PAC/BTI stubs i MTE tags, znacznie poprawiając analizę bibliotek skompilowanych przy użyciu Android 14 NDK.
  2. Zdecyduj o static vs dynamic reversing: stripped, obfuscated code often needs instrumentation (Frida, ptrace/gdbserver, LLDB).

Dynamic Instrumentation (Frida ≥ 16)

Fridas 16-series brought several Android-specific improvements that help when the target uses modern Clang/LLD optimisations:

  • thumb-relocator can now hook tiny ARM/Thumb functions generated by LLDs aggressive alignment (--icf=all).
  • Enumerating and rebinding ELF import slots works on Android, enabling per-module dlopen()/dlsym() patching when inline hooks are rejected.
  • Java hooking was fixed for the new ART quick-entrypoint used when apps are compiled with --enable-optimizations on Android 14.

Przykład: 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 will work out of the box on PAC/BTI-enabled devices (Pixel 8/Android 14+) as long as you use frida-server 16.2 or later earlier versions failed to locate padding for inline hooks.

Lokalna telemetria JNI w procesie przez preładowane .so (SoTap)

Gdy pełna instrumentacja jest przesadą lub zablokowana, nadal możesz uzyskać widoczność na poziomie natywnym przez wstępne załadowanie małego loggera wewnątrz docelowego procesu. SoTap to lekka natywna biblioteka Android (.so), która loguje zachowanie w czasie wykonywania innych bibliotek JNI (.so) w tym samym procesie aplikacji (nie wymaga uprawnień root).

Kluczowe właściwości:

  • Inicjalizuje się wcześnie i obserwuje JNI/native interactions wewnątrz procesu, który ją ładuje.
  • Przechowuje logi przy użyciu wielu zapisywalnych ścieżek z zapasowym przejściem do Logcat, gdy pamięć jest ograniczona.
  • Możliwość modyfikacji źródła: edytuj sotap.c, aby rozszerzyć/dostosować, co jest logowane, i przebuduj dla każdego ABI.

Setup (repack the APK):

  1. Wstaw odpowiednią kompilację ABI do APK, aby loader mógł rozwiązać libsotap.so:
  • lib/arm64-v8a/libsotap.so (dla arm64)
  • lib/armeabi-v7a/libsotap.so (dla arm32)
  1. Upewnij się, że SoTap ładuje się przed innymi bibliotekami JNI. Wstrzyknij wywołanie wcześnie (np. Application subclass static initializer lub onCreate), aby logger został zainicjalizowany pierwszy. Smali snippet example:
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
  1. Przebuduj/podpisz/zainstaluj, uruchom aplikację, a następnie zbierz logi.

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 alignment is mandatory. A mismatch will raise UnsatisfiedLinkError and the logger wont load.
  • Storage constraints are common on modern Android; if file writes fail, SoTap will still emit via Logcat.
  • Behavior/verbosity is intended to be customized; rebuild from source after editing sotap.c.

This approach is useful for malware triage and JNI debugging where observing native call flows from process start is critical but root/system-wide hooks arent available.


Zobacz także: inmemory native code execution via JNI

A common attack pattern is to download a raw shellcode blob at runtime and execute it directly from memory through a JNI bridge (no ondisk ELF). Details and readytouse JNI snippet here:

{{#ref}} in-memory-jni-shellcode-execution.md {{#endref}}


Recent vulnerabilities worth hunting for in APKs

Rok CVE Biblioteka dotknięta Uwagi
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.

When you spot third-party .so files inside an APK, always cross-check their hash against upstream advisories. SCA (Software Composition Analysis) is uncommon on mobile, so outdated vulnerable builds are rampant.


  • Pointer Authentication (PAC) & Branch Target Identification (BTI): Android 14 enables PAC/BTI in system libraries on supported ARMv8.3+ silicon. Decompilers now display PACrelated pseudo-instructions; for dynamic analysis Frida injects trampolines after stripping PAC, but your custom trampolines should call pacda/autibsp where necessary.
  • MTE & Scudo hardened allocator: memory-tagging is opt-in but many Play-Integrity aware apps build with -fsanitize=memtag; use setprop arm64.memtag.dump 1 plus adb shell am start ... to capture tag faults.
  • LLVM Obfuscator (opaque predicates, control-flow flattening): commercial packers (e.g., Bangcle, SecNeo) increasingly protect native code, not only Java; expect bogus control-flow and encrypted string blobs in .rodata.

Resources

References

{{#include ../../banners/hacktricks-training.md}}