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(filterStr) {
  const cls = Java.classFactory.use("java.util.HashMap");
  cls.get.implementation = function (key) {
    const res = this.get(key)
    const keyStr = key ? key.toString() : ''
    if (keyStr.includes(filterStr)) {
      console.error("hookMap get", key)
      printStack()
    }
    return res
  }
  cls.put.implementation = function (key, value) {
    const res = this.put(key, value)
    const keyStr = key ? key.toString() : ''
    if (keyStr.includes(filterStr)) {
      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

Xposed真的可以为所欲为——终 · 庖丁解码

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