基于元数据和指令流的64位Windows堆栈取证
2021-01-16翟继强徐晓陈攀杨海陆
翟继强 徐晓 陈攀 杨海陆
摘要:为解决64位Windows环境中,现有工具针对含有恶意进程的转储文件中没有堆栈帧指针和调试符號时,构建的堆栈取证会产生漏报问题和没有元数据时构建的堆栈取证会产生错报问题,提出了从内存转储构建堆栈跟踪方法。从内存转储中检索目标进程的用户上下文,确定堆栈跟踪的起始点,然后基于异常处理的元数据展开。如果元数据不可用,使用基于指令流的验证方法生成等效数据。基于框架Volatility实现了相应插件,实验表明,方法不依赖堆栈帧指针和调试符号,利用元数据可获取更加完整的堆栈跟踪;没有元数据时,基于指令流的验证可以极大地提高取证的精确性。
关键词:内存取证;Windows堆栈;元数据;指令流;返回地址
DOI:10.15938/j.jhust.2021.05.007
中图分类号:TP319 文献标志码:A 文章编号:1007-2683(2021)05-0051-09
0 引言
随着网络和信息技术的迅速发展和深入应用,以及软件漏洞的不断出现,网络攻击和网络犯罪问题层出不穷,造成了严重的网络信息安全威胁[1]。堆栈跟踪是一种分析恶意软件的内存取证方法[2]。堆栈是操作系统用于存储关于每个程序的活动子程序信息的结构,存储的信息包括从调用者程序传递到被调用程序的参数,子程序的局部变量,子程序完成后的返回的地址。堆栈跟踪可以提取主机中程序的返回地址,从而得到调用的函数和已执行代码的历史操作,这些将成为取证分析人员发现事件起因的重要线索[3]。
在Windows x86环境下,可以利用传统技术,即利用帧指针进行堆栈跟踪[4]。由于Microsoft采用“x64软件约定(x64 software conventions)”,根据这些约定,64位Windows中进程所有空间在函数开始处就预分配好,堆栈中不构造帧指针链[5]。因此基于帧指针的堆栈追踪技术对64位应用程序无效。针对这一问题,Pshoul[6]提出基于扫描的方案并将方法实现为开源框架Volatility插件malthfind。但其方法在扫描堆栈时会错误地将堆栈中子函数的返回地址误识别为最终返回地址,从而造成误报;对内存转储文件中相关展开数据结构利用不充分,导致结果不完整。Otsuki等[7]在2018年DFRWS(digitalforensic research workshop,数字取证研讨会)上阐述了扫描验证的堆栈重构方法并将方法实现为开源框架 Rekall插件malinfind,提高了堆栈重构的准确性。但其方法在利用元数据时未曾考虑RIP(registerinstruction pointer,寄存器指令指针)位于函数pro-log与epilog的情况,会导致在利用相关结构时发生信息匹配错位,从而造成结果的误报;其不利用元数据时,未考虑相关RSP(register stack pointer,寄存器基址指针)的偏移量且程序基本执行语句仅顺序执行一次,未将堆栈中人栈数据进行完全出栈,造成结果漏报问题。微软(Microsoft)发布的调试工具WinDbg可以在x86或x64环境下找到一定的返回地址,但是WinDbg强烈依赖于调试符号(程序数据库文件,也称为符号文件),同时可能错误地检测堆栈区域中的普通函数指针作为返回地址。而恶意软件大多数代码区既没有调试符号也没有任何元数据[8]。
为了解决上述问题,本文提出从64位Windows的内存转储构建堆栈跟踪的方法,并基于Volatility内存取证框架设计和实现相应的插件。通过实验测试评估了方法的精确性和完整性,确认可以更准确地对使用或不使用元数据的64位进程构建堆栈跟踪。
1 Windows x64堆栈
1.1 Windows x64堆栈分配
Windows x64环境中的运行进程分为两种类型:在本机环境中执行的64位进程;在WOW64层上执行的32位进程。内存就像是所有正在运行的进程的游戏表,要成为游戏的一部分,应该将数据带到此表中。该数据包括但不限于进程的可执行代码,进程访问的数据文件,通过Web浏览器访问的URL,用户名和密码。Windows操作系统利用虚拟内存方法管理系统内存。进程可用的所有虚拟地址集称为虚拟地址空间,其可分为两个范围:用户空间与系统空间。其中的用户空间是用户模式处理特定数据和用户模式动态链接库(dynamic link library,DLL)文件映射到的地址范围;系统空间(又称内核空间)是操作系统所在的地址范围,只能由内核模式代码访问。Windows内存管理器使用虚拟地址描述符(virtual address descriptors,VADs)来描述进程在分配时使用的内存范围[9]。当进程使用Virutal-Alloc函数分配内存时,内存管理器会在VAD树中创建一个条目。VAD树本身是一个自平衡二叉树,在任何给定节点,可以在左子树中找到低于当前节点包含的内存地址,在右边可以找到较高的内存地址。动态内存分配使程序能够在运行时分配内存空间,一种方法是内存池分配,其不一定是连续的,Windows以及许多其他操作系统提供可分配给进程的分页(可以驻留在物理内存中,也可以存储到磁盘)和非分页内存池(驻留在物理内存中);分配动态内存的另一种方法是堆栈分配,以先进后出的方式添加和移除数据[10]。
1.2 调用堆栈和堆栈帧
调用堆栈是操作系统用于存储关于每个程序的活动子进程信息的结构,此结构也称为执行堆栈,控制堆栈。堆栈存储的信息包括从主进程传递到被调用的子进程的参数,子进程中所需的局部变量和子进程执行结束后的返回地址。函数的堆栈帧是ESP(extended stack pointer,扩展栈指针)寄存器和EBP(extended base pointer,扩展基址指针或帧指针)寄存器中包含的地址之间的内存块,其存储在调用堆栈上,包含保存和恢复进程状态所需的所有信息。
根据编译器优化配置,可以生成不使用帧指针的函数,如在集成开发环境Visual Studio下,使用FPO(frame pointer omission,幀指针缺省)选项的函数调用堆栈中不存在EBP链。虽然在Windows XPSP(Service Pack,补丁包)2之后发布的所有Win-dows x86平台都禁用了动态链接库和可执行文件的FPO,但恶意软件或第三方二进制文件可能会使用FPO。此外,还有一些函数不对所有Windows的官方二进制文件使用帧指针,例如完全内部函数。Windows x64平台上的64位应用程序通常符合x64软件约定,它们不会构造堆栈中的帧指针链,需要程序数据库文件或其他信息才能来构建这些应用程序的堆栈跟踪。
由于返回地址通常存储在进程空间中,可以使用其虚拟地址进行访问,而虚拟地址又存储在堆栈中,从而有必要分析堆栈。
2 堆栈重构方法分析
实现堆栈跟踪基本上需要识别堆栈所有者线程使用的活动堆栈的实际范围,以及定位堆栈中每个返回地址的位置[11]。
2.1 具有帧指针的函数的堆栈跟踪
Windows使用内部数据结构来管理内存中的操作和对象。Windows的EPROCESS块包含KPRO-LESS结构,也被称为PCB(processing control block,进程控制块),它包含有关进程的调度信息的内核结构。KPROCESS块中偏移量0x0050处“Thread-ListHead”的LIST_ENTRY数组的每个项包含KTHREAD结构,KTHREAD是包含有关线程调度信息的内核表示[12]。这个结构主要包含:StackLimit、KernelStack、StackBase,这些信息分别提供堆栈的最大值、堆栈指针的当前值以及堆栈的起始地址。
具有帧指针的函数通常将当前帧指针寄存器的值推送到堆栈,对应操作为push ebp。然后,该函数用堆栈指针当前的值更新它,对应操作为mov ebp,esp。由于帧指针寄存器始终指向局部缓冲区和先前存储的帧指针之间的位置,局部变量和参数与EBP的偏移量保持不变,因此函数中的代码能够通过帧指针寄存器访问存储在堆栈中的局部变量或其函数参数。
帧指针EBP指向前一个帧指针,函数的返回地址存储在堆栈中的帧指针之前。基于这些指针,可以构造一个在当时执行状态下的调用堆栈,形成一个调用函数和被调用函数的正确顺序列表。
2.2 没有帧指针的堆栈跟踪
对于Windows堆栈中不构造帧指针链的函数,需要Microsoft的调式符号文件或一些额外的信息才能有效地遍历堆栈,该额外信息以编译器和链接器生成的展开信息(元数据)的形式出现。若元数据不可用,也有不依赖于帧指针的方法可扫描堆栈的返回地址并基于找到的返回地址构建调用堆栈[13]。其从线程环境块(thread environment block,TEB)收集StackBase和StackLimit作为堆栈的最高(栈底为栈的最高地址)和最低地址(栈顶为栈的最低地址),并扫描它们之间的内存区域来查找满足以下条件的地址:指向可执行内存区的地址;指向前一条被调用指令的所在地址的地址[14]。通过了解这些值并将它们转换为物理地址,可以到达与每个堆栈对应的区域。
2.3 现有技术存在的问题
Windows x64已经成为主流的操作系统,即没有帧指针的函数已经非常普遍,且恶意软件和已发布的应用程序通常没有调试符号和元数据,此时只能使用基于扫描的技术在内存上下文中构建堆栈跟踪。但是,这些技术会错误检测到普通函数指针并将堆栈中之前函数的返回地址作为本次的返回地址。另外,虽然许多现有方法通过Windows的TEB识别堆栈区域,但可以通过让堆栈指针指向任意内存区域来使其作为堆栈。所以必须确定用作堆栈的区域,建立一种更精确的方法来构建堆栈跟踪。
3 Windows x64堆栈跟踪
3.1 堆栈跟踪方法
3.1.1 获取x64进程的用户上下文
操作系统通常在事件(如上下文切换、系统调用和中断)发生时存储上下文。Windows体系结构中,用于保存上下文的结构为TrapFrame的成员KTRAP FRAME[15]。ETHREAD的Tcb结构中KTHREAD对象保存指向线程的最后一个KTRAPFRAME结构的指针[16]。ETHREAD.Tcb.Previous-Mode成员表明TrapFrame成员是具有用户上下文还是内核上下文。因此本文提出从ETHREAD.Tcb.TrapFrame获取目标进程中线程的最后一个上下文,这个上下文包含寄存器的最后一个值,例如指向堆栈顶部的RSP寄存器,以及指向下一条指令的RIP寄存器。即使RSP指向StackLimit和StackBase之间,也可以将堆栈的实际范围视为RSP寄存器指向位置的连续页面。Windows x64通常在发生用户到内核模式转换时将所有寄存器保存到KTRAPFRAME。现有的在Windows x86环境下获得最后EBP值的方法也采用这种结构。
3.1.2 利用元数据的堆栈重构
每个分配堆栈空间、调用其他函数、保存非易失性寄存器或使用展开信息进行异常处理的函数都有prolog、主体和epilog。prolog将执行函数前的参数寄存器保存在其本地地址中,将非易失性寄存器压入堆栈,为本地和临时对象分配堆栈,并可建立帧指针。epilog代码在函数的每个出口,其可将堆栈修整为固定的分配大小,释放堆栈,通过从堆栈中弹出保存的值来恢复非易失性寄存器。默认情况下,PE(portable executable,可移植可执行)32+文件具有用于结构化异常处理(structured exception handling,SEH)时堆栈展开的元数据,在内存取证中,可以基于加载到内存中的这些展开信息来获得堆栈跟踪。
在x64软件约定下用于栈展开的结构为RUNT-IME_FUNCTION,其位于Exception Directory的.pdata部分[17]。该结构包含3个相对虚拟地址(relative virtual addresses,RVAs),两个相对虚拟地址指向函数的prolog和epilog,第三个指向函数的UNWIND_INFO结构。UNWIND_INFO结构包含一个UNWIND_CODE结构数组,每个UNWIND_CODE表示函数中的一个操作,它会影响寄存器RSP或其他非易失性寄存器。其中,UWOP_SET_FPREG操作是通过将寄存器设置为当前RSP的某个偏移量来建立帧指针寄存器;SAVE_NONVOL操作是将非易失性寄存器的值进行保存;UWOP_ALLOC_SMALL和UWOP_ALLOC_LARGE操作是在堆栈上分配空间。如果函数使用帧指针在x64软件约定下动态分配堆栈空间,则函数的UNWIND_CODE包含UWOP_SET_FPREG代码以指定用作帧指针的寄存器。图1展示了这些结构的基本关系。
在内存转储上解释这些数据结构,并展开堆栈来获取先前的返回地址,恢复寄存器的值,步骤如下:
1)通过进程用户空间的VADs的进程树获得当前RIP指向的区域的基地址,同时检查基地址中的PE文件标头的签名来确认当前RIP是否指向PE32+映像文件内部[18]。
2)获取Exception Directory的RVA并计算该目录的虚拟地址,可得到RUNTIME_FUNCTION结构,其范围包含Exception Directory中的当前RIP。
3)判断RIP位置,此时有3种情况:①检查从RIP开始的代码流,若该代码流可以与epilog的结尾部分匹配,则确定它在epilog中。此时控制权离开了该函数,从UNWIND_CODE数组中向后扫描偏移量小于或等于RIP的偏移量的第一个条目,向后展开,并在处理每条指令时更新上下文记录。在该处理之后,重复步骤2)。②从函数开始到RIP的距离小于或等于展开信息中编码的prolog大小,则RIP在prolog内。此时指令尚未进入该函数,该函数没有与此异常相关联的数据。通过从UNWINDCODE数组中向前扫描偏移量小于或等于RIP的偏移量的第一个条目,向前展开,然后取消该展开代码数组中其余项的数据,重复步骤2)。③如果RIP不在prolog或epilog之内,则直接利用展开数据数组信息,进入步骤4)。
4)计算已获取的RUNTIME_FUNCTION的Un-windData成员指向的UNWIND_INFO的虚拟地址。
5)展开RSP信息并根据UNWIND_INFO包含的每个UNWIND_CODE恢复其他寄存器。
6)如果展开信息链接到其他信息,则该子方法获取到链接的展开信息并从步骤2)重复直到得到最后一个链接的展开信息。
7)在完成所有展开操作后从堆栈中弹出前一个返回地址,并将RIP设置为该值。
编译器和链接器负责在构建时将可展开信息放入可执行文件中,同时对于动态编译的代码也有展开信息,否则在异常之后就无法遍历堆栈或展开堆栈。Windows的相关API应用程序编程接口支持动态生成代码的异常处理,生成的展开信息以一个链接列表为根,其中头部位于ntdll!RtlpDynamicFunc-tionTable。即如果在步骤1)中未找到PE签名,也可以在该表格中获得展开信息。在这种情况下,用以下过程替换步骤2):通过引用VAD树获取nt-dll.dll的基址,并计算RtlpDynamicFunctionTable的虚拟地址,可得到RUNTIME FUNCTION结构(此方式与从Exception Directory获取的方式相同)。
3.1.3 基于指令流的堆栈重构
当在代码区的顶部检测不到PE头同时在Rtlp-DynamicFunctionTable中没有找到元数据,或者在获取内存转储时,元数据尚未加载到内存中,则采用此方法。
当调用发生时,函数指令的執行路径可到达当前RIP。首先使用基于扫描的技术,将检测到所有可能的上一个返回地址标记下来作为候选地址。然后,根据调用过程形成的指令流,找到抵达当前RIP的那条指令,则该指令对应的返回地址即是正确的返回地址。以图2为例说明该子方法的过程:
1)方法从当前RSP扫描返回地址。在图2所示的情况下,找到Pointerl和Pointer2。
2)根据Pointer1和Pointer2获得调用指令,分析每个调用指令要调用的函数内部的控制流。
3)找到抵达当前RIP的执行路径,并在指向该函数的调用指令之后标记这个候选地址,路径作为当前路径。在图2的情况下,Pointer2即是正确的返回地址。
4)将确认的返回地址设置为当前RIP的值,并将RSP递增8,同时设置为指向下一个返回地址的虚拟地址。
5)重复3.1.2中步骤1)。
验证返回地址过程如图2中所示。
在步骤2)中,如果调用是通过寄存器的间接调用,如调用RBX(Register B Extended,扩展B寄存器),则不能对这些候选地址执行基于指令流的验证。但是如果其他候选地址没有到达RIP的路径,则确定在当前RIP中候选地址为返回地址。
3.2 基于Volatility插件实现
Volatility框架是用于内存取证的开源软件,并提供可扩展的API[19-20]。根据以上分析,利用Win-dows内核数据将本方法实现为Volatility内存分析框架的插件trastack。首先利用已经实现的插件枚举内存转储文件包含的进程对象,进一步根据内核数据结构EPROCESS的Wow64Process成员检查进程是否是64位进程,从而根据进程的不同类型选择不同的堆栈跟踪方法。如果Wow64Process成员持有NULL指针,则目标进程为x64进程,然后获取进程所对应线程的KTRAP_FRAME结构得到上下文内容与堆栈范围,利用VAD树得到虚拟地址。由元数据是否存在分别进行堆栈返回地址定位。
插件核心代码如下:
trapframe=thread.Tcb.TrapFrame.dereference_as(“_KTRAP_FRAME”)
if bits64 trapframe:
address_space=thread.owning_process().get_process_address_space()
if:thread_callstack.set_bits(“64”)
current_ebp=trapframe.Rbp
thread_callstack.add_callstack_item
(CallstackItem(trapframe.Rip,trapframe.Rip))
thread_callstack.eip=True
while current_ebp:
if address_space.is_valid_address(current_ebp)and address_space.is_valid_address(current_ebp+pointer_size):
call_address=struct.unpack(unpack_size,ad-dress_space.zread(current_ebp+pointer_size,point-er_size))[0]
current_ebp=struct.unpack(unpack_size,ad-dress_space.zread(current_ebp,pointer_size))[0]
if len(thread_callstack.callstack)>1:
if current_ebp==thread_callstack.callstack[-1].frame address:
break
if call_address:thread_callstack.callstack.add_
callstack_item(CallstackItem(call_
address,current_ebp))
thread_callstack.callstack=parse_callstack_i-tems address
(thread,thread_callstack.callstack,config)
return thread callstack
插件执行流程如图3所示:
4 测试与分析
4.1 测试方法
针对普通用户进程和系统进程使用物理机进行测试;针对攻击进程,在VMware虚拟机监视器上运行的虚拟机(Virtual Machine,VM)中获取内存转储。默认情况下,物理机和VM均具有4GB的内存,安装了Windows 7 x64 SP1。用本文实现的volatility插件trastack与WinDbg(WinDbg对Microsoft提供的调试符号完全支持)、文[5]方法malthfind和文[6]方法malinfind执行结果作比较。每个样本分别进行10次实验,去掉最优结果和最劣结果后取均值得出最终结果。
4.2 评价指标
本方法可以重构堆栈中的返回地址,如果输入的测试数据中原本的函数返回地址被正确识别时,标记为TP(True Positive);输入的测试数据中子函数的返回地址被识别为最终返回地址(误报)时,标记为FP(False Positive);输入的测试数据中原本函数的返回地址被识别为普通子函数返回地址(漏报)时,标记为FN(False Negative);输入的测试数据中子函数返回地址被正确识别时,标记为TN(TrueNegative)。则在本文中相关评价指标精确率(Preci-sion)与召回率(Recall)如下:
4.3 实验结果分析
4.3.1 有元数据的进程测试
在此测试中,对Windows x64环境中运行有元数据的notepad.exe、explorer.exe、WINWORD.EXE、wininit.exe等进程的转储文件进行分析,分别执行插件trastack、WinDbg、malthfind和malinfind進行回溯栈。结果如表1所示。
结果表明,对于有元数据、支持Windows官方调试符号的典型可执行文件notepad.exe、explor-er.exe、WINWORD.EXE、wininit.exe,微软的WinD-bg可以全面并且无误地对堆栈进行回溯;在无法利用Windows官方调试符号的其它方法中,本文所实现的插件trastack利用元数据对堆栈展开回溯,执行结果达到了与WinDbg一样的精确率与召回率,同时用时小于WinDbg的1/2,一定程度上提高了效率;文[5]malthfind方法由于扫描时会错误地将堆栈中未及时出栈的不相关返回地址识别为函数最终返回地址,造成结果精确率低同时其方法中没有充分利用展开代码数组(元数据)信息,造成结果缺失;文[6]malinfind方法在利用元数据时,未曾考虑RIP在函数prolog与epilog内的情况,导致展开代码数组信息利用出错从而造成返回地址覆盖不全面且有误报,使结果不具有足够的可信性。
至此,确认本方法可以正确获取在Windows x64上有元数据的进程的堆栈跟踪。
4.3.2 无元数据的进程测试
在此测试中,为了模拟从没有元数据的代码区域获取堆栈跟踪的情况,WinDbg在执行k命令之前卸载了user32.dll和notepad.exe、explorer.exe、WIN-WORD.EXE、wininit.exe的调试符号,同时插件强制使用不利用元数据的子方法。依然利用上述进程,插件的调试消息表明user32.dll的.pdata部分不存在,此时元数据不可用[21],插件trastack依然能获得完整结果,同时在时间性能上,trastack所用时间仅增加了20.46%;WinDbg仅获得了上述正确堆栈跟踪的前两项,但时间上增加了74.84%;malthfind方法在无法利用元数据的情况下精确率明显降低,但由于其方法本身不依赖于调试符号,因此精确率高于Windbg,同时由于缺失元数据,漏报情况增加;malinfind方法中可利用元数据,其准确率略高于malthfind方法,但由于其在利用子方法时,RSP偏移量不够精确,导致结果中有误报;没有将返回地址循环出栈,降低了结果的召回率。实验结果如表2所示。
至此,确认本法可以在没有元数据的情况下获得正确的结果并可以减少扫描技术中由于x64的软件约定造成的堆栈返回地址的误报。
4.3.3 不同进程类型测试
为了进一步确定方法的有效性,分别选取用户进程、系统进程、栈溢出攻击进程和其他类型攻击进程对基于流的验证方法进行堆栈回溯。其中每种进程类型分别选取5个可执行文件样本;用户进程的样本分别为:explorer.exe、notepad.exe、wordpad.exe、mspaint.exe、gsview.exe,系统进程的样本分别为:vschost.exe、wininit.exe、smss.exe、lsm.exe、alg.exe,栈溢出攻击漏洞样本为:WINWORD.EXE(CVE-2012-0158(Common Vulnerabilities&Expo-sures(通用漏洞披露),2012年,编号0158。下同),Microsoft Office 2010 SP1漏洞)、PCManFTPD2.exe(CVE-2013 X1.730,PCMan’s FTP Server 2.0.7漏洞)、EQNEDT32.EXE(CVE-2017-11882,MicrosoftOffice2010SP2漏洞)、EQNEDT32.EXE(CVE-2018-0802,Microsoft Office2013漏洞)、fcrip.exe(CVE-2019-9766,Free MP3 CD Ripper2.6漏洞),其它攻击样本:tomcat8.exe(CVE-2018-11784,Apache Tom-cat8.5.0重定向漏洞)、devenv.exe(CVE-2019-1354,Microsoft Visual Studio 15.OGit插件远程代码执行漏洞)、firefox.exe(CVE-2019-17026,MozillaFirefox 72.0代碼执行漏洞)、iexplore.exe(CVE-2020-0674,Internet Explorerl 1脚本引擎内存损坏漏洞)、WINWORD.EXE(CVE-2020-0852,Microsoft Of-fice 2019远程代码执行漏)。为减轻恶意软件对物理机的伤害,保证实验环境的稳定性,恶意软件的测试在虚拟环境下执行。测试结果如表3所示。
实验结果表明在Windows x64环境下,插件可以针对微软官方用户进程或非微软用户进程进行完整而正确的堆栈回溯;针对系统进程,由于非微软产品无法利用Window调试符号,本方法在执行时会有少许漏报情况。在保证高召回率的情况下,针对提取到的堆栈结果,方法做到了一定的精确性;针对攻击进程,其通常利用正常进程的漏洞,将其代码注入相关进程的漏洞模块中并于执行结束再次修改返回地址以达到隐藏目的。如实验中CVE-2012-0158漏洞攻击就是利用WINWORD.EXE进程的漏洞,其将攻击代码注入MSCOMCTL.OCX模块。堆栈回溯发现漏洞函数在返回地址0x275C895C处而在其执行完后,返回地址被更改为0x41414141。这使得本方法不可避免地出现漏报和误报情况。
4.3.4 不同操作系统版本测试
由于Windows操作系统现有版本众多且其处于不断更新的状态,这要求方法应具有兼容性。实验选取配有不同大小内存的主流64位Windows版本,对多个栈溢出攻击漏洞样本WINWORD.EXE(CVE-2012-0158,Microsoft Office 2010 SPl漏洞)、PCManFTPD2.exe(CVE-2013-4730,PCMan’s FTPServer 2.0.7漏洞)、EQNEDT32.EXE(CVE-2017-11882,Microsoft Office2010SP2漏洞)、EQNEDT32.EXE(CVE-2018-0802,Microsoft Office2013漏洞)、fcrip.exe(CVE-2019-9766,Free MP3 CD Ripper2.6漏洞)执行本方法进行测试,结果如表4所示。
从实验结果整体上看,针对Windows7SP1版本的精确率和召回率最高,在Windows8.1版本出现最低精确率和召回率,但两者召回率之差低于1%同时精确率之差低于0.2%,说明本方法具有一定的兼容性。
5 结论
本文提出并实现了一种从Windows x64环境的内存转储构建堆栈跟踪的方法,从内存转储中获取每个进程的用户上下文,并基于异常处理的元数据展开堆栈,还实现了基于指令流的验证方法。本方法可以使用或不使用元数据获取x64进程的堆栈跟踪,和传统的基于扫描的技术比较,可以更准确地获得堆栈跟踪。
由于堆栈跟踪技术只是在当前执行状态下获取调用堆栈,对于使用等待方法而不保留线程的钩子恶意软件,很难有效地进行取证。下一步考虑使用实时监测方法,以实时获取内存转储从而进行堆栈分析来解决这个问题。
参考文献:
[1]许洪军,张洪,贺维.一种基于鼠标行为的云用户异常检测方法[J].哈尔滨理工大学学,2019,24(4):127.
[2]张瑜,刘庆中.李涛,等.内存取证研究与进展[J].软件学报,2015,26(5):1151
[3]SATRYA G B,KURNIAWAN F.A Novel Android Memory Foren-sics for Discovering Remnant Data[J].International journal onAdvanced Science,Engineering and Information Technology,2020,10(3):1008.
[4]CHENG Y,FU X,DU X,et al.A Lightweight Live Memory Fo-rensic Approach Based on Hardware Virtualization[J].Informa-tion Sciences,2016,379(1);23.
[5]COLIN R,ANDY P,CAI S S,et al.x64-software-conventions[EB/OL].Microsoft(2018-12-17)[2020-05-15].ht-tps://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx.
[6]PSHOUL D.Volatility Foundation[EB/OL].Github(2019-05-17)[2020-05-17].https://github.com/volati lityfounda-tion/community/blob/master/DimaPshoul/malthfind.py.
[7]OTSUKI Y,KAWAKOYA Y,IWAMURA M,et al.Building StackTraces from Memory Dump of Windows x64[J].Digital Investiga-tion,2018,24(S1):101.
[8]刘剑,苏璞睿,杨珉,等.软件与网络安全研究综述[J].软件学报,2018,29(1):42.
[9]FRANK B,ANDREAS D.Windows Memory Forensics:Detecting(Un)Intentionally Hidden Injected Code by Examining Page TableEntries[J]Digital Investigation,2019,29(S1):3.
[10]Al-SALEH M,QAWASMEH E,Al-SHARIF Z.Utilizing DebuggingInformation of Applications in Memory Forensics[J].Journal of U-niversal Computer Science,2020,28(7):805.
[11]SRIVASTAVA A,JONES J H.Detecting Code Injection by Cross-validating Stack and VAD Information in Windows Physical Memo-ry[C]//2017 IEEE Conference on Open Systems,ICOS.Miri,2017:83.
[12]BALAOURA S.Process Injection Techniques and Detection usingthe Volatility Framework[D].Piraeus:University of Piraeus,2018.25.
[13]MUTHUMANICKAM K,ILAVARASAN E.An Effective Methodfor Protecting Native API Hook Attacks in User-mode[J].Re-search Journal of Applied Sciences,Engineering and Technology,2015,9(1):33,
[14]YOSIFOVICH P,SOLOMON D A,IONESCU A.Windows Inter-nals,Part 1:System Architecture,Processes,Threads,MemoryManagement,and More[M].California:Microsoft Press,2017.
[15]NATHAN L,ANDREW C,AISHA A,et al.Memory Forensicsand the Windows Subsystem for Linux[J].Digital Investigation,2018,26(S1):3.
[16]LEE K,HWANG H,KIM K,et al.Robust Bootstrapping MemoryAnalysis Against Anti-forensics[J]Digital Investigation,2016,18(S1):23.
[17]COHEN M I.Characterization of the Windows Kernel Version Vari-ability for Accurate Memory Analysis[J].Digital Investigation,2015,12(S1):38.
[18]BRENDAN D.The VAD Tree:A Process-eye View of PhysicalMemory[J].Digital Investigation,2007,4(S1):62.
[19]RALPH P,FELIX F.Styx:Countering Robust Memory Acquisi-tion[J].Digital Investigation,2018,24(S1):18.
[20]COHEN A,NISSIM N.Trusted Detection of Ransomware in a Pri-vate Cloud Using Machine Learning Methods Leveraging Meta-fea-tures from Volatile Memory[J].Expert Systems with Applications,2018,102:158.
[21]HUDA S,MIAH S,HASSAN M M,et al.Defending Unknown At-tacks on Cyber Physical Systems by Semi-supervised Approach andAvailable Unlabeled Data[J].Information Sciences,2017,379(10):211.
(編辑:温泽宇)
收稿日期:2020-07-22
基金项目:国家自然科学基金(61403109);黑龙江省自然科学基金(F2016024);黑龙江省教育厅科学技术研究面上项目(12531121).
作者简介:徐晓(1994-),女,硕士研究生;陈攀(1994-),男,硕士研究生.
通信作者:翟继强(1972-),男,博士,教授,E-mail:zaijiqiang@163.com.