frida 学习笔记
自用环境:
1
2
3
4
5 > 电脑: Mircrosoft Windows10 版本1909 (OS内部版本 18353.778)
> 终端: 雷电模拟器4 android 7.1.2 x86架构(虚拟机实际上是电脑cpu)
> frida版本: 12.8.20
> Python版本: Python 3.7.7
>
1 安装
1.1 电脑安装frida
因为博客是用的hexo,所以电脑里之前是有node.js和npm的,但是很奇怪,使用npm安装有问题,后面还是选择的python
1 | $ py -3 -m pip install frida |
因为电脑里有两个版本的python,且新的PowerShell支持选择python版本,所以有一点微小的区别。
1.2 手机安装frida server
选择对应frida版本和终端型号的frida-server,下载
1 | adb push frida-server /data/local/tmp |
之后就可以愉快的玩耍了
1 | $ frida-ps -U |
如果能看见PID和对应的name,则说明成功安装
1 | $ frida-trace -U -i open com.android.chrome |
2 Tools
2.1 frida CLI
1 | # Connect Frida to an Android over USB and start debugging APP |
2.2 frida-ps
列出进程的命令行工具,在与远程系统交互时很有用
1 | # Connect Frida to an Android over USB and list running processes |
2.3 frida-trace
用于动态跟踪函数调用
1 | # 具体信息使用frida-trace -h来查看,一下列举常用的参数 |
2.4 frida-discover
在模拟器上运行,发现
Abort message: 'Failed to resolve labels'
待解决
2.5 frida-ls-devices
1 | # 通过USB将Frida连接到远程终端并列出正在运行的进程 |
2.6 frida-kill
This is a command-line tool for killing processes.
1 | $ frida-kill -D <DEVICE-ID> <PID> |
3 JavaScript API
不一一列举了,需要的时候再来查,熟能生巧
重点关注Java:
java.available
: 一个判断当前当前进程是否已加载Java VM(dvm,art)的bool值Java.androidVersion
: 获取正在运行的android 版本Java.enumerateLoadedClasses(callbacks)
: 枚举立即加载的类,其中callbacks
是一个指定以下内容的对象:onMatch : function (name, handle):
为每个已加载的类调用,其名称可以传递给use()
以获取JavaScript包装器。 也可以使用Java.cast()
处理java.lang.Class
的handle
onComplete :function()
当所有的类都被枚举后调用
Java.enumerateLoadedClassesSync()
:enumerateLoadedClasses()
的同步版本,该版本在数组中返回类名称。Java.enumerateClassLoaders(callbacks)
: 枚举Java VM中存在的类加载器,callbacks
是一个对象,它指定了以下内容:onMatch
omComplete
也可以将这样的加载器传递给Java.ClassFactory.get()
以便能够在指定的类加载器上使用.use()
类。
Java.enumerateClassLoadersSync()
:enumerateClassLoaders()
的同步版本,该版本在数组中返回类加载器。Java.scheduleOnMainThread(fn)
: 在VM的主线程上运行fn
。Java.perform(fn)
: 确保当前线程已连接到VM,并调用fn
. (在Java的回调中这不是必需的。) 如果该应用的类加载器尚不可用,则将延迟调用fn
。 如果不需要访问应用程序的类,请使用Java.performNow()
。
1 | Java.perform(function () { |
Java.use(className)
: 动态获取className
的JavaScript
包装器,您可以通过在其上调用$new()
来调用构造函数来实例化对象。 在实例上调用$dispose()
以对其进行显式清理(或等待JavaScript对象进行垃圾回收,或等待脚本卸载). 静态和非静态方法都可用,甚至可以替换方法实现并从中引发异常:1
2
3
4
5
6
7Java.perform(function () {
var Activity = Java.use('android.app.Activity');
var Exception = Java.use('java.lang.Exception');
Activity.onResume.implementation = function () {
throw Exception.$new('Oh noes!');
}
});Java.openClassFile(filePath)
: 在filepath
中打开一个.dex
文件, 使用以下方法返回对象:load()
将包含的类加载到VM中。getClassNames()
获取可用类名的数组
Java.choose(className, callbacks)
通过扫描Java堆来枚举className
类的活动实例,其中callbacks
是一个指定以下内容的对象:onMatch:function (instance)
: 在每个具有可用intance
的活动实例中调用 , 就像使用此特定实例的原始句柄调用Java.cast()
一样onComplete: function ()
Java.retain(obj)
复制JavaScript包装器obj
,以供以后在外部替换方法中使用。1
2
3
4
5
6
7
8Java.perform(function () {
var Activity = Java.use('android.app.Activity');
var lastActivity = null;
Activity.onResume.implementation = function () {
lastActivity = Java.retain(this);
this.onResume();
};
});Java.cast(handle, klass)
: 根据Java.use()
返回的给定类klass
的handle
,在给定现有实例的情况下创建一个JavaScript包装器。 这样的包装器还具有用于为其类获取包装器的class
属性,以及用于获取其类名称的字符串表示形式的$className
属性。1
2var Activity = Java.use('android.app.Activity');
var activity = Java.cast(ptr('0x1234'), Activity);Java.array(type, elements)
从JavaScript数组元素创建具有指定类型的元素的Java数组。 生成的Java数组的行为类似于JS数组,但可以通过引用传递给Java API,以允许它们修改其内容。1
2
3
4var values = Java.array('int', [ 1003, 1005, 1007 ]);
var JString = Java.use('java.lang.String');
var str = JString.$new(Java.array('byte', [ 0x48, 0x65, 0x69 ]));Java.isMainThread()
确定调用者是否在主线程上运行。Java.registerClass(spec)
创建一个新的Java类并为其返回一个包装器,其中spec是一个包含以下内容的对象:name
superclass
implements
fields
methods
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
52var SomeBaseClass = Java.use('com.example.SomeBaseClass');
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var MyTrustManager = Java.registerClass({
name: 'com.example.MyTrustManager',
implements: [X509TrustManager],
methods: {
checkClientTrusted: function (chain, authType) {
},
checkServerTrusted: function (chain, authType) {
},
getAcceptedIssuers: function () {
return [];
},
}
});
var MyWeirdTrustManager = Java.registerClass({
name: 'com.example.MyWeirdTrustManager',
superClass: SomeBaseClass,
implements: [X509TrustManager],
fields: {
description: 'java.lang.String',
limit: 'int',
},
methods: {
$init: function () {
console.log('Constructor called');
},
checkClientTrusted: function (chain, authType) {
console.log('checkClientTrusted');
},
checkServerTrusted: [{
returnType: 'void',
argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String'],
implementation: function (chain, authType) {
console.log('checkServerTrusted A');
}
}, {
returnType: 'java.util.List',
argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'],
implementation: function (chain, authType, host) {
console.log('checkServerTrusted B');
return null;
}
}],
getAcceptedIssuers: function () {
console.log('getAcceptedIssuers');
return [];
},
}
});Java.deoptimizeEverything()
强制VM使用其解释器执行所有操作。 在某些情况下,有必要防止优化绕过方法挂钩,并允许ART的Instrumentation API用于跟踪运行时。java.vm
对象具有以下方法:perform(fn)
getEnv()
tryGetEnv()
Java.classFactory
用于实现默认类工厂eg: Java.use()
。使用应用程序的主类加载器。Java.ClassFactory
具有以下属性的类:get(classLoader)
loader
cacheDir
tempFileNaming
use(className)
openClassFile(filePath)
choose(className, callbacks)
retain(obj)
cast(handle, klass)
array(type, elements)
registerClass(spec)
4 Example
4.1 下载安装,查看信息
1 | $ frida-ps -Ua |
查询得到 对应的应用完整Identifier com.example.seccon2015.rock_paper_scissors
4.2 frida 脚本实例
1 | # ctf.py |
4.3 使用JS脚本的例子
1 | Java.perform(function () { |
5 原理
1 | import frida, sys |
1 | process = frida.get_usb_device().attach('应用完整包名') |
跟进找到DeviceManager
,enumerate_devices()
返回的是一个Device()
类的实例化对象的List,跟进Device()
能找到attach()
方法,提供完整的应用名称,找到对应的pid,然后通过pid去attach 相应的方法, 最终得到的是一个对应进程号pid的Session实例化对象process。
1 | script = process.create_script(jscode) |
接下来调用load()方法,在服务端就启动javascript脚本了
6 使用积累
1 | Java.perform(function () { |
1 | Java.perform(function () { |
1 | Java.perform(function () { |
1 | Java.perform(function () { |
1 | Java.perform(function () { |
1 | var Activity = Java.use('android.app.Activity'); |
1 | // 创建数组 |
1 | Java.perform(function () { |
参考资料
[1] 官方文档
[2] 使用Frida框架进行hook
[3] FRIDA脚本系列(一)入门篇:在安卓8.1上dump蓝牙接口和实例
[4] FRIDA脚本系列(二)成长篇:动静态结合逆向WhatsApp