返回介绍

JavaScript API 1

发布于 2025-01-02 22:31:48 字数 17044 浏览 0 评论 0 收藏 0

目录

  1. Global
  2. console
  3. rpc
  4. Frida
  5. Process
  6. Module
  7. ModuleMap
  8. Memory
  9. MemoryAccessMonitor
  10. Thread
  11. Int64
  12. UInt64
  13. NativePointer
  14. NativeFunction
  15. NativeCallback
  16. SystemFunction
  17. Socket
  18. SocketListener
  19. SocketConnection
  20. IOStream
  21. InputStream
  22. OutputStream
  23. UnixInputStream
  24. UnixOutputStream
  25. Win32InputStream
  26. Win32OutputStream
  27. File
  28. SqliteDatabase
  29. SqliteStatement
  30. Interceptor
  31. Stalker
  32. ApiResolver
  33. DebugSymbol
  34. Instruction
  35. ObjC
  36. Java
  37. WeakRef
  38. X86Writer
  39. X86Relocator
  40. X86_enum_types
  41. ArmWriter
  42. ArmRelocation
  43. ThumbWriter
  44. ThumbRelocator
  45. ARM_enum_types
  46. Arm64Writer
  47. Arm64Relocator
  48. AArch64_enum_types
  49. MipsWriter
  50. MipsRelocator
  51. Mips_enum_types

Global

  • hexdump(target[, options]): 把一个 ArrayBuffer 或者 NativePointer 的 target 变量,附加一些 options 属性,按照指定格式进行输出,比如:
  • int64(v): new Int64(v) 的缩写格式
  • uint64(v): new UInt64(v) 的缩写格式
  • ptr(s): new NativePointer(s) 的缩写格式
  • NULL ptr("0") 的缩写格式
  • recv([type, ]callback): 注册一个回调,当下次有消息到来的时候会收到回调消息,可选参数 type 相当于一个过滤器,表示只接收这种类型的消息。 需要注意的一点是 , 这个消息回调是一次性的, 收到一个消息之后,如果需要继续接收消息,那就需要重新调用一个 recv
  • send(message[, data]): 从目标进程中往主控端发 message (必须是可以序列换成 Json 的),如果你还有二进制数据需要附带发送(比如使用 Memory.readByteArray 拷贝的内存数据),就把这个附加数据填入 data 参数,但是有个要求,就是 data 参数必须是一个 ArrayBuffer 或者是一个整形数组(数值是 0-255)
  • setTimeout(fn, delay): 在延迟 delay 毫秒之后,调用 fn ,这个调用会返回一个 ID,这个 ID 可以传递给 clearTimeout 用来进行调用取消。
  • clearTimeout(id): 取消通过 setTimeout 发起的延迟调用
  • setInterval(fn, delay): 每隔 delay 毫秒调用一次 fn ,返回一个 ID,这个 ID 可以传给 clearInterval 进行调用取消。
  • clearInterval(id): 取消通过 setInterval 发起的调用

console

  • console.log(line), console.warn(line), console.error(line): 向标准输入输出界面写入 line 字符串。 比如:使用 Frida-Python 的时候就输出到 stdout 或者 stderr ,使用 frida-qml 的时候则输出到 qDebug ,如果输出的是一个 ArrayBuffer,会以默认参数自动调用 hexdump 进行格式化输出。

rpc

  • rpc.exports: 可以在你的程序中导出一些 RPC-Style API 函数,Key 指定导出的名称,Value 指定导出的函数,函数可以直接返回一个值,也可以是异步方式以 Promise 的方式返回,举个例子:
  • 对于 Python 主控端可以使用下面这样的脚本使用导出的函数:
  • 在上面这个例子里面,我们使用 script.on('message', on_message) 来监控任何来自目标进程的消息,消息监控可以来自 scriptsession 两个方面,比如,如果你想要监控目标进程的退出,可以使用下面这个语句 session.on('detached', my_function)

Frida

  • Frida.version: 包含当前 Frida 的版本信息

Process

  • Process.arch: CPU 架构信息,取值范围: ia32、x64、arm、arm64
  • Process.platform: 平台信息,取值范围: windows、darwin、linux、qnx
  • Process.pageSize: 虚拟内存页面大小,主要用来辅助增加脚本可移植性
  • Process.pointerSize: 指针占用的内存大小,主要用来辅助增加脚本可移植性
  • Process.codeSigningPolicy: 取值范围是 optional 或者 required ,后者表示 Frida 会尽力避免修改内存中的代码,并且不会执行未签名的代码。默认值是 optional ,除非是在 Gadget 模式下通过配置文件来使用 required ,通过这个属性可以确定 Interceptor API 是否有限制,确定代码修改或者执行未签名代码是否安全。( 译者注:这个目前没有实验清楚,可以参考原文 )
  • Process.isDebuggerAttached(): 确定当前是否有调试器附加
  • Process.getCurrentThreadId(): 获取当前线程 ID
  • Process.enumerateThreads(callbacks): 枚举所有线程,每次枚举到一个线程就执行回调类 callbacks:
    • onMatch: function(thread): 当枚举到一个线程的时候,就调用这个函数,其中 thread 参数包含 :
      1. id,线程 ID
      2. state,线程状态,取之范围是 running, stopped, waiting, uninterruptible, halted
      3. context, 包含 pc, sp ,分别代表 EIP/RIP/PC 和 ESP/RSP/SP,分别对应于 ia32/x64/arm 平台,其他的寄存器也都有,比如 eax, rax, r0, x0 等。
      4. 函数可以直接返回 stop 来停止枚举。
    • onComplete: function(): 当所有的线程枚举都完成的时候调用。
  • Process.enumerateThreadSync(): enumerateThreads() 的同步版本,返回线程对象数组
  • Process.findModuleByAddress(address), Process.getModuleByAddress(address), Process.findModuleByName(name), Process.getModuleByName(name): 根据地址或者名称来查找模块,如果找不到这样的模块,find 开头的函数返回 null ,get 开头的函数会抛出异常。
  • Process.enumerateModules(callbacks): 枚举已经加载的模块,枚举到模块之后调用回调对象:
    • onMatch: function(module): 枚举到一个模块的时候调用,module 对象包含如下字段:
      1. name, 模块名
      2. base, 基地址
      3. size,模块大小
      4. path,模块路径
      5. 函数可以返回 stop 来停止枚举 。
    • onComplete: function(): 当所有的模块枚举完成的时候调用。
  • Process.enumerateModulesSync(): enumerateModules() 函数的同步版本,返回模块对象数组
  • Process.findRangeByAddress(address), Process.getRangeByAddress(address): 返回一个内存块对象, 如果在这个 address 找不到内存块对象,那么 findRangeByAddress() 返回 nullgetRangeByAddress 则抛出异常。
  • Process.numerateRanges(protection | specifier, callbacks): 枚举指定 protection 类型的内存块,以指定形式的字符串给出: rwx ,而 rw- 表示最少是可读可写,也可以用分类符,里面包含 protection 这个 Key,取值就是前面提到的 rwx,还有一个 coalesce 这个 Key,表示是否要把位置相邻并且属性相同的内存块合并给出结果,枚举过程中回调 callbacks 对象:
    • onMatch: function(range): 每次枚举到一个内存块都回调回来,其中 Range 对象包含如下属性:
      1. base:基地址
      2. size:内存块大小
      3. protection:保护属性
      4. file:(如果有的话)内存映射文件: 4.1 path,文件路径 4.2 offset,文件内偏移
      5. 如果要停止枚举过程,直接返回 stop 即可
    • onComplete: function(): 所有内存块枚举完成之后会回调
  • Process.enumerateRangesSync(protection | specifier): enumerateRanges() 函数的同步版本,返回内存块数组
  • Process.enumerateMallocRanges(callbacks): 用于枚举在系统堆上申请的内存块
  • Process.enumerateMallocRangesSync(protection): Process.enumerateMallocRanges() 的同步版本
  • Process.setExceptionHandler(callback): 在进程内安装一个异常处理函数(Native Exception),回调函数会在目标进程本身的异常处理函数之前调用 ,回调函数只有一个参数 details ,包含以下几个属性:
    • type ,取值为下列之一:
      1. abort
      2. access-violation
      3. guard-page
      4. illegal-instruction
      5. stack-overflow
      6. arithmetic
      7. breakpoint
      8. single-step
      9. system
    • address ,异常发生的地址,NativePointer
    • memory ,如果这个对象不为空,则会包含下面这些属性:
      1. operation: 引发一场的操作类型,取值范围是 read, write 或者 execute
      2. address: 操作发生异常的地址,NativePointer
    • context ,包含 pcsp 的 NativePointer,分别代表指令指针和堆栈指针
    • nativeContext ,基于操作系统定义的异常上下文信息的 NativePointer,在 context 里面的信息不够用的时候,可以考虑用这个指针,但是一般不建议使用( 译者注:估计是考虑到可移植性或者稳定性
    • 捕获到异常之后,怎么使用就看你自己了,比如可以把异常信息写到日志里面,然后发送个信息给主控端,然后同步等待主控端的响应之后处理,或者直接修改异常信息里面包含的寄存器的值,尝试恢复掉异常,继续执行。如果你处理了异常信息,那么这个异常回调里面你要返回 true ,Frida 会把异常交给进程异常处理函数处理,如果到最后都没人去处理这个异常,就直接结束目标进程。

Module

  • Module.emuerateImports(name, callbacks): 枚举模块 name 的导入表,枚举到一个导入项的时候回调 callbacks, callbacks 包含下面 2 个回调:
    • onMatch: function(imp): 枚举到一个导入项到时候会被调用, imp 包含如下的字段:
      1. type ,导入项的类型, 取值范围是 function 或者 variable
      2. name ,导入项的名称
      3. module ,模块名称
      4. address ,导入项的绝对地址
      5. 以上所有的属性字段,只有 name 字段是一定会有,剩余的其他字段不能保证都有,底层会尽量保证每个字段都能给出数据,但是不能保证一定能拿到数据,onMatch 函数可以返回字符串 stop 表示要停止枚举。
    • onComplete: function(): 当所有的导入表项都枚举完成的时候会回调
  • Module.eumerateImportsSync(name): enumerateImports() 的同步版本
  • Module.emuerateExports(name, callbacks): 枚举指定模块 name 的导出表项,结果用 callbacks 进行回调:
    • onMatch: function(exp): 其中 exp 代表枚举到的一个导出项,包含如下几个字段:
      1. type ,导出项类型,取值范围是 function 或者 variable
      2. name ,导出项名称
      3. address ,导出项的绝对地址,NativePointer
      4. 函数返回 stop 的时候表示停止枚举过程
    • onComplete: function(): 枚举完成回调
  • Module.enumerateExportsSync(): Module.enumerateExports() 的同步版本
  • Module.enumerateSymbols(name, callbacks): 枚举指定模块中包含的符号,枚举结果通过回调进行通知:
    • onMatch: function(sym): 其中 sym 包含下面几个字段:
      • isGlobal ,布尔值,表示符号是否全局可见
      • type ,符号的类型,取值是下面其中一种:
        1. unknown
        2. undefined
        3. absolute
        4. section
        5. prebound-undefined
        6. indirect
      • section ,如果这个字段不为空的话,那这个字段包含下面几个属性:
        1. id ,小节序号,段名,节名
        2. protection ,保护属性类型, rwx 这样的属性
      • name ,符号名称
      • address ,符号的绝对地址,NativePointer
      • 这个函数返回 stop 的时候,表示要结束枚举过程
    • Module.enumerateSymbolsSync(name): Module.enumerateSymbols() 的同步版本
  • Module.enumerateRanges(name, protection, callbacks): 功能基本等同于 Process.enumerateRanges() ,只不过多了一个模块名限定了枚举的范围
  • Module.enumerateRangesSync(name, protection): Module.enumerateRanges() 的同步版本
  • Module.findBaseAddress(name): 获取指定模块的基地址
  • Module.findExportByName(module | null, exp): 返回模块 module 内的导出项的绝对地址,如果模块名不确定,第一个参数传入 null,这种情况下会增大查找开销,尽量不要使用。

ModuleMap

  • new ModuleMap([filter]): 可以理解为内存模块快照,主要目的是可以作为一个模块速查表,比如你可以用这个快照来快速定位一个具体的地址是属于哪个模块。创建 ModuleMap 的时候,就是对目标进程当前加载的模块的信息作一个快照,后续想要更新这个快照信息的时候,可以使用 update 进行更新。 这个 filter 参数是可选的,主要是用来过滤你关心的模块,可以用来缩小快照的范围( 注意:filter 是过滤函数,不是字符串参数 ),为了让模块进入这个快照里,过滤函数的返回值要设置为 true,反之设为 false,如果后续内存中的模块加载信息更新了, 还会继续调用这个 filter 函数。
  • has(address): 检查 address 这个地址是不是包含在 ModuleMap 里面,返回 bool 值
  • find(address), get(address): 返回 address 地址所指向的模块对象详细信息,如果不存在 find 返回 null,get 直接会抛出异常,具体的返回的对象的详细信息,可以参考 Process.enumerateModules()
  • findName(address), getName(address), findPath(address), getPath(address): 功能跟 find(), get() 类似,但是只返回 name 或者 path 字段,可以省点开销
  • update(): 更新 ModuleMap 信息,如果有模块加载或者卸载,最好调用一次,免得使用旧数据。

Memory

  • Memory.scan(address, size, pattern, callbacks):address 开始的地址, size 大小的内存范围内以 pattern 这个模式进行匹配查找,查找到一个内存块就回调 callbacks,各个参数详细如下:
    • pattern 比如使用 13 37 ?? ff 来匹配 0x13 开头,然后跟着 0x37,然后是任意字节内容,接着是 0xff 这样的内存块
    • callbacks 是扫描函数回调对象:
      1. onMatch: function(address, size): 扫描到一个内存块,起始地址是 address ,大小 size 的内存块,返回 stop 表示停止扫描
      2. onError: function(reason): 扫描内存的时候出现内存访问异常的时候回调
      3. onComplete: function(): 内存扫描完毕的时候调用
  • Memory.scanSync(address, size, pattern): 内存扫描 scan() 的同步版本
  • Memory.alloc(size): 在目标进程中的堆上申请 size 大小的内存,并且会按照 Process.pageSize 对齐,返回一个 NativePointer,并且申请的内存如果在 JavaScript 里面没有对这个内存的使用的时候会自动释放的。也就是说,如果你不想要这个内存被释放,你需要自己保存一份对这个内存块的引用。
  • Memory.copy(dust, src, n): 就像是 memcpy
  • Memory.dup(address, size): 等价于 Memory.alloc()Memory.copy() 的组合。
  • Memory.protect(address, size, protection): 更新 address 开始,size 大小的内存块的保护属性, protection 的取值参考 Process.enumerateRanges() ,比如: Memory.protect(ptr("0x123", 4096, 'rw-'));
  • Memory.patchCode(address, size, apply): apply 是一个回调函数,这个函数是用来在 address 开始的地址和 size 大小的地方开始 Patch 的时候调用,回调参数是一个 NativePointer 的可写指针,需要在 apply 回调函数里面要完成 patch 代码的写入, 注意 ,这个可写的指针地址不一定和上面的 address 是同一个地址,因为在有的系统上是不允许直接写入代码段的,需要先写入到一个临时的地方,然后在影射到响应代码段上,(比如 iOS 上, 会引发进程丢失 CS_VALID 状态),比如:
  • 下面是接着是一些数据类型读写:
    1. Memory.readPointer(address)
    2. Memory.writePointer(address, ptr)
    3. Memory.readS8, Memory.readU8
    4. ...

MemoryAccessMonitor

  • MemoryAccessMonitor.enable(ranges, callbacks): 监控一个或多个内存块的访问,在触发到内存访问的时候发出通知。 ranges 要么是一个单独的内存块,要么是一个内存块数组,每个内存块包含如下属性:
    • base: 触发内存访问的 NativePointer 地址
    • size: 被触发访问的内存块的大小
    • callbacks: 回调对象结构:
    • onAccess: function(details): 发生访问的时候同步调用这个函数,details 对象包含如下属性:
    • operation: 触发内存访问的操作类型,取值范围是 read, write 或者 execute
    • from: 触发内存访问的指令地址,NativePointer
    • address: 被访问的内存地址
    • rangeIndex: 被访问的内存块的索引,就是调用 MemoryAccessMonitor.enable() 的时候指定的内存块序号
    • pageIndex: 在被监控内存块范围内的页面序号
    • pagesCompleted: 到目前为止已经发生过内存访问的页面的个数(已经发生过内存访问的页面将不再进行监控)
    • pagesTotal: 初始指定的需要监控的内存页面总数
  • MemoryAccessMonitor.disable(): 停止监控页面访问操作

Thread

  • Thread.backtrace([context, backtracer]): 抓取当前线程的调用堆栈,并以 NativePointer 指针数组的形式返回。
    1. 如果你是在 Interceptor.onEnter 或者 Interceptor.onLeave 中调用这个函数的话,那就必须要把 this.context 作为参数传入,这样就能拿到更佳精准的堆栈调用信息,如果省略这个参数不传,那就意味着从当前堆栈的位置开始抓取,这样的抓取效果可能不会很好,因为有不少 V8 引擎的栈帧的干扰。
    2. 第二个可选参数 backtracer ,表示使用哪种类型的堆栈抓取算法,目前的取值范围是 Backtracer.FUZZYBacktracer.ACCURATE ,目前后者是默认模式。精确抓取模式下,如果如果程序是调试器友好(比如是标准编译器编译的结果,没有什么反调试技巧)或者有符号表的支持,抓取效果是最好的,而模糊抓取模式下,抓取器会在堆栈上尝试抓取,并且会猜测里面包含的返回地址,也就是说中间可能包含一些错误的信息,但是这种模式基本能在任何二进制程序里面工作:
  • Thread.sleep(delay): 线程暂停 delay 秒执行

下篇继续

  • 一篇文章写太长发现浏览器容易崩溃,所以下篇继续

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文