Android App抓包防护概述 什么是抓包防护 抓包防护 是Android应用为了防止网络流量被第三方工具(如Burp Suite、Fiddler、Charles等)拦截和分析而实施的安全措施。这些防护措施旨在保护敏感数据的传输安全,防止中间人攻击和数据泄露。
Android App抓包防护架构图
graph TB
A[Android App] --> B[网络请求]
B --> C{防护机制检测}
C --> D[SSL Pinning]
C --> E[证书验证检测]
C --> F[代理检测]
C --> G[反调试检测]
C --> H[应用完整性检测]
D --> I[证书固定验证]
E --> J[CA证书检测]
F --> K[代理设置检查]
G --> L[调试状态检查]
H --> M[签名验证]
I --> N{验证通过?}
J --> N
K --> N
L --> N
M --> N
N -->|是| O[允许网络请求]
N -->|否| P[拒绝网络请求]
Q[抓包工具] --> R[Burp Suite]
Q --> S[Charles]
Q --> T[Fiddler]
R --> U[中间人攻击]
S --> U
T --> U
U --> V[拦截网络流量]
V --> W[分析敏感数据]
主要防护机制 1. SSL/TLS证书固定(Certificate Pinning) 防护原理 :应用在代码中硬编码了服务器的证书信息,只信任特定的证书,拒绝其他证书(包括用户安装的CA证书)。
SSL Pinning证书固定流程图 sequenceDiagram
participant App as Android App
participant SSL as SSL/TLS层
participant Server as 服务器
participant Proxy as 代理工具
Note over App,Proxy: 正常连接流程
App->>SSL: 发起HTTPS请求
SSL->>Server: 建立TLS连接
Server->>SSL: 返回服务器证书
SSL->>App: 验证证书有效性
App->>App: 检查证书哈希值
App->>App: 与预设哈希值比较
App->>Server: 证书验证通过,继续通信
Note over App,Proxy: 代理拦截流程
App->>SSL: 发起HTTPS请求
SSL->>Proxy: 连接被代理拦截
Proxy->>App: 返回代理证书
App->>App: 检查证书哈希值
App->>App: 与预设哈希值不匹配
App->>App: 拒绝连接,抛出异常
实现方式 : 证书固定通常通过OkHttp库实现,开发者会在代码中硬编码服务器的证书哈希值。当应用发起HTTPS请求时,会验证服务器证书是否与预设的哈希值匹配,如果不匹配则拒绝连接。这种方式可以有效防止中间人攻击,但也会阻止合法的抓包工具。
1 2 3 4 5 6 7 OkHttpClient client = new OkHttpClient .Builder() .certificatePinner(new CertificatePinner .Builder() .add("api.example.com" , "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" ) .add("api.example.com" , "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" ) .build()) .build();
检测方法 : 检测证书固定可以通过尝试使用自签名证书连接服务器来实现。如果应用没有实施证书固定,连接会成功;如果实施了证书固定,会抛出证书验证异常。
1 2 3 4 5 6 7 8 9 10 11 12 public boolean isCertificatePinningEnabled () { try { URL url = new URL ("https://api.example.com" ); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.connect(); return false ; } catch (Exception e) { return true ; } }
2. 证书验证绕过检测 防护原理 :应用检测是否安装了用户CA证书,如果检测到则拒绝连接。
实现方式 : 应用通过检查系统的信任存储来检测是否安装了用户CA证书。常见的抓包工具如Burp Suite、Charles、Fiddler等都会安装自己的CA证书,应用可以通过检查证书的颁发者信息来识别这些工具。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public boolean hasUserCACertificate () { try { TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init((KeyStore) null ); X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0 ]; X509Certificate[] acceptedIssuers = defaultTrustManager.getAcceptedIssuers(); for (X509Certificate cert : acceptedIssuers) { String subject = cert.getSubjectDN().getName(); if (subject.contains("Burp" ) || subject.contains("Charles" ) || subject.contains("Fiddler" ) || subject.contains("Custom" )) { return true ; } } return false ; } catch (Exception e) { return false ; } }
3. 网络环境检测 防护原理 :检测设备是否连接了代理服务器,如果检测到代理则拒绝网络请求。
实现方式 : 网络环境检测主要通过检查系统代理设置和网络连接信息来实现。当用户配置了代理服务器时,系统会设置相应的代理属性,应用可以通过检查这些属性来判断是否存在代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public boolean isProxyEnabled () { try { String proxyHost = System.getProperty("http.proxyHost" ); String proxyPort = System.getProperty("http.proxyPort" ); if (proxyHost != null && proxyPort != null ) { return true ; } ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); if (activeNetwork != null && activeNetwork.isConnected()) { ProxyInfo proxyInfo = activeNetwork.getProxyInfo(); if (proxyInfo != null ) { return true ; } } return false ; } catch (Exception e) { return false ; } }
4. 反调试检测 防护原理 :检测应用是否在调试模式下运行,如果检测到调试则拒绝网络请求。
反Hook检测流程图 flowchart TD
A[应用启动] --> B[检测Hook工具]
B --> C{检测Frida}
C -->|发现| D[标记为被Hook]
C -->|未发现| E{检测Xposed}
E -->|发现| D
E -->|未发现| F{检测Substrate}
F -->|发现| D
F -->|未发现| G{检测内存修改}
G -->|发现| D
G -->|未发现| H{检测异常行为}
H -->|发现| D
H -->|未发现| I[正常运行]
D --> J[触发反Hook机制]
J --> K[清除Hook痕迹]
K --> L[退出应用]
I --> M[继续正常执行]
N[检测方法] --> O[文件系统检查]
N --> P[进程检查]
N --> Q[端口检查]
N --> R[内存检查]
N --> S[行为分析]
实现方式 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public boolean isDebugging () { try { boolean isDebug = (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 ; if (isDebug) { return true ; } boolean isDebuggerConnected = Debug.isDebuggerConnected(); if (isDebuggerConnected) { return true ; } try { Socket socket = new Socket ("127.0.0.1" , 8700 ); socket.close(); return true ; } catch (Exception e) { } return false ; } catch (Exception e) { return false ; } }
5. 应用完整性检测 防护原理 :检测应用是否被修改或重打包,如果检测到则拒绝网络请求。
实现方式 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public boolean isAppModified () { try { PackageManager pm = getPackageManager(); PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES); Signature[] signatures = packageInfo.signatures; String currentSignature = signatures[0 ].toCharsString(); String expectedSignature = "预期签名的SHA1值" ; if (!currentSignature.equals(expectedSignature)) { return true ; } return false ; } catch (Exception e) { return true ; } }
绕过技术详解 Frida Hook原理图 graph LR
A[原始函数] --> B[函数入口点]
B --> C[原始函数体]
C --> D[函数返回]
E[Frida Hook] --> F[Hook函数入口]
F --> G[自定义Hook函数]
G --> H[修改参数/返回值]
H --> I[调用原始函数]
I --> J[处理返回结果]
J --> K[返回修改后的结果]
B -.->|被Hook| F
F -.->|替换| B
L[应用进程] --> M[加载Frida Agent]
M --> N[注册Hook点]
N --> O[动态修改函数]
O --> P[执行Hook逻辑]
1. SSL Pinning绕过 方法一:Frida Hook Frida Hook是最常用的SSL Pinning绕过方法,通过Hook证书验证相关的函数来实现绕过。这种方法不需要修改应用代码,只需要在运行时动态Hook即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Java .perform (function ( ) { var CertificatePinner = Java .use ("okhttp3.CertificatePinner" ); CertificatePinner .check .overload ("java.lang.String" , "java.util.List" ).implementation = function (hostname, peerCertificates ) { console .log ("绕过SSL Pinning: " + hostname); return ; }; var X509TrustManager = Java .use ("javax.net.ssl.X509TrustManager" ); X509TrustManager.checkClientTrusted .implementation = function (chain, authType ) { console .log ("绕过客户端证书检查" ); }; X509TrustManager.checkServerTrusted .implementation = function (chain, authType ) { console .log ("绕过服务端证书检查" ); }; X509TrustManager.getAcceptedIssuers .implementation = function ( ) { return Java .use ("java.util.ArrayList" ).$new(); }; });
方法二:Xposed模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class SSLUnpinning implements IXposedHookLoadPackage { @Override public void handleLoadPackage (XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { XposedHelpers.findAndHookMethod("okhttp3.CertificatePinner" , lpparam.classLoader, "check" , String.class, List.class, new XC_MethodReplacement () { @Override protected Object replaceHookedMethod (MethodHookParam param) throws Throwable { return null ; } }); XposedHelpers.findAndHookMethod("javax.net.ssl.X509TrustManager" , lpparam.classLoader, "checkServerTrusted" , X509Certificate[].class, String.class, new XC_MethodReplacement () { @Override protected Object replaceHookedMethod (MethodHookParam param) throws Throwable { return null ; } }); } }
方法三:内存补丁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <jni.h> #include <string.h> JNIEXPORT void JNICALL Java_com_example_SSLUnpinner_unpin (JNIEnv *env, jobject thiz) { void *ssl_ctx_set_verify = dlsym(RTLD_DEFAULT, "SSL_CTX_set_verify" ); if (ssl_ctx_set_verify) { unsigned char patch[] = {0x90 , 0x90 , 0x90 , 0x90 , 0x90 }; mprotect(ssl_ctx_set_verify, 5 , PROT_READ | PROT_WRITE | PROT_EXEC); memcpy (ssl_ctx_set_verify, patch, 5 ); mprotect(ssl_ctx_set_verify, 5 , PROT_READ | PROT_EXEC); } }
2. 代理检测绕过 方法一:Hook系统属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Java .perform (function ( ) { var System = Java .use ("java.lang.System" ); System .getProperty .overload ("java.lang.String" ).implementation = function (key ) { if (key === "http.proxyHost" || key === "http.proxyPort" || key === "https.proxyHost" || key === "https.proxyPort" ) { console .log ("绕过代理检测: " + key); return null ; } return this .getProperty (key); }; var ConnectivityManager = Java .use ("android.net.ConnectivityManager" ); ConnectivityManager .getActiveNetworkInfo .implementation = function ( ) { var networkInfo = this .getActiveNetworkInfo (); if (networkInfo) { networkInfo.setProxyInfo (null ); } return networkInfo; }; });
方法二:修改网络配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class ProxyBypass { public static void bypassProxyDetection () { try { System.setProperty("http.proxyHost" , "" ); System.setProperty("http.proxyPort" , "" ); System.setProperty("https.proxyHost" , "" ); System.setProperty("https.proxyPort" , "" ); ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); if (cm != null ) { cm.setNetworkPreference(ConnectivityManager.TYPE_WIFI); } } catch (Exception e) { e.printStackTrace(); } } }
3. 反调试绕过 方法一:Frida Hook反调试检测 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Java .perform (function ( ) { var Debug = Java .use ("android.os.Debug" ); Debug .isDebuggerConnected .implementation = function ( ) { console .log ("绕过调试检测" ); return false ; }; var ApplicationInfo = Java .use ("android.content.pm.ApplicationInfo" ); ApplicationInfo .flags .implementation = function ( ) { var flags = this .flags ; flags = flags & ~ApplicationInfo .FLAG_DEBUGGABLE .value ; return flags; }; var Socket = Java .use ("java.net.Socket" ); Socket .$init .overload ("java.lang.String" , "int" ).implementation = function (host, port ) { if (host === "127.0.0.1" && port === 8700 ) { console .log ("绕过调试端口检测" ); throw Java .use ("java.net.ConnectException" ).$new("Connection refused" ); } return this .$init(host, port); }; });
方法二:修改应用标志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class DebugBypass { public static void bypassDebugDetection () { try { ApplicationInfo appInfo = getApplicationInfo(); appInfo.flags = appInfo.flags & ~ApplicationInfo.FLAG_DEBUGGABLE; System.setProperty("ro.debuggable" , "0" ); System.setProperty("ro.secure" , "1" ); } catch (Exception e) { e.printStackTrace(); } } }
4. 应用完整性绕过 方法一:Hook签名验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Java .perform (function ( ) { var PackageManager = Java .use ("android.content.pm.PackageManager" ); PackageManager .getPackageInfo .overload ("java.lang.String" , "int" ).implementation = function (packageName, flags ) { var packageInfo = this .getPackageInfo (packageName, flags); if (flags == PackageManager .GET_SIGNATURES .value ) { var Signature = Java .use ("android.content.pm.Signature" ); var fakeSignature = Signature .$new("伪造的签名信息" ); packageInfo.signatures = [fakeSignature]; } return packageInfo; }; });
方法二:修改签名文件 1 2 3 4 5 6 7 apktool d app.apk apktool b app -o app_modified.apk jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore app_modified.apk alias_name
进阶绕过技术 1. 动态Hook技术 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Java .perform (function ( ) { Java .choose ("com.example.SecurityChecker" , { onMatch : function (instance ) { console .log ("找到SecurityChecker实例" ); instance.checkSecurity .implementation = function ( ) { console .log ("绕过安全检查" ); return false ; }; }, onComplete : function ( ) { console .log ("搜索完成" ); } }); });
2. 内存修改技术 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <sys/mman.h> #include <unistd.h> void patchMemory (void *addr, unsigned char *patch, size_t patchSize) { mprotect(addr, patchSize, PROT_READ | PROT_WRITE | PROT_EXEC); memcpy (addr, patch, patchSize); mprotect(addr, patchSize, PROT_READ | PROT_EXEC); }
3. 系统调用Hook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <sys/ptrace.h> #include <sys/user.h> long syscall_hook (int syscall_num, struct user_regs_struct *regs) { switch (syscall_num) { case SYS_ptrace: return 0 ; case SYS_kill: return 0 ; default : return syscall(syscall_num, regs->rdi, regs->rsi, regs->rdx); } }
检测与防护 1. 检测Hook工具 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public boolean isHooked () { try { if (isFridaDetected()) { return true ; } if (isXposedDetected()) { return true ; } if (isSubstrateDetected()) { return true ; } return false ; } catch (Exception e) { return true ; } } private boolean isFridaDetected () { try { String[] fridaFiles = { "/data/local/tmp/frida-server" , "/data/local/tmp/re.frida.server" , "/system/bin/frida-server" }; for (String file : fridaFiles) { if (new File (file).exists()) { return true ; } } try { Socket socket = new Socket ("127.0.0.1" , 27042 ); socket.close(); return true ; } catch (Exception e) { } return false ; } catch (Exception e) { return false ; } }
2. 反Hook技术 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class AntiHook { public static void enableAntiHook () { if (isHooked()) { System.exit(0 ); } Timer timer = new Timer (); timer.scheduleAtFixedRate(new TimerTask () { @Override public void run () { if (isHooked()) { System.exit(0 ); } } }, 0 , 5000 ); } }
结语 技术对抗演进图 timeline
title Android抓包防护与绕过技术演进
section 初期阶段
2010-2015 : 基础HTTPS
: 简单代理检测
: 基础反调试
section 发展阶段
2015-2020 : SSL Pinning普及
: 证书固定技术
: 应用完整性检测
: Frida Hook兴起
: Xposed模块开发
section 成熟阶段
2020-2023 : 多层防护机制
: 智能检测算法
: 动态Hook技术
: 内存修改技术
: 系统调用Hook
section 未来趋势
2023+ : AI驱动检测
: 机器学习防护
: 云原生安全
: 零信任架构
: 量子加密
绕过技术分类图 mindmap
root((Android抓包绕过技术))
静态修改
重打包应用
修改证书验证
替换CA证书
修改网络配置
动态Hook
Frida Hook
SSL Pinning绕过
代理检测绕过
反调试绕过
签名验证绕过
Xposed模块
系统API Hook
应用层Hook
框架层Hook
Substrate Hook
iOS平台Hook
运行时修改
内存修改
直接内存操作
函数替换
指令修改
数据结构修改
系统调用Hook
内核层Hook
系统调用拦截
进程间通信Hook
文件系统Hook
在实际应用中,无论是进行安全测试、漏洞挖掘,还是应用安全评估,都需要在合法合规的前提下使用这些技术。同时,开发者也需要不断加强应用的安全防护,采用多层防护策略,提高应用的安全性。