程序插桩方法、装置、设备和存储介质转让专利

申请号 : CN202211350276.3

文献号 : CN115617687B

文献日 :

基本信息:

PDF:

法律信息:

相似专利:

发明人 : 张超殷婷婷

申请人 : 清华大学

摘要 :

本申请提供一种程序插桩方法、装置、设备和存储介质,涉及计算机技术领域。该方法包括:获取待分析程序,待分析程序为二进制程序;基于分析需求,识别待分析程序中的冗余指令,确定待插入的桩函数,冗余指令为与待分析程序的原始语义和/或分析需求无关的代码,所述冗余指令的位置为所述桩函数的插桩点;根据桩函数所在的位置和插桩点之间的偏移量,对函数调用指令中的地址偏移量进行赋值;将冗余指令替换为函数调用指令。通过移除待分析程序中的冗余指令,可以在不破坏待分析程序原始结构和指令偏移的前提下,留出插桩空间,向待分析程序注入新的代码,具有较高的稳定性,不仅适用于中小型程序,还适用于操作系统内核等复杂的大型程序。

权利要求 :

1.一种程序插桩方法,所述方法用于闭源程序,其特征在于,包括:获取待分析程序,所述待分析程序为二进制程序;

基于分析需求,识别所述待分析程序中的冗余指令,确定待插入的桩函数,所述冗余指令为与所述待分析程序的原始语义和/或所述分析需求无关的代码,所述冗余指令的位置为所述桩函数的插桩点,所述桩函数用于向所述待分析程序中增加符合所述分析需求的功能、保存所述待分析程序执行的上下文信息以及基于所述上下文信息恢复执行所述待分析程序,其中,在模糊测试场景下,所述冗余指令包括程序运行时的防护指令、用于日志记录的指令和由于编译器优化缺陷产生的可以合并或删除的指令;

根据所述桩函数所在的位置和所述插桩点之间的偏移量,对函数调用指令中的地址偏移量进行赋值;

将所述冗余指令替换为函数调用指令,所述冗余指令替换后无需补充执行。

2.根据权利要求1所述的程序插桩方法,其特征在于,所述基于分析需求,识别所述待分析程序中的冗余指令,包括:基于所述分析需求,确定所述冗余指令;

对所述待分析程序中的冗余指令进行扫描与记录。

3.根据权利要求2所述的程序插桩方法,其特征在于,对所述待分析程序中的冗余指令进行扫描,包括:对于定长指令集,通过指令字节码扫描所述冗余指令;

对于非定长的指令集,通过反汇编工具扫描所述冗余指令。

4.根据权利要求1至3中任一项所述的程序插桩方法,其特征在于,还包括:若所述桩函数与所述待分析程序之间以动态链接库的形式进行链接,则所述函数调用指令基于位置无关函数的跳转表调用所述桩函数,所述跳转表包括程序链接表或桩函数表;

若所述待分析程序的文件结构无法增加位置无关函数的跳转表,则所述函数调用指令基于非关键函数的表项调用所述桩函数,所述非关键函数包括日志函数。

5.根据权利要求1至3中任一项所述的程序插桩方法,其特征在于,所述将所述冗余指令替换为函数调用指令之后,还包括:确定所述冗余指令所占第一空间是否大于所述函数调用指令所占的第二空间;

响应于所述第一空间大于所述第二空间,根据多余空间部分在所述函数调用指令之前和/或所述函数调用指令之后填充空指令。

6.根据权利要求1至3中任一项所述的程序插桩方法,其特征在于,所述上下文信息包括寄存器数值与内存状态。

7.一种程序插桩装置,其特征在于,包括:

获取模块,用于获取待分析程序,所述待分析程序为二进制程序;

识别模块,用于基于分析需求,识别所述待分析程序中的冗余指令,确定待插入的桩函数,所述冗余指令为与所述待分析程序的原始语义和/或所述分析需求无关的代码,所述冗余指令的位置为所述桩函数的插桩点,所述桩函数用于向所述待分析程序中增加与所述分析需求对应的功能、保存所述待分析程序执行的上下文信息以及基于所述上下文信息恢复执行所述待分析程序,其中,在模糊测试场景下,所述冗余指令包括程序运行时的防护指令、用于日志记录的指令和由于编译器优化缺陷产生的可以合并或删除的指令;

赋值模块,用于根据所述桩函数所在的位置和所述插桩点之间的偏移量,对函数调用指令中的地址偏移量进行赋值;

替换模块,用于将所述冗余指令替换为函数调用指令,所述冗余指令替换后无需补充执行。

8.根据权利要求7所述的程序插桩装置,其特征在于,所述识别模块具体用于:基于所述分析需求,确定所述冗余指令;

对所述待分析程序中的冗余指令进行扫描与记录。

9.一种电子设备,其特征在于,包括:存储器和处理器;

所述存储器用于存储程序指令;

所述处理器用于调用所述存储器中的程序指令执行如权利要求1至6中任一项所述的程序插桩方法。

10.一种计算机可读存储介质,其特征在于,所述计算机可读存储介质中存储有计算机执行指令,所述计算机执行指令被执行时用于实现如权利要求1至6任一项所述的程序插桩方法。

说明书 :

程序插桩方法、装置、设备和存储介质

技术领域

[0001] 本申请涉及计算机技术领域,尤其涉及一种程序插桩方法、装置、设备和存储介质。

背景技术

[0002] 网络空间是现代社会的重要组成部分,广泛应用于工业生产等重要场景中。作为网络空间的重要组成部分,程序的功能也越来越多样化和复杂化,因此,对程序测试的要求也随之提高,需要通过程序测试及时发现程序问题,避免程序存在故障或安全漏洞,给用户日常使用等带来负面影响。
[0003] 程序插桩技术,作为一种程序分析的辅助手段,被广泛应用于程序的动态测试中,该技术通过向程序中注入新的代码,来获取程序在执行过程中的内部状态,给动态测试提供反馈信息;或改变程序原本的执行流程,以适应于特殊的分析需求。
[0004] 其中,较具代表性的程序插桩技术的应用案例之一是模糊测试中的代码覆盖率反馈插桩。模糊测试被认为是目前有效的自动化漏洞挖掘技术,通过大量生成测试用例,反复执行测试目标,尝试触发程序中的漏洞。模糊测试通常以测试用例触发的代码量来评价测试用例的质量,然而程序通常不会主动记录、反馈代码执行情况,所以需要使用程序插桩技术在程序的基本块中加入指令,在基本块被执行时记录信息。除了动态测试,程序插桩技术还可以被引用在程序补丁等丰富的场景中,例如在关键功能前增加条件检查,或者在相关联的功能中批量增加日志信息等。总体来说,程序插桩技术为程序开发、测试、维护的整个流程极大地增加了便捷性与灵活性。
[0005] 相关技术中,程序插桩技术通常仅支持源码插桩,即源代码插桩,在编译阶段向程序注入代码。如开发框架(Spring boot)支持开发者在程序中主动定义“通知”行为,在指定功能开始执行的前后,调用自定义代码;编译器,包括编译器集(GNU  Compiler Collection,简称GCC)与轻量级编译器(clang),支持对源码进行自动化的代码覆盖率插桩,能够在编译过程中向每个基本块增加代码,记录基本块是否被触发。然而,源码常常是难以获得的,如大量的商用软件、操作系统出于商业需求,不开放源码;而一些开发时间较早的软件,也常常存在源码丢失的问题。这些情况,催生了大量闭源程序(即二进制程序)分析的需求,因此,亟需提供一种适用于闭源程序的有效的程序插桩方案,以辅助动态测试。

发明内容

[0006] 本申请提供一种程序插桩方法、装置、设备和存储介质,用以提供一种适用于闭源程序的有效的程序插桩方案,以辅助动态测试。
[0007] 第一方面,本申请提供一种程序插桩方法,包括:获取待分析程序,待分析程序为二进制程序;基于分析需求,识别待分析程序中的冗余指令,确定待插入的桩函数,冗余指令为与待分析程序的原始语义和/或分析需求无关的代码,冗余指令的位置为桩函数的插桩点,桩函数用于向待分析程序中增加与分析需求对应的功能、保存待分析程序执行的上下文信息以及基于上下文信息恢复执行待分析程序;根据桩函数所在的位置和插桩点之间的偏移量,对函数调用指令中的地址偏移量进行赋值;将冗余指令替换为函数调用指令。
[0008] 一种可能的实施方式中,基于分析需求,识别待分析程序中的冗余指令,包括:基于分析需求,确定冗余指令;对待分析程序中的冗余指令进行扫描与记录。
[0009] 一种可能的实施方式中,对待分析程序中的冗余指令进行扫描,包括:对于定长指令集,通过指令字节码扫描冗余指令;对于非定长的指令集,通过反汇编工具扫描冗余指令。
[0010] 一种可能的实施方式中,若程序插桩方法中的桩函数与待分析程序之间以动态链接库的形式进行链接,则函数调用指令基于位置无关函数的跳转表调用桩函数,跳转表包括程序链接表或桩函数表;若待分析程序的文件结构无法增加位置无关函数跳转表,则函数调用指令基于非关键函数的表项调用桩函数,非关键函数包括日志函数。
[0011] 一种可能的实施方式中,将冗余指令替换为函数调用指令之后,还包括:确定冗余指令所占第一空间是否大于函数调用指令所占的第二空间;响应于第一空间大于第二空间,根据多余空间部分在函数调用指令之前和/或函数调用指令之后填充空指令。
[0012] 一种可能的实施方式中,上下文信息包括寄存器数值与内存状态。
[0013] 第二方面,本申请提供一种程序插桩装置,包括:获取模块,用于获取待分析程序,待分析程序为二进制程序;识别模块,用于基于分析需求,识别待分析程序中的冗余指令,确定待插入的桩函数,冗余指令为与待分析程序的原始语义和/或分析需求无关的代码,冗余指令的位置为桩函数的插桩点,桩函数用于向待分析程序中增加与分析需求对应的功能、保存待分析程序执行的上下文信息以及基于上下文信息恢复执行待分析程序;赋值模块,用于根据桩函数所在的位置和插桩点之间的偏移量,对函数调用指令中的地址偏移量进行赋值;替换模块,用于将冗余指令替换为函数调用指令。
[0014] 一种可能的实施方式中,识别模块可以用于基于分析需求,识别待分析程序中的冗余指令,具体为:基于分析需求,确定冗余指令;对待分析程序中的冗余指令进行扫描与记录。
[0015] 一种可能的实施方式中,识别模块还可以用于:对于定长指令集,通过指令字节码扫描冗余指令;对于非定长的指令集,通过反汇编工具扫描冗余指令。
[0016] 一种可能的实施方式中,还包括:在桩函数与待分析程序之间以动态链接库的形式进行链接时,函数调用指令基于位置无关函数的跳转表调用桩函数,跳转表包括程序链接表或桩函数表;若待分析程序的文件结构无法增加位置无关函数跳转表,则函数调用指令基于非关键函数的表项调用桩函数,非关键函数包括日志函数等。
[0017] 一种可能的实施方式中,替换模块还可以用于:确定冗余指令所占第一空间是否大于函数调用指令所占的第二空间;响应于第一空间大于第二空间,根据多余空间部分在函数调用指令之前和/或函数调用指令之后填充空指令。
[0018] 一种可能的实施方式中,识别模块中识别到的待插入的桩函数保存的待分析程序执行的上下文信息包括寄存器数值与内存状态。
[0019] 第三方面,本申请提供一种电子设备,包括:存储器和处理器。存储器用于存储程序指令;处理器用于调用存储器中的程序指令执行第一方面的程序插桩方法。
[0020] 第四方面,本申请提供一种计算机可读存储介质,计算机可读存储介质中存储有计算机执行指令,计算机执行指令被执行时实现第一方面的程序插桩方法。
[0021] 第五方面,本申请提供一种计算机程序产品,计算机程序产品包含计算机程序,计算机程序被执行时用于实现第一方面的程序插桩方法。
[0022] 本申请提供的程序插桩方法、装置、设备和存储介质,通过获取待分析程序,待分析程序为二进制程序;基于分析需求,识别待分析程序中的冗余指令以及待插入的桩函数,冗余指令为与待分析程序的原始语义和/或分析需求无关的代码,冗余指令的位置为桩函数的插桩点,桩函数用于向待分析程序中增加与分析需求对应的功能、保存待分析程序执行的上下文信息以及基于上下文信息恢复执行待分析程序;根据桩函数所在的位置和插桩点之间的偏移量,对函数调用指令中的地址偏移量进行赋值;将冗余指令替换为函数调用指令。由于本申请是通过移除待分析程序中的冗余指令进行插桩,移除此类指令不影响待分析程序正常运行,从而在不破坏待分析程序原始结构和指令偏移的前提下,留出插桩空间,向待分析程序注入新的代码,具有很高的稳定性,不仅适用于中小型用户态程序,还可以应用于操作系统内核等复杂的大型程序,或对稳定性需要高的其他程序,应用范围更广。

附图说明

[0023] 此处的附图被并入说明书中并构成本说明书的一部分,示出了符合本申请的实施例,并与说明书一起用于解释本申请的原理。
[0024] 图1是本申请一实施例提供的程序插桩前后的示意图;
[0025] 图2是本申请一实施例提供的程序插桩方法的流程示意图;
[0026] 图3是本申请一实施例提供的在macOS驱动中实现代码覆盖率反馈插桩的操作示意图;
[0027] 图4是本申请一实施例提供的程序插桩装置的结构示意图;
[0028] 图5是本申请一实施例提供的电子设备的结构示意图。
[0029] 通过上述附图,已示出本申请明确的实施例,后文中将有更详细的描述。这些附图和文字描述并不是为了通过任何方式限制本申请构思的范围,而是通过参考特定实施例为本领域技术人员说明本申请的概念。

具体实施方式

[0030] 这里将详细地对示例性实施例进行说明,其示例表示在附图中。下面的描述涉及附图时,除非另有表示,不同附图中的相同数字表示相同或相似的要素。以下示例性实施例中所描述的实施方式并不代表与本申请相一致的所有实施方式。相反,它们仅是与如所附权利要求书中所详述的、本申请的一些方面相一致的装置和方法的例子。
[0031] 本申请的说明书和权利要求书中的术语“第一”、“第二”等是用于区别类似的对象,而不必用于描述特定的顺序或先后次序。应该理解这样使用的数据在适当情况下可以互换,以便这里描述的本申请的实施例例如能够以除了在这里图示或描述的那些以外的顺序实施。此外,术语“包括”和“具有”以及他们的任何变形,意图在于覆盖不排他的包含,例如,包含了一系列步骤或单元的过程、系统、产品或设备不必限于清楚地列出的那些步骤或单元,而是可包括没有清楚地列出的或对于这些过程、产品或设备固有的其它步骤或单元。
[0032] 相关技术中,闭源程序的插桩技术还较为稀少,已有的工具也往往存在稳定性与可扩展性的问题。闭源程序插桩技术包括静态插桩技术和动态插桩技术,其中,静态插桩技术常因为破坏指令间的偏移而引入引用歧义,使程序由于代码插桩而产生异常,导致程序插桩后稳定性下降。这些技术支持的程序类型也较为有限,如静态二进制重写工具(e9patch)对程序布局有很大的修改,通常只适用于用户态程序,且仅支持x64指令集;改造编译器二进制重写工具(Retrowrite)不能支持常用语言(如c++语言)开发的二进制程序。动态插桩技术常依赖于虚拟化、指令翻译等技术,有较高的运行环境需求和很大的运行开销,难以广泛应用于各类应用程序。
[0033] 针对上述问题,本申请提出一种程序插桩方法,通过移除程序中的冗余指令,留出插桩空间,在不破坏程序原始结构和指令偏移的前提下向程序注入代码,可以应用于模糊测试的覆盖率收集、程序补丁等场景。由于该方案具有极高的稳定性且不依赖复杂的静态分析,可以应用于操作系统内核等复杂的大型程序。
[0034] 图1为本申请一实施例提供的程序插桩前后的示意图。如图1所示,包括程序插桩前和程序插桩后两部分。其中,程序插桩前的待分析程序是按照原来的执行顺序执行,如图中所示,先执行基本块1的程序,然后接着执行基本块2的程序,再接着执行基本块3的程序,按顺序依次执行。
[0035] 本申请是通过移除该待分析程序的冗余指令进行程序插桩,例如,基本块中包括有冗余指令,作为可替换桩函数的部分。程序插桩后的待分析程序是把程序插桩前的待分析程序中识别到的冗余指令替换成桩函数调用指令,程序插桩后的执行过程为先执行基本块1的程序,但在执行到桩函数调用指令的部分后,会先跳转到所调用的桩函数执行程序,调用的桩函数程序执行完成后,再接着执行基本块1中原冗余指令执行完成后的程序,在基本块1中的程序全部执行完成后,会接着执行基本块2的程序,执行过程与基本块1相同。
[0036] 示例地,桩函数调用指令为bl_funcA,funcA指具体调用的桩函数是funcA函数。其中,桩函数可以作为一个函数存储在桩函数的代码段、动态链接库或驱动中。桩函数是一个函数,该函数需要实现两个功能:1、在函数入口保存上下文信息,在函数出口恢复上下文信息(上下文信息具体指当前寄存器取值);2、实现插桩工具使用者希望向分析程序中添加的额外功能,即与分析需求对应的功能。
[0037] 下面结合图1的示例,参考图2来描述根据本申请示例性实施方式的程序插桩方法。需要注意的是,上述应用场景仅是为了便于理解本申请的精神和原理而示出,本申请的实施方式不受图1所示应用场景的限制。
[0038] 图2为本申请一实施例提供的程序插桩方法的流程示意图。如图2所示,本申请实施例中的程序插桩方法包括以下步骤:
[0039] S201:获取待分析程序,待分析程序为二进制程序。
[0040] 在该步骤中,待分析程序是程序开发人员提供给用户使用的程序。对于开源程序,用户可以获取到待分析程序的源代码以及二进制程序;但对于闭源程序,用户只能获得该闭源程序的二进制程序,而不能获取到待分析程序的源代码,因此,为同时适用于开源程序和闭源程序的分析,本申请获取到的待分析程序为二进制程序。
[0041] 示例地,当分析场景具体为测试场景时,待分析程序为待测试程序。
[0042] S202:基于分析需求,识别待分析程序中的冗余指令,确定待插入的桩函数,冗余指令为与待分析程序的原始语义和/或分析需求无关的代码,冗余指令的位置为桩函数的插桩点,桩函数用于向待分析程序中增加与分析需求对应的功能、保存待分析程序执行的上下文信息以及基于上下文信息恢复执行待分析程序。
[0043] 在该步骤中,分析需求是对某一待分析程序的各功能进行验证,通过逐项测试,检查或记录待分析程序执行情况功能。示例地,测试功能包括:收集代码覆盖率(即哪些代码被执行过)、记录程序内部特殊变量的取值等。
[0044] 示例地,待插入的桩函数是基于分析需求,事先开发好的函数,可通过后续步骤调用。
[0045] 示例地,冗余指令可结合实际需求进行设定。如,在模糊测试的场景下,常见的冗余指令来自于程序运行时的防护措施,如栈保护机制指令(canary)、指针验证指令(PA)等,这类指令主要用于保护程序在用户使用过程中不受攻击者利用,而在以漏洞挖掘为目标的模糊测试的场景下,没有程序保护的需求,因此可以删除。其他常见的冗余指令还包括用于日志记录的指令和由于编译器优化缺陷产生的可以合并或删除的指令。
[0046] 示例地,待分析程序在执行完桩函数的功能后,需要主动跳转回待分析程序原本的执行流程。在该步骤中,桩函数还负责保存待分析程序执行的上下文信息以及基于上下文信息恢复执行待分析程序,基于此,待分析程序在执行完桩函数功能后可以继续正常执行待分析程序原本的代码。示例地,上下文信息包括寄存器数值与内存状态等。进一步地,上下文信息可以包括监听待分析程序运行状态的信息。
[0047] S203:根据桩函数所在的位置和插桩点之间的偏移量,对函数调用指令中的地址偏移量进行赋值。
[0048] 在该步骤中,基于步骤S202中的分析需求,确定待分析程序所要插入的桩函数,进而获得桩函数所在的位置。示例地,桩函数所在的位置和函数调用指令的插桩点之间存在一定距离的偏移量。使用同一个函数调用指令,根据桩函数所在的位置和插桩点间的偏移量,修改函数调用指令中的地址偏移量,不同的函数地址对应不同的桩函数,从而实现调用不同的桩函数。
[0049] 示例地,桩函数用于实现使用者希望向待分析程序中增加的额外功能,如代码覆盖率采集,变量取值记录等功能。桩函数可以由本申请的使用者自行定义部分功能,但在执行自定义功能前后,需要保存待分析程序执行的上下文信息以及基于上下文信息恢复执行待分析程序。示例地,上下文信息包括寄存器数值与内存状态,桩函数可在函数入口处分配适当大小的栈空间,将原控制流中的寄存器数值暂存在栈上,避免后续桩函数功能覆盖寄存器数值。示例地,在桩函数内部,如非存在特殊需求,应当避免修改待分析程序的堆栈数据,避免待分析程序出现异常;在函数执行完成,返回待分析程序的原控制流前,需要释放桩函数内申请的堆空间,恢复暂存在栈上的寄存器数值,并释放当前栈空间。
[0050] S204:将冗余指令替换为函数调用指令。
[0051] 在该步骤中,由于冗余指令所占据的空间通常较小,不足以完成复杂程序插桩的功能,示例地,本申请通过桩函数来实现新增功能,而在原冗余指令所占据空间中填充桩函数调用指令/而使用桩函数调用指令替换冗余指令,此处的函数调用指令在待分析程序的原始逻辑与新增功能之间充当跳板。
[0052] 示例地,函数调用指令的功能在不同指令集下有所差别,但通常仅需要一到两条指令即可实现。
[0053] 示例地,在该步骤中,将冗余指令替换为函数调用指令,可以通过移除待分析程序中的冗余指令或压缩待分析程序中的冗余指令,留出待分析程序插桩的函数调用指令空间,将函数调用指令放入所留出的空间,待分析程序在执行函数调用指令的程序时,即可成功调用桩函数,执行使用者需要通过插桩完成的功能。
[0054] 本申请实施例提供的程序插桩方法中,通过移除待分析程序中的冗余指令,移除此类指令不影响待分析程序正常运行,从而在不破坏待分析程序原始结构和指令与指令间、指令与数据间偏移的前提下,留出插桩空间,向待分析程序注入新的代码,具有极高的稳定性,不仅适用于中小型用户态程序,还可以应用于操作系统内核等复杂的大型程序,或其他对稳定性需求较高的程序,应用范围更广。
[0055] 在上述实施例的基础上,可选地,基于分析需求,识别待分析程序中的冗余指令,包括:基于分析需求,确定冗余指令;对待分析程序中的冗余指令进行扫描与记录。
[0056] 示例地,根据分析需求,本申请的使用者可以指定一类或多类冗余指令作为被替换的目标。被选中的冗余指令需要在删除或修改后不影响程序的正常运行,其中,冗余指令可以是单个指令,也可以是多个指令的组合。在冗余指令确定后,需要对待分析程序中的冗余指令进行扫描与记录。
[0057] 进一步地,对待分析程序中的冗余指令进行扫描,包括:对于定长指令集,通过指令字节码扫描冗余指令;对于非定长的指令集,通过反汇编工具扫描冗余指令。
[0058] 示例地,定长指令集包括高级精简指令集(Advanced Reduced Instruction Set Computing Machines,简称ARM);非定长的指令集包括x64。示例地,本申请提出的程序插桩方法不受限于程序的指令集或开发语言,可以应用于不同种类的待分析程序,不影响待分析程序的原本结构、大小和基本功能,且本申请提出的方法基于静态二进制改写方案实现,在程序加载前完成程序插桩,不依赖于复杂的静态分析,避免了在待分析程序运行时动态修改函数调用指令、调整控制流带来的运行开销,可以支持大型程序插桩。
[0059] 一种可能的实施方式中,若程序插桩方法中的桩函数与待分析程序之间以动态链接库的形式进行链接,则函数调用指令基于位置无关函数的跳转表调用桩函数,跳转表包括程序链接表或桩函数表;若待分析程序的文件结构无法增加位置无关函数跳转表,则函数调用指令基于非关键函数的表项调用桩函数,非关键函数包括日志函数。
[0060] 示例地,当桩函数与待分析程序之间以动态链接库的形式进行链接时,二者间相对地址偏移量无法确定,函数调用指令需要借助程序链接表(Procedure Link Table,简称plt)或桩函数表(stub)等位置无关函数的跳转表实现对桩函数的调用,而对于无法增加plt或stub表项的文件结构来说,则可以替换日志函数等不影响待分析程序主要功能的非关键函数的表项,用于调用桩函数。其中,Linux系统的的可执行连接格式(Executable and Linkable Format,简称ELF)文件使用plt,MacOS系统的的二进制可执行文件(Mach‑O)使用stub。
[0061] 示例地,桩函数代码在用户态,可以以动态链接库的形式与待插入的桩函数进行组合;在内核态,可以以驱动的形式被其他内核代码调用;对于可执行连接格式(Executable and Linkable Format,简称ELF)等支持增加代码段的文件结构来说,还可以将桩函数放置在新增的代码段中。
[0062] 一种可能的实施方式中,将冗余指令替换为函数调用指令之后,还包括:确定冗余指令所占第一空间是否大于函数调用指令所占的第二空间;响应于第一空间大于第二空间,根据多余空间部分在函数调用指令之前和/或函数调用指令之后填充空指令。
[0063] 示例地,通过对待分析程序的二进制文件进行修改,可以将冗余指令替换为桩函数调用指令。当冗余指令所占空间大于函数调用指令所需的空间时,可以将额外的空间用空指令(nop)填充,其中,nop指令不执行操作,但占一个程序步。
[0064] 示例地,本申请提出的程序插桩方法也适用于程序开发人员提供源代码的开源程序,其实现过程与上述实施例类似,此处不再赘述。
[0065] 综上,本申请至少具有以下优势:
[0066] 1、高稳定性:本申请提出的方法将冗余指令替换为函数调用指令,通过函数调用指令调用待插入的桩函数,该操作过程避免了破坏待分析程序的原程序中指令与指令间、指令与数据间的偏移,使得插桩后的待分析程序可以稳定运行,不会由于地址引用歧义导致待分析程序的崩溃,适用于操作系统内核等对稳定性要求较高的程序插桩。
[0067] 2、可扩展性:本申请提出的方法不受限于具体的开发语言或指令集,不影响待分析程序的原本结构、大小和基本功能,不依赖于复杂的静态分析,可以支持大型程序插桩。
[0068] 3、高效性:本申请提出的方法基于静态二进制改写方案实现,在程序加载前完成程序插桩,不依赖于复杂的静态分析,避免了动态改写在程序运行时读写程序内存、修改程序指令带来的开销。
[0069] 图3为本申请一实施例提供的在macOS驱动中实现代码覆盖率反馈插桩的操作示意图。如图3所示,macOS驱动二进制文件格式里的组件包括LC_TEXT、LC_SYMTAB、LC_DYSYMTAB、_text、_stubs、String Table,其中,所有的基本块(basic blocks,简称bb)都在_text组件里,将_text组件里的冗余指令替换成BL offset_x、BL offset_y和BL offset_z,其中,函数调用点1(bb_1)和桩函数之间的地址偏移是x,函数调用指令为BL offset_x;函数调用点2(bb_2)和桩函数之间的地址偏移是y,函数调用指令为BL offset_y;函数调用点3(bb_3)和桩函数之间的地址偏移是z,函数调用指令为BL offset_z,地址偏移x、y、z互不相同。示例地,在macOS驱动中,当桩函数与待分析程序之间以动态链接库的形式进行链接时,二者间的相对地址偏移量无法确定,函数调用指令需要借助位置无关函数的跳转表stub实现对桩函数的调用,而对于无法增加stub表的文件结构来说,则可以替换日志函数(_IOLog)等不影响待分析程序主要功能的非关键函数的表项,用于调用桩函数。在图3中,通过复用stub表调用桩函数,把_IOLog函数替换成_COVPC函数,再通过系统自带的程序链接工具(如macOS中的kmutil),把_COVPC函数所在的地址自动填入BL_COVPC函数中保存。
[0070] 示例地,macOS驱动程序是难度较大的一类程序插桩,其运行在系统内核态,对稳定性有极高的要求,且文件格式高度紧凑,校验机制严格,难以直接在程序中添加额外指令,现有的程序插桩技术均无法对其进行修改。而本申请提出的方法通过移除macOS驱动程序中的指针验证指令,留出了插桩空间,在不破坏macOS驱动程序功能和结构的情况下实现了稳定的覆盖率插桩。具体地,macOS驱动程序中的指针验证指令作为冗余指令,被替换为函数调用指令,用于调用负责记录代码覆盖率的桩函数,具体替换过程如下所示:
[0071]
[0072] 示例地,7‑9行左边的内容是冗余指令,“=>”后的内容是函数调用指令。
[0073] 下述为本申请装置实施例,可以用于执行本申请方法实施例。对于本申请装置实施例中未披露的细节,请参照本申请方法实施例。
[0074] 图4为本申请一实施例提供的程序插桩装置的结构示意图。为了便于说明,仅示出了与本申请实施例相关的部分。如图4所示,该程序插桩装置40包括:获取模块41、识别模块42、赋值模块43和替换模块44。其中,
[0075] 获取模块41,用于获取待分析程序,待分析程序为二进制程序;
[0076] 识别模块42,用于基于分析需求,识别待分析程序中的冗余指令,确定待插入的桩函数,冗余指令为与待分析程序的原始语义和/或分析需求无关的代码,冗余指令的位置为桩函数的插桩点,桩函数用于向待分析程序中增加与分析需求对应的功能、保存待分析程序执行的上下文信息以及基于上下文信息恢复执行待分析程序;
[0077] 赋值模块43,用于根据桩函数所在的位置和插桩点之间的偏移量,对函数调用指令中的地址偏移量进行赋值;
[0078] 替换模块44,用于将冗余指令替换为函数调用指令。
[0079] 一种可能的实施方式中,识别模块42可以用于基于分析需求,识别待分析程序中的冗余指令,具体为:基于分析需求,确定冗余指令;对待分析程序中的冗余指令进行扫描与记录。
[0080] 一种可能的实施方式中,识别模块42用于对待分析程序中的冗余指令进行扫描,具体为:对于定长指令集,通过指令字节码扫描冗余指令;对于非定长的指令集,通过反汇编工具扫描冗余指令。
[0081] 一种可能的实施方式中,在桩函数与待分析程序之间以动态链接库的形式进行链接时,函数调用指令基于位置无关函数的跳转表调用桩函数,跳转表包括程序链接表或桩函数表;若待分析程序的文件结构无法增加位置无关函数跳转表,则函数调用指令基于非关键函数的表项调用桩函数,非关键函数包括日志函数。
[0082] 一种可能的实施方式中,替换模块44还可以用于:确定冗余指令所占第一空间是否大于函数调用指令所占的第二空间;响应于第一空间大于第二空间,根据多余空间部分在函数调用指令之前和/或函数调用指令之后填充空指令。
[0083] 一种可能的实施方式中,识别模块42中识别到的待插入的桩函数保存的待分析程序执行的上下文信息包括寄存器数值与内存状态。
[0084] 本申请实施例提供的程序插桩装置,其实现原理和技术效果与上述实施例类似,具体可参考上述实施例,此处不再赘述。
[0085] 图5为本申请一实施例提供的电子设备的结构示意图。如图5所示,该电子设备500包括:至少一个处理器510、存储器520、通信接口530和系统总线540。其中,存储器520和通信接口530通过系统总线540与处理器510连接并完成相互间的通信,存储器520用于存储指令,通信接口530用于和其他设备进行通信,处理器510用于调用存储器中的指令以执行如上述程序插桩方法实施例的方案,具体实现方式和技术效果类似,这里不再赘述。
[0086] 图5中提到的处理器510可以是通用处理器,包括中央处理器、网络处理器(Network Processor,简称NP)等;数字信号处理器(Digital Signal Processor,简称DSP)、专用集成电路(Application Specific Integrated Circuit,简称ASIC)、现场可编程逻辑门阵列(Field Programmable Gate Array,简称FPGA)或者其他可编程逻辑器件、分立门或者晶体管逻辑器件、分立硬件组件。
[0087] 存储器520可能包含随机存取存储器(Random Access Memory,简称RAM),也可能还包括静态随机存取存储器(Static Random Access Memory,简称SRAM),电可擦除可编程只读存储器(Electric Erasable Programmable Read‑Only Memory,简称EEPROM),可擦除可编程只读存储器(Erasable Programmable Read‑Only Memory,简称EPROM),可编程只读存储器(Programmable Read‑Only Memory,简称PROM),只读存储器(Read Only Memory,简称ROM),磁存储器,快闪存储器,磁盘或光盘,例如至少一个磁盘存储器。
[0088] 通信接口530用于实现程序插桩装置与其他设备(例如客户端)之间的通信。
[0089] 系统总线540可以是外设部件互连标准(Peripheral Component Interconnect,简称PCI)总线或扩展工业标准结构(Extended Industry Standard Architecture,简称EISA)总线等。该系统总线540可以分为地址总线、数据总线、控制总线等。为便于表示,图中仅用一条粗线表示,但并不表示仅有一根总线或一种类型的总线。
[0090] 本领域技术人员可以理解,图5示出的电子设备并不构成对电子设备的限定,可以包括比图示更多或更少的组件,或者组合某些组件,或者不同的部件布置。
[0091] 本申请实施例还提供一种计算机可读存储介质,计算机可读存储介质中存储有计算机执行指令,当计算机执行指令被执行时,实现如上程序插桩方法。
[0092] 本申请实施例还提供一种计算机程序产品,包括计算机程序,该计算机程序被执行时实现如上程序插桩方法。
[0093] 本申请实施例还提供一种运行指令的芯片,芯片用于执行如上任一方法实施例的程序插桩方法。
[0094] 本领域技术人员在考虑说明书及实践这里公开的发明后,将容易想到本申请的其它实施方案。本申请旨在涵盖本申请的任何变型、用途或者适应性变化,这些变型、用途或者适应性变化遵循本申请的一般性原理并包括本申请未公开的本技术领域中的公知常识或惯用技术手段。说明书和实施例仅被视为示例性的,本申请的真正范围和精神由下面的权利要求书指出。
[0095] 应当理解的是,本申请并不局限于上面已经描述并在附图中示出的精确结构,并且可以在不脱离其范围进行各种修改和改变。本申请的范围仅由所附的权利要求书来限制。