乐趣区

关于android: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 调用, 用于测试埋点的协定等场景

    外链地址

    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/…

退出移动版