本版本有不少令人兴奋的内容。让我们直接开始。

FreeBSD

我们的目标是支持用户关心的所有平台。在本版本中,我想迈出将支持扩展到 BSD 的第一步。现在我很高兴地宣布,Frida 终于也支持 FreeBSD 了!🎉

目前只支持 x86_64 和 arm64,但如果有人愿意帮忙,扩展到其余体系结构并不困难。

移植工作带来了多项架构改进,也普遍提升了基于 ELF 的操作系统上的稳健性。它还让我想到了一些改进 Linux 注入器、使其支持向容器注入的方案;这是我今后希望完成的工作。

Stalker 性能

在 15.1.10 中,Stalker 在 x86/64 上获得了巨大的性能提升。本版本将相同思路应用到了 arm64 后端,包括改进局部性、更好的内联缓存等。据说我们当时在 FuzzBench 中已经能超越或追平 QEMU,现在 arm64 上的表现应该也相当不错。与此同时,我们还提升了稳定性。令人兴奋!

GObject 内省

早在 14.1 中,@meme 就为构建系统接入了 GObject Introspection 支持。这意味着所有 API 都有了机器可读的描述,使我们能够复用现有的语言绑定基础设施,甚至免费获得自动生成的参考文档。

本版本为 Gum 添加了大量注解和文档字符串,我们比以往任何时候都更接近自动生成参考文档。在适合发布生成的文档之前仍有一些工作要做,但已经不远了。如果有人愿意参与,请查看 Gum 的 CI,并留意 GObject Introspection 输出的警告。

Meson 子项目支持

我非常喜欢 Meson 构建系统的一点是它支持子项目。Gum 现在可以作为子项目使用。你们有些人可能已经通过 devkit 二进制文件使用 Gum,现在又有了一个更加简便的新选择。

与使用 devkit 相比,主要优势是所有内容都从源代码构建,因此很容易试验代码。

假设有一个 hello.c 文件,内容如下:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <fcntl.h>
#include <gum.h>
#include <stdio.h>
#include <unistd.h>

static int (* open_impl) (const char * path, int oflag, ...);
static int replacement_open (const char * path, int oflag, ...);

int
main (int argc,
      char * argv[])
{
  gum_init ();

  GumInterceptor * interceptor = gum_interceptor_obtain ();

  gum_interceptor_begin_transaction (interceptor);

  open_impl = dlsym (RTLD_DEFAULT, "open");
  gum_interceptor_replace (interceptor, open_impl, replacement_open,
      NULL, NULL);

  gum_interceptor_end_transaction (interceptor);

  close (open_impl ("/etc/hosts", O_RDONLY));
  close (open_impl ("/etc/fstab", O_RDONLY));

  return 0;
}

static int
replacement_open (const char * path,
                  int oflag,
                  ...)
{
  printf ("!!! open(\"%s\", 0x%x)\n", path, oflag);

  return open_impl (path, oflag);
}

为了构建它,可以在旁边创建包含以下内容的 meson.build:

project('hello', 'c')
gum = dependency('frida-gum-1.0')
executable('hello', 'hello.c', dependencies: [gum])

再创建 subprojects/frida-gum.wrap,内容如下:

[wrap-git]
url = https://github.com/frida/frida-gum.git
revision = main
depth = 1

[provide]
dependency_names = frida-gum-1.0, frida-gum-heap-1.0, frida-gum-prof-1.0, frida-gumjs-1.0

如果尚未安装 Meson 和 Ninja,请运行 pip install meson ninja。

然后进行构建并运行:

$ meson setup build
$ meson compile -C build
$ ./build/hello
!!! open("/etc/hosts", 0x0)
!!! open("/etc/fstab", 0x0)

体积

我们投入了大量精力,确保 Frida 能从桌面系统一直扩展到嵌入式系统。本版本中,我花了一些时间分析二进制体积,并据此做出大量调整、添加构建选项来减小体积。

我很好奇,只使用 Interceptor 的 Gum Hello World 程序最小能做到多小。最终结果在使用 Thumb 指令的 32 位 ARM 上测得,其中 Gum 及其依赖静态链接,唯一的外部依赖是系统 libc。

结果小到只有 55K(!),让我非常兴奋。我在 Gum、GLib 和 Capstone 中引入了新的构建选项。Gum 现在支持 “diet” 模式,不使用 GObject,只提供普通 C API。这意味着它不支持 GObject Introspection 和高级语言绑定,也不提供完整 Gum API,不过未来可以继续扩展。

类似地,GLib 也新增了 “diet” 模式,主要是禁用 slice 分配器、调试功能,并做一些其他小调整。

对于 Capstone,我引入了一个可设为 “tiny” 的新 “profile” 选项。启用后,Capstone 只理解足以确定每条指令长度的指令集内容,并提供一些与位置相关指令的细节。其思路是仅支持 Relocator 实现所需的能力,因为 Interceptor 和 Stalker 背后的大部分繁重工作都由它们完成。

除非确实需要如此小的体积,否则我不建议使用这些构建选项;但了解能够做到什么仍然很有价值。我们也提供其他不那么极端的选项,详情请阅读体积文档。

多构建系统之苦

自 Frida 诞生以来一直困扰我的一件事是,构建 Frida 需要与多个构建系统打交道。我们当然会尽量把复杂性隐藏在脚本和 makefile 后面,但总会有用户不得不费力弄清 Frida 为何无法构建,并因此感到沮丧。

有些人还希望为略有不同的 libc、工具链或其他环境交叉编译 Frida,甚至可能想支持尚未支持的操作系统。但当他们意识到需要处理四种不同构建系统——Meson、autotools、自定义 Perl 脚本(OpenSSL)和 GN(V8)——时,很可能立刻失去动力。

我们是 Meson 的满意用户,因此我的目标是“让一切 Meson 化!”到本版本为止,我们终于基本实现所有必需依赖都使用 Meson 构建。唯一的例外是 V8,但希望有一天也能用 Meson 构建它。(来自未来的剧透:Frida 16 会实现这一目标!)

结束语

此外还有许多令人兴奋的变化,请务必查看下面的变更日志。

祝使用愉快!

变更日志

  • 添加 FreeBSD 支持。
  • 添加 ARMv4 支持,用于为旧式嵌入式系统插桩。
  • 添加减小二进制体积的构建选项(参见 config.mk)。
  • 调整 Capstone 和 GLib,以实现小得多的二进制体积。
  • droidy:添加 AXML 解码器。感谢 @meme!
  • windows:修复 dbghelp 回溯器对 libffi frame 的支持。
  • linux:修复与新版 glibc 的兼容性。
  • linux:安装资源后只使用一个 frida-helper。
  • qnx:注入器关闭时移除临时文件。
  • core:修复取消 agent 清理时的泄漏。
  • gumjs:修复返回 null 的 RPC 调用处理。
  • interceptor:无法分配 “JMP ” 可达内存时,按需在 x86 上使用 “jumbo” JMP。
  • interceptor:生成可变大小的 x86 NOP 填充。
  • stalker:将近期用于优化 x86/64 后端的思路应用于 arm64 后端,例如改进局部性和内联缓存,从而提升性能。
  • stalker:修复大小为零的 freeze/thaw() 处理。
  • stalker:重做 x86 PLT 排除代码,避免跟踪期间的重入问题。
  • stalker:不再要求存在 C++ 运行时。
  • arm-writer:添加 put_bl_reg()、put_call_reg()。
  • arm-writer:修复 put_call_address*() 中的寄存器破坏。
  • arm64-reader:公开 disassemble_instruction_at()。
  • arm64-writer:修复混合宽度字面量的处理。
  • arm64-writer:修复 TBZ/TBNZ 编码。
  • arm64-writer:修复 32 位系统上的 put_and_reg_reg_imm()。
  • arm64-writer:添加 put_{ldr,str}_reg_reg_offset_mode()。
  • elf-module:公开更多细节,添加离线模式支持和 Vala 绑定。
  • exceptor:添加 reset() 以恢复异常处理程序。
  • gum:添加大量 GObject Introspection 注解和 API 文档。
  • gum:支持作为 Meson 子项目使用。
  • gum:将 Meson 构建系统移植到 Windows。
  • gum:消除 GIO 依赖。
  • gum:添加 diet 模式,使使用 Interceptor 的 “Hello World” C 程序在采用 Thumb 指令的 32 位 ARM 上可小至 55K。
  • gum:添加 CI。感谢 @meme!
  • 对构建系统进行多项可移植性改进。
  • 使用 Meson 构建 elfutils、libiconv、libdwarf、libunwind、openssl、xz。
  • 升级依赖:capstone、elfutils、libdwarf、libunwind。
  • 发布 Fedora 35 而非 34 的软件包。
  • python:使用 PEP 503 代替 PyPI xmlrpc。感谢 @GalaxySnail!
  • python:修复 Windows 上对 Python >= 3.10 的支持。