检测、分析并唯一标识应用程序缺陷。
你是否...
……那么 BugId 可能适合你!
要使用 BugId,请下载并安装以下软件:
如果你使用默认设置安装 Python 和 Windows 调试工具,BugId 应该无需调整任何设置即可运行。你可以将 BugId 解压到本地文件系统的任何位置。
在启动 BugId 之前,你应该在目标应用程序中启用全页堆(full page heap)。这可以通过设置某些 全局标志(Global Flags) 来针对每个二进制文件完成。最简单的方法是使用 BugId 自带的 pageheap.cmd 脚本。例如,要为 notepad 启用全页堆,请运行以下命令:
C:\BugId>pageheap notepad.exe ON
(请注意,此命令必须在具有计算机管理权限的提升的命令提示符中运行)。
为了更方便,pageheap.cmd 内置了一个 已知应用程序 列表。你可以通过提供名称来启用或禁用其中任何一个应用程序的全页堆,例如 pageheap msie ON 为 Microsoft Internet Explorer 启用全页堆。使用 pageheap /? 获取有关命令行参数的更多信息。
此时,你可能想测试 BugId 是否正常工作。我们可以通过在 BugId 中运行一个应用程序并使其崩溃,来查看 BugId 是否正确报告该缺陷。一个适合此测试的好应用程序是 rundll32.exe,它在所有 Windows 安装中都可以在 Windows 文件夹(%WinDir%)的 system32 子文件夹中找到。它可用于加载本地文件系统中的任何 dll,并以特定的调用格式调用该 dll 中的导出函数。system32 文件夹中有许多 dll 导出函数。这些导出函数中的大多数期望的参数格式与 rundll32 提供的完全不同,从而导致应用程序崩溃。
首先,我们必须使用以下命令在 rundll32 中启用全页堆:
C:\BugId>pageheap rundll32.exe ON
接下来,我们将启动 BugId,并告诉它以参数启动 rundll32,使其加载 advapi32.dll 并调用 CloseThreadWaitChainSession。在撰写本文时,这会导致所谓的 NULL 指针访问冲突,BugId 应检测并报告该冲突。
C:\BugId>BugId.cmd %WinDir%\system32\rundll32.exe -- advapi32 CloseThreadWaitChainSession
请注意,在 rundll32.exe 和传递给它的参数之间有一个 --。这是因为你可能希望同时为 BugId 本身和要测试的应用程序提供参数。这两组参数应在命令行上用 -- 分隔。-- 之前的所有内容由 BugId 处理,之后的所有内容 BugId 会忽略,但会传递给应用程序。
如果一切正常,BugId 的输出将如下所示:
* Command line: C:\WINDOWS\system32\rundll32.exe advapi32 CloseThreadWaitChainSession
+ Main process 8024/0x1F58 (rundll32.exe): Attached; command line = C:\WINDOWS\system32\rundll32.exe advapi32 CloseThreadWaitChainSession.
,-- A bug was detect in the application ----------------------------------------
| Id @ Location: AVR@NULL a1f.904 @ rundll32.exe!advapi32.dll!WctRemoveEntry
| Description: Access violation while reading memory at 0x0 using a NULL pointer.
| Security impact: Denial of Service
| Version: rundll32.exe 10.0.16299.15 (x64)
| advapi32.DLL 10.0.16299.15 (x64)
| Bug report: AVR@NULL a1f.904 @ rundll32.exe!advapi32.dll!WctRemoveEntry.html (60703 bytes)
'-------------------------------------------------------------------------------
第一行告诉你 BugId 将要启动的命令行。第二行告诉你,这导致创建了一个进程 ID 为 8024 的新进程,运行 rundll32.exe,并显示该进程的命令行(当然,与第一行中的命令行相同)。应用程序启动后不久,检测到一个缺陷。BugId 为此缺陷生成了一个唯一 ID(AVR@NULL a1f.904),并报告其位置位于 rundll32.exe 加载的 advapi32.dll dll 的 WctRemoveEntry 函数中。由于 NULL 指针崩溃通常除了使应用程序崩溃外不可利用,因此该缺陷的安全影响是 Denial of Service(拒绝服务)。默认情况下,BugId 会为它发现的每个缺陷生成一份 HTML 格式的报告,并告诉你这份报告的存储位置。如你所见,报告的文件名基于缺陷 ID 和位置。
BugId 生成的每个缺陷 ID 由以空格分隔的两部分组成。第一部分描述缺陷的类型。在上面的例子中,AVR@NULL 表示 Access Violation Reading memory at address NULL(在 NULL 地址处读取内存的访问冲突)。第二部分描述缺陷的位置;它由两个以点分隔的短哈希组成。这些哈希是根据堆栈上 被认为与缺陷相关的 顶部函数计算得出的。在示例中,a1f.904 由 a1f(根据 advapi32.dll!WctRemoveEntry 计算)和 904(根据 advapi32.dll!CloseThreadWaitChainSession 计算)组成。
如果你再次运行同一条命令,BugId 将报告完全相同的 BugId,因为相同代码中会引发完全相同的缺陷。
恭喜!你现在已经准备好使用 BugId 测试自己的崩溃了,但你可能需要在某个时候运行 BugId.cmd --help 来获取有关 BugId 支持的众多不同命令行选项的信息。
BugId 在模糊测试期间针对大量应用程序进行了开发和测试,用于分析由数百个不同缺陷引起的数十万次崩溃。在这一角色中,它被证明在分析缺陷时极其准确,误报率和漏报率都非常低;两者均低于 1%。
当然,并非所有类型的缺陷都易于检测和分析。有些缺陷目前无法在其发生时被可靠地检测到,而是会在之后更晚的时候在完全无关的代码中导致崩溃,从而产生完全错误的分析。在这种情况下,重复复现同一缺陷将导致多个不同的缺陷 ID,因为应用程序会在不同时间以不同方式崩溃。为确保不是这种情况,建议你在 BugId 中多次运行测试用例,以查看缺陷 ID 是否保持不变。
另一些缺陷导致的崩溃看起来像是另一种类型的缺陷引起的。这可能导致错误的分析和缺陷 ID。最值得注意的是,由 C/C++ 代码中的*错误类型转换(bad casts)*导致的缺陷目前无法被 BugId 检测和报告。它们可能导致各种不同类型的崩溃;最常见的是当对象属性中存储的值被错误地用作指针时发生的访问冲突。如果你预计错误类型转换可能是崩溃的原因,则应仔细检查 BugId 所做的分析,以确保其正确。
BugId 高度依赖应用程序使用全页堆来检测和分析大量与堆相关的缺陷。这意味着,对于使用自己的内部堆管理器而不依赖标准 Windows 堆的应用程序,BugId 在检测和分析缺陷方面的效果将大打折扣。
BugId 为商业和非商业用途提供 30 天的免费试用期。在试用期内,本作品根据 [知识共享署名-非商业性使用 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc/4.0/) 授权。
试用期结束后,如果你想继续使用 BugId,必须在 license.skylined.nl 获取付费许可证。
非商业许可证只需支付很少的费用;刚好足以支付我的开销。个人安全研究人员的商业许可证也有很大的折扣。
如果你对许可有任何疑问,或想讨论批量折扣,请联系 [email protected]。
BugId 设有试用期,以便你评估其有用性。如果你想在试用期结束后继续使用 BugId,我希望你能贡献一小笔费用,以支付我开发 BugId 和处理许可请求的工作。如果你在商业上使用 BugId,我希望你支付常规许可费,以分享你因使用 BugId 而获得的部分利润。