Contournement de l’Anti-Instrumentation Android et du SSL Pinning (Frida/Objection)

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Cette page fournit un workflow pratique pour retrouver une analyse dynamique contre des apps Android qui détectent/empêchent l’instrumentation ou qui appliquent du TLS pinning. Elle se concentre sur le triage rapide, les détections courantes et des hooks/tactiques copiables pour les contourner sans repacking quand c’est possible.

Surface de détection (ce que les apps vérifient)

  • Root checks : binaire su, chemins Magisk, valeurs getprop, paquets root courants
  • Frida/debugger checks (Java) : Debug.isDebuggerConnected(), ActivityManager.getRunningAppProcesses(), getRunningServices(), scan de /proc, classpath, libs chargées
  • Native anti‑debug : ptrace(), syscalls, anti‑attach, breakpoints, inline hooks
  • Early init checks : Application.onCreate() ou hooks au démarrage du process qui plantent si une instrumentation est présente
  • TLS pinning : custom TrustManager/HostnameVerifier, OkHttp CertificatePinner, Conscrypt pinning, pins natifs

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 : renommage global des identifiants frida (server/agent/helper) et rebuild du helper DEX avec un package Java renommé.
  • Targeted build/runtime patches : ajustements meson, memfd label changé en jit-cache, étiquettes SELinux (ex. frida_file) renommées, hooks libc sur exit/signal désactivés pour éviter les détecteurs de hook.
  • Post-build rename : symbole exporté frida_agent_main renommé après la première compilation (Vala l’émet), nécessitant une seconde compilation incrémentale.
  • Binary hex patches : noms de threads (gmain, gdbus, pool-spawner) remplacés ; balayage optionnel pour enlever les chaînes restantes frida/Frida.

Vecteurs de détection couverts :

  • Base (1–8) : nom de process frida-server, mapping libfrida-agent.so, noms de threads, memfd label, symbole exporté frida_agent_main, étiquettes SELinux, effets secondaires des hooks libc, et service D-Bus re.frida.server sont renommés/neutralisés.
  • Extended (9–16) : changement du port d’écoute (--port), renommage des interfaces D-Bus/symboles C internes/noms GType, chemins temporaires comme .frida/frida-, balayage des chaînes binaires, renommage des defines au build et des chemins d’assets (libdir/frida). Les noms d’interface D-Bus qui font partie du protocole wire restent inchangés en mode base pour éviter de casser les clients 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

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

Step 1 — Quick win: hide root with Magisk DenyList

  • Activer Zygisk dans Magisk
  • Activer DenyList, ajouter le package cible
  • Redémarrer et retester

De nombreuses apps ne cherchent que des indicateurs évidents (su/Magisk paths/getprop). DenyList neutralise souvent les vérifications naïves.

Références:

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

Play Integrity / Zygisk détections (post‑SafetyNet)

Les apps bancaires/d’identité récentes lient les vérifications runtime à Google Play Integrity (remplaçant de SafetyNet) et peuvent aussi planter si Zygisk est présent. Conseils rapides de triage :

  • Désactiver temporairement Zygisk (toggle off + reboot) et réessayer ; certaines apps plantent dès que l’injection Zygote se charge.
  • Si l’attestation bloque la connexion, patcher Google Play Services avec PlayIntegrityFix/Fork + TrickyStore ou utiliser ReZygisk/Zygisk‑Next uniquement pendant les tests. Garder la cible dans DenyList et éviter les modules LSPosed qui leak props.
  • Pour des exécutions ponctuelles, utiliser KernelSU/APatch (no Zygote injection) pour rester sous les heuristiques Zygisk, puis attacher Frida.

Step 2 — 30‑second Frida Codeshare tests

Tester des scripts drop‑in courants avant d’explorer en profondeur :

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

Exemple:

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

Ces modules stubent généralement les root/debug checks Java, les process/service scans et le ptrace() natif. Utile sur des apps faiblement protégées ; les hardened targets peuvent nécessiter des hooks sur mesure.

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

Automatiser avec Medusa (Frida framework)

Medusa fournit plus de 90 modules prêts à l’emploi pour SSL unpinning, root/emulator detection bypass, HTTP comms logging, crypto key interception, et plus encore.

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

Astuce : Medusa est idéal pour des victoires rapides avant d’écrire des custom hooks. Vous pouvez aussi cherry-pick des modules et les combiner avec vos propres scripts.

Étape 3 — Contourner les détecteurs d’init en s’attachant tard

Beaucoup de détections ne s’exécutent que pendant le process spawn/onCreate(). Spawn‑time injection (-f) ou gadgets se font détecter ; s’attacher après le chargement de l’UI peut passer inaperçu.

# 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 cela fonctionne, maintenez la session stable et passez à la cartographie et aux vérifications des stubs.

Étape 4 — Cartographier la logique de détection via Jadx et la recherche de chaînes

Mots-clés de triage statique dans Jadx:

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

Modèles Java typiques:

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

APIs courantes à examiner/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)

Étape 5 — Runtime stubbing with Frida (Java)

Surchargez les garde-fous personnalisés pour renvoyer des valeurs sûres sans repacking :

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(); };
});

Triage des plantages précoces ? Dump classes juste avant qu’il ne plante pour repérer les detection namespaces probables :

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

Exemple rapide de stub de détection de root (adapter aux noms de package/classe cibles) :

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

Consigner et neutraliser les méthodes suspectes pour confirmer le flux d’exécution :

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)

Heuristiques courantes : Build.FINGERPRINT/MODEL/MANUFACTURER/HARDWARE contenant generic/goldfish/ranchu/sdk ; artefacts QEMU comme /dev/qemu_pipe, /dev/socket/qemud ; MAC par défaut 02:00:00:00:00:00 ; NAT 10.0.2.x ; absence de téléphonie/senseurs.

Exemple rapide de spoof des champs Build :

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';
});

Complétez avec des stubs pour les vérifications d’existence de fichiers et les identifiants (TelephonyManager.getDeviceId/SubscriberId, WifiInfo.getMacAddress, SensorManager.getSensorList) afin de renvoyer des valeurs réalistes.

SSL pinning bypass quick hook (Java)

Neutraliser les TrustManagers personnalisés et forcer des contextes SSL permissifs:

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);
};
});

Remarques

  • Étendre pour OkHttp : hook okhttp3.CertificatePinner et HostnameVerifier selon les besoins, ou utiliser un script d’unpinning universel depuis CodeShare.
  • Exemple d’exécution : frida -U -f com.target.app -l ssl-bypass.js --no-pause

OkHttp4 / gRPC / Cronet pinning (2024+)

Les stacks modernes effectuent le pinning au sein d’API plus récentes (OkHttp4+, gRPC over Cronet/BoringSSL). Ajoutez ces hooks lorsque le hook SSLContext de base bloque :

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 échoue toujours, passez au natif et modifiez les points d’entrée de vérification de BoringSSL utilisés par 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 — Suivre la piste JNI/native lorsque les hooks Java échouent

Tracez les points d’entrée JNI pour localiser les native loaders et detection init:

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

Triage natif rapide des fichiers .so inclus :

# 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'

Interactif/native reversing:

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

Exemple : neutraliser ptrace pour contourner un anti‑debug simple dans libc:

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

Voir aussi : Reversing Native Libraries

Étape 7 — Objection patching (embed gadget / strip basics)

Quand vous préférez repacking aux runtime hooks, essayez :

objection patchapk --source app.apk

Remarques :

  • Nécessite apktool ; assurez-vous d’utiliser une version récente depuis le guide officiel pour éviter des problèmes de build : https://apktool.org/docs/install
  • Gadget injection enables instrumentation without root but can still be caught by stronger init‑time checks.

Optionnellement, ajoutez des modules LSPosed et Shamiko pour un masquage du root plus efficace dans les environnements Zygisk, et configurez DenyList pour couvrir les processus enfants.

Pour un workflow complet incluant la configuration du Gadget en mode script et l’inclusion de votre agent Frida 17+ dans l’APK, voir :

Frida Tutorial — Self-contained agent + Gadget embedding

Références:

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

Étape 8 — Repli : Patch TLS pinning pour la visibilité réseau

Si l’instrumentation est bloquée, vous pouvez toujours inspecter le trafic en supprimant statiquement le pinning :

apk-mitm app.apk
# Then install the patched APK and proxy via Burp/mitmproxy
  • Outil: https://github.com/shroudedcode/apk-mitm
  • Pour les astuces CA‑trust de la configuration réseau (et la confiance aux CA utilisateur sur Android 7+), voir:

Make APK Accept CA Certificate

Install Burp Certificate

Récapitulatif pratique des commandes

# 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)

Les applications modernes ignorent souvent les system proxies et appliquent plusieurs couches de pinning (Java + native), rendant la capture du trafic pénible même avec les user/system CAs installées. Une approche pratique consiste à combiner universal TLS unpinning avec proxy forcing via des Frida hooks prêts à l’emploi, et à rediriger tout le trafic via mitmproxy/Burp.

Workflow

  • Run mitmproxy on your host (or Burp). Ensure the device can reach the host IP/port.
  • Load HTTP Toolkit’s consolidated Frida hooks to both unpin TLS and force proxy usage across common stacks (OkHttp/OkHttp3, HttpsURLConnection, Conscrypt, WebView, etc.). This bypasses CertificatePinner/TrustManager checks and overrides proxy selectors, so traffic is always sent via your proxy even if the app explicitly disables proxies.
  • Start the target app with Frida and the hook script, and capture requests in 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

Remarques

  • Combinez avec un proxy système via adb shell settings put global http_proxy <host>:<port> lorsque possible. Les Frida hooks forceront l’utilisation du proxy même lorsque les apps contournent les paramètres globaux.
  • Cette technique est idéale lorsque vous devez MITM des flux d’onboarding mobile-vers-IoT où le pinning/évitage du proxy est courant.
  • Hooks: https://github.com/httptoolkit/frida-interception-and-unpinning

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks