当前位置:首页>安卓APP>安卓逆向系列 09:Frida 反检测与对抗——魔高一尺道高一丈

安卓逆向系列 09:Frida 反检测与对抗——魔高一尺道高一丈

  • 2026-08-06 21:45:40
安卓逆向系列 09:Frida 反检测与对抗——魔高一尺道高一丈

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/

绕过:

  • 安装 Shamiko Magisk 模块(自动隐藏 Root 特征)

  • 使用 MagiskHide(旧版 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 全流程逆向

最新文章

随机文章