迷雾退散:揭秘创建进程时ebx为什么指向peb的答案

VSole2021-10-14 16:31:33

一、背景

这篇文章的起因,是笔者之前在做样本分析的时候,经常会遇到需要调试傀儡进程的情况,而其中有一种情景是将启动的白进程PE文件整个掏空并用黑进程进行替换。

为了确保被替换后的进程能顺利执行不崩溃,需要获取原进程各种上下文,并修改被替换后的新进程上下文,其中在原进程被挂起还没开始执行的时候,需要将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.1Ring3下创建进程

1. 判断处理dwCreationFlag各种标志位,包括是否包含不合法标记组合,判断优先级。

2. 判断lpEnvironment是否为空,

不为空则调用RtlAnsiStringToUnicodeString将其转为UniCode字符串。

3. 判断lpApplicationName、lpCommandLine是否为空。

如果lpApplicationName不为空直接调用RtlDosPathNameToNtPathName_U函数。

将DOS路径(C:\\WINDOWS\\XXX)转换为NT路径(\\Device\\HarddiskVolume1\\WINDOWS\\XXX),

为空则会解析lpCommandLine,主要按照’”’引号,’ ’空格,’\t’制表符作为分隔符进行解析并获取相应的PE文件,然后将DOS路径转换为NT路径。

4. 调用NtOpenFile得到文件句柄,调用了NtCreateSectiond函数得到内存区对象句柄。

5. 调用BasepIsProcessAllowed函数, 该函数用来判断应用程序名是否在授权文件列表中。

6. 之后会经过一大段的函数进行各种校验,再得到内存区对象句柄后调用NtQuerySection函数,

返回后得到节的基本信息(节基地址,大小,属性),并判断创建标志中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,

如果不包含该标志,则判断PEB->ReadImageFileExecOptions域是否为0,

如果包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,或者不包含该标志但ReadImageFileExecOptions域不为0,

调用LdrQueryImageFileExecutionOptions函数查询该信息。

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

8. 加载advapi32.dll并获得CreateProcessAsUserSecure函数的地址。

9. 调用BaseFormatObjectAttributes将安全属性结构格式为NT对象属性结构(得到了对象属性),接着调用了_DbgUiConnectToDbg在实现通过调用NtCreateDebugObject函数来创建调试对象,

调用DbgUiGetThreadDebugObject来获得调试对象(作为参数传递到0环)。

10. 最后调用NtCreateProcessEx函数。

2.1.2Ring0下创建进程

NtCreateProcessEx内为ring0下创建进程主要流程。

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

在PspCreateProcess中,保存当前线程运行的前一个模式。通过KTHREAD->PreviousMode可以得到前一个模式。判断创建标志是否包含除DEBUG_PROCESSDEBUG_ONLY_THIS_PROCESS,CREATE_SUSPENDED之外其它标志, 如果包含其他的标志,则报错退出。


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

4. 判断参数 JobMemberLevel是否为0, 如果不为0,接着判断父进程的EPROCESS->Job是否为0,如果JobMemberLevel不为为0且EPROCESS->Job为0,则返回无效参数错误后退出该函数;否则的话,将父进程对象中的属性保存到局部变量中。

5. 调用ObCreateObject函数创建新进程对象并将对象内容初始化为0,然后从父进程继承配额信息(PspInheritQuot)和设备位图信息(ObInheritDeviceMap),将父进程对象中的部分域给新进程。

6. 判断参数SectionHandle是否为0,若不为0,调用ObReferenceObjectByHandle函数得到区对象指针,然后将区对象指针赋值给新进程EPROCESS的相应域。

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

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

9. 接着调用PspInitializeProcessSecurity函数来设置新进程的安全属性, 主要是设置新进程的安全令牌对象。该函数会调用SeSubProcessToken函数来设置新进程对象的令牌对象。

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

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

12. 调用ObInitProcess函数来初始化新进程对象的表。

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

14. 调用PspMapSystemDll函数映射新进程对象的系统DLL(即NTDLL,映射第一个DLL),该函数会调用MmMapViewOfSection映射节区,而MmMapViewOfSection会调用MiMapViewOfImageSection函数将DLL作为镜像映射。

15. 接着调用MmGetSessionId函数获得指定进程的会话ID,然后调用SeSetSessionIdToken函数设置令牌的会话ID,

之后再调用ExCreateHandle函数在PspCidTable中添加一项(PID)。

16. 调用MmCreatePeb为新进程创建PEB,该函数首先通过调用KeAttachProcess函数将当前线程切换到新进程对象,然后通过MmMapViewOfSection函数将NLS节区映射到新进程的地址空间中,随后调用MiCreatePebOrTeb创建PEB/TEB。

在MiCreatePebOrTeb函数中首先会通过ExAllocatePoolWithTag来申请0x34大小的空间,接着通过MiFindEmptyAddressRangeDownTree函数在VAD树中查找一块未被使用的地址空间范围,并返回该范围的起始地址,最后通过MiInsertVad函数将申请的地址空间插入到VAD树中。

在创建PEB结构后,初始化PEB中部分域的值(镜像基地址,操作系统编译号等域),最后调用KeDetachProcess函数使线程回到原来的线程中。截止此步骤,PEB创建完成。

同时观察也可以发现,这里也解析了包括Nt头、扩展头、扩展头魔术字效验等关键PE结构信息,联想到之前分析流程也处理了一部分PE结构,可以猜测早期的PE文件结构逆向可能也是通过逆向进程创建过程,即逆向CreateProcess API来实现的。

17. 最后将新进程对象EPROCESS.ActiveProcessLinks更新为全局的活动进程链表(PsActiveProcessHead), 判断父进程是否为系统进程,调用SeCreateAccessStateEx设置访问状态,

调用ObInsertObject函数将进程对象加入到进程对象的句柄表中,并通过KeQuerySystemTime(获取当前系统时间)结束PspCreateProcess的调用,完成ring0下进程的创建。

接下来我们回到CreateProcessInternalW,以NtCreateThread为分界线,NtCreateProcessEx之后到NtCreateThread之前为ring3下创建线程流程,而NtCreateThread内则是ring0下创建线程流程,经过分析发现,我们所需要寻找的线程上下文设置其实就在ring3下创建线程流程内。

函数调用初始化
本作品采用《CC 协议》,转载必须注明作者和本文链接
概述在windows系统上,涉及到内核对象的功能函数,都需要从应用层权限转换到内核层权限,然后再执行想要的内核函数,最终将函数结果返回给应用层。本文就是用OpenProcess函数来观察函数从应用层到内核层的整体调用流程。OpenProcess函数,根据指定的进程ID,返回进程句柄。NTSTATUS Status; //保存函数执行状态。OBJECT_ATTRIBUTES Obja; //待打开对象的对象属性。HANDLE Handle; //存储打开的句柄。CLIENT_ID ClientId; //进程、线程ID. dwDesiredAccess, //预打开进程并获取对应的权限。ObjectNamePresent = ARGUMENT_PRESENT ; //判断对象名称是否为空
关于堆栈ShellCode操作:基础理论002-利用fs寄存器寻找当前程序dll的入口:从动态运行的程序中定位所需dll003-寻找大兵LoadLibraryA:从定位到的dll中寻找所需函数地址004-被截断的shellCode:加解密,解决shellCode的零字截断问题
uds诊断协议-逆向题 WP
2022-08-14 16:17:02
介绍这是一道uds诊断协议的逆向题。比赛的时候时间太短没做出来,又花时间研究了一下拿出来分享。UdsRoutineControlService这个函数从getflag这看起来就像目标函数,要求的条件很多。
EXP编写学习之绕过GS
2023-02-20 09:58:16
栈中的守护天使 :GSGS原理向栈内压入一个随机的DWORD值,这个随机数被称为canary ,IDA称为 Security Cookie。Security Cookie 放入 ebp前,并且data节中存放一个 Security Cookie的副本。栈中发生溢出时,Security Cookie首先被淹没,之后才是ebp和返回地址。函数返回之前,会添加一个Security Cookie验证操作,称为Security Check。检测到溢出时,系统将进入异常处理流程,函数不会正常返回,ret也不会被执行。函数使用无保护的关键字标记。缓冲区不是8字节类型 且 大小不大于4个字节。可以为函数强制启用GS。
一般情况下,SSRF针对的都是一些外网无法访问的内网,所以需要SSRF使目标后端去访问内网,进而达到我们攻击内网的目的。
shellcode编写探究
2022-06-09 15:34:57
前言shellcode是不依赖环境,放到任何地方都可以执行的机器码。shellcode的应用场景很多,本文不研究shellcode的具体应用,而只是研究编写一个shellcode需要掌握哪些知识。要使用字符串,需要使用字符数组。所以我们需要用到 LoadLibrary 和 GetProcAddress 这两个函数,来动态获取系统API的函数指针。
在所有函数调用发生时,向栈帧内压入一个额外的随机 DWORD,随机数标注为“SecurityCookie”。在函数返回之前,系统将执行一个额外的安全验证操作,被称做 Security check。
独立 LSA 进程存储的数据使用基于虚拟化的安全性进行保护,操作系统的其余部分无法访问。LSA 使用远程过程调用来与隔离的 LSA 进程进行通信。使用可用于自定义安全包的 LSA 支持函数,开发人员可以实现高级安全功能,例如令牌创建、 补充凭据支持和直通身份验证。这样便可以绕过 Credential Guard 的保护机制。这两种执行模式分别称为 LSA 模式和用户模式。结构数组的形式传递给 LSA。SpAcceptCredentials将为经过身份验证的安全主体存储的凭据传递给安全包。
当线程从等待状态苏醒后,会自动检测自己得APC队列中是否存在APC过程。所以只需要将目标进程的线程的APC队列里面添加APC过程,当然为了提高命中率可以向进程的所有线程中添加APC过程。然后促使线程从休眠中恢复就可以实现APC注入。往线程APC队列添加APC,系统会产生一个软中断。第二个参数表示插入APC的线程句柄,要求线程句柄必须包含THREAD_SET_CONTEXT访问权限。第三个参数表示传递给执行函数的参数。如果直接传入shellcode不设置第三个函数,可以直接执行shellcode。
01高门槛,勿入在Cisco平台上有一个很有用的Traceback log功能,实时记录当前Code运行到特
VSole
网络安全专家