保姆级教程:在Ubuntu上配置Frida环境,搞定Android App的IO重定向与签名绕过
在Ubuntu上构建Android逆向工程环境:Frida实战与IO重定向技术解析
对于习惯Linux环境的安全研究人员而言,Windows-centric的逆向工具链往往带来诸多不便。本文将系统性地介绍如何在Ubuntu上搭建完整的Android逆向环境,并深入探讨如何利用Frida实现高级Hook技巧,特别是针对getPackageCodePath()等关键方法的拦截与重定向。
1. 环境准备与工具链配置
1.1 基础依赖安装
在Ubuntu上开展Android逆向工作,首先需要确保以下基础组件就位:
sudo apt update && sudo apt install -y \ android-tools-adb \ python3-pip \ git \ openjdk-11-jdk \ wget \ unzip关键组件说明:
android-tools-adb:Android Debug Bridge工具套件openjdk-11-jdk:Java开发环境(建议使用OpenJDK 11以兼容多数逆向工具)python3-pip:Python包管理工具(Frida依赖)
1.2 ADB配置与设备连接
配置USB调试权限后,通过以下命令验证设备连接:
adb devices # 预期输出示例 List of devices attached emulator-5554 device若遇到设备未授权问题,可尝试:
adb kill-server && adb start-server1.3 Frida生态部署
Frida环境需要客户端与服务端组件协同工作:
安装Python客户端:
pip3 install frida-tools --user部署Android端Server:
- 查询设备CPU架构:
adb shell getprop ro.product.cpu.abi - 下载对应版本的Frida-server:
wget https://github.com/frida/frida/releases/download/16.0.8/frida-server-16.0.8-android-arm64.xz unxz frida-server-*.xz - 推送到设备并启动:
adb push frida-server-* /data/local/tmp/frida-server adb shell "chmod 755 /data/local/tmp/frida-server" adb shell "/data/local/tmp/frida-server &"
验证安装:
frida-ps -U2. Android签名校验机制深度解析
2.1 常见校验类型与技术原理
| 校验类型 | 实现方式 | 典型特征 |
|---|---|---|
| 基础签名校验 | PackageInfo.signatures | 比对APK签名证书哈希值 |
| V2+签名校验 | SigningInfo.getApkContentsSigners | Android 9+新增API |
| CRC校验 | ZipEntry.getCrc() | 校验DEX文件完整性 |
| SO文件校验 | System.loadLibrary | Native层实现的复杂校验逻辑 |
2.2 典型校验代码反编译示例
// 新API签名校验实现 public final boolean useNewAPICheck() { String str; Signature[] signatureArr; try { if (Build.VERSION.SDK_INT >= 28) { signatureArr = getPackageManager() .getPackageInfo(getPackageName(), 134217728) .signingInfo.getApkContentsSigners(); } else { signatureArr = getPackageManager() .getPackageInfo(getPackageName(), 64) .signatures; } str = MD5Utils.INSTANCE.MD5( Base64Utils.INSTANCE.encodeToString( signatureArr[0].toByteArray() ) ); } catch (Exception e) { str = ""; } return str.equals("074f64af5821ae6aa1ac1779ad5687ad"); }3. Frida高级Hook技术实战
3.1 基础Hook模式对比
- 返回值覆写:直接修改函数返回值为预期值
- 参数篡改:拦截并修改传入参数
- 流程劫持:完全接管函数执行逻辑
- IO重定向:修改文件路径相关操作
3.2 针对getPackageCodePath的Hook实现
创建redirect.js脚本:
Java.perform(function () { const ContextWrapper = Java.use("android.content.ContextWrapper"); ContextWrapper.getPackageCodePath.implementation = function () { const originalPath = this.getPackageCodePath(); console.log(`Original path: ${originalPath}`); // 重定向到备用APK路径 const newPath = "/data/local/tmp/original.apk"; console.log(`Redirecting to: ${newPath}`); return newPath; }; });执行脚本:
frida -U -l redirect.js -f com.target.app --no-pause3.3 复合Hook策略应对多重校验
对于同时存在签名校验和CRC校验的场景,可采用分层拦截策略:
Java.perform(function () { // 第一层:签名校验绕过 Java.use("com.target.app.SignatureChecker").verify.overload().implementation = function() { return true; }; // 第二层:CRC校验重定向 Java.use("android.app.ApplicationPackageManager").getPackageInfo.overload('java.lang.String', 'int').implementation = function(pkg, flags) { const info = this.getPackageInfo(pkg, flags); if (pkg === "com.target.app") { info.signatures[0] = ... // 伪造签名数据 } return info; }; });4. 实战案例:完整绕过流程演示
4.1 目标APK分析准备
- 提取目标APK:
adb shell pm path com.target.app adb pull /data/app/~~.../base.apk - 反编译分析:
apktool d base.apk -o decompiled
4.2 多维度校验绕过方案
方案对比表:
| 方法类型 | 实施难度 | 通用性 | 隐蔽性 | 适用场景 |
|---|---|---|---|---|
| 返回值强制修改 | ★★☆ | 高 | 低 | 简单校验 |
| 签名数据伪造 | ★★★☆ | 中 | 中 | V1/V2签名校验 |
| IO重定向 | ★★★★ | 高 | 高 | 文件完整性校验 |
| Native层Hook | ★★★★☆ | 低 | 高 | SO文件校验 |
4.3 自动化Hook脚本开发
创建auto_hook.py控制脚本:
import frida import sys def on_message(message, data): if message['type'] == 'send': print(f"[*] {message['payload']}") else: print(message) device = frida.get_usb_device() pid = device.spawn(["com.target.app"]) session = device.attach(pid) with open("hook_script.js") as f: script = session.create_script(f.read()) script.on("message", on_message) script.load() device.resume(pid) sys.stdin.read()配套的hook_script.js应包含针对目标应用的特化Hook逻辑。
5. 高级技巧与疑难排解
5.1 常见问题解决方案
Frida-server无法保持运行:
adb shell "nohup /data/local/tmp/frida-server &"Hook失效的可能原因:
- 目标方法已被混淆
- 存在反调试检测
- 架构不匹配(如误将x86脚本用于arm设备)
动态加载代码的Hook策略:
Java.enumerateClassLoaders({ onMatch: function(loader) { try { Java.classFactory.loader = loader; const DynamicClass = Java.use("dynamic.ClassName"); // Hook逻辑... } catch(e) {} }, onComplete: function() {} });
5.2 性能优化建议
- 避免在Hook回调中执行耗时操作
- 使用
NativeFunction替代JavaScript实现性能关键代码 - 合理设置
setImmediate处理异步任务
在长期逆向工程实践中,发现IO重定向方案对Android 10+系统的兼容性需要特别注意。某些厂商ROM会对/data/local/tmp路径的访问施加额外限制,此时可尝试使用应用私有目录(如/data/data/pkg.name/files)作为重定向目标。
