在前面的文章中,探讨了SSL/TLS协议解析、SSL Pinning绕过技术、Frida Hook、Xposed Hook以及ADB命令等移动安全技术。本篇内容将基于这些技术基础,来分析Android App的抓包防护机制,从SSL Pinning到证书固定,从网络检测到反调试,全面解析防护原理并提供相应的绕过技术,干货拉满了。。
Android App抓包防护概述
什么是抓包防护
抓包防护是Android应用为了防止网络流量被第三方工具(如Burp Suite、Fiddler、Charles等)拦截和分析而实施的安全措施。这些防护措施旨在保护敏感数据的传输安全,防止中间人攻击和数据泄露。
Android App抓包防护架构图
主要防护机制
1. SSL/TLS证书固定(Certificate Pinning)
防护原理:应用在代码中硬编码了服务器的证书信息,只信任特定的证书,拒绝其他证书(包括用户安装的CA证书)。
SSL Pinning证书固定流程图
实现方式: 证书固定通常通过OkHttp库实现,开发者会在代码中硬编码服务器的证书哈希值。当应用发起HTTPS请求时,会验证服务器证书是否与预设的哈希值匹配,如果不匹配则拒绝连接。这种方式可以有效防止中间人攻击,但也会阻止合法的抓包工具。
// OkHttp证书固定示例
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
.build())
.build();
检测方法: 检测证书固定可以通过尝试使用自签名证书连接服务器来实现。如果应用没有实施证书固定,连接会成功;如果实施了证书固定,会抛出证书验证异常。
// 检测证书固定
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证书,应用可以通过检查证书的颁发者信息来识别这些工具。
// 检测用户CA证书
public boolean hasUserCACertificate() {
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
// 检查是否有用户添加的CA证书
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")) {
returntrue;
}
}
returnfalse;
} catch (Exception e) {
returnfalse;
}
}
3. 网络环境检测
防护原理:检测设备是否连接了代理服务器,如果检测到代理则拒绝网络请求。
实现方式: 网络环境检测主要通过检查系统代理设置和网络连接信息来实现。当用户配置了代理服务器时,系统会设置相应的代理属性,应用可以通过检查这些属性来判断是否存在代理。
// 检测代理设置
public boolean isProxyEnabled() {
try {
// 检查系统代理设置
String proxyHost = System.getProperty("http.proxyHost");
String proxyPort = System.getProperty("http.proxyPort");
if (proxyHost != null && proxyPort != null) {
returntrue;
}
// 检查网络连接
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork != null && activeNetwork.isConnected()) {
// 检查是否有代理
ProxyInfo proxyInfo = activeNetwork.getProxyInfo();
if (proxyInfo != null) {
returntrue;
}
}
returnfalse;
} catch (Exception e) {
returnfalse;
}
}
4. 反调试检测
防护原理:检测应用是否在调试模式下运行,如果检测到调试则拒绝网络请求。
反Hook检测流程图
实现方式:
// 检测调试状态
public boolean isDebugging() {
try {
// 检查调试标志
boolean isDebug = (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
if (isDebug) {
returntrue;
}
// 检查调试器连接
boolean isDebuggerConnected = Debug.isDebuggerConnected();
if (isDebuggerConnected) {
returntrue;
}
// 检查调试端口
try {
Socket socket = new Socket("127.0.0.1", 8700);
socket.close();
returntrue;
} catch (Exception e) {
// 端口未开放,不是调试状态
}
returnfalse;
} catch (Exception e) {
returnfalse;
}
}
5. 应用完整性检测
防护原理:检测应用是否被修改或重打包,如果检测到则拒绝网络请求。
实现方式:
// 检测应用签名
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)) {
returntrue;
}
returnfalse;
} catch (Exception e) {
returntrue;
}
}
绕过技术详解
Frida Hook原理图
1. SSL Pinning绕过
方法一:Frida Hook
Frida Hook是最常用的SSL Pinning绕过方法,通过Hook证书验证相关的函数来实现绕过。这种方法不需要修改应用代码,只需要在运行时动态Hook即可。
// Frida Hook SSL Pinning
Java.perform(function() {
// Hook OkHttp的CertificatePinner
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;
};
// Hook X509TrustManager
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模块
// Xposed模块绕过SSL Pinning
publicclass SSLUnpinning implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
// Hook OkHttp
XposedHelpers.findAndHookMethod("okhttp3.CertificatePinner", lpparam.classLoader,
"check", String.class, List.class, new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
returnnull;
}
});
// Hook TrustManager
XposedHelpers.findAndHookMethod("javax.net.ssl.X509TrustManager", lpparam.classLoader,
"checkServerTrusted", X509Certificate[].class, String.class, new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
returnnull;
}
});
}
}
方法三:内存补丁
// 内存补丁绕过SSL Pinning
#include <jni.h>
#include <string.h>
JNIEXPORT void JNICALL Java_com_example_SSLUnpinner_unpin(JNIEnv *env, jobject thiz) {
// 获取SSL_CTX_set_verify函数地址
void *ssl_ctx_set_verify = dlsym(RTLD_DEFAULT, "SSL_CTX_set_verify");
if (ssl_ctx_set_verify) {
// 修改函数实现
unsignedchar patch[] = {0x90, 0x90, 0x90, 0x90, 0x90}; // NOP指令
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系统属性
// Frida Hook代理检测
Java.perform(function() {
// Hook System.getProperty
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);
returnnull;
}
returnthis.getProperty(key);
};
// Hook ConnectivityManager
var ConnectivityManager = Java.use("android.net.ConnectivityManager");
ConnectivityManager.getActiveNetworkInfo.implementation = function() {
var networkInfo = this.getActiveNetworkInfo();
if (networkInfo) {
// 清除代理信息
networkInfo.setProxyInfo(null);
}
return networkInfo;
};
});
方法二:修改网络配置
// 修改网络配置绕过代理检测
publicclass 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反调试检测
// Frida Hook反调试检测
Java.perform(function() {
// Hook Debug.isDebuggerConnected
var Debug = Java.use("android.os.Debug");
Debug.isDebuggerConnected.implementation = function() {
console.log("绕过调试检测");
returnfalse;
};
// Hook ApplicationInfo.FLAG_DEBUGGABLE
var ApplicationInfo = Java.use("android.content.pm.ApplicationInfo");
ApplicationInfo.flags.implementation = function() {
var flags = this.flags;
// 清除调试标志
flags = flags & ~ApplicationInfo.FLAG_DEBUGGABLE.value;
return flags;
};
// Hook Socket连接检测
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");
}
returnthis.$init(host, port);
};
});
方法二:修改应用标志
// 修改应用标志绕过反调试
publicclass 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签名验证
// Frida Hook签名验证
Java.perform(function() {
// Hook PackageManager.getPackageInfo
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;
};
});
方法二:修改签名文件
# 使用apktool重新签名
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技术
// 动态Hook技术
Java.perform(function() {
// 等待类加载
Java.choose("com.example.SecurityChecker", {
onMatch: function(instance) {
console.log("找到SecurityChecker实例");
// Hook实例方法
instance.checkSecurity.implementation = function() {
console.log("绕过安全检查");
returnfalse; // 返回安全检查通过
};
},
onComplete: function() {
console.log("搜索完成");
}
});
});
2. 内存修改技术
// 内存修改技术
#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
// 系统调用Hook
#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:
// 绕过ptrace检测
return0;
case SYS_kill:
// 绕过kill检测
return0;
default:
return syscall(syscall_num, regs->rdi, regs->rsi, regs->rdx);
}
}
检测与防护
1. 检测Hook工具
// 检测Hook工具
public boolean isHooked() {
try {
// 检测Frida
if (isFridaDetected()) {
returntrue;
}
// 检测Xposed
if (isXposedDetected()) {
returntrue;
}
// 检测Substrate
if (isSubstrateDetected()) {
returntrue;
}
returnfalse;
} catch (Exception e) {
returntrue;
}
}
private boolean isFridaDetected() {
try {
// 检测Frida相关文件
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()) {
returntrue;
}
}
// 检测Frida端口
try {
Socket socket = new Socket("127.0.0.1", 27042);
socket.close();
returntrue;
} catch (Exception e) {
// 端口未开放
}
returnfalse;
} catch (Exception e) {
returnfalse;
}
}
2. 反Hook技术
// 反Hook技术
publicclass AntiHook {
public static void enableAntiHook() {
// 检测并清除Hook
if (isHooked()) {
// 退出应用
System.exit(0);
}
// 定期检查
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (isHooked()) {
System.exit(0);
}
}
}, 0, 5000); // 每5秒检查一次
}
}
结语
技术对抗演进
绕过技术分类

