【安全报告】揭秘创建进程时ebx为什么指向peb的答案

【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第1张图片

一、背景

为了确保被替换后的进程能顺利执行不崩溃,需要获取原进程各种上下文,并修改被替换后的新进程上下文,其中在原进程被挂起还没开始执行的时候,需要将eax指向新oep,而ebx指向新peb,而为什么这样设置的原因却很少有人提及。为此,在经过查阅了一定的资料与简单的分析后,我们可以找到答案。

二、具体分析

先抛出结论,这里的eax与ebx属于线程上下文信息,在一个PE文件开始被运行的过程中,主线程上下文初始化过程是在进程已经创建完成,而主线程还没创建的阶段发生的,下面是具体更详细的分析:

首先我们需要对进程的创建有一个大概的认识,在ring3下创建进程API无非是CreateProcessA/W,但是无论调用哪一个,最终都会将相关参数转化为Unicode字符串,并最终调用CreateProcessInternalW,因此以下将主要分析CreateProcessInternalW,而在xp和win7下,它具体实现又有一些不一样的地方。

2.1 XP下执行流程
在xp下,它大概分为四个部分,分别是ring3下创建进程,ring0下创建进程,ring3下创建线程,ring0下创建线程,以NtCreateProcessEx为分界线,NtCreateProcessEx之前为ring3下创建进程主要流程。

2.1.1 Ring3下创建进程

  1. 判断处理dwCreationFlag各种标志位,包括是否包含不合法标记组合,判断优先级。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第2张图片
  2. 判断lpEnvironment是否为空,不为空则调用RtlAnsiStringToUnicodeString将其转为UniCode字符串。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第3张图片
  3. 判断lpApplicationName、lpCommandLine是否为空。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第4张图片
    如果lpApplicationName不为空直接调用RtlDosPathNameToNtPathName_U函数将DOS路径(C:\WINDOWS\XXX)转换为NT路径(\Device\HarddiskVolume1\WINDOWS\XXX),为空则会解析lpCommandLine,主要按照’”’引号,’ ’空格,’\t’制表符作为分隔符进行解析并获取相应的PE文件,然后将DOS路径转换为NT路径。

【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第5张图片
【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第6张图片
【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第7张图片
4. 调用NtOpenFile得到文件句柄,调用了NtCreateSectiond函数得到内存区对象句柄。

  1. 调用BasepIsProcessAllowed函数, 该函数用来判断应用程序名是否在授权文件列表中。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第8张图片

  2. 之后会经过一大段的函数进行各种校验,再得到内存区对象句柄后调用NtQuerySection函数,返回后得到节的基本信息(节基地址,大小,属性),并判断创建标志中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,如果不包含该标志,则判断PEB->ReadImageFileExecOptions域是否为0,如果包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,或者不包含该标志但ReadImageFileExecOptions域不为0, 调用LdrQueryImageFileExecutionOptions函数查询该信息。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第9张图片
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第10张图片

  3. 之后会经过一大段的函数进行各种校验,再得到内存区对象句柄后调用NtQuerySection函数,返回后得到节的基本信息(节基地址,大小,属性),并判断创建标志中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,如果不包含该标志,则判断PEB->ReadImageFileExecOptions域是否为0,如果包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,或者不包含该标志但ReadImageFileExecOptions域不为0, 调用LdrQueryImageFileExecutionOptions函数查询该信息。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第11张图片

  4. 检查镜像文件的部分信息的有效性,并调用函数BasepIsImageVersionOk判断镜像文件版本是否合法。

  5. 加载advapi32.dll并获得CreateProcessAsUserSecure函数的地址。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第12张图片

  6. 调用BaseFormatObjectAttributes将安全属性结构格式为NT对象属性结构(得到了对象属性),接着调用了_DbgUiConnectToDbg在实现通过调用NtCreateDebugObject函数来创建调试对象,调用DbgUiGetThreadDebugObject来获得调试对象(作为参数传递到0环)。

  7. 最后调用NtCreateProcessEx函数。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第13张图片
    2.1.2 Ring0下创建进程
    NtCreateProcessEx内为ring0下创建进程主要流程。

  8. 判断父进程是否存在,若不存在则退出,否则,调用PspCreateProcess。

【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第14张图片

  1. 在PspCreateProcess中,保存当前线程运行的前一个模式,通过KTHREAD->PreviousMode可以得到前一个模式,并判断创建标志是否包含除DEBUG_PROCESS, DEBUG_ONLY_THIS_PROCESS,CREATE_SUSPENDED的标志之外其它标志, 如果包含其他的标志,则报错退出。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第15张图片

  2. 通过参数ParentProcess调用ObReferenceObjectByHandle函数得到父进程对象的指针。

  3. 判断参数 JobMemberLevel是否为0, 如果不为0,接着判断父进程的EPROCESS->Job是否为0,如果JobMemberLevel不为为0且EPROCESS->Job为0,则返回无效参数错误后退出该函数;否则的话,将父进程对象中的属性保存到局部变量中。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第16张图片

  4. 调用ObCreateObject函数创建新进程对象并将对象内容初始化为0,然后从父进程继承配额信息(PspInheritQuot)和设备位图信息(ObInheritDeviceMap),将父进程对象中的部分域给新进程。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第17张图片

  5. 判断参数SectionHandle是否为0,若不为0,调用ObReferenceObjectByHandle函数得到区对象指针,然后将区对象指针赋值给新进程EPROCESS的相应域。
    在这里插入图片描述

  6. 接着就判断参数DebugPort是否为0,若不为0,调用ObReferenceObjectByHandle函数通过调试对象句柄得到调试对象指针,否则调用DbgkCopyProcessDebugPort函数从父进程拷贝DebugPort给新进程。

  7. 判断参数ExceptionPort是否为0,若不为0,调用ObReferenceObjectByHandle函数通过异常端口对象句柄得到异常端口对象指针。

  8. 接着调用PspInitializeProcessSecurity函数来设置新进程的安全属性, 主要是设置新进程的安全令牌对象。该函数会调用SeSubProcessToken函数来设置新进程对象的令牌对象。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第18张图片
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第19张图片

  9. 接着调用MmCreateProcessAddressSpace为新进程创建地址空间,并构建页目录表、页表及物理页的关系。

  10. 调用KeInitializeProcess函数初始化新进程对象中内核对象、优先级、亲和性、页目录表物理地址帧号。

  11. 调用ObInitProcess函数来初始化新进程对象的表。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第20张图片

  12. 调用MmInitializeProcessAddressSpace函数初始化进程地址空间,该函数的实现中调用了KiAttachProcess函数来实现进程的切换(将当前线程挂靠到新进程中),以及初始化EPROCESS中的部分域和PFN、工作集列表等。

  13. 调用PspMapSystemDll函数映射新进程对象的系统DLL(即NTDLL,映射第一个DLL),该函数会调用MmMapViewOfSection映射节区,而MmMapViewOfSection会调用MiMapViewOfImageSection函数将DLL作为镜像映射。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第21张图片
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第22张图片

  14. 接着调用MmGetSessionId函数获得指定进程的会话ID,然后调用SeSetSessionIdToken函数设置令牌的会话ID,之后再调用ExCreateHandle函数在PspCidTable中添加一项(PID)。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第23张图片

  15. 调用MmCreatePeb为新进程创建PEB,该函数首先通过调用KeAttachProcess函数将当前线程切换到新进程对象,然后通过MmMapViewOfSection函数将NLS节区映射到新进程的地址空间中,随后调用MiCreatePebOrTeb创建PEB/TEB。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第24张图片
    在MiCreatePebOrTeb函数中,该函数首先会通过ExAllocatePoolWithTag来申请0x34大小的空间,接着通过MiFindEmptyAddressRangeDownTree函数在VAD树中查找一块未被使用的地址空间范围,并返回该范围的起始地址,最后通过MiInsertVad函数将申请的地址空间插入到VAD树中。

【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第25张图片
【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第26张图片
在创建PEB结构后,初始化PEB中部分域的值(镜像基地址,操作系统编译号等域),最后调用KeDetachProcess函数使线程回到原来的线程中。截止此步骤,PEB创建完成。

【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第27张图片
同时观察也可以发现,这里也解析了包括Nt头、扩展头、扩展头魔术字效验等关键PE结构信息,联想到之前分析流程也处理了一部分PE结构,可以猜测早期的PE文件结构逆向可能也是通过逆向进程创建过程,即逆向CreateProcess API来实现的。

  1. 最后将新进程对象EPROCESS.ActiveProcessLinks更新为全局的活动进程链表(PsActiveProcessHead), 判断父进程是否为系统进程,调用SeCreateAccessStateEx设置访问状态,调用ObInsertObject函数将进程对象加入到进程对象的句柄表中,并通过KeQuerySystemTime(获取当前系统时间)结束PspCreateProcess的调用,完成ring0下进程的创建。
    【安全报告】揭秘创建进程时ebx为什么指向peb的答案_第28张图片
    接下来我们回到CreateProcessInternalW,以NtCreateThread为分界线,NtCreateProcessEx之后到NtCreateThread之前为ring3下创建线程流程,而NtCreateThread内则是ring0下创建线程流程,经过分析发现,我们所需要寻找的线程上下文设置其实就在ring3下创建线程流程内。

你可能感兴趣的:(程序员,安全,网络,windows,python,网络安全,计算机网络)