Frida 17.18.0 已经发布,Barebone 也向前迈出了一大步。XNU agent 现在可作为 macOS 内核扩展加载;Linux agent 获得了更广泛的体系结构支持,并能访问内核自身的类型信息;后端还能在 Linux、XNU、Windows NT 乃至 Windows 9x 上同时为内核本身和用户态进程插桩。我们还将 Frida.Compiler 升级到 TypeScript 7.0,并新增配套的 Frida.LanguageServer API。
一项令人兴奋的新功能是能够把 XNU agent 构建为 .kext。以前,要让它进入内核,必须通过兼容 GDB 的远程 stub(例如 QEMU 的 stub)从外部注入,或使用 JTAG/SWD 硬件调试器。现在 macOS 可以自行加载 agent,/dev/frida 则提供配置和通信通道。这为使用 Frida 进行内核插桩开辟了另一种方式。kext 目前支持内核侧;要在用户进程中放置 agent 副本,仍需使用注入式 XNU agent。
本版本的 Barebone 还有更多内容。注入式 agent 现在把熟悉的 Frida 工作流带入客户机进程:枚举进程、附加、运行脚本、挂钩函数,以及在程序开始运行前就完成插桩并启动它。相关工作覆盖 Linux、XNU、两种字长的 Windows NT,以及 32 位 Windows 9x。Linux agent 注入现在支持 x86、x86-64、Arm 和 Arm64;Linux agent 既可注入运行中的内核,也可作为内核模块加载。
在 Linux 上,脚本现在可以发现已加载的内核模块及其符号,模块注册表会跟踪驱动的加载与卸载。新的 Btf 命名空间还允许脚本在可用时查询内核的 BTF 类型信息。结构体大小、字段偏移与类型、枚举、常量和函数签名都可直接从 JavaScript 获取。例如:
if (Btf.available) {
const module = Btf.getStruct('module');
console.log('struct module size:', module.size);
console.log('name offset:', module.getOffsetOf('name'));
console.log('name field:', JSON.stringify(module.fields.name));
console.log('MODULE_STATE_LIVE:', Btf.getConstant('MODULE_STATE_LIVE'));
}
这意味着脚本可以询问内核其结构体如何布局,避免使用绑定到特定构建的硬编码偏移。底层是一个新的 GumJS 原生 API 注册表,使嵌入方能够独立于所用 JavaScript 运行时公开原生函数命名空间。
与此同时,Frida.Compiler 已升级到 TypeScript 7.0。新的 Frida.LanguageServer API 将同一编译器的语言服务带给嵌入 Frida 的工具。它为 TypeScript 和 JavaScript 项目实现 Language Server Protocol:可以为项目目录创建并启动服务器,通过 post() 发送 JSON-RPC 消息,再通过 message 信号接收回复和通知。这样便可结合 Frida 内置的类型定义和编译器配置,集成代码补全等编辑器功能。编译器和语言服务器还共享解析缓存,因此相同文件内容无需分别解析。
其他亮点和修复:
- barebone:添加用于增删 Barebone 设备的公共 API,ID、名称和图标由调用方提供。公开注入式和常驻 agent 的配置,包括显式 hostlink 地址。
- barebone:为 Linux、XNU 和 Windows agent 添加进程启动和启动门控;在 XNU 和 Windows 上添加应用枚举,并在 XNU 上添加应用启动。
- barebone:改进模块和线程观察、故障恢复、进程清理,以及对 agent 自身线程和映射的隐藏。
- barebone:改进各 agent 的传输交付和唤醒,包括大消息和二进制脚本消息载荷。会话分离时保持常驻 agent 存活。
- barebone:通过可写别名添加 XNU 内核文本修补,并改进内核边界处的代码分配和指针认证。
- barebone:从 kallsyms 和导出表公开 Linux 内核模块符号,并在清理期间注销 API 和模块观察器。
- barebone:将 Linux 内存操作移入客户机,正确处理可写与可执行映射。把重映射和修补扩展到 x86、x86-64、Arm 和 Arm64。
- barebone:访问 QEMU 物理内存模式时暂停客户机,并修复影子页源自 Linux 线性映射时内核文本补丁被静默丢弃的问题。
- barebone:在内核空间放置 x86 别名,修复 CModule 访问其数据时的故障。允许虚拟内存扫描跨越多个叶表。
- barebone:添加 Arm 地址转换和内核空间别名,扩宽 32 位重映射请求中的页地址,并直接刷新 Arm 指令缓存,而不是尝试从内核发起用户空间系统调用。
- barebone:向 JavaScript 运行时报告实际可用栈空间,防止普通脚本递归溢出 Linux 内核栈。
- barebone:使用
_end确定 Linux 内核大小,避免无关映射使 32 位内核看起来大出数 GB。通过GumElfModule读取复制的内核映像,不解引用指向实时内核的指针。 - barebone:修复并完善 Linux 内核模块构建,包括构造函数数组边界和特定变体的运行时依赖。
- linux:加载 Frida 内核模块时添加内核辅助注入;模块不存在时回退到现有注入路径。
- gdb:内存写入使用二进制数据包,并遵循目标寄存器大小。
- interceptor:修复可写视图和可执行视图使用不同映射时 Arm trampoline 的寻址。
- memory:查找指针时跳过坏页。感谢 @IPMegladon!
- memory:允许扫描模式边缘使用通配符,包括 Barebone。感谢 @Xoffio!
- arm64:没有可用着陆垫时避免 BTI。感谢 @inforcqb!
- arm64:检测进入正在重定位指令的分支,使 Interceptor 能选择更小的重定向,避免分支进入已覆盖代码并崩溃。感谢 @WHW0x455!
- cmodule:将 CModule 从 GumJS 移入 Gum,使其可独立于 JavaScript 绑定使用。感谢 @cputnam-a11y!
- elf-module:从文件读取程序头,修复程序头被
patchelf等工具移动的模块;仅有实时映射可用时限制后备读取范围。感谢 @tracyliving! - windows:调整 ACL 以提高注入成功率。感谢 @jamiechapmanbrn!
- python:修复 Python 3.11 之前版本的 typing 导入。
- ci:构建 XNU 内核扩展、额外 Linux agent 和所需 Barebone SDK 与 devkit。在 Android 上启用 Barebone 后端。
- deps:精简 Barebone SDK 中的 Capstone,将归档从约 29 MB 减至 4.4 MB。允许独立式 GLib 构建使用 C 库较小的
printf实现,并精简区域设置和文件名转换支持。 - deps:优化 QuickJS 以减少栈消耗,并调整 GLib 以减小 Barebone 场景中的体积。
oleavr