针对进程用户空间的电子数据取证方法研究
2014-04-25罗文华
罗文华
(中国刑事警察学院 网络犯罪侦查系,辽宁 沈阳110854)
Windows操作系统环境下,每个进程都被赋予自己的虚拟地址空间。以32位操作系统为例,每个进程的虚拟地址空间为4GB,因此32位指针可以寻址从0x00000000至0xFFFFFFFF之间的任意地址。虽然每一个32位进程可使用4GB的地址空间,但并不意味着每一个进程实际拥有4GB的物理地址空间。4GB仅仅是一个虚拟地址空间,进程实际可以得到的物理内存要远小于其虚拟地址空间。并且,进程的虚拟地址空间是为每个进程所私有的,在进程内运行的线程对内存空间的访问都被限制在调用进程之内,而不能访问属于其他进程的内存空间。这样,在不同的进程中可以使用相同地址的指针来指向属于各自调用进程的内容而不会由此引起混乱。
为进一步保证操作系统安全,进程的虚拟地址空间被设计者人为地分为两个部分——用户空间与内核空间。32位操作系统中,用户最大可以使用3GB的空间,主要存储程序执行所需的管理结构(如进程环境块、线程环境块等)与数据;内核则可占据从3GB到4GB的这段地址,管理诸如进程块(包括执行体进程块、核心进程块)、线程块(包括执行体线程块、核心线程块)、句柄表(包含所有已被进程打开对象的指针)一类的系统结构(见图1)。用户进程通常情况下只能访问用户空间的虚拟地址,需要进行系统调用(代表用户进程在内核态执行)时才可以访问内核空间。用户空间对应进程,每当进程切换,用户空间就会随之变化;而内核空间则由内核负责映射,并不随进程改变,是固定的[1]。
图1 特定进程的用户空间与系统空间内容信息
以往的内存电子数据取证研究往往重点关注系统空间中的管理结构分析,相对忽视了用户空间中与特定进程直接关联的数据内容剖析,无法全面深刻揭示应用程序的细节。然而,实践发现系统空间中存储的一般性进程信息证据价值并不高,很难与特定的用户行为进行关联。因此,研究进程用户空间中典型数据结构的证据属性,挖掘应用程序行为秘密,业已成为内存电子数据取证领域新的关注热点。
1 进程用户空间中典型数据结构证据属性分析
本节重点以32位Windows 7操作系统为背景,结合用户空间中最重要的进程环境块、线程环境块、虚拟地址描述符等数据结构,描述其取证特性与分析方法。
1.1 进程环境块(Process Environment Block,PEB)
每一个Windows进程都有一个执行体进程块(EPROCESS),负责描述进程的基本信息(如进程ID、父进程ID、程序名、进程优先级、内存管理信息、设备映像等),并指向其他与进程控制相关的数据结构。EPROCESS结构的首部是核心进程块(KPROCESS,也成PCB),主要包含CPU调度时需要的信息,如进程状态、时间片大小等。需要指出的是,EPROCESS与KPROCESS均位于系统空间中,然而另一描述进程的重要结构——进程环境块(PEB)则处于用户空间。PEB位于EPROCESS头部偏移0x1a8处(见图2),重点存放进程运行所需的环境信息,因为其中的数据经常需要用户进行修改,放在系统空间中会导致系统隐患和频繁的操作模式切换,因此该结构位于用户空间中。
图2 EPROCESS头部偏移0x1a8处含有指向PEB结构的指针
PEB结构中(见图3),有如下域值信息值得取证调查人员特别关注。ImageBaseAddress(偏移0x008处)域可以用来找寻映像(即可执行文件)的基地址;Ldr(偏移0x008处)列举所有被加载的模块(利用下文所述VAD结构也可实现此目的);基于ProcessParameters(偏移0x010处)则可以抽取进程传递的参数;Process Heap(偏移0x018处)指向堆指针列表,NumberOfHeaps(偏移0x088处)给出了堆列表的大小,ProcessHeaps(偏移0x090处)则直接指向了堆列表中的第一个堆(即缺省堆),综合上述三种域值信息能够实现归属于特定进程的堆结构分析;AnsiCodePageData(偏移0x058处)、OemCodePageData(偏移 0x05C 处)、UnicodeCase TableData(偏移0x060处)分别指向了代码页中特定编码格式的数据,可在其指定的页面空间中实现已知关键字的搜索。
图3 PEB结构体具体信息(部分)
1.2 线程环境块(Thread Environment Block,TEB)
每个进程都有一个或多个线程,这些线程由执行体线程块(ETHREAD)(主要包含有线程创建时间、结束时间、当前状态、优先级别等信息)进行描述。ETHREAD的首部为核心线程块(KTHREAD),其内存储有分发器(内核中负责调度的例程集合)头部、总用户时间、总内核时间、栈结构等。ETHREAD和它所指向的结构通常都位于系统地址空间中,唯一的例外是线程环境块(TEB)。由于操作系统要在TEB中保存频繁使用的线程相关数据,因此它位于用户地址空间中。KTHREAD结构偏移0x088处即为指向TEB结构体的指针(见图4),它存放在比PEB所在地址低的地方。进程中的每个线程都有自己的一个TEB,并主要以栈的方式进行存储[1]。
图4 KTHREAD头部偏移0x088处含有指向TEB结构的指针
TEB的首部为NT_TIB结构格式的NtTib域,基于其内含的StackBase与StackLimit可实现栈信息的深度挖掘;偏移0x030处的ProcessEnvironmentBlock指向PEB结构,用于线程与所属进程的关联;偏移0x040处的Win32ThreadInfo则含有32位进程的描述信息;ActivationContextStackPointer(偏移 0x1a8 处)负责指向栈的上下文数据;StaticUnicodeString(偏移0xbf8 处)、StaticUnicodeBuffer(偏移 0xc00 处)、Thread PoolData(偏移0xf90)指向了非二进制信息的存储区域,可用于关键字搜索;0xf6c处的WinSockData则描述了网络通讯数据(如果存在的话)。具体信息如图5所示。
图5 TEB结构体具体信息(部分)
1.3 虚拟地址描述符(VirtualAddressDescriptor,VAD)
Windosws系统使用虚拟地址描述符VAD描述进程的虚拟地址空间。对于每一个进程,内存管理器都需要为其分配一组VAD节点,用来描述该进程地址空间的状态。这些VAD节点被组织成一棵自平衡的二叉树,以便使查找过程迅速高效。当一个进程提出内存空间请求时,内存管理器就创建一个VAD节点来保存此次内存请求所提供的相关信息,比如被保留的地址范围、共享或是私有、是否可被继承,以及应用页面的保留属性等。EPROCESS头部偏移0x278处的VadRoot域使用MMVAD结构描述VAD二叉树节点,内含该VAD节点的左右孩子节点、所属进程等信息(见图6),因此利用该结构可以实现特定进程涉及的所有VAD节点的遍历。另外,依靠VAD二叉树能够确定地址空间类型,私有(private)或是已映射(mapped)。所谓私有是指该区域只存在于特定进程的地址空间中,而已映射区域却可以存在于多个进程的地址空间中[2]。
图6 MMVAD结构体具体信息
基于图6还可发现该结构体中包含有一指向MSUBSECTION结构的指针MappedSubsection(偏移0x024处),MSUBSECTION结构首部为指向控制区域CONTROL_AREA的指针,如果该指针为空,则表示该区域属性为私有,否则为已映射。已映射区域可细分为FILE_OBJECT和SECTION_OBJECT两种类型,FILE_OBJECT表示磁盘文件在内存中的映射,SECTION_OBJECT则表示多个进程间的共享内存区域(如果CONTROL_AREA结构体中的FilePointer指针为空,即表示该区域类型为 SECTION_OBJECT)。CONTROAL_AREA结构首部就是指向SEGMENT结构的指针,而在SEGMENT结构中同样含有一指针指向CONTROAL_AREA结构。在使用Rootkit技术修改EPROCESS结构体中的双向链表指针ActiveProcessLinks,使进程从链表中脱离的情况下,可通过扫描SEGMENT结构体,逆向发现被隐藏的进程[3]。利用VAD二叉树可以发现进程间的共享区域,直至挖掘出对象句柄表(Object Handle Table)的细节信息,具体流程如图7所示。
图7 基于VAD发现共享的内存区域
2 应用实例
本节利用Windbg(Windows平台下的用户态和内核态调试工具)工具,重点基于第2节所述管理结构说明针对进程用户空间的电子数据取证方法具体应用。Windbg能够用于调试在线系统和镜像文件 (扩展名为DMP),利用工具自带的“process”命令可获得特定进程的EPROCESS结构的内存地址信息,利用该地址可以追溯用户空间中其他重要的管理结构。实例中“win32dd.exe”的EPROCESS结构首地址为0x87ccb030,其结构头部偏移0x1a8处出现有“Peb:0x7ffdd000_PEB”,可知该进程环境块的起始地址为0x7ffdd000。图8所示为基于0x7ffdd000地址解析出的PEB内容。
图8 起始地址为0x7ffdd000的PEB具体信息(部分)
以Ldr域为例,该域当前值为 0x77df7880,指向PEB_LDR_DATA结构。深入分析该结构发现,其内含有名为 InLoadOrderModuleList(0x221990-0x245648)和 InMemoryOrderModuleList(0x221998-0x245650)的链表(LIST_ENTRY)(见图9)。依据链表存储的地址,即可实现加载模块的挖掘。
图9 起始地址为0x77df7880的PEB_LDR_DATA具体信息
按照 EPROCESS→KPROCESS→ThreadListHead→ETHREAD→KTHREAD→TEB的次序,找寻到该进程的第一个线程的地址为0x87e7e298,其具体内容如图10所示。该结构体中最重要的就是首部NtTib域,其中含有StackBase、StackLimit等归属于特定线程的栈属性,利用这些信息可以抽取出被调用函数的地址及参数。
图10 起始地址为0x87e7e298的TEB具体信息(部分)
EPROCESS头部偏移0x278处的VadRoot值为0x875c9780,其左右孩子节点地址分别为0x87dd3 b00、0x87ff0ac8,Subsection 结构与 MappedSubsection结构的指针同为0x88518a70(见图11)。需要指出的是,每个VAD二叉树节点均为MMVAD结构,因此基于根节点可以获知整个二叉树的信息。
图11 VAD二叉树根节点信息
vad命令后接二叉树根节点地址可以显示该树全部节点信息(见图12),其中包括每个树节点的虚拟地址、所在层级、提交类型、读写权限等。以DLL文件为例,通常就包含在提交类型为“Mapped”、读写权限为“EXECUTE_WRITECOPY”的VAD空间中。如果要对DLL文件进行调查,便可重点针对此类VAD空间进行挖掘。
图12 获取VAD二叉树信息
选取0x87a7bef8节点描述的虚拟地址区域进行解析,其 MappedSubsection域值为 0x87afc428;MappedSubsection域中ControlArea指针值为0x87 afc3d8;ControlArea域中包含有指向Segment的指针0x8ac56c50;而Segment域中包含有指向ControlArea的指针0x87afc3d8(见图13)。利用互逆的这两个指针可实现进程结构体与对象句柄的追踪。另外,ControlArea域中还包含有一名为FilePointer的指针,负责指向具体的FILE_OBJECT对象;在该对象的FileName域中会出现有被调用的动态链接库文件的完整路径与文件名称。
图13 基于内存数据结构获取DLL文件信息
3 结语
文中重点分析对象是Windows操作系统内存转储(DMP)文件,同时 ProDiscover IR、Mandiant Memoryze、Moonsols DumpIt等工具也可生成该格式文件。而对于由DD(已升级到Windows和Linux环境下均可使用)、Mantech mdd等工具生成的内存映像文件,则无法使用WinDbg进行信息获取,但可使用Winhex一类的十六进制编辑器完成取证分析。另外,本文应用实例基于特定进程,在需要针对整个镜像中的进程用户空间进行深度分析时,可借助开源工具Volatility予以实现[4]。进程用户空间中还有一些用于实现特定功能或传递关键信息的子结构,进一步挖掘这些结构的电子数据取证特性,从而全面深刻揭示用户程序操作行为,将是课题组下一步的重点工作。
参考文献:
[1]Russinovich MR,Solomon DA.Microsoft windows Internals:Windows Server 2008 and Window Vista[M].5th ed.USA:Microsoft Press,2012:146-253.
[2]Okolica J,Peterson GL.Windows operating systems agnostic memory analysis[J].Digital Investigation 2010,(7)::48-56.
[3]Hejazi SM,Talhi C,Debbabi M.Extraction of forensically sensitive information from windows physical memory[J].Digital Investigation,2009,6(S1):121-131.
[4]罗文华,汤艳君.基于Volatility的内存信息调查方法研究[J].中国司法鉴定,2012,(4):90-93.