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.dex未解密时,1秒轮寻一次,直到可以被 hook 停止:

Java.perform(function() {
    var interval = 1000; // 轮询间隔,单位:毫秒

    function hookWhenAvailable() {
        try {
            var targetClass= Java.use('com.maimemo.android.momo.d0')//改为需要hook的类名
            //重写其中的V 函数
            targetClass.V.implementation = function () {
              var result = this.V();
              console.log('result: ', result);
              return 98654;
            }
        } catch (e) {
            console.log('Target class not available yet, retrying...');
            setTimeout(hookWhenAvailable, interval);
        }
    }

    hookWhenAvailable();
});

8.其他:

获取内部类:

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中的名称,如果是基本类型数组需要左括号加缩写,如果是普通类型,则需要类名全拼。如下:

  1. 基本类型不变:int short char byte boolean float double long
  2. 类型 重载时
    boolean[] [Z
    byte[] [B
    char[] [C
    double[] [D
    float[] [F
    int[] [I
    long[] [J
    short[] [S
  3. 任意类: 完整类名 例如:java.lang.String
  4. 对象数组:加左括号 例如: [java.lang.String

不同的 frida 版本的区别:

  1. frida-clr-16.3.0:
    这是一个用于 .NET/CLR (Common Language Runtime) 平台的 Frida 版本。它允许在 .NET 应用程序中进行动态分析和调试。
  2. frida-core-devkit-16.3.0:
    这是 Frida 核心库的开发工具包,提供了核心 API 的头文件和库,用于开发自定义的 Frida 应用。
  3. frida-gadget-16.3.0:
    Frida Gadget 是一个轻量级的嵌入式版本,适合于将 Frida 集成到现有的应用程序中,特别是移动设备上的应用。
  4. frida-gum-devkit-16.3.0:
    Gum 是 Frida 的低级 API,用于提供跨平台的代码注入功能。这个开发工具包包含了使用 Gum 的必要文件。
  5. frida-gumjs-devkit-16.3.0:
    这是一个基于 Gum 的 JavaScript 引擎开发工具包,使开发者可以使用 JavaScript 进行跨平台的代码注入。
  6. frida-inject-16.3.0:
    这是 Frida 用于将代码注入到目标进程的工具,允许用户在运行时将脚本注入到应用程序中。
  7. frida-portal-16.3.0:
    Frida Portal 是一个 Web 界面,用于管理和监控 Frida 实例,方便用户进行远程操作。
  8. frida-qml-16.3.0:
    这是用于 QML (Qt Modeling Language) 应用程序的 Frida 版本,适用于 Qt 开发者。
  9. frida-server-16.3.0:
    Frida Server 是运行在目标设备上的一个服务,使得远程设备可以被 Frida 控制和调试。常用于 Android 和 iOS 设备。
  10. frida-v16.3.0-electron:
    这是一个预编译的 Frida 版本,适用于基于 Electron 框架的应用程序,方便开发者进行调试和分析。
  11. frida-v16.3.0-node:
    这是一个预编译的 Frida 版本,适用于 Node.js 应用程序,帮助开发者在 Node.js 环境中进行动态分析。
  12. frida_16.3.0_appletvos:
    这是 Frida 适用于 Apple TV (tvOS) 的版本,帮助开发者在 Apple TV 上进行应用分析和调试。
  13. frida_16.3.0_iphoneos:
    这是 Frida 适用于 iOS 设备 (如 iPhone、iPad) 的版本,帮助开发者在 iOS 设备上进行应用分析和调试。