bulk_extractor
数字取证(DF)是一个快速发展的领域,涉及范围极其广泛。数字调查员必须能够分析“可能在地球上任何设备的任何位置找到的任何数据”。12 因此,开发人员必须不断更新DF工具,以应对新的文件格式、新的编码方案以及调查对象使用计算机的新方式。与此同时,工具必须保留分析遗留数据格式的能力——事实上,所有的数据格式。
大多数DF工具在消费级桌面操作系统上运行,这增加了另一层复杂性:这些操作系统也在不断发展。分析师必须更新和升级他们的系统,以免面临恶意软件的风险,恶意软件会降低生产力,并可能在法庭上使分析结果失去信誉。即使对于“气隙”(未连接到互联网)的工作站来说也是如此,因为证据中的恶意软件可能会利用取证软件中的漏洞。19
令人惊讶的是,以源代码形式分发的开源取证工具在底层操作系统升级时面临更大的挑战:软件兼容性层通常强调ABI(应用程序二进制接口)的兼容性,而不是源代码。从源代码编译的软件必须应对升级后的编译器、库和文件位置。因此,较旧的开源软件通常无法在现代系统上运行,除非进行更新。解决此问题的一种方法是在虚拟机内部运行旧软件——但较旧的虚拟机无法防范现代恶意软件威胁。
开源软件的一个优势是最终用户拥有源代码,因此能够更新应用程序(或付费请程序员更新应用程序)。但在实践中,许多DF工具用户缺乏专业知识、经济资源和时间来更新他们赖以完成工作的开源工具集合。相反,这项任务落在了工具开发人员身上,他们必须同时应对DF最佳实践以及操作系统、编译器和库的必要更改,同时避免对重要功能进行无意的更改。开发人员还必须抵制添加新的、扩展功能的激进重写冲动,以免屈服于“第二系统效应”。5
本文介绍了我们在最初发布十年后更新高性能DF工具BE (bulk_extractor
)16 的经验。在2018年至2022年期间,我们将程序从C++98更新到C++17。我们还进行了完整的代码重构,并采用了单元测试框架。
新版本的吞吐量通常比以前的版本高出75%,这归功于改进的多线程处理。本文为其他DF工具维护人员提供了经验教训和建议。所有开发人员都可以从详细的讨论中受益,了解如何利用C++17标准和现代软件工程实践中的特性来提高取证软件的正确性、可靠性和吞吐量。企业和资助机构可以利用这些经验来帮助证明更新甚至重写看似运行正常的DF工具的巨大成本是合理的。学生可以通过阅读本文,然后查阅GitHub上的BE源代码来获益。
典型的DF检查包括五个步骤:(1)策略和能力开发;(2)证据评估;(3)证据采集;(4)证据检查;以及(5)文档记录和报告。20 BE在证据检查阶段提供协助。
有许多种证据检查工具。文件提取工具使用元数据从磁盘映像和网络流中提取单个文件;文件雕刻工具尝试仅基于内容识别来识别批量数据(例如磁盘映像和产品文件)中的文件;文件分析工具理解文件格式,并尝试提取信息(通常称为工件),例如文本和Microsoft Office文件元数据。
BE并不完全属于这些类别。相反,它被设计成所谓的“查找证据按钮”。它类似于文件雕刻工具,因为它尝试识别批量数据中的已知格式,并将该数据用于进一步处理。除了识别文件(例如JPEG图像)外,BE还识别较小的“特征”,例如JPEG图像中的EXIF(可交换图像文件)元数据,甚至EXIF字段中的电子邮件地址。BE还可以识别其他类型的身份信息,例如URL和信用卡号:事实证明,此类信息在调查中非常有价值。BE还会检查每个输入块,以查看它是否包含FAT32(文件分配表32)和NTFS(新技术文件系统)的目录条目结构,如果找到任何结构,则报告解码后的元数据。
总的来说,BE同时处理数十种数据格式。然后,该程序构建重要字符串(例如电子邮件地址和互联网搜索查询)的标准化Unicode直方图。经验表明,这种“厨房水槽”方法——对每个字节都使用所有工具——可以找到其他工具遗漏的数据,而这些数据在调查中可能很重要。虽然这种分析在计算上是昂贵的,但它是高度并行的。
BE还利用极其简单的I/O模型(顺序读取)和内存分析。因此,BE通常可以使用多核工作站的所有内核。
BE的另一个显著特点是它对数据块执行递归重分析。BE检查每个字节,以查看它是否是可以解压缩或解码的流的开始;如果是,则对结果字节进行递归重分析。因此,BE的JPEG雕刻器不仅可以找到普通的JPEG,还可以找到gzip压缩数据中的JPEG和Base64 MIME(多用途互联网邮件扩展)附件中的JPEG。递归解码数据和识别有趣数据(不考虑文件系统结构)的组合使BE成为传统取证工具的强大补充。
由于BE忽略文件边界,因此它用于识别内容的模块(称为扫描器)通常比其他取证程序中的格式解码器(有时称为解剖器)更复杂。当然,每个扫描器在使用输入之前都会检查输入的每个字段是否用于内存引用。但是BE扫描器还会检查内存结束条件,因为扫描器可能正在对解压缩内存块的片段进行操作。由于BE并行处理内存,每个块都在不同的线程中,因此所有扫描器都必须是可重入的。
该程序一些最重要的扫描器是用GNU flex(快速词法分析器生成器)10 编写的大型词法分析器,它们扫描批量数据以查找电子邮件地址、电话号码、MAC(媒体访问控制)地址、IP地址、URL和其他类型的格式化文本字符串(有时称为选择器18)。为此目的使用GNU flex的方法最初由SBook14 使用,用于识别自由文本地址簿条目中的电子邮件地址、电话号码和其他格式化信息,这意味着BE中的某些代码现在已有30年的历史。(与Flex相关的是Bison解析器生成器,它主要由Robert Corbett编写,并由Richard Stallman使其与yacc兼容。9)
BE批量数据分析方法最初被部署用于在二级市场上购买的一组150个硬盘驱动器上查找机密信息。17 该程序经过改进并进行了多线程处理,以跟上Real Data Corpus15 构建期间收集的硬盘驱动器和其他存储设备数量的增加。一项研究揭示了对执法部门有用的具体要求。最重要的是,用户希望有一个工具可以在Linux或Windows上无需用户输入即可运行,以文本文件形式生成输出,并且不会崩溃。13
BE通过模块化架构实现了这些目标。核心引擎将内容识别扫描器应用于数据块,并提供递归重分析。第二部分包含程序的主循环以及读取磁盘映像所需的所有代码。程序的这部分以重叠块读取数据,并将数据馈送到框架的API。BE的第三部分是扫描器本身,可以编译到可执行文件中,也可以在运行时从共享库(Linux和macOS上的.so文件,Windows上的.DLL文件)加载。BE框架允许扫描器提供元数据、声明配置变量并修改程序的帮助消息,从而允许用户创建和部署他们自己的专有扫描器。
BE已广泛应用于数字取证教育中,YouTube上有400多个教学视频证明了这一点,其中许多视频展示了学生使用该工具的项目成果。
BE无疑是一个成功的教育工具,因为它易于使用;可在Windows、Mac和Linux平台上运行;并能找到各种取证工件。对于高级学生,BE已被用作用于各种项目的数据输入。BE也在DF专业发展培训课程中教授。
自创建以来,BE已被世界各地的政府机构和私营公司使用。2011年,该程序获得了美国国防部价值工程奖。21 该程序是Blacklight数字取证工具7 的一部分,并已纳入BitCurator4,BitCurator被数字人文领域的策展人使用。
Stroz Friedberg的DFIR(数字取证和事件响应)咨询实践在一些调查中使用了BE。在一个大型事件响应案例中,装有XFS文件系统的Linux服务器受到了攻击,没有流行的取证工具能够处理XFS文件系统的分析。BE被用于对这些服务器进行分类,以查找相关的入侵指标,从而在调查的早期阶段取得了快速进展。在一个著名的知识产权盗窃案件(Waymo诉Uber)中,BE被用作多种流程之一,以搜寻取证证据中的相关材料。
总而言之,BE十多年来一直是一个强大的工具,有引人注目的使用轶事,但对其使用范围或频率知之甚少。
BE是一个遗留的C++程序。生产者-消费者线程池于2008年开发,而基于扫描器的底层架构和递归重分析则于2009年到位。所有这些都是使用基于大约1998年的STL(标准模板库)的C++版本完成的,远早于C++11标准的批准。
BE的开发在很大程度上于2014年停止。然而,软件维护仍然是一个持续关注的问题:随着每个新版本的开源操作系统的发布,autoconf系统通常需要进行一些更改,以便BE可以在新系统上编译。到2018年,此类更改的频率令人担忧。基于此经验,现在似乎是着手对BE进行有序更新以创建2.0版本的合适时机。
我们对更新有具体的目标
• 通过依赖C++标准,使程序更易于编译和维护。BE升级的主要原因是该程序将无法在现代开源操作系统上编译。
我们特别渴望依赖C++标准库来提供平台独立性,因为符合标准的C++编译器保证符合标准的代码将来可以在今天不存在的平台上编译。他们通过让构建系统指定编译和链接可执行文件时要使用的标准版本来实现这一点:C++11、C++14、C++17等等。
将现有代码升级到现代C++标准需要选择一个特定的标准,并将以前经过艰苦编写、调试和维护的代码替换为使用C++标准的新代码。这开启了将错误引入工作代码的可能性,因此需要更好的测试策略。
我们首先选择了C++14标准,因为在升级开始时,完整的C++17实现尚未广泛可用。然而,由于该项目拖延了太久,以至于C++17变得更加可用,我们最终迁移到C++17以获得std::filesystem
支持。
2021年,我们考虑迁移到C++20,但尝试使用特定功能遇到了失败,因此BE2使用C++17。
• 简化代码库。虽然BE的内部结构是合理的,但由于10年的开发,在某些地方实现过于复杂。我们简化代码的一种方法是修改整个代码库,以便仅在nullptr
是有效值时才传递指针;否则,完全传递C++引用。
• 从代码库中删除实验性和研究性代码。BE最初是为支持数字取证研究而开发的,其中包含大量实验性研究代码。BE2中删除了这些代码:实验可以继续,但它们将被限制为使用插件系统。
• 使BE运行更快。最终目标是减少程序运行所需的时间。最初,目标是使BE2在同一硬件上比BE1运行得更快。进一步分析表明,BE2可以更好地利用多个处理器内核,而无需相应地需要高性能I/O系统。
BE1的并行性来自于将磁盘映像分解为16MB的块,称为页面,每个页面都放在工作队列中进行处理。一个工作线程获取一个工作单元,并在每个页面上顺序运行每个扫描器。为了增加BE2中的并行性,工作单元现在同时指定一个页面和一个扫描器,以便同一页面的扫描器现在可以潜在地在不同的线程中并发运行。这需要为与页面关联的内存实现引用计数垃圾回收。修改后的系统允许在递归处理解压缩或解码的数据块时将新的工作单元排队。结果是允许为每次磁盘读取并行完成更多的工作——这是一个好的工程决策,因为现代高性能系统通常比十年前的系统具有更高的每秒每字节读取处理能力。
BE具有处理文件目录的能力。在BE1中,每个文件都用单个线程处理。在BE2中,所有文件都预先扫描,然后按顺序处理,每个文件都分成多个页面,每个页面都与多个线程并行处理。再次,这导致更多增加并行性的机会,这在具有大量内核的系统上会带来丰厚的回报。
作为重构代码库的一部分,我们还提高了底层C++代码的质量。
我们首先阅读了Bjarne Stroustrup的大部分教科书《C++程序设计语言》。24 这本书超过1000页,可能很少有人完整阅读。此外,它只涵盖了C++11之前的版本,而我们使用的是C++14(然后是C++17)。然而,BE是基于C++11标准之前的C++版本(其开发者也是如此),并且该版本与C++11之间的变化与随后的变化相比是巨大的。熟悉阅读这样的教科书可以让人更好地利用该语言的特性,这些特性同时更高效和更安全。
下一步是提高BE基本内存管理C++类sbuf(搜索缓冲区)的效率、安全性和速度。此类表示从证据中读取或从另一个sbuf解码的字节序列。sbuf记录了包含的内存是如何分配的(因此,它需要如何释放);提供类型安全、内存安全和线程安全的访问器方法;具有从磁盘文件(其他sbuf的切片)或传递给编解码器或解压缩器的新内存创建新sbuf的功能;并提供丰富的调试功能。
为了提高封装性和提供更好的代码重用,许多函数从扫描器和BE框架移动到sbuf中。将功能移动到sbuf中使得几乎消除了BE其余部分中的所有原始内存引用,以及许多冗余的安全检查。(我们考虑过但最终决定不使用Rust编程语言实现BE的部分,因为这会导致额外的复杂性。)
代码库中的其他改进包括
• 将通用代码从扫描器移出并移入底层BE2框架。例如,框架可以理解如何为任何命名的扫描器设置模式,而不是每个扫描器都具有用于设置其雕刻模式的选项。
• 简化API,合并功能几乎相同的功能和方法。
• 在API中添加显式阶段,扫描器在其中分配和释放全局内存。现在,扫描器应该释放它们在运行期间分配的所有内存,而不是让操作系统在进程退出时丢弃内存。这使得单元测试能够找到原本会被遗漏的内存泄漏。
• 按值将字符串作为std::string
传递,而不是像以前那样通过引用作为const std::string &
传递。这可能需要字符串复制,但对性能没有有意义的影响,特别是与提高的安全性相比,可以防止可能使用无效的引用。此决定简化了代码,并消除了几个释放后使用错误。(我们决定不使用C++智能指针,因为存在线程安全问题。)
• 为内存中的所有对象定义清晰的分配/释放策略。特别注意C++移动运算符的实现,允许编译器通过使用它们而不是复制和删除运算符来提高效率。
• 将为Windows、macOS和Linux #ifdef
的代码替换为对C++17库的调用(如果可能)。特别是,广泛使用了std::filesystem
类。这些更改的结果使代码更小,更易于验证。
• 同样,尽可能用C++内联静态常量替换许多预处理器#define
常量。这使得这些值可用于调试器,并使代码更易于理解。
• 消除用于跟踪状态的全局变量。剩下的全局变量的唯一用途是用于CPU密集型函数的预计算值的静态表。现在,使用这些变量的代码会检查以验证它们是否已初始化,如果未初始化,则抛出异常。本质上,它们现在是单例。最好将这些变量的内存保护更改为只读,但这无法以可移植的方式完成,并且需要这些变量具有自己的内存页。相反,只读行为在语言级别使用const正确性强制执行。
• 在大多数情况下,删除必须检查以检测错误的代码返回。相反,C++异常机制用于发出信号并捕获错误条件。
• 消除许多显式互斥锁,并用C++ std::atomic<>
模板替换它们。相比之下,BE1.6在某些位置使用GCC(GNU编译器集合)编译器内在函数进行原子递增,但广泛使用互斥锁来保护线程之间共享的变量。
• 删除遗留的Posix getopt
处理,并用 cxxopts
8 替换它,cxxopts
是一个命令行选项处理模块,它是可重入的,并且不使用全局变量。这对于允许测试选项处理的单元测试是必要的。
• 启用所有编译器警告,而不仅仅是用 -Wall
启用的警告,尽管它的名字,但它并没有启用所有警告。
由于这些更改
• BE2配置脚本现在在我们的参考系统(六核2019 Mac mini)上运行时间为16秒,而不是BE1.6配置脚本的25秒。这对用户来说不是显着的改进,但对BE开发人员来说却是如此。使用 make -j12
时,两者的编译时间均为32秒。
• 即使考虑到为新的单元测试添加的行数,C++代码库也减少了大约10,000行,即17%。该程序现在大约有46,000行C++和GNU flex代码。
正如之前暗示的那样,尽管BE被广泛使用,但它缺乏现代的测试方法。具体来说,BE代码库中没有系统的单元测试。相反,该程序在开发过程中偶尔在测试数据集上运行,并将输出与先前运行的输出手动比较:如果两个输出基本相似,则认为该程序没有明显的损坏。
对于BE2,我们决定为BE源代码的所有级别实现单元测试,包括低级数据操作例程、取证扫描器、结果报告代码、选项处理和端到端测试。对Wikipedia上的C++单元测试框架的评论导致选择了Catch26,Catch2支持测试支架,实现了最小的CLI(命令行界面),可以测试是否抛出异常(或不存在异常),并且似乎得到了良好的支持和维护。
我们还在开发系统上默认启用了AddressSanitizer22。(我们启用了ThreadSanitizer23 并发现了一些线程共享错误,但遇到了一个误报,该误报是由其启发式算法之一与我们的多线程范例之间的冲突引起的,从而阻止了默认启用它。)
我们从BE2框架的单元测试开始,通常在设计和实现新接口时编写它们,将每个新测试的创建与相关的重构相结合。跟踪单元测试的代码覆盖率并系统地增加,然后与GitHub的“actions”系统集成,以便测试在每次推送时运行。流行的CodeCov.io网站显示了代码覆盖率结果。
在所有新的和重构的代码都进行了单元测试之后,代码覆盖率报告显示了哪些遗留代码未被新编写的单元测试覆盖。目标代码覆盖率为60%。在某些情况下,遗留代码确实被新测试覆盖,因为新代码调用了旧代码。然而,对于大约三分之二的遗留代码,没有测试覆盖率,我们通过从头开始编写新测试来解决这个问题。起初,这感觉像是一个毫无意义的合规性练习——毕竟,BE已经使用了十多年,因此我们假设代码库中不会存在重大错误,例如内存分配错误和差一错误。然而,编写单元测试的行为迫使我们澄清内部文档,简化内部实现,并在某些情况下,消除不再使用的遗留代码。我们甚至发现了一些休眠的错误!
除了删除实验性功能外,我们还通过禁用计算密集但很少或从未提取有用取证数据的扫描器来提高BE的性能。
例如,我们现在默认禁用hiberfil(休眠文件)扫描器(xpress解压缩),因为我们缺乏可用于证明我们的实现正确性的测试向量,并且因为Windows可能不再使用BE1实现的压缩算法。
我们还禁用(默认情况下)扫描内存中192位AES(高级加密标准)密钥,因为AES很少在其192位模式下使用。
所有默认禁用的功能都可以使用命令行选项重新启用。
我们还删除了未使用的关键功能
• 使用互联网搜索引擎查看是否在开源程序、脚本甚至提供BE使用教程的博客文章中引用了程序的一些更晦涩的命令行选项。消除了用户社区未使用的晦涩选项。
• 据我们所知,没有人(原始开发人员除外)使用过BE的共享库来让程序的扫描器系统从C++或Python调用,因此我们将其删除,尽管将来可以重新实现它。
• 启动时将扫描器作为共享库加载的能力尚未针对BE2进行更新,尽管此更新微不足道,并且将在用户请求时实现。
尽管努力保持BE1和BE2之间的完全兼容性,但为了正确性和现代化,需要进行一些小的、不兼容的更改
• BE特征文件是UTF-8,但其中的一些信息是二进制的,必须进行转义。在BE1中,存在非Unicode字符,并以八进制转义。在BE2中,非Unicode字符以十六进制转义。
• 一个长期存在的问题是如何在UTF-8特征文件中表示UTF-16特征。BE1将UTF-16表示为八进制转义值,这很难阅读。BE2在第二列(“特征”)中将UTF-16转换为UTF-8,但在第三列(“上下文”)中将特征保留为(转义的)UTF-16。
• ZIP扫描器未正确报告ZIP解码数据块中工件的位置;这已得到纠正(稍后在本文中讨论)。
• 我们在BE2中放弃了对MD5(消息摘要算法)的支持,现在使用SHA-1(安全哈希算法-1)。2 (可悲的是,DF社区在转向SHA-256或SHA-3方面一直很慢。)
尽管努力消除所有内存副本,但BE2的临时版本比BE1.6慢得多。例如,在参考Mac mini上扫描2009-domexusers15(一个运行Windows XP的计算机上的NTFS文件,包含两个用户帐户)磁盘映像,BE1.6大约需要10分钟,但BE2开发版本需要70分钟。
长期以来,BE一直具有测量每个扫描器对运行时的贡献的能力。具体来说,它保留计数器(在 std::atomic<>
变量中),记录每个扫描器被调用的次数以及它花费的执行纳秒数。这些计数器显示,只有三个扫描器(rar
、net
和aes
)负责扫描所花费的大部分时间。
这些扫描器中的每一个都具有一个手动编码的循环,该循环扫描内存映像,扫描幻数。该循环已重新实现,现在为每个位置创建一个新的sbuf。我们删除了循环,并将扫描功能移动到sbuf类实现本身中,这消除了为每个字符创建和销毁sbuf的需要。
进行这些更改后,rar
扫描器不再是最慢的。现在最慢的扫描器是 net
、aes
和基于flex的 email
和 accts
(但奇怪的是,不是其他基于flex的扫描器)。
我们进行了迭代。通过微基准测试和更多测试,我们能够识别出许多其他加速机会。我们再怎么强调使用程序来测量和提高自身性能比使用现成的性能监控工具更有效,也不为过。
DF工具社区越来越多地将注意力转向为工具预期操作的各个方面开发规范和测试。毕竟,“未指定程序不可能是错误的;它只能是令人惊讶的。”25
我们对BE2进行了两种验证:正确性和吞吐量。对于正确性,BE2需要产生与BE1结果一样好的结果。对于吞吐量,BE2需要至少与BE1一样快。
当BE1和BE2的输出之间出现差异时,有些情况是BE2是正确的。在这些情况下,似乎BE1的输出从未经过详细验证。其中大多数与特征文件中递归分析的特征的位置有关。
所有DF工具都需要能够指定在证据中找到特征(例如电子邮件地址)的位置。对于BE,指定此类位置的能力因字节流可能需要解码、解压缩或以其他方式转换而变得复杂。BE1引入了取证路径的概念,该概念允许指定位置和一个或多个转换。例如,BE取证路径 456536-ZIP-1255117
被理解为特征位于从磁盘映像开始算起偏移456,536字节的膨胀ZIP流中偏移1,255,117字节的位置。
在单元测试的开发过程中,我们发现BE1报告的一些取证路径位置未能包括ZIP标头的六个字节。通过添加新代码纠正了这一点,以便sbuf类计算偏移量,而不是将计算硬编码到每个扫描器中。这个特定错误是在为取证路径打印机编写单元测试时发现的——取证路径打印机是BE的一部分,它读取取证路径,执行指定的转换,并执行证据的十六进制转储。尽管此代码已在BE中使用超过10年,但显然它从未为ZIP扫描器正常工作,并且BE的任何用户都从未报告过它无法工作。(gzip扫描器正确报告了取证路径。)
BE1代码库中的许多代码路径都是在特定的测试用例上经过艰苦开发的,但这些用例尚未作为单元测试添加到代码库中。它们已添加到BE2中,确保测试将使用GitHub的“Actions”持续集成工具在每次提交时自动运行。
测量BE处理磁盘映像或其他形式的电子证据的速度非常简单。解释速度的变化要困难得多。BE处理证据所花费的时间高度依赖于内容:包含许多压缩存档的磁盘映像将花费更长的时间来处理,因为每个压缩字节运行都将被解压缩并递归地重新分析。充满JPEG的磁盘将分析得很快,但如果启用雕刻模式1,则每个JPEG都需要复制到新文件中。但是,如果雕刻模式设置为2(默认值),则只会复制那些必须解压缩或以其他方式解码的JPEG——通常会被其他雕刻工具遗漏的JPEG。
BE还包含许多技术,可以在应用完整的递归分析之前丢弃数据。例如,重复数据不会被第二次分析,这具有保护程序免受压缩炸弹攻击的附带好处。同样,除非扫描器在其元数据中指示需要此类分析,否则由重复n-gram(例如,ABCABCABC...)组成的页面将不会被分析。
性能的另一个因素是运行程序的计算机。CPU内核的数量、RAM的数量、RAM的速度以及I/O系统的速度都会影响吞吐量。所有这些因素都与正在检查的证据相互作用:具有大量空白和重复扇区的磁盘映像将更多地受益于更快的I/O系统,而具有大量复杂数据结构的磁盘映像将更多地受益于额外的内核。
因此,吞吐量和基准测试结果最好使用生态学上有效的1 证据(例如实际的磁盘映像)报告。尽管此类媒体通常用于软件开发和内部基准测试,但出于隐私原因,它们往往不会公开发布。
表格 1 显示了 BE1.6 和 BE2 在三台不同参考计算机上,使用来自 Digital Corpora 集合的三个参考磁盘镜像的性能。这些磁盘镜像包括 nps-2009-ubnist1,这是一个 2.1GB 的可启动 USB 驱动器 Ubuntu Linux 磁盘镜像;nps-2009-domexusers,这是一个 42GB 的 Microsoft Windows 系统磁盘镜像,该系统曾在实验室中被多人使用;以及 nps-2011-2tb,这是一个 2.0TB 的磁盘镜像,其中包含整个 GovDocs1 语料库以及 Digital Corpora 的其他几个参考磁盘镜像。这些镜像是在 2009 年至 2011 年间在美国海军研究生院制作的,并托管在 Digital Corpora 网站 (digitalcorpora.org) 上。
nps-2009-ubnist1 和 nps-2009-domexusers 的时间是三次运行的平均值。nps-2009-ubnist1 和 nps-2009-domexusers 从系统固态硬盘 (SSD) 读取和写入,而 nps-2013-2tb 由于存储方面的考虑,从系统固态硬盘读取并写入外部 USB3 硬盘。BE1.6 的速度报告是针对启用标准 30 个默认扫描器的运行:accts、aes、base64、elf、email、evtx、exif、find、gps、gzip、hiberfile、httplogs、json、kml、msxml、net、ntfsindx、ntfslogfile、ntfsmft、ntfsusn、pdf、rar、sqlite、utmp、vcard、windirs、winlnk、winpe、winprefetch 和 zip。BE2 的速度是针对启用标准 29 个扫描器(hiberfil 已禁用)且禁用 AES192 密钥搜索的运行,以及添加 hiberfil 和 AES192 密钥搜索的 BE1.6 配置的运行。Apple M1 Pro 10 核处理器具有八个性能核心和两个效率核心。吞吐量已归一化为 BE1.6 在相同硬件和相同磁盘镜像上的速度;200% 的吞吐量意味着磁盘镜像的分析时间将缩短一半。
性能报告是使用三台 Apple Macintosh 计算机进行的。BE1.6 和 BE2 均在运行基准测试的计算机上使用 Apple 提供的当前 LLVM 编译器编译。所有编译均在 -O3
优化级别下完成,并禁用了 AddressSanitizer 和 ThreadSanitizer。我们报告了使用默认分析的 BE1.6 和 BE2。在此配置中,BE1.6 启用了 30 个扫描器,但 BE2 禁用了 hiberfil 和 AES192 密钥搜索。因此,BE2 也使用 BE1.6 配置(最右边的两列)进行了报告。如表 1 所示,BE2 在几乎所有情况下都比 BE1.6 快,尽管在具有更多核心的现代硬件上,速度提升更为明显。
这项多年的实践表明,即使工具看起来运行正常且没有错误,更新工具以适应当前的软件工程实践也是有价值的。我们建议对所有现代数字取证工具进行一次彻底检查,因为重写这些工具可能会使它们更快、更可靠。
阅读 Stroustrup 的书是这个项目耗时的准备工作,但非常值得投入。在开始大型 Python 项目之前,通读整个 Python 参考手册也获得了类似的好处。详细阅读实现语言和工具的所有开发者文档是一个好主意。投资于数字取证研究和工具的组织也应准备好进行长期投资,为有前途的工具的维护、调整和发展以及对开发人员的重点关注提供支持。
追求 60% 单元测试代码覆盖率所带来的代码质量提升令人震惊。AddressSanitizer 在发现各种错误方面的能力也令人惊讶。测试驱动开发3 和测试驱动重构11 应被采纳为主要工具,并且在开发过程中应始终启用 AddressSanitizer。
与 Python 相比,C++ 的速度是使用这种语言进行速度关键型应用程序的明显动机。然而,鉴于数字取证社区中 C++ 程序员的缺乏,BE 显然需要一个接口来允许调用 Python 扫描器。由于 Python 不是线程安全的,因此每个分析线程都需要一个单独的 Python 解释器。C++ 应该与精心设计的类一起使用,以提供内存安全,并且应该提供基于 Python 的 API 来访问其功能。
我们为 BE2 框架实现了 61% 的代码覆盖率,但仅为 BE2 代码库的其余部分(不包括框架)实现了 47% 的代码覆盖率。显然,仍有改进空间。
最后,文件系统级压缩和加密的日益普及,加上固态硬盘上 TRIM 命令的使用,意味着未来对原始存储设备进行批量数据分析可能比系统地从驻留文件中提取批量数据产生更少的数据。也就是说,在挂载的文件系统上使用 -r
(递归)选项运行 BE2,有一天可能会比在原始设备上运行它产生更有用的信息。理想情况下,可以在文件系统上运行 BE2,跟踪已扫描的扇区,然后以原始方式处理剩余的扇区。另一种方法是执行两次扫描:一次扫描挂载的文件,另一次扫描原始设备。这些策略的评估留待未来的工作。
1. Andrade, C. 2018. 研究设计、实施和评估中的内部、外部和生态效度。Indian Journal of Psychological Medicine 40(5), 498–499; https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6149308/.
2. Barker, R., Roginsky, A. 2019. 加密算法和密钥长度使用的过渡。美国国家标准与技术研究院,特别出版物 800-131A,修订版 2; https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf.
3. Beck, K. 2002. 测试驱动开发:实例. Addison-Wesley Professional.
4. BitCurator; https://bitcurator.net/.
5. Brooks, F. P. 1975. 人月神话——软件工程文集. Addison-Wesley.
6. Catch2. GitHub; https://github.com/catchorg/Catch2.
7. Cellebrite. Blackbag technology software user license agreements; https://cellebrite.com/en/blackbag-agreements/.
8. cxxopts. GitHub; https://github.com/jarro2783/cxxopts.
9. Donnelly, C., Stallman, R. M. Bison—the Yacc-compatible parser generator. Free Software Foundation; https://gnu.ac.cn/software/bison/manual/; ftp://ftp.gnu.org/pub/gnu/bison/.
10. Flex—fast lexical analyzer generator, GNU software package; https://github.com/westes/flex.
11. Fowler, M. 2018. 重构:改善现有代码的设计,第二版. Addison-Wesley Professional.
12. Garfinkel, S. L. 2013. 数字取证. American Scientist 101(5), 370-377; https://www.americanscientist.org/article/digital-forensics.
13. Garfinkel, S. L. 2013. 使用批量数据分析和 bulk_extractor 进行数字媒体分类. Computers and Security 32(C), 56–72; https://dl.acm.org/doi/10.5555/2748150.2748581.
14. Garfinkel, S. 1992. SBook: Simson Garfinkel's Address Book, Version 2.0, Simson Garfinkel and Associates; https://simson.net/ref/1992/SBook20.pdf.
15. Garfinkel, S. L., Farrell, P., Roussev, V., Dinolt, G. 2009. Bringing science to digital forensics with standardized forensic corpora. In Digital Investigation, Proceedings of the Ninth Annual Digital Forensic Research Workshop 6 (supplement); https://www.sciencedirect.com/science/article/pii/S1742287609000346.
16. Garfinkel, S., 2013. Digital media triage with bulk data analysis and bulk_extractor. Computers and Security 32, 56-72.
17. Garfinkel, S., Shelat, A. 2003. 逝去数据的追忆. IEEE Security and Privacy 1(1), 17–27; https://dl.acm.org/doi/abs/10.1109/MSECP.2003.1176992.
18. Liu, J., Moore, R. T. 2015. NSA 解密的情报监督委员会报告概述. Lawfare;
https://www.lawfareblog.com/overview-nsas-declassified-intelligence-oversight-board-reports.
19. Marlinspike, M. 2021. 从应用程序的角度利用 Cellebrite UFED 和物理分析仪中的漏洞. Signal; https://signal.org/blog/cellebrite-vulnerabilities/.
20. 国家司法研究所. 2004. 数字证据的法庭检验:执法指南; https://www.ojp.gov/pdffiles1/nij/199408.pdf.
21. 国防部副助理部长办公室,系统工程. 2011. 价值工程:最佳实践和工具指南; https://www.usace.army.mil/Portals/2/docs/Value%20Engineering/DoD%20SD-24_VE%20Handbook.pdf.
22. Serebryany, K., Bruening, D., Potapenko, A., Vyukov, D. 2012. AddressSanitizer:快速地址健全性检查器. In Proceedings of the Usenix Annual Technical Conference, 28; https://dl.acm.org/doi/10.5555/2342821.2342849.
23. Serebryany, K., Iskhodzhanov, T. 2009. ThreadSanitizer:实践中的数据竞争检测. In Proceedings of the Workshop on Binary Instrumentation and Applications, 62-71; https://dl.acm.org/doi/10.1145/1791194.1791203.
24. Stroustrup, B. 2013. C++ 程序设计语言,第四版. Addison-Wesley; https://www.stroustrup.com/4th.html.
25. Young, W. D., Boebert, W., Kain, R. 1985. 证明计算机系统安全. The Scientific Honeyweller 6(2), 18–27.
Simson Garfinkel 目前的研究兴趣是将人工智能应用于数字取证和数据治理。他是 会士和美国国家科学作家协会成员。
Jon Stewart 是 Aon Cyber Solutions 的副总裁,并在其 Stroz Friedberg 数字取证咨询实践中领导软件开发工作。
版权 © 2023 归所有者/作者所有。出版权已授权给 。
最初发表于 Queue 第 21 卷,第 1 期—
在 数字图书馆 中评论这篇文章
Ethan Miller, Achilles Benetopoulos, George Neville-Neil, Pankaj Mehra, Daniel Bittman - 远内存中的指针
有效地利用新兴的远内存技术需要考虑在父进程上下文之外操作丰富连接的数据。正在开发中的操作系统技术通过公开诸如内存对象和全局不变指针之类的抽象来提供帮助,这些抽象可以被设备和新实例化的计算遍历。这些想法将使运行在未来具有分离内存节点的异构分布式系统上的应用程序能够利用近内存处理来获得更高的性能,并独立扩展其内存和计算资源以降低成本。
Pat Helland - 自主计算
自主计算是一种业务工作模式,它使用协作来连接各自的领地及其特使。这种基于纸质表格的模式已经使用了几个世纪。在这里,我们解释领地、协作和特使。我们研究特使如何在自主边界之外工作,并在保持局外人身份的同时提供便利。我们还研究了如何启动、长时间运行并最终完成跨不同领地的工作。
Archie L. Cobbs - 持久性编程
几年前,我的团队正在为一个增强型 911 (E911) 紧急呼叫中心进行一个商业 Java 开发项目。我们试图使用传统的 Java over SQL 数据库模型来满足该项目的数据存储需求,但感到沮丧。在对该项目的特定需求(和非需求)进行一些反思之后,我们深吸一口气,决定从头开始创建我们自己的自定义持久层。
Torsten Ullrich - 真实世界的字符串比较
在许多语言中,字符串比较是初学者的一个陷阱。以任何 Unicode 字符串作为输入,即使对于高级用户,比较也常常会引起问题。Unicode 中不同字符的语义等价性要求在比较字符串之前对其进行规范化。本文展示了如何正确处理 Unicode 序列。两个字符串的相等性比较常常会引发关于按值比较、对象引用比较、严格相等和宽松相等之间差异的问题。最重要的方面是语义等价性。