使用 frida 来做 Android 隐私 API 检测

Android, Flutter @ guangzhou

背景

应用软件合规最近一两年越来越规范和严格,对于不合规的应用,可能会有被通报甚至被下架的风险;对于应用不合规的问题点,有些问题是开发者可以知道哪里不合规,直接找到修改就好;但是还有另外一些问题,不是开发者故意为之,而是比较隐蔽难以被发现,比如第三方 SDK 里面的逻辑、非固定时机触发的逻辑等等,往往这类问题是比较容易遗漏的,从而给应用带来不可预期的合规风险;

所以需要有一种方式,可以用来检测 Android 里面比较敏感的方法是否会被调用到,常见的比如获取 Mac 地址、获取 IMEI、使用传感器、获取运行应用进程列表等;也调研过一些方案,比如 Xposed、VirtualXposed 等,各有利弊,但是对于检测定制 ROM 上的预装软件来说,这两者都会遇到一些不适用的情况;本文主要是使用了 frida 这个框架来做检测。

关于 frida 环境如何搭建等,就不在这里展开,Google 一下会有比较多的教程。

frida 检测隐私方法

frida 里面内置了好几个工具;隐私 API 调用检测使用的是 frida-trace 工具进行实现,文档可见:frida.re/docs/frida-… 隐私 API 调用检测实际上就是对方法的调用进行追踪;

以检测 getMacAddress 为例:

1、先从源码看到这个方法的所在类的路径:

image.png

可以看到是在 android.net.wifi.WifiInfo 里面,因此可以使用以下命令:

frida-trace -U -f com.xxx.xxxx -j 'android.net.wifi.WifiInfo*!*getMacAddress'
复制代码

参数解释:

  • -U: 连接到 USB 设备
  • -f: spawn 模式,会新拉起一个进程,下面第二部分会简单介绍
  • -j: JAVA 方法的意思,多个方法可以用多个 -j 拼接;如 -j 'xxx' -j 'xxxx'

还有很多参数可以看:frida-trace

2、执行完上述命令,可以看到设备上对应包名的应用被拉起了,同时可以看到有1个 function 正在被 tracing,说明上述命令写得没错,如果写错的话,会显示 0 function;

image.png

执行上述命令之前,需要先建立一个 adb 的转发

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
复制代码

接着 demo 里面调用获取 Mac 地址操作,这时候可以看到命令行输出了信息,表明了什么时间点调用了什么方法,返回了什么值;前面的毫秒是从进程被拉起到调用经过的时间;

image.png

附上获取 Mac 地址调试代码

val manager: WifiManager = getSystemService(WIFI_SERVICE) as WifiManager
val info: WifiInfo = manager.connectionInfo
val address = info.macAddress
Log.d("fridaDemo", "macAddress = $address")
复制代码

但是呢,上面这个例子比较简单,我们可以明确知道是哪里调用的;但我们实际应用的调用的时机可能不太确定,看到上面日志的时候,也只能知道有调用了获取 Mac 地址方法,但是是哪里调用了,相对来说很懵逼;所以如果能够看到调用堆栈,那岂不更好;

其实也是可以做到的,上面 frida-trace 命令执行后,会在对应 __handlers_ 目录生成 getMacAddress.js 文件(命令行窗口可以看到路径),打开 js 文件,在 onEnter 方法后面加上

onEnter(log, args, state) {
  log(`WifiInfo.getMacAddress(${args.map(JSON.stringify).join(', ')})`);

  // 加入的代码块 start
  var Log = Java.use('android.util.Log');
  var Exception = Java.use('java.lang.Exception');
  var String = Java.use('java.lang.String')
  var stack = String.valueOf(Log.getStackTraceString(Exception.$new())).replaceAll("\n", 'newLine');
  log("stacktrace: " + stack.replaceAll("/(?:\r\n|\r|\n)/g", 'newLine'));
  // 加入的代码块 end
},
复制代码

大概原理就是,trace 到这个方法的时候,就会调用到这个 js 的 onEnter 方法,这时候加入的这段代码,其实是模拟了一个异常抛出,并将异常的堆栈打印出来,这样就可以看到调用堆栈了,效果如下:

image.png

以上是 frida 非常简单的一种实践。那么 frida 的工作方式是怎样的?

frida 工作模式

frida 是一种动态插桩工具,可以插入一些代码到原生 app 的内存空间,从而达到动态地追踪和修改其行为。这里列出它的两种主要的工作模式,可以结合这个例子稍作了解;深入原理或者对源码有兴趣的可以看下:frida 源码 或者 官方文档

spawn 模式

也就是我们上面例子中的模式,这种模式下 frida 会启动一个新的进程并挂起,在启动的同时注入 frida 代码 (也就是 __handlers_ 里面生成的 js 代码,frida trace 自动生成的);这种模式比较适用于我们要检测应用启动时的一些方法调用。

attach 模式

而我们可能会有另外的使用场景,就是 trace 已经启动的应用,这时候就可以用到 attach 模式; attach 模式可以 trace 已经存在的进程,核心原理是 ptrace 修改进程内存。而 attach 模式使用也比较简单,只要对上面的命令稍加修改即可:

frida-trace -U -p $PID -j 'android.net.wifi.WifiInfo*!*getMacAddress'
复制代码

$PID 改为应用对应的进程 ID,可以通过 adb shell ps -e | grep 包名 查到

以上,抛砖引玉;frida 很强大也很深奥,有兴趣的可以深入学习。

分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改