#gadget #instructions #rop #engine #binaries #endianness #elf

app inspector-gadget

基于命令行的多架构gadget查找工具,专为快速操作设计,即使是对浏览器引擎和操作系统内核等大型二进制文件也能快速运行。

1 个不稳定版本

0.3.0 2024年4月30日

#6 in #gadget

GPL-3.0 许可证

79KB
1K SLoC

inspector-gadget

基于命令行的多架构gadget查找工具,专为快速操作设计,即使是对浏览器引擎和操作系统内核等大型二进制文件也能快速运行。

Crates.io

主要功能

  • 检测并解析PE/COFF、ELF和Mach-O目标文件,以提取架构、端序和可执行部分/段的标志。
  • 支持arm、arm64、x86、x86_64、mips、mips64、ppc、ppc64、riscv、riscv64、sparc64和s390x/systemz架构。
  • 定位的gadget进行去重,对于给定的gadget,在输出中列出所有加载地址。
  • 内置正则表达式引擎,用于预先过滤gadget结果的助记符。
  • 可配置gadget指令计数和结束指令。
  • 它非常快速。

使用方法

inspector-gadget 的最小调用非常简单

inspector-gadget /usr/bin/grep

inspector-gadget 的默认行为是

  1. 解析输入的二进制文件,假设它是一个目标文件。
  2. 通过默认只查找以返回指令结束的gadget,在标记为可执行的部分和段中查找每个gadget。
  3. 去重gadget。
  4. 将找到的每个唯一gadget的助记符和起始地址列表打印到 stdout

更改输出目标

使用 -o (--out-file) 标志

inspector-gadget -o gadgets.txt /usr/bin/grep

或者,gadget将被写入到 stdout,但进度消息将被写入到 stderr,所以只需将输出重定向到文件,也会将gadget写入到文件

inspector-gadget /usr/bin/grep > gadgets.txt

过滤gadget

为了使初始过滤更容易,或者使处理较小文件大小的二进制文件的工作步骤更少,inspector-gadget 支持使用正则表达式通过 @burntsushi 的优秀 regex 库来过滤结果gadget助记符。

当处理大型文件大小的二进制文件时,我建议找到符合一组宽松约束的所有gadget,将它们写入文件,然后重复grep该文件以找到您可能正在寻找的任何特定内容。 如果您想要类似性能的 grep 实现,我建议 ripgrep (也是由 @burntsushi 编写的),它可以通过 cargo install 或通过大多数Linux发行版的包管理器(或在mac上通过 brew)获得。

使用 -r (--regex-str) 标志

inspector-gadget -r "^pop rdx;.*pop r[a-z1-589]{1,3}; ret" /usr/bin/grep

注意:每个小工具的助记符之间用"; "分隔,因此在构建任何正则表达式模式时请考虑这一点。

更改小工具长度限制

默认情况下,inspector-gadget将查找包含2到10条指令的小工具。如果您想更改这些限制,请使用--min-insns--max-insns标志。

inspector-gadget --min-insns 4 --max-insns 6 /usr/bin/grep

更改小工具结束指令

默认情况下,inspector-gadget将仅搜索以给定架构中用于函数返回的指令结束的小工具。如果您还希望允许终止间接跳转或终止间接调用,请使用--allow-terminating-call--allow-terminating-jmp标志。

inspector-gadget --allow-terminating-call /usr/bin/grep

注意:这对于具有明显的jmp/call/ret指令区别的架构非常有效,但对于重用相同指令进行控制流的RISC架构来说,效果较差或无法使用。我为每个架构手动实现了返回指令的启发式算法,但如果Capstone没有为您的目标架构特别标记call/jmp指令,我还没有手动实现它们。

手动指定架构和字节序

默认情况下,inspector-gadget将解析输入的对象文件以确定在反汇编过程中要使用的正确架构和字节序。如果您想手动指定这些值,请使用--arch--endianness标志。

inspector-gadget --arch x86_64 --endianness little /usr/bin/grep

如果传递了--arch--endianness标志,则将使用手动指定的值而不是从对象文件中解析的值。

使用原始二进制数据作为输入

如果您想使用原始数据作为输入,请使用--raw-binary标志。

inspector-gadget --arch x86_64 --raw-binary /tmp/grep_text_dump.bin

注意:在使用--raw-binary时,必须手动指定二进制的架构和字节序。如果未传递--endianness标志,则字节序将默认为小端,因为它更为常见。 --arch 必须在使用 --raw-binary 时始终传递。

实现细节

inspector-gadget基于Capstone反汇编框架构建,并支持为每个可用的Capstone反汇编架构进行小工具查找。反汇编和小工具查找过程使用预先计算的搜索起始地址的工作队列以及rayon数据并行库进行高度多线程化。

在Capstone找到有效的小工具后,inspector-gadget使用自定义的基于树的数据库结构来去重二进制中多个地址可以找到的小工具。

到目前为止,尚未访问或存储小工具指令的助记符字符串,因此inspector-gadget使用Capstone反汇编所有找到的唯一小工具并生成它们的最终助记符字符串。

由于小工具的长度较短,并且与原始搜索相比所需的操作数量显著较少,因此第二次反汇编遍历既节省了大量的内存使用,又比在初始遍历中存储助记符字符串要快得多。

inspector-gadget还实现了对PE/COFF、ELF和Mach-O文件格式的对象文件解析,以及支持原始输入数据块。

更多文档

文档可在docs.rs上找到。

函数和结构都使用Rust的文档注释进行注释,因此可以使用rust doc生成相当好的文档。

性能

在我的测试箱上,找到Android arm64构建中的每个gadget(包括Chrome和Android系统webview使用的dylib libmonochrome.so)的时间不到10秒。

time target/release/inspector-gadget test_bins/libmonochrome_64.so > /dev/null
[...]
real    0m9.758s

下一个最好的选择是Ropper,它大约需要35分钟,而且不费心去去重或排序找到的gadget。

Windows x86_64构建的Firefox dylib xul.dll处理大约需要一分半钟。

time target/release/inspector-gadget test_bins/xul.dll > /dev/null
[...]
real    1m25.907s

我在大约3小时后放弃了等待Ropper的实际数字,但据我所知,它过去是一个“让它过夜”的问题。

当针对较小的二进制文件时,结果几乎是瞬间的。

time target/release/inspector-gadget test_bins/apple-mfi-fastcharge.ko
[...]
real    0m0.113s

TODO(garnt):收集更好的数据并制作一些图表。

许可证

inspector-gadgetGPLv3下授权。

依赖关系

~49MB
~1M SLoC