1.获取 com.example.ClassName
类,并修改其中的 getVip(i) 方法。
Java.perform(function () {var targetClass= Java.use('com.example.ClassName')//改为需要hook的类名//重写其中的getVip(i) 函数targetClass.getVip.implementation = function (i) {console.log('i: ', i);var result = this.getVip(i);console.log('result: ', result);return result;}
});
2.从内存中寻找包含 com.example.ClassName
的类,并打印出来
Java.perform(function() {Java.enumerateLoadedClasses({onMatch: function(className) {if (className.includes('com.example.ClassName')) {改为需要hook的类名console.log(className);}},onComplete: function() {console.log('Class enumeration complete');}});
});
3.获取某类中的所有成员变量 打印:名称+数值+类型
Java.perform(function() {// 找到目标类var targetClass = Java.use('com.example.ClassName');//改为需要hook的类名// hook指定函数从而找到实例,打印实例中所有的字段targetClass.e.implementation = function(str) {var fields = this.class.getDeclaredFields();for (var i = 0; i < fields.length; i++) {fields[i].setAccessible(true);//确保可以访问console.log("[*] Field " + fields[i].getName() + ": " + fields[i].get(this)," Type: ",fields[i].getType().getName());}}
});
4.Hook 已经存在的实例,通过构造函数或内存查找找到实例,从而调用其中的函数。不通过拦截某函数的方式。
global.instances = [];// 存储实例的全局数组
Java.perform(function () {// 获取目标类var MyClass = Java.use('com.example.ClassName');// Hook构造函数以捕获新创建的实例,重写构造函数时需要具体修改传入的变量类型'java.lang.String'MyClass.$init.overload('java.lang.String').implementation = function (str) {console.log('MyClass instance created');var instance = this;// 将实例存储到全局数组中global.instances.push(instance);return this.$init(str);};// 查找所有已经存在的实例Java.choose('com.example.ClassName', {onMatch: function (instance) {console.log('Found instance: ' + instance);global.instances.push(instance);},onComplete: function () {console.log('Instance search complete');}});// 调用方法示例Java.perform(function () {if (global.instances.length > 0) {var instance = global.instances[0]; // 获取第一个实例console.log('Calling myMethod on: ' + instance);instance.myMethod(); // 调用方法} else {console.log('No instances found');}});
});
5.打印构造函数的调用堆栈(通过抛出错误来获取)
Java.perform(function () {// 替换`com.example.ClassName`为你要监控的类名var targetClass = 'com.example.ClassName';var targetConstructor = Java.use(targetClass).$init.overload('long', 'java.lang.ref.WeakReference', 'boolean');//参数需要根据具体的构造方法修改targetConstructor.implementation = function (param1, param2, param3) {console.log('Constructor of ' + targetClass + ' called with parameters:');console.log('param1: ' + param1);console.log('param2: ' + param2);console.log('param3: ' + param3);// 获取当前的调用堆栈var stackTrace = Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Exception').$new());console.log(stackTrace);// 调用原始构造函数return targetConstructor.call(this, param1, param2, param3);};
});
6.当某实例类作为参数传入时,通过反射来修改其实例类中的私有字段
Java.perform(function() {// 找到目标类var targetClass = Java.use('com.example.ClassName');// hook指定函数从而找到实例,打印实例中所有的字段targetClass.D0.implementation = function(userInfo) {console.log("==================");// 使用反射来访问和修改字段var fieldName = 'someFieldName'; // 替换为实际的字段名var field = userInfo.class.getDeclaredField(fieldName);field.setAccessible(true); // 确保可以访问// 打印原始字段值console.log("[*] Before modification: " + field.get(userInfo));// 修改字段值field.set(userInfo, 'newValue'); // 替换'newValue'为你想设置的值fields.set(this, Long.$new(6666));//设置的值必须复合要求的格式// 打印修改后的字段值console.log("[*] After modification: " + field.get(userInfo));// 调用原始函数this.D0(userInfo);return;};
});
7.其他:
获取内部类:
const className = Java.use('外部类$内部类') //通过添加 $ 符号
当变量与函数名称一样时,修改变量的值:
this._a.value='1'; //在要修改的变量名前面加下划线_
获取匿名类:
匿名类根据内存生成,没有显式的类名,通过 smali 代码来判断,获取到的可能像下面这样:
const className = Java.use('包名.MainActivity$1') // 匿名类名称:1,调用类似内部类
获取所有类:
Java.enumerateLoadedClassesSync() // 同步获取已加载所有类,返回一个数组
Java.enumerateLoadedClasses() // 异步
加载类下所有方法,属性:
const Utils = Java.use('com.example.ClassName')
const methods = Utils.class.getDeclaredMethods() // 方法
const constructors = Utils.class.getDeclaredConstructors() // 构造函数
const fields = Utils.class.getDeclaredFields() // 字段
const classes = Utils.class.getDeclaredClasses() // 内部类
const superClass = Utils.class.getSuperclass() // 父类(抽象类)
const interfaces = Utils.class.getInterfaces() // 所有接口
类型转换:
将 variable 转换为 String
类型
var StringClass=Java.use("java.lang.String");
var NewTypeClass=Java.cast(variable,StringClass);
自己初始化一个实例并调用参数:
var ClassName=Java.use("com.example.ClassName");
var instance = ClassName.$new();//创建一个实例,$new 实际代表调用构造方法
instance.func();
8.函数参数类型表示:
当重载某个函数时,frida需要根据参数的类型来找到特定的重载函数,参数类型如果是基本类型,可直接用其Java/JavaScript中的名称,如果是基本类型数组需要左括号加缩写,如果是普通类型,则需要类名全拼。如下:
- 基本类型不变:
int short char byte boolean float double long
类型 | 重载时 |
---|---|
boolean[] | [Z |
byte[] | [B |
char[] | [C |
double[] | [D |
float[] | [F |
int[] | [I |
long[] | [J |
short[] | [S |
- 任意类: 完整类名 例如:
java.lang.String
- 对象数组:加左括号 例如:
[java.lang.String
不同的 frida 版本的区别:
- frida-clr-16.3.0:
这是一个用于 .NET/CLR (Common Language Runtime) 平台的 Frida 版本。它允许在 .NET 应用程序中进行动态分析和调试。 - frida-core-devkit-16.3.0:
这是 Frida 核心库的开发工具包,提供了核心 API 的头文件和库,用于开发自定义的 Frida 应用。 - frida-gadget-16.3.0:
Frida Gadget 是一个轻量级的嵌入式版本,适合于将 Frida 集成到现有的应用程序中,特别是移动设备上的应用。 - frida-gum-devkit-16.3.0:
Gum 是 Frida 的低级 API,用于提供跨平台的代码注入功能。这个开发工具包包含了使用 Gum 的必要文件。 - frida-gumjs-devkit-16.3.0:
这是一个基于 Gum 的 JavaScript 引擎开发工具包,使开发者可以使用 JavaScript 进行跨平台的代码注入。 - frida-inject-16.3.0:
这是 Frida 用于将代码注入到目标进程的工具,允许用户在运行时将脚本注入到应用程序中。 - frida-portal-16.3.0:
Frida Portal 是一个 Web 界面,用于管理和监控 Frida 实例,方便用户进行远程操作。 - frida-qml-16.3.0:
这是用于 QML (Qt Modeling Language) 应用程序的 Frida 版本,适用于 Qt 开发者。 - frida-server-16.3.0:
Frida Server 是运行在目标设备上的一个服务,使得远程设备可以被 Frida 控制和调试。常用于 Android 和 iOS 设备。 - frida-v16.3.0-electron:
这是一个预编译的 Frida 版本,适用于基于 Electron 框架的应用程序,方便开发者进行调试和分析。 - frida-v16.3.0-node:
这是一个预编译的 Frida 版本,适用于 Node.js 应用程序,帮助开发者在 Node.js 环境中进行动态分析。 - frida_16.3.0_appletvos:
这是 Frida 适用于 Apple TV (tvOS) 的版本,帮助开发者在 Apple TV 上进行应用分析和调试。 - frida_16.3.0_iphoneos:
这是 Frida 适用于 iOS 设备 (如 iPhone、iPad) 的版本,帮助开发者在 iOS 设备上进行应用分析和调试。