Android Hook
文章目录
Frida
万金油动态调试工具,配合自己收集定制的 hook 模板代码,稍作修改就可以快速查看 Java 层的类及其方法成员信息和 Native 层函数的参数与返回值,便于验证自己的想法,但实际上手可能还会遇到不少坑点令人苦恼:
- Java/Native 层数据结构映射到 JS 这种动态语言,可能需要 cast 或者自己转换成 JS 中的类型
- Native 层通过
findExportByName
获取函数不够准确,可能还要通过地址 - 不应发生的 cannot access address … 问题
https://www.anquanke.com/post/id/195869
https://kevinspider.github.io/fridahookjava/
https://kevinspider.github.io/fridahookso/
https://kevinspider.github.io/zhuabao/
https://eternalsakura13.com/2020/07/04/frida/
https://github.com/lasting-yang/frida_dump/
http://www.juziss.cn/2020/07/11/彻底搞定Hook不上/
function map2obj(map) {
var res = {};
var keyset = map.keySet();
var it = keyset.iterator();
while (it.hasNext()) {
var keystr = it.next().toString();
var valuestr = map.get(keystr).toString();
res[keystr] = valuestr
}
return res;
}
function body2str(reqBody) {
const Buffer = Java.classFactory.use("okio.Buffer");
const buf = Buffer.$new();
reqBody.writeTo(buf);
return buf.readUtf8();
return buf.toString(); // only get summary
}
function printable(variable, type) {
if (type.includes("Map")) {
return JSON.stringify(map2obj(variable), null, 4)
return variable.entrySet().toArray()
}
if (type.includes("RequestBody")) {
return body2str(variable)
}
return variable;
}
function dfs(self, depth) {
if (depth > 6) return {}
const obj = {}
const cls = self.getClass()
const fields = cls.getDeclaredFields()
// console.log("-".repeat(depth), "dfs", cls, self)
// console.log("-".repeat(depth), "fields:", fields)
const immediates = ['short', 'int', 'long', 'float', 'double', 'boolean', 'String']
fields.forEach(x => {
x.setAccessible(true)
const v = x.get(self)
if (v === null) return
const s = x.toString() // public type fullname
// const type = x.getType() // class java.lang.String
// const k = x.getName() // short name
// console.warn(x, v, k, type)
if (immediates.some(type => s.includes(type))) {
obj[x] = v.toString()
} else { // inner class
obj[x] = dfs(v, depth+1)
}
})
return obj
}
function hookJava() {
var cls = Java.classFactory.use("com.package.classname");
cls.methodName.implementation = function (a1, a2, a3, a4) {
console.log('>'.repeat(10), "hookJava begin")
let a2str = JSON.stringify(map2obj(a2), null, 4)
console.log(a1, a3, a4)
console.warn(a2str)
var res = this.methodName(a1, a2, a3, a4)
console.warn('res:', res)
return res
console.log("hookJava end", '<'.repeat(10))
}
}
function printStack() {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()))
}
function hookJavaFunc(clsName, funcName, argtypes, rettype, stack, func) {
const cls = Java.classFactory.use(clsName)
let funcObj = cls[funcName];
if (argtypes !== undefined) funcObj = funcObj.overload(...argtypes);
const defaultFunc = function () {
console.log('>'.repeat(10), funcName, "begin")
const argc = Array.from(arguments).length
if (argtypes == null) argtypes = Array(argc)
for (let i = 0; i < argc; i++) {
console.log(printable(arguments[i], argtypes[i]));
}
const res = funcObj.apply(this, Array.from(arguments))
console.warn('res:', printable(res, rettype))
console.log(funcName, "end", '<'.repeat(10))
if (stack) printStack()
return res
}
funcObj.implementation = func || defaultFunc;
}
function hookMap(keywords) {
const cls = Java.classFactory.use("java.util.HashMap")
cls.get.implementation = function (key) {
const res = this.get(key)
const kStr = key ? key.toString() : ''
if (keywords.some(w => kStr.includes(w))) {
console.error("hookMap get", key, " => ", res)
printStack()
}
return res
}
cls.put.implementation = function (key, value) {
const res = this.put(key, value)
const kStr = key ? key.toString() : ''
// const vStr = value ? value.toString() : ''
if (keywords.some(w => kStr.includes(w))) {
console.error("hookMap put", key, value)
printStack()
}
return res
}
}
function hookProxy() {
var cls = Java.classFactory.use("okhttp3.OkHttpClient$Builder");
cls.proxy.implementation = function (a1) {
console.log('>'.repeat(10), "hookProxy begin")
console.warn(a1)
return this
}
}
function hookNative() {
let m = Process.findModuleByName('lib.so')
let f = Module.findExportByName('lib.so', 'Functions_xx')
console.log(m.base, f)
// f = m.base.add(0xBDB8C)
Interceptor.attach(f, {
onEnter: function (args) {
console.warn("args:", args[1], args[1].readCString())
},
onLeave: function (ret) {
console.warn("ret:", ret, ret.readCString())
// this.context.r0 = 1
}
})
}
function findModules(name) {
const mods = Process.enumerateModules()
const found = name ? mods.filter(m.path.includes(name)) : mods;
found.forEach(m => console.log(JSON.stringify(m)))
console.log(found.length, "modules found")
return found
}
function main() {
if (Java.available) {
Java.perform(() => {
console.log("Performing Java hook...")
hookJava();
hookJavaFunc("okhttp3.Request$Builder", "post", ["okhttp3.RequestBody"], undefined, true);
})
}
// hookNative();
// findModules("libart");
}
setImmediate(main)
Xposed
https://wooyun.js.org/drops/Android.Hook框架xposed篇(Http流量监控).html
https://www.cnblogs.com/baiqiantao/p/10699552.html
https://www.huruwo.top/使用xposed-hook-native详解/
https://blog.bluarry.top/2020/02/28/2020-02-28-xposed模块编写之常用hook函数API/
流程如下: 先建安卓项目,Empty Activity 或 No Activity 均可
在 AndroidManifest.xml
的 <application>
标签下添加
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="Learn Xposed" />
<meta-data
android:name="xposedminversion"
android:value="89" />
在 app 的 build.gradle
中添加 dependency
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
任意新建类 implements IXposedHookLoadPackage
,创建文件 assets/xposed_init
(Android Studio 右键 app 新建 Folder -> Assets Folder 即可,实际位置在 src/app/main 下),向其中写入完整类名,即可生效。
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals(lpparam.processName)) return; // 保证每个应用只在其主进程来一次
if (!lpparam.packageName.equals("com.example.appname")) return;
}
评论正在加载中...如果评论较长时间无法加载,你可以 搜索对应的 issue 或者 新建一个 issue 。