一、Frida简介
Frida是一款流行的hook框架,通杀android\ios\linux\win\osx等各平台,相比xposed和substrace cydia更加便捷。Frida的官网为:http://www.frida.re/,其核心原理是实现了一套inline hook框架,在Andorid平台的具体原理可参考这里。
本文重点介绍Frida在android平台的使用。
二、环境配置
官方教程https://frida.re/docs/installation/,主要操作如下:
- pip install frida-tools 安装frida环境
- 在root的手机上运行frida server端
- 1.下载对应平台的frida-server (https://github.com/frida/frida/releases)
- 2.adb push frida-server /data/lcoal/tmp
- 3.chmod 755 frida-server
- 4.setenforce 0(运行前关闭SELinux)
- 5../frida-server &
- 建立调试通道
- USB连接+默认端口号可直接略过
- 改变端口号需要 adb forward tcp:12345 tcp:12345
- 完成配置,直接在PC端运行hook脚本即可
需要注意的是,frida的客户端和服务度版本应该相同,否则会出现不可预知的其他错误。使用“frida –version” 查看frida的版本
三、免root实现frida hook(重打包)
注意: 无root环境下frida的部分功能受限
apktool反编译apk
1
$ apktool d test.apk -o test
将对应版本的gadget拷贝到/lib目录
下载地址:
https://github.com/frida/frida/releases/smali注入加载library,选择application类或者Activity入口.
1
2const-string v0, "frida-gadget"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V回编译apk
1
$ apktool b -o newtest.apk test/
重新签名安装运行.成功后启动app会有如下日志
Frida: Listening on TCP port 27042
四、frida-python模板
以下为frida hook的框架代码,只需要修改jscode就可以实现自定义的hook1
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
28
29
30
31
32
33
34
35
36
37
38import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
// 要hook的类名
var MainActivity = Java.use('com.example.xxx.MainActivity');
// hook按钮点击事件
MainActivity.onClick.implementation = function (v) {
console.log('hook onClick');
//修改MainActivity中属性的值
this.m.value = 0;
this.n.value = 1;
this.cnt.value = 999;
//打印日志
console.log('Done:' + JSON.stringify(this.cnt));
// 执行原始方法代码
this.onClick(v);
};
});
"""
device = frida.get_usb_device()
# 设置要注入的进程名或者pid
# attach mode
process = device.attach('com.tencent.mm')
# spawn mode
pid = device.spawn('com.tencent.mm')
process = device.attach(pid)
device.resume(pid)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running hook')
script.load()
sys.stdin.read()
五、采坑记录
1.延时hook问题
spawn模式下,有时候hook的lib还没有被加载到,导致无法hook,选择hook的时机很关键。
可以在hook代码前,用enumerateModulesSync验证是否加载1
2
3
4
5var mod = Process.enumerateModulesSync();
var modLen = mod.length;
for (var i = 0; i < modLen; i++) {
console.log("[+] module: " + mod[i]['name']);
}
确认是延时问题后,有以下几种解决办法:
- 如果不在意时效性,启动时不加“–no-pause”,可以等一段时间后,手动”%resume”
- 使用setTimeout(func, delay) 函数延时
- 使用frida的Module.load()或Module.ensureInitialized()来手动加载lib
- hook libart.so中的LoadNativeLibrary()或java层的System.loadLibrary(),判断加载完成特定so后,再进行后续的hook。
(1)hook libart.so中的LoadNativeLibrary()代码:
1 | function readStdString(str) { |
(2)hook System.loadLibrary()
1 | const System = Java.use('java.lang.System'); |
2. hook不上的原因
- 要hook的函数是不是正好被打了热补丁?
- hook的目标属于动态load, 需要确定时机或手动长期load到内存
- hook的进程是否正确,比如com.tencent.mm有好几个进程
对于多进程问题,参考child gating(https://frida.re/news/2018/04/28/frida-10-8-released/)和spawn gating, 示例代码: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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61# -*- coding: utf-8 -*-
import codecs
import frida
import sys
import threading
#device = frida.get_remote_device()
device = frida.get_device_manager().enumerate_devices()[-1]
print(device)
pending = []
sessions = []
scripts = []
event = threading.Event()
jscode = """
Java.perform(function() {
//your js code
})
"""
def spawn_added(spawn):
event.set()
if(spawn.identifier.startswith('com.ss.android.ugc.aweme')):
print('spawn_added:', spawn)
session = device.attach(spawn.pid)
script = session.create_script(jscode)
script.on('message', on_message)
script.load()
device.resume(spawn.pid)
def spawn_removed(spawn):
print('spawn_removed:', spawn)
event.set()
def on_message(spawn, message, data):
print('on_message:', spawn, message, data)
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
device.on('spawn-added', spawn_added)
device.on('spawn-removed', spawn_removed)
device.enable_spawn_gating()
event = threading.Event()
print('Enabled spawn gating')
pid = device.spawn(["com.ss.android.ugc.aweme"])
session = device.attach(pid)
print("[*] Attach Application id:",pid)
device.resume(pid)
sys.stdin.read()
3. 同名函数返回值不同
hook中可能出现“函数名参数完全相同,但返回值不同的情况”,这种情况frida暂时无法实现,测试此情况代码编译不会通过,但编译后可以改bytecode,不影响执行结果
- 这种情况可以枚举函数的overloads属性,挨个hook,或者使用数组索引hook,例如a.overloads[1].implement=xxxx
4. 使用enumerateLoadedClasses报“VM::GetEnv failed”错误
enumerateLoadedClasses需要在Java.perform()下使用,参考https://github.com/frida/frida/issues/237
5. app没有入口,无法使用spawn
Failed to spawn: unable to find application with identifier ‘com.dzvuhumnjt.kfwmalytfds’
如果包名是正确的,应该是这个包名是一个服务,没有lunch activity, 此时只能靠启动后抢时间的方式来attach, spawn行不通
6. 判断一个函数是public还是privte
反射出 method 类来调getModifiers()
7. Magisk Hide
unable to access process with pid 1204 due to system restrictions; try sudo sysctl kernel.yama.ptrace_scope=0
, or run Frida as root
magisk 里面的一个叫magisk hide的东西会妨碍frida的进程,关掉这个即可
8. 明明有这个函数却hook不了,即使自己调用也无法触发
常见于一些final或static函数,系统进行了优化,在开头使用Java.deoptimizeEverything() 强制VM使用其解释器执行所有操作。参考https://github.com/frida/frida/issues/1298
例如JIT的内联优化(method lining)会根据调用频次决定是否走解释执行,也会出现这种情况。
解决方案:
(1)Frida API
Java.deoptimizeEverything()
Java.deoptimizeBootImage() 14.2新增https://frida.re/news/2021/02/10/frida-14-2-released/#deoptimization
(2)系统级别
adb shell setprop dalvik.vm.usejit false (关闭JIT)
adb shell cmd package compile –reset com.tencent.mm (清除配置文件数据并移除经过编译的代码)
参考https://github.com/frida/frida/issues/817
https://source.android.google.cn/devices/tech/dalvik/jit-compiler
六、 Frida基础数据类型
为以后写脚本时方便,这里列举下常用的类型:
Int
1 | var Integerclass = Java.use("java.lang.Integer"); |
Long
1 | var Longclass = Java.use("java.lang.Long"); |
Bool
1 | Java.use("java.lang.Boolean").$new(true); |
ByteArray
js传给python
1
2
3
4
5
6
7
8
9// js中send(byte[]数组)
def on_message(message, data):
if message['type'] == 'send':
data = message['payload']
for i in range(len(data)):
data[i] = data[i] & 0xff
open("tmp",'wb').write(bytearray(data))
else:
passpython传给js
1
var a = Java.array('byte', [ 0xac,0x37,0x43,0x4f,0xaf,0xa8]);
遍历map
1 | var mapcls = Java.use("java.util.Map"); |
七、 Frida Hook实例
以下为我曾经用过或自己实现的frida脚本,根据不同的使用场景进行了分类。
1. 打印hook函数的返回值
直接运行一遍原函数,将结果赋值给一个新变量,输出并return新变量即可,例如:
1 | Java.perform(function () { |
2. hook重载函数
1 | cls.loadUrl.overload("java.lang.String").implementation = function(param)…… |
3. 打印java函数的调用栈
1 | var Exc = Java.use("java.lang.Exception"); |
或1
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()))
4. 打印native函数调用栈
1 | console.log("begin===="); |
5. dump内存
1 | var data = Memory.readByteArray(ptr(0x824a9000), 159744); |
6. Hook Android IMEI
1 | Java.perform(function () { |
7. Hook Android webview http请求
主要针对以下webview中的以下函数:
1 | - loadUrl(String url) |
1 | Java.perform(function () { |
8. 获取context
1 | var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); |
9. 创建bundle对象
1 | var bundle = Bundle.$new(); |
10. hook 对象
1 | //读取实例对象的属性值,对于得到的对象,需要使用Java.cast()方法转换后才可以使用 |
11. 导出函数并被任意调用
1 | rdev = frida.get_remote_device() |
需要获取返回值的情况1
2
3
4
5
6
7
8
9
10
11
12rpc.exports.foo = function () {
return new Promise(function (resolve, reject) {
Java.perform(function () {
try {
var result = ...
resolve(result);
} catch (e) {
reject(e);
}
});
});
};
12. hook构造函数
1 | obj.$init.implementation = function (){ |
13.枚举所有加载的类
1 | Java.enumerateLoadedClasses({ |
14.打印函数参数类型
15.注册一个类(Java.registerClass)
1 | Java.perform(function() { |
16.输出一个类的所有field和Method
1 | const Class = Java.use("com.Awesome.App.MainActivity"); |
17.修改native函数的返回值
1 | Interceptor.attach(Module.getExportByName('libnative-lib.so', 'Jniint'), { |
18.枚举native导出函数
1 | //enumerateExports(null,... 所有lib |
19.内存中注入so和dex
1 |
|
20.使用google的gson打印object
参考:https://bbs.pediy.com/thread-259186.htm
21.利用反射找类的Interface
https://bbs.pediy.com/thread-259631.htm
其他参考链接
- https://github.com/FloatingGuy/fg-Blog/blob/7df0bd47b42d11b787fe394259fa30288307ae48/source/_posts/%E5%BC%80%E5%8F%91/frida%20hook%20%E6%93%8D%E4%BD%9C%E6%89%8B%E5%86%8C.md
- r0ysue的相关文章 https://bbs.pediy.com/user-home-581423.htm
- Frida学习笔记 https://api-caller.com/2019/03/30/frida-note/#%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%A0%86%E6%A0%88