Frida笔记 - Android 篇 (一)

智码微光
• 阅读 3420

前言

相信不少小伙伴对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
  • 配置完成后, 使用对应函数就会有相应代码提示及函数说明

Frida笔记 - Android 篇 (一)

  • 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/...

点赞
收藏
评论区
推荐文章
frida调试不了怎么办?着急,在线等!
一、目标frida越来越流行,针对他的检测也越来越多了,什么特征串检测,TracerPid检测,双进程保护。搞的我们茶饭不思,啤酒都不香了。今天的目标是数字壳的调试,双进程保护。二、步骤侦测下火力fspawn模式启动App,提示:fridaUfcom.asiainfo.appltest.jsnopauseProcesscrashed
Frida在windows上的玩法
一、目标frida玩了很久,andriod和ios下都玩的不错。不过飞哥其实是混windows出道的,那frida能不能分析winPE呢?今天介绍下Windows下的玩法,要点如下:HookWindowsApi修改参数和返回值主动调用WindowsApi二、步骤打开心爱的MFC写个demo小程序,密码是1234,输入正确提示"密码正确"
Frida-syscall-interceptor
一、目标现在很多App不讲武德了,为了防止openat、read、kill等等底层函数被hook,干脆就直接通过syscall的方式来做系统调用,导致无法hook。应对这种情况有两种方案:刷机重写系统调用表来拦截内核调用inlineHookSWI/SVC指令我们今天采用第二种方法,用frida来实现内联汇编SWI/SVC做系统调用,sysc
IOS 某电商App签名算法解析(二) Frida RPC调用
一、目标Android下用frida来做rpc调用计算签名,我们已经玩的很熟练了。今天介绍在IOS下的玩法。要点如下:参数类型确认NSDictionaryNSArray等ObjectC对象的构造和复制ObjectC类方法和对象方法的调用附送福利,ObjectC的nil参数如何构造二、步骤参考Android下的玩法参照某段子App协议分
Frida + AndroidAsync 实现 RPC
一、目标我们在之前的教程里面使用python的Flask库启动一个webServer来实现App函数的RPC调用。今天我们介绍一个新盆友,AndroidAsync,用AndroidAsync来启动webServer,这样frida就直接搞定,不需要再请Python来帮忙了。二、步骤AndroidAsyncAndroidAsync的详细介绍大家可
IOS 联真机签名解决方案
一、目标我们之前介绍过和。那么他们搭配起来能解决什么问题呢?在Android联真机签名方案中,我们提到过Fridarpc方案的缺点:frida不是很稳定,偶尔会崩溃出退frida启动需要连PC(不过这个缺点已经被给解决了)那么在Ios下有没有类似Xposed的东东?是的,就是Tweak。二、步骤GCDWebServerGCDWebS
Frida Stalker 是什么?
一、目标在分析so中的算法时,Trace和Debug是常用的手段。了解一些调试器原理的同学都知道,Trace和Debug需要修改原始代码加上个int3,来激活调试器。这样有些App可以依赖检测关键代码来判断是否被调试。也许你会说,我们可以patch掉检测代码,上次飞哥遇到一个狠人app,B去检测A处的代码,C去检测B处的代码,D去检测C处的代码,……反正
Stella981 Stella981
3年前
AndroidStudio环境安装与配置
前言大家好,给大家带来AndroidStudio环境安装与配置的概述,希望你们喜欢AndroidStudioIDE下载我们选择用AndroidStudio开发Android的App,AndroidStudio提供给Windows、MacOS、Linux三个平台官方下载地址:Android
Stella981 Stella981
3年前
S2JH新增WIKI页面:开发基础环境配置说明,基于SSH的企业Web应用开发框架
概要说明以下以我本人实际开发环境为例,简要说明开发环境配置过程,供初学者参考。当然你也完全可以根据熟悉的开发工具和环境可自行参考调整配置。本说明仅对配置过程予以说明,其中涉及到诸如Maven,Git等工具的使用相关请自行通过其他渠道了解。提示说明:以下说明和截图以自己平时使用的Ubuntu14 X64位操作系统环境,Windo
黄忠 黄忠
1年前
(完整)全新版FRIDA与安卓 应用安全与逆向实战宝典
全新版FRIDA与安卓应用安全与逆向实战宝典download:Firda介绍Frida是一款根据PythonJavaScript的Hook与调试结构。Firda是一款易用的跨平Hook东西,Java层到Native层的Hook无所不能,是一种动态的插桩东