首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 归档 标签 关于

错误的二元覆盖率

2026-07-05 1 阅读 约5分钟阅读 matt_d
分享:
字号:
正确的方法早在恐龙时代,如果您编写了一个程序并想要测试它是否正确,您通常有两种选择:您可以手动构建格式错误的输入并连续尝试它们,或者连接一个可以随机生成输入并自动尝试它们的程序 1 。这些程序被称为模糊器,有时也称为生成器,这就是“模糊测试”。在相当长的一段时间内,它也不是很好:除了“这个随机输入是否崩溃”之外,生成器不会从被测程序获得任何反馈。这种反馈虽然有用,但不允许模糊器判断它相对于最后的随机输入是否正在取得进展或做得很好,因此您的模糊器只会重复命中相同的浅层代码。你会让它运行一周,然后当你回来时意识到它永远不会生成会影响程序中有趣部分的输入,并且它们都立即被丢弃或永远不会通过 if(packet_header == 0xdeadbeef) 检查。美国 Fuzzy Lop 通过引入覆盖引导模糊测试彻底改变了该领域。它不会向模糊器提供输入是否崩溃的唯一反馈,而是重新编码在程序执行过程中遇到的代码覆盖率;如果输入执行了以前从未到达过的程序的一部分,那么它会遇到新的代码,并且现在可能还能够测试程序中可能存在一些错误的一些新的有趣的表面区域。这比过去的“愚蠢的模糊测试”要有效得多。 AFL 通过使用自定义 clang 编译器通道编译被测程序来收集程序的覆盖率,这会在 CFG 边缘上添加覆盖率反馈:如果您在代码中命中新的基本块,您将获得一个新的覆盖率位图条目集 2 。如果在输入集上运行程序会产生比以前具有新位集的位图,则它会命中新代码;该输入可以保存下来用作种子输入,以便将来执行时发生变异。您还可以对没有源代码的黑盒二进制文件进行基于反馈的模糊测试。例如,AFL++ 有一个 qemu_mode 后端:它使用 qemu-user 执行测试中的程序,这会将您的 x86 程序集提升到 QEMU 的 TCG IR,然后使用其 JIT 编译器将其降低到 x86,但现在可以在其类型 2 虚拟机管理程序环境下安全地执行。 AFL++ 有一个 QEMU 的分支,它添加了一个自定义 TCG 钩子,该钩子将覆盖率检测添加到中端的块中;每个块的 IR 都通过写入覆盖位图条目的 TCG 操作进行扩展,就像 AFL 通过额外的 LLVM 操作增强 clang IR 一样。尽管听起来工作量很大,但这种 qemu_mode 只比本地运行程序慢约 3-5 倍。黑盒二进制覆盖的黄金标准是让硬件为您做这件事。您无需向被测程序添加额外的操作(在运行模糊测试用例时需要执行这些操作),而是可以使用 Intel PT 。启用某些性能计数器配置寄存器将启动处理器跟踪:然后您的 CPU 将自动记录元数据作为其执行管道的一部分,并且模糊器可以查看生成的元数据以提取其执行的基本块的跟踪。这是非常有效的,基于 Intel PT 的跟踪仅比正常本机执行慢约 10%。当然,问题是,如果您像我一样,您的笔记本电脑配备的是 AMD 处理器,而不是英特尔处理器。 AMD 有自己神奇的性能计数功能,包括记录正在执行的代码的分支跟踪缓冲区,但其 1)记录更差 2)比 Intel PT 慢得多 3)不可能以精确的方式使用 - 也就是说,计数器要么基于采样,所以只能给你 1/n 事件,或者如果元数据缓冲区在你能够耗尽它之前填满,则可能最终丢弃事件。无论哪种情况,这都使它不适用于模糊测试(从技术上讲,你可以在仅仅随机覆盖的基础上构建一个模糊测试器,甚至可能没那么糟糕。但实际上没有人这样做,而且可能不值得。)这让我想到了我的坏主意。我想为一个业余项目编写自己的模糊器,因此想收集精确的黑盒二进制覆盖率。但不想(再次)编写自己的动态重新编译引擎,或者购买一台新笔记本电脑。软件工程师要做什么?错误的方式我们可以通过用 INT3 指令替换所有分支来检测访客代码。等我回来我可以解释。为了收集精确的覆盖范围,我们需要程序在遇到分支时执行某些操作。我们无法在每个块中插入新行为,因为它已经将块中的所有字节用于其正常指令:尝试调整块大小需要修复代码指针,然后您就回到了 QEMU,但很糟糕。分支指令的最小长度为两个字节长: Jcc 的形式仅为单个操作码 b
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱