希望你们当中已经有人开始享受 frida.Compiler 了!如果你完全不知道它是什么,请查看 15.2.0 版本说明。
性能
回到 15.2.0,当时 frida.Compiler 有一点让我很困扰:即便在我的 i9-12900K Linux 工作站上,仅仅编译一个很小的“Hello World”也需要几秒钟:
$ time frida-compile explore.ts -o _agent.js
real 0m1.491s
user 0m3.016s
sys 0m0.115s经过大量性能剖析和难以想象的繁琐打磨后,我终于得到了这样的结果:
$ time frida-compile explore.ts -o _agent.js
real 0m0.325s
user 0m0.244s
sys 0m0.109s差别相当大!这意味着 frida -l explore.ts 这类即时编译场景现在顺畅了很多。更关键的是,基于 Frida 的工具可以用这种方式加载用户脚本,而不会让用户忍受数秒的启动延迟。
快照
你或许想知道我们是如何让编译器如此快速启动的。查看内部实现会发现,它使用 TypeScript 编译器。启动时需要解析并运行的代码相当多。而且,加载和处理定义所有相关类型的 .d.ts 文件,开销其实还要更高。
我们在 15.2 中实现的第一项优化,是在 V8 运行时可用时直接使用它。仅这一项就带来了可观的提速。不过,稍加性能剖析便能看出,当我们开始处理 .d.ts 文件时,V8 意识到自己面对的是繁重工作负载,因此花费了大量时间来优化 TypeScript 编译器的代码。
这让我想起很久以前注意到的一个非常酷的 V8 功能:自定义启动快照。简单来说,如果我们能够预先预热 TypeScript 编译器,并在构建 Frida 时提前创建所有 .d.ts 源文件,就可以在那个时间点为虚拟机状态制作快照,并嵌入生成的启动快照。之后在运行时,我们便可以从快照启动,立即进入工作状态。
在实现这项功能的过程中,我扩展了 GumJS,使 create_script() 可以同时接收快照和 agent 源代码。另有 snapshot_script(),用于首先创建快照。
例如:
import frida
session = frida.attach(0)
snapshot = session.snapshot_script("const example = { magic: 42 };",
warmup_script="true",
runtime="v8")
print("Snapshot created! Size:", len(snapshot))随后可以将该快照保存到文件,并像这样在以后加载:
script = session.create_script("console.log(JSON.stringify(example));",
snapshot=snapshot,
runtime="v8")
script.load()请注意,创建快照时使用的操作系统、架构和 V8 版本必须与之后加载快照时相同。
V8 10.x
另一个令人兴奋的消息是,我们已将 V8 升级到 10.x,这意味着可以享用最新的虚拟机改进和 JavaScript 语言特性。考虑到上一次升级已经是两年多以前,这次无疑是一次相当扎实的升级。
多构建系统的诅咒,第二部分
你或许还记得 15.1.15 版本说明 中提到,我们前所未有地接近“整个 Frida 都能使用单一构建系统完成构建”这一里程碑。当时唯一剩下的组件是 V8,我们过去使用 Google 的 GN 构建系统来构建它。很高兴地告诉大家,我们终于到达了这个里程碑。现在,我们为 V8 配备了一套全新的 Meson 构建系统。太棒了!
结语(EOF)
此外还有许多令人兴奋的变化,务必查看下面的更改日志。
用得开心!
更改日志
- compiler:使用快照缩短启动时间。
- compiler:升级 frida-compile 和其他依赖项。
- 增加对 JavaScript 虚拟机快照的支持。目前只有 V8 后端实现了该功能,因为 QuickJS 暂不支持。
- 将调试器 API 从 Session 移至 Script。这是必需的,因为 V8 的调试器按 Isolate 工作,而为了支持快照,现在每个 Script 都需要一个 Isolate。
- server+portal:修复守护进程父进程就绪失败时的退出问题。感谢 @pachoo!
- resource-compiler:增加压缩支持。我们将其用于 frida.Compiler 的堆快照。
- ipc:增大 UNIX 套接字缓冲区以提高吞吐量。
- meson:将 frida-payload 提升为公共 API。这样就能为 frida-agent 和 frida-gadget 不适用的场景实现自定义 payload。
- windows:迁移到 Visual Studio 2022。
- windows:将工具链/SDK 逻辑改为使用细粒度 SDK。
- windows:不再依赖 .py 文件关联。
- darwin:修复与 macOS 13 和 iOS >= 15.6.1 的兼容性。
- darwin:如果存在 Apple 的 libffi-trampolines.dylib,则使用它,从而支持 iOS 15 及更高版本。感谢 @hsorbo 带来的愉快结对编程时光!
- fruity:修复 USBMUXD_SOCKET_ADDRESS 的处理。感谢 @0x3c3e!
- fruity:停止支持 USBMUXD_SERVER_* 环境变量。感谢 @as0ler!
- droidy:改进 ADB 环境变量的处理。感谢 @0x3c3e!
- java:(android) 修复 Android 11 和 12 上 ClassLinker 偏移量的检测(#264)。感谢 @sh4dowb!
- java:(android) 修复 Android 13 上的早期插桩。
- java:处理以 $ 为前缀的方法和字段。感谢 @eybisi!
- android:迁移到 NDK r25。
- arm64:优化内存复制实现。
- stalker:确保 EventSink 在拆卸时停止。
- stalker:修复分支涉及移位时 ARM 栈遭破坏的问题。
- stalker:处理涉及移位寄存器的 ARM PC 加载。
- stalker:应用回填补丁时通知 ARM observer。
- stalker:收到通知时应用 ARM 回填补丁。
- stalker:为 switch block 回调增加 ARM 支持。
- arm-reader:公开 disassemble_instruction_at()。
- thumb-reader:公开 disassemble_instruction_at()。
- memory:重新调整 API,使其与当前 V8 语义一致。
- gumjs:将 V8 后端改为每个脚本使用一个 Isolate。
- gumjs:支持通过环境变量传递 V8 标志:FRIDA_V8_EXTRA_FLAGS。
- gumjs:在 Darwin/arm* 上使用 V8 写保护。
- gumjs:增加对动态定义脚本的支持。
- prof:支持 Linux/MIPS 上的旧系统头文件。
- devkit:改进示例在 UNIX 上的编译文档。
- ci:将其余旧版 CI 迁移到 GitHub Actions。
- quickjs:修复模块求值期间出错时的释放后使用问题。
- v8:升级到最新的 V8 10.x。
- v8:增加 Meson 构建系统。
- usrsctp:将 Windows 要求降低到 XP,与其他组件保持一致。
- xz:避免使用 ANSI 时代的 Windows API。
- libc-shim:支持 Linux/MIPS 上的旧系统头文件。
- glib:为 MIPS 增加 Linux libc 回退实现。
- 增加 config.mk 选项,以便在 Android 上禁用模拟 agent,从而构建更小的二进制文件。感谢 @muhzii!
- python:停止支持 Python 2,现代化代码,增加文档字符串和类型标注,使用现代工具增加 CI,以及其他许多改进。感谢 @yotamN!
- python:构建 Python wheel,而不是 egg。感谢 @oriori1703!
- python:修复 Device.get_bus()。此前的实现调用了并不存在的 _Device.get_bus()。感谢 @oriori1703!
- python:迁移到稳定版 Python C API。
- python:增加使用 frida-core devkit 从源代码构建的支持。
- python:增加对新快照 API 的支持。
- node:增加对新快照 API 的支持。
- node:修复与 Electron v20 的兼容性。
oleavr