第8章 实战场景

8.1 SSL Pinning 绕过

通用 TrustManager 绕过

Java.perform(function() {
    // 创建宽容的 TrustManager
    var TrustManager = Java.registerClass({
        name: 'com.frida.TrustManager',
        implements: [Java.use('javax.net.ssl.X509TrustManager')],
        methods: {
            checkClientTrusted(chain, authType) {},
            checkServerTrusted(chain, authType) {},
            getAcceptedIssuers() { return []; }
        }
    });
 
    // 替换 SSLContext
    var SSLContext = Java.use('javax.net.ssl.SSLContext');
    SSLContext.init.overload(
        '[Ljavax.net.ssl.KeyManager;',
        '[Ljavax.net.ssl.TrustManager;',
        'java.security.SecureRandom'
    ).implementation = function(km, tm, sr) {
        console.log('[*] SSLContext.init 被拦截');
        this.init(km, [TrustManager.$new()], sr);
    };
});

OkHttp CertificatePinner 绕过

Java.perform(function() {
    try {
        var CertificatePinner = Java.use('okhttp3.CertificatePinner');
 
        CertificatePinner.check.overload('java.lang.String', 'java.util.List')
            .implementation = function(hostname, peerCerts) {
                console.log('[*] OkHttp3 pinning 绕过: ' + hostname);
                return;
            };
 
        // Kotlin 变体
        CertificatePinner['check$okhttp'].overload(
            'java.lang.String', 'kotlin.jvm.functions.Function0')
            .implementation = function(hostname, peerCerts) {
                console.log('[*] OkHttp3 check$okhttp 绕过: ' + hostname);
                return;
            };
    } catch(e) {
        console.log('[!] OkHttp3 未找到: ' + e);
    }
});

Android 系统 TrustManagerImpl 绕过

Java.perform(function() {
    try {
        var TrustManagerImpl = Java.use(
            'com.android.org.conscrypt.TrustManagerImpl');
        TrustManagerImpl.verifyChain.implementation = function(
            untrustedChain, trustAnchorChain, host,
            clientAuth, ocspData, tlsSctData) {
            console.log('[*] TrustManagerImpl 绕过: ' + host);
            return untrustedChain;
        };
    } catch(e) {}
});

Android 14/15 Network Security 绕过

Android 14+ 强化了网络安全策略,增加了对证书透明度(CT)的验证:

Java.perform(function() {
    // Android 14+ NetworkSecurityConfig 绕过
    try {
        var NetworkSecurityTrustManager = Java.use(
            'android.security.net.config.NetworkSecurityTrustManager');
        NetworkSecurityTrustManager.checkServerTrusted.overload(
            '[Ljava.security.cert.X509Certificate;',
            'java.lang.String',
            'java.lang.String'
        ).implementation = function(chain, authType, host) {
            console.log('[*] NetworkSecurityTrustManager 绕过: ' + host);
            return Java.use('java.util.Arrays').asList(chain);
        };
    } catch(e) {
        console.log('[!] NetworkSecurityTrustManager 不可用: ' + e);
    }
 
    // Certificate Transparency 绕过(Android 14+)
    try {
        var CTVerifier = Java.use(
            'android.net.http.X509TrustManagerExtensions');
        CTVerifier.checkServerTrusted.overload(
            '[Ljava.security.cert.X509Certificate;',
            'java.lang.String',
            'java.lang.String'
        ).implementation = function(chain, authType, host) {
            console.log('[*] X509TrustManagerExtensions 绕过: ' + host);
            return Java.use('java.util.Arrays').asList(chain);
        };
    } catch(e) {}
 
    // Conscrypt Platform 绕过(Android 14+ 内部实现变更)
    try {
        var ConscryptPlatform = Java.use(
            'com.android.org.conscrypt.Platform');
        ConscryptPlatform.checkServerTrusted.overload(
            'javax.net.ssl.X509TrustManager',
            '[Ljava.security.cert.X509Certificate;',
            'java.lang.String',
            'com.android.org.conscrypt.AbstractConscryptSocket'
        ).implementation = function(tm, chain, authType, socket) {
            console.log('[*] Conscrypt Platform 绕过');
            return Java.use('java.util.Collections')
                .emptyList();
        };
    } catch(e) {}
});

Flutter / BoringSSL 绕过(Native 层)

var m = Process.findModuleByName("libflutter.so");
if (m) {
    // ARM64 ssl_verify_peer_cert 特征码
    var patterns = [
        "FF 03 05 D1 FD 7B 0F A9",  // 旧版 Flutter
        "F4 4F 02 A9 FD 7B 03 A9",  // Flutter 3.x+
    ];
    patterns.forEach(function(pattern) {
        Memory.scan(m.base, m.size, pattern, {
            onMatch(address, size) {
                console.log('[*] SSL verify @ ' + address);
                Interceptor.attach(address, {
                    onLeave(retval) {
                        retval.replace(0x0);  // 返回 ssl_verify_ok
                    }
                });
            },
            onComplete() {}
        });
    });
}

一体化 SSL 绕过(推荐方案)

对于 Android 14/15,建议使用 httptoolkit/frida-interception-and-unpinning 工具包,覆盖主流场景:

# 克隆最新版本
git clone https://github.com/httptoolkit/frida-interception-and-unpinning
 
# 使用(自动处理多种 pinning 实现 + 混淆回退)
frida -U -f com.target.app \
  -l frida-interception-and-unpinning/frida-script.js

该工具包特性:

  • 自动流量重定向到代理
  • 系统证书注入
  • 主流库 pinning 绕过(OkHttp、Volley、Flutter、Conscrypt)
  • 混淆实现的自动回退修补
  • Root 检测绕过
  • HTTP/3 阻断强制 HTTP/2 回退
  • 支持 Android 14/15 的新证书验证链

8.2 反检测与绕过

常见检测方法

检测方式原理Android 14/15 新增
端口扫描检查 TCP 27042 是否开放-
进程名扫描扫描 /proc/*/cmdline 找 “frida-server”-
内存映射扫描检查 /proc/self/maps 中的 frida-agent*.so更严格的 SELinux 限制
字符串扫描搜索内存中 “LIBFRIDA”、“frida:rpc” 等-
线程名检测查找 “gmain”、“gdbus”、“gum-js-loop” 线程-
ptrace 自检ptrace(PTRACE_TRACEME) 失败说明已被附加-
D-Bus 协议检测Frida 内部使用 D-Bus-
匿名内存 setArgV0 检测检查匿名内存区域标记17.8.2 已修复
Key Attestation设备完整性远程验证Android 14+ 强制
异常 fd 检测检查 /proc/self/fd 中的异常 socket-

绕过端口检测

// 方式1: Java 层拦截
Java.perform(function() {
    var Socket = Java.use("java.net.Socket");
    Socket.$init.overload("java.lang.String", "int")
        .implementation = function(host, port) {
        if (port === 27042 || port === 27043) {
            console.log("[*] 阻止连接 Frida 端口: " + port);
            throw Java.use("java.net.ConnectException")
                .$new("Connection refused");
        }
        return this.$init(host, port);
    };
});
 
// 方式2: 17.9.0+ 使用自定义端口(推荐)
// 服务端: ./frida-server -l 0.0.0.0:8888
// 客户端: 使用 Droidy backend 的 control-endpoint 选项
// 或 abstract socket: ./frida-server -l localabstract:my-frida

绕过 /proc/self/maps 扫描

// 完整的 maps 过滤方案
var fopen = Module.findExportByName("libc.so", "fopen");
var fgets = Module.findExportByName("libc.so", "fgets");
 
var filterActive = false;
var filteredFp = null;
 
Interceptor.attach(fopen, {
    onEnter(args) {
        var path = args[0].readUtf8String();
        if (path && path.indexOf("/proc/") !== -1 &&
            path.indexOf("/maps") !== -1) {
            this.isMaps = true;
        }
    },
    onLeave(retval) {
        if (this.isMaps) {
            filteredFp = retval;
            filterActive = true;
        }
    }
});
 
Interceptor.attach(fgets, {
    onEnter(args) {
        this.buf = args[0];
        this.fp = args[2];
    },
    onLeave(retval) {
        if (filterActive && this.fp && this.fp.equals(filteredFp)) {
            var line = this.buf.readUtf8String();
            if (line && (
                line.indexOf("frida") !== -1 ||
                line.indexOf("gadget") !== -1 ||
                line.indexOf("gum-js") !== -1
            )) {
                // 跳过这行,读取下一行
                retval.replace(ptr(0));
            }
        }
    }
});

绕过字符串检测

Interceptor.attach(Module.findExportByName("libc.so", "strstr"), {
    onEnter(args) {
        this.needle = args[1].readUtf8String();
    },
    onLeave(retval) {
        if (this.needle && (
            this.needle.indexOf("frida") !== -1 ||
            this.needle.indexOf("LIBFRIDA") !== -1 ||
            this.needle.indexOf("gum-js-loop") !== -1 ||
            this.needle.indexOf("gmain") !== -1 ||
            this.needle.indexOf("linjector") !== -1
        )) {
            retval.replace(ptr(0));  // 返回 NULL(未找到)
        }
    }
});
 
// 同时 hook strcmp/memcmp
Interceptor.attach(Module.findExportByName("libc.so", "strcmp"), {
    onEnter(args) {
        var s1 = args[0].readUtf8String();
        var s2 = args[1].readUtf8String();
        if ((s1 && s1.indexOf("frida") !== -1) ||
            (s2 && s2.indexOf("frida") !== -1)) {
            this.shouldSpoof = true;
        }
    },
    onLeave(retval) {
        if (this.shouldSpoof) {
            retval.replace(ptr(-1));  // 不匹配
        }
    }
});

绕过线程名检测(Android 14/15 强化)

// Android 14+ 使用更严格的线程名扫描
Interceptor.attach(Module.findExportByName("libc.so", "pthread_getname_np"), {
    onEnter(args) {
        this.buf = args[1];
        this.tid = args[0];
    },
    onLeave(retval) {
        if (retval.toInt32() === 0) {
            var name = this.buf.readUtf8String();
            if (name && (
                name === "gmain" ||
                name === "gdbus" ||
                name === "gum-js-loop" ||
                name === "pool-frida" ||
                name.indexOf("frida") !== -1
            )) {
                // 替换为无害的线程名
                this.buf.writeUtf8String("pool-thread-" +
                    this.tid.toString().slice(-2));
            }
        }
    }
});
 
// Hook opendir + readdir 隐藏 /proc/self/task 中的线程
Interceptor.attach(Module.findExportByName("libc.so", "openat"), {
    onEnter(args) {
        var path = args[1].readUtf8String();
        if (path && path.indexOf("/proc/self/task") !== -1) {
            this.isTask = true;
        }
    }
});

隐蔽部署技巧(17.9.11 推荐方案)

# 1. 重命名 frida-server(基本)
mv frida-server-17.9.11-android-arm64 hluda-server
chmod 755 hluda-server
 
# 2. 使用 abstract socket(不暴露 TCP 端口,17.9.0+ 支持)
./hluda-server -l localabstract:my-hidden-service &
 
# 3. 客户端通过 ADB 转发连接
adb forward tcp:27042 localabstract:my-hidden-service
frida -H 127.0.0.1:27042 -f com.target.app
 
# 4. 或使用完全自定义端口
./hluda-server -l 0.0.0.0:31337 &
frida -H 192.168.1.100:31337 -f com.target.app
 
# 5. Gadget 注入模式(免 Root,最隐蔽)
# 见第10章 objection patchapk

Android 14/15 特有防护绕过

// Android 14+ 引入了更严格的 SELinux 和进程隔离
// 以下处理 restricted file access 检测
 
Java.perform(function() {
    // 绕过 Play Integrity API(替代 SafetyNet)
    try {
        var IntegrityManager = Java.use(
            'com.google.android.play.core.integrity.IntegrityManager');
        // 注意:Play Integrity 使用远程验证,客户端绕过效果有限
        // 推荐配合 Magisk + Shamiko 使用
    } catch(e) {}
 
    // 绕过 PackageManager 检测(检查已安装的逆向工具)
    var PackageManager = Java.use('android.app.ApplicationPackageManager');
    PackageManager.getInstalledPackages.overload('int')
        .implementation = function(flags) {
        var packages = this.getInstalledPackages(flags);
        var filtered = Java.use('java.util.ArrayList').$new();
        var iter = packages.iterator();
        while (iter.hasNext()) {
            var pkg = iter.next();
            var name = pkg.packageName.value;
            if (name.indexOf("frida") === -1 &&
                name.indexOf("xposed") === -1 &&
                name.indexOf("magisk") === -1) {
                filtered.add(pkg);
            }
        }
        return filtered;
    };
});

8.3 系统调用追踪

libc 层追踪

// 文件操作
Interceptor.attach(Module.findExportByName("libc.so", "open"), {
    onEnter(args) {
        this.path = args[0].readUtf8String();
    },
    onLeave(retval) {
        console.log("[OPEN] " + this.path + " => fd=" + retval.toInt32());
    }
});
 
// 网络操作
Interceptor.attach(Module.findExportByName("libc.so", "connect"), {
    onEnter(args) {
        var fd = args[0].toInt32();
        var addr = args[1];
        var family = addr.readU16();
        if (family === 2) {  // AF_INET
            var port = (addr.add(2).readU8() << 8) | addr.add(3).readU8();
            var ip = addr.add(4).readU8() + '.' +
                     addr.add(5).readU8() + '.' +
                     addr.add(6).readU8() + '.' +
                     addr.add(7).readU8();
            console.log("[CONNECT] fd=" + fd + " " + ip + ":" + port);
        }
    }
});
 
// ioctl(Binder IPC)
Interceptor.attach(Module.findExportByName("libc.so", "ioctl"), {
    onEnter(args) {
        var fd = args[0].toInt32();
        var request = args[1].toInt32();
        console.log("[IOCTL] fd=" + fd + " request=0x" + request.toString(16));
    }
});

使用 frida-trace 快速追踪

# 文件操作
frida-trace -U -f com.target.app -i "open*" -i "read" -i "write" -i "close"
 
# 网络操作
frida-trace -U -f com.target.app -i "connect" -i "send*" -i "recv*" -i "socket"
 
# 内存操作
frida-trace -U -f com.target.app -i "mmap*" -i "mprotect" -i "munmap"

使用 frida-strace 系统调用追踪(17.8.0+ 新功能)

Frida 17.8.0 引入了基于 eBPF 的 frida-strace 工具,提供类似 strace 的系统调用追踪能力:

# 追踪目标应用的所有系统调用
frida-strace -U -f com.target.app
 
# 追踪正在运行的进程
frida-strace -U -n com.target.app
 
# 适用场景:
# - 检测 RASP(运行时应用自我保护)机制
# - 分析反调试系统调用(ptrace, prctl)
# - 监控文件/网络访问模式
# - 检测异常进程间通信

8.4 Android Framework Hook

Activity 生命周期

Java.perform(function() {
    var Activity = Java.use("android.app.Activity");
 
    Activity.onCreate.overload("android.os.Bundle")
        .implementation = function(savedInstanceState) {
        console.log("[*] onCreate: " + this.getClass().getName());
        this.onCreate(savedInstanceState);
    };
 
    Activity.onResume.implementation = function() {
        console.log("[*] onResume: " + this.getClass().getName());
        this.onResume();
    };
 
    Activity.startActivity.overload("android.content.Intent")
        .implementation = function(intent) {
        console.log("[*] startActivity: " + intent.getComponent());
        this.startActivity(intent);
    };
});

View 渲染性能监控

Java.perform(function() {
    var View = Java.use("android.view.View");
 
    View.draw.overload("android.graphics.Canvas")
        .implementation = function(canvas) {
        var start = Date.now();
        this.draw(canvas);
        var elapsed = Date.now() - start;
        if (elapsed > 16) {
            console.log("[JANK] View.draw " + elapsed + "ms: " +
                this.getClass().getName());
        }
    };
});

Binder 事务监控

Java.perform(function() {
    var Binder = Java.use("android.os.Binder");
    Binder.transact.overload(
        "int", "android.os.Parcel", "android.os.Parcel", "int")
        .implementation = function(code, data, reply, flags) {
        console.log("[*] Binder.transact code=" + code +
            " from " + this.getClass().getName());
        return this.transact(code, data, reply, flags);
    };
});

8.5 加密函数监控

Java.perform(function() {
    // 监控所有 Cipher 操作
    var Cipher = Java.use('javax.crypto.Cipher');
 
    Cipher.doFinal.overload('[B').implementation = function(input) {
        console.log('[Cipher] doFinal 输入 (' + input.length + ' bytes):');
        console.log('  ' + bytesToHex(input));
        var result = this.doFinal(input);
        console.log('[Cipher] doFinal 输出 (' + result.length + ' bytes):');
        console.log('  ' + bytesToHex(result));
        return result;
    };
 
    // 监控 SecretKeySpec(捕获密钥)
    var SecretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');
    SecretKeySpec.$init.overload('[B', 'java.lang.String')
        .implementation = function(key, algorithm) {
        console.log('[Key] algorithm=' + algorithm +
            ' key=' + bytesToHex(key));
        this.$init(key, algorithm);
    };
 
    // 监控 MessageDigest(Hash 操作)
    var MessageDigest = Java.use('java.security.MessageDigest');
    MessageDigest.digest.overload('[B').implementation = function(input) {
        var algo = this.getAlgorithm();
        console.log('[Hash] ' + algo + ' input (' + input.length + ' bytes)');
        var result = this.digest(input);
        console.log('[Hash] ' + algo + ' output: ' + bytesToHex(result));
        return result;
    };
});
 
function bytesToHex(bytes) {
    var hex = [];
    for (var i = 0; i < bytes.length; i++) {
        hex.push(('0' + (bytes[i] & 0xFF).toString(16)).slice(-2));
    }
    return hex.join(' ');
}

8.6 SharedPreferences 监控

Java.perform(function() {
    var SharedPreferencesImpl = Java.use(
        'android.app.SharedPreferencesImpl$EditorImpl');
 
    SharedPreferencesImpl.putString.implementation = function(key, value) {
        console.log('[SP] putString("' + key + '", "' + value + '")');
        return this.putString(key, value);
    };
 
    SharedPreferencesImpl.putInt.implementation = function(key, value) {
        console.log('[SP] putInt("' + key + '", ' + value + ')');
        return this.putInt(key, value);
    };
 
    SharedPreferencesImpl.putBoolean.implementation = function(key, value) {
        console.log('[SP] putBoolean("' + key + '", ' + value + ')');
        return this.putBoolean(key, value);
    };
});

8.7 Android 14/15 特定场景

绕过 Credential Manager(Android 14+)

Java.perform(function() {
    // Android 14 引入的 CredentialManager API
    try {
        var CredentialManager = Java.use(
            'android.credentials.CredentialManager');
        console.log('[*] CredentialManager 可用,监控中...');
 
        // 监控凭据请求
        CredentialManager.getCredential.overload(
            'android.content.Context',
            'android.credentials.GetCredentialRequest',
            'android.os.CancellationSignal',
            'java.util.concurrent.Executor',
            'android.os.OutcomeReceiver'
        ).implementation = function(ctx, request, cancel, executor, callback) {
            console.log('[Credential] getCredential 请求');
            this.getCredential(ctx, request, cancel, executor, callback);
        };
    } catch(e) {}
});

监控 Android 15 预测性返回手势

Java.perform(function() {
    try {
        var OnBackAnimationCallback = Java.use(
            'android.window.OnBackAnimationCallback');
        // 监控返回手势预测动画
        console.log('[*] 预测性返回手势监控已启用');
    } catch(e) {}
});

ContentProvider 安全访问监控(Android 14+ 强化)

Java.perform(function() {
    var ContentResolver = Java.use('android.content.ContentResolver');
 
    ContentResolver.query.overload(
        'android.net.Uri',
        '[Ljava.lang.String;',
        'android.os.Bundle',
        'android.os.CancellationSignal'
    ).implementation = function(uri, projection, queryArgs, cancel) {
        console.log('[ContentProvider] query: ' + uri.toString());
        return this.query(uri, projection, queryArgs, cancel);
    };
});