本发明要解决的是现有测试系统和测试方法在测试过程中因无法避免重复的 编译、链接过程而导致测试效率比较低的技术问题。
一种使用脚本补丁的白盒测试系统,包括测试控制系统和被测试系统,其特 征在于,还包括通讯模
块、脚本模块和测试模块;所述测试模块包括补丁处理中心、 哑巴补丁函数群和地址信息表;
所述地址信息表用于记录动态获得的所述被测试系统中的函数的地址信息; 所述哑巴补丁函数群是一系列的哑巴函数;所述通讯模块通过socket或串口通讯连 接所述测试控制系统;所述脚本模块通过通讯模块接收测试控制系统发出的测试指 令和脚本桩函数,解释执行测试指令;所述补丁处理中心调用所述脚本模块的相应
接口,根据所述测试指令实现所述脚本桩函数的执行;
所述被测试系统与所述通讯模块、所述脚本模块、所述测试模块一起编译、 链接成为可执行系统用于运行。
所述通讯模块、所述脚本模块和所述测试模块不随被测试系统的不同而改变;
当所述测试控
制模块与系统脱离时,所述被测试系统能够独立运行,所述通讯 模块、所述脚本模块和所述测试模块处于不影响所述被测试系统运行的休眠状态。
一种使用脚本补丁的白盒测试方法,包括如下步骤:
①根据被测试系统的需要动态增加脚本桩函数;
②增加所述脚本桩函数与所述被测试系统中函数的对应关系;
③采用补丁技术激活所述脚本桩函数,修改代码段的指令,实现执行过程从 所述被测试系统到所述脚本桩函数的迁转,
覆盖运行所述脚本桩函数或者 插入运行所述脚本桩函数。
上述使用脚本补丁的白盒测试方法,在所述步骤③中覆盖运行脚本桩函数的 过程包括如下步骤:
a1、当所述被测试系统中的A函数调用处于补丁状态的B函数时,不执行所 述B函数,直接跳转到与所述B函数对应的哑巴补丁函数;
a2、所述哑巴补丁函数调用所述脚本模块的对应接口,执行对应的脚本桩函 数,并将所述脚本桩函数的返回值传递给所述哑巴补丁函数;
a3、所述哑巴补丁函数将所述脚本桩函数的返回值返回给A函数。
上述使用脚本补丁的白盒测试方法,其步骤③中插入运行脚本桩函数的过程 包括如下步骤:
b1、当所述被测试系统中的A函数调用处于补丁状态的B函数时,不执行所 述B函数,直接跳转到与所述B函数对应的哑巴补丁函数;
b2、所述哑巴补丁函数调用所述脚本模块的对应接口,执行对应的脚本桩函 数,并将脚本桩函数的返回值传递给所述哑巴补丁函数;
b3、恢复所述B函数为未补丁状态;
b4、在所述哑巴补丁函数中直接调用所述B函数,获得并保存所述B函数的 返回值;
b5、设置B函数为补丁状态;
b6、将所述B函数的返回值返回给所述A函数。
所述步骤a2或步骤b2是通过对类ESP寄存器的
访问,将调用函数时的输入值 影射为脚本实体,使所述脚本桩函数能够访问、修改所述A函数调用所述B函数时 传入的参数来实现的。
本发明采用了上述技术方案后,由于采用了补丁技术、脚本语言技术,通过 脚本函数来构造测试过程中需要的各种类型的桩函数,而脚本语言技术具备直接编 写、直接起作用的特点,避免了反复的编译、链接过程,从而提高测试效率。与现 有技术相比,通过该技术能够灵活用脚本语言构造桩函数,保证不修改被测试系统 的情况下动态修改桩函数,灵活决定桩函数的返回值,同时实现测试脚本与被测试 系统的相对独立,减少了测试代码维护的成本。本发明还可以用于市场已经运行系 统的错误诊断和紧急修补。本发明经过仿真、模拟,并且在镜像测试工具中使用, 经实践证明可靠可行。
附图说明
图1是直接用编程语言构造测试桩函数进行软件测试的过程示意图;
图2是用格式化语言描述桩函数进行软件测试的过程示意图;
图3是本发明的白盒测试系统的结构示意
框图;
图4是本发明的白盒测试系统的测试
流程图;
图5是本发明的自盒测试系统采用插入方式使用脚本补丁的详细流程图;
图6是本发明的白盒测试系统运行流程示意图。
如图3所示是本发明的白盒测试系统的结构示意框图,包括测试控制系统10; 通讯模块20;脚本模块30;测试模块40和被测试系统50。被测试系统50与通讯 模块20、脚本模块30、测试模块40一起编译、链接成为可执行系统。通讯模块20 和测试控制系统10通过socket或串口通讯联接,接收测试指令、脚本桩函数。通讯 模块20、脚本模块30、测试模块40是固定的,不因为被测试系统的不同而改变。
本发明采用补丁技术、脚本语言技术,通过脚本函数来构造测试过程中需要 的各种类型的桩函数,测试控制系统10简单地说就是用户操作界面,用户可以在此 编写脚本桩函数,然后发送到通讯模块20,同时接收通讯模块20的消息,并将该 消息显示给用户。测试控制系统10向通讯模块20发起连接
请求,连接成功后,测 试控制系统10和通讯模块20就进入交互状态。通讯模块20和脚本模块30是两个 独立的任务,通过消息队列进行通讯,通讯模块20收到测试控制系统10的测试指 令后就通过消息队列将指令传送给脚本模块30,脚本模块30负责解释执行该测试 指令;脚本桩函数的传送方式也可以与指令的传送方式相同,脚本桩函数由通讯模 块20从测试
控制模块10中导入,存在脚本模块30中。
测试模块40包括补丁处理中心模块41;哑巴补丁函数群42;地址信息表43。
哑巴补丁函数群42包括一系列的哑巴函数,当一个打补丁后被测函数被调用 时,首先跳转到对应的哑巴补丁函数中,然后该哑巴补丁函数直接调用补丁处理中 心模块41接口,告诉补丁中心41一个函数进入补丁,需要执行对应的脚本函数。
所谓哑巴补丁函数,主要用于充当被补丁函数与脚本处理中心41之间的
桥梁, 通过修改被补丁函数的代码指令,直接跳转(JMP)到对应的哑巴补丁函数,哑巴补丁 函数根据被补丁函数的返回值类型,决定调用脚本处理中心的不同接口,将补丁号 和ESP寄存器的地址告诉补丁处理中心,补丁处理中心41通过补丁号可以正确调 用对应的脚本补丁函数;通过访问类ESP寄存器的适当地址可以将调用被补丁函数 时传入的参数解析出来,从而保证传入的参数可以被脚本补丁函数所使用。
地址信息表43用于记录被测试系统50中函数的地址信息,该地址信息是在 编译、链接后的测试过程中动态获得的。具体获得办法包括如下步骤:
Extem**函数1(***);
Extem**函数2(***);
……
Extern**函数n(***);
Addressinf[]={(unsigned long)函数1,(unsigned long)函数2,...,
unsigned long}函数n}
上述代码中Extem是引用被测试系统50中的每个函数,是地址信息表43构造 的
基础,Addressinf[]是地址信息表43。
补丁处理中心模块41调脚本模块30的相应接口,实现脚本桩函数的执行。 补丁处理中心模块41主要包含两个函数:
unsigned long mirrorProcssPatchUlong(unsigned long patchId,unsigned long
espAddr)
double mirrorProcssPatch Double(unsigned long patchId,unsigned long
espAddr)。
上述C语言函数中的参数patchId表示当前的脚本补丁序列号(ID)。参数 espAddr表示当前的类esp寄存器的值。这两个函数的功能是在该函数中调用的第ID 个脚本补丁对应的脚本桩函数,并获得脚本桩函数的返回值。
单纯用一个脚本函数代替被测系统50中具体C语言函数的运行,该脚本函数 称之为脚本补丁。如果一个函数本身不存在,而用一个脚本函数代替之完成一定的 功能,则该脚本函数称之为脚本桩函数。脚本补丁与脚本桩函数的关联在于,脚本 桩函数的实现依赖脚本补丁技术,通过脚本桩函数可以灵活构造各种类型的返回值, 主要用于测试;脚本补丁可以广泛应用于BUG修改、错误诊断。其实脚本桩函数、 脚本函数补丁是同样一个内容,只是应用的场合不同,习惯名称的不同,脚本桩函 数的概念主要应用在测试领域,脚本函数补丁的概念主要应用在BUG的
定位。
本发明的技术同样可以用于市场已经运行系统的错误诊断和紧急修补。本发 明的白盒测试系统代码量其实很小,大约只有2万行C代码,完全可以驻留到市场 产品中。当市场产品发现问题时,再将测试控制模块10于该系统连接,通过脚本补 丁诊断BUG原因,然后用一个脚本函数,替代错误函数的运行,从而避免错误。没 有和测试控制模块10连接时,系统的所有模块都可以处于休眠状态,不对产品系统 构成任何影响。
图4是本发明的白盒测试系统的测试流程图,图中A函数、B函数均为被测 试系统50中的C语言函数,具体关系为:B函数被A函数调用。测试过程中,需 要用脚本函数替换B函数的运行,从而在脚本函数中灵活构造返回值给A函数。
图4中步骤1.2.表示:在控制系统10中增加脚本桩函数,并通过通讯模块20 传递给脚本模块30;
图4中步骤3.4.5.表示:通过控制系统10和通讯模块20在测试模块40的补 丁处理中心模块41中增加脚本桩函数与被测试系统对应函数的对应关系;
图4中步骤6.7.表示:采用通用的补丁技术,由控制系统10通过测试模块40 激活脚本桩,修改代码段的指令,实现执行过程的迁转;
图4中步骤8.表示:当被测系统中的A函数调用B函数时,由于B函数被打 补丁,直接JMP到哑巴补丁函数群42中对应的哑巴补丁函数;
图4中步骤9.10.表示:哑巴补丁函数调用补丁处理中心模块41对应接口,告 诉补丁处理中心模块41,B函数补丁已经被触发;
图4中步骤11.12.表示:补丁处理中心模块41调用脚本模块30对应接口,执 行对应的脚本桩函数,同时获得脚本桩函数的返回值;
图4中步骤13.14.表示:补丁处理中心模块41将脚本桩的返回值传递给哑巴补 丁函数,然后哑巴补丁函数直接将该值返回给A函数,从而实现了用脚本桩函数代 替B函数的运行。
上述步骤描述了脚本补丁即覆盖运行脚本桩的运行过程,通过在图4中步骤 13.14.中间添加其他操作,可以实现插入脚本桩或脚本补丁的测试运行过程,所谓插 入脚本桩就是在运行B函数前,执行脚本函数,然后再继续运行B函数,插入脚本 补丁广泛应用于对函数输入参数的合法性判断。
图5是采用插入方式使用脚本补丁的详细流程图,其具体步骤1.--13.和图4 相同,下面详细介绍步骤14.--17.:
图5中步骤14.表示:恢复函数B为未补丁状态;
图5中步骤15.表示:调用B函数,获得并保存B函数的返回值,(调用B函数 时,要对ESP寄存器做适当处理,以保证B函数能够正确获得A函数调用时传入的 参数)。
图5中步骤16.表示:恢复函数B为补丁状态
图5中步骤17.表示:将图5中步骤15.得到的B函数的返回值,直接返回给A 函数。
上述步骤14-17的操作过程简单、明了,实现该操作过程可能有多种操作技 巧,例如怎么调整ESP寄存器,怎么保证B函数能够正确获得A函数调用时传入的 参数,怎么保存B函数的返回值等。插入方式使用脚本补丁时,这些参数的传入与 返回值的获得方法的不同,都属于本发明的保护范围。
图6是本发明的白盒测试系统运行流程示意图,更加形象地描述了本发明的 思路。在测试过程中,B函数被打补丁;当被测试系统中的A函数调用B函数时, 直接跳转到对应的哑巴函数,该哑巴函数调用哑巴补丁中心41对应接口,通过该接 口调用脚本系统30对应接口,执行对应的脚本桩函数C,同时获得脚本桩函数C的 返回值,补丁中心41将脚本桩的返回值传递给哑巴函数,然后哑巴补丁函数直接将 该值返回给A函数,从而实现了用脚本桩函数C代替B函数的运行。
配合镜像测试技术(主要思想是将被测试系统实体(变量、函数)影射为脚本实 体,从而通过对脚本实体的操作,达到对被测试系统实体操作的目的),通过对ESP 寄存器的访问,可以将调用函数时的输入值影射为脚本实体,从而在脚本桩(脚本 补丁)中同样可以访问、修改调用这些参数,例如图6脚本桩函数C同样能访问A 函数调用B函数时传入的参数。
本发明可应用于各种被测试系统。
在以上描述中以C语言举例说明,并不表示对本发明应用范围的限制。
为了进一步帮助理解本发明,作为本发明的一个
实施例,下面以C语言为例 给出一个哑巴补丁函数dummyPatchFunc0 PROC的代码和描述:
dummyPatchFunc0 PROC push 0 call mpGetPatchedFuncType;获得被补丁函数的返回值的类型 add esp,4 push eax pop functype0 push esp;将ESP寄存器,通过该值可以获得A调用B时,压入堆
栈的参数 push 0;补丁ID号 .if functype0=0 call mirrorProcssPatchDouble .else call mirrorProcssPatchUlong .endif add esp,8 pushad push 0 call mpGetPatchType;获得补丁的类型 add esp,4 push eax pop patchtype0 popad .ifpatchtype0=0;覆盖补丁直接返回 ret .else;插入补丁 .if functype0=0 fstp qword ptr[dummy0] .endif push 0 call mpResumeUnPatchState;将被补丁函数恢复为未补丁状态 add esp,4 mov funcaddr0,eax pop oldeip0 call funcaddr0;调用被补丁的函数 push oldeip0
pushad push 0 call mpResumePatchState;恢复被补丁函数为补丁状态 add esp,4 popad .endif ret dummyPatchFunc0 endp dummyPatchFunc1 PROC push 1 call mpGetPatchedFuncType add esp,4 push eax pop functype1 push esp push 1 .if functype1=0 call mirrorProcssPatchDouble .else call mirrorProcssPatchUlong .endif add esp,8 pushad push 1 call mpGetPatchType add esp,4 push eax
pop patchtype 1 popad .ifpatchtype1=0 ret .else .if functype1=0 fstp qword ptr[dumy1] .endif push 1 call mpResumeUnPatchState add esp,4 mov funcaddr1,eax pop oldeip1 call funcaddr 1 push oldeip1 pushad push 1 call mpResumePatchState add esp,4 popad .endif ret dummyPatchFunc 1endp dummyPatchFunc2 PROC push 2 call mpGetPatchedFuncType add esp,4
push eax pop functype2 push esp push 2 .if functype2=0 call mirrorProcssPatchDouble .else call mirrorProcssPatchUlong .endif add esp,8 pushad push 2 call mpGetPatchType add esp,4 push eax pop patchtype2 popad .if patchtype2=0 ret .else .if functype2=0 fstp qword ptr[dummy2] .endif push 2 call mpResumeUnPatchState add esp,4 mov funcaddr2,eax pop oldeip2
call funcaddr2 push oldeip2 pushad push 2 call mpResumePatchState add esp,4 popad .endif ret dummyPatchFunc2 endp dummyPatchFunc3 PROC push 3 call mpGetPatchedFuncType add esp,4 push eax pop functype3 push esp push 3 .if functype3=0 call mirrorProcssPatchDouble .else call mirrorProcssPatchUlong .endif add esp,8 pushad push 3 call mpGetPatchType
add esp,4 push eax pop patchtype3 popad .if patchtype3=0 ret .else .if functype3=0 fstp qword ptr[dummy3] .endif push 3 call mpResumeUnPatchState add esp,4 mov funcaddr3,eax pop oldeip3 call funcaddr3 push oldeip3 pushad push3 call mpResumePatchState add esp,4 popad .endif ret dummyPatchFunc3 endp