本研究由来自南京大学、史蒂文斯理工学院和宾夕法尼亚州立大学的科研人员合作完成。主要作者包括 Dongliang Mu(南京大学)、Yunlan Du(南京大学)、Jianhao Xu(南京大学)、Jun Xu(史蒂文斯理工学院)、Xinyu Xing(宾夕法尼亚州立大学)、Bing Mao(南京大学)和 Peng Liu(宾夕法尼亚州立大学)。该项研究成果以标题“POMP++: Facilitating Postmortem Program Diagnosis with Value-Set Analysis”发表于计算机软件工程领域的国际知名期刊 IEEE Transactions on Software Engineering,发表于2021年9月(第47卷第9期)。
本研究属于软件工程与系统安全交叉领域,具体聚焦于事后程序诊断。随着现代软件系统复杂度的增加,程序崩溃难以避免。软件厂商每天可能收到海量崩溃报告,因此,高效、准确的事后诊断技术至关重要。传统的事后诊断主要依赖核心转储(core dump),它仅提供了程序崩溃时的内存快照,信息有限,难以完整重构导致崩溃的数据流和控制流。
近年来,硬件辅助的处理器追踪技术(如Intel PT)的出现为事后诊断带来了新机遇。该技术能以极低的运行时开销记录程序执行的指令轨迹,并在程序崩溃时将这些轨迹整合到核心转储中,形成一种新型的“事后崩溃工件”。然而,现有分析工具(如作者团队早期工作POMP)主要依赖已解析的运行时信息进行反向执行,在面对长指令轨迹时,存在数据流恢复能力有限和分析效率低下的问题。特别是当需要验证内存别名关系(即两个不同的符号化内存访问是否指向同一物理地址)时,POMP采用递归的假设检验(Hypothesis Testing, HT)方法,可能导致计算复杂度指数级增长,难以应对大规模的日常崩溃分析。
基于此,本研究旨在克服现有工作的局限性,目标是开发一种能够自动化、准确且高效地分析事后崩溃工件,并精确定位导致崩溃的根本原因(即关键程序语句)的工具。为此,作者提出了POMP++,其核心创新在于将值集分析这一高效的静态分析技术引入到动态反向执行框架中,以增强内存别名验证能力,从而更全面地恢复数据流,并大幅提升分析效率。
POMP++ 的工作流程是一个将静态分析(VSA)与动态反向执行深度融合的迭代过程,主要包含以下几个核心步骤:
1. 事后崩溃工件的生成与预处理 * 研究对象:崩溃的进程。研究假设崩溃会产生包含Intel PT指令轨迹和内存快照的核心转储文件。 * 处理方式:研究团队在Linux 32位系统(内核4.4)上实现了POMP++原型,并集成了Intel PT追踪子系统。该系统配置PT以TOPA模式运行,使用16MB的循环缓冲区记录用户空间指令流。当程序崩溃时,操作系统将包含PT轨迹的缓冲区内容整合到核心转储中。POMP++的解析模块(基于libelf和libdisasm)负责读取核心转储,分离出指令轨迹和崩溃时的内存/寄存器状态。由于PT轨迹可能不包含完整的函数上下文(例如从函数中间开始记录),POMP++会利用开源项目Angr的CFG恢复功能,将轨迹的起点向前扩展到函数入口,以确保后续静态分析的完整性。
2. 定制化的值集分析 * 研究目的:在反向执行开始前,对单一的、线性的执行轨迹进行静态分析,获取每个内存访问操作(a-loc)的地址值集(Value-Set),为后续别名验证提供先验知识。 * 定制化方法: * 内存区域识别:扩展了传统VSA的区域识别模式。除了通过栈帧指针(ebp/esp)识别栈、通过动态分配函数(如malloc)识别堆外,还总结了识别全局变量、静态变量、线程局部存储(TLS)变量以及特定函数(如仅操作自身栈或TLS的函数)访问模式的规则。 * 单一路径分析:与对整个二进制文件进行分析不同,POMP++仅对崩溃相关的执行轨迹进行分析。这避免了间接跳转/调用目标不确定的问题,简化了分析。VSA将每个a-loc(寄存器或内存位置)的值集表示为一个n元组(对应n个内存区域),每个元素是一个偏移量范围。算法沿着执行轨迹(必要时从函数入口开始)向前传播这些值集。 * 算法输出:为轨迹中的每个内存访问指令(如 [eax], [ebp+0x8])计算出一个可能的地址集合。例如,[eax] 可能被分析为指向堆区域偏移[0x4, 0x4]的位置。
3. VSA增强的反向执行与数据流恢复 * 核心算法:这是POMP++最核心的步骤,目标是逆向恢复程序崩溃前每一步的执行状态(内存足迹)。 * 流程细节: * a. 使用-定义链构造:算法从崩溃点开始,反向遍历指令轨迹。对于每条指令,基于其语义提取操作数的使用和定义,并将它们链接成一个使用-定义链。例如,mov [eax], 0x0 定义(写入)了内存 [eax],而 add ebx, [eax] 使用了内存 [eax]。算法通过检查变量定义的可达性(能否无干扰地传递到其使用点)来尝试解析变量的值。当遇到地址未知的内存写操作(如 def:[eax])时,保守地将其视为一个“干扰标签”,暂时阻断可能与之存在别名关系的上游数据流传播。 * b. 内存别名验证:这是解决数据流恢复阻塞的关键。当需要判断两个内存访问(如 [ebp+0x8] 和 [eax])是否为别名时,POMP++采用了 VSA+HT混合方案(经评估为最优方案): 1. VSA优先查询:首先查询定制化VSA的结果,比较两个内存访问的地址值集。如果值集没有重叠,则判定为非别名;如果值集完全相同且为单值,则判定为别名。若VSA无法给出确定结论(大部分情况),则进入下一步。 2. HT补充验证:对于VSA无法判定的情况,启用动态的假设检验。算法会生成两个对立的假设(是别名/不是别名),并基于当前的约束集(从指令语义和使用-定义关系中提取)进行反向模拟。如果某个假设导致约束冲突(如数据依赖不一致或产生无效值),则该假设被拒绝,接受另一个假设。 * c. 信息迭代与固定点计算:VSA和反向执行之间存在双向反馈的潜力。恢复出的运行时信息(如具体的寄存器值)可以用于细化VSA的计算(例如,将未知值集具体化),而更精确的VSA结果又能进一步辅助后续的别名验证。POMP++实现了这种迭代机制(算法中称为“固定点”计算),直至恢复的数据流不再增长为止。 * 特殊处理:对于系统调用,POMP++根据调用约定分析其对用户空间内存和寄存器的影响,并在使用-定义链中引入相应的定义或干扰标签。
4. 基于反向污点分析的根本原因定位 * 研究目的:在完整恢复数据流的基础上,自动化地精确定位真正导致崩溃的指令,而非整条长轨迹。 * 工作流程: * 确定污点源:根据崩溃类型确定污点源(sink)。如果是执行非法指令,则将程序计数器(eip)作为污点源;如果是解引用非法地址,则将持有该地址的寄存器作为污点源。 * 反向污点传播:从污点源开始,沿着上一步构建的使用-定义链反向传播污点。传播规则遵循定义的可达性,即污点一个变量后,会追溯并污点所有能无干扰到达该变量使用的定义。不仅污点被访问的内存,也污点其基址和索引寄存器。 * 输出结果:被污点标记的指令集合即为算法认为与崩溃相关的关键指令。POMP++会高亮显示这些指令,极大缩小了开发人员需要人工审查的代码范围。
1. VSA定制化与混合方案的有效性验证 * 结果:评估使用了来自31个真实世界安全漏洞的33个程序崩溃案例。实验表明,纯VSA方案在内存地址恢复能力上弱于纯HT方案(在33个案例中,HT在24个案例中优于或等于VSA)。然而,VSA+HT混合方案在所有案例中的内存地址恢复能力均超过纯HT方案,平均提升了12%的可解析内存地址。固定点方案恢复能力最强,平均提升16%。 * 解释与逻辑关系:这一结果证实了静态分析的局限性(对不完整轨迹分析可能不精确),但也凸显了其与动态分析结合的价值。VSA能解决一些HT因缺乏运行时信息而无法判定的别名问题(如图5中gdb案例),而HT能弥补VSA因静态近似导致的误判(如图6中unrar案例)。两者结合实现了优势互补,为后续精确定位根本原因提供了更完整的数据流基础。
2. 根本原因定位的成功率 * 结果:以人工分析结果为基准,POMP++的VSA+HT方案和固定点方案在33个案例中成功定位了31个案例的根本原因。纯HT方案漏报了2个案例(gdb-7.5.1和jpegtoavi-1.5),纯VSA方案漏报了3个案例(unrar-3.9.3, jpegtoavi-1.5,以及aireplay-ng)。 * 解释:失败案例(aireplay-ng和0verkill)是由于所有方案均无法恢复足够的数据流。成功案例的对比清晰地展示了混合方案的优势:在jpegtoavi案例中(图7),需要同时确认两个非别名关系,VSA和HT各自只能解决一个,只有结合两者才能成功定位。这直接证明了POMP++设计理念的正确性——静态与动态分析的协同能攻克单一方法无法解决的难题。
3. 分析效率的显著提升 * 结果:效率测试显示,纯VSA方案速度最快,平均耗时仅为纯HT方案的1/15。VSA+HT混合方案在保持高诊断率的同时,平均减少了60%的执行时间。而固定点方案由于迭代计算,耗时反而高于纯HT方案。 * 解释与贡献:时间节省主要来源于用高效的VSA查询替代了大量递归的HT操作。这验证了研究的另一个核心目标:提升分析效率以应对海量崩溃报告。VSA+HT方案在效果和效率之间取得了最佳平衡,使其更具实用价值。
4. 系统实现与评估 * 结果:POMP++原型系统包含约30,000行C代码,实现了84个不同的指令处理器。评估涵盖了从大型软件(如binutils)到小型工具(如stftp)的多种程序,覆盖了堆栈溢出、整数溢出、释放后使用等多种漏洞类型,证明了其通用性。
本研究成功设计并实现了POMP++,一个利用值集分析增强事后程序诊断的系统。核心结论是:将静态值集分析融入动态反向执行框架,可以显著提高数据流恢复的完整性和根本原因定位的准确性,同时大幅降低分析的时间开销。
科学价值: 1. 方法论创新:提出了一种静动态结合的程序分析新范式,为事后诊断领域提供了新的技术思路。特别是定制化的VSA用于单一路径分析,以及VSA与HT双向反馈的混合方案,都具有很强的创新性。 2. 问题深化:不仅解决了“能否定位”的问题,还深入探讨了“如何更高效、更准确定位”的问题,并通过严谨的实验对比了不同技术路径的优劣。
应用价值: 1. 助力软件调试与维护:能够自动化地从复杂的崩溃工件中提取出极少量的关键指令,极大降低了软件开发者和安全分析师的诊断负担,使调试工作更加高效。 2. 提升安全应急响应能力:对于由安全漏洞引发的崩溃,能够快速定位漏洞触发点,有助于及时理解漏洞机理和制定修复方案。 3. 可扩展的框架:尽管当前针对32位Linux实现,但其设计理念(VSA增强的反向执行)是平台无关的,为后续扩展(如支持64位、其他硬件追踪技术)奠定了基础。
论文还坦诚讨论了POMP++当前的局限性及未来方向: * 多线程问题:当前版本仅分析崩溃线程自身的轨迹,可能错过由线程间交互导致的漏洞。作者指出,结合Intel PT的时间信息有望合成完整的多线程执行轨迹,这是未来的一个改进方向。 * 即时编译代码:POMP++从磁盘上的二进制文件获取指令,无法分析运行时生成的JIT(Just-In-Time)本地代码。未来需要增加对可执行内存的监控和JIT代码的转储能力。 * 威胁模型清晰:研究明确了其适用的威胁模型,即关注能产生包含PT轨迹的核心转储的进程崩溃,并假设轨迹包含了导致崩溃的完整指令序列,这使得研究的范围和假设条件非常清晰。
POMP++是一项在软件工程和安全领域具有重要价值的扎实研究,它通过精巧的设计和深入的实验,有效推进了事后自动化诊断技术的实用化进程。