近年来,Windows内核驱动逐渐成为攻击者针对的攻击对象。通过攻破内核驱动,攻击者可以为后续攻击打开受害主机的“大门”(比如通过加载恶意驱动进入系统的Ring0层)。攻击者通过构建rootkits(能够持久且可靠、无法检测地存在于计算机上以修改操作系统,从而把自己及其他一些对象进行隐藏的软件)[1],结合挂钩(hook)、直接内核对象操作(DKOM)等技术对恶意驱动进行隐藏,导致在应用层无法直接观察到异常[2-3]。有效、快速地扫描出相关的非正常驱动,对事件响应、攻击分析和系统防护至关重要。
Windows操作系统中维护着多个内核模式的堆,称为“内存池”,用来存储操作系统内核对象的内存分配(线程对象的_EPROCESS描述符,驱动对象的_DRIVER_OBJECT结构等)。大多数内存池分配以_POOL_HEAD ER结构开始,基于此特征产生的内存池标记扫描技术可以识别出内存中的各种数据结构,从中发现关键证据[4-5]。volatility和rekall是2个内存取证的主流框架,借助池标记扫描实现了自己的驱动对象扫描插件[6-7]。
现有池标记扫描技术需要对整个物理存储器进行完整搜索,花费大量的时间,而且伴随着操作系统的内存空间越来越大,这将导致池标记扫描技术的执行时间迅猛增长。针对这一问题,Sylve等人在2016年DFRWS上阐述了一种内存池标记快速扫描技术,极大地提高了池标记扫描的效率[8]。
本文提出了一种使用内存池标记快速扫描技术对内存映像中的内核驱动对象进行扫描的方法,在保证误报率不变的情况下,极大地提高对驱动对象的扫描效率,并通过实验验证了方法的有效性。
1 内存池标记扫描 1.1 分页池和非分页池Windows NT内核维护着动态化的内存池,用于大多数内核逐渐分配系统内存,这种优秀的内存分配方式,可以使内存分配效率得到有效提升。
内存管理器创建的内存池包括分页池(paged pool)和非分页池(non-paged pool)。2种内存池都位于为系统保留的地址空间区域中,并映射到每个进程的虚拟地址空间。分页池包含可以在系统内外进行换入和换出的内存页,保存不常驻内存的代码和数据(如注册表、内存映射文件);非分页池包含一系列虚拟内存地址,保存必须驻留在内存的代码和数据(如进程和线程对象、互斥体对象、引用文件、I/O请求包)[9]。大多数的内核结构(如表 1所示)均在非分页池上进行分配[10]。
对象 | 内存池标记 |
Driver object | Driv |
File object | File |
Kernel module | MmLd |
Logon session | SeLs |
Process | Proc |
TCP endpoint | TcpE |
TCP listener | TcpL |
Thread | Thre |
UDP endpoint | UdpA |
32位系统上大于4 080字节的池分配以及64位系统上大于4 064字节的池分配,存储在大页池(big page pool)中,称为“large pool allocation table”。这些内存分配无法使用池标记扫描技术,可以通过其他方法枚举出表中的项。
1.2 池分配非分页池的初始大小取决于操作系统的物理内存大小,一般为RAM的3%或40 MB(以较大者为准),64位系统的内存池扩展到最大为RAM的75%或128 GB(以较小者为准)[11]。64位系统初始池大小由内存管理器进行设定,并进行动态稀疏分配。
对于64位系统,所有池分配大小都小于4 064字节,32位系统的池分配大小都小于4 080字节。一个典型的池分配结构如图 1所示。
池分配的首部是一个_POOL_HEADER结构,如图 2所示。
现代版本的操作系统中,池分配按照chunk大小(ChunkSize)对齐分配。32位系统的ChunkSize为8字节,64位系统中的ChunkSize为16字节。
1.3 池标记扫描池标记扫描(pool tag scanning)是一种通过扫描内存池的内存池标记(pool tag)获取相关内存结构信息的技术。以_POOL_HEADER结构为起始位置,按照ChunkSize的整数倍扫描内存池。该技术主要扫描验证3个字段:BlockSize, PoolType以及PoolTag。
BlockSize与ChunkSize相乘,可以得到池分配大小。池分配的空间大小需要满足分配的内存对象(包括_OBJECT_HEADER和任何合适的可选头)本身所占空间与_POOL_HEADER所占空间的和。一个正确的PoolType数据节包含一个_POOL_TYPE结构,指定用于分配的内存类型:NonPagedPool(0)和PagedPool(1)。已释放的PoolType值为0,仍可存储在非分页池中。PoolTag是一个4字节标记,由ASCII字符组成,与动态分配的池内存块相关联。PoolTag由驱动程序在分配内存时指定,并调用例程函数ExAllocatePoolWithTag来分配池内存[12]。一些常见的PoolTag如前面表 1所示。PoolTag是池分配最明显的特征,没有PoolTag标记的分配,不属于池分配。
基于以上3个字段对内存池分配进行扫描,可以扫描出内存中的相关对象。例如定位一个进程对象,可以通过扫描转储文件中所有的PoolTag为Proc的对象。相比基于循环双向链表的扫描技术,池标记扫描可以扫描出已停止运行的进程对象,而且可以挫败某些rootkit隐藏技术,例如依赖于修改活动对象链表的DKOM及其扩展AL-DKOM这2种技术[13-15]。但池标记扫描在扫描空间方面具有线性时间复杂度,且扫描的空间可能非常大,效率很低。
1.4 内存池标记快速扫描针对传统池扫描技术效率低的问题,一种可行的改进思路是添加合适的扫描约束条件,在扫描之前确定哪些内存空间应该被扫描,过滤掉多余的、不符合要求的内存分配。
64位系统中,每个内存池的虚拟地址范围由动态虚拟地址(dynamic virtual address, DVA)子系统管理。DVA稀疏分配虚拟地址空间,而且允许内核为内存池预留一片较大的虚拟地址空间,这片空间只在实际需要时进行分配。内核为每个内存池维护一个虚拟地址分配位图(bitmap),以跟踪与物理页面形成映射关系且处于虚拟地址空间范围内的内存页。位图中的每个条目代表 2 MB的物理内存分配,通过迭代内存池的分配位图,可以识别出已经和物理页面形成映射关系的内存页,从而在进行池扫描时只扫描这些内存页。
基于以上思路产生的内存池标记快速扫描技术,只扫描与内存池分配相关的内存页。该技术分为以下3个主要步骤:
步骤1 确定虚拟地址空间范围
通过nt! MmMaximumNonPagedPoolInBytes符号确定非分页池的虚拟地址空间范围。对于Windows Vista SP0,先扫描存储在nt! MmNonPagedPoolStart0符号和nt! MmNonPagedPoolEnd0符号中的地址信息指向的内存空间,该地址空间中的内存页与物理页形成了映射关系,不需要引用虚拟地址位图,并且分配到该范围内的所有字节数信息存储在nt! MmMaximumNonPagedPoolInBytes符号中。nt! MmNonPagedExpansionStart符号存储着非分页池的动态分配部分开始位置的地址,扫描该部分的地址需要引用分配位图,然后定位已分配的内存页。在Windows Vista SP0之后的Windows版本中,只有一个简单的动态分配地址空间范围,并且该范围的起始地址存储在nt! MmNonPagedPoolStartAligened符号中,扫描该部分的地址空间需要引用分配位图。该部分的相关信息如表 2所示。
Windows版本 | 静态内存范围 | 动态分配开始地址 | 分配位图 |
Vista-SP0 | MmNon PagedPool Start |
MmNonPaged PoolExpansion Start |
MiNonPaged PoolVa BitMap |
Vista SP1~8 | N/A | MiNonPa-ged PoolStart Aligned |
MiNonPaged PoolVa BitMap |
8.1 | N/A | MiNonPaged PoolStart Aligned |
MiDynamic BitMapNon PagedPool |
步骤2 位图定位
自Windows Vista一直到Windows 8.1之前的版本,均可以通过nt! MiNonPagedPoolVaBitMap符号存储的地址找到非分页池的DVA分配位图。之后的版本需要通过nt! MiDynamicBitMapNonPagedPool符号存储的地址来定位分配位图。利用具有可以从PDBs中分析内核符号功能的内存取证工具,可以在内核映像中找到这些符号的偏移,并且基于内核基地址计算出符号地址。
步骤3 _RTL_BITMAP迭代
图 3展示了一个简单的位图结构。SizeOfBitMap表明位图中的位数量,Buffer指向位图数据开始处。位图数据以32位整数序列的形式存储,buffer中位图的第一个32位存储第一个整数的0~31位,第二个32位存储第二个整数的0~31位,依次类推。对于池位图,设置位表示与分配的物理内存形成映射的2 MB虚拟地址空间,未设置位表示未形成映射的2 MB虚拟地址空间。
通过以上3步,可以确定出需要扫描的内存空间范围,再使用Schuster提出的扫描技术遍历区域中的位图[4],确定哪些内存页需要扫描。
2 Windows内核驱动Windows操作系统由用户模式和内核模式的各种组件组成。根据处理器上执行的代码类型,处理器在2个模式之间进行切换。用户模式下,Windows会为应用程序创建专用的虚拟地址空间和句柄列表;内核模式下,执行的全部代码共享单个虚拟地址空间。
2.1 驱动驱动(drivers)程序主要调用由各种内核组件导出的例程。例如,创建一个设备(device)对象,可以调用由I/O管理器导出的IoCreateDevice例程。此外,驱动程序必须响应来自操作系统的特定调用,并且可以响应其他系统调用[16]。
Windows系统中,存在2种基本类型的驱动:
1) 用户驱动:驱动程序运行在用户模式下,为Win32应用程序和内核驱动或其他操作系统组件提供通信接口。
2) 内核驱动:驱动程序作为可执行程序的一部分运行在内核模式,可执行程序由I/O管理、即插即用内存、进程和线程等的内核模式操作系统组件组成。与操作系统本身一样,内核模式驱动程序实现为离散的模块化组件,具有明确定义的所需功能集。所有内核模式驱动程序都提供一组系统定义的标准驱动程序例程。
通常,内核模式驱动程序分层实现,高层驱动程序从应用程序接收数据、过滤数据,然后传递给支持设备功能的低层驱动程序,如图 4所示。
驱动程序的堆栈中有3种类型的内核模式驱动程序,最高级别驱动(如NTFS、FAT等文件系统的文件系统驱动)依赖低层较低级别驱动程序的支持,中间级别驱动依赖于更低级别的驱动程序的支持,最低级别驱动控制连接外围设备的I/O总线。
2.2 _DRIVER_OBJECT和IRP通常情况下,内核模型加载时,为创建KLDR_DATA_TABLE_ENTRY结构,会初始化一个相应的驱动对象(_DRIVER_OBJECT),其结构如图 5所示。
_DRIVER_OBJECT包含内核模型的关键信息(例如模型基址的副本、未加载的例程和指向处理函数的指针等),可帮助我们在元数据结构遭到破坏时,找到恶意模型。此外,通过检查_DRIVER_OBJECT,也可以检测针对处理例程的钩子。
I/O请求包(I/O request packets, IRP)是Windows内核的一种系统组件,用于Windows应用程序和驱动程序间的通信。当上层应用程序需要访问底层输入输出设备时,发出I/O请求,系统把请求转化为IRP数据,不同的IRP启动I/O设备驱动中对应的派遣函数。IRP一个最基本的结构是MajorFuction,记录IRP的主要类型。MajorFunction是一组函数指针数组,不同的项记录处理当前请求的回调函数。IRP部分类型列表如表 3所示:
IRP类型 | 描述 |
IRP_MJ_CREATE | 请求一个句柄 |
IRP_MJ_CLOSE | 关闭句柄 |
IRP_MJ_CLEANUP | 关闭句柄时取消悬挂的IRP |
IRP_MJ_DEVICE_CONTROL | 控制操作(利用IOCTL宏) |
IRP_MJ_PNP | 即插即用消息 |
IRP_MJ_POWER | 系统处理电源消息时产生 |
IRP_MJ_QUERY_INFORMATION | 获取文件长度 |
IRP_MJ_READ | 从设备得到数据 |
IRP_MJ_SET_INFORMATION | 设置文件长度 |
IRP_MJ_SHUTDOWN | 关闭系统 |
IRP_MJ_WRITE | 传送数据到设备 |
使用内存池标记快速扫描技术扫描内核驱动,仅根据BlockSize、PoolType、PoolTag 3个属性容易产生较高的误报率,所以需要针对驱动对象的特征添加额外特定的扫描约束条件。
由于每个合法驱动对象的_DRIVER_OBJECT结构中必然包含MajorFunction,而MajorFunction中包含28个IRP类型。确定扫描的对象中在满足原始3个条件的前提下,另外确定包含大多数驱动都必须要处理的3个例程DispatchCreate、DispatchClose和DispatchRead所对应的3个IRP主要类型IRP_MJ_CREATE、IRP_MJ_CLOSE、IRP_MJ_READ,即可确定扫描对象为内核驱动,保证扫描精准度。
3.2 基于Volatility实现扫描插件根据以上分析,本文借鉴Volatility内存取证框架中关于内存对象扫描技术的思想,实现了qdriverscan驱动扫描插件,测试使用内存池标记快速扫描技术扫描内核驱动的可行性。
插件使用内存池标记快速扫描技术扫描非分页池的_DRIVER_OBJECT分配。首先使用内存池标记快速扫描技术缩小扫描范围,只扫描与物理内存页形成映射关系的虚拟内存,而没有与物理内存页形成映射关系的内存视为不必扫描的内存空间。然后根据对象分配的位图信息确定需要扫描驱动对象的内存页,减小扫描的内存数量。最后根据内核驱动对象的相关特征,从符合要求的虚拟内存页中搜索出符合要求的驱动对象。具体扫描执行步骤如下:
步骤1 读取系统版本信息,根据版本信息选取对应的profile配置文件;
步骤2 根据profile确定合适的内核符号;
步骤3 根据合适的内核符号确定image内存中非分页池的范围;
步骤4 根据合适的内核符号读取位图信息;
步骤5 根据步骤3和步骤4得到需要扫描的内存页;
步骤6 在步骤5得到的内存页中迭代扫描驱动对象;
步骤7 进行3次判断对扫描出的内存对象进行筛选。判断是否含有驱动对象的内存池标记“Driv”、扫描的Blocksize是否属于_DRIVER_OBJECT的有效大小范围以及Major Function中是否含有IRP_MJ_CREATE、IRP_MJ_CLOSE和IRP_MJ_ CLEANUP。符合以上条件,则直接输出扫描结果;如果不符合任意一个条件,则跳过该对象,扫描下一个对象分配;
步骤8 整理输出扫描结果。
根据以上执行步骤,可总结出qdriverscan的搜索流程如图 6所示。
4 测试与分析在以时间效率和准确率为主要因素的前提下,对新技术的验证将通过提取不同情况下系统内存镜像然后进行转储内存文件扫描的方式,对采用了内存池标记快速扫描技术的qdriverscan和传统driverscan的扫描效率和准确率进行对比,分析优劣。
测试主机系统为Windows 10 version 1803(x64),RAM为8 G,CPU频率为3.6 GHz。所有实验分别进行10次,去掉每次实验中数据的最大值和最小值,以剩余8次的平均值作为最终的实验数据。
4.1 物理内存扫描测试物理内存测试所用image提取自RAM为8G、系统为Windows 7 SP1 x64的中度使用的笔记本电脑。将使用了快速内存池扫描技术的qdriverscan的扫描结果与Rekall和Volatility框架的driverscan(默认设置)的扫描结果进行对比,可得实验数据如表 4所示。
插件 | 内存类型 | 平均时间/s | prior boot | 重复分配 |
qdriverscan | virtual | 0.802 | 0 | 0 |
driverscan (Rekall) |
physical | 45.438 | 8 | 21 |
driverscan (Volatility) |
physical | 33.872 | 8 | 21 |
实验数据表明,使用了内存池标记快速扫描技术的qdriverscan只扫描了8GB内存映像的80MB空间,另外2种插件扫描了完整的8GB映像。从表中可以明显看出,qdriverscan扫描时间相对传统技术的driverscan扫描时间减少了几十倍,但扫描准确率与传统扫描插件相同。
实验结果还表明,2种driverscan的扫描结果有一部分由于在物理存储器中不同偏移处发现了2个或者更多的_DRIVER_OBJECT分配从而引起了重复,这可能是因为内存管理器使用了垃圾收集技术导致内存分配在物理内存中重新定位时发生了复制。由于这些重复的结果在取证调查时对调查人员并没有利用价值,所以认定这些结果无效。qdriverscan没有出现任何重复。
prior boot指系统重新启动前的内存分配。Chow等人曾指出:系统重启后,会因为硬件的原因导致对RAM所驻留的数据产生影响[17]。在某些情况下,当切断主机所有电源后,即使是"硬"重启仍然会存在一些波动数据。测试表明,实验中使用的测试笔记本电脑出现了这种情况。qdriverscan的扫描结果是driverscan扫描结果的一部分,这是因为driverscan扫描所有物理内存,能够找到以前系统启动的池分配在重新启动过程中没有被覆盖或丢弃的部分。由于qdriverscan只扫描非分页池的虚拟地址空间,因此无法找到这些分配。但是鉴于使用了快速池扫描技术极大地提高了扫描速度,本文认为这样的取舍是合适的。在时间充裕的情况下,进行完整的物理内存扫描是更合适的取证方式,但从Windows 10系统开始,这可能不再适用,因为该系统上用于池扫描的所有现有技术都需要搜索虚拟内存[18]。
4.2 虚拟内存扫描测试对于虚拟内存的扫描比详尽的物理内存搜索速度要慢。为保证对比完整性,进行只针对内核虚拟内存的内存池标记快速扫描和传统池标记扫描的对比实验。Rekall中的driverscan使用scan_kernel属性,Volatility中的driverscan使用-virtual属性,这两个属性的设置可以让插件在内核的虚拟内存空间中进行池扫描。实验数据如表 5所示。
插件 | 内存类型 | 平均时间/s | prior boot | 重复分配 |
qdriverscan | virtual | 0.802 | 0 | 0 |
driverscan (Rekall) |
virtual | 99.538 | 0 | 1 |
driverscan (Volatility) |
virtual | 85.641 | 0 | 1 |
由实验结果可知,快速内存池扫描相对于传统池标记扫描,在保证准确率相同的情况下,速度上提升了近百倍。与物理扫描相比,启用虚拟扫描导致volatility和rekall的扫描时间加倍。volatility和rekall的扫描结果中包含一个重复值,虽然扫描虚拟内存空间时重复值一般不会存在,但在极少数情况下仍然可能出现,这是因为内存分配在进行重新分配的复制时,可能与原始内存分配从内存中进行释放之间存在一个短暂的时间窗。
4.3 不同版本操作系统扫描测试由于Windows操作系统一直在进行更新和变更,为验证使用了内存池标记快速扫描技术的qdriverscan在各个Windows系统版本中的适用性,本文针对6个不同的Windows版本进行测试。表 6列出了使用内存池标记快速扫描技术的qdriverscan在不同系统版本下的扫描结果。所有image都来自具有不同RAM的轻度使用的系统。
Windows版本 | 扫描的数据量/MB | RAM大小/GB | 平均时间/s | 重复分配 |
Vista SP1 |
60 | 1 | 0.112 | 0 |
Vista SP2 |
76 | 1 | 0.335 | 0 |
7 SP0 | 64 | 2 | 0.145 | 0 |
7 SP1 | 64 | 2 | 0.181 | 0 |
8 | 44 | 4 | 0.086 | 0 |
8.1 | 244 | 8 | 0.307 | 0 |
Vista SP0之后的系统版本的内存池采用了动态分配技术,避免了重复分配的出现。
实验数据表明,对不同系统的image的测试,符合前2步的测试结果。
4.4 大内存扫描测试为验证采用内存池标记快速扫描技术的qdriverscan在扫描具有大量内存的系统时仍然稳定有效,实验从全新安装的Windows 7 SP1中提取了一个完整的内存映像,它的最大内存为192 GB。3种插件的扫描结果如表 7所示。
插件 | 扫描的数据量/GB | 平均时间/s |
qdriverscan | 5.72 | 16.802 |
driverscan (Rekall) |
192 | 568.359 |
driverscan (Volatility) |
192 | 397.621 |
从实验结果可以看出,在扫描大量内存时,采用快速内存池标记技术仍然可以在速度上提升几十倍左右,这将极大地提高内存取证时的分析效率。
4.5 网络带宽测试在事件响应方案中,调查人员通常需要在尽可能短的时间内在多个系统中定位和分类恶意软件。常见的一种解决方案是使用类似F-Response的系统远程访问目标系统上的RAM[19]。在这种情况下,时间是关键因素,网络宽带限制可能成为一个瓶颈。由于采用了内存池标记快速扫描技术,无需对RAM进行详尽搜索,因此理论上对带宽要求也将降低。
参考Joe Sylve等人的带宽测试方案,在多个目标Windows系统上运行F-Response。对于每个目标系统,将RAM作为iSCSI设备安装在位于同一千兆位LAN上的Linux分析主机上。在收集网络数据包时,针对目标系统分别实施qdriverscan和driverscan插件。扫描完成后,分析网络流量并记录目标系统和主机系统之间传输的数据量。分析结果如表 8所示。
RAM大小/GB | 插件 | 扫描的数据量/GB | 时间/s | 传输的数据量/GB |
2 | qdriverscan | 0.103 | 15.562 | 0.122 |
2 | driverscan | 2 | 36.457 | 2.089 |
4 | qdriverscan | 0.131 | 23.843 | 0.175 |
4 | driverscan | 4 | 69.452 | 4.043 |
8 | qdriverscan | 0.243 | 24.861 | 0.295 |
8 | driverscan | 8 | 285.338 | 8.126 |
实验数据中driverscan扫描的各项数据为Rekall和Volatility的driverscan插件扫描数据的平均值。由实验数据可以明显看出,采用内存池标记快速扫描技术可以有效降低对网络带宽的要求,而且传输的数据量也更少,缩短事件响应时间。
5 结论针对系统底层驱动的攻击日益增多且计算机内存普遍增大的情况,提高对系统内核驱动对象的扫描效率在当今事件响应和取证过程中日益重要。本文主要针对内存池标记扫描方法扫描驱动对象效率较低的问题进行研究,使用内存池标记快速扫描技术扫描内核驱动对象,极大地提高了驱动扫描效率,降低了通过网络在目标系统上执行实时存储器分析的带宽要求。实验表明,在以时间为关键因素的事件响应或在网络带宽有限制的情况下,采用内存池标记快速扫描技术扫描驱动对象的方法,切实可行且效率很高。
[1] | AMRITA H, WONJUN L. Hiding Kernel Level Rootkits Using Buffer Overflow and Return Oriented Programming[J]. Information Systems Security, 2017(10717): 107-126. |
[2] |
王宁, 刘志军, 麦永浩. Windows RootKit检测与取证技术研究[J]. 信息网络安全, 2012(2): 51-52.
WANG Ning, LIU Zhijun, MAI Yonghao. Windows RootKit Detection and Forensics[J]. Netinfo Security, 2012(2): 51-52. (in Chinese) DOI:10.3969/j.issn.1671-1122.2012.02.014 |
[3] |
兰芸, 李宝林. 木马恶意软件的电子数据勘查与取证分析初探[J]. 信息网络安全, 2014(5): 87-91.
LAN Yun, LI Baolin. The Digital Investigation and Forensics of Trojan Malware[J]. Netinfo Security, 2014(5): 87-91. (in Chinese) DOI:10.3969/j.issn.1671-1122.2014.05.018 |
[4] | SCHUSTER A. Pool Allocations as an Information Source in Windows Memory Forensics[J]. IMF, 2006: 104-115. |
[5] | COHEN M. Characterization of the Windows Kernel Version Variability for Accurate Memory Analysis[J]. Digital Investigation, 2015, 12(1): 38-49. |
[6] | STüTTGEN J, COHEN M. Anti-Forensic Resilient Memory Acquisition[J]. Digital Investigation, 2013, 10: 105-115. DOI:10.1016/j.diin.2013.06.012 |
[7] | STüTTGEN J, VMEL S, DENZEL M. Acquisition and Analysis of Compromised Firmware Using Memory Forensics[J]. Digital Investigation, 2015, 12(1): S50-S60. |
[8] | JOE T SYLVE, VICO M, GOLDEN G R. Pool Tag Quick Scanning for Windows Memory Analysis[J]. Digital Investigation, 2016, 16(suppl): S25-S32. |
[9] | SCHUSTER A. The Impact of Microsoft Windows Pool Allocation Strategies on Memory Forensics[J]. Digital Investigation, 2008, 5: S58-S64. DOI:10.1016/j.diin.2008.05.007 |
[10] | COHEN M. Scanning Memory with Yara[J]. Digital Investigation, 2017, 20: 34-43. DOI:10.1016/j.diin.2017.02.005 |
[11] | SOLOMON David A Russinovich. Windows Internals Part 2(Developer Reference)[M]. Beijing: Posts & Telecom Press, 2012: 213. |
[12] | LIGH M, CASE A, LEVY J. The Art of Memory Forensics[M]. Indianapolis: John Wiley & Sons, 2014: 142-146. |
[13] | QUYNH N A, TAKEFUJI Y. Towards a Tamper-Resistant Kernel Rootkit Detector[C]//Proceedings of the 2007 ACM Symposium on Applied Computing, Seoul, Korea, 2007: 276-283 https://www.researchgate.net/publication/221001838_Towards_a_tamper-resistant_kernel_rootkit_detector |
[14] | WANGTONG L, SENLIN L, YU L, LIMIN P, QAMAS S. A Kernel Stack Protection Model against Attacks from Kernel Execution Units[J]. Computers & Security, 2018, 72: 96-106. |
[15] | CHOI W, PARK J, BYEON J. Dual-Mode Kernel Rootkit Scan and Recovery with Process ID Brute-Force[J]. Advanced Science Letters, 2017, 23(3): 1568-1572. DOI:10.1166/asl.2017.8612 |
[16] |
章怡. Windows驱动程序信息库分析与开发研究[D].大连: 大连理工大学, 2012 ZHANG YI.Windows Driver Library To Parse And Development[D]. Dalian, Dalian University of Technology, 2012 http://cdmd.cnki.com.cn/Article/CDMD-10141-1013200924.htm |
[17] | CHOW J, PFAFF B, GARFINKEL T, et al. Shredding Your Garbage:Reducing Data Lifetime through Secure Deallocation[J]. Usenix Security, 2005, 14: 22-22. |
[18] | COHEN M. Rekall Forensics blog: Adding Rekall's Windows 10 Support[EB/OL]. (2015-06-10)[2018-09-27]. http://rekall-forensic.blogspot.com/2015/06/adding-rekalls-windows-10-support.html |
[19] | WU T, DISSO J, JONES K. Towards a SCADA Forensics Architecture[C]//Proceedings of the 1st International Symposium on ICS & SCADA Cyber Security Research, 2013: 12-21 https://www.researchgate.net/publication/285798835_Towards_a_SCADA_forensics_architecture |