白乐天

道阻且长,行则将至。

FRIDA-JSAPI

Java

perform()

1
2
3
Java.perform(function(){
console.log("This is Java.perform!");
});

use(className)

1
2
3
4
5
6
7
Java.perform(function(){
console.log("This is Java.perform!");
var class = Java.use(className);
class.method.implementation = function(){
console.log("This is class.method!");
};
});

Java.choose(className, callbacks)

1
2
3
4
5
6
7
8
9
10
11
Java.perform(function () {
Java.choose("com.example.SomeClass", {
onMatch: function(instance) {
console.log("找到一个实例: " + instance);
// 可以在这里对 instance 进行操作,比如调用其方法或修改属性
},
onComplete: function() {
console.log("实例枚举完成!");
}
});
});

Java.enumerateLoadedClasses(callbacks)

枚举已加载的类

1
2
3
4
5
6
7
8
9
10
Java.perform(function () {
Java.enumerateLoadedClasses({
onMatch: function (className) {
console.log("Loaded class: " + className);
},
onComplete: function () {
console.log("Class enumeration complete");
}
});
});

Java.enumerateLoadedClassesSync()

枚举当前 Java 虚拟机中已经加载的所有类,返回一个包含所有类名的数组。

1
2
3
4
5
6
7
8
9
Java.perform(function () {
// 获取所有已加载的类
var loadedClasses = Java.enumerateLoadedClassesSync();

// 输出所有类名
loadedClasses.forEach(function(className) {
console.log(className);
});
});

Java.enumerateClassLoaders(callbacks)

枚举所有的类加载器

1
2
3
4
5
6
7
8
9
10
Java.perform(function () {
Java.enumerateClassLoaders({
onMatch: function (loader) {
console.log("Found ClassLoader: " + loader);
},
onComplete: function () {
console.log("ClassLoader enumeration complete");
}
});
});

Java.enumerateClassLoadersSync()

枚举当前 Java 虚拟机中所有活跃的类加载器(ClassLoader)实例,返回一个数组。

1
2
3
4
5
6
7
8
9
10
Java.perform(function () {
// 枚举所有已加载的类加载器
var classLoaders = Java.enumerateClassLoadersSync();

// 遍历并输出每个类加载器的信息
classLoaders.forEach(function(classLoader, index) {
console.log("ClassLoader[" + index + "]: " + classLoader);
// 如果需要,可以调用 classLoader 的方法,或者进一步分析其属性
});
});

Java.enumerateMethods(query)

根据 query 参数筛选符合特定名称模式的类,并返回这些类中的所有方法。

1
2
3
4
5
6
7
Java.enumerateMethods(query)
.then(function(methods) {
// 处理返回的 methods 数组
})
.catch(function(error) {
console.error(error);
});

cast(object, targetClass)

将一个 Java 对象转换为特定的类类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java.perform(function () {
var MainActivity = Java.use("com.example.app.MainActivity");

var someObject = MainActivity.getSomeObject(); // 返回类型可能是 Object
console.log("原始类型: " + someObject.$className);

// 强制转换为 MyClass
var MyClass = Java.use("com.example.app.MyClass");
var castedObject = Java.cast(someObject, MyClass);

console.log("转换后类型: " + castedObject.$className);

// 现在可以调用 MyClass 的方法
castedObject.someMethod();
});

classFactory

Frida 中用于操作 Java 类的一个工厂类,它提供了一系列方法来与 Java 类进行交互。默认情况下,Java.ClassFactory 使用应用程序的主类加载器,但也可以通过指定其他类加载器来创建不同的类工厂实例。

loader

当前的类加载器。Java.use() 依赖它来 Hook Java 类。

array()

用于动态创建 Java 数组对象

1
Java.array(type, elements)

Process

pointerSize

1
Process.pointerSize

以字节为单位获取指针的大小。

getCurrentThreadId()

1
Process.getCurrentThreadId()

获取当前线程Id。

findModuleByName(name)

根据模块名称查找并返回对应的模块对象。

如果没有找到匹配的模块,则返回一个空值(根据具体实现,可能是 nullundefined

1
Process.findModuleByName(name)

Module

Module属性

  • name

    模块名

  • base

    模块基地址

  • size

    模块的字节大小

  • path

    模块的路径

enumerateSymbols()

1
var symbols = ModuleName.enumerateSymbols()

枚举指定模块中的所以符号。

枚举的符号symbol的属性

  • isGlobal

  • type

  • section

  • name

    符号的名字

  • address

    符号的地址,指针

  • size

findExportByName(exportName)

1
var addr = Module.findExportByName(moduleName|null,exportName)

查找指定模块中导出的符号(通常是函数)的地址。

findBaseAddress(name)

1
var addr = Module.findBaseAddress(name)

获取指定模块的基地址。

Memory

protect(address, size, protection)

更新一块内存区域的保护属性,其中 protection 是一个字符串。

1
Memory.protect(address,size,'rwx');

alloc(size)

在目标进程的内存中分配一块指定大小的内存。

1
var ptr = Memory.alloc(size);

allocUtf8String(str)

在目标进程的内存中分配一段 UTF-8 编码的字符串。

1
var ptr = Memory.allocUtf8String(str);

Native

NativePointer

new NativePointer(s)

从包含内存地址的字符串 s 创建一个新的 NativePointer 对象。

字符串可以是十进制的,也可以是以 0x 开头的十六进制表示。

ptr(s)

NativePointer 对象也可以通过ptr(s)这种简写方式进行创建。

toInt32()

将当前 NativePointer 转换为有符号的 32 位整数。

add(rhs), sub(rhs), and(rhs), or(rhs), xor(rhs)

创建一个新的 NativePointer,它是将当前的 NativePointerrhs 相加、相减、按位与、按位或或按位异或后的结果,其中 rhs 可以是一个数字或另一个 NativePointer

readCString([size = -1])

从当前内存位置读取 ASCII字符串的字节。如果知道字符串的字节数,可以提供可选的 size 参数;如果字符串以 NULL 结尾,可以省略该参数或指定为 -1。

如果地址中读取的任何 size / length 字节不可读,将抛出一个 JavaScript 异常。

NativeFunction

  • new NativeFunction(address, returnType, argTypes)

    • address:用NativePointer指定的函数地址。
    • returnType:指定返回值类型。
    • argTypes:一个数组,指定参数类型。
    1
    const FunctionName = new NativeFunction(functionAddr, "int", ["pointer", "pointer", "pointer", "pointer"]);

NativeFunction 用于调用目标进程中指定内存地址处的原生函数。

NativeCallback

  • new NativeCallback(func, returnType, argTypes)

    • func: 创建一个新的由JavaScript function实现的NativeCallback
    • returnType: 指定返回类型。
    • argTypes: 数组,指定参数类型。

    返回的对象是NativeFunction,可以传递给Interceptor.replace

NativeCallback 用于在目标进程中创建一个自定义的原生函数(回调函数),以便在目标进程的上下文中被调用,它的主要用途是模拟或替换目标函数的实现。

1
2
3
Interceptor.replace(functionAddr, new NativeCallback(function () {
console.log("This is a NativeCallback!")
}, "void", []))

Interceptor

Interceptor.attach(target, callbacks)

1
2
3
4
5
6
7
8
9
10
11
var functionAddr = ptr(0x7777);
Interceptor.attach(functionAddr,{
OnEnter:function(args){
console.log(args[0]);
console.log(args[1]);
console.log(args[2]);
},
OnLeave:function(ret){
console.log(ret);
}
});

注意:在32位ARM架构中,地址参数target的最低有效位必须为0(对于ARM函数)或1(对于Thumb函数)。如果通过Frida API(例如Module.getExportByName())获取地址,Frida会自动处理这个细节。

Interceptor.replace(target, replacement)

使用 replacement 替换 target 处的函数。
可以使用 NativeCallback 在 JavaScript 中实现替换。

1
2
3
Interceptor.replace(functionAddr, new NativeCallback(function () {
console.log("This is a NativeCallback!")
}, "void", []))

Stalker

Stalker是基于动态重新编译的代码跟踪器。 它将代码指令复制到内存中的另一个位置,在该位置对其进行调整以适应新位置并包括其他跟踪指令。 如果应用程序在原始位置检查其代码,则会发现该代码是完整无缺的,因为它是被篡改的代码的副本。

follow()

1
2
3
Stalker.follow(threadId, options);
// threadId 要追踪的线程 ID。可通过 Process.getCurrentThreadId() 或其他方式获取目标线程 ID。
// options 一个对象,用于配置追踪时的行为。

Frida 对目标线程进行监控,捕获它执行过程中发生的事件(例如函数调用、返回、基本块边界等),从而进行动态分析、调试或者行为记录。

options详解

options 参数是一个对象,可以配置多种选项来控制跟踪行为。

options 参数的详细说明:

transform

自定义代码转换函数,允许对跟踪的代码进行动态修改(如插入日志、过滤指令等)。

1
2
3
4
5
6
7
8
9
10
11
Stalker.follow(tid, {
transform: function(iterator) {
while (true){
const instruction = iterator.next()
if (instruction === null){
break;
}
iterator.keep();
}
}
});

优化版本

1
2
3
4
5
6
7
8
9
10
Stalker.follow(tid, {
transform: function(iterator) {
// 使用 while 循环,直接判断 iterator.next() 的返回值
let instruction;
while ((instruction = iterator.next()) !== null) {
// 对每条指令执行保留操作,不进行修改
iterator.keep();
}
}
});

unfollow()

终止在该线程上通过 Stalker.follow() 启动的追踪操作。

flush()

刷新缓冲区。

garbageCollect()

用于垃圾回收和清理资源,释放由 Stalker 模块在追踪过程中使用的内存和系统资源。

CPU Instruction

Instruction

Instruction.parse(target)

解析内存中 target 地址处的指令,返回一个Instruction对象。

Instruction对象包含如下字段:

  • address: 此指令的地址(EIP),类型为 NativePointer
  • next: 指向下一条指令的指针,您可以使用 parse() 解析它
  • size: 此指令的大小
  • mnemonic: 指令助记符的字符串表示
  • opStr: 指令操作数的字符串表示
  • operands: 描述每个操作数的对象数组,每个对象至少指定类型和值,但可能还包含取决于架构的其他属性
  • regsRead: 此指令隐式读取的寄存器名称数组
  • regsWritten: 此指令隐式写入的寄存器名称数组
  • groups: 此指令所属的组名称数组
  • toString(): 转换为人类可读的字符串

Arm64Writer

new Arm64Writer(codeAddress[, { pc: ptr(‘0x1234’) }])

创建一个用于生成 ARM 机器码的代码写入器,该代码会直接写入 codeAddress 指定的内存位置,codeAddress 是一个 NativePointer 类型,第二个参数是可选的选项对象,可以通过其中的 pc 属性指定初始程序计数器(Program Counter, PC)。

返回一个ArmWriter对象。

dispose()

释放 Arm64Writer 对象,清理内存。

flush()

用于将已生成或修改的指令刷新到内存中,以确保这些指令可以被正确执行。

putRet()

在生成的代码末尾插入一个返回指令,确保动态生成的代码块或函数能够正确返回。