MSRC(微软安全应急响应中心)致力于联合世界范围内的厂商和安全研究人员,共同防御安全威胁并推动提升微软的产品安全。MSRC会尽可能快地评估外部报告给他们的漏洞,但如果微软的工作人员必须在与研究人员确认漏洞发生的经过以后,才能确认该漏洞的情况,则可能会浪费时间。鉴于此,微软已经向工作人员发布了“时间旅行调试”(Time Travel Debugging,TTD)工具,以方便他们可以轻松提供完整的样本,缩短调查时间。借助于时间旅行调试技术,工作人员可以通过前进和回滚的方式来跟踪和分析漏洞,这样的话,他们不仅能够找出导致代码崩溃的所有用户输入,同时,还能深入了解漏洞本身的相关情况。经过测试,工作人员使用TTD工具后,确认漏洞发生原因的时间,只是原来时间的一半。
如果你想知道在哪里可以获得TTD工具以,及如何使用它,本文就能告诉你。
时间旅行调试介绍
无论你将时间旅行调试称之为“永恒的调试(Timeless debugging)”,“记录重放调试(record-replay debugging)”,“反向调试(reverse-debugging)”或“时间旅行调试(time travel debugging)”,这些概念都会表达出一个意思:我有记录程序执行的能力。一旦你有了这个记录,你就可以通过前进和回滚的方式来跟踪和分析漏洞,也可以与同事共享漏洞。更好的是,执行跟踪是一种确定性记录,每个看到该记录的人都会获取相同的信息。当问题软件的开发人员收到TTD跟踪记录时,他们甚至不需要重新还原漏洞,只需浏览跟踪文件,即可对漏洞做出分析。
通常有三个与时间旅行调试相关的关键组件:
1. 一个记录器,记录所有发生的事;
2.一个跟踪文件,记录所有生成的文件;
3.一个重放器,重放所有发生的事。
究其本质来讲,TTD其实就是个调试器
一说起调试器,我们就非常熟悉了,这几十年来,调试技术也没有发生过什么质的提升。通常情况下,整个过程分为两步:
1.观察调试器运行过程中的行为:在此步骤中,你将重新创建一个类似于漏洞出现的模拟环境。该过程就像在你的设备上运行简单的概念验证程序并观察漏洞检查一样简单,也可以像使用特定的软件配置设置一个完整的基础设施来运行错误代码一样复杂。如果漏洞报告足够准确和详细,就可以正确地模拟漏洞发生的过程。
2.了解问题发生的原因,这就是调试器的用武之地。无论体系结构和平台如何,你对调试器的期望就是能够精确控制目标的执行(指令、源代码行),进而设置断点,编辑内存以及编辑处理器上下文。这些操作下来,分析也就基本上完成了。但问题是,这样做耗时耗力。
无论你是报告漏洞的研究人员还是微软公司来确认漏洞的工作人员,Time Travel Debugging都可以帮助你快速进行漏洞的调查,并且用最短的时间和流程来反馈漏洞发生的细节。
微软开发的用于时间旅行调试的技术被称为“TTD”,该技术在2006年左右,被微软研究院发布,当时的发布标题是“用于指令级跟踪和程序执行分析的框架”,后来经过微软的调试团队的改进和产品化。该项目依赖于代码模拟来记录重放所需的每个事件,以重现完全相同的执行过程以及完全相同的输入和输出的指令序列。模拟器跟踪的数据包括内存读取,寄存器值,线程创建,模块加载等。
记录/重放
记录软件CPU, TTDRecordCPU.dll被注入目标进程并劫持线程的控制流。模拟器将本机指令解码为内部自定义中间语言(以模仿简单的RISC指令),缓存块并执行它们。从现在开始,它会执行这些线程,并在事件发生时进行回调。例如,当转换指令时,这些回调允许跟踪文件写入器组件,以收集软件CPU重放所需的信息,而这些信息都是基于跟踪文件的。
重放软件CPU ,TTDReplayCPU.dll与记录CPU共享大部分相同的代码库。但不同的是,它不是读取目标内存,而是直接通过跟踪文件加载数据。这允许你在不需要运行程序的情况下,重放整个程序的执行过程。
跟踪文件
跟踪文件是文件系统中的一个常规文件,以“run”扩展名结束。该文件使用自定义文件格式和压缩来优化文件大小。你还可以将此文件视为填充了丰富信息的数据库,要快速访问调试器所需的信息,“WinDbg 预览”会在你第一次打开跟踪文件时创建一个索引文件。创建过程通常需要持续几分钟,通常,此索引大约是原始跟踪文件的一到两倍。例如,在我的设备上跟踪程序ping.exe会生成一个37MB的跟踪文件和一个41MB的索引文件。大约有1973647条指令(每条指令大约132位)。请注意,在此示例中,跟踪文件非常小,以至于跟踪文件的内部结构占用了大部分空间。而较大的执行跟踪通常包含的指令,大约每条1到2位。
使用WinDbg预览记录跟踪的情况
现在你已熟悉TTD的各个功能部分了,下面介绍如何使用它们。
获取TTD:TTD目前在Windows 10上可以通过Microsoft商店中的“WinDbg预览”应用程序获得:https://www.microsoft.com/en-us/p/windbg-preview/9pgjgd53tn86?activetab=pivot:overviewtab。
安装应用程序后,“Time Travel Debugging - Record a trace” 教程将引导你完成记录你的第一个执行跟踪。
使用TTD构建自动化调查进程
最近对Windows调试器的一个改进是,增加了调试器数据模型,这样你就可以通过JavaScript(以及C ++)与之交互。对数据模型的详细介绍已经超出了本文的范围,但你可以将其视为向用户和调试器扩展使用和公开结构化数据的一种方式。TTD通过在@$cursession.TTD 和 @$curprocess.TTD节点下引入非常强大且独特的功能来扩展数据模型。
TTD.Calls是一个函数,你可以通过该函数找到诸如“给我调用foo!bar的每个位置”或“是否有一个foo!bar在追踪中返回10”等问题的答案。另外,就像数据模型中的每个集合一样,你可以使用LINQ运算符查询它们。TTD.Calls对象看起来如下所示:
API完全隐藏了ISA的特定细节,因此你可以构建与架构无关的查询。
TTD.Calls:重建stdout
为了演示这些功能是多么的强大且使用起来非常简单,工作人员记录了“ping.exe 127.0.0.1”的执行,并从记录中重新生成控制台输出。
在JavaScript中构建它非常简单,只需要3步:
1.按时间位置排序每个调用后,遍历对msvcrt的每个调用;
2.读取第二个参数指向的几个字节(数量在第三个参数中);
3.显示累计结果。
TTD.Memory:查找触及LastErrorValue的每个线程
TTD.Memory是一个功能强大的API,它允许你在全部的内存中查询特定类型(读、写、执行)的内存访问跟踪文件。如下所示的就是每一个内存查询的结果对象:
这个结果代表了完成的内存访问类型、启动和完成的时间戳、访问内存的线程、访问的内存地址、访问的位置以及读取/写入/执行的值。
为了演示它的强大功能,工作人员创建了另一个脚本,在应用程序每次写入当前线程的环境块中的LastErrorValue时,都会收集调用堆栈:
1.遍历每个访问&@ $ teb-> LastErrorValue的内存写入;
2.前往目的地,转储当前的调用堆栈;
3.显示最终的结果。
请注意,你可以使用更多的TTD特定对象来获取与跟踪中发生的事件相关的信息,线程等信息,所有这些都记录在“时间旅行调试对象简介”页面上。
总结
Time Travel Debugging是安全软件工程师的强大工具,也可用于恶意软件分析,漏洞搜索和性能分析。我们希望TTD的这个介绍对你有帮助,并鼓励你使用它为你发现的安全问题创建执行跟踪。TTD生成的跟踪文件,会以压缩格式呈现,工作人员建议在将文件上传到你喜欢的文件存储服务之前使用7zip(通常将文件缩小到原始大小的10%)。
实践中的常见问题解答
问:我可以在重放时间编辑内存吗?
答:不能,由于记录器只保存重放程序中特定执行路径所需的内容,因此它无法保存足够的信息来重新模拟不同的执行。
问:为什么在读取文件时看不到字节?
答:记录器只负责记录,这意味着如果另一个对象(这里的NT内核,但也可能是写入共享内存部分的另一个进程)将数据写入内存,则模拟器无法知道它的存在。因此,如果目标程序从不读取这些值,那么它们将永远不会出现在跟踪文件中。如果稍后读取它们,那么当模拟器再次获取内存时,它们的值就会在读取文件中被看到。
问:我需要私有符号或源代码吗?
答:你不需要源代码或私有符号来使用TTD,记录器使用本机代码,并且不需要任何额外的工作来完成其工作。如果私有符号和源代码可用,则调试器将使用它们并提供与使用源/符号进行调试时相同的体验。
问:我可以记录内核模式执行吗?
答:TTD仅用于用户模式执行。
问:记录器是否支持自修改代码?
答:支持!
问:是否存在已知的不兼容性问题?
答:有一些,建议你可以在“注意事项”中阅读它们。
问:我是否需要WinDbg预览才能记录跟踪呢?
答:是的,截至目前,TTD记录器功能只是“WinDbg预览”的一部分功能,只能从Microsoft Store进行下载。