# Frida
是 hook
安卓的一款强大工具,比 xposed
要方便很多,下面就介绍一下 frida
安装到使用的方法。(基于有 python
环境)
# 先安装
python
的frida
模块,再下载frida-server
并推到手机里网上大佬们讲
12
版本和14
版本比较稳定,我建议安卓6,7
用12
,安卓8
以上用14
(如有不匹配,还得自己试😂),我用的14.2.18
,再安装对应版本的frida-tools
, 可以到github
上看对应版本Github: https://github.com/frida/frida/releases/tag/14.2.18
pip install frida==14.2.18
pip install frida-tools==9.2.4
# 然后再根据安卓系统下载
frida-server
, 一般真机是arm
内核,模拟器是x86
内核,可以用下面的adb
指令查看adb shell getprop ro.product.cpu.abi
返回:
arm64-v8a # 这里可以看到我的是 arm64
到
git
上下载对应的server
# 把下载的
server
解压,然后用adb
推送到手机/data/local/tmp
里,并给执行权限。adb push frida-server-14.2.18-android-arm64 /data/local/tmp/frida-server # 注意一定要解压之后再推送到手机
adb shell # 进入 adb 命令行,前提是电脑只连接了一部手机或模拟器,否则需要指定 id
$ su # 打开 root 权限
$ cd /data/local/tmp # 切换到 tmp 目录下
$ chmod +x frida-server # 给 frida-server 添加可执行权限
$ ./frida-server # 启动 frida-server 如下图光标闪烁,表示启动成功
server
启动后,控制台不要关闭,再打开一个控制台设置端口转发:
adb forward tcp:27042 tcp:27042
# 然后开始写
python
脚本,用来注入js
脚本,输出log
,我这里是直接获取手机当前打开的app
信息,懒得每次自己查询包名,js
代码写在单独文件。import frida
import sys
# 读取 js 代码
def get_jscode():
fn = "jscode.js"
with open(fn, "r", encoding="utf-8") as f:
data = f.read()
return data
def on_message(message, data):
# print "回调函数信息:", message
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = get_jscode()
# rdev = frida.get_remote_device () # 模拟器
rdev = frida.get_usb_device() # 数据线 好像两种都可以
front_app = rdev.get_frontmost_application() # 获取当前打开的 app
try:
pkg_name = front_app.identifier # 获取当前 app 包名
# 启动 app 两种方式,运行时用一种就可以。
# 方式一:spawn 模式,下面用 resume(不加这个函数,可能会卡死)重启 app,可以 hook app 启动阶段的函数(比如 root 检测)
pid = rdev.spawn([pkg_name]) # 根据 app 包名获取 pid
session = rdev.attach(pid) # 添加此 pid 进程
script = session.create_script(jscode) # 添加 js 代码
script.on('message', on_message) # log 输出
script.load() # 加载脚本
rdev.resume(pid) # 重启 app, 用来 hook app 启动阶段的函数
sys.stdin.read() # 持续输出控制台,不加这句代码,执行一次就结束了
# 方式二:直接 attach (pkg_name),hook app 执行后的代码,不需要重启
session = rdev.attach(pkg_name) # 通过包名添加进程,后面代码一样,只是没有重启的函数
script = session.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()
except AttributeError as e:
print(e)
print("当前app的pid为空!")
# 编写
js
脚本,hook
的核心代码,首先找到 hook 点,然后修改下面代码即可。Java.perform(function () {
var hook_class = Java.use("com.tencent.news.MainActivity") // 比如 hook 某新闻的 MainActivity 类
console.log("success..")
hook_class.a.implementation = function (str){ // MainActivity 类的 a 函数
console.log("str", str) // 打印 str 的值,也可以用 send (str)
return this.a(str) // 如果原函数有 return,这里必须也要调用 this.a (str) 并 return 出去
}
})
# 关于不同
java
函数hook
方式# 一般函数 --
implementation
var hook_class = Java.use('com.example.Mainclass');
hook_class.fun.implementation = function (arg) {
console.log('arg:', arg);
return this.fun(arg);
}
# 重载函数 --
overload
, 重载的函数就是同一个类下面函数名相同,参数不同的函数var hook_class = Java.use('com.example.Mainclass');
hook_class.fun.overload("java.lang.String").implementation = function (arg) {
console.log('arg:', arg);
return this.fun(arg);
}
overload
里面需要添加参数的类型,按位置对应,下面列出一些常用的类型Java
中参数类型frida
脚本中参数类型int
int
byte
byte
float
float
String
java.lang.String
HashMap
java.util.HashMap
boolean
boolean
# 构造函数 --
$init
, 如果构造函数还有重载还需要再$init
后面加overload
var hook_class = Java.use('com.example.Mainclass');
hook_class.$init.implementation = function () {
return this.$init();
}
// 构造函数重载
hook_class.$init.overload().implementation = function () {
return this.$init();
}
# 构造对象 --
$new
const hashmap = Java.use("java.util.HashMap");
var hashobj = hashmap.$new()
hashobj.put("key","value")
const javaString = Java.use("java.lang.String");
var strobj = javaString.$new("我是一个string")
# 类中类 --
$
要hook
内部类的函数var hook_class = Java.use('com.example.Mainclass$innerclass');
hook_class.fun.implementation = function (arg) {
console.log('arg:', arg);
return this.fun(arg);
}
# 静态变量 -- value
var hook_class = Java.use("com.example.Mainclass");
console.log("hook_class中method的值", hook_class.method.value)
#
so
层native
静态注册函数 -- 关键是使用IDA
找到关键函数// 方法一:通过 intercept 拦截器,根据函数名 hook
var so_name = "xxxlib.so"; // 要 hook 的 lib.so 文件名
var func_name = "func_name"; // 要 hook 的函数名
var func_addr = Module.findExportByName(so_name , func_name);
console.log("func addr is ---" + func_addr);
Interceptor.attach(n_addr_func, {
// 在 hook 函数之前执行的语句
onEnter: function(args)
{
console.log("hook on enter")
},
// 在 hook 函数之后执行的语句
onLeave:function(retval)
{
console.log("hook on leave")
}
});
// 方法二:通过偏移地址 inlinehook
console.log("so start:")
// 如果是 32 位 下面地址需要改成 0x22F0+1
var address = Module.findBaseAddress("libencrypt.so").add(0x22F0);
console.log("func address:", address)
Interceptor.attach(pointer, {
onEnter: function (args) {
// 根据指针地址获取 string,根据参数的实际类型打印
console.log("params1", Memory.readCString(args[0]));
},
onLeave: function (retval) {
console.log("here???", retval)
}
})
# 打印 object 类型的值
// 目前我就知道这三种,而且也不一定对所有 obj 有效
>> console.log(obj.toString())
>> consloe.log(JSON.toJSONString(obj))
>> for(var i in obj){
console.log(obj[i].toString()) // 遍历 obj,打印每一条属性
}
# 打开手机
app
,然后运行python
脚本,就可以进行hook
了frida只是工具,重点还是要找到hook位置。