Frida-JSAPI

Runtime information

Frida

  • Frida.version
    包含当前 Frida 版本的属性,类型为字符串。
  • Frida.heapSize
    动态属性,包含 Frida 私有堆的当前大小,由所有脚本和 Frida 自身的运行时共享。

Process

  • Process.id
    包含进程 PID 的属性,类型为数字。
  • Process.arch
    包含架构信息的属性,类型为字符串,可能的值为 ia32x64armarm64
  • Process.platform
    包含平台信息的属性,类型为字符串,可能的值为 windowsdarwinlinuxqnx
  • Process.pageSize
    包含虚拟内存页大小的属性(以字节为单位),类型为数字。这个属性用于使你的脚本更具可移植性。
  • Process.pointerSize
    包含指针大小的属性(以字节为单位),类型为数字。这个属性用于使你的脚本更具可移植性。
  • Process.codeSigningPolicy
    包含代码签名策略的属性,类型为字符串,可能的值为 optionalrequiredrequired 意味着 Frida 将避免修改内存中的现有代码,并且不会尝试运行未签名的代码。
    目前,除非使用 Gadget 并配置为假设需要代码签名,否则此属性将始终设置为 optional。此属性允许你确定是否可以使用 Interceptor API,以及是否可以安全地修改代码或运行未签名的代码。
  • Process.mainModule
    包含表示进程主可执行文件的模块的属性。
  • Process.getCurrentDir()
    返回一个字符串,指定当前工作目录的文件系统路径。
  • Process.getHomeDir()
    返回一个字符串,指定当前用户主目录的文件系统路径。
  • Process.getTmpDir()
    返回一个字符串,指定用于临时文件的目录的文件系统路径。
  • Process.isDebuggerAttached()
    返回一个布尔值,指示当前是否有调试器附加。
  • Process.getCurrentThreadId()
    获取该线程的操作系统特定 ID,类型为数字。
  • Process.enumerateThreads()
    枚举所有线程,返回一个包含以下属性的对象数组:
    • id: 操作系统特定的 ID。
    • state: 字符串,指定线程状态,可能的值为 running(运行中)stopped(已停止)waiting(等待中)、uninterruptible(不可中断)halted(已终止)
    • context: 包含 pc 和 sp 键的对象,这些键是 NativePointer 对象,分别指定 EIP/RIP/PC 和 ESP/RSP/SP(用于 ia32/x64/arm)。也可以使用其他处理器特定的键,例如 eax、rax、r0、x0 等。
  • Process.findModuleByAddress(address), Process.getModuleByAddress(address), Process.findModuleByName(name), Process.getModuleByName(name)
    返回一个与指定地址或名称匹配的模块(Module)。如果找不到这样的模块,以 find 开头的函数返回 null,而以 get 开头的函数则抛出异常。
  • Process.enumerateModules()
    枚举当前加载的模块,返回一个模块(Module)对象的数组。
  • Process.findRangeByAddress(address), getRangeByAddress(address)
    返回一个对象,包含指定地址所在内存范围的详细信息。如果找不到这样的范围,findRangeByAddress() 返回 null,而 getRangeByAddress() 则抛出异常。

Thread

  • Thread.backtrace([context, backtracer])
    为当前线程生成一个调用栈回溯,返回一个 NativePointer 对象的数组。
    如果在 InterceptoronEnteronLeave 回调中调用此函数,应为可选的 context 参数提供 this.context,这样可以生成更准确的调用栈回溯。
    可选的 backtracer 参数指定要使用的回溯器类型,必须是 Backtracer.FUZZY 或 Backtracer.ACCURATE,如果未指定,默认使用后者。
    1
    console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
  • Thread.sleep(delay)
    暂停当前线程的执行,暂停时间由 delay 指定,以秒为单位。

Module

属性

  • name:模块的规范名称,类型为字符串
  • base: 基址,类型为 NativePointer
  • size: 模块大小,以字节为单位
  • path: 文件系统中的完整路径,类型为字符串

方法

  • enumerateImports()
    枚举模块导入表,返回一个包含以下属性的对象数组:
    • type: 字符串,指定为 functionvariable
    • name: 导入名称,类型为字符串
    • module: 模块名称,类型为字符串
    • address: 绝对地址,类型为 NativePointer
    • slot: 存储导入的内存位置,类型为 NativePointer
  • enumerateExports()
    枚举模块导出表,返回一个包含以下属性的对象数组:
    • type: 字符串,指定为 functionvariable
    • name: 导出名称,类型为字符串
    • address: 绝对地址,类型为 NativePointer
  • enumerateSymbols()
    枚举模块符号表,返回一个包含以下属性的对象数组:
    • isGlobal: 布尔值,指定符号是否是全局可见的
    • type: 字符串,指定以下类型之一:
      • unknown(未知)
      • section(节)
      • undefined(未定义,Mach-O 格式)
      • absolute(绝对地址,Mach-O 格式)
      • prebound-undefined(预绑定未定义,Mach-O 格式)
      • indirect(间接,Mach-O 格式)
      • object(对象,ELF 格式)
      • function(函数,ELF 格式)
      • file(文件,ELF 格式)
      • common(公共,ELF 格式)
      • tls(线程本地存储,ELF 格式)
    • section: 如果存在,则是一个对象,包含以下属性:
      • id: 字符串,包含节索引、段名称(如果适用)和节名称,格式与 r2 的节 ID 相同
      • protection: 与 Process.enumerateRanges() 中的保护属性相同
    • name: 符号名称,类型为字符串
    • address: 绝对地址,类型为 NativePointer
    • size: 如果存在,为一个数字,指定符号的大小,以字节为单位

Memory

  • Memory.alloc(size[, options])
    在堆上分配 size 字节的内存,或者,如果 sizeProcess.pageSize 的倍数,则分配由操作系统管理的一个或多个原始内存页。
    当使用页面粒度分配时,如果需要将内存分配在接近某个地址的位置,可以通过指定 { near: address, maxDistance: distanceInBytes } 作为可选的 options 对象。
    返回值是一个 NativePointer 对象,并且当所有指向该内存的 JavaScript 句柄消失后,底层内存将被释放。
    这意味着在代码中使用该指针时,需要保留对它的引用,以防止它在 JavaScript 运行时之外的代码使用时被垃圾回收。

  • Memory.protect(address, size, protection)
    更新一块内存区域的保护属性,其中 protection 是一个字符串。

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

    返回一个布尔值,指示操作是否成功完成。

  • Memory.allocUtf8String(str), Memory.allocUtf16String(str), Memory.allocAnsiString(str)
    在堆上分配、编码并写入 str 作为 UTF-8/UTF-16/ANSI 字符串。
    返回的对象是一个 NativePointer。

  • Memory.patchCode(address, size, apply)
    安全地修改由 address 指定的内存位置(类型为 NativePointer)处的size大小的字节。提供的 JavaScript 函数 apply 会被调用,并传入一个可写指针,在返回之前,你必须在该指针处写入所需的修改。

    • address:指定要修改的内存区域的起始地址。
    • size:指定要修改的内存区域的大小(通常以字节为单位)。
    • apply:指定要应用的修改内容。
      1
      2
      3
      4
      5
      6
      7
      8
      Memory.patchCode(address,size,function(code){
      var writer =new Arm64Writer(code);
      writer.putNop();
      console.log(Instruction.parse(add));
      console.log(writer.base);
      console.log(writer.code);
      console.log(writer.pc);
      })

DebugSymbol

  • DebugSymbol.fromAddress(address), DebugSymbol.fromName(name)
    查找地址/名称的调试信息,并将其作为包含以下属性的对象返回:
    • address: 符号对应的地址,作为 NativePointer。
    • name: 符号的名称,作为字符串,如果未知则为 null。
    • moduleName: 拥有这个符号的模块名称,作为字符串,如果未知则为 null。
    • fileName: 拥有这个符号的文件名称,作为字符串,如果未知则为 null。
    • lineNumber: 在 fileName 中的行号,作为数字,如果未知则为 null。

Data Types, Function and Callback

NativePointer

  • new NativePointer(s)
    从包含内存地址的字符串 s 创建一个新的 NativePointer 对象。
    字符串可以是十进制的,也可以是以 0x 开头的十六进制表示。
    可以使用 ptr(s) 的简写形式来创建。

  • isNull()
    返回一个布尔值,方便检查指针是否为 NULL。

  • add(rhs), sub(rhs), and(rhs), or(rhs), xor(rhs)
    创建一个新的 NativePointer,其值为当前 NativePointer 加上/减去/与/或/异或 rhs,其中 rhs 可以是一个数字或另一个 NativePointer

  • shr(n), shl(n)
    创建一个新的 NativePointer,其值为当前 NativePointer 向右/向左移位 n 位。

  • not()
    创建一个新的 NativePointer,其值为当前 NativePointer 的按位取反。

  • sign([key, data])
    通过对当前 NativePointer 的位添加指针认证位,创建一个签名指针。
    如果当前进程不支持指针认证,这个操作无效,返回当前 NativePointer 而不是新的值。
    可选的 key 参数可以指定为字符串。支持的值包括:

    • iaIA 密钥,用于签署代码指针。这是默认值。
    • ibIB 密钥,用于签署代码指针。
    • daDA 密钥,用于签署数据指针。
    • dbDB 密钥,用于签署数据指针。

    data 参数也可以指定为 NativePointer/数字类值,用于为签名提供额外的数据,默认值为 0。

  • strip([key])
    通过移除指针认证位,从当前 NativePointer 创建一个原始指针。如果当前进程不支持指针认证,这个操作无效,返回当前 NativePointer 而不是新的值。可选的 key 参数可以指定用于签名指针的密钥,默认值为 ia。

  • blend(smallInteger)
    通过将当前 NativePointer 的位与常量混合,创建一个新的 NativePointer,该常量可作为数据传递给 sign()。

  • equals(rhs)
    返回一个布尔值,指示 rhs 是否与当前指针相等,即它们的指针值是否相同。

  • compare(rhs)
    返回一个整数比较结果,类似于 String#localeCompare()。

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

  • toString([radix = 16])
    将当前 NativePointer 转换为字符串,使用可选的 radix 进制(默认值为 16)。

  • toMatchPattern()
    返回一个字符串,包含当前指针的原始值的 Memory.scan() 兼容匹配模式。

  • readPointer()
    从当前内存位置读取一个 NativePointer。
    如果地址不可读,将抛出一个 JavaScript 异常。

  • writePointer(ptr)
    将 ptr 写入当前内存位置。
    如果地址不可写,将抛出一个 JavaScript 异常。

  • readS8(), readU8(), readS16(), readU16(), readS32(), readU32(), readShort(), readUShort(), readInt(), readUInt(), readFloat(), readDouble()
    从当前内存位置读取一个有符号或无符号的 8/16/32 位整数,或 float/double 值,并将其作为数字返回。
    如果地址不可读,将抛出一个 JavaScript 异常。

  • writeS8(value), writeU8(value), writeS16(value), writeU16(value), writeS32(value), writeU32(value), writeShort(value), writeUShort(value), writeInt(value), writeUInt(value), writeFloat(value), writeDouble(value)
    将有符号或无符号的 8/16/32 位整数,或 float/double 值写入当前内存位置。
    如果地址不可写,将抛出一个 JavaScript 异常。

  • readS64(), readU64(), readLong(), readULong() 从当前内存位置读取一个有符号或无符号的 64 位或长整型值,并将其作为 Int64/UInt64 值返回。
    如果地址不可读,将抛出一个 JavaScript 异常。

  • writeS64(value), writeU64(value), writeLong(value), writeULong(value)
    将 Int64/UInt64 值写入当前内存位置。
    如果地址不可写,将抛出一个 JavaScript 异常。

  • readByteArray(length)
    从当前内存位置读取 length 个字节,并将其返回为 ArrayBuffer。此缓冲区可以通过 send() 的第二个参数有效地传输到基于 Frida 的应用程序中。
    如果地址中读取的任何字节不可读,将抛出一个 JavaScript 异常。

  • writeByteArray(bytes)
    将 bytes 写入当前内存位置,其中 bytes 可以是 ArrayBuffer(通常由 readByteArray() 返回)或由 0 到 255 之间的整数组成的数组。例如:[ 0x13, 0x37, 0x42 ]。
    如果地址中写入的任何字节不可写,将抛出一个 JavaScript 异常。

  • readCString([size = -1]), readUtf8String([size = -1]), readUtf16String([length = -1]), readAnsiString([size = -1])
    从当前内存位置读取 ASCII、UTF-8、UTF-16 或 ANSI 字符串的字节。如果知道字符串的字节数,可以提供可选的 size 参数;如果字符串以 NULL 结尾,可以省略该参数或指定为 -1。同样,如果知道字符串的字符数,可以提供可选的 length 参数。
    如果地址中读取的任何 size / length 字节不可读,将抛出一个 JavaScript 异常。
    注意:readAnsiString() 仅在 Windows 上可用(且相关)。

  • writeUtf8String(str), writeUtf16String(str), writeAnsiString(str)
    将 JavaScript 字符串编码并写入当前内存位置(带有 NULL 终止符)。
    如果地址中写入的任何字节不可写,将抛出一个 JavaScript 异常。
    注意:writeAnsiString() 仅在 Windows 上可用(且相关)。

NativeFunction

  • new NativeFunction(address, returnType, argTypes[, abi])
    创建一个新的 NativeFunction,以调用位于指定地址(由 NativePointer 指定)的函数。
    • address:指定地址
    • returnType:指定返回类型
    • argTypes[, abi]argTypes 数组指定参数类型。
      还可以选择性地指定abi,如果没有指定,则使用系统默认的abi。
      对于可变参数函数,在固定参数和可变参数之间添加一个 ... 条目到 argTypes 中。
  • Structs & Classes by Value(按值传递的结构体和类)
    对于按值传递的结构体或类,不是提供一个字符串,而是提供一个包含结构体字段类型的数组,这些字段类型是按顺序排列的。你可以按需要深度嵌套这些数组,以表示嵌套结构体。注意,返回的对象也是一个 NativePointer,因此可以传递给 Interceptor#attach。
    这必须与结构体/类完全匹配,所以如果你有一个包含三个 int 的结构体,你必须传递 [‘int’, ‘int’, ‘int’]。
    对于具有虚方法的类,第一个字段将是指向虚函数表(vtable)的指针。
    对于 C++ 中涉及返回值大于 Process.pointerSize 的情况,典型的 ABI 可能会要求传入一个指向预分配空间的 NativePointer 作为第一个参数。(这种情况在 WebKit 中很常见。)
  • Supported Types
    • void
    • pointer
    • int
    • uint
    • long
    • ulong
    • char
    • uchar
    • size_t
    • ssize_t
    • float
    • double
    • int8
    • uint8
    • int16
    • uint16
    • int32
    • uint32
    • int64
    • uint64
    • bool
  • Supported ABIs
    • default
    • Windows 32-bit:
      sysv
      stdcall
      thiscall
      fastcall
      mscdecl
    • Windows 64-bit:
      win64
    • UNIX x86:
      sysv
      unix64
    • UNIX ARM:
      sysv
      vfp
  • new NativeFunction(address, returnType, argTypes[, options])
    类似于之前的构造函数,但是第四个参数 options 是一个对象,可以包含以下一个或多个键:
    • abi:与上面相同的枚举类型。

    • scheduling:调度行为,作为字符串指定。支持的值有:

      • cooperative:在调用本地函数时允许其他线程执行 JavaScript 代码,即在调用前释放锁,并在调用后重新获取锁。这是默认行为。
      • exclusive:在调用本地函数时不允许其他线程执行 JavaScript 代码,即保持持有 JavaScript 锁。这种方式更快,但可能导致死锁。
    • exceptions:异常行为,作为字符串指定。支持的值有:

      • steal:如果被调用的函数产生了本地异常(例如,通过解引用无效指针),Frida 将展开堆栈并捕获异常,将其转化为可以处理的 JavaScript 异常。这可能会导致应用程序处于未定义的状态,但在实验时避免进程崩溃非常有用。这是默认行为。
      • propagate:让应用程序处理在函数调用期间发生的任何本地异常。(或者由 Process.setExceptionHandler() 安装的处理程序来处理。)
    • traps:需要启用的代码陷阱,作为字符串指定。支持的值有:

      • default:如果函数调用触发了任何钩子,Interceptor.attach() 回调将会被调用。
      • all:除了 Interceptor 回调外,在每个函数调用期间还可以临时重新激活 Stalker。这对例如测量代码覆盖率、指导模糊测试、在调试器中实现“步入”等功能非常有用。请注意,当使用 Java 和 ObjC API 时也可以实现此功能,因为方法包装器也提供 clone(options) API,用于创建具有自定义 NativeFunction 选项的新方法包装器。

NativeCallback

  • new NativeCallback(func, returnType, argTypes[, abi])

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

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

Instrumentation

通过动态插入代码来监控和收集程序的运行信息。

Interceptor

  • Interceptor.attach(target, callbacks[, data])
    拦截对target函数的调用。
    • target
      target是一个NativePointer,用于指定你想要拦截的函数的地址。
      在32位ARM架构中,该地址的最低有效位必须为0(对于ARM函数)或1(对于Thumb函数)。如果通过Frida API(例如Module.getExportByName())获取地址,Frida会自动处理这个细节。
    • callbacks
      callbacks
      参数是一个包含一个或多个回调函数的对象。
      • onEnter(args)
        回调函数,接收一个参数args,它是一个NativePointer对象数组,可以用它来读取或写入函数的参数。
      • onLeave(retval)
        回调函数,接收一个参数retval,它是一个基于NativePointer的对象,包含了函数的原始返回值。
        你可以调用retval.replace(1337)将返回值替换为整数1337,或者调用retval.replace(ptr(“0x1234”))将返回值替换为一个指针。
    • 该对象包含一些属性
      • returnAddress
        返回地址,类型为NativePointer
      • context
        一个对象,包含键pc和sp,它们是NativePointer对象,分别指定ia32/x64/arm架构下的EIP/RIP/PC和ESP/RSP/SP。
      • errno
        (UNIX系统中)当前的errno值(你可以替换它)。
      • lastError
        (Windows系统中)当前的操作系统错误值(你可以替换它)。
      • threadId
        操作系统的线程ID。
      • depth
        相对于其他调用的调用深度。
  • Interceptor.replace(target, replacement[, data])
    使用 replacement 替换 target 处的函数。
    可以使用 NativeCallback 在 JavaScript 中实现替换。

Java

  • Java.perform(fn)
    确保当前线程已连接到虚拟机并调用函数fn。

  • Java.use(className)
    动态获取一个 JavaScript 包装器(wrapper),用于指定的类名(className)。
    通过调用 $new() 可以从这个包装器中实例化对象,以调用构造函数。
    通过调用 $dispose() 可以显式地清理一个实例(或者可以等待 JavaScript 对象被垃圾回收,或者等待脚本被卸载)。
    可以使用静态和非静态方法,甚至可以替换方法的实现,并从中抛出异常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 被动调用
    setImmediate(function (){
    Java.perform(function (){
    var Class = Java.use("className");
    Class.method.implementation=function (args){
    console.log("args:",args);
    var result = this.method(args);
    console.log("result:",result);
    return result;
    }
    })
    })
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 主动调用 
    setImmediate(function (){
    Java.perform(function (){
    var Class = Java.use("className");
    var result = Class.method(args);
    console.log("result:",result);
    })
    })

    // 主动调用实例方法
    setImmediate(function (){
    Java.perform(function (){
    var Class = Java.use("className");
    var ClassObj = Class.$new();
    var result = ClassObj.method(args);
    console.log("result:",result);
    })
    })
  • Java.choose(className, callbacks)
    通过扫描 Java 堆枚举指定类名(className)的活动实例。

    • callbacks是一个指定回调函数的对象:
      • onMatch(instance):
        当找到一个活动实例时调用,该实例已经准备好使用,就像你通过 Java.cast() 将原始句柄转换为该特定实例一样。
      • onComplete():
        当所有实例都被枚举完成后调用。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        setImmediate(function (){
        Java.perform(function (){
        Java.choose("ClassName",{
        onMatch:function (obj){
        console.log(obj.method());
        },
        onComplete:function (){
        console.log("over!")

        }
        })
        })
        })

CPU Instruction

Instruction

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

Arm64Writer

创建一个新的代码写入器,用于生成直接写入内存的 AArch64 机器代码

构造函数

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

  • codeAddress: 代码将被写入的内存地址,指定为 NativePointer 类型。
  • pc (可选): 初始程序计数器,当在临时缓冲区中生成代码时非常有用。

方法

  • reset(codeAddress[, { pc: ptr(‘0x1234’) }])
    回收实例,重新初始化 Arm64Writer 实例。
  • dispose()
    立即清理内存。
  • flush()
    解析标签引用并将待处理的数据写入内存。在生成代码完成后,应始终调用此方法。在一次生成多个函数时,通常也需要在不相关的代码片段之间调用此方法。

属性

  • base
    输出的第一个字节的内存位置,作为 NativePointer 类型。
  • code
    下一个字节的输出内存位置,作为 NativePointer 类型。
  • pc
    下一个字节的输出程序计数器,作为 NativePointer 类型。
  • offset
    当前偏移量,作为 JavaScript 数字类型。

代码生成方法

  • skip(nBytes)
    跳过指定字节数。
  • putRet()
    生成一个 RET 指令。
  • putNop()
    生成一个NOP指令

Hexdump

  • hexdump(target[, options])
    从提供的 ArrayBuffer 或 NativePointer 目标生成十六进制转储,可选择自定义输出选项。
    • target:
      提供的NativePointer对象

    • options:

      1
      2
      3
      4
      5
      6
      7
      const libc = Module.findBaseAddress('libc.so');
      console.log(hexdump(libc, {
      offset: 0, //偏移
      length: 64, //输出长度,必须是十进制
      header: true, //头
      ansi: true //颜色
      }));