欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]64位CreateProcess逆向:(四)创建进程的过程

[原创]64位CreateProcess逆向:(四)创建进程的过程

点击下面进入总目录
http://bbs.pediy.com/showthread.php?t=207430
这个系列的这篇及下一篇文章,将涉及到操作系统创建进程中的进程、线程管理。本文是与进程管理有关的内容。主要涉及到内核函数PspAllocateProcess。


PspAllocateProcess的函数原型如下:

NTSTATS PspAllocateProcess( 

        //父进程内核对象指针

        IN PEPROCESS pParentEProcess,  

        //访问模式,表明调用来自用户态,还是内核态

        IN  KPROCESSOR_MODE AccessMode,

        //新进程对象安全属性    

        IN POBJECT_ATTRIBUTES pProcessObjectAttributes,

        //新进程的内存区对象句柄, 代表进程的映像文件

        IN HANDLE SectionObject,  

        //令牌对象句柄

        IN HANDLE hSeTokenObject, 

        //进程标识符

        OUT PVOID  pProcessFlags, 

        //传入传出结构体指针, 存放进程创建过程中一些句柄 内核对象, 大小0xC4

        IN OUT CREATEPROCESSCONTEXT *pCreateProcessContext,  

        IN PVOID  pUnKnow,

        //如果成功创建新进程的EProcess , 则从这里传出PEprocess    

        OUT PPEPROCESS ppNewEprocess 

);


    各个参数的含义已经给出注释,大家可以自己对照理解。倒数第三个参数pCreateProcessContext的定义请参照此系列的
http://bbs.pediy.com/showthread.php?t=207683这篇文章。
    这个函数的主要功能,是创建新进程的EPROCESS,为进程分配进程空间,创建进程的句柄表、PEB,以及设置进程的安全属性。
概念介绍
    不是所有的人都对Windows的进程、线程管理熟悉,所以我们先在这简要介绍下相关概念,深入的了解,大家还是需要翻阅相关资料或者使用WinDbg查看。
Windows的进程与线程管理
    我们知道,Windows中进程与线程的管理,是一对多的关系,即一个进程下可以拥有多个线程,这些线程共用所属进程的内存空间
    在Windows内核中,用_EPROCESS、_ETHREAD结构体来代表进程、线程。实际分析中,你又会发现这些“E”前缀的结构体中,还包含了“K”前缀的同类型结构体,如_KPROCESS、_KTHREAD。这其实是Windows系统分层设计带来的后果,K前缀代表着内核层次,E代表着执行层次。
    这些内核结构体是可能随操作系统版本变化而变化的,比如以下是WinDbg中查看到的Windows 7的_KPROCESS的结构:

nt!_KPROCESS

   +0x000 Header           : _DISPATCHER_HEADER

   +0x018 ProfileListHead  : _LIST_ENTRY

   +0x028 DirectoryTableBase : Uint8B

   +0x030 ThreadListHead   : _LIST_ENTRY

   +0x040 ProcessLock      : Uint8B

   +0x048 Affinity         : _KAFFINITY_EX

   ...

   +0x15a LdtTableLength   : Uint2B

它与http://www.nirsoft.net/kernel_struct/vista/KPROCESS.html上记录的vista版本的KPROCESS还是有区别的:

typedef struct _KPROCESS

{

     DISPATCHER_HEADER Header;

     LIST_ENTRY ProfileListHead;

     ULONG DirectoryTableBase;

     ULONG Unused0;

     KGDTENTRY LdtDescriptor;

     KIDTENTRY Int21Descriptor;

     WORD IopmOffset;

     UCHAR Iopl;

     UCHAR Unused;

     ULONG ActiveProcessors;

     ULONG KernelTime;

     ULONG UserTime;

     LIST_ENTRY ReadyListHead;

     SINGLE_LIST_ENTRY SwapListEntry;

     PVOID VdmTrapcHandler;

     LIST_ENTRY ThreadListHead;

     ULONG ProcessLock;

     ...

     UINT64 CycleTime;

} KPROCESS, *PKPROCESS;

    不过,还是可以发现,不管哪个版本,KPROCESS中都包含有一个链表ThreadListHead,即线程链表,Windows用这种在进程结构体中挂线程链表的方式,体现进程与线程的一对多关系。
句柄表
在EPROCESS结构体中,有一个句柄表成员:

    我们知道,句柄是为了操作内核对象的“序号”,它的好处在于统一了接口,使得进程(准确来说是线程)能够用统一的方式操作不同的对象资源。一个进程能够使用的句柄,都放在EPROCESS中的句柄表ObjectTable中。PspAllocateProcess这个函数中也涉及到新进程的句柄表的初始化。
函数流程详细解析

    函数最开始,先调用ObCreateObject函数,根据全局的参数cs:PsProcessType创建EPROCESS对象,并进行部分初始化。

    接着,通过调用MmGetSectionInformation,根据传入参数Section Handle,拿到对应的Section。这个Section在此系列的之前文章中已经谈过,就是PE文件在内存中的映射。


    接着,会根据参数CreateProcessContext中的标志,来决定是否读取注册表中的信息来进行相应的设置。注册表中的相关设置,大都与系统设置有关,如用WinDbg动态跟踪,可以找到与新进程默认调试器有关的注册表地址(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug)。还有其它与分页大小、内存映射方式一类的键值设置,大家可以查看IDA文件中,对应注视自己看。



    此外,ntdll.dll这个所有进程都会加载的dll文件,也是在PspAllocateProcess中查询该文件,并最终映射到新进程的内存空间的。

    接着,PspAllocateProcess根据传入参数,获取token,并根据token设置进程相关的安全属性。token来自于CreateProcessContext.hSeTokenObject。
    父进程不存在则获取系统默认Quota,并初始化为handleBuild进程。


    然后,PspAllocateProcess所做的工作就与进程空间有关了。我们知道,在Windows的内存管理方式下,每一个进程使用的都是虚拟内存,也就是说,不同进程间的内存空间是相互独立的。为了达到这个目的,操作系统起码要做两个事:
1.  将新进程的虚拟内存地址,映射到物理内存地址上   
2.  保证不同进程所使用的物理内存地址不相互冲突

    Windows把这些工作封装在了MmCreateProcessAddressSpace函数中,想要了解内存映射、内存管理相关细节的人,应该深入看下这个函数。不过这是个很大并且有些偏离我们此系列的话题,我们就不多剖析了,只贴出调用MmCreateProcessAddressSpace的相关代码位置:

    至此,新进程已经拥有了它自己的虚拟内存空间(但还没初始化),操作系统可以初始化代表着新进程的内核对象EPROCESS了。

    调用KeInitializeProcess初始化EPROCESS:
1、  初始化_KPROCESS结构体的_DISPATCHER_HEADER(分发器对象)结构体成员;一般同步大家会用到自旋锁,自旋锁虽然效率高,不过有些浪费系统资源,_DISPATCHER_HEADER就可以用于完成同步的任务,援引《寒江独钓》中的一句话“在windows内核中,凡是头部以DISPATCH_HEADER开始的对象都是"分发器对象”。
2、  填充页目录表地址(PDE),内存对齐检查标志,进程可以在哪些处理器上运行,所有线程的时限;
3、  初始化性能分析、线程等链表,为一个进程选择优先的处理器节点。

    调用ObInitProcess,创建新进程的句柄表:若父进程(当前进程)的某些句柄有“可继承”属性,那么新进程可以直接继承这些句柄并操作相关对象。这个继承的过程,其实就是从父进程的句柄表中复制相关句柄到自己的句柄表中(EPROCESS里的ObjectTable)。这些工作在内核函数ObInitProcess中完成。

    接着,会对新进程的内存空间进行一些操作。包括:
    初始化进程地址空间:

    创建PEB,主要调用函数MmCreatePeb,在这里会创建一个关键的结构VAD,所谓VAD(Virtual Address Descriptor),就是虚拟地址描述符。VAD记录了一个进程内各个内存段的分段信息,要深入了解用户空间(3环)如何管理的,可以深入研究这个函数。网上也有一些关于VAD的资料,比如这篇:http://blog.csdn.net/saict/article/details/3572691

    创建用户地址空间

    此外,PspAllocateProcess还会做设置虚拟内存保护等工作。
    以上工作都完成后,操作系统会调用ExCreateHandle创建进程句柄,并写入EPORCESS结构体中,这是为了操作系统之后好管理新进程而必须做的:

    最后,向EPROCESS中写入进程创建的时间,这使得我们可以在程序运行时,调用一些与运行时长相关的API:

总流程图:


创建进程的过程.doc
创建进程的过程.pdf
IDB:
ntoskrnl.7z.001.rar
ntoskrnl.7z.002.rar
ntoskrnl.7z.003.rar
请将IDB文件的后缀名“.rar”删除后用7z解压

上传的附件 1.png
2.png
3.png
4.png
5.png
6.png
7.png
8.png
9.png
10.png
11.png
12.png
13.png
14.png
15.png
16.png
17.png
18.png
19.png
20.png
21.png
PspAllocateProcess.png
创建进程的过程.doc
创建进程的过程.pdf
ntoskrnl.7z.001.rar
ntoskrnl.7z.002.rar
ntoskrnl.7z.003.rar

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功