8.5 KiB
Reversión de bibliotecas nativas
{{#include ../../banners/hacktricks-training.md}}
Para más información consulta: https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html
Las apps Android pueden usar bibliotecas nativas, típicamente escritas en C o C++, para tareas que requieren rendimiento. Los creadores de malware también abusan de estas bibliotecas porque los ELF shared objects siguen siendo más difíciles de decompilar que el byte-code DEX/OAT.
Esta página se centra en flujos de trabajo prácticos y mejoras recientes en tooling (2023-2025) que hacen más fácil revertir archivos .so
de Android.
Flujo rápido de triage para un libfoo.so
recién extraído
- Extraer la biblioteca
# 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/
- Identificar arquitectura y protecciones
file libfoo.so # arm64 or arm32 / x86
readelf -h libfoo.so # OS ABI, PIE, NX, RELRO, etc.
checksec --file libfoo.so # (peda/pwntools)
- Listar símbolos exportados y bindings JNI
readelf -s libfoo.so | grep ' Java_' # dynamic-linked JNI
strings libfoo.so | grep -i "RegisterNatives" -n # static-registered JNI
- Cargar en un descompilador (Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper or Cutter/Rizin) y ejecutar el auto-análisis. Las versiones más recientes de Ghidra introdujeron un descompilador AArch64 que reconoce PAC/BTI stubs y MTE tags, mejorando enormemente el análisis de bibliotecas compiladas con el Android 14 NDK.
- Decidir entre reversing estático vs dinámico: el código stripped u ofuscado a menudo necesita instrumentation (Frida, ptrace/gdbserver, LLDB).
Instrumentación dinámica (Frida ≥ 16)
La serie 16 de Frida trajo varias mejoras específicas para Android que ayudan cuando el objetivo usa optimizaciones modernas de Clang/LLD:
thumb-relocator
can now hook tiny ARM/Thumb functions generated by LLD’s 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.
Ejemplo: enumerar todas las funciones registradas mediante RegisterNatives
y volcar sus direcciones en tiempo de ejecución:
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.
Telemetría JNI a nivel de proceso mediante .so precargado (SoTap)
Cuando la instrumentación completa es exagerada o está bloqueada, aún puedes obtener visibilidad a nivel nativo precargando un pequeño logger dentro del proceso objetivo. SoTap es una librería nativa ligera de Android (.so) que registra el comportamiento en tiempo de ejecución de otras librerías JNI (.so) dentro del mismo proceso de la app (no se requiere root).
Propiedades clave:
- Se inicializa temprano y observa las interacciones JNI/nativas dentro del proceso que la carga.
- Persiste registros usando múltiples rutas escribibles con retroceso a Logcat cuando el almacenamiento está restringido.
- Personalizable en origen: edita sotap.c para ampliar/ajustar lo que se registra y recompila por ABI.
Configuración (reempacar el 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
Notas y solución de problemas:
- ABI alignment is mandatory. A mismatch will raise UnsatisfiedLinkError and the logger won’t 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 aren’t available.
Véase también: in‑memory 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 on‑disk ELF). Details and ready‑to‑use JNI snippet here:
{{#ref}} in-memory-jni-shellcode-execution.md {{#endref}}
Vulnerabilidades recientes que vale la pena buscar en APKs
Año | CVE | Biblioteca afectada | Notas |
---|---|---|---|
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 . |
Cuando encuentres archivos .so
de third-party dentro de un APK, siempre verifica su hash contra las advisories upstream. SCA (Software Composition Analysis) es poco común en mobile, por lo que las builds vulnerables y desactualizadas son rampantes.
Tendencias de Anti-Reversing & Hardening (Android 13-15)
- Pointer Authentication (PAC) & Branch Target Identification (BTI): Android 14 enables PAC/BTI in system libraries on supported ARMv8.3+ silicon. Decompilers now display PAC‐related 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
; usesetprop arm64.memtag.dump 1
plusadb 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
.
Recursos
- Learning ARM Assembly: Azeria Labs – ARM Assembly Basics
- JNI & NDK Documentation: Oracle JNI Spec · Android JNI Tips · NDK Guides
- Debugging Native Libraries: Debug Android Native Libraries Using JEB Decompiler
Referencias
- Frida 16.x change-log (Android hooking, tiny-function relocation) – frida.re/news
- NVD advisory for
libwebp
overflow CVE-2023-4863 – nvd.nist.gov - SoTap: Lightweight in-app JNI (.so) behavior logger – github.com/RezaArbabBot/SoTap
- SoTap Releases – github.com/RezaArbabBot/SoTap/releases
- How to work with SoTap? – t.me/ForYouTillEnd/13
- CoRPhone — JNI memory-only execution pattern and packaging
{{#include ../../banners/hacktricks-training.md}}