如何用Frida做Android隐私API检测!一篇全搞定

应用软件合规最近一两年越来越规范和严格,对于不合规的应用,可能会有被通报甚至被下架的风险。

对于应用不合规的问题点,有些问题是开发者可以知道哪里不合规,直接找到修改就好。

但是还有另外一些问题,不是开发者故意为之,而是比较隐蔽难以被发现,比如第三方 SDK 里面的逻辑、非固定时机触发的逻辑等等,往往这类问题是比较容易遗漏的,从而给应用带来不可预期的合规风险。

所以需要有一种方式,可以用来检测 Android 里面比较敏感的方法是否会被调用到,常见的比如获取 Mac 地址、获取 IMEI、使用传感器、获取运行应用进程列表等。

也调研过一些方案,比如说 Xposed、VirtualXposed 等,各有利弊,但是对于检测定制 ROM 上的预装软件来说,这两者都会遇到一些不适用的情况,本文主要是使用了 Frida 这个框架来做检测。

Frida 环境搭建

一、虚拟环境

Anaconda

Python 虚拟环境

二、安装依赖包

1、更换源

我这里使用的 Anaconda 。如果使用的不是 Anaconda,而是 Python 新建的虚拟环境。可以参考下面的链接。更改源。

pip 更新所有安装包

conda 更换源

# 命令行执行即可
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
conda config --set show_channel_urls yes


2、安装

# 安装这两个库,时间有点长
pip install frida frida-tools
# 查看版本
Frida --v



三、frida-server

下载 frida-server

安卓模拟器 的架构都是 x86 的,真机 一般是 ARM 架构的,所以这几个都下载吧。
(注:下载的这个版本必须和虚拟环境 Frida --v 相同)



四、启动 frida 服务

1、上传文件并启动服务

上传
frida-server-14.0.5-android-x86 到 /data/local/tmp 目录下,然后赋予 frida-server-14.0.5-android-x86 可执行权限。

这里我使用的是 雷电模拟器。

# 上传文件
adb push F:\Android\Frida\Frida\frida-server-14.0.5-android-x86 /data/local/tmp
# 赋予可执行权限
chmod 777 ./frida-server-14.0.5-android-x86
# 启动 frida 服务
./frida-server-14.0.5-android-x86




注:因为我电脑上已经有了一个 SDK,我把雷电模拟器目录下的 adb.exe 复制了一份,更改为 adbld3.exe 文件,为了是不与我原来的 SDK 在命令上的冲突。

2、端口转发

因为 frida 是在模拟器上启动的,宿主机是没办法直接访问的,需要进行端口转发,默认端口是 27042。这里按 Enter 键之后,是没有任何输出的。
adbld3 forward tcp:27042 tcp:27042



Frida 检测隐私方法

Frida 里面内置了好几个工具,隐私 API 调用检测使用的是 Frida-trace 工具进行实现,文档可见:
frida.re/docs/frida-tra

隐私 API 调用检测实际上就是对方法的调用进行追踪,

以检测 getMacAddress 为例:

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

可以看到是在 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’

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

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

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

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

附上获取 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 方法,这时候加入的这段代码,其实是模拟了一个异常抛出,并将异常的堆栈打印出来,这样就可以看到调用堆栈了,效果如下:

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

Frida 工作模式

Frida 是一种动态插桩工具,可以插入一些代码到原生 app 的内存空间,从而达到动态地追踪和修改其行为。

这里列出它的两种主要的工作模式,可以结合这个例子稍作了解。

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 很强大也很深奥,有兴趣的可以深入学习。

希望本文对你有所帮助~~如果对软件测试、接口测试、自动化测试、面试经验交流感兴趣可以私聊我或关注公众号“特斯汀软件测试”。免费领取最新软件测试大厂面试资料和Python自动化、接口、框架搭建学习资料!技术大牛解惑答疑,同行一起交流。

发布于 2022-05-25 11:57