Android Anti-Instrumentation & SSL Pinning Bypass (Frida/Objection)

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Revisa el catálogo completo de HackTricks Training para las rutas de evaluación (ARTA/GRTA/AzRTA) y Linux Hacking Expert (LHE).

Apoya a HackTricks

Esta página ofrece un flujo de trabajo práctico para recuperar el análisis dinámico contra apps Android que detectan/bloquean instrumentation o imponen TLS pinning. Se centra en triage rápido, detecciones comunes y hooks/tácticas copy‑paste para evadirlas sin repacking cuando sea posible.

Detection Surface (what apps check)

  • Root checks: su binary, Magisk paths, getprop values, common root packages
  • Frida/debugger checks (Java): Debug.isDebuggerConnected(), ActivityManager.getRunningAppProcesses(), getRunningServices(), scanning /proc, classpath, loaded libs
  • Native anti‑debug: ptrace(), syscalls, anti‑attach, breakpoints, inline hooks
  • Early init checks: Application.onCreate() or process start hooks that crash if instrumentation is present
  • TLS pinning: custom TrustManager/HostnameVerifier, OkHttp CertificatePinner, Conscrypt pinning, native pins

Bypassing Anti-Frida Detection / Stealth Frida Servers

phantom-frida rebuilds Frida from source and applies ~90 patches so common Frida fingerprints disappear while the stock Frida protocol remains compatible (frida-tools can still connect). Target: apps that grep /proc (cmdline, maps, task comm, fd readlink), D-Bus service names, default ports, or exported symbols.

Phases:

  • Source patches: renombrado global de identificadores frida (server/agent/helper) y helper DEX reconstruido con un Java package renombrado.
  • Targeted build/runtime patches: ajustes de meson, memfd label cambiado a jit-cache, SELinux labels (ej., frida_file) renombrados, hooks en libc sobre exit/signal deshabilitados para evitar detectores de hooks.
  • Post-build rename: exported symbol frida_agent_main renombrado después de la primera compilación (Vala lo emite), requiriendo una segunda compilación incremental.
  • Binary hex patches: nombres de thread (gmain, gdbus, pool-spawner) reemplazados; barrido opcional elimina cadenas residuales frida/Frida.

Detection vectors covered:

  • Base (1–8): process name frida-server, mapped libfrida-agent.so, thread names, memfd label, exported frida_agent_main, SELinux labels, libc hook side-effects, y D-Bus service re.frida.server son renombrados/neutralizados.
  • Extended (9–16): cambiar puerto de escucha (--port), renombrar interfaces D-Bus/símbolos C internos/GType names, paths temporales como .frida/frida-, barrido de strings binarios, renombrar defines y asset paths de build (libdir/frida). Los nombres de interfaz D-Bus que forman parte del protocolo en wire se mantienen sin cambios en modo base para evitar romper clientes stock.

Build/usage (Android arm64 example):

python3 build.py --version 17.7.2 --name myserver --port 27142 --extended --verify
adb push output/myserver-server-17.7.2-android-arm64 /data/local/tmp/myserver-server
adb shell chmod 755 /data/local/tmp/myserver-server
adb shell /data/local/tmp/myserver-server -D &
adb forward tcp:27142 tcp:27142
frida -H 127.0.0.1:27142 -f com.example.app

Opciones: --skip-build (patch only), --skip-clone, --arch, --ndk-path, --temp-fixes; Ayudante WSL: wsl -d Ubuntu bash build-wsl.sh.

Paso 1 — Ganancia rápida: ocultar root con Magisk DenyList

  • Habilita Zygisk en Magisk
  • Habilita DenyList, añade el paquete objetivo
  • Reinicia y vuelve a probar

Muchas apps solo buscan indicadores obvios (su/Magisk paths/getprop). DenyList a menudo neutraliza comprobaciones ingenuas.

Referencias:

  • Magisk (Zygisk & DenyList): https://github.com/topjohnwu/Magisk

Play Integrity / Zygisk detecciones (post‑SafetyNet)

Las aplicaciones bancarias/ID más recientes vinculan las comprobaciones en tiempo de ejecución a Google Play Integrity (reemplazo de SafetyNet) y también pueden bloquearse si Zygisk está presente. Consejos rápidos de triage:

  • Desactiva temporalmente Zygisk (toggle off + reboot) y vuelve a probar; algunas apps se bloquean en cuanto se carga la inyección de Zygote.
  • Si la attestation bloquea el inicio de sesión, parchea Google Play Services con PlayIntegrityFix/Fork + TrickyStore o usa ReZygisk/Zygisk‑Next solo durante las pruebas. Mantén el objetivo en DenyList y evita módulos LSPosed que leak props.
  • Para ejecuciones puntuales, usa KernelSU/APatch (no Zygote injection) para permanecer por debajo de las heurísticas de Zygisk, y luego adjunta Frida.

Paso 2 — Pruebas rápidas de 30 segundos con Frida Codeshare

Prueba scripts comunes de drop‑in antes de profundizar:

  • anti-root-bypass.js
  • anti-frida-detection.js
  • hide_frida_gum.js

Ejemplo:

frida -U -f com.example.app -l anti-frida-detection.js

Normalmente anulan las comprobaciones Java de root/debug, los escaneos de process/service y el ptrace() nativo. Útiles en apps poco protegidas; los objetivos hardened pueden necesitar hooks personalizados.

  • Codeshare: https://codeshare.frida.re/

Automatizar con Medusa (Frida framework)

Medusa proporciona 90+ módulos listos para usar para SSL unpinning, root/emulator detection bypass, HTTP comms logging, crypto key interception, y más.

git clone https://github.com/Ch0pin/medusa
cd medusa
pip install -r requirements.txt
python medusa.py

# Example interactive workflow
show categories
use http_communications/multiple_unpinner
use root_detection/universal_root_detection_bypass
run com.target.app

Consejo: Medusa es ideal para obtener resultados rápidos antes de escribir custom hooks. También puedes cherry-pick módulos y combinarlos con tus propios scripts.

Automatizar con Auto-Frida (spawn-mode + consolidated hooks)

Auto-Frida es un toolkit de automatización para Frida que se centra en una configuración repetible además de la auto-detección de protecciones y la generación consolidada de scripts de bypass. Es útil cuando las apps ejecutan checks muy temprano o cuando múltiples módulos de bypass, de otro modo, engancharían doblemente las mismas APIs.

Ideas clave de automatización:

  • Spawn-mode analysis para instalar hooks antes de Application.onCreate() para que se detecten tempranamente SSL pinning, root, emulator o anti-Frida checks.
  • Protection detection + auto-bypass: los resultados de detección generan un único script consolidado que engancha cada método Java/símbolo nativo una sola vez, reduciendo los crashes por hooks superpuestos.
  • Frida server lifecycle checks: validar la salud del server (process + port 27042 + frida-ps handshake) antes de descargar/reiniciar para mantener las ejecuciones estables.

Inicio rápido:

git clone https://github.com/ommirkute/Auto-Frida.git
cd Auto-Frida
pip install -r requirements.txt
python auto_frida.py

Notas

  • Auto-Frida puede instalar automáticamente frida/frida-tools si faltan y soporta selección de múltiples dispositivos.
  • Los scripts generados pueden ejecutarse inmediatamente o fusionarse con tus custom hooks después del análisis.

Paso 3 — Evadir detectores en tiempo de inicialización adjuntando más tarde

Muchas detecciones solo se ejecutan durante el spawn del proceso/onCreate(). La inyección en tiempo de spawn (-f) o los gadgets son detectados; adjuntar después de que la UI cargue puede pasar desapercibido.

# Launch the app normally (launcher/adb), wait for UI, then attach
frida -U -n com.example.app
# Or with Objection to attach to running process
aobjection --gadget com.example.app explore  # if using gadget

Si esto funciona, mantén la sesión estable y procede a mapear y realizar stub checks.

Paso 4 — Mapear la lógica de detección a través de Jadx y string hunting

Palabras clave para triage estático en Jadx:

  • “frida”, “gum”, “root”, “magisk”, “ptrace”, “su”, “getprop”, “debugger”

Patrones típicos de Java:

public boolean isFridaDetected() {
return getRunningServices().contains("frida");
}

APIs comunes para revisar/hook:

  • android.os.Debug.isDebuggerConnected
  • android.app.ActivityManager.getRunningAppProcesses / getRunningServices
  • java.lang.System.loadLibrary / System.load (native bridge)
  • java.lang.Runtime.exec / ProcessBuilder (probing commands)
  • android.os.SystemProperties.get (root/emulator heuristics)

Step 5 — Runtime stubbing con Frida (Java)

Sobrescribe validaciones personalizadas para devolver valores seguros sin repackear:

Java.perform(() => {
const Checks = Java.use('com.example.security.Checks');
Checks.isFridaDetected.implementation = function () { return false; };

// Neutralize debugger checks
const Debug = Java.use('android.os.Debug');
Debug.isDebuggerConnected.implementation = function () { return false; };

// Example: kill ActivityManager scans
const AM = Java.use('android.app.ActivityManager');
AM.getRunningAppProcesses.implementation = function () { return java.util.Collections.emptyList(); };
});

¿Triaging early crashes? Dump classes justo antes de que falle para identificar posibles detection namespaces:

Java.perform(() => {
Java.enumerateLoadedClasses({
onMatch: n => console.log(n),
onComplete: () => console.log('Done')
});
});

Ejemplo rápido de stub para detección de root (adáptalo a los nombres de paquete/clase del objetivo):

Java.perform(() => {
try {
const RootChecker = Java.use('com.target.security.RootCheck');
RootChecker.isDeviceRooted.implementation = function () { return false; };
} catch (e) {}
});

Registra y neutraliza métodos sospechosos para confirmar el flujo de ejecución:

Java.perform(() => {
const Det = Java.use('com.example.security.DetectionManager');
Det.checkFrida.implementation = function () {
console.log('checkFrida() called');
return false;
};
});

Bypass emulator/VM detection (Java stubs)

Heurísticas comunes: Build.FINGERPRINT/MODEL/MANUFACTURER/HARDWARE que contienen generic/goldfish/ranchu/sdk; artefactos de QEMU como /dev/qemu_pipe, /dev/socket/qemud; MAC por defecto 02:00:00:00:00:00; NAT 10.0.2.x; ausencia de telefonía/sensores.

Quick spoof of Build fields:

Java.perform(function(){
var Build = Java.use('android.os.Build');
Build.MODEL.value = 'Pixel 7 Pro';
Build.MANUFACTURER.value = 'Google';
Build.BRAND.value = 'google';
Build.FINGERPRINT.value = 'google/panther/panther:14/UP1A.231105.003/1234567:user/release-keys';
});

Complementa con stubs para comprobaciones de existencia de archivos e identificadores (TelephonyManager.getDeviceId/SubscriberId, WifiInfo.getMacAddress, SensorManager.getSensorList) para devolver valores realistas.

SSL pinning bypass quick hook (Java)

Neutraliza TrustManagers personalizados y fuerza contextos SSL permisivos:

Java.perform(function(){
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var SSLContext = Java.use('javax.net.ssl.SSLContext');

// No-op validations
X509TrustManager.checkClientTrusted.implementation = function(){ };
X509TrustManager.checkServerTrusted.implementation = function(){ };

// Force permissive TrustManagers
var TrustManagers = [ X509TrustManager.$new() ];
var SSLContextInit = SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;','[Ljavax.net.ssl.TrustManager;','java.security.SecureRandom');
SSLContextInit.implementation = function(km, tm, sr){
return SSLContextInit.call(this, km, TrustManagers, sr);
};
});

Notas

  • Extiende para OkHttp: hook okhttp3.CertificatePinner y HostnameVerifier según sea necesario, o usa un script universal de unpinning desde CodeShare.
  • Ejemplo de ejecución: frida -U -f com.target.app -l ssl-bypass.js --no-pause

OkHttp4 / gRPC / Cronet pinning (2024+)

Stacks modernas pin dentro de APIs más nuevas (OkHttp4+, gRPC over Cronet/BoringSSL). Añade estos hooks cuando el hook básico de SSLContext se quede colgado:

Java.perform(() => {
try {
const Pinner = Java.use('okhttp3.CertificatePinner');
Pinner.check.overload('java.lang.String', 'java.util.List').implementation = function(){};
Pinner.check$okhttp.implementation = function(){};
} catch (e) {}

try {
const CronetB = Java.use('org.chromium.net.CronetEngine$Builder');
CronetB.enablePublicKeyPinningBypassForLocalTrustAnchors.overload('boolean').implementation = function(){ return this; };
CronetB.setPublicKeyPins.overload('java.lang.String', 'java.util.Set', 'boolean').implementation = function(){ return this; };
} catch (e) {}
});

Si TLS aún falla, baja al código nativo y parchea los puntos de entrada de verificación de BoringSSL usados por Cronet/gRPC:

const customVerify = Module.findExportByName(null, 'SSL_CTX_set_custom_verify');
if (customVerify) {
Interceptor.attach(customVerify, {
onEnter(args){
// arg0 = SSL_CTX*, arg1 = mode, arg2 = callback
args[1] = ptr(0); // SSL_VERIFY_NONE
args[2] = NULL;  // disable callback
}
});
}

Step 6 — Sigue la pista JNI/native cuando los Java hooks dejan de funcionar

Rastrea los puntos de entrada JNI para localizar native loaders y detection init:

frida-trace -n com.example.app -i "JNI_OnLoad"

Triage nativo rápido de archivos .so empaquetados:

# List exported symbols & JNI
nm -D libfoo.so | head
objdump -T libfoo.so | grep Java_
strings -n 6 libfoo.so | egrep -i 'frida|ptrace|gum|magisk|su|root'

Reversing interactivo/nativo:

  • Ghidra: https://ghidra-sre.org/
  • r2frida: https://github.com/nowsecure/r2frida

Ejemplo: neutralizar ptrace para eludir anti‑debug simple en libc:

const ptrace = Module.findExportByName(null, 'ptrace');
if (ptrace) {
Interceptor.replace(ptrace, new NativeCallback(function () {
return -1; // pretend failure
}, 'int', ['int', 'int', 'pointer', 'pointer']));
}

Ver también: Reversing Native Libraries

Paso 7 — Objection patching (embed gadget / strip basics)

Si prefieres repacking a runtime hooks, prueba:

objection patchapk --source app.apk

Notas:

  • Requiere apktool; asegúrate de usar una versión actual desde la guía oficial para evitar problemas de compilación: https://apktool.org/docs/install
  • Gadget injection permite instrumentation sin root, pero aún puede ser detectado por comprobaciones más estrictas en tiempo de inicialización.

Opcionalmente, añade módulos LSPosed y Shamiko para un ocultamiento de root más robusto en entornos Zygisk, y ajusta DenyList para cubrir procesos hijos.

Para un flujo de trabajo completo que incluya la configuración del Gadget en modo script y el empaquetado de tu Frida 17+ agent dentro del APK, consulta:

Frida Tutorial — Self-contained agent + Gadget embedding

Referencias:

  • Objection: https://github.com/sensepost/objection

Paso 8 — Plan alternativo: Parchear TLS pinning para visibilidad de red

Si instrumentation está bloqueada, aún puedes inspeccionar el tráfico eliminando el pinning de forma estática:

apk-mitm app.apk
# Then install the patched APK and proxy via Burp/mitmproxy
  • Tool: https://github.com/shroudedcode/apk-mitm
  • For network config CA‑trust tricks (and Android 7+ user CA trust), see:

Make APK Accept CA Certificate

Install Burp Certificate

Abuso de Hooking con LSPosed/Xposed (Telephony/SMS)

En dispositivos rooteados, los módulos LSPosed/Xposed pueden hookear las Java telephony/SMS APIs en tiempo de ejecución, manteniendo el APK sin modificar en disco mientras controlan completamente lo que la app ve. Esto se abusa comúnmente para eludir flujos SIM‑binding que confían en las APIs de telephony locales o en el estado del SMS provider local.

Primitivas clave

  • Suprimir SMS de verificación salientes mientras exfiltras el token cortocircuitando SmsManager.sendTextMessage en beforeHookedMethod.
  • Falsificar MSISDN/número de línea forzando que TelephonyManager.getLine1Number() y SubscriptionInfo.getNumber() devuelvan un valor controlado por el atacante.
  • Insertar un registro falso “Sent” en el SMS provider para que las apps que consultan el historial local de SMS vean un envío exitoso aunque el operador nunca lo recibiera.

Ejemplo: bloquear el envío de SMS y capturar el contenido

XposedHelpers.findAndHookMethod(
"android.telephony.SmsManager",
lpparam.classLoader,
"sendTextMessage",
String.class, String.class, String.class, PendingIntent.class, PendingIntent.class,
new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) {
String body = (String) param.args[2];
// exfiltrate body to operator channel
param.setResult(null); // suppress real SMS send
}
}
);

Ejemplo: spoof número de teléfono del dispositivo

XposedHelpers.findAndHookMethod(
"android.telephony.TelephonyManager",
lpparam.classLoader,
"getLine1Number",
new XC_MethodHook() {
protected void afterHookedMethod(MethodHookParam param) {
param.setResult(spoofedMsisdn);
}
}
);
XposedHelpers.findAndHookMethod(
"android.telephony.SubscriptionInfo",
lpparam.classLoader,
"getNumber",
new XC_MethodHook() {
protected void afterHookedMethod(MethodHookParam param) {
param.setResult(spoofedMsisdn);
}
}
);

Ejemplo: inyectar un registro SMS falso “Sent”

ContentValues v = new ContentValues();
v.put("address", dest);
v.put("body", body);
v.put("type", 2);   // sent
v.put("status", 0); // success
context.getContentResolver().insert(Uri.parse("content://sms/sent"), v);

Hoja de referencia rápida de comandos

# List processes and attach
frida-ps -Uai
frida -U -n com.example.app

# Spawn with a script (may trigger detectors)
frida -U -f com.example.app -l anti-frida-detection.js

# Trace native init
frida-trace -n com.example.app -i "JNI_OnLoad"

# Objection runtime
objection --gadget com.example.app explore

# Static TLS pinning removal
apk-mitm app.apk

Universal proxy forcing + TLS unpinning (HTTP Toolkit Frida hooks)

Las aplicaciones modernas a menudo ignoran los proxies del sistema y aplican múltiples capas de pinning (Java + native), lo que dificulta la captura de tráfico incluso con los CA de usuario/sistema instalados. Un enfoque práctico es combinar universal TLS unpinning con proxy forcing mediante Frida hooks ya preparados, y enrutar todo a través de mitmproxy/Burp.

Workflow

  • Ejecuta mitmproxy en tu host (o Burp). Asegúrate de que el dispositivo pueda alcanzar la IP/puerto del host.
  • Carga los Frida hooks consolidados de HTTP Toolkit para tanto unpin TLS como forzar el uso de proxy en pilas comunes (OkHttp/OkHttp3, HttpsURLConnection, Conscrypt, WebView, etc.). Esto elude las comprobaciones de CertificatePinner/TrustManager y sobrescribe los proxy selectors, de modo que el tráfico siempre se envía a través de tu proxy incluso si la app deshabilita explícitamente los proxies.
  • Inicia la app objetivo con Frida y el script hook, y captura las peticiones en mitmproxy.

Example

# Device connected via ADB or over network (-U)
# See the repo for the exact script names & options
frida -U -f com.vendor.app \
-l ./android-unpinning-with-proxy.js \
--no-pause

# mitmproxy listening locally
mitmproxy -p 8080

Notas

  • Combina con un proxy a nivel de sistema mediante adb shell settings put global http_proxy <host>:<port> cuando sea posible. Los Frida hooks harán cumplir el uso del proxy incluso cuando las apps omitan la configuración global.
  • Esta técnica es ideal cuando necesitas realizar MITM en flujos de onboarding mobile-to-IoT, donde la evitación de pinning/proxy es común.
  • Hooks: https://github.com/httptoolkit/frida-interception-and-unpinning

Referencias

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Revisa el catálogo completo de HackTricks Training para las rutas de evaluación (ARTA/GRTA/AzRTA) y Linux Hacking Expert (LHE).

Apoya a HackTricks