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的handleonComplete :function()当所有的类都被枚举后调用
Java.enumerateLoadedClassesSync():enumerateLoadedClasses()的同步版本,该版本在数组中返回类名称。Java.enumerateClassLoaders(callbacks): 枚举Java VM中存在的类加载器,callbacks是一个对象,它指定了以下内容:onMatchomComplete
也可以将这样的加载器传递给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是一个包含以下内容的对象:namesuperclassimplementsfieldsmethods
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)loadercacheDirtempFileNaminguse(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