Android应用学习(二)——Frida入门系列篇I

APP安全 2019-02-15

说起来本篇才是Frida入门的第一篇教程,本篇会尽量从基础开始介绍Frida的用途,安装配置,使用Frida运行第一个脚本等知识点。最近由于项目需要,之前一直使用xposed框架,每次修改完需要重启手机这个确实降低了效率,而长期以来也听闻Frida是一款相当优秀的hook框架,这里系统学习一下,也是希望给自己留个备忘。

作者:古月蓝旻

何为Frida

简介部分我这边直接引用了前人的介绍,会在最后参考链接处注明来源:

Frida是一款基于python + javascript 的hook框架,通杀androidioslinuxwinosx等各平台,由于是基于脚本的交互,因此相比xposed和substrace cydia更加便捷,Frida使用的主要是动态二进制插桩技术[Dynamic Binary Instrumentation(DBI)],下面也科普一下什么是插桩技术以及DBI可以干什么

插桩技术

插桩技术是指将额外的代码注入程序中以收集运行时的信息,可分为两种:

(1)源代码插桩[Source Code Instrumentation(SCI)]:额外代码注入到程序源代码中。

(2)二进制插桩(Binary Instrumentation):额外代码注入到二进制可执行文件中。

●静态二进制插桩[Static Binary Instrumentation(SBI)]:在程序执行前插入额外的代码和数据,生成一个永久改变的可执行文件。

●动态二进制插桩[Dynamic Binary Instrumentation(DBI)]:在程序运行时实时地插入额外代码和数据,对可执行文件没有任何永久改变。

你能用DBI做些什么呢

(1)访问进程的内存

(2)在应用程序运行时覆盖一些功能

(3)从导入的类中调用函数

(4)在堆上查找对象实例并使用这些对象实例

(5)Hook,跟踪和拦截函数等等

从上面的介绍其实可以看到,DBI这种技术实际上能够很大程度上提高安全研究人员在反编译应用程序时的效率,同时配合frida的--no-pause参数和脚本中的setImmediate()方法,能够帮助我们实时测试脚本的注入情况,后面也会介绍。

Frida主要提供了功能简单的python接口和功能丰富的js接口,使得hook函数和修改so编程化,值得一提的是接口中包含了主控端与目标进程的交互接口,由此我们可以即时获取信息并随时进行修改。使用frida可以获取进程的信息(模块列表,线程列表,库导出函数),可以拦截指定函数和调用指定函数,可以注入代码,总而言之,使用frida我们可以对进程模块进行手术刀式剖析。

它主要的工作方式是将脚本库注入到目标进程,在目标进程执行脚本。这里需要注意的是,它是将脚本库注入到已经启动的进程,但并不是说,对于进程初始化所做的动作,frida无能为力。

Frida的优势相对而言还是非常明显的,比起Xposed框架每修改一次模块就要重启一次手机,Frida能实时注入并修改这一点对效率的提升确实不是一星半点。

好了,下面介绍一下如何安装Frida吧~

Frida安装

在Frida安装之前我们需要准备什么呢?

首先确认一下本次学习的目标,Frida号称全平台通杀,而本次我们主要使用它来破解Android应用,主要是apk,所以前提是有能够运行apk的环境

安卓设备可以使用模拟器也可以使用实体安卓手机,这里推荐使用实体手机,我用的是Nexus 5X,某宝9.9新包邮也就300+,可以让卖家直接root或者刷入指定低版本android系统,省心省力,我们本次的Frida的server端就会安装在该手机上,同时由于Frida的server端在安卓设备上运行需要相关执行权限,为了注入进程也需要高权限,所以安卓设备需要root

操作系统而Frida的工具包我这边选择使用虚拟机中的CentOS6.5系统上,主要是Frida使用命令行方式进行交互,Windows的CMD实在是...所以还是转投Linux大家族。既然是让CentOS和安卓设备交互,那么相关AndroidSDK和Java环境还是需要准备一下

Java环境的配置就不多说了,网上很多资料,我这边安装的是1.8的版本

AndroidSDK环境的配置在CentOS下也很简单,一条命令足矣

yum install adb

使用adb version查看版本信息,如果安装正确则效果如下

Python3/pip3环境由于Frida是基于Python编写的,我们可以编写js或者py脚本注入进程,所以Python环境也是必须的,这里推荐安装python3,Frida官网的样例也是基于python3的,同时pip3也很有必要,毕竟安装Frida的时候可以直接使用pip install的方式进行安装,我这边是直接在CentOS上安装了python3和pip3

Frida 工具包安装Frida的工具包(客户端)我们安装在CentOS6.5上,根据官网https://www.frida.re/描述,直接使用一条命令即可

pip3 install frida-tools

此时frida会被安装到python所在目录下,可以自行修改环境变量或者新建软连接的方式运行frida相关命令

注:为了截图,我在其它主机上又安装了一次Frida,和我下面介绍的版本号略有差异,请忽略

如果在安装frida-tools的途中发现脚本迟迟没有动静,可通过安装setuptools工具,前往https://pypi.org/project/frida/寻找对应版本的egg文件,使用easy_install手动安装

安装后的效果如下,Frida相关工具集一共6个

Frida Server端安装Frida的server端我们安装在安卓设备上,首先将安卓设备连接到我们的CentOS主机,然后执行以下命令查看cpu架构

adb shell getprop ro.product.cpu.abi

比如我的Nexus就是arm架构的,然后前往以下网站寻找对应的版本

https://github.com/frida/frida/releases

由于我们的客户端Frida是12.2.6,所以我们的服务端也要去下载对应版本,且是arm架构,因此我这边需要安装的Frida server是frida-server-12.2.6-android-arm.xz

将该xz压缩包解压以后,其中的文件为frida-server-12.2.6-android-arm,是一个32位elf可执行程序

将该文件先放在CentOS上,然后使用以下命令推送到安卓设备中

adb push frida-server-12.2.6-android-arm  /data/local/tmp

接着进入安卓设备的shell,提升为root,对该文件添加可执行权限,并后台运行该server

adb shell
su
cd /data/local/tmp
chmod +x frida-server-12.2.6-android-arm
./frida-server-12.2.6-android-arm &

此时服务端已在安卓设备运行

在CentOS主机(客户端)再开启一个shell,运行以下命令,可以看到当前USB设备上运行的进程情况

frida-ps -U

亦可转发android TCP端口到本地:

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

此时使用以下命令可看远程服务端(安卓设备)运行进程情况

frida-ps -R

此时可认为基础环境已经搭建成功,这里小结一下相关基础环境信息

客户端:
    主机环境:Vmware
    操作系统:CentOS 6.5
    Java环境: jre1.8.0_60
    AndroidSDK环境:1.0.31
    Python环境:python 3.6.5
    pip环境:10.0.1
    Frida版本:12.2.6

服务端:
    主机信息:Nexus 5X
    操作系统:Android 4.4.4
    CPU架构:armeabi-v7a
    Frida版本:frida-server-12.2.6-android-arm

至此环境篇就介绍完毕了

Frida牛刀小试

在一开始的介绍里面,我们提到了一点:Frida是跨平台的hook框架。何以见得呢,除了Android平台外,我们先演示一下官网给出的示例尝试在Linux系统上hook相关进程

Frida hook cat进程

本例在安装了Frida的CentOS主机测试,不涉及Android

首先,我们将系统的cat命令所对应文件/bin/cat复制到/tmp目录下

cp /bin/cat  /tmp/cat

我们使用该/tmp/cat同样是可以读取文件的

然后我们开始写一段Frida的脚本example.py用于hook cat进程

import frida

def on_message(message, data):
    print("[on_message] message:", message, "data:", data)

session = frida.attach("cat")

script = session.create_script("""'use strict';

rpc.exports.enumerateModules = function () {
  return Process.enumerateModulesSync();
};"""
)
script.on("message", on_message)
script.load()
print([m["name"] for m in script.exports.enumerate_modules()])

然后新开一个窗口运行

/tmp/cat

在原先的窗口运行

python3 example.py

结果如下:

此例就是使用Frida在进程中注入py和js代码的实例,如果暂时不理解也没有关系。不过这和我想要的结果不太一样,我想看的是cat进程加载的系统so文件,为啥这里是frida的?很简单,我们的CentOS系统只安装的frida客户端,没有安装服务端,所以无法对系统本身进行注入,有兴趣的可以自行安装一个Frida的服务器来hook Linux系统的进程

下面给出一个比较简单的py实例(列出当前安卓设备上的全部进程)

import frida
import sys
rdev = frida.get_remote_device()
processes = rdev.enumerate_processes()
for processe in processes:
    print (processe)

将以上代码保存为bbb.py,通过调用frida和sys库中函数,将远程设备中的所有进程进行枚举

注意前提要在CentOS上将android的端口转发出来,否则frida 无法连接远程设备

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

之后执行该py,效果拔群

python3 bbb.py

frida hook android进程

好了,我们还是回到一开始的思路,如何利用Frida对安卓设备上的进程进行hook操作呢?

这里我们参考一篇国外的教程,很适合初学的小伙伴HACKING ANDROID APPS WITH FRIDA I

根据教程,我们首先执行一下

frida-trace -i "open" -U com.android.chrome 

这个需要我们在安卓设备上运行chrome程序,我们可以尝试在其中打开一个网页

然后从frida的输出可以看出,此处将/root/__handlers__/libc.so/open.js注入到了该进程中

输出的内容则类似

open(pathname=0x7e724ce0, flags=0x0)

这是为什么呢,我们打开这个js文件看一下

输出的内容由以下语句控制

log("open(" + "pathname=" + args[0] + ", flags=" + args[1] + ")");

可以看出这里输出的是chrome进程内部函数的参数值,我们也可将它修改成下面这样,使得进程中内部函数的参数在内存中所对应的地址以UTF-8字符的形式打印出来

 log("open(" + "pathname=" + Memory.readUtf8String(args[0])+ ", flags=" + args[1] + ")");

效果就是这样

这里使用frida-trace配合-U参数,hook的是已经运行的进程

我们也可以使用frida配合-f参数,自己启动一个应用进程

我们使用frida配合-l参数,注入我们自定义的脚本

frida最大的特点使用DBI技术实现了动态调试,我们只需要在frida命令后添加--no-pause参数或在js代码中添加setImmediate可以实现无需停止frida的情况下,修改脚本且frida实时使用最新的脚本

脚本如何编写

我们可以编写js或者py脚本注入到进程中,那么脚本该如何编写呢?

其实frida官网的doc里有很多相关的api可以提供参考

https://www.frida.re/docs/home/

比如教程中的两段示例代码

Java.enumerateLoadedClasses(//枚举已经加载的所有类
  {
  "onMatch": function(className){ 
        console.log(className) //输出类名
    },
  "onComplete":function(){}
  }
)

我们将上述代码保存为bb.js,注入到chrome的进程中

frida -U -l bb.js com.android.chrome

输出量挺大,这里只截取部分

也有这样的,该脚本的作用在于当系统中的应用发生切换时,输出一句[*] onResume() got called!

Java.perform(function () {
    var Activity = Java.use("android.app.Activity"); //使用android.app.Activity类
    Activity.onResume.implementation = function () {//重写该类中的onResume方法
        console.log("[*] onResume() got called!");
        this.onResume();
    };
});

格式大致是固定的

Java.perform(fn):相当于frida的main,脚本必须要放在其中

Java.use("xx"); :用于找源码中的指定xx类

xx.yy.implementation: 用于重新实现xx类中的yy方法

this.yy():用于继续运行源码中的yy方法

...当然还有很多,完整API见官方文档

Frida JavaScript API

frida常用api翻译之java篇

当然,由于frida是基于python编写,我们同样可以将上面的js代码放入py中

#!/usr/bin/python3
import frida

# put your javascript-code here
jscode= """
console.log("[*] Starting script");

Java.enumerateLoadedClasses(
  {
  "onMatch": function(className){ 
        console.log(className) 
    },
  "onComplete":function(){}
  }
)

"""

# startup frida and attach to com.android.chrome process on a usb device
session = frida.get_usb_device().attach("com.android.chrome")

# create a script for frida of jsccode
script = session.create_script(jscode)

# and load the script
script.load()

我们创建一个cc.py,内容是枚举chrome进程加载的全部类

效果类似

到这里Frida的基础教学就结束了,其实就是搭了一个架子,还是需要多看API,多实际动手操作。受限于时间和精力,这部分讲解有限,可能有地方也不是很全,可以多看参考链接里一些其它的入门教程。

参考链接

Frida从入门到入门—安卓逆向菜鸟的frida食用说明

详解Hook框架frida

Frida官方手册 - JavaScript API(篇一)

Android hook神器frida(一)

Frida知识小记

Android逆向之hook框架frida篇


本文由 古月蓝旻 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论