Android 안티 인스트루멘테이션 & SSL Pinning 우회 (Frida/Objection)
Reading time: 9 minutes
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: 
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기: 
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
 - **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
 - HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
 
이 페이지는 Android 앱이 instrumentation을 감지/차단하거나 TLS pinning을 적용할 때 dynamic analysis를 복구하기 위한 실용적인 워크플로우를 제공합니다. 빠른 분류, 일반적인 감지 기법, 가능한 경우 재패키징 없이 우회할 수 있는 복사·붙여넣기 가능한 훅/전술에 중점을 둡니다.
Detection Surface (앱이 검사하는 항목)
- 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
 
Step 1 — Quick win: Magisk DenyList로 루트 숨기기
- Magisk에서 Zygisk 활성화
 - DenyList 활성화하고 대상 패키지 추가
 - 재부팅 후 재검증
 
많은 앱은 명백한 지표(su/Magisk 경로/getprop)만 확인합니다. DenyList는 대개 단순한 검사들을 무력화합니다.
References:
- Magisk (Zygisk & DenyList): https://github.com/topjohnwu/Magisk
 
Step 2 — 30‑second Frida Codeshare 테스트
심층 분석에 들어가기 전에 흔히 사용하는 drop‑in 스크립트를 시도해보세요:
- anti-root-bypass.js
 - anti-frida-detection.js
 - hide_frida_gum.js
 
Example:
frida -U -f com.example.app -l anti-frida-detection.js
이들은 일반적으로 Java의 root/debug 체크, 프로세스/서비스 스캔, 그리고 네이티브 ptrace()를 스텁 처리합니다. 보호 수준이 낮은 앱에서는 유용하지만, 강화된 대상은 맞춤형 훅이 필요할 수 있습니다.
- Codeshare: https://codeshare.frida.re/
 
Medusa로 자동화 (Frida framework)
Medusa는 SSL unpinning, root/emulator detection bypass, HTTP comms logging, crypto key interception 등에서 사용할 수 있는 90개 이상의 기성 모듈을 제공합니다.
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
팁: Medusa는 custom hooks를 작성하기 전에 빠른 성과를 얻기에 매우 좋습니다. 모듈을 선별(cherry-pick)하여 자체 스크립트와 결합할 수도 있습니다.
Step 3 — 늦게 attach하여 init-time detectors를 우회
많은 탐지들은 process spawn/onCreate() 동안에만 동작합니다. Spawn‑time injection (-f)이나 gadgets는 탐지될 수 있고, UI가 로드된 이후에 attaching하면 우회할 수 있습니다.
# 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
Step 4 — Jadx와 string hunting을 통해 탐지 로직 매핑
Jadx에서의 정적 분류 키워드:
- "frida", "gum", "root", "magisk", "ptrace", "su", "getprop", "debugger"
 
일반적인 Java 패턴:
public boolean isFridaDetected() {
return getRunningServices().contains("frida");
}
검토/후킹할 일반적인 API:
- 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)
 
5단계 — Frida (Java)를 사용한 런타임 스텁 적용
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(); };
});
초기 크래시를 분류 중인가요? 종료 직전에 클래스를 덤프하여 탐지 가능성이 높은 네임스페이스를 찾아보세요:
Java.perform(() => {
Java.enumerateLoadedClasses({
onMatch: n => console.log(n),
onComplete: () => console.log('Done')
});
});
// Quick root detection stub example (adapt to target package/class names) Java.perform(() => { try { const RootChecker = Java.use('com.target.security.RootCheck'); RootChecker.isDeviceRooted.implementation = function () { return false; }; } catch (e) {} });
실행 흐름을 확인하려면 의심스러운 메서드를 기록하고 무력화하세요:
Java.perform(() => {
const Det = Java.use('com.example.security.DetectionManager');
Det.checkFrida.implementation = function () {
console.log('checkFrida() called');
return false;
};
});
에뮬레이터/VM 탐지 우회 (Java stubs)
일반적인 휴리스틱: Build.FINGERPRINT/MODEL/MANUFACTURER/HARDWARE 필드에 generic/goldfish/ranchu/sdk 포함; QEMU 아티팩트 예: /dev/qemu_pipe, /dev/socket/qemud; 기본 MAC 02:00:00:00:00:00; 10.0.2.x NAT; telephony/sensors가 없음.
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';
});
파일 존재 검사 및 식별자 (TelephonyManager.getDeviceId/SubscriberId, WifiInfo.getMacAddress, SensorManager.getSensorList)에 대한 스텁을 추가하여 현실적인 값을 반환하도록 보완하세요.
SSL pinning bypass quick hook (Java)
사용자 정의 TrustManagers를 무력화하고 permissive SSL contexts를 강제하세요:
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);
};
});
참고
- OkHttp 확장: 필요에 따라 okhttp3.CertificatePinner와 HostnameVerifier를 hook하거나 CodeShare의 universal unpinning script를 사용하세요.
 - 실행 예시: 
frida -U -f com.target.app -l ssl-bypass.js --no-pause 
Step 6 — Java hooks가 실패할 때 JNI/native 흔적을 따라가세요
JNI 엔트리 포인트를 추적해 native loaders와 detection init을 찾아보세요:
frida-trace -n com.example.app -i "JNI_OnLoad"
번들된 .so 파일의 빠른 네이티브 선별:
# 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'
인터랙티브/네이티브 리버싱:
- Ghidra: https://ghidra-sre.org/
 - r2frida: https://github.com/nowsecure/r2frida
 
예: ptrace를 무력화하여 libc의 간단한 anti‑debug를 우회:
const ptrace = Module.findExportByName(null, 'ptrace');
if (ptrace) {
Interceptor.replace(ptrace, new NativeCallback(function () {
return -1; // pretend failure
}, 'int', ['int', 'int', 'pointer', 'pointer']));
}
참고: Reversing Native Libraries
단계 7 — Objection patching (embed gadget / strip basics)
runtime hooks 대신 repacking을 선호한다면, 시도해보세요:
objection patchapk --source app.apk
Notes:
- apktool 필요; 빌드 문제를 피하려면 공식 가이드에서 최신 버전을 사용하세요: https://apktool.org/docs/install
 - Gadget injection은 루트 없이 instrumentation을 가능하게 하지만, 여전히 더 강력한 init‑time 검사에 의해 탐지될 수 있습니다.
 
선택적으로 LSPosed 모듈과 Shamiko를 추가하여 Zygisk 환경에서 더 강력하게 루트 숨김을 하고, DenyList를 조정하여 자식 프로세스까지 포함하세요.
For a complete workflow including script-mode Gadget configuration and bundling your Frida 17+ agent into the APK, see:
Frida Tutorial — Self-contained agent + Gadget embedding
References:
- Objection: https://github.com/sensepost/objection
 
Step 8 — 폴백: 네트워크 가시성 확보를 위한 TLS pinning 패치
instrumentation이 차단되면, pinning을 정적으로 제거하여 트래픽을 여전히 검사할 수 있습니다:
apk-mitm app.apk
# Then install the patched APK and proxy via Burp/mitmproxy
- 도구: https://github.com/shroudedcode/apk-mitm
 - 네트워크 구성 CA‑trust 트릭(및 Android 7+ user CA trust)에 대해서는 다음을 참조:
 
Make APK Accept CA Certificate
유용한 명령어 치트시트
# 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
범용 proxy 강제화 + TLS unpinning (HTTP Toolkit Frida hooks)
최신 앱들은 종종 system proxies를 무시하고 pinning(Java + native)의 여러 레이어를 적용해, user/system CAs가 설치되어 있어도 트래픽 캡처가 매우 어려워집니다. 실무적으로는 범용 TLS unpinning과 준비된 Frida hooks를 통한 proxy 강제화를 결합하고 모든 트래픽을 mitmproxy/Burp로 라우팅하는 방법이 효과적입니다.
Workflow
- 호스트에서 mitmproxy(또는 Burp)를 실행합니다. 디바이스가 호스트 IP/포트에 접근 가능한지 확인하세요.
 - HTTP Toolkit의 통합 Frida hooks를 로드하여 TLS unpinning과 OkHttp/OkHttp3, HttpsURLConnection, Conscrypt, WebView 등 일반 스택 전반에 대한 proxy 사용 강제화를 수행합니다. 이 스크립트는 CertificatePinner/TrustManager 검사를 우회하고 proxy selectors를 오버라이드하므로, 앱이 명시적으로 proxy를 비활성화해도 트래픽이 항상 당신의 proxy를 통해 전송됩니다.
 - Frida와 훅 스크립트로 대상 앱을 시작한 뒤 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
노트
- 가능한 경우 시스템 전체 프록시와 함께 사용하세요: 
adb shell settings put global http_proxy <host>:<port>. Frida 후크는 앱이 글로벌 설정을 우회할 때에도 프록시 사용을 강제합니다. - 이 기술은 pinning/proxy 회피가 흔한 모바일→IoT 온보딩 플로우를 MITM해야 할 때 이상적입니다.
 - Hooks: https://github.com/httptoolkit/frida-interception-and-unpinning
 
References
- Reversing Android Apps: Bypassing Detection Like a Pro
 - Frida Codeshare
 - Objection
 - apk-mitm
 - Jadx
 - Ghidra
 - r2frida
 - Apktool install guide
 - Magisk
 - Medusa (Android Frida framework)
 - Build a Repeatable Android Bug Bounty Lab: Emulator vs Magisk, Burp, Frida, and Medusa
 
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: 
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기: 
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
 - **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
 - HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
 
HackTricks