实战复盘:用Frida绕过Android App签名校验的三种思路(附完整JS脚本)
2026/6/1 7:57:20 网站建设 项目流程

深度解析:Frida在Android签名校验绕过中的高阶应用

签名校验是Android应用安全防护的重要机制之一,但逆向工程师和安全研究人员经常需要绕过这些校验进行安全评估。本文将系统性地介绍三种基于Frida的签名校验绕过方法,并深入分析其适用场景和实现细节。

1. 签名校验机制概述

Android应用的签名校验通常分为三个层级:Java层校验、Native层校验和基于新API的校验。理解这些校验机制的工作原理是选择合适绕过方法的前提。

Java层校验通常通过PackageManager获取签名信息,与预设值进行比对。典型代码如下:

public boolean checkSignature() { String realSignature = getSignature(); return "预设签名值".equals(realSignature); }

Native层校验则通过JNI调用so库中的验证函数,这类校验更难直接分析:

JNIEXPORT jboolean JNICALL Java_com_example_checkSignature(JNIEnv *env, jobject thiz) { // 复杂的校验逻辑 }

新API校验主要针对Android 9及以上版本,使用SigningInfo类获取签名:

if (Build.VERSION.SDK_INT >= 28) { signingInfo = getPackageManager() .getPackageInfo(getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES) .signingInfo; }

2. 直接修改返回值法

这是最直接的绕过方式,适用于简单的Java层校验。通过Frida Hook校验函数并强制返回true即可。

2.1 基础实现脚本

Java.perform(function() { let SignatureCheck = Java.use("com.example.SignatureChecker"); SignatureCheck.checkSignature.implementation = function() { console.log("[+] Bypassing signature check"); return true; }; });

2.2 进阶技巧:条件绕过

在实际场景中,我们可以根据上下文决定是否绕过:

SignatureCheck.checkSignature.implementation = function() { let stack = Thread.backtrace(this.context, Backtracer.ACCURATE); // 只在特定调用路径下绕过 if (stack.includes("com.example.auth")) { return true; } return this.checkSignature(); };

2.3 优缺点分析

优势:

  • 实现简单,适合快速验证
  • 对性能影响最小

局限:

  • 无法处理Native层校验
  • 容易被完整性检查检测到

3. 关键函数Hook法

对于更复杂的校验逻辑,特别是涉及多层验证的情况,需要Hook关键函数来获取和修改校验参数。

3.1 签名获取函数Hook

Java.perform(function() { let PackageManager = Java.use("android.content.pm.PackageManager"); PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(pkgName, flags) { let result = this.getPackageInfo(pkgName, flags); // 修改签名信息 if (pkgName.equals(this.getPackageName())) { let fakeSignature = "伪造的签名值"; result.signatures[0].hashCode = function() { return fakeSignature; }; } return result; }; });

3.2 Native函数Hook

对于JNI函数,需要使用Interceptor:

let checkSignature = Module.findExportByName("libnative.so", "Java_com_example_checkSignature"); Interceptor.attach(checkSignature, { onLeave: function(retval) { // 强制返回验证成功 retval.replace(0x1); } });

3.3 动态参数修改

有些校验会比对多个参数,需要全面拦截:

Java.use("com.example.SignatureUtils").compare.implementation = function(a, b) { console.log(`Comparing ${a} and ${b}`); // 始终返回相等 return true; };

4. IO重定向技术

对于基于文件校验的防护(如CRC校验),IO重定向是最有效的解决方案。

4.1 基本原理

通过Hook文件访问相关函数,将校验文件路径重定向到原始未修改的APK:

原始流程: 校验代码 → 读取/data/app/.../base.apk → 计算CRC 重定向后: 校验代码 → 读取/data/local/tmp/original.apk → 计算CRC

4.2 完整实现方案

Java.perform(function() { let File = Java.use("java.io.File"); let originalApk = "/data/local/tmp/original.apk"; File.$init.overload('java.lang.String').implementation = function(path) { // 重定向特定路径 if (path.contains("base.apk")) { return this.$init(originalApk); } return this.$init(path); }; // 处理FileDescriptor情况 let FileInputStream = Java.use("java.io.FileInputStream"); FileInputStream.$init.overload('java.lang.String').implementation = function(path) { if (path.contains("base.apk")) { return this.$init(originalApk); } return this.$init(path); }; });

4.3 增强版重定向

针对不同API版本和实现方式的兼容处理:

const ENCRYPTED_APK = "/data/user/0/com.example/app_encrypted.apk"; function shouldRedirect(path) { return path.includes("base.apk") || path.includes("split_config.") || path.endsWith(".so"); } Java.perform(function() { // 处理多种文件访问方式 const redirectMap = { "java.io.File": ["$init", "getAbsolutePath"], "android.content.res.AssetManager": ["open"], "java.nio.file.Path": ["of"] }; Object.entries(redirectMap).forEach(([cls, methods]) => { methods.forEach(method => { try { let target = Java.use(cls)[method]; if (target) { target.implementation = function() { let original = target.apply(this, arguments); if (shouldRedirect(original)) { return ENCRYPTED_APK; } return original; }; } } catch (e) { console.warn(`Hook ${cls}.${method} failed: ${e}`); } }); }); });

5. 综合应用与选择策略

在实际逆向工程中,需要根据具体情况选择合适的绕过方法或组合使用多种技术。

5.1 方法选择决策树

是否涉及Native校验? ├─ 是 → 使用关键函数Hook法或IO重定向 └─ 否 → 校验是否基于文件? ├─ 是 → 优先考虑IO重定向 └─ 否 → 直接修改返回值或Hook校验函数

5.2 组合技术示例

针对混合校验的应用示例:

// 1. 绕过Java层校验 Java.use("com.example.JavaCheck").verify.implementation = () => true; // 2. 绕过Native校验 let nativeCheck = Module.findExportByName("libsecurity.so", "native_verify"); Interceptor.attach(nativeCheck, { onLeave: retval => retval.replace(1) }); // 3. 处理文件校验 Java.use("java.io.File").getAbsolutePath.implementation = function() { let path = this.getAbsolutePath(); return path.includes("base.apk") ? "/data/local/tmp/original.apk" : path; };

5.3 反检测技巧

现代应用会检测Frida的存在,需要采取对抗措施:

// 隐藏Frida特征 Interceptor.attach(Module.findExportByName("libc.so", "fopen"), { onEnter: function(args) { let path = Memory.readUtf8String(args[0]); if (path.includes("frida")) { this.fridaDetected = true; } }, onLeave: function(retval) { if (this.fridaDetected) { retval.replace(NULL); } } }); // 随机化脚本特征 const junkCode = Array(100).fill(0).map(() => Math.random().toString(36));

6. 实战案例分析

通过一个综合案例演示如何分析并绕过复杂的签名校验系统。

6.1 目标应用分析

假设目标应用具有以下保护:

  • Java层签名校验
  • Native层关键函数校验
  • classes.dex的CRC校验
  • 完整性检查线程

6.2 分步绕过实现

步骤1:识别校验点

// 监控所有可能包含校验的类 Java.enumerateLoadedClasses({ onMatch: function(className) { if (className.toLowerCase().includes("check") || className.toLowerCase().includes("verify")) { console.log(`Found potential check class: ${className}`); } }, onComplete: function() {} });

步骤2:Java层绕过

Java.use("com.secureapp.SignatureCheck").verify.implementation = function() { console.log("[+] Bypassing Java layer check"); return true; };

步骤3:Native层绕过

let symbols = Module.enumerateSymbolsSync("libsecurity.so"); symbols.forEach(sym => { if (sym.name.includes("check") || sym.name.includes("verify")) { Interceptor.attach(sym.address, { onLeave: function(retval) { retval.replace(1); } }); } });

步骤4:文件校验绕过

const fs = require("frida-fs"); let originalDex = null; Java.use("java.util.zip.ZipFile").$init.overload('java.lang.String').implementation = function(path) { if (path.includes("classes.dex")) { if (!originalDex) { originalDex = "/data/local/tmp/original.dex"; if (!fs.exists(originalDex)) { fs.copy(path, originalDex); } } return this.$init(originalDex); } return this.$init(path); };

6.3 完整性检查处理

// 检测并终止完整性检查线程 Process.enumerateThreads().forEach(thread => { if (thread.context.pc.toString().includes("integrity")) { console.log(`Killing integrity check thread: ${thread.id}`); Process.killThread(thread.id); } });

7. 高级技巧与疑难问题解决

在实际操作中会遇到各种特殊情况,需要更高级的技术手段。

7.1 多进程应用处理

对于使用多进程的应用,需要在所有进程中注入:

Process.enumerateProcesses().forEach(proc => { if (proc.name.includes("target")) { try { let session = attach(proc.pid); session.createScript(script).load(); } catch (e) { console.warn(`Attach to ${proc.name} failed: ${e}`); } } });

7.2 动态加载代码处理

针对运行时加载的DEX或SO文件:

Java.use("dalvik.system.DexClassLoader").loadClass.implementation = function(name) { let loaded = this.loadClass(name); if (name.includes("Security")) { console.log(`[!] Blocking security class loading: ${name}`); return null; } return loaded; };

7.3 性能优化技巧

长时间Hook可能导致性能问题,需要优化:

let cache = new Map(); Java.use("com.example.Checker").verify.implementation = function(input) { if (cache.has(input)) { return cache.get(input); } let result = this.verify(input); cache.set(input, result); return result; };

8. 安全防护与检测对抗

了解防护方的检测手段,才能更好地隐藏我们的Hook行为。

8.1 常见检测手段

检测类型典型实现绕过方法
Frida检测检查端口、进程名、内存特征修改默认端口、隐藏进程
签名校验PackageManager.getPackageInfoHook相关API
环境检测检查调试状态、模拟器特征修改系统属性
完整性校验classes.dex CRC检查IO重定向

8.2 高级隐藏技术

// 隐藏Frida线程 const hideThread = () => { let threads = Process.enumerateThreads(); threads.forEach(t => { if (t.context.pc.readUtf8String().includes("frida")) { console.log(`Hiding Frida thread: ${t.id}`); Thread.sleep(1); // 干扰检测 } }); setTimeout(hideThread, 5000); }; hideThread();

8.3 动态行为模拟

// 模拟正常调用模式 let originalFunc = null; Java.use("com.example.Checker").verify.implementation = function(input) { // 随机延迟模拟正常处理 let delay = Math.random() * 100; Thread.sleep(delay); // 偶尔返回失败结果 if (Math.random() > 0.9) { return false; } return originalFunc ? originalFunc.call(this, input) : true; };

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询