参考文章
FRIDA-API使用篇:Java、Interceptor、NativePointer(Function/Callback)使用方法及示例-安全客 - 安全资讯平台 (anquanke.com)
JavaScript API | Frida • A world-class dynamic instrumentation toolkit
文章目录
- 参考文章
- 1、Java.available
- 2、Java.androidVersion
- 3、附加调用Java.perform
- 4、获取类Java.use
- 5、Hook静态方法和实例方法、修改函数参数和返回值
- 6、Hook构造方法:$init
- 7、Hook重载方法
- 8、扫描实例类Java.choose
- 9、主动调用
- 10、获取和修改类的字段
- 11、Hook内部类与匿名类
- 12、Hook枚举类
- 13、枚举所有类
- 14、枚举类的所有方法
- 15、Hook类的所有方法
- 16、枚举所有类加载器
- 17、类型转换器Java.cast
- 18、定义任意数组类型Java.array
- 19、注册类Java.registerClass(spec)
- 20、注入dex
- 21、写文件
- 22、全局上下文环境获取
- 23、打印函数堆栈
- 24、Hook只在指定函数内生效
- 25、Hook定位接口/抽象类的实现类
- 26、Hook动态加载的Dex
- 27、注意事项
1、Java.available
判断当前进程是否加载了JavaVM,Dalvik
或ART
虚拟机
function frida_Java() {
Java.perform(function () {
if(Java.available)
{
console.log("hello java vm");
}else{
console.log("error");
}
});
}
setImmediate(frida_Java);//setImmediate(回调方法, 参数1,参数2,...) 异步任务,立即执行
输出如下:
hello java vm
2、Java.androidVersion
显示android系统版本号
function frida_Java() {
Java.perform(function () {
if(Java.available)
{
console.log("",Java.androidVersion);
}else{
console.log("error");
}
});
}
setImmediate(frida_Java);
输出如下:
10
3、附加调用Java.perform
主要用于当前线程附加到Java VM
并且调用fn
方法它也有一个好兄弟:Java.performNow(fn)
4、获取类Java.use
- 构造实例:$new
- 实例的一些属性
- .$class == .getClass()
- .$className == .getClass().getName()
- .$superClass == .getClass().getSuperclass()
- .$dispose 回收类
5、Hook静态方法和实例方法、修改函数参数和返回值
let str=Java.use("java.lang.String");
str.toString.implementation=function () {
console.log("HooktoString")
return "修改值"
}
6、Hook构造方法:$init
Java.use("java.io.File").$init.overload("java.lang.String").implementation = function (path) {
console.log("",path)
return this.$init(path);
}
7、Hook重载方法
-
overload
Java.use(className).方法名.overload(参数类型1,…).implementation
-
overloads
Java.use(className).方法名.overloads返回重载函数数组
8、扫描实例类Java.choose
用法Java.choose(“全类名”,回调对象),在堆上查找实例化的对象
function frida_Java(className) {
Java.perform(function () {
Java.choose(className,{
onMatch:function (instance) {
console.log("搜索到实例对象:",instance);
return instance;
},
onComplete:function () {
console.log("内存搜索完毕!");
}
});
});
}
setImmediate(frida_Java,"com.android.settingslib.utils.PowerUtil");
9、主动调用
- 静态方法:直接调用
- 实例方法
- 使用$new新建实例调用方法
- 使用Java.choose在内存中寻找实例,使用找到的实例调用方法
10、获取和修改类的字段
设置/获取字段的值需要"字段.value"
- 静态字段:直接获取/修改
- 实例字段
- 使用$new新建实例获取/修改字段
- 使用Java.choose在内存中寻找实例,使用找到的实例获取/修改字段
11、Hook内部类与匿名类
- 内部类
- Java.use(“全类名$内部类名”)
- Java.choose(“全类名$内部类名”,JSON对象)
- 匿名内部类
- Java.use(’全类名$1‘)
- Java.choose(“全类名$1”,JSON对象)
12、Hook枚举类
-
Java.use(‘枚举类路径’)
枚举类.values():返回枚举类中所有的值
let protocol=Java.use('okhttp3.Protolcol') console.sole(protocol.values())
-
Java.choose(“枚举类路径”,JSON对象)
枚举对象.ordinal():返回该枚举常量的索引
Java.choose("okhttp3.Protolcol", { //枚举时调用 onMatch:function(instance){ //打印实例 console.log(instance.ordinal()); }, //枚举完成后调用 onComplete:function() { console.log("end") }});
13、枚举所有类
-
Java.enumerateLoadedClassesSync()
枚举当前加载的所有类,并返回string数组
console.log(Java.enumerateLoadedClassesSync().join("\n"));
-
Java.enumerateLoadedClasses(callbacks)
枚举当前加载的所有类信息,它有一个回调函数分别是
onMatch(每找到一次,执行一次)、onComplete(只执行一次,在搜索完成内存后)
函数function frida_Java() { Java.perform(function () { //枚举当前加载的所有类 Java.enumerateLoadedClasses({ //每一次回调此函数时其参数className就是类的信息 onMatch: function (className) { //输出类字符串 console.log("",className); }, //枚举完毕所有类之后的回调函数 onComplete: function () { //输出类字符串 console.log("输出完毕"); } }); }); } setImmediate(frida_Java);
14、枚举类的所有方法
通过Java反射
function frida_Java(className) {
Java.perform(function () {
let cn=Java.use(className);
let methods=cn.class.getDeclaredMethods();
for (let i = 0; i < methods.length; i++) {//循环遍历所有方法
let methodName = methods[i].getName();
console.log("",methodName)
}
});
}
setImmediate(frida_Java,"com.android.settingslib.utils.PowerUtil");
15、Hook类的所有方法
function frida_Java(className) {
Java.perform(function () {
let cn=Java.use(className);
let methods=cn.class.getDeclaredMethods();//cn.class:相当于Object.class 获取所有方法
for (let i = 0; i < methods.length; i++){//循环遍历所有方法
let methodName=methods[i].getName();//获取方法名
let loads=cn[methodName].overloads;//获取该方法的所有重载
for (let j = 0; j < loads.length; j++) {//遍历该方法的所有重载
loads[j].implementation=function () {//Hook方法
let result=this[methodName].apply(this,arguments);
console.log('参数:'+JSON.stringify(arguments));
console.log('返回值:'+JSON.stringify(result));
return result;
}
}
}
});
}
setImmediate(frida_Java,"com.android.settingslib.utils.PowerUtil");
16、枚举所有类加载器
-
Java.enumerateClassLoadersSync()
枚举所有的ClassLoader,并返回Wrapper数组
let loaders=Java.enumerateClassLoadersSync(); for (let i = 0; i < loaders.length; i++) { try { Java.classFactory.loader = classloader; //Hook代码 }catch (e) { } }
-
替换默认ClassLoader
Java.classFactory.loader = classloader;
-
Java.enumerateClassLoaders(callbacks)
举
Java VM
中存在的类加载器,它有一个回调函数分别是onMatch(每找到一次,执行一次)、onComplete(只执行一次,在搜索完成内存后)
函数function frida_Java() { Java.perform(function () { Java.enumerateClassLoaders({ onMatch: function (classloader) { console.log(JSON.stringify(classloader)); try { //替换默认classLoader Java.classFactory.loader = classloader; //Hook代码 } catch (e) { } }, onComplete: function () { console.log("内存搜索完毕!"); } }); }); } setImmediate(frida_Java);
17、类型转换器Java.cast
Java.cast(handle, klass)
,handle即对象实例,klass是要强转成的句柄,从Java.use获取。
//此类包装器还具有用于获取其类的包装器的类属性,以及用于获取其类名的字符串表示的$className属性,通常在拦截so层时会使用此函数将jstring、jarray等等转换之后查看其值。
Java.cast(map,Java.use("java.util.HashMap"))//将map实例对象强转成HashMap对象
18、定义任意数组类型Java.array
使用Java.array(“类型”,[实例对象,实例对象])
function frida_Java() {
Java.perform(function () {
let name=Java.use('java.lang.String').$new('Jack')
let age=Java.use('java.lang.Integer').$new(18)
Java.array('java.lang.Object',[name,age])
/**
* 可省略描述符 [name,age] == Java.array('java.lang.Object',[name,age])
* 可变参数本质上就是数组,按数组处理即可
* 基本数据类型数据要使用其包装类构造 Java.use('java.lang.Integer').$new(18)
* 类型可以是"java.lang.Object",也可以是"Ljava/lang/Object;"
*/
});
}
setImmediate(frida_Java);
19、注册类Java.registerClass(spec)
-
Java.registerClass
:创建一个新的Java
类并返回一个包装器name
:指定类名称的字符串。superClass
:(可选)父类。要从java.lang.Objec
t 继承的省略。implements
:(可选)由此类实现的接口数组。fields
:(可选)对象,指定要公开的每个字段的名称和类型。methods
:(可选)对象,指定要实现的方法。
function frida_Java() { Java.perform(function () { //注册一个目标进程中的类,返回的是一个类对象 var r0ysue = Java.registerClass({ name: 'com.roysue.roysueapplication', implements:[Java.use('java.lang.Runnable')], methods:{ run:function () { console.log('Thread Starting...') } } }); Java.use('java.lang.Thread').$new(r0ysue.$new()).start()//开启一个线程 }); } setImmediate(frida_Java);
20、注入dex
使用Java.openClassFile(“dex路径”).load()
//加载Dex文件,相当于new DexClassLoader(...)
Java.openClassFile("/data/local/tmp/classes.dex").load()
21、写文件
function writeToFile(filePath,data) {
Java.perform(function () {
let ios=new File(filePath,'w');
ios.write(data);
ios.flush();
ios.close();
});
}
22、全局上下文环境获取
function getApplicationContext() {
//获取Application实例对象
let activityThread=Java.use("android.app.ActivityThread");
let application=activityThread.currentApplication();
//获取context
// let context=application.getBaseContext();
let context=application.getApplicationContext();
return context;
}
23、打印函数堆栈
function showStacks() {
console.log(
Java.use("android.util.Log")
.getStackTraceString(
Java.use("java.lang.Throwable").$new()
)
);
}
24、Hook只在指定函数内生效
-
步骤
-
Hook指定函数
-
在指定函数内Hook要生效的函数
-
在指定函数结束前,将要生效的函数Hook赋值null
类名.方法名.implementation=null
-
-
代码
function makeHookMethodInSpecifiedFunction(specifiedClass,specifiedMethod,className,methodName) { Java.perform(function () { //Hook指定函数 let specifiedClazz=Java.use(specifiedClass); specifiedClazz[specifiedMethod].implementation=function () { console.log("指定的Hook函数生效"); //Hook 要Hook的函数 let clazz=Java.use(className); clazz[methodName].implementation=function () { console.log("要Hook的函数生效"); return this[methodName](arguments); } //关闭要Hook的函数 clazz[methodName].implementation=null; return this[specifiedMethod](arguments); } }); }
25、Hook定位接口/抽象类的实现类
-
定位接口的实现类
- 枚举所有类
- 获取类的所有接口
- 遍历接口过滤符合条件的类
-
定位抽象类的实现类
- 枚举所有类
- 获取类的父类
- 遍历父类过滤符合条件的类
-
代码
//Hook定位接口/抽象类的实现类:className是接口全路径或抽象类全路径 type:0表示接口、1表示抽象类 function HookLocateInterfaceOrAbstract(superClassName,type) { Java.perform(function () { if (type===0){//接口 try { //1.遍历类 let classes=Java.enumerateLoadedClassesSync(); for (let i = 0; i < classes.length; i++) { //2.获取类的所有接口 let name=classes[i]; let clazz=Java.use(name); let interfaces=clazz.class.getInterfaces(); //3.遍历接口 for (let j = 0; j < interfaces.length; j++) { //4.获取接口类名 let interClass=interfaces[j]; let interName=interClass.getName(); //5.过滤 if (interName==superClassName){ //输出实现类名 console.log(clazz.class.getName()); } } } }catch (e) { } }else if (type===1){//抽象类 //1.遍历类 Java.enumerateLoadedClasses({ onMatch:function (className) { try { //2.获取类的父类 let clazz=Java.use(className); let superClass=clazz.class.getSuperclass(); //3.获取父类名 let superName=superClass.getName(); //4.过滤 if (superName==superClassName){ //输出实现类名 console.log(className); } }catch (e){ } }, onComplete:function () { console.log("内存搜索完毕!"); } }); } }); }
26、Hook动态加载的Dex
-
Java.enumerateClassLoaders
onMatch:function (classloader) { try { Java.classFactory.loader=classloader; //Hook 代码 }catch (e) { } }
-
Java.enumerateClassLoadersSync()
for (let i = 0; i < loaders.length; i++) { try { Java.classFactory.loader = classloader; //Hook代码 }catch (e) { } }
-
Hook DexClassLoader.loadClass
function HookDexClassLoaderLoadClass() { Java.perform(function () { let loader=Java.use("dalvik.system.DexClassLoader"); loader.loadClass.overload("java.lang.String").implementation=function (className) { try { Java.classFactory.loader=this; //Hook代码 }catch (e) { } return this.loadClass(className); } }); }
27、注意事项
-
Java类型对象调用Java方法,js类型对象调用js方法
-
frida会自动处理Java和js字符串的相互转换
-
Hook属性字段时,设置/获取字段的值需要"字段.value"
-
定义变量时,尽量使用let而非var
var是全局变量,在遍历Hook函数时,很容易导致最后返回方法时,方法名与变量名对不上,导致Hook出错
-
Hook函数中的"this"指的是Java.use获取的对象
-
this.方法名.apply(this,arguments)
遍历重载函数时,可用此返回
-
Java.use获得的只是句柄,若需要对象,则使用句柄.$new()。若需要class字节码,则使用句柄.class