写点什么

Frida 笔记 - Android 篇 (一)

发布于: 3 小时前
Frida笔记 - Android 篇 (一)

前言

相信不少小伙伴对 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.xzadb rootadb push frida-server-15.0.8-android-arm64 /data/locl/tmp/adb shellchmod 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.gitnpm 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.jsfunction 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);
复制代码


使用场景

  1. 绕过证书绑定、校验, 进行埋点请求验证

  2. SDK 开发过程中, 一般客户反馈问题都需要使用客户的 app 进行问题的复现及排查, 此时通过 frida 获取运行时特定函数的参数信息及返回信息, 能有效缩短与客户的沟通时间, 该场景使用 objection 最为方便

  3. 新客户在集成前, 希望看到 SDK 能够提供的效果, 通过 frida 加载 dex 并完成初始化, 可以提前发现兼容性问题

  4. 当碰到集成早期版本 SDK 的应用反馈异常, 通过类似 Tinker 热修复的思想替换 SDK 验证是否已经在当前版本修复

  5. 开放 SDK 相关函数远程 rpc 调用, 用于测试埋点的协议等场景


外链地址

  1. Frida 官方文档: Installation

  2. Frida 作者提供的 example github 地址: GitHub - oleavr/frida-agent-example: Example Frida agent written in TypeScript

  3. JavaScript API 官方文档: JavaScript API

  4. 功能介绍中所使用 demo: GitHub - growingio/growingio-sdk-android-autotracker: GrowingIO Autotracker 具备自动采集基本的用户行为事件,比如访问和行为数据等。目前支持代码埋点、无埋点、可视化圈选、热图等功能。

  5. r0capture 安卓应用层通杀脚本 github 地址: GitHub - r0ysue/r0capture: 安卓应用层抓包通杀脚本

  6. objection github 地址: GitHub - sensepost/objection: 📱 objection - runtime mobile exploration

发布于: 3 小时前阅读数: 3
用户头像

GrowingIO 技术团队经验分享 2020.05.09 加入

GrowingIO(官网网站www.growingio.com)的官方技术专栏,内容涵盖微服务架构,前端技术,数据可视化,DevOps,大数据方面的经验分享。 公众号:GrowingIO技术团队

评论

发布
暂无评论
Frida笔记 - Android 篇 (一)