Frida与雷电模拟器兼容版本
- Frida版本:12.0.8
- 雷电模拟器版本:4.x或3.x
- frida-tools版本:1.1.0
常用命令
-
获取模拟器所有进程
frida-ps -U
-
使用命令行加载程序(spawn)
frida -U -f 包名
- 使用
%resume
来启动程序 - 使用
%load js脚本位置
来加载脚本
- 使用
-
直接启动Hook
frida -U -l js脚本位置 包名
常用语法
Java层Hook
Java.perform
Java层的Hook都是从Java.perform
开始的。相当于main()
方法,下面的Java语法都是需要写在这个里面的。具体用法如下:
1 | Java.perform(function(){ |
Java.choose
用于查找堆中指定类的实例。获得实例后可以调用实例的函数。声明为:Java.choose(className,callbacks)
具体用法如下:
1 | Java.choose("top.zyzling.User",{ |
Java.available
确认当前进程的java虚拟机是否已经启动,虚拟机包括Dalbik或者ART等。虚拟机没有启动的情况下不要唤醒其他java的属性或者方法。返回值是一个boolean
Java.enumerateLoadedClasses
列出(枚举)当前已经加载的类,用回调函数处理。声明为:Java.enumerateLoadedClasses(callbacks)
.用法如下:
1 | Java.enumerateLoadedClasses({ |
Java.enumerateClassLoaders
主要用于列出Java JVM
中存在的类加载器。声明为:Java.enumerateClassLoaders(callbacks)
。用法如下:
1 | Java.enumerateClassLoaders({ |
Java.registerClass
用于注册一个类到内存,这个类可以是我们自己定义的,也就是说我们可以通过这个方式来自定义类加入到内存中,也可以是已经存在的类。声明为:Java.registerClass(callbacks)
。用法如下:
1 | var obj = Java.registerClass({ |
下面看看官方是怎么使用的。
1 | //获取目标进程的SomeBaseClass类 |
通过类的全限定名加载类
在Frida中通过Java.use(类的全限定名)
来加载类,相当于Java的Class.forName()
。用法如下:
1 | //加载String类 |
加载类后,怎样创建一个对象
使用类.$new()
来创建,例如创建一个String
对象
1 | var jStringClass = Java.use("java.lang.String"); |
数据类型相关
Frida中的基本类型定义
Frida中的基本类型全名 | Frida中的基本类型缩写(定义数组时使用) |
---|---|
boolean | Z |
byte | B |
char | C |
double | D |
float | F |
int | I |
long | J |
short | S |
Frida中数组的定义
在Frida中用[
表示数组。
例如是int
类型的数组,写法为:[I
如果是String
类型的数组,则写法为:[java.lang.String;
注意:后面还有个;
号
Frida创建任意类型的数组
-
通过
Java.array()
。用法如下:Java.array('type',[value1,value2,value3])
,例子如下:1
2
3
4
5
6
7//标准写法:
var objClass = Java.use("java.lang.Object");
var objArray = Java.array("Ljava.lang.Object;",[objClass.class]);
//-------------以上就是相当于定义了一个Class[]数组,里面有一个Object.class元素。-------
//偷懒写法
var objArray = [objClass.class]假设我们需要定义
int[]
数组,并且需要往里面添加2个值。可以按照下面的写法写:1
2
3
4
5
6
7
8var intClass = Java.use("java.lang.Integer");
var num1 = intClass.$new(1);
var num2 = intClass.$new(2);
var intArray = Java.array("Ljava.lang.Object;",[num1,num2]);
//-------------也可以如下写法-----------------
var num1 = intClass.$new(3);
var num2 = intClass.$new(4);
var intArray = [num1,num2];那么怎么表示一个空数组呢?按照上面的例子,大概就是这样的
1
2
3var emptyArray = Java.array("Ljava.lang.Object;",[]);
//----------------也可以偷懒像下面这样写------------
var emptyArray = [];
类型强转
在Frida中使用Java.cast()
来强转类型。例如我想获取某个对象的Class
,那么就可以如下写:
1 | var clazz = Java.use("java.lang.Class"); |
获取Frida中[object Object]
的值
假设console.log(data)
打印出来的结果是[object Object]
,那么我们可以直接使用console.log(data.value)
来打印他的值。
反射操作
获取方法
Frida也支持Java的反射操作。例子如下:
1 | var clazz = Java.use("java.lang.Class"); |
执行方法
使用method.invoke(对象,参数)
来执行方法,例如执行String.valueOf(1)
方法,例子如下:
1 | var jString = Java.use("java.lang.String"); |
获取属性
获取属性和方法差不多,就是把Method
改为了``Field`
1 | var jString = Java.use("java.lang.String"); |
获取属性值和设置属性值
和java一样,使用get(对象)
来获取属性值,使用set(对象,值)
来设置属性值。例子如下:
1 | //假设我有个User类,里面有个私有name属性。 |
Firda Hook操作
基础说完了,现在我们来说说Frida具体是怎么用的。
Hook方法(没得重载的方法)
直接使用类.方法名.implementation =function(){}
来对一个方法进行Hook。举个栗子,我们要HookUser
类的getName()
方法,让它返回zyzling
1 | var userClass = Java.use("top.zyzling.User"); |
Hook重载方法
上面那种是没得重载的,如果有重载,就得使用下面这种方法来hook。
类.方法名.overload(参数).implementation =function(){}
还是举个栗子说明,假设我们在User
类里有个setName()
方法,但是他有重载方法,所谓重载方法就是在一个类中,方法名一样,但是方法的参数签名不一样。比如setName(String)
和setName(int)
这两个方法就是重载方法,所以需要加上参数来分辨他们。例子如下:
1 | var userClass = Java.use("top.zyzling.User"); |
Hook构造方法
上面对普通的方法进行Hook,那现在我们说说构造方法怎么hook。这里也分有参和无参,但是不管是有参还是无参,都是使用类名.$init
来进行Hook的,如果有重载的构造方法,则也需要.overload
。例子如下:
1 | //我们还是以User类为例,假设他有个User(int i,String name)的构造方法。 |
加固的类Hook
这里我们可以先HookApplicationContext
的attach()
方法,然后在里面加载类。比方说下面的代码
1 | var application = Java.use("android.app.Application"); |
简单脚本(抄的)
枚举所有的类
1 | //定时调用 |
打印堆栈信息
1 | var showStacks = function () { |
混淆代码Hook
因为类名和方法名都混淆了(成了不可见字符),这时候我们只需要复制混淆的类名和方法名在百度上随便找一个有URLEncode
功能的网站,先URLEncode
一下。然后我们可以在js里面使用decodeURIComponent
得到混淆的类名和方法名。这样Frida才能找得到。示例如下:
1 | //假设我们URLEncode过后的混淆方法名为:%EE%A0%91,类名为:%EE%A0%94%EE%A0%94%EE%A0%94%EE%A0 |
调用隐藏方法
1 | Java.choose("com.roysue.demo02.MainActivity" , { |