Frida apk hook

Frida apk hook

四月 15, 2021

Frida简介

frida是一款基于python + javascript 的hook框架,可运行在android、ios、linux、windows、macos等各平台,主要使用动态二进制插桩技术。frida分为两部分,服务端运行在目标机上,通过注入进程的方式来实现劫持应用函数,另一部分运行在系统机器上。frida上层接口支持js、python、c等。

Frida官方github地址

Frida安装

  1. 安装python3

  2. pip安装frida模块

    1
    pip install frida
  3. pip安装frida-tools模块

    1
    pip install frida-tools
  4. 下载运行在目标机器上的服务端:官方地址,要根据目标机器cpu架构选择。可以在/proc/cpuinfo中查看cpu信息。

  5. adb连接手机

  6. 在手机上运行该服务端程序并进行端口映射

    1
    2
    3
    4
    5
    6
    adb push frida-server-14.2.14-android-x86 /data/local/tmp
    adb forward tcp:27042 tcp:27042
    adb shell
    cd /data/local/tmp
    chmod +x frida-server-14.2.14-android-x86
    ./frida-server-14.2.14-android-x86

    本机执行命令frida-ps -U进行测试,若成功打印手机端的进程则表示安装成功。

Frida hook Java层

以攻防世界的Ph0en1x-100为例,jeb查看Java代码找到关键判断:

用getSecret()分别处理getFlag()和encrypt(sInput)后将两个值比较,sInput是输入,只需要让getFlag()和encrypt(sInput)的值相同即可,ida查看so文件发现encrypt()函数只是将输入字符串的每一位ascii码-1,而getFlag()函数没有输入,故返回值不变,可以通过hook得到返回值。

尝试修改getSecret()的返回值为”qaq”,脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import frida,sys #导入模块

jscode="""
Java.perform(function(){
var MainActivity=Java.use('com.ph0en1x.android_crackme.MainActivity'); //获取MainActivity类
MainActivity.getSecret.implementation=function(){ //重写MainActivity.get_Secret()函数
send("hook ret qaq"); //发送信息,回调python函数
return "qaq"; //返回字符串"qaq"
}
});
"""

def on_message(message,data): #js中执行send函数后要回调的函数
print(message)

process=frida.get_remote_device().attach('com.ph0en1x.android_crackme') #找到设备并劫持进程
script=process.create_script(jscode) #创建js脚本
script.on('message',on_message) #加载回调函数
script.load() #加载脚本
sys.stdin.read() #不让python代码自动结束

先在手机端运行app,之后在本地执行python脚本,之后在手机端输入框随便输入:

执行了两次getSecret()函数,发送了两次消息回调了两次on_message(),而两次getSecret()的返回值都被修改成了”qaq”,所以无论输入什么值都可以通过判断。

Frida hook Native层

还说用这个程序,我们尝试hook Native层的getFlag()函数打印出返回值。需要说明一下关于so文件当中的函数,分为导出函数和未导出函数两种,导出函数打开IDA后能够在导出表中找到的函数就是导出函数,未导出函数则在导出表中寻找不到,一般来说静态编写的native函数都能在导出表中寻找到,而动态加载的则无法在导出表中发现。

python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import frida,sys

jscode="""
Java.perform(function(){
Interceptor.attach(Module.findExportByName("libphcm.so","Java_com_ph0en1x_android_1crackme_MainActivity_getFlag"),{ //找到libphcm.so中的getFlag()函数,函数名在ida中找
onEnter:function(args){ //进入该函数前执行的函数,其中args是传入的参数,一般so层函数第一个参数都是JniEnv,第二个参数是jclass,从第三个参数开始才是我们java层传入的参数
},
onLeave:function(retval){ //退出该函数时执行的代码,retval是返回值,在C语言中返回的是一个`char*`,需要转化成java的string
var String_java = Java.use('java.lang.String');
var args_4 = Java.cast(retval, String_java);
send("getFlag()==>"+args_4);
}
});
});
"""

def printMessage(message,data):
if message['type'] == 'send':
print('[*] {0}'.format(message['payload']))
else:
print(message)

process=frida.get_remote_device().attach('com.ph0en1x.android_crackme')
script=process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()

先在手机端运行app,之后在本地执行python脚本,之后在手机端输入框随便输入:

可以看到js代码把字符串的值发回给python,之后python打印出来了。