注册

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.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);

使用场景




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




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




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




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




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




外链地址




  1. Frida官方文档: frida.re/docs/instal…




  2. Frida作者提供的example github地址: github.com/oleavr/frid…




  3. JavaScript API官方文档: frida.re/docs/javasc…




  4. 功能介绍中所使用demo: github.com/growingio/g…




  5. r0capture 安卓应用层通杀脚本 github地址: github.com/r0ysue/r0ca…




  6. objection github地址: github.com/sensepost/o…


作者:GrowingIO技术社区
链接:https://juejin.cn/post/7005889219595862023
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册