App 检测到 Frida 就崩溃?这是一场攻防游戏。本篇系统梳理所有检测手段和对应绕过方法,附完整反检测脚本。
一、为什么 App 要检测 Frida?
金融类、游戏类、电商类 App 普遍集成了安全 SDK,会主动检测运行环境,一旦发现 Frida/Root 特征,立即:
二、常见检测手段全览
1. 端口检测(最基础)
// 检测 Frida 默认端口 27042
intsock=socket(AF_INET, SOCK_STREAM, 0);
structsockaddr_inaddr= { AF_INET, htons(27042), INADDR_LOOPBACK };
if (connect(sock, &addr, sizeof(addr)) ==0) {
// Frida 在运行!
}
绕过: 修改 frida-server 端口
./frida-server -l0.0.0.0:19999 # 使用非默认端口
frida -U-H127.0.0.1:19999 com.target.app
2. /proc/self/maps 检测
FILE*fp=fopen("/proc/self/maps", "r");
while (fgets(buf, sizeof(buf), fp)) {
if (strstr(buf, "frida") ||strstr(buf, "linjector")) {
// 检测到注入!
}
}绕过: Hook fopen 过滤掉 frida 相关行
varfgets=newNativeFunction(
Module.findExportByName("libc.so", "fgets"),
'pointer', ['pointer', 'int', 'pointer']
);
Interceptor.replace(
Module.findExportByName("libc.so", "fgets"),
newNativeCallback(function(buf, size, fp) {
varresult=fgets(buf, size, fp);
if (result.isNull()) returnresult;
varline=Memory.readCString(result);
if (line.includes("frida") ||line.includes("linjector")) {
Memory.writeU8(buf, 0); // 返回空行
}
returnresult;
}, 'pointer', ['pointer', 'int', 'pointer'])
);
3. Root 检测
常见检测点:
/system/bin/su
/system/xbin/su
/data/local/tmp/su
com.topjohnwu.magisk (包名)
/sbin/.magisk/
绕过:
// 或者 Hook Java 层 Root 检测
Java.perform(function() {
varRuntime=Java.use("java.lang.Runtime");
Runtime.exec.overload("java.lang.String")
.implementation=function(cmd) {
if (cmd.includes("su") ||cmd.includes("which")) {
console.log("[Root检测] 拦截命令: "+cmd);
throwJava.use("java.io.IOException")
.$new("Cannot run program");
}
returnthis.exec(cmd);
};
});
4. 完整性校验
// App 校验自身签名
PackageInfopi=pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
Stringsig=pi.signatures[0].toCharsString();
if (!sig.equals(EXPECTED_SIGNATURE)) {
// 被重打包!
}
绕过:
Java.perform(function() {
varPackageManager=Java.use("android.app.ApplicationPackageManager");
PackageManager.getPackageInfo.overload(
"java.lang.String", "int"
).implementation=function(pkg, flags) {
varinfo=this.getPackageInfo(pkg, flags);
// 将签名替换为原始签名
if (flags===64) { // GET_SIGNATURES = 0x40
// 此处替换为真实签名(从未修改版 APK 提取)
}
returninfo;
};
});
5. 反调试(ptrace)
// App 先对自身调用 ptrace,占用调试槽
// 这样 GDB/LLDB 就无法再附加
ptrace(PTRACE_TRACEME, 0, 0, 0);
绕过: Hook ptrace
varptrace=Module.findExportByName("libc.so", "ptrace");
Interceptor.replace(ptrace, newNativeCallback(
function(request, pid, addr, data) {
// PTRACE_TRACEME = 0,直接返回成功
if (request===0) return0;
varoriginal=newNativeFunction(ptrace, 'long',
['int', 'int', 'pointer', 'pointer']);
returnoriginal(request, pid, addr, data);
},
'long', ['int', 'int', 'pointer', 'pointer']
));
6. frida-gadget 嵌入模式(最强方案)
当以上方案都失效时,使用 gadget 模式——将 frida 嵌入到 APK 中,伪装成普通 so 文件:
# 1. 解包 APK
apktool d target.apk -o out/
# 2. 下载 frida-gadget.so(对应架构)
# 重命名为无特征名称
mv frida-gadget-arm64.so out/lib/arm64-v8a/libsecurity_helper.so
# 3. 在 App 入口 smali 中加载 so
# 找到 Application 类的 <init> 方法,添加:
# const-string v0, "security_helper"
# invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
# 4. 重打包签名
apktool b out/ -o patched.apk
# 签名...
三、完整反检测脚本模板
// anti_detection.js —— 通用反检测脚本
"use strict";
(function() {
// ===== 1. 绕过 Root 检测 =====
Java.perform(function() {
// Hook 文件存在性检测
varFile=Java.use("java.io.File");
File.exists.implementation=function() {
varpath=this.getAbsolutePath();
varrootPaths= ["/system/bin/su", "/system/xbin/su",
"/sbin/.magisk", "/data/adb/magisk"];
for (varpofrootPaths) {
if (path===p) {
console.log("[Anti-Root] 拦截: "+path);
returnfalse;
}
}
returnthis.exists();
};
});
// ===== 2. 绕过 maps 扫描 =====
varfgets=newNativeFunction(
Module.findExportByName("libc.so", "fgets"),
'pointer', ['pointer', 'int', 'pointer']
);
Interceptor.replace(
Module.findExportByName("libc.so", "fgets"),
newNativeCallback(function(buf, size, fp) {
varresult=fgets(buf, size, fp);
if (!result.isNull()) {
varline=Memory.readCString(result);
varblacklist= ["frida", "linjector", "gadget"];
for (varbofblacklist) {
if (line.toLowerCase().includes(b)) {
Memory.writeU8(buf, 0);
returnresult;
}
}
}
returnresult;
}, 'pointer', ['pointer', 'int', 'pointer'])
);
// ===== 3. 绕过 ptrace 反调试 =====
varptrace=Module.findExportByName("libc.so", "ptrace");
if (ptrace) {
Interceptor.replace(ptrace, newNativeCallback(
function(req) { returnreq===0?0 : -1; },
'long', ['int', 'int', 'pointer', 'pointer']
));
}
console.log("[*] 反检测脚本已激活");
})();
四、各方案综合评级
| 方案 | 操作难度 | 绕过成功率 | 适用场景 |
|---|
| 修改端口 | ⭐ | 80% | 端口检测 |
| Hook fgets | ⭐⭐ | 85% | maps 扫描 |
| Shamiko | ⭐ | 90% | Root 检测 |
| 完整反检测脚本 | ⭐⭐ | 85% | 综合检测 |
| frida-gadget 嵌入 | ⭐⭐⭐⭐ | 95% | 强检测 App |
下一篇:实战综合——某出行 App 全流程逆向