为什么要用Frida
Frida是逆向的一个基本工具, 具体原理不多介绍, 参看文章 www.jianshu.com/p/018498c65…
我们利用一些hook工具, 比如说Xposed, 来调用or查看其它app的一些实现, 但是比较烦人的是, Xposed每修改安装一次, 手机就需要重启一次, 这显然是不行的. Frida的优势就在于不用重启, 就可以完成调用
配置
配置指导链接 www.jianshu.com/p/018498c65… 配置前请使用python3.7, 并将npm和node升级到最新版本, 否则安装的时候会出错
pip install frida
pip install frida-tools
npm install frida
然后到 github.com/frida/frida… 中下载对应平台的服务端, 解压
adb push frida-server-12.5.7-android-x86 /data/local/tmp/
adb shell
cd /data/local/tmp/
chmod 777 frida-server-12.5.7-android-x86
./frida-server-12.5.7-android-x86
同时要在电脑上完成adb命令的转发端口
adb forward tcp:27042 tcp:27042
hook示例
一个程序如下:
#!/usr/bin/env python
# encoding: utf-8
import frida
import sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
if __name__ == '__main__':
print("this is main")
jscode = """
if(Java.available){
Java.perform(function(){
var homeProfileFragmentClazz = Java.use("com.plbear.hookdemo.Utils");
var mainActivity = homeProfileFragmentClazz.$new();
var result = mainActivity.cal(3,3);
mainActivity.$dispose();
send(result)
});
}
"""
# 查找USB设备并附加到目标进程
session = frida.get_usb_device().attach('com.plbear.hookdemo')
# 在目标进程里创建脚本
script = session.create_script(jscode)
# 注册消息回调
script.on('message', on_message)
print('[*] Start attach')
# 加载创建好的javascript脚本
script.load()
# 读取系统输入
sys.stdin.read()
主要改变的是, js代码部分. 这里js中会经常用的接口如下
- Java.use(className) 获取类的JavaScript wrapper(即包装Java类)
- $new 调用构造器
- $dispose() 清除实例
- Java.choose(className,classBack) 枚举Java堆中存活的实例
- $init 实例的构造方法 具体其他API 请参考完整的API文档 frida.re/docs/javasc…
下面给出部分示例, 来演示下不同的调用
调用Activity中的方法(私有方法也可以调用到, 方法一样)
Android App实现
package com.plbear.hookdemo
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun cal(a: Int, b: Int): Int {
return a + b
}
override fun onResume() {
super.onResume()
val v = findViewById<TextView>(R.id.tv)
v.text = cal(1, 2).toString()
}
}
主动调用Activity的cal方法
if(Java.available){
Java.perform(function(){
var homeProfileFragmentClazz = Java.use("com.plbear.hookdemo.MainActivity");
var looperClazz = Java.use("android.os.Looper");
looperClazz.prepare(); // 这里注意, 需要掉调用下这个, 否则没有办法创建一个新的Activity示例
var mainActivity = homeProfileFragmentClazz.$new();
var result = mainActivity.cal(3,3);
send(result)
});
}
hook cal方法, 每次系统调用cal方法, 就可以看到结果
if(Java.available){
Java.perform(function(){
var homeProfileFragment = Java.use("com.plbear.hookdemo.MainActivity");
homeProfileFragment.cal.overload("int","int").implementation=function(a,b){
console.log("[javascript] isExcellent be called.");
send("isExcellent be called.");
send(a);
send(b);
return this.cal(75,1); // 修改系统调用的结果, 当然这个地方可以用a,b, 只是打印下日志
}
});
}
静态方法的调用
静态方法其实差不多 app 代码
package com.plbear.hookdemo;
public class UtilsV2 {
public static int cal(int i, int j) {
return i + j;
}
}
hook代码
if(Java.available){
Java.perform(function(){
var homeProfileFragmentClazz = Java.use("com.plbear.hookdemo.UtilsV2");
var result = homeProfileFragmentClazz.cal(3,3);
send(result)
});
}
ok, 到这里就完成了简单的入门, 基本上可以满足日常需要. 更高级的用法还是要参看frida的官方API