前言
置信不少小伙伴对 Xposed、Cydia Substrate、Frida 等 hook 工具都有所理解, 并且用在了本人的工作中, 本文次要分享 Frida 的环境配置以及根本应用, 以及相干性能在日常开发调试带来的帮忙
配置 Frida 的环境
Frida 的环境装置能够参考官网文档, 或者参考网上分享的实际, 应用较为稳固的特定版本
# 通过 pip3 装置 Frida 的 CLI 工具
pip3 install frida-tools
# 装置的 frida 版本
frida --version
# 本机目前应用的 15.0.8 的 frida 版本
# 在 https://github.com/frida/frida/releases 下载对应的 server 版本 frida-server-15.0.8-android-arm64.xz
# unxz 解压缩
unxz frida-server-15.0.8-android-arm64.xz
adb root
adb push frida-server-15.0.8-android-arm64 /data/locl/tmp/
adb shell
chmod 755 /data/local/tmp/frida-server-15.0.8-android-arm64
/data/local/tmp/frida-server-15.0.8-android-arm64 &
# 打印已安装程序及包名
frida-ps -Uai
根本应用
-
Frida 的开发环境, 能够参考作者在 github 上的 exmaple
# 下载实现后通过 vscode 关上 git clone git://github.com/oleavr/frida-agent-example.git npm install
- 配置实现后, 应用对应函数就会有相应代码提醒及函数阐明
-
JavaScript API 能够参考官网文档阐明, 理解根本应用
Hook 类的构造函数
// frida -U --no-pause -f com.gio.test.three -l agent/constructor.js function main() {Java.perform(function () { Java.use("com.growingio.android.sdk.autotrack.AutotrackConfiguration").$init.overload("java.lang.String", "java.lang.String").implementation = function (projectId, urlScheme) { // 调用原函数 var result = this.$init(projectId, urlScheme); // 打印参数 console.log("projectId, urlScheme:", projectId, urlScheme); return result; }; }); } setImmediate(main);
Hook 类的一般函数
// frida -U --no-pause -f com.gio.test.three -l agent/function.js function main() {Java.perform(function () { Java.use("com.growingio.android.sdk.CoreConfiguration").setDebugEnabled.implementation = function (enabled) {console.log("enabled:", enabled); // 间接返回原函数执行后果 return this.setDebugEnabled(enabled); }; }); } setImmediate(main);
批改类 / 实例参数
// 以 attach 的形式附加到过程, 或者应用 setTimeout 替换 setImmediate // frida -U -n demos -l agent/instance.js function main() {Java.perform(function () { Java.choose("com.growingio.android.sdk.autotrack.AutotrackConfiguration", {onMatch: function (instance) {console.log("instance.mProjectId", instance.mProjectId.value); console.log("instance.mUrlScheme", instance.mUrlScheme.value); // 批改变量时通过赋值, 如果变量与函数同名, 须要在变量前加 '_', 如: _mProjectId instance.mProjectId.value = "t-bfc5d6a3693a110d"; instance.mUrlScheme.value = "t-growing.d80871b41ef40518"; }, onComplete: function () {}, }); }); } setImmediate(main);
结构数组
frida -U -n demos -l agent/array.js function main() {Java.perform(function () { // 结构 byte 数组 var byteArray = Java.array("byte", [0x46, 0x72, 0x69, 0x64, 0x61]); // 输入为: Frida console.log(Java.use("java.lang.String").$new(byteArray)); // 结构 char 数组 var charArray = Java.array("char", ["F", "r", "i", "d", "a"]); console.log(Java.use("java.lang.String").$new(charArray)); }); } setImmediate(main);
动态函数被动调用
// 以 attach 的形式附加到过程, 或者应用 setTimeout 替换 setImmediate // frida -U -n demos -l agent/staticFunction.js function main() {Java.perform(function () { // setWebContentsDebuggingEnabled 须要在主线程调用 Java.scheduleOnMainThread(function () {console.log("isMainThread", Java.isMainThread()); // 被动触发动态函数调用, 容许 WebView 调试 Java.use("android.webkit.WebView").setWebContentsDebuggingEnabled(true); }); }); } setImmediate(main);
动静函数被动调用
// 以 attach 的形式附加到过程, 或者应用 setTimeout 替换 setImmediate // frida -U -n demos -l agent/dynamicFunction.js function main() {Java.perform(function () { Java.choose("com.growingio.android.sdk.autotrack.AutotrackConfiguration", {onMatch: function (instance) { // 被动触发动静函数调用 console.log("instance.isDebugEnabled:", instance.isDebugEnabled()); console.log("instance.getChannel:", instance.getChannel()); }, onComplete: function () {}, }); }); } setImmediate(main);
定义一个类
// 以 attach 的形式附加到过程, 或者应用 setTimeout 替换 setImmediate // frida -U -n demos -l agent/registerClass.js function main() {Java.perform(function () { var TestRunnable = Java.registerClass({ name: "com.example.TestRunnable", // 实现接口 implements: [Java.use("java.lang.Runnable")], // 成员变量 fields: {testFields: "java.lang.String",}, methods: { // 构造函数 $init: [ { returnType: "void", argumentTypes: ["java.lang.String"], implementation: function (testFields) { // 调用父类构造函数 this.$super.$init(); // 给成员变量赋值 this.testFields.value = testFields; console.log("$init:", this.testFields.value); }, }, ], // 办法 run: [ { returnType: "void", implementation: function () { console.log( "testFields:", this.testFields.value ); }, }, ], }, }); TestRunnable.$new("simple test").run();}); } setImmediate(main);
打印函数调用堆栈
// 以 attach 的形式附加到过程, 或者应用 setTimeout 替换 setImmediate // frida -U -n demos -l agent/printStackTrace.js function main() {Java.perform(function () { Java.use("com.growingio.android.sdk.autotrack.click.ViewClickInjector").viewOnClick.overload( "android.view.View$OnClickListener", "android.view.View" ).implementation = function (listener, view) { // 打印以后调用堆栈信息 console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()) ); return this.viewOnClick(listener, view); }; }); } setImmediate(main);
枚举 classLoader
// 以 attach 的形式附加到过程, 或者应用 setTimeout 替换 setImmediate // frida -U -n demos -l agent/enumerateClassLoaders.js // 实用于加固的利用, 找到对应的 classloader // 通常间接在 application.attach.overload('android.content.Context').implementation 获取 context 对应的 classloader function main() {Java.perform(function () { Java.enumerateClassLoaders({onMatch: function (loader) { try { // 判断该 loader 中是否存在咱们须要 hook 的类 if (loader.findClass("com.growingio.android.sdk.CoreConfiguration")) {console.log("found loader:", loader); Java.classFactory.loader = loader; } } catch (error) {console.log("found error:", error); console.log("failed loader:", loader); } }, onComplete: function () {console.log("enum completed!"); }, }); console.log(Java.use("com.growingio.android.sdk.CoreConfiguration").$className ); }); } setImmediate(main);
枚举类
// 以 attach 的形式附加到过程, 或者应用 setTimeout 替换 setImmediate // frida -U -n demos -l agent/enumerateLoadedClasses.js function main() {Java.perform(function () { Java.enumerateLoadedClasses({onMatch: function (name, handle) { // 判断是否是咱们要查找的类 if (name.toString() == "com.growingio.android.sdk.CoreConfiguration") {console.log("name, handle", name, handle); Java.use(name).isDebugEnabled.implementation = function () {return true;}; } }, onComplete: function () {}, }); }); } setImmediate(main);
加载内部 dex 并通过 gson 打印对象
// 以 attach 的形式附加到过程, 或者应用 setTimeout 替换 setImmediate // frida -U -n demos -l agent/printObject.js // 通过 d8 将 gson.jar 转为 classes.dex // ~/Library/Android/sdk/build-tools/30.0.3/d8 --lib ~/Library/Android/sdk/platforms/android-30/android.jar gson-2.8.8.jar // 如果 SDK 中曾经有了, 能够间接应用 Java.use 加载 // adb push classes.dex /data/local/tmp function main() {Java.perform(function () { Java.choose("com.growingio.android.sdk.autotrack.AutotrackConfiguration", {onMatch: function (instance) { // 加载内部 dex Java.openClassFile("/data/local/tmp/classes.dex").load(); var Gson = Java.use("com.google.gson.Gson"); // JSON.stringify: "<instance: com.growingio.android.sdk.autotrack.AutotrackConfiguration>" console.log("JSON.stringify:", JSON.stringify(instance)); // Gson.$new().toJson: {"mImpressionScale":0.0,"mCellularDataLimit":10,"mDataCollectionEnabled":true,"mDataCollectionServerHost":"http://api.growingio.com","mDataUploadInterval":15,"mDebugEnabled":true,"mOaidEnabled":false,"mProjectId":"bfc5d6a3693a110d","mSessionInterval":30,"mUploadExceptionEnabled":false,"mUrlScheme":"growing.d80871b41ef40518"} console.log("Gson.$new().toJson:", Gson.$new().toJson(instance)); }, onComplete: function () {}, }); }); } setImmediate(main);
应用场景
- 绕过证书绑定、校验, 进行埋点申请验证
- SDK 开发过程中, 个别客户反馈问题都须要应用客户的 app 进行问题的复现及排查, 此时通过 frida 获取运行时特定函数的参数信息及返回信息, 能无效缩短与客户的沟通工夫, 该场景应用 objection 最为不便
- 新客户在集成前, 心愿看到 SDK 可能提供的成果, 通过 frida 加载 dex 并实现初始化, 能够提前发现兼容性问题
- 当碰到集成晚期版本 SDK 的利用反馈异样, 通过相似 Tinker 热修复的思维替换 SDK 验证是否曾经在以后版本修复
-
凋谢 SDK 相干函数近程 rpc 调用, 用于测试埋点的协定等场景
外链地址
Frida 官网文档: https://frida.re/docs/install…
Frida 作者提供的 example github 地址: https://github.com/oleavr/fri…
JavaScript API 官网文档: https://frida.re/docs/javascr…
性能介绍中所应用 demo: https://github.com/growingio/…
r0capture 安卓应用层通杀脚本 github 地址: https://github.com/r0ysue/r0c…
objection github 地址: https://github.com/sensepost/…