frida 学习笔记
自用环境:
2
3
4
5
> 终端: 雷电模拟器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
 7- Java.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
 8- Java.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 
 2- var 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
 4- var 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
 52- var 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