-
-
[原创]Windows内核学习笔记之进程(上)
-
2021-12-13 15:03 14996
-
一.基本概念
仅仅有执行程序的指令是无法让一个程序正常运行的,因为程序的运行需要用到各种各样的资源。进程就是各种资源的容器,它定义了一个地址空间作为基本的执行环境。在操作系统的管理规则中,很多资源是针对进程分配的,必须要鲜有一个进程,才能为其分配资源。
在Windows操作系统中,每个进程拥有如下资源:
一个虚拟的地址空间,一般称为进程空间。进程空间是操作系统分配给每个进程的虚拟地址空间,每个进程运行在这个受操作系统保护的虚拟空间之中,它的地址指针指向的都是这个空间中的虚拟地址,根本无法指到另一个进程空间中。也就是每个进程都在操作系统分配给它的虚拟空间中运行,它无法访问其他进程的空间,也不必担心自己的空间会被其他的进程所侵占
全局唯一的进程ID,也就是常说的PID
一个可执行映像,也就是该进程的程序文件(可执行文件)在内存中的表示
一个或多个线程
一个位于内核空间中名为EPROCESS(进程执行块)的数据结构,用以记录该进程的关键信息,包括进程的创建时间,映像文件名称等
一个位于内核空间中的对象句柄表,用以记录和索引该进程所创建/打开的内核对象。操作系统根据该表格将用户模式下的句柄翻译为指向内核对象的指针
一个用于描述内存目录表起始位置的基地址,简称页目录基地址(DirBase),当CPU切换到该进程时,会将该地址加载到页表基地址寄存器(x86之CR3, ARM之TTBR),这样当前进程的虚拟地址就会被翻译为正确的物理地址
一个位于用户空间中的进程环境块(PEB)
一个访问令牌,用于表示该进程的用户,安全组及优先级
二.进程数据结构
为了保存创建的进程的各种信息,同时也为了方便系统管理创建的进程,在用户层和内核层系统都有相应的数据结构。
1.内核层数据结构
当成功创建一个进程的时候,Windows内核中就会创建相应的进程内核对象。内核对象保存了进程的各种信息,同时操作系统也通过创建的内核对象来对进程进行管理。
Windows内核中的执行体层负责各自与管理策略相关的功能,而内核层(或微内核)实现了操作系统的核心机制。进程在这两个层上都有对应的数据结构
A.微内核层数据结构
KPROCESS是保存在微内核层最基本的进程数据结构,每个KPROCESS都代表了一个进程,该结构定义如下:
kd> dt _KPROCESS ntdll!_KPROCESS +0x000 Header : _DISPATCHER_HEADER +0x010 ProfileListHead : _LIST_ENTRY +0x018 DirectoryTableBase : [2] Uint4B +0x020 LdtDescriptor : _KGDTENTRY +0x028 Int21Descriptor : _KIDTENTRY +0x030 IopmOffset : Uint2B +0x032 Iopl : UChar +0x033 Unused : UChar +0x034 ActiveProcessors : Uint4B +0x038 KernelTime : Uint4B +0x03c UserTime : Uint4B +0x040 ReadyListHead : _LIST_ENTRY +0x048 SwapListEntry : _SINGLE_LIST_ENTRY +0x04c VdmTrapcHandler : Ptr32 Void +0x050 ThreadListHead : _LIST_ENTRY +0x058 ProcessLock : Uint4B +0x05c Affinity : Uint4B +0x060 StackCount : Uint2B +0x062 BasePriority : Char +0x063 ThreadQuantum : Char +0x064 AutoAlignment : UChar +0x065 State : UChar +0x066 ThreadSeed : UChar +0x067 DisableBoost : UChar +0x068 PowerState : UChar +0x069 DisableQuantum : UChar +0x06a IdealNode : UChar +0x06b Flags : _KEXECUTE_OPTIONS +0x06b ExecuteOptions : UChar
偏移 | 名称 | 含义 |
0x000 | Header | 表明KPROCESS对象也是一个分发器对象,说明进程本身是可等待的对象 |
0x010 | ProfileListHead | 当该进程参与性能分析时,作为一个节点加入到全局性能分析进程列表(内核全局变量KiProfileListHead) |
0x018 | DirectoryTableBase | 是一个只有两项的数组,第一项指向该进程的页目录表地址,第二项指向进程的超空间的页目录表地址 |
0x020 | LdtDescriptor | 该进程LDT(局部描述发表)的描述符 |
0x028 | Int21Descriptor | 兼容DOS程序,允许它们通过int 21H指令来调用DOS系统功能 |
0x30 | IopmOffset | 指定IOPM(I/O权限表)的位置,内核通过IOPM可控制进程的用户模式I/O访问权限 |
0x32 | Iopl | 定义进程的I/O优先级 |
0x34 | ActiveProcessors | 记录当前进程正在哪些处理器上运行 |
0x38 | KernelTime | 记录一个进程对象在内核模式下所花的时间 |
0x3C | UserTime | 记录一个进程对象在用户模式下所花的时间 |
0x40 | ReadListHead | 是一个双向链表头,记录了进程中处于就绪状态但尚未被加入全局就绪链表的线程,这个域的意义在于,当一个进程被换出内存后,它所属的线程一旦就绪,则被挂到此链表中,并要求换入该进程。此后,当该进程被换入内存时,ReadyListHead中的所有线程被加入到系统全局的就绪线程链表中 |
0x048 | SwapListEntry | 单链表项,当一个进程要被换出内存时,它通过此域加入到KiProcessOutSwapListHead为链头的单链表中;当一个进程被换入内存时,它通过此域加入到以KiProcessInSwapListHead为链头的单链表中 |
0x050 | ThreadListHead | 指向一个链表头,此链表包含了该进程的所有当前进程。当一个线程被创建的时候,被加入到此链表中,在终止的时候被从链表中移除 |
0x058 | ProcessLock | 自旋锁对象,用途是保护此进程中的数据成员 |
0x05C | Affinity | 指定该进程的线程可以在哪些处理器上运行,其类型是KAFFINITY,这是一个32位或64位整数,其二进制表示的每一位分别对应于当前机器上的一个处理器(核) |
0x060 | StackCount | 记录当前进程有多少个线程栈位于内存中 |
0x062 | BasePriority | 指定进程中的线程优先级,所有线程在启动时都会继承进程的BasePriority |
0x064 | AutoAlignment | 用于进程中的内存访问对齐设置,此标志位会被传递到线程的数据结构中,当一个线程的对齐检查开关打开时,该线程中的未对齐数据访问将会导致对齐错误(Alignment Fault) |
0x065 | State | 说明进程是否在内存中,共有六种可能状态:
|
0x066 | ThreadSeed | 用于为该进程的线程指定理想处理器 |
0x067 | DisableBoost | 与线程调度过程中的优先级提升和时限分配有关 |
0x068 | PowerState | 用于记录电源状态 |
0x069 | DisableQuantum | 与线程调度过程中的优先级提升和时限分配有关 |
0x06A | IdealNode | 用于为一个进程选择优先的处理节点,这是在进程初始化时设定 |
0x06B | ExecuteOptions | 用于设置一个进程的执行选项,这是为了支持NX而引入到系统中的 |
B.执行体数据结构
EPROCESS是保存在执行体的数据结构,几乎包括了进程的所有关键的信息,它侧重于提供各自管理策略,同时为上层应用程序提供基本的功能接口。所以,在执行层的数据结构中,有些成员直接对应于上层应用程序中所看到的功能,该结构定义如下:
kd> dt _EPROCESS ntdll!_EPROCESS +0x000 Pcb : _KPROCESS +0x06c ProcessLock : _EX_PUSH_LOCK +0x070 CreateTime : _LARGE_INTEGER +0x078 ExitTime : _LARGE_INTEGER +0x080 RundownProtect : _EX_RUNDOWN_REF +0x084 UniqueProcessId : Ptr32 Void +0x088 ActiveProcessLinks : _LIST_ENTRY +0x090 QuotaUsage : [3] Uint4B +0x09c QuotaPeak : [3] Uint4B +0x0a8 CommitCharge : Uint4B +0x0ac PeakVirtualSize : Uint4B +0x0b0 VirtualSize : Uint4B +0x0b4 SessionProcessLinks : _LIST_ENTRY +0x0bc DebugPort : Ptr32 Void +0x0c0 ExceptionPort : Ptr32 Void +0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE +0x0c8 Token : _EX_FAST_REF +0x0cc WorkingSetLock : _FAST_MUTEX +0x0ec WorkingSetPage : Uint4B +0x0f0 AddressCreationLock : _FAST_MUTEX +0x110 HyperSpaceLock : Uint4B +0x114 ForkInProgress : Ptr32 _ETHREAD +0x118 HardwareTrigger : Uint4B +0x11c VadRoot : Ptr32 Void +0x120 VadHint : Ptr32 Void +0x124 CloneRoot : Ptr32 Void +0x128 NumberOfPrivatePages : Uint4B +0x12c NumberOfLockedPages : Uint4B +0x130 Win32Process : Ptr32 Void +0x134 Job : Ptr32 _EJOB +0x138 SectionObject : Ptr32 Void +0x13c SectionBaseAddress : Ptr32 Void +0x140 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK +0x144 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY +0x148 Win32WindowStation : Ptr32 Void +0x14c InheritedFromUniqueProcessId : Ptr32 Void +0x150 LdtInformation : Ptr32 Void +0x154 VadFreeHint : Ptr32 Void +0x158 VdmObjects : Ptr32 Void +0x15c DeviceMap : Ptr32 Void +0x160 PhysicalVadList : _LIST_ENTRY +0x168 PageDirectoryPte : _HARDWARE_PTE_X86 +0x168 Filler : Uint8B +0x170 Session : Ptr32 Void +0x174 ImageFileName : [16] UChar +0x184 JobLinks : _LIST_ENTRY +0x18c LockedPagesList : Ptr32 Void +0x190 ThreadListHead : _LIST_ENTRY +0x198 SecurityPort : Ptr32 Void +0x19c PaeTop : Ptr32 Void +0x1a0 ActiveThreads : Uint4B +0x1a4 GrantedAccess : Uint4B +0x1a8 DefaultHardErrorProcessing : Uint4B +0x1ac LastThreadExitStatus : Int4B +0x1b0 Peb : Ptr32 _PEB +0x1b4 PrefetchTrace : _EX_FAST_REF +0x1b8 ReadOperationCount : _LARGE_INTEGER +0x1c0 WriteOperationCount : _LARGE_INTEGER +0x1c8 OtherOperationCount : _LARGE_INTEGER +0x1d0 ReadTransferCount : _LARGE_INTEGER +0x1d8 WriteTransferCount : _LARGE_INTEGER +0x1e0 OtherTransferCount : _LARGE_INTEGER +0x1e8 CommitChargeLimit : Uint4B +0x1ec CommitChargePeak : Uint4B +0x1f0 AweInfo : Ptr32 Void +0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO +0x1f8 Vm : _MMSUPPORT +0x238 LastFaultCount : Uint4B +0x23c ModifiedPageCount : Uint4B +0x240 NumberOfVads : Uint4B +0x244 JobStatus : Uint4B +0x248 Flags : Uint4B +0x248 CreateReported : Pos 0, 1 Bit +0x248 NoDebugInherit : Pos 1, 1 Bit +0x248 ProcessExiting : Pos 2, 1 Bit +0x248 ProcessDelete : Pos 3, 1 Bit +0x248 Wow64SplitPages : Pos 4, 1 Bit +0x248 VmDeleted : Pos 5, 1 Bit +0x248 OutswapEnabled : Pos 6, 1 Bit +0x248 Outswapped : Pos 7, 1 Bit +0x248 ForkFailed : Pos 8, 1 Bit +0x248 HasPhysicalVad : Pos 9, 1 Bit +0x248 AddressSpaceInitialized : Pos 10, 2 Bits +0x248 SetTimerResolution : Pos 12, 1 Bit +0x248 BreakOnTermination : Pos 13, 1 Bit +0x248 SessionCreationUnderway : Pos 14, 1 Bit +0x248 WriteWatch : Pos 15, 1 Bit +0x248 ProcessInSession : Pos 16, 1 Bit +0x248 OverrideAddressSpace : Pos 17, 1 Bit +0x248 HasAddressSpace : Pos 18, 1 Bit +0x248 LaunchPrefetched : Pos 19, 1 Bit +0x248 InjectInpageErrors : Pos 20, 1 Bit +0x248 VmTopDown : Pos 21, 1 Bit +0x248 Unused3 : Pos 22, 1 Bit +0x248 Unused4 : Pos 23, 1 Bit +0x248 VdmAllowed : Pos 24, 1 Bit +0x248 Unused : Pos 25, 5 Bits +0x248 Unused1 : Pos 30, 1 Bit +0x248 Unused2 : Pos 31, 1 Bit +0x24c ExitStatus : Int4B +0x250 NextPageColor : Uint2B +0x252 SubSystemMinorVersion : UChar +0x253 SubSystemMajorVersion : UChar +0x252 SubSystemVersion : Uint2B +0x254 PriorityClass : UChar +0x255 WorkingSetAcquiredUnsafe : UChar +0x258 Cookie : Uint4B
偏移 | 名称 | 含义 |
0x000 | Pcb | 即上面介绍的KPROCESS内嵌结构体,因此,在系统内部,一个进程的EPROCESS对象地址和KPROCESS对象的地址是相同的 |
0x06C | ProcessLock | 与KPROCESS的自旋锁同名,但是它们类型不同,保护成员也不同。这里的ProcessLock域是一个推锁对象,用于保护EPROCESS中的数据成员 |
0x070 | CreateTime | 进程创建时间 |
0x078 | ExitTime | 进程退出时间 |
0x080 | RundownProtect | 进程停止保护锁,当一个进程到最后被销毁时,它要等到所有其他进程和线程已经释放此锁才可以继续进行 |
0x084 | UniqueProcessId | 进程的唯一编号,在进程创建时设定 |
0x088 | ActiveProcessLinks | 双向链表,用于将所有活动进程连接在一起,表头是全局变量PsActiveProcessHead |
0x0B4 | SessionProcessLinks | 双链表节点,当一个进程加入到一个系统会话中,其SessionProcessLinks域将作为一个节点加入到该会话的进程链表中 |
0x0BC | DebugPort | 指向调试端口的句柄 |
0x0C0 | Exception | 指向异常端口的句柄 |
0x0C4 | ObjectTable | 指向进程的句柄表 |
0x0C8 | Token | 是一个快速引用,指向该进程的访问令牌 |
0x11C | VadRoot | 虚拟地址描述符二叉树根节点 |
0x130 | Win32Process | 指向由Windows子系统管理的进程区域,如果此指不为NULL,说明这是一个Windows子系统进程(GUI进程) |
0x170 | Session | 所属会话对象 |
0x174 | ImageName | 指向进程名 |
0x190 | ThreadListHead | 双向链表节点,该链表包含了一个进程中所有的线程 |
0x1A0 | ActiveThreads | 记录当前进程有多少个活动线程。当该值为0,所有线程都将退出,进程也就会退出 |
0x1A4 | GrantedAccess | 进程的访问权限 |
0x1B0 | Peb | 进程的环境块,这是一个位于进程地址空间的内存块,其中包含了有关进程地址空间中的堆和系统模块信息 |
0x24C | ExitStatus | 包含了进程的退出状态,从进程的退出状态通常可以获知进程非正常退出的大致原因 |
0x252 | SubSystemMinorVersion | 子系统次版本号 |
0x253 | SubSystemMajorVersion | 子系统主版本号 |
0x254 | PriorityClass | 单字节,表明进程的优先级程度,分别有以下几个类别:
|
2.用户层数据结构
PEB的全程是进程环境块,它包含了进程大多数的用户模式信息。与EPROCESS结构位于内核空间中不同,PEB是在内核模式建立后映射到用户空间的。因此,多个进程的PEB地址可能是同一个指,PEB的结构如下:
kd> dt _PEB ntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 SpareBool : UChar +0x004 Mutant : Ptr32 Void +0x008 ImageBaseAddress : Ptr32 Void +0x00c Ldr : Ptr32 _PEB_LDR_DATA +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS +0x014 SubSystemData : Ptr32 Void +0x018 ProcessHeap : Ptr32 Void +0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION +0x020 FastPebLockRoutine : Ptr32 Void +0x024 FastPebUnlockRoutine : Ptr32 Void +0x028 EnvironmentUpdateCount : Uint4B +0x02c KernelCallbackTable : Ptr32 Void +0x030 SystemReserved : [1] Uint4B +0x034 AtlThunkSListPtr32 : Uint4B +0x038 FreeList : Ptr32 _PEB_FREE_BLOCK +0x03c TlsExpansionCounter : Uint4B +0x040 TlsBitmap : Ptr32 Void +0x044 TlsBitmapBits : [2] Uint4B +0x04c ReadOnlySharedMemoryBase : Ptr32 Void +0x050 ReadOnlySharedMemoryHeap : Ptr32 Void +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void +0x058 AnsiCodePageData : Ptr32 Void +0x05c OemCodePageData : Ptr32 Void +0x060 UnicodeCaseTableData : Ptr32 Void +0x064 NumberOfProcessors : Uint4B +0x068 NtGlobalFlag : Uint4B +0x070 CriticalSectionTimeout : _LARGE_INTEGER +0x078 HeapSegmentReserve : Uint4B +0x07c HeapSegmentCommit : Uint4B +0x080 HeapDeCommitTotalFreeThreshold : Uint4B +0x084 HeapDeCommitFreeBlockThreshold : Uint4B +0x088 NumberOfHeaps : Uint4B +0x08c MaximumNumberOfHeaps : Uint4B +0x090 ProcessHeaps : Ptr32 Ptr32 Void +0x094 GdiSharedHandleTable : Ptr32 Void +0x098 ProcessStarterHelper : Ptr32 Void +0x09c GdiDCAttributeList : Uint4B +0x0a0 LoaderLock : Ptr32 Void +0x0a4 OSMajorVersion : Uint4B +0x0a8 OSMinorVersion : Uint4B +0x0ac OSBuildNumber : Uint2B +0x0ae OSCSDVersion : Uint2B +0x0b0 OSPlatformId : Uint4B +0x0b4 ImageSubsystem : Uint4B +0x0b8 ImageSubsystemMajorVersion : Uint4B +0x0bc ImageSubsystemMinorVersion : Uint4B +0x0c0 ImageProcessAffinityMask : Uint4B +0x0c4 GdiHandleBuffer : [34] Uint4B +0x14c PostProcessInitRoutine : Ptr32 void +0x150 TlsExpansionBitmap : Ptr32 Void +0x154 TlsExpansionBitmapBits : [32] Uint4B +0x1d4 SessionId : Uint4B +0x1d8 AppCompatFlags : _ULARGE_INTEGER +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER +0x1e8 pShimData : Ptr32 Void +0x1ec AppCompatInfo : Ptr32 Void +0x1f0 CSDVersion : _UNICODE_STRING +0x1f8 ActivationContextData : Ptr32 Void +0x1fc ProcessAssemblyStorageMap : Ptr32 Void +0x200 SystemDefaultActivationContextData : Ptr32 Void +0x204 SystemAssemblyStorageMap : Ptr32 Void +0x208 MinimumStackCommit : Uint4B
偏移 | 名称 | 含义 |
0x002 | BeginDebugged | 是否被调试 |
0x008 | ImageBaseAddress | 执行映像的基地址 |
0x018 | ProcessHeap | 进程堆 |
0x064 | NumberOfProcessors | CPU个数 |
0x068 | NtGlobalFlag | 全局标志 |
0x078 | HeapSegmentReserve | 默认进程堆的总保留空间,1MB |
0x07C | HeapSegmentCommit | 默认进程堆的已提交空间 |
0x088 | NumberOfHeaps | 堆的个数 |
0x08C | MaximumNumberOfHeaps | 堆的最多个数 |
0x090 | ProcessHeaps | 保存堆句柄的数组地址 |
0x094 | GdiSharedHandleTable | GDI共享句柄表 |
0x0A4 | OSMajorVersion | 操作系统主版本号 |
0x0A8 | OSMinorVersion | 操作系统子版本号 |
0x0AC | OSBuildNumber | 操作系统构建号 |
0x0AE | OSCSDVersion | Service Pack版本号 |
0x0B0 | OSPlatformId | 系统类别,2代表NT,1代表9x,3代表Windows CE |
0x0B4 | ImageSubsystem | 环境子系统ID |
0x0B8 | ImageSubsystemMajorVersion | 环境子系统主版本号 |
0x0BC | ImageSubsystemMinorVersion | 环境子系统次版本号 |
0x1D4 | SessionId | 所属会话ID |
通过上面的三个结构分别在用户层和内核层保留了创建的进程的所有的信息,操作系统通过这些信息完成对进程的管理工作。因此,在创建进程的过程中上述的三个结构体是一定要在内存中被申请出来并初始化的。
三.进程的创建
Windows API提供了很多用于创建进程的函数。其中最简单的是CreateProcess,该函数会尝试使用与创建者相同的访问令牌新建一个进程。如果需要不同的令牌,可以使用CreateProcessAsUser函数或CreateProcessWithTokenW函数来实现。如果系统快速用特定用户凭据登录并使用所获得的令牌创建进程,此时可以使用CreateProcesssWithLoginW来实现。
创建一个进程的主要步骤如下:
验证参数,将Windows子系统标志和选项转换为原生形式,解析,验证并转换属性列表为原生形式
打开要在进程中执行的映像文件(.exe)
创建Windows执行体进程对象
创建初始线程(栈,上下文,Windows执行体线程对象)
执行创建之后需要的,与Windows子系统有关的进程初始化操作
开始执行初始线程(除非指定了CREATE_SUSPENDED标志)
在新进程和线程上下文中完成地址空间的初始化操作(如加载需要的DLL),并开始执行程序的入口点
所有文档化的进程创建函数最终都会调用Kernel32.dll中的CreateProcessInternalW。以常见的CreateProcessW函数为例,可以看到在这个函数中就是将参数压入栈中,然后调用CreateProcessInternalW,因此接下来会以此函数为主进行分析
.text:7C802336 ; BOOL __stdcall CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) .text:7C802336 public _CreateProcessW@40 .text:7C802336 _CreateProcessW@40 proc near ; CODE XREF: UnhandledExceptionFilter(x)+835↓p .text:7C802336 ; ConsoleIMERoutine(x)+D2↓p .text:7C802336 ; DATA XREF: ... .text:7C802336 .text:7C802336 lpApplicationName= dword ptr 8 .text:7C802336 lpCommandLine = dword ptr 0Ch .text:7C802336 lpProcessAttributes= dword ptr 10h .text:7C802336 lpThreadAttributes= dword ptr 14h .text:7C802336 bInheritHandles = dword ptr 18h .text:7C802336 dwCreationFlags = dword ptr 1Ch .text:7C802336 lpEnvironment = dword ptr 20h .text:7C802336 lpCurrentDirectory= dword ptr 24h .text:7C802336 lpStartupInfo = dword ptr 28h .text:7C802336 lpProcessInformation= dword ptr 2Ch .text:7C802336 .text:7C802336 mov edi, edi .text:7C802338 push ebp .text:7C802339 mov ebp, esp .text:7C80233B push 0 ; hNewToken .text:7C80233D push [ebp+lpProcessInformation] ; lpProcessInformation .text:7C802340 push [ebp+lpStartupInfo] ; lpStartupInfo .text:7C802343 push [ebp+lpCurrentDirectory] ; lpCurrentDirectory .text:7C802346 push [ebp+lpEnvironment] ; lpEnvironment .text:7C802349 push [ebp+dwCreationFlags] ; dwCreationFlags .text:7C80234C push [ebp+bInheritHandles] ; bInheritHandles .text:7C80234F push [ebp+lpThreadAttributes] ; lpThreadAttributes .text:7C802352 push [ebp+lpProcessAttributes] ; lpProcessAtributes .text:7C802355 push [ebp+lpCommandLine] ; lpCommandLine .text:7C802358 push [ebp+lpApplicationName] ; lpApplicationName .text:7C80235B push 0 ; hToken .text:7C80235D call _CreateProcessInternalW@48 ; CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x) .text:7C802362 pop ebp .text:7C802363 retn 28h .text:7C802363 _CreateProcessW@40 endp
1.转换并验证参数和标志
在函数最开始,首先是将参数赋值到相应的局部变量中,同时也会一部分局部变量初始化为0。
.text:7C8197B0 ; int __stdcall CreateProcessInternalW(HANDLE hToken, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAtributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, PHANDLE hNewToken) .text:7C8197B0 public _CreateProcessInternalW@48 .text:7C8197B0 _CreateProcessInternalW@48 proc near ; CODE XREF: CreateProcessW(x,x,x,x,x,x,x,x,x,x)+27↑p .text:7C8197B0 ; CreateProcessInternalA(x,x,x,x,x,x,x,x,x,x,x,x)+EC↓p .text:7C8197B0 ; DATA XREF: ... .text:7C8197B0 .text:7C8197B0 RegionSize = dword ptr -0A0Ch .text:7C8197B0 var_9FC = dword ptr -9FCh .text:7C8197B0 JobInformation = byte ptr -9F8h .text:7C8197B0 var_9F4 = dword ptr -9F4h .text:7C8197B0 var_9F0 = dword ptr -9F0h .text:7C8197B0 var_9EC = dword ptr -9ECh .text:7C8197B0 var_9E8 = dword ptr -9E8h .text:7C8197B0 var_9E4 = dword ptr -9E4h .text:7C8197B0 var_9E0 = dword ptr -9E0h .text:7C8197B0 var_9DC = dword ptr -9DCh .text:7C8197B0 var_9D8 = dword ptr -9D8h .text:7C8197B0 var_9D4 = dword ptr -9D4h .text:7C8197B0 var_9D0 = dword ptr -9D0h .text:7C8197B0 var_9CC = dword ptr -9CCh .text:7C8197B0 var_9C8 = dword ptr -9C8h .text:7C8197B0 var_9C4 = dword ptr -9C4h .text:7C8197B0 var_9C0 = dword ptr -9C0h .text:7C8197B0 var_9BC = dword ptr -9BCh .text:7C8197B0 var_TeB = dword ptr -9B8h .text:7C8197B0 var_9B4 = dword ptr -9B4h .text:7C8197B0 var_9B0 = dword ptr -9B0h .text:7C8197B0 var_9AC = dword ptr -9ACh .text:7C8197B0 var_9A8 = dword ptr -9A8h .text:7C8197B0 var_9A4 = dword ptr -9A4h .text:7C8197B0 var_9A0 = dword ptr -9A0h .text:7C8197B0 var_99C = dword ptr -99Ch .text:7C8197B0 var_998 = dword ptr -998h .text:7C8197B0 var_994 = dword ptr -994h .text:7C8197B0 var_990 = dword ptr -990h .text:7C8197B0 var_TEb = dword ptr -98Ch .text:7C8197B0 var_988 = dword ptr -988h .text:7C8197B0 var_984 = dword ptr -984h .text:7C8197B0 var_980 = dword ptr -980h .text:7C8197B0 var_97C = dword ptr -97Ch .text:7C8197B0 var_978 = dword ptr -978h .text:7C8197B0 var_Teb = dword ptr -974h .text:7C8197B0 var_970 = dword ptr -970h .text:7C8197B0 var_TEB = dword ptr -96Ch .text:7C8197B0 var_968 = dword ptr -968h .text:7C8197B0 var_964 = dword ptr -964h .text:7C8197B0 var_960 = dword ptr -960h .text:7C8197B0 var_95C = dword ptr -95Ch .text:7C8197B0 var_958 = dword ptr -958h .text:7C8197B0 var_954 = dword ptr -954h .text:7C8197B0 var_950 = dword ptr -950h .text:7C8197B0 var_94C = dword ptr -94Ch .text:7C8197B0 var_948 = LSA_UNICODE_STRING ptr -948h .text:7C8197B0 var_940 = dword ptr -940h .text:7C8197B0 MessageStrings = dword ptr -93Ch .text:7C8197B0 var_938 = dword ptr -938h .text:7C8197B0 var_934 = dword ptr -934h .text:7C8197B0 var_930 = dword ptr -930h .text:7C8197B0 var_92C = dword ptr -92Ch .text:7C8197B0 var_928 = dword ptr -928h .text:7C8197B0 var_924 = dword ptr -924h .text:7C8197B0 var_920 = dword ptr -920h .text:7C8197B0 Parameters = dword ptr -91Ch .text:7C8197B0 var_lpStartupInfo= dword ptr -914h .text:7C8197B0 var_910 = dword ptr -910h .text:7C8197B0 var_dwCreateFlags= dword ptr -90Ch .text:7C8197B0 var_908 = dword ptr -908h .text:7C8197B0 var_hNewToken = dword ptr -904h .text:7C8197B0 var_900 = dword ptr -900h .text:7C8197B0 var_8FC = dword ptr -8FCh .text:7C8197B0 var_8F8 = dword ptr -8F8h .text:7C8197B0 var_8F4 = dword ptr -8F4h .text:7C8197B0 var_8F0 = dword ptr -8F0h .text:7C8197B0 SourceString = _STRING ptr -8ECh .text:7C8197B0 InJob = byte ptr -8E4h .text:7C8197B0 DestinationString= LSA_UNICODE_STRING ptr -8E0h .text:7C8197B0 var_8D8 = dword ptr -8D8h .text:7C8197B0 var_8D4 = dword ptr -8D4h .text:7C8197B0 DebugPort = dword ptr -8D0h .text:7C8197B0 var_8CC = byte ptr -8CCh .text:7C8197B0 var_lpThreadAttributes= dword ptr -8C8h .text:7C8197B0 var_8C1 = byte ptr -8C1h .text:7C8197B0 var_lpProcessInformation= dword ptr -8C0h .text:7C8197B0 ThreadInformation= dword ptr -8BCh .text:7C8197B0 Response = dword ptr -8B8h .text:7C8197B0 var_lpProcessAttributes= dword ptr -8B4h .text:7C8197B0 var_8B0 = dword ptr -8B0h .text:7C8197B0 dwErrCode = dword ptr -8ACh .text:7C8197B0 var_8A8 = dword ptr -8A8h .text:7C8197B0 var_8A4 = dword ptr -8A4h .text:7C8197B0 var_StartupInfo = dword ptr -8A0h .text:7C8197B0 var_DesktopInfoBuffer= dword ptr -898h .text:7C8197B0 var_874 = dword ptr -874h .text:7C8197B0 lpFileName = dword ptr -85Ch .text:7C8197B0 var_858 = dword ptr -858h .text:7C8197B0 var_854 = word ptr -854h .text:7C8197B0 var_850 = dword ptr -850h .text:7C8197B0 var_84C = dword ptr -84Ch .text:7C8197B0 var_848 = dword ptr -848h .text:7C8197B0 Handle = dword ptr -844h .text:7C8197B0 JobHandle = dword ptr -840h .text:7C8197B0 var_hToken = dword ptr -83Ch .text:7C8197B0 var_838 = dword ptr -838h .text:7C8197B0 SectionInformation= dword ptr -834h .text:7C8197B0 var_82C = dword ptr -82Ch .text:7C8197B0 var_828 = dword ptr -828h .text:7C8197B0 var_824 = dword ptr -824h .text:7C8197B0 var_820 = word ptr -820h .text:7C8197B0 var_81E = word ptr -81Eh .text:7C8197B0 var_817 = byte ptr -817h .text:7C8197B0 var_814 = word ptr -814h .text:7C8197B0 Flags = dword ptr -804h .text:7C8197B0 var_800 = dword ptr -800h .text:7C8197B0 var_7FC = dword ptr -7FCh .text:7C8197B0 var_SearchPath = dword ptr -7F8h .text:7C8197B0 ProcessInformation= byte ptr -7F4h .text:7C8197B0 var_7F0 = dword ptr -7F0h .text:7C8197B0 Source = dword ptr -7DCh .text:7C8197B0 var_HeapMemory = dword ptr -7D8h .text:7C8197B0 var_7D1 = byte ptr -7D1h .text:7C8197B0 var_7D0 = dword ptr -7D0h .text:7C8197B0 ObjectAttributes= _OBJECT_ATTRIBUTES ptr -7CCh .text:7C8197B0 UserStack = _INITIAL_TEB ptr -7B4h .text:7C8197B0 var_7A0 = dword ptr -7A0h .text:7C8197B0 var_79A = byte ptr -79Ah .text:7C8197B0 var_799 = byte ptr -799h .text:7C8197B0 var_798 = dword ptr -798h .text:7C8197B0 var_794 = dword ptr -794h .text:7C8197B0 var_790 = byte ptr -790h .text:7C8197B0 var_784 = dword ptr -784h .text:7C8197B0 var_780 = byte ptr -780h .text:7C8197B0 var_774 = dword ptr -774h .text:7C8197B0 var_770 = dword ptr -770h .text:7C8197B0 var_76C = dword ptr -76Ch .text:7C8197B0 var_768 = dword ptr -768h .text:7C8197B0 var_764 = dword ptr -764h .text:7C8197B0 var_760 = dword ptr -760h .text:7C8197B0 var_75C = dword ptr -75Ch .text:7C8197B0 var_754 = dword ptr -754h .text:7C8197B0 var_Environment = dword ptr -750h .text:7C8197B0 var_749 = byte ptr -749h .text:7C8197B0 Status = dword ptr -748h .text:7C8197B0 DirectoryInfo = _RTL_RELATIVE_NAME_U ptr -744h .text:7C8197B0 var_734 = dword ptr -734h .text:7C8197B0 var_72C = dword ptr -72Ch .text:7C8197B0 var_728 = dword ptr -728h .text:7C8197B0 var_lpCommandLine= dword ptr -720h .text:7C8197B0 var_lpApplicationName= dword ptr -71Ch .text:7C8197B0 var_716 = byte ptr -716h .text:7C8197B0 var_715 = byte ptr -715h .text:7C8197B0 IoStatusBlock = _IO_STATUS_BLOCK ptr -714h .text:7C8197B0 var_709 = byte ptr -709h .text:7C8197B0 var_708 = dword ptr -708h .text:7C8197B0 var_704 = dword ptr -704h .text:7C8197B0 var_700 = dword ptr -700h .text:7C8197B0 var_6FC = dword ptr -6FCh .text:7C8197B0 var_6F8 = dword ptr -6F8h .text:7C8197B0 var_6F4 = dword ptr -6F4h .text:7C8197B0 var_6F0 = dword ptr -6F0h .text:7C8197B0 var_6EC = dword ptr -6ECh .text:7C8197B0 ClientId = _CLIENT_ID ptr -6E8h .text:7C8197B0 var_6E0 = UNICODE_STRING ptr -6E0h .text:7C8197B0 AnsiString = _STRING ptr -6D8h .text:7C8197B0 UnicodeString = LSA_UNICODE_STRING ptr -6D0h .text:7C8197B0 var_6C8 = LSA_UNICODE_STRING ptr -6C8h .text:7C8197B0 Destination = LSA_UNICODE_STRING ptr -6C0h .text:7C8197B0 NtPathName = LSA_UNICODE_STRING ptr -6B8h .text:7C8197B0 var_6AD = byte ptr -6ADh .text:7C8197B0 SuspendCount = dword ptr -6ACh .text:7C8197B0 SystemInformation= byte ptr -6A8h .text:7C8197B0 var_6A4 = dword ptr -6A4h .text:7C8197B0 FilePart = dword ptr -6A0h .text:7C8197B0 BaseAddress = dword ptr -69Ch .text:7C8197B0 var_698 = dword ptr -698h .text:7C8197B0 NumberOfBytesToWrite= dword ptr -694h .text:7C8197B0 CaptureBuffer = dword ptr -690h .text:7C8197B0 P = dword ptr -68Ch .text:7C8197B0 var_688 = dword ptr -688h .text:7C8197B0 var_684 = dword ptr -684h .text:7C8197B0 var_680 = dword ptr -680h .text:7C8197B0 ThreadHandle = dword ptr -67Ch .text:7C8197B0 FileHandle = dword ptr -678h .text:7C8197B0 var_674 = dword ptr -674h .text:7C8197B0 SectionHandle = dword ptr -670h .text:7C8197B0 ProcessHandle = dword ptr -66Ch .text:7C8197B0 var_668 = byte ptr -668h .text:7C8197B0 var_PriorityClass= byte ptr -667h .text:7C8197B0 ThreadContext = CONTEXT ptr -664h .text:7C8197B0 ApiMessage = byte ptr -398h .text:7C8197B0 ExitStatus = dword ptr -378h .text:7C8197B0 var_370 = byte ptr -370h .text:7C8197B0 var_348 = dword ptr -348h .text:7C8197B0 var_33C = byte ptr -33Ch .text:7C8197B0 var_314 = byte ptr -314h .text:7C8197B0 var_2F0 = byte ptr -2F0h .text:7C8197B0 var_2D8 = byte ptr -2D8h .text:7C8197B0 var_2C4 = dword ptr -2C4h .text:7C8197B0 var_2B4 = byte ptr -2B4h .text:7C8197B0 var_2A0 = dword ptr -2A0h .text:7C8197B0 var_290 = byte ptr -290h .text:7C8197B0 var_27C = dword ptr -27Ch .text:7C8197B0 var_26C = byte ptr -26Ch .text:7C8197B0 var_258 = dword ptr -258h .text:7C8197B0 UnicodeStringOrUnicodeStringBuffer= byte ptr -248h .text:7C8197B0 var_234 = dword ptr -234h .text:7C8197B0 Buffer = word ptr -224h .text:7C8197B0 var_Cookie = dword ptr -1Ch .text:7C8197B0 ms_exc = CPPEH_RECORD ptr -18h .text:7C8197B0 hToken = dword ptr 8 .text:7C8197B0 lpApplicationName= dword ptr 0Ch .text:7C8197B0 lpCommandLine = dword ptr 10h .text:7C8197B0 lpProcessAtributes= dword ptr 14h .text:7C8197B0 lpThreadAttributes= dword ptr 18h .text:7C8197B0 bInheritHandles = dword ptr 1Ch .text:7C8197B0 dwCreationFlags = dword ptr 20h .text:7C8197B0 lpEnvironment = dword ptr 24h .text:7C8197B0 lpCurrentDirectory= dword ptr 28h .text:7C8197B0 lpStartupInfo = dword ptr 2Ch .text:7C8197B0 lpProcessInformation= dword ptr 30h .text:7C8197B0 hNewToken = dword ptr 34h .text:7C8197B0 .text:7C8197B0 push 0A08h .text:7C8197B5 push offset stru_7C819A88 .text:7C8197BA call __SEH_prolog .text:7C8197BF mov eax, ___security_cookie .text:7C8197C4 mov [ebp+var_Cookie], eax .text:7C8197C7 mov eax, [ebp+hToken] .text:7C8197CA mov [ebp+var_hToken], eax .text:7C8197D0 mov eax, [ebp+lpApplicationName] .text:7C8197D3 mov [ebp+var_lpApplicationName], eax .text:7C8197D9 mov eax, [ebp+lpCommandLine] .text:7C8197DC mov [ebp+var_lpCommandLine], eax .text:7C8197E2 mov eax, [ebp+lpProcessAtributes] .text:7C8197E5 mov [ebp+var_lpProcessAttributes], eax .text:7C8197EB mov eax, [ebp+lpThreadAttributes] .text:7C8197EE mov [ebp+var_lpThreadAttributes], eax .text:7C8197F4 mov eax, [ebp+lpEnvironment] .text:7C8197F7 mov [ebp+var_Environment], eax .text:7C8197FD mov eax, [ebp+lpCurrentDirectory] .text:7C819800 mov [ebp+lpFileName], eax .text:7C819806 mov eax, [ebp+lpStartupInfo] .text:7C819809 mov [ebp+var_lpStartupInfo], eax .text:7C81980F mov esi, [ebp+lpProcessInformation] .text:7C819812 mov [ebp+var_lpProcessInformation], esi .text:7C819818 mov edx, [ebp+hNewToken] ; 将hNewToken赋值到edx中 .text:7C81981B mov [ebp+var_hNewToken], edx .text:7C819821 xor ebx, ebx ; ebx清0 .text:7C819823 mov [ebp+var_674], ebx .text:7C819829 mov [ebp+Source], ebx .text:7C81982F mov [ebp+var_680], ebx .text:7C819835 mov [ebp+var_754], ebx .text:7C81983B mov [ebp+var_7D0], ebx .text:7C819841 mov [ebp+var_8D8], ebx .text:7C819847 mov eax, [ebp+dwCreationFlags] .text:7C81984A and eax, CREATE_NO_WINDOW ; dwCreateFlags加上CREATE_NO_WINDOWS标志 .text:7C81984F mov [ebp+var_dwCreateFlags], eax .text:7C819855 mov [ebp+var_749], bl .text:7C81985B mov [ebp+DebugPort], ebx .text:7C819861 mov [ebp+P], ebx .text:7C819867 mov [ebp+NumberOfBytesToWrite], ebx .text:7C81986D mov [ebp+var_715], bl .text:7C819873 mov [ebp+var_688], ebx .text:7C819879 mov [ebp+var_698], ebx .text:7C81987F mov [ebp+CaptureBuffer], ebx .text:7C819885 mov [ebp+var_84C], ebx .text:7C81988B lea eax, [ebp+var_2B4] .text:7C819891 mov [ebp+var_930], eax .text:7C819897 lea eax, [ebp+var_26C] .text:7C81989D mov [ebp+var_92C], eax .text:7C8198A3 lea eax, [ebp+UnicodeStringOrUnicodeStringBuffer] .text:7C8198A9 mov [ebp+var_928], eax .text:7C8198AF lea eax, [ebp+var_2D8] .text:7C8198B5 mov [ebp+var_924], eax .text:7C8198BB lea eax, [ebp+var_290] .text:7C8198C1 mov [ebp+var_920], eax .text:7C8198C7 mov [ebp+var_764], ebx .text:7C8198CD xor eax, eax ; eax清0 .text:7C8198CF lea edi, [ebp+var_760] .text:7C8198D5 stosd .text:7C8198D6 stosd .text:7C8198D7 stosd .text:7C8198D8 mov [ebp+var_784], ebx .text:7C8198DE xor eax, eax ; eax清0 .text:7C8198E0 lea edi, [ebp+var_780] .text:7C8198E6 stosd .text:7C8198E7 stosd .text:7C8198E8 stosd .text:7C8198E9 lea eax, [ebp+var_6C8] .text:7C8198EF mov [ebp+var_6F8], eax .text:7C8198F5 lea eax, [ebp+var_6F0] .text:7C8198FB mov [ebp+var_6F4], eax .text:7C819901 lea eax, [ebp+var_2B4] .text:7C819907 mov [ebp+var_708], eax .text:7C81990D lea eax, [ebp+var_2D8] .text:7C819913 mov [ebp+var_704], eax .text:7C819919 lea eax, [ebp+var_26C] .text:7C81991F mov [ebp+var_700], eax .text:7C819925 lea eax, [ebp+var_290] .text:7C81992B mov [ebp+var_6FC], eax .text:7C819931 mov [ebp+var_794], ebx .text:7C819937 xor eax, eax .text:7C819939 lea edi, [ebp+var_790] .text:7C81993F stosd .text:7C819940 stosd .text:7C819941 stosd .text:7C819942 mov [ebp+var_7FC], ebx .text:7C819948 mov dword ptr [ebp+InJob], ebx .text:7C81994E mov [ebp+JobHandle], ebx .text:7C819954 mov [ebp+Handle], ebx .text:7C81995A mov dword ptr [ebp+var_8CC], ebx .text:7C819960 mov [ebp+var_72C], ebx .text:7C819966 xor eax, eax .text:7C819968 lea edi, [ebp+var_728] .text:7C81996E stosd .text:7C81996F stosd .text:7C819970 mov [ebp+DirectoryInfo.CurDirRef], ebx .text:7C819976 xor eax, eax .text:7C819978 lea edi, [ebp+var_734] .text:7C81997E stosd .text:7C81997F stosd .text:7C819980 mov [ebp+var_799], bl .text:7C819986 push 18h .text:7C819988 pop ecx .text:7C819989 xor eax, eax .text:7C81998B lea edi, [ebp+var_348] .text:7C819991 rep stosd .text:7C819993 mov edi, esi .text:7C819995 stosd .text:7C819996 stosd .text:7C819997 stosd .text:7C819998 stosd
接下来函数会查看hNewToken中是否传入了参数,如果有参数,就将对应地址中的内容赋值为0
.text:7C819999 cmp edx, ebx ; 保存hNewToken地址是否为NULL,不为NULL则跳转 .text:7C81999B jnz loc_7C82E52F ; 。。。 .text:7C82E52F loc_7C82E52F: .text:7C82E52F mov [edx], ebx ; 将hNewToken赋值为0 .text:7C82E531 jmp loc_7C8199A1
接下来函数会对进程的优先级进行设定,而进程优先级是由参数dwCreateFlags标志来决定的。
在windows中,对于参数有如下宏定义
#define CREATE_NO_WINDOW 0x08000000
所以如下代码的作用就是从标志中删除CREATE_NO_WINDOWS参数
.text:7C8199A1 loc_7C8199A1: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+14D81↓j .text:7C8199A1 mov eax, [ebp+dwCreationFlags] .text:7C8199A4 and eax, 0F7FFFFFFh ; 清除CREATE_NO_WINDOWS标志 .text:7C8199A9 mov [ebp+dwCreationFlags], eax
再根据这两个宏定义
#define DETACHED_PROCESS 0x00000008 #define CREATE_NEW_CONSOLE 0x00000010
可以直到接下来的代码就是判断标志中是否同时有DETACHED_PROCESS和CREATE_NEW_CONSOLE
.text:7C8199AC mov ecx, eax .text:7C8199AE and ecx, 18h .text:7C8199B1 cmp cl, 18h .text:7C8199B4 jz loc_7C8427DE
是的话就会跳转到下面的代码,此时会设定错误并退出函数
.text:7C8427DE loc_7C8427DE: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+204↑j .text:7C8427DE push ERROR_INVALID_PARAMETER .text:7C8427E0 call _SetLastError@4 ; SetLastError(x) ;设置错误类型 .text:7C8427E5 jmp loc_7C81D783 。。。 .text:7C81D783 loc_7C81D783: .text:7C81D783 .text:7C81D783 xor eax, eax ; 返回值清0 .text:7C81D785 jmp loc_7C81A058 。。。 .text:7C81A058 loc_7C81A058: .text:7C81A058 mov ecx, [ebp+var_Cookie] .text:7C81A05B call @__security_check_cookie@4 ; __security_check_cookie(x) .text:7C81A060 call __SEH_epilog .text:7C81A065 retn 30h .text:7C81A065 ; } // starts at 7C819BDF
以下宏定义则是用来判别如何设置进程优先级
#define IDLE_PRIORITY_CLASS 0x00000040 #define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 #define NORMAL_PRIORITY_CLASS 0x00000020 #define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 #define HIGH_PRIORITY_CLASS 0x00000080 #define REALTIME_PRIORITY_CLASS 0x00000100
函数会使用这些宏来判断创建进程的标志中指定了什么样的进程优先级
.text:7C8199C6 test al, 40h ; 标记是否有IDLE_PRIORITY_CLASS .text:7C8199C8 jnz loc_7C8427EA .text:7C8199CE test ah, 40h ; 标记是否有BELOW_NORMAL_PRIORITY_CLASS .text:7C8199D1 jnz loc_7C8427F6 .text:7C8199D7 test al, 20h ; 标记是否有NORMAL_PRIORITY_CLASS .text:7C8199D9 jnz loc_7C82C9A1 .text:7C8199DF test ah, ah ; 最高位是否为1,此时就会判断是否有ABOVE_NORMAL_PRIORITY_CLASS标志 .text:7C8199E1 js loc_7C842802 .text:7C8199E7 test al, al ; 最高位是否为1,此时会判断是否有HIGH_PRIORITY_CLASS .text:7C8199E9 js loc_7C84280E .text:7C8199EF test ah, 1 ; 判断是否有REALTIME_PRIORITY_CLASS标志 .text:7C8199F2 jnz loc_7C84281A .text:7C8199F8 mov [ebp+var_PriorityClass], bl ; 如果不是上述情况,进程优先级就会赋值为0
接着根据指定的优先级来赋值不同的进程优先级
.text:7C8427EA loc_7C8427EA: .text:7C8427EA mov [ebp+var_PriorityClass], PROCESS_PRIORITY_CLASS_IDLE .text:7C8427F1 jmp loc_7C8199FE .text:7C8427F6 ; --------------------------------------------------------------------------- .text:7C8427F6 .text:7C8427F6 loc_7C8427F6: .text:7C8427F6 mov [ebp+var_PriorityClass], PROCESS_PRIORITY_CLASS_BELOW_NORMAL .text:7C8427FD jmp loc_7C8199FE .text:7C842802 ; --------------------------------------------------------------------------- .text:7C842802 .text:7C842802 loc_7C842802: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+231↑j .text:7C842802 mov [ebp+var_PriorityClass], PROCESS_PRIORITY_CLASS_ABOVE_NORMAL .text:7C842809 jmp loc_7C8199FE .text:7C84280E ; --------------------------------------------------------------------------- .text:7C84280E mov [ebp+var_PriorityClass], PROCESS_PRIORITY_CLASS_HIGH .text:7C842815 jmp loc_7C8199FE .text:7C84281A ; --------------------------------------------------------------------------- .text:7C84281A push ebx .text:7C84281B call _BasepIsRealtimeAllowed@4 ; BasepIsRealtimeAllowed(x) .text:7C842820 test eax, eax .text:7C842822 setnz al .text:7C842825 add al, 3 .text:7C842827 mov [ebp+var_PriorityClass], al .text:7C84282D jmp loc_7C8199FE
设置完进程优先级,函数就会把创建进程中指定的标志中有关进程优先级的内容删除
.text:7C8199FE mov [ebp+var_668], bl .text:7C819A04 and word ptr [ebp+dwCreationFlags], 3E1Fh ;删除标志中和进程优先级有关的内容
接下来的内容则判断标记中和VDM相关的内容
.text:7C819A0A mov edi, CREATE_SEPARATE_WOW_VDM .text:7C819A0F mov esi, CREATE_SHARED_WOW_VDM .text:7C819A14 test [ebp+dwCreationFlags], edi .text:7C819A17 jnz loc_7C842832 .text:7C819A1D test [ebp+dwCreationFlags], esi .text:7C819A20 jnz short loc_7C819A33 .text:7C819A22 mov eax, _BaseStaticServerData .text:7C819A27 cmp [eax+19F4h], bl .text:7C819A2D jnz loc_7C84283C .text:7C819A33 .text:7C819A33 loc_7C819A33: .text:7C819A33 test [ebp+dwCreationFlags], edi .text:7C819A36 jnz short loc_7C819A56 .text:7C819A38 push ebx ; ReturnLength .text:7C819A39 push 4 ; JobInformationLength .text:7C819A3B lea eax, [ebp+JobInformation] .text:7C819A41 push eax ; JobInformation .text:7C819A42 push 4 ; JobInformationClass .text:7C819A44 push ebx ; JobHandle .text:7C819A45 call ds:__imp__NtQueryInformationJobObject@20 ; NtQueryInformationJobObject(x,x,x,x,x) .text:7C819A4B cmp eax, STATUS_ACCESS_DENIED .text:7C819A50 jnz loc_7C842844 。。。 .text:7C842832 loc_7C842832: .text:7C842832 test [ebp+dwCreationFlags], esi .text:7C842835 jnz short loc_7C8427DE .text:7C842837 jmp loc_7C819A33 .text:7C8427DE loc_7C8427DE: .text:7C8427DE push ERROR_INVALID_PARAMETER .text:7C8427E0 call _SetLastError@4 ; SetLastError(x) .text:7C8427E5 jmp loc_7C81D783
接下去就会判断创建进程时传入的参数lpEnvironment是否有效以及是否有CREATE_PROTECTED_PROCESS标志,满足的话则会将eax指向保存环境字符串的地址的末尾
.text:7C819A56 loc_7C819A56: .text:7C819A56 mov ecx, [ebp+var_Environment] .text:7C819A5C cmp ecx, ebx ; 传入的lpEnvironment是否为NULL .text:7C819A5E jz loc_7C818E17 .text:7C819A64 test byte ptr [ebp+dwCreationFlags+1], 4 ; 标志中是否带有CREATE_PROTECTED_PROCESS .text:7C819A68 jnz loc_7C818E17 .text:7C819A6E mov eax, ecx .text:7C819A70 mov [ebp+SourceString.Buffer], ecx .text:7C819A76 .text:7C819A76 loc_7C819A76: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+2CF↓j .text:7C819A76 cmp [eax], bl ; 找到末尾为0的位置 .text:7C819A78 jz loc_7C818D8A .text:7C819A7E .text:7C819A7E loc_7C819A7E: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)-A23↑j .text:7C819A7E inc eax .text:7C819A7F jmp short loc_7C819A76
然后把传入的Unicode字符串转换为Ansi字符串后保存
.text:7C818D8A loc_7C818D8A: .text:7C818D8A cmp [eax+1], bl .text:7C818D8D jnz loc_7C819A7E .text:7C818D93 sub eax, ecx .text:7C818D95 inc eax .text:7C818D96 mov [ebp+SourceString.Length], ax .text:7C818D9D mov eax, dword ptr [ebp+SourceString.Length] .text:7C818DA3 inc eax .text:7C818DA4 mov [ebp+SourceString.MaximumLength], ax .text:7C818DAB movzx eax, ax .text:7C818DAE shl eax, 1 .text:7C818DB0 mov [ebp+RegionSize], eax .text:7C818DB6 mov [ebp+DestinationString.Buffer], ebx .text:7C818DBC push PAGE_READWRITE ; Protect .text:7C818DBE push esi ; AllocationType .text:7C818DBF lea eax, [ebp+RegionSize] .text:7C818DC5 push eax ; RegionSize .text:7C818DC6 push ebx ; ZeroBits .text:7C818DC7 lea eax, [ebp+DestinationString.Buffer] .text:7C818DCD push eax ; BaseAddress .text:7C818DCE push 0FFFFFFFFh ; ProcessHandle .text:7C818DD0 call ds:__imp__NtAllocateVirtualMemory@24 ; NtAllocateVirtualMemory(x,x,x,x,x,x) .text:7C818DD6 cmp eax, ebx .text:7C818DD8 jl loc_7C842856 .text:7C818DDE mov ax, word ptr [ebp+RegionSize] .text:7C818DE5 mov [ebp+DestinationString.MaximumLength], ax .text:7C818DEC push ebx ; AllocateDestinationString .text:7C818DED lea eax, [ebp+SourceString] .text:7C818DF3 push eax ; SourceString .text:7C818DF4 lea eax, [ebp+DestinationString] .text:7C818DFA push eax ; DestinationString .text:7C818DFB call ds:__imp__RtlAnsiStringToUnicodeString@12 ; RtlAnsiStringToUnicodeString(x,x,x) .text:7C818E01 mov esi, eax .text:7C818E03 cmp esi, ebx .text:7C818E05 jl loc_7C842861 .text:7C818E0B mov eax, [ebp+DestinationString.Buffer] .text:7C818E11 mov [ebp+var_Environment], eax ; 保存转换后的环境变量
接着函数会继续初始化局部变量,并且将传入的StartupInfo内容保存到局部变量中
.text:7C818E17 loc_7C818E17: .text:7C818E17 mov [ebp+FileHandle], ebx .text:7C818E1D mov [ebp+SectionHandle], ebx .text:7C818E23 mov [ebp+ProcessHandle], ebx .text:7C818E29 mov [ebp+ThreadHandle], ebx .text:7C818E2F mov [ebp+var_SearchPath], ebx .text:7C818E35 mov [ebp+var_HeapMemory], ebx .text:7C818E3B mov [ebp+UnicodeString.Buffer], ebx .text:7C818E41 mov [ebp+BaseAddress], 1 .text:7C818E4B mov [ebp+var_684], ebx .text:7C818E51 mov [ebp+var_8B0], ebx .text:7C818E57 mov [ebp+FilePart], ebx .text:7C818E5D mov [ebp+Destination.Buffer], ebx .text:7C818E63 mov [ebp+var_716], bl .text:7C818E69 mov [ebp+var_800], ebx .text:7C818E6F mov [ebp+ms_exc.registration.TryLevel], ebx .text:7C818E72 push 11h .text:7C818E74 pop ecx .text:7C818E75 mov esi, [ebp+var_lpStartupInfo] .text:7C818E7B lea edi, [ebp+var_StartupInfo] .text:7C818E81 rep movsd ; 将传入的参数StartupInfo保存到局部变量中
接下来的内容最主要的就是传入的将要启动的文件名通过RtlDosPathNameToNtPathName转换为NT内部名称(比如:c:\temp\1.exe会转换为\device\harddiskvolumn1\temp\1.exe),因为在内核中需要使用这样的名字来打开文件。
.text:7C818EF9 loc_7C818EF9: .text:7C818EF9 lea eax, [ebp+DirectoryInfo] .text:7C818EFF push eax ; DirectoryInfo .text:7C818F00 push ebx ; NtFileNamePart .text:7C818F01 lea eax, [ebp+NtPathName] ; 要在内核中使用的文件名 .text:7C818F07 push eax ; NtPathName .text:7C818F08 mov edi, [ebp+var_lpApplicationName] .text:7C818F0E push edi ; DosPathName .text:7C818F0F call ds:__imp__RtlDosPathNameToNtPathName_U@16 ; RtlDosPathNameToNtPathName_U(x,x,x,x) .text:7C818F15 mov [ebp+var_8C1], al .text:7C818F1B cmp al, bl .text:7C818F1D jz loc_7C84293F .text:7C818F23 mov eax, [ebp+NtPathName.Buffer] .text:7C818F29 mov [ebp+var_SearchPath], eax .text:7C818F2F push edi ; SourceString .text:7C818F30 lea eax, [ebp+var_6C8] .text:7C818F36 push eax ; DestinationString .text:7C818F37 mov esi, ds:__imp__RtlInitUnicodeString@8 ; RtlInitUnicodeString(x,x) .text:7C818F3D call esi ; RtlInitUnicodeString(x,x) ; RtlInitUnicodeString(x,x) .text:7C818F3F push edi ; Path .text:7C818F40 call ds:__imp__RtlDetermineDosPathNameType_U@4 ; RtlDetermineDosPathNameType_U(x) .text:7C818F46 mov [ebp+var_984], eax .text:7C818F4C cmp eax, 2 .text:7C818F4F jnz loc_7C81D712
2.打开要执行的映像
将经过转换以后的文件名作为参数调用NtOpenFile来获得文件句柄。
.text:7C818F82 loc_7C818F82: .text:7C818F82 mov [ebp+ObjectAttributes.Length], 18h .text:7C818F8C mov eax, [ebp+DirectoryInfo.ContainingDirectory] .text:7C818F92 mov [ebp+ObjectAttributes.RootDirectory], eax .text:7C818F98 mov [ebp+ObjectAttributes.Attributes], OBJ_CASE_INSENSITIVE .text:7C818FA2 lea eax, [ebp+NtPathName] ; 经过转换以后的映像名 .text:7C818FA8 mov [ebp+ObjectAttributes.ObjectName], eax .text:7C818FAE mov [ebp+ObjectAttributes.SecurityDescriptor], ebx .text:7C818FB4 mov [ebp+ObjectAttributes.SecurityQualityOfService], ebx .text:7C818FBA push 60h ; OpenOptions .text:7C818FBC push 5 ; ShareAccess .text:7C818FBE lea eax, [ebp+IoStatusBlock] .text:7C818FC4 push eax ; IoStatusBlock .text:7C818FC5 lea eax, [ebp+ObjectAttributes] .text:7C818FCB push eax ; ObjectAttributes .text:7C818FCC push 1000A1h ; 要获得句柄的权限 .text:7C818FD1 lea eax, [ebp+FileHandle] .text:7C818FD7 push eax ; FileHandle .text:7C818FD8 mov esi, ds:__imp__NtOpenFile@24 ; NtOpenFile(x,x,x,x,x,x) .text:7C818FDE call esi ; NtOpenFile(x,x,x,x,x,x) ; NtOpenFile(x,x,x,x,x,x) .text:7C818FE0 mov [ebp+Status], eax .text:7C818FE6 cmp eax, ebx ; 如果返回值小于0说明打开失败 .text:7C818FE8 jl loc_7C81D7AD
而第二个参数指定了句柄的权限,而根据以下的宏定义可以知道要获得的权限。
#define SYNCHRONIZE (0x00100000L) #define FILE_READ_DATA ( 0x0001 ) // file & pipe #define FILE_READ_ATTRIBUTES ( 0x0080 ) // all #define FILE_EXECUTE ( 0x0020 ) // file
如果第一次调用NtOpenFile失败,则函数会再次调用该函数打开文件,只是此时要获得的权限发生改变。
.text:7C81D7AD loc_7C81D7AD: .text:7C81D7AD push 60h ; OpenOptions .text:7C81D7AF push 5 ; ShareAccess .text:7C81D7B1 lea eax, [ebp+IoStatusBlock] .text:7C81D7B7 push eax ; IoStatusBlock .text:7C81D7B8 lea eax, [ebp+ObjectAttributes] .text:7C81D7BE push eax ; ObjectAttributes .text:7C81D7BF push 100020h ; DesiredAccess .text:7C81D7C4 lea eax, [ebp+FileHandle] .text:7C81D7CA push eax ; FileHandle .text:7C81D7CB call esi ; NtOpenFile(x,x,x,x,x,x) ; NtOpenFile(x,x,x,x,x,x) .text:7C81D7CD mov [ebp+Status], eax .text:7C81D7D3 cmp eax, ebx ; 如果大于等于则说明打开成功 .text:7C81D7D5 jge loc_7C818FEE
根据宏定义可以知道,这次是以只要获得执行权限的方式打开
#define FILE_EXECUTE ( 0x0020 ) // file
如果这次打开还是失败,那么函数接下来就会判断失败原因。首先通过RtlIsDosDeviceName判断是否是因为正在执行设备导致失败,是的话会设置相应的错误码。
.text:7C81D7DC call ds:__imp__RtlIsDosDeviceName_U@4 ; RtlIsDosDeviceName_U(x) .text:7C81D7E2 test eax, eax ; 非0则说明正在执行设备 .text:7C81D7E4 jnz loc_7C84294D 。。。 .text:7C84294D loc_7C84294D: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+4034↑j .text:7C84294D push ERROR_BAD_DEVICE .text:7C842952 jmp loc_7C83589C 。。。 .text:7C83589C loc_7C83589C: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+29191↓j .text:7C83589C ; CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+29198↓j ... .text:7C83589C call _SetLastError@4 ; SetLastError(x) .text:7C8358A1 jmp loc_7C81D776
如果不是则会将NtOpenFile函数执行失败的结果作为error code返回。
.text:7C81D7F0 loc_7C81D7F0: .text:7C81D7F0 call _BaseSetLastNTError@4 ; BaseSetLastNTError(x) .text:7C81D7F5 jmp loc_7C81D776
接下来函数会判断调用方是否指定了桌面,如果没有的话就会将本进程PEB中的桌面内容赋值过去
.text:7C818FEE loc_7C818FEE: .text:7C818FEE cmp [ebp+var_DesktopInfoBuffer], ebx .text:7C818FF4 jz loc_7C819BA7 。。。 .text:7C819BA7 loc_7C819BA7: .text:7C819BA7 mov eax, large fs:18h .text:7C819BAD mov [ebp+var_TEb], eax .text:7C819BB3 mov eax, [eax+_TEB.ProcessEnvironmentBlock] .text:7C819BB6 mov eax, [eax+_PEB.ProcessParameters] .text:7C819BB9 mov eax, [eax+RTL_USER_PROCESS_PARAMETERS.DesktopInfo.Buffer] .text:7C819BBC mov [ebp+var_DesktopInfoBuffer], eax .text:7C819BC2 jmp loc_7C818FFA .text:7C819BC2 ; } // starts at 7C819BA7
随后函数就通过NtCreateSection来对打开的文件句柄创建内存映射
.text:7C818FFA loc_7C818FFA: .text:7C818FFA push [ebp+FileHandle] ; FileHandle .text:7C819000 push SEC_IMAGE ; AllocationAttributes .text:7C819005 push PAGE_EXECUTE ; SectionPageProtection .text:7C819007 push ebx ; MaximumSize .text:7C819008 push ebx ; ObjectAttributes .text:7C819009 push SECTION_ALL_ACCESS ; DesiredAccess .text:7C81900E lea eax, [ebp+SectionHandle] .text:7C819014 push eax ; SectionHandle .text:7C819015 call ds:__imp__NtCreateSection@28 ; NtCreateSection(x,x,x,x,x,x,x) .text:7C81901B mov edi, eax .text:7C81901D mov [ebp+Status], edi .text:7C819023 cmp edi, ebx .text:7C819025 jl short loc_7C81904C
打开成功以后,将会调用BasepIsProcessAllowed函数来得知是否可以加载进程
.text:7C819027 push [ebp+var_lpApplicationName] ; Size .text:7C81902D call _BasepIsProcessAllowed@4 ; BasepIsProcessAllowed(x) .text:7C819032 mov edi, eax .text:7C819034 mov [ebp+Status], edi .text:7C81903A cmp edi, ebx ; 返回值小于0则说明无法加载 .text:7C81903C jl loc_7C842957 。。。 .text:7C842957 loc_7C842957: .text:7C842957 push edi ; Status .text:7C842958 call _BaseSetLastNTError@4 ; BaseSetLastNTError(x) .text:7C84295D push [ebp+SectionHandle] ; Handle .text:7C842963 call ds:__imp__NtClose@4 ; NtClose(x) .text:7C842969 jmp loc_7C81D776
根据宏定义
#define CREATE_FORCEDOS 0x00002000
可以知道,接下来会判断创建进程的标志中是否带有CREATE_FORCEDOS
.text:7C819042 test byte ptr [ebp+dwCreationFlags+1], 20h ; 是否具有CREATE_FORCEDOS标志 .text:7C819046 jnz loc_7C84296E
如果没有,则会设置错误码,释放句柄等等
.text:7C84296E loc_7C84296E: .text:7C84296E mov eax, _BaseStaticServerData .text:7C842973 cmp [eax+19F5h], bl .text:7C842979 jz loc_7C81904C .text:7C84297F and byte ptr [ebp+dwCreationFlags+1], 0CFh .text:7C842983 mov esi, CREATE_SEPARATE_WOW_VDM .text:7C842988 or [ebp+dwCreationFlags], esi .text:7C84298B mov edi, STATUS_INVALID_IMAGE_WIN_16 .text:7C842990 mov [ebp+Status], edi .text:7C842996 mov [ebp+var_8D8], 1 .text:7C8429A0 push [ebp+SectionHandle] ; Handle .text:7C8429A6 call ds:__imp__NtClose@4 ; NtClose(x) .text:7C8429AC mov [ebp+SectionHandle], ebx .text:7C8429B2 jmp loc_7C819051
如果内存映射成功,函数会进行一系列验证,然后通过NtQuerySection来获得文件的信息
.text:7C819128 loc_7C819128: .text:7C819128 push ebx ; ResultLength .text:7C819129 push 30h ; Length .text:7C81912B lea eax, [ebp+SectionInformation] .text:7C819131 push eax ; SectionInformation .text:7C819132 xor edi, edi .text:7C819134 inc edi .text:7C819135 push edi ; SectionInformationClass .text:7C819136 push [ebp+SectionHandle] ; SectionHandle .text:7C81913C call ds:__imp__NtQuerySection@20 ; NtQuerySection(x,x,x,x,x) .text:7C819142 mov [ebp+Status], eax .text:7C819148 cmp eax, ebx .text:7C81914A jl loc_7C82DF58
根据宏定义
#define DEBUG_PROCESS 0x00000001 #define DEBUG_ONLY_THIS_PROCESS 0x00000002
函数会判断传入的参数中是否带有调试相关的标志
.text:7C819164 test byte ptr [ebp+dwCreationFlags], 3 ; 是否带有DEBUG_PROCESS或DEBUG_ONLY_THIS_PROCESS标志 .text:7C819168 jnz loc_7C842CEF
如果有,则会进一步验证PEB中的ReadImageFileExecOptions字段是否为0
.text:7C842CEF loc_7C842CEF: .text:7C842CEF mov eax, large fs:18h .text:7C842CF5 mov [ebp+var_tEB], eax .text:7C842CFB mov eax, [eax+_TEB.ProcessEnvironmentBlock] .text:7C842CFE cmp [eax+_PEB.ReadImageFileExecOptions], bl .text:7C842D01 jnz loc_7C81916E .text:7C842D07 jmp loc_7C81918D
如果不为0,接下来就会通过LdrQueryImageFileExecutionOptions来查询调试器信息
.text:7C81916E loc_7C81916E: .text:7C81916E push ebx ; RetunedLength .text:7C81916F push 208h ; BufferSize .text:7C819174 lea eax, [ebp+Buffer] .text:7C81917A push eax ; Buffer .text:7C81917B push edi ; ValueSize .text:7C81917C push offset aDebugger ; "Debugger" .text:7C819181 lea eax, [ebp+NtPathName] .text:7C819187 push eax ; SubKey .text:7C819188 call _LdrQueryImageFileExecutionOptions@24
随后函数会再次判断参数中是否有调试标志
.text:7C819279 loc_7C819279: .text:7C819279 test byte ptr [ebp+dwCreationFlags], 3 ; 是否有DEBUG_PROCESS或DEBUG_ONLY_THIS_PROCESS标志 .text:7C81927D jnz loc_7C842E87
有的话就会调用DbgUiConnectToDbg来建立调试关系
.text:7C842E87 loc_7C842E87: .text:7C842E87 call _DbgUiConnectToDbg@0 ; DbgUiConnectToDbg() .text:7C842E8C mov [ebp+Status], eax .text:7C842E92 cmp eax, ebx .text:7C842E94 jl loc_7C82DF58 .text:7C842E9A call _DbgUiGetThreadDebugObject@0 ; DbgUiGetThreadDebugObject() .text:7C842E9F mov [ebp+DebugPort], eax .text:7C842EA5 test byte ptr [ebp+dwCreationFlags], 2 .text:7C842EA9 jz loc_7C819283 .text:7C842EAF or [ebp+Flags], 2 .text:7C842EB6 jmp loc_7C819283
3.创建Windows执行体对象
至此已经打开了有效的Windows可执行文件并将其映射到相应的地址空间,接下来就需要通过系统调用NtCreateProcessEx来创建内核对象,由于这个部分内容比较多,后面再详细说。
.text:7C8192AD push dword ptr [ebp+InJob] ; InJob .text:7C8192B3 push ebx ; ExceptionPort .text:7C8192B4 push [ebp+DebugPort] ; DebugPort .text:7C8192BA push [ebp+SectionHandle] ; SectionHandle .text:7C8192C0 push [ebp+Flags] ; Flags .text:7C8192C6 or esi, 0FFFFFFFFh .text:7C8192C9 push esi ; ParentProcess .text:7C8192CA push [ebp+var_838] ; ObjectAttributes .text:7C8192D0 push 1F0FFFh ; DesiredAccess .text:7C8192D5 lea eax, [ebp+ProcessHandle] .text:7C8192DB push eax ; ProcessHandle .text:7C8192DC call ds:__imp__NtCreateProcessEx@36 ; NtCreateProcessEx(x,x,x,x,x,x,x,x,x) .text:7C8192E2 mov [ebp+Status], eax .text:7C8192E8 cmp eax, ebx .text:7C8192EA jl loc_7C82DF60
创建完成以后函数会判断是否设置了进程的优先级
.text:7C8192F0 cmp [ebp+var_PriorityClass], bl .text:7C8192F6 jnz loc_7C82C954
如果设置了,接着会判断进程优先级是否为实时优先级
.text:7C82C954 loc_7C82C954: .text:7C82C954 mov [ebp+RealTimePrivilegeState], ebx .text:7C82C95A cmp [ebp+var_PriorityClass], PROCESS_PRIORITY_CLASS_REALTIME .text:7C82C961 jz loc_7C842EC6
是的话就会调用函数来判断是否权限是否足够
.text:7C842EC6 loc_7C842EC6: .text:7C842EC6 push edi .text:7C842EC7 call _BasepIsRealtimeAllowed@4 ; BasepIsRealtimeAllowed(x) .text:7C842ECC mov [ebp+RealTimePrivilegeState], eax .text:7C842ED2 jmp loc_7C82C967
如果符合条件,接下来就会调用函数来设置进程的优先级
.text:7C82C967 loc_7C82C967: .text:7C82C967 push 2 ; ProcessInformationLength .text:7C82C969 lea eax, [ebp+var_668] .text:7C82C96F push eax ; ProcessInformation .text:7C82C970 push ProcessPriorityClass ; ProcessInformationClass .text:7C82C972 push [ebp+ProcessHandle] ; ProcessHandle .text:7C82C978 call ds:__imp__NtSetInformationProcess@16 ; NtSetInformationProcess(x,x,x,x) .text:7C82C97E mov [ebp+Status], eax .text:7C82C984 cmp [ebp+RealTimePrivilegeState], ebx .text:7C82C98A jnz loc_7C842ED7
根据以下的宏定义就可以知道
#define CREATE_DEFAULT_ERROR_MODE 0x04000000
设置完进程优先级以后,函数会继续判断传入的标志中是否有
.text:7C8192FC loc_7C8192FC: .text:7C8192FC test byte ptr [ebp+dwCreationFlags+3], 4 ; 是否有CREATE_DEFAULT_ERROR_MODE标志 .text:7C819300 jnz loc_7C81FEFB
如果有,接下来就会通过函数来设置错误模式
.text:7C81FEFB loc_7C81FEFB: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)-4B0↑j .text:7C81FEFB ; __unwind { // __SEH_prolog .text:7C81FEFB mov [ebp+var_ProcessInformation], edi .text:7C81FF01 push 4 ; ProcessInformationLength .text:7C81FF03 lea eax, [ebp+var_ProcessInformation] .text:7C81FF09 push eax ; ProcessInformation .text:7C81FF0A push ProcessDefaultHardErrorMode ; ProcessInformationClass .text:7C81FF0C push [ebp+ProcessHandle] ; ProcessHandle .text:7C81FF12 call ds:__imp__NtSetInformationProcess@16 ; NtSetInformationProcess(x,x,x,x) .text:7C81FF18 jmp loc_7C819306
接下来会调用函数从创建的进程的内存中读出数据
.text:7C81FE2E push ebx ; NumberOfBytesRead .text:7C81FE2F push 4 ; NumberOfBytesToRead .text:7C81FE31 lea eax, [ebp+var_798] .text:7C81FE37 push eax ; Buffer .text:7C81FE38 mov eax, [ebp+var_848] .text:7C81FE3E add eax, 10h .text:7C81FE41 push eax ; BaseAddress .text:7C81FE42 push [ebp+ProcessHandle] ; ProcessHandle .text:7C81FE48 call ds:__imp__NtReadVirtualMemory@20 ; NtReadVirtualMemory(x,x,x,x,x) .text:7C81FE4E mov [ebp+Status], eax .text:7C81FE54 cmp eax, ebx .text:7C81FE56 jl loc_7C819DEC
接下来会分别对输入输出以及错误句柄进行判断,如果不是控制台句柄就会通过StuffStdHandle进行复制
.text:7C81FE5C mov eax, large fs:18h .text:7C81FE62 mov [ebp+var_teb0], eax .text:7C81FE68 mov eax, [eax+_TEB.ProcessEnvironmentBlock] .text:7C81FE6B mov eax, [eax+_PEB.ProcessParameters] .text:7C81FE6E mov eax, [eax+RTL_USER_PROCESS_PARAMETERS.StandardInput] ; 对输入句柄判断 .text:7C81FE71 mov esi, 10000003h .text:7C81FE76 and eax, esi .text:7C81FE78 cmp eax, edi .text:7C81FE7A jnz loc_7C82DF7C 。。。 .text:7C82DF7C loc_7C82DF7C: .text:7C82DF7C mov eax, large fs:18h .text:7C82DF82 mov [ebp+var_teb5], eax .text:7C82DF88 mov ecx, [ebp+var_798] .text:7C82DF8E add ecx, 18h .text:7C82DF91 push ecx ; BaseAddress .text:7C82DF92 mov eax, [eax+_TEB.ProcessEnvironmentBlock] .text:7C82DF95 mov eax, [eax+_PEB.ProcessParameters] .text:7C82DF98 push [eax+RTL_USER_PROCESS_PARAMETERS.StandardInput] ; 对输入句柄复制 .text:7C82DF9B push [ebp+ProcessHandle] ; TargetProcessHandle .text:7C82DFA1 call _StuffStdHandle@12 ; StuffStdHandle(x,x,x) .text:7C82DFA6 jmp loc_7C81FE80
.text:7C81FE80 loc_7C81FE80: .text:7C81FE80 mov eax, large fs:18h .text:7C81FE86 mov [ebp+var_teb1], eax .text:7C81FE8C mov eax, [eax+_TEB.ProcessEnvironmentBlock] .text:7C81FE8F mov eax, [eax+_PEB.ProcessParameters] .text:7C81FE92 mov eax, [eax+RTL_USER_PROCESS_PARAMETERS.StandardOutput] ; 输出句柄的判断 .text:7C81FE95 and eax, esi .text:7C81FE97 cmp eax, edi .text:7C81FE99 jnz loc_7C82DF29 。。。 .text:7C82DF29 loc_7C82DF29: .text:7C82DF29 mov eax, large fs:18h .text:7C82DF2F mov [ebp+var_teb4], eax .text:7C82DF35 mov ecx, [ebp+var_798] .text:7C82DF3B add ecx, 1Ch .text:7C82DF3E push ecx ; BaseAddress .text:7C82DF3F mov eax, [eax+_TEB.ProcessEnvironmentBlock] .text:7C82DF42 mov eax, [eax+_PEB.ProcessParameters] .text:7C82DF45 push [eax+RTL_USER_PROCESS_PARAMETERS.StandardOutput] ; 对输出句柄复制 .text:7C82DF48 push [ebp+ProcessHandle] ; TargetProcessHandle .text:7C82DF4E call _StuffStdHandle@12 ; StuffStdHandle(x,x,x) .text:7C82DF53 jmp loc_7C81FE9F
.text:7C81FE9F loc_7C81FE9F: .text:7C81FE9F mov eax, large fs:18h .text:7C81FEA5 mov [ebp+var_teb2], eax .text:7C81FEAB mov eax, [eax+_TEB.ProcessEnvironmentBlock] .text:7C81FEAE mov eax, [eax+_PEB.ProcessParameters] .text:7C81FEB1 mov eax, [eax+RTL_USER_PROCESS_PARAMETERS.StandardError] ; 对错误句柄进行判断 .text:7C81FEB4 and eax, esi .text:7C81FEB6 cmp eax, edi .text:7C81FEB8 jz loc_7C819DEC .text:7C81FEBE mov eax, large fs:18h .text:7C81FEC4 mov [ebp+var_teb3], eax .text:7C81FECA mov ecx, [ebp+var_798] .text:7C81FED0 add ecx, 20h .text:7C81FED3 push ecx ; BaseAddress .text:7C81FED4 mov eax, [eax+_TEB.ProcessEnvironmentBlock] .text:7C81FED7 mov eax, [eax+_PEB.ProcessParameters] .text:7C81FEDA push [eax+RTL_USER_PROCESS_PARAMETERS.StandardError] ; 对错误句柄进行复制 .text:7C81FEDD push [ebp+ProcessHandle] ; TargetProcessHandle .text:7C81FEE3 call _StuffStdHandle@12 ; StuffStdHandle(x,x,x) .text:7C81FEE8 jmp loc_7C819DEC
现在进程执行体已经创建完成,但是想要执行指令,还需要线程才可以完成。因此,要继续创建再进程中运行的线程。
4.创建线程和栈及上下文
至此,Windows执行体进程对象已经创建完成,然而其中依然不具备线程,因此还无法开始工作。接下来的工作就需要创建线程,而创建线程之前需要初始化创建线程需要的资源。
首先需要调用BaseCreateStack来创建线程栈
.text:7C819DEC loc_7C819DEC: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+631↑j .text:7C819DEC ; CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+665C↓j ... .text:7C819DEC mov eax, 40000h ; 是否小于0x4000 .text:7C819DF1 cmp [ebp+ImageInformation.MaximumStackSize], eax .text:7C819DF7 jb short loc_7C819DFF .text:7C819DF9 mov eax, [ebp+ImageInformation.MaximumStackSize] .text:7C819DFF .text:7C819DFF loc_7C819DFF: ; CODE XREF: CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+647↑j .text:7C819DFF lea ecx, [ebp+InitialTeb] .text:7C819E05 push ecx ; int .text:7C819E06 push eax ; RegionSize .text:7C819E07 push [ebp+ImageInformation.CommittedStackSize] ; UINT_PTR .text:7C819E0D push [ebp+ProcessHandle] ; ProcessHandle .text:7C819E13 call _BaseCreateStack@16 ; BaseCreateStack(x,x,x,x) .text:7C819E18 mov [ebp+var_CreateStackStatus], eax .text:7C819E1E cmp eax, ebx .text:7C819E20 jl loc_7C82DF58
接着需要调用BaseInitializeContext来初始化线程的CONTEXT
.text:7C819E26 push ebx .text:7C819E27 push [ebp+InitialTeb.StackBase] .text:7C819E2D push [ebp+SectionInformation] .text:7C819E33 push [ebp+var_PEB] .text:7C819E39 lea eax, [ebp+ThreadContext] .text:7C819E3F push eax .text:7C819E40 call _BaseInitializeContext@20 ; BaseInitializeContext(x,x,x,x,x)
最后通过系统调用NtCreateThread来创建内核线程对象,但是此时是以挂起的方式创建的线程
.text:7C819E72 push 1 ; CreateSuspended .text:7C819E74 lea eax, [ebp+InitialTeb] .text:7C819E7A push eax ; UserStack .text:7C819E7B lea eax, [ebp+ThreadContext] .text:7C819E81 push eax ; ThreadContext .text:7C819E82 lea eax, [ebp+ClientId] .text:7C819E88 push eax ; ClientId .text:7C819E89 push [ebp+ProcessHandle] ; ProcessHandle .text:7C819E8F push [ebp+var_838] ; ObjectAttributes .text:7C819E95 push THREAD_ALL_ACCESS ; DesiredAccess .text:7C819E9A lea eax, [ebp+ThreadHandle] .text:7C819EA0 push eax ; ThreadHandle .text:7C819EA1 call ds:__imp__NtCreateThread@32 ; NtCreateThread(x,x,x,x,x,x,x,x) .text:7C819EA7 mov [ebp+Status], eax .text:7C819EAD cmp eax, ebx .text:7C819EAF jl loc_7C82DF58
5.执行与Windows子系统有关的初始化工作
当创建了进程与线程对象以后,接下来函数就会使用CsrClientCallServer来给Windows子系统(Csrss)发送消息
.text:7C819F76 loc_7C819F76: .text:7C819F76 push 98h ; DataLength .text:7C819F7B push 10000h ; ApiNumber .text:7C819F80 push [ebp+CaptureBuffer] ; CaptureBuffer .text:7C819F86 lea eax, [ebp+ApiMessage] .text:7C819F8C push eax ; ApiMessage .text:7C819F8D call ds:__imp__CsrClientCallServer@16 ; CsrClientCallServer(x,x,x,x) .text:7C819F93 cmp [ebp+CaptureBuffer], ebx .text:7C819F99 jnz loc_7C82BA1A
该消息包含以下信息:
路径名称和SxS路径名称
进程和线程句柄
区域句柄
访问令牌句柄
媒体信息
AppCompat和填充数据
沉浸式进程信息
PEB地址
各种标志,如是否为受保护进程,是否需要再提权后运行等
代表进程是否属于某个Windows应用程序(Csrss借此决定是否显示启动光标)的标志
UI语言信息
DLL重定向和.local标志
清单文件信息
收到该消息后,Windows子系统将执行下列操作:
CsrCreateProcess复制进程和线程句柄。在这一步中,进程和线程的使用计数会从1增加到2
分配Csrss进程结构(CSR_PROCESS)
新进程的异常端口设置为Windows子系统的通用功能端口,借此Windows子系统能再进程中发送二次异常
如果要新建进程组,且该新进程称为进程组的根(在CreateProcess中使用了CREATE_NEW_PROCESS_GROUP标志),则在CSR_PROCESS中设置。进程组可用于将控制事件发送给一组共享了同一控制台进程
分配并初始化Csrss线程结构(CSR_THREAD)
CsrCreateThread将该线程插入到进程的线程列表
递增会话中的进程计数
进程的关机级别(shutdown level)设置为0x280,这是默认的进程关机级别
新建的Csrss进程结构被插入到Windows子系统范围内的进程列表
6.初始线程的启动执行
由于上面创建线程对象的时候是以挂起的形式创建的,所以现在需要通过NtResumeThread来恢复线程的执行
.text:7C819FC3 loc_7C819FC3: .text:7C819FC3 test byte ptr [ebp+dwCreationFlags], CREATE_SUSPENDED .text:7C819FC7 jnz short loc_7C819FDC .text:7C819FC9 lea eax, [ebp+SuspendCount] .text:7C819FCF push eax ; SuspendCount .text:7C819FD0 push [ebp+ThreadHandle] ; ThreadHandle .text:7C819FD6 call ds:__imp__NtResumeThread@8 ; NtResumeThread(x,x)
7.在新线程的上下文中执行进程初始化工作
此时新创建的进程中的初始线程正式开始工作,由于进程中还有一些资源需要初始化以后才可以保证线程的正常运行。所以,线程最开始的工作依然是对进程的初始化,这部分内容较多,之后再详细说。
四.系统调用NtCreateProcess
上面说到,创建进程过程中会通过系统调用NtCreateProcess来创建进程的内核对象,而再该函数中则是对几个参数进行检验并赋值相应的标记以后就调用NtCreateProcessEx来完成操作。其中参数InherObjectTable说明新创建的进程是否要继承父进程的句柄表,如果要,则参数ParentProcess就是父进程的句柄,一般这就是当前进程,但也可以不是。换言之,当前进程可以为别的进程创建子进程,让新创建的进程从别的进程继承句柄表,实际上还包括一些资源的配额和有关的属性。另一个参数SectionHandle是一个文件映射区的句柄,这个映射区代表目标映像文件。最后两个进程间的通信端口DebugPort和ExceptionPort的句柄。顾名思义,这两个端口是供新创进程发送调试信息和异常处理信息的端口,是可以为所有进程共用的系统资源。
PAGE:004DFDCD ; NTSTATUS __stdcall NtCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, BOOLEAN InheritObjectTable, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort) PAGE:004DFDCD _NtCreateProcess@32 proc near ; DATA XREF: .text:0040D8DC↑o PAGE:004DFDCD PAGE:004DFDCD ProcessHandle = dword ptr 8 PAGE:004DFDCD DesiredAccess = dword ptr 0Ch PAGE:004DFDCD ObjectAttributes= dword ptr 10h PAGE:004DFDCD ParentProcess = dword ptr 14h PAGE:004DFDCD InheritObjectTable= byte ptr 18h PAGE:004DFDCD SectionHandle = dword ptr 1Ch PAGE:004DFDCD DebugPort = dword ptr 20h PAGE:004DFDCD ExceptionPort = dword ptr 24h PAGE:004DFDCD PAGE:004DFDCD ; FUNCTION CHUNK AT PAGE:0052E198 SIZE 00000016 BYTES PAGE:004DFDCD PAGE:004DFDCD mov edi, edi PAGE:004DFDCF push ebp PAGE:004DFDD0 mov ebp, esp PAGE:004DFDD2 xor eax, eax PAGE:004DFDD4 test byte ptr [ebp+SectionHandle], 1 PAGE:004DFDD8 jnz loc_52E198 PAGE:004DFDDE PAGE:004DFDDE loc_4DFDDE: ; CODE XREF: NtCreateProcess(x,x,x,x,x,x,x,x)+4E3CC↓j PAGE:004DFDDE test byte ptr [ebp+DebugPort], 1 PAGE:004DFDE2 jnz loc_52E19E PAGE:004DFDE8 PAGE:004DFDE8 loc_4DFDE8: ; CODE XREF: NtCreateProcess(x,x,x,x,x,x,x,x)+4E3D4↓j PAGE:004DFDE8 cmp [ebp+InheritObjectTable], 0 PAGE:004DFDEC jnz loc_52E1A6 PAGE:004DFDF2 PAGE:004DFDF2 loc_4DFDF2: ; CODE XREF: NtCreateProcess(x,x,x,x,x,x,x,x)+4E3DC↓j PAGE:004DFDF2 push 0 ; InJob PAGE:004DFDF4 push [ebp+ExceptionPort] ; ExceptionPort PAGE:004DFDF7 push [ebp+DebugPort] ; DebugPort PAGE:004DFDFA push [ebp+SectionHandle] ; SectionHandle PAGE:004DFDFD push eax ; Flags PAGE:004DFDFE push [ebp+ParentProcess] ; ParentProcess PAGE:004DFE01 push [ebp+ObjectAttributes] ; ObjectAttributes PAGE:004DFE04 push [ebp+DesiredAccess] ; DesiredAccess PAGE:004DFE07 push [ebp+ProcessHandle] ; ProcessHandle PAGE:004DFE0A call _NtCreateProcessEx@36 ; NtCreateProcessEx(x,x,x,x,x,x,x,x,x) PAGE:004DFE0F pop ebp PAGE:004DFE10 retn 20h PAGE:004DFE10 _NtCreateProcess@32 endp 。。。 PAGE:0052E198 loc_52E198: ; CODE XREF: NtCreateProcess(x,x,x,x,x,x,x,x)+B↑j PAGE:0052E198 inc eax PAGE:0052E199 jmp loc_4DFDDE PAGE:0052E19E ; --------------------------------------------------------------------------- PAGE:0052E19E PAGE:0052E19E loc_52E19E: ; CODE XREF: NtCreateProcess(x,x,x,x,x,x,x,x)+15↑j PAGE:0052E19E or eax, 2 PAGE:0052E1A1 jmp loc_4DFDE8 PAGE:0052E1A6 ; --------------------------------------------------------------------------- PAGE:0052E1A6 PAGE:0052E1A6 loc_52E1A6: ; CODE XREF: NtCreateProcess(x,x,x,x,x,x,x,x)+1F↑j PAGE:0052E1A6 or eax, 4 PAGE:0052E1A9 jmp loc_4DFDF2
NtCreateProcessEx函数首先就是对PreviousMode进行判断
PAGE:004B4A0C ; NTSTATUS __stdcall NtCreateProcessEx(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort, BOOLEAN InJob) PAGE:004B4A0C _NtCreateProcessEx@36 proc near ; CODE XREF: NtCreateProcess(x,x,x,x,x,x,x,x)+3D↓p PAGE:004B4A0C ; DATA XREF: .text:0040D8E0↑o PAGE:004B4A0C PAGE:004B4A0C var_1C = dword ptr -1Ch PAGE:004B4A0C ms_exc = CPPEH_RECORD ptr -18h PAGE:004B4A0C ProcessHandle = dword ptr 8 PAGE:004B4A0C DesiredAccess = dword ptr 0Ch PAGE:004B4A0C ObjectAttributes= dword ptr 10h PAGE:004B4A0C ParentProcess = dword ptr 14h PAGE:004B4A0C Flags = dword ptr 18h PAGE:004B4A0C SectionHandle = dword ptr 1Ch PAGE:004B4A0C DebugPort = dword ptr 20h PAGE:004B4A0C ExceptionPort = dword ptr 24h PAGE:004B4A0C InJob = byte ptr 28h PAGE:004B4A0C push 0Ch PAGE:004B4A0E push offset stru_4285B0 PAGE:004B4A13 call __SEH_prolog PAGE:004B4A18 mov eax, large fs:124h ; 获取当前线程地址 PAGE:004B4A1E xor edx, edx PAGE:004B4A20 cmp [eax+140h], dl ; PreviousMode是否为0 PAGE:004B4A26 jz loc_4E9036
如果不为0,说明是从用户层模式调用的函数,此时就需要对句柄是否可读进行验证
PAGE:004B4A2F mov ecx, [ebp+ProcessHandle] PAGE:004B4A32 mov eax, _MmUserProbeAddress PAGE:004B4A37 cmp ecx, eax PAGE:004B4A39 jnb loc_52E165 PAGE:004B4A3F loc_4B4A3F: ; CODE XREF: NtCreateProcessEx(x,x,x,x,x,x,x,x,x)+7975B↓j PAGE:004B4A3F mov eax, [ecx] PAGE:004B4A41 mov [ecx], eax
接着判断父进程句柄是否为空,如果是则报错
PAGE:004B4A47 loc_4B4A47: ; CODE XREF: NtCreateProcessEx(x,x,x,x,x,x,x,x,x)+3462D↓j PAGE:004B4A47 cmp [ebp+ParentProcess], edx ; 判断父进程句柄是否为NULL PAGE:004B4A4A jz loc_4E903E 。。。 PAGE:004E903E loc_4E903E: ; CODE XREF: NtCreateProcessEx(x,x,x,x,x,x,x,x,x)+3E↑j PAGE:004E903E mov eax, STATUS_INVALID_PARAMETER PAGE:004E9043 jmp loc_4B4A6E
如果不是则将参数入栈以后调用PspCreateProcess
PAGE:004B4A50 push dword ptr [ebp+InJob] ; int PAGE:004B4A53 push [ebp+ExceptionPort] ; HANDLE PAGE:004B4A56 push [ebp+DebugPort] ; HANDLE PAGE:004B4A59 push [ebp+SectionHandle] ; HANDLE PAGE:004B4A5C push [ebp+Flags] ; int PAGE:004B4A5F push [ebp+ParentProcess] ; Handle PAGE:004B4A62 push [ebp+ObjectAttributes] ; int PAGE:004B4A65 push [ebp+DesiredAccess] ; AccessMask PAGE:004B4A68 push ecx ; int PAGE:004B4A69 call _PspCreateProcess@36 ; PspCreateProcess(x,x,x,x,x,x,x,x,x)
在PspCreateProcess函数中首先会对局部变量进行赋值,然后会判断创建标志中是否有相关标志
PAGE:004B450E ; int __stdcall PspCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort, ULONG JobMemberLevel) PAGE:004B450E _PspCreateProcess@36 proc near ; CODE XREF: NtCreateProcessEx(x,x,x,x,x,x,x,x,x)+5D↓p PAGE:004B450E ; PsCreateSystemProcess(x,x,x)+1B↓p ... PAGE:004B450E PAGE:004B450E var_12C = dword ptr -12Ch PAGE:004B450E var_B8 = dword ptr -0B8h PAGE:004B450E var_9C = dword ptr -9Ch PAGE:004B450E SubjectSecurityContext= _SECURITY_SUBJECT_CONTEXT ptr -98h PAGE:004B450E var_88 = dword ptr -88h PAGE:004B450E var_CurThraed = dword ptr -84h PAGE:004B450E ParentProcObject= dword ptr -80h PAGE:004B450E var_7C = dword ptr -7Ch PAGE:004B450E var_78 = dword ptr -78h PAGE:004B450E var_74 = dword ptr -74h PAGE:004B450E var_70 = dword ptr -70h PAGE:004B450E var_6C = dword ptr -6Ch PAGE:004B450E var_WorkingSetMaximum= dword ptr -68h PAGE:004B450E var_Affinity = dword ptr -64h PAGE:004B450E var_WorkingSetMinimum= dword ptr -60h PAGE:004B450E var_5C = dword ptr -5Ch PAGE:004B450E var_CurEProcess = dword ptr -58h PAGE:004B450E var_ProcObject = dword ptr -54h PAGE:004B450E var_50 = dword ptr -50h PAGE:004B450E AccessStatus = dword ptr -4Ch PAGE:004B450E SpinLock = dword ptr -48h PAGE:004B450E var_44 = dword ptr -44h PAGE:004B450E var_40 = dword ptr -40h PAGE:004B450E var_3C = dword ptr -3Ch PAGE:004B450E MemoryAllocated = byte ptr -38h PAGE:004B450E var_34 = dword ptr -34h PAGE:004B450E BugCheckParameter1= dword ptr -30h PAGE:004B450E SecurityDescriptor= dword ptr -2Ch PAGE:004B450E var_28 = dword ptr -28h PAGE:004B450E var_22 = byte ptr -22h PAGE:004B450E AccessMode = byte ptr -21h PAGE:004B450E var_1D = byte ptr -1Dh PAGE:004B450E var_ParentProcObject= dword ptr -1Ch PAGE:004B450E ms_exc = CPPEH_RECORD ptr -18h PAGE:004B450E ProcessHandle = dword ptr 8 PAGE:004B450E AccessMask = dword ptr 0Ch PAGE:004B450E ObjectAttributes= dword ptr 10h PAGE:004B450E ParentProcessHandle= dword ptr 14h PAGE:004B450E Flags = dword ptr 18h PAGE:004B450E SectionHandle = dword ptr 1Ch PAGE:004B450E DebugPort = dword ptr 20h PAGE:004B450E ExceptionPort = dword ptr 24h PAGE:004B450E JobMemberLevel = dword ptr 28h PAGE:004B450E push 11Ch PAGE:004B4513 push offset stru_4285C0 PAGE:004B4518 call __SEH_prolog PAGE:004B451D mov eax, large fs:124h ; 获取ETHREAD PAGE:004B4523 mov [ebp+var_CurThraed], eax PAGE:004B4529 mov cl, [eax+140h] ; 获取PrevMode PAGE:004B452F mov [ebp+AccessMode], cl PAGE:004B4532 mov eax, [eax+44h] ; 获取当前进程EPROCESS PAGE:004B4535 mov [ebp+var_CurEProcess], eax PAGE:004B4538 xor esi, esi PAGE:004B453A mov [ebp+var_1D], 0 PAGE:004B453E mov [ebp+SpinLock], esi PAGE:004B4541 mov [ebp+var_44], esi PAGE:004B4544 test [ebp+Flags], 0FFFFFFF0h ; 是否具有PROCESS_CREATE_FLAGS_LEGAL_MSK标志 PAGE:004B454B jnz loc_52DE13
如果没有,则会返回错误
PAGE:0052DE13 loc_52DE13: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+3D↑j PAGE:0052DE13 mov eax, STATUS_INVALID_PARAMETER PAGE:0052DE18 jmp loc_4B49D0
接下来会判断是否有父进程句柄
PAGE:004B4551 cmp [ebp+ParentProcessHandle], esi PAGE:004B4554 jz loc_4F1B06
如果没有,则会对会父进程的EPROCESS赋值为空且使用KeActiveProcessors来赋值eax
PAGE:004F1B06 loc_4F1B06: PAGE:004F1B06 mov [ebp+var_ParentProcObject], esi PAGE:004F1B09 mov eax, _KeActiveProcessors PAGE:004F1B0E jmp loc_4B458F
否则会调用ObReferenceObjectByHandle来获得父进程的对象地址,并且使用父进程的Affinity来赋值eax
PAGE:004B455A push esi ; HandleInformation PAGE:004B455B lea eax, [ebp+ParentProcObject] PAGE:004B455E push eax ; Object PAGE:004B455F push dword ptr [ebp+AccessMode] ; AccessMode PAGE:004B4562 push _PsProcessType ; ObjectType PAGE:004B4568 push PROCESS_CREATE_PROCESS ; DesiredAccess PAGE:004B456D push [ebp+ParentProcessHandle] ; Handle PAGE:004B4570 call _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x) PAGE:004B4575 mov ecx, [ebp+ParentProcObject] ; Object PAGE:004B4578 mov [ebp+var_ParentProcObject], ecx PAGE:004B457B cmp eax, esi PAGE:004B457D jl loc_4B49D0 PAGE:004B458C loc_4B458C: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+798FA↓j PAGE:004B458C mov eax, [ecx+5Ch] ; EPROCESS->Pcb.Affinity
接着在对局部变量进行赋值
PAGE:004B458F loc_4B458F: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+3D600↓j PAGE:004B458F mov [ebp+var_Affinity], eax PAGE:004B4592 mov eax, ds:_PsMinimumWorkingSet PAGE:004B4597 mov [ebp+var_WorkingSetMinimum], eax PAGE:004B459A mov eax, ds:_PsMaximumWorkingSet PAGE:004B459F mov [ebp+var_WorkingSetMaximum], eax
接下来就会通过ObCreateObject来在内核中创建进程对象并将创建的进程对象赋值到局部变量中
PAGE:004B45A2 lea eax, [ebp+var_ProcObject] PAGE:004B45A5 push eax ; Object PAGE:004B45A6 push esi ; NonPagedPoolCharge PAGE:004B45A7 push esi ; PagePoolCharge PAGE:004B45A8 push 260h ; ObjectBodySize PAGE:004B45AD push esi ; ParentContext PAGE:004B45AE push dword ptr [ebp+AccessMode] ; OwnershipMode PAGE:004B45B1 push [ebp+ObjectAttributes] ; ObjectAttributes PAGE:004B45B4 push _PsProcessType ; ObjectType PAGE:004B45BA push dword ptr [ebp+AccessMode] ; PreviousMode PAGE:004B45BD call _ObCreateObject@36 ; ObCreateObject(x,x,x,x,x,x,x,x,x) PAGE:004B45C2 mov edi, eax PAGE:004B45C4 cmp edi, esi PAGE:004B45C6 jl loc_4B49C2 PAGE:004B45CC mov ecx, 98h PAGE:004B45D1 xor eax, eax PAGE:004B45D3 mov ebx, [ebp+var_ProcObject] ; 将EPROCESS赋值给ebx PAGE:004B45D6 mov edi, ebx PAGE:004B45D8 rep stosd
创建好了内核对象以后,就要开始完成对对象的初始化。要注意此时得到的地址指向的是内核对象的对象体,也就是EPROCESS结构,此时ebx的值保存的就是创建的进程对象的EPROCESS地址,所以接下来的代码执行的就是对其成员的赋值,根据上面的EPROCESS结构不难知道要赋值的内容
PAGE:004B45DA mov [ebx+80h], esi ; 将RundownProtect赋值为NULL PAGE:004B45E0 mov [ebx+6Ch], esi ; 将ProcessLock赋值为NULL PAGE:004B45E3 lea eax, [ebx+190h] ; 取出ThreadListHead的地址赋给eax PAGE:004B45E9 mov [eax+LIST_ENTRY.Blink], eax ; 为ThreadListHead初始化 PAGE:004B45EC mov [eax+LIST_ENTRY.Flink], eax
初始化过程也包含了继承父进程资源和属性的过程。这里首先通过PspInheritQuota继承父进程的资源配额,在通过ObInheritDeviceMap继承父进程的(磁盘)设备位图DeviceMap
PAGE:004B45EE push [ebp+var_ParentProcObject] PAGE:004B45F1 push ebx PAGE:004B45F2 call _PspInheritQuota@8 ; PspInheritQuota(x,x) PAGE:004B45F7 push [ebp+var_ParentProcObject] PAGE:004B45FA push ebx PAGE:004B45FB call _ObInheritDeviceMap@8 ; ObInheritDeviceMap(x,x)
接下来判断是否存在父进程的对象
PAGE:004B4600 mov edi, [ebp+var_ParentProcObject] PAGE:004B4603 cmp edi, esi PAGE:004B4605 jz loc_4F1B13
如果不存在,则会为创建的进程初始化为默认数值
PAGE:004F1B13 loc_4F1B13: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+F7↑j PAGE:004F1B13 mov dword ptr [ebx+1A8h], 1 ; 为DefaultHardErrorProcessing赋值为1 PAGE:004F1B1D mov [ebx+14Ch], esi ; 为InheritedFromUniquePricessId赋值为0 PAGE:004F1B23 jmp loc_4B4623
否则就用父进程的数据来初始化创建的进程
PAGE:004B460B mov eax, [edi+1A8h] ; 取出父进程的DefaultHardErrorProcessing PAGE:004B4611 mov [ebx+1A8h], eax ; 将其赋给创建的进程的DefaultHardErrorProcessing PAGE:004B4617 mov eax, [edi+84h] ; 取出父进程的UniqueProcessId PAGE:004B461D mov [ebx+14Ch], eax ; 将其赋值给创建的进程的InheritedFromUniquePricessId
接着判断传入的SectionHandle是否为NULL
PAGE:004B4623 cmp [ebp+SectionHandle], esi PAGE:004B4626 jz loc_4F1B28
如果不为空,则会通过函数来获得想要的对象地址并将其赋值到局部变量SectionObj中
PAGE:004B462C push esi ; HandleInformation PAGE:004B462D lea eax, [ebp+var_SectionObject] PAGE:004B4633 push eax ; Object PAGE:004B4634 push dword ptr [ebp+AccessMode] ; AccessMode PAGE:004B4637 push _MmSectionObjectType ; ObjectType PAGE:004B463D push SECTION_MAP_EXECUTE ; DesiredAccess PAGE:004B463F push [ebp+SectionHandle] ; Handle PAGE:004B4642 call _ObReferenceObjectByHandle@24 PAGE:004B4647 mov ecx, [ebp+var_SectionObject] PAGE:004B464D mov [ebp+var_SectionObj], ecx PAGE:004B4650 mov edi, eax PAGE:004B4652 cmp eax, esi PAGE:004B4654 jl loc_4B49BB
如果为空会对父进程进行判断
PAGE:004F1B28 loc_4F1B28: PAGE:004F1B28 mov [ebp+var_SectionObj], esi PAGE:004F1B2B cmp edi, _PsInitialSystemProcess PAGE:004F1B31 jz loc_4B465D PAGE:004F1B37 jmp loc_52DE63
如果父进程为PsInitialSystemProcess则会对SectionObj赋值为空,否则会将父进程的SectionObject赋给局部变量SectionObj,注意此时的edi指向的是父进程对象
PAGE:0052DE63 loc_52DE63: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+3D629↑j PAGE:0052DE63 lea ecx, [edi+80h] ; 取出RunDownProtect PAGE:0052DE69 call @ExAcquireRundownProtection@4 ; ExAcquireRundownProtection(x) PAGE:0052DE6E test al, al PAGE:0052DE70 jz short loc_52DE8F PAGE:0052DE72 mov ecx, [edi+138h] ; 取出父进程SectionObjection PAGE:0052DE78 mov [ebp+var_SectionObj], ecx
随后将局部变量赋值到创建的进程的SectionObject中
PAGE:004B465D loc_4B465D: PAGE:004B465D mov eax, [ebp+var_SectionObj] PAGE:004B4660 mov [ebx+138h], eax ; 赋值给创建的进程的SectionObject
接下来函数会判断是否有DebugPort句柄,有的话获取对象地址然后赋值到当前进程中
PAGE:004B4666 cmp [ebp+DebugPort], esi PAGE:004B4669 jnz loc_52DE1D 。。。 PAGE:0052DE1D loc_52DE1D: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+15B↑j PAGE:0052DE1D push esi ; HandleInformation PAGE:0052DE1E lea eax, [ebp+var_DebugPortObj] PAGE:0052DE21 push eax ; Object PAGE:0052DE22 push dword ptr [ebp+AccessMode] ; AccessMode PAGE:0052DE25 push _DbgkDebugObjectType ; ObjectType PAGE:0052DE2B push 2 ; DesiredAccess PAGE:0052DE2D push [ebp+DebugPort] ; Handle PAGE:0052DE30 call _ObReferenceObjectByHandle@24 ; PAGE:0052DE35 mov edi, eax PAGE:0052DE37 cmp edi, esi PAGE:0052DE39 jl loc_4B49BB PAGE:0052DE3F mov eax, [ebp+var_DebugPortObj] PAGE:0052DE42 mov [ebx+0BCh], eax ; 将DebugPortObj地址赋值给当前进程的DebugPort
接着在判断ExceptionPort句柄是否存在,存在的话依然是获取对象地址后赋值到当前进程中
PAGE:004B467A cmp [ebp+ExceptionPort], esi PAGE:004B467D jnz loc_52DEA2 。。。 PAGE:0052DEA2 loc_52DEA2: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+16F↑j PAGE:0052DEA2 push esi ; HandleInformation PAGE:0052DEA3 lea eax, [ebp+var_ExceptionPortObj] PAGE:0052DEA6 push eax ; Object PAGE:0052DEA7 push dword ptr [ebp+AccessMode] ; AccessMode PAGE:0052DEAA push _LpcPortObjectType ; ObjectType PAGE:0052DEB0 push esi ; DesiredAccess PAGE:0052DEB1 push [ebp+ExceptionPort] ; Handle PAGE:0052DEB4 call _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x) PAGE:0052DEB9 mov edi, eax PAGE:0052DEBB cmp edi, esi PAGE:0052DEBD jl loc_4B49BB PAGE:0052DEC3 mov eax, [ebp+var_ExceptionPortObj] PAGE:0052DEC6 mov [ebx+0C0h], eax ; 赋值到当前进程的ExceptionPort PAGE:0052DECC jmp loc_4B4683
随后初始化创建的进程的退出状态及调用函数来设置父进程的安全属性
PAGE:004B4683 loc_4B4683: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+799BE↓j PAGE:004B4683 mov dword ptr [ebx+24Ch], STATUS_PENDING ; 为当前进程的ExitStatus赋值 PAGE:004B468D push ebx PAGE:004B468E push [ebp+var_ParentProcObject] PAGE:004B4691 call _PspInitializeProcessSecurity@8 PAGE:004B4696 mov edi, eax PAGE:004B4698 cmp edi, esi PAGE:004B469A jl loc_4B49BB
接下来会判断是否具有父进程对象
PAGE:004B46A0 mov edi, [ebp+var_ParentProcObject] PAGE:004B46A3 cmp edi, esi PAGE:004B46A5 jz loc_4FA05C
如果没有的话,就会将当前进程的句柄表赋值给创建的进程并调用函数来完成进程地址空间的初始化
PAGE:004FA05C loc_4FA05C: ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+197↑j PAGE:004FA05C mov eax, [ebp+var_CurEProcess] PAGE:004FA05F mov eax, [eax+0C4h] ; 将当前进程的ObjectTable赋值给eax PAGE:004FA065 mov [ebx+0C4h], eax ; 将eax赋值给创建的进程的ObjectTable PAGE:004FA06B lea eax, [ebp+SpinLock] PAGE:004FA06E push eax ; SpinLock PAGE:004FA06F push ebx ; int PAGE:004FA070 call _MmInitializeHandBuiltProcess@8 ; MmInitializeHandBuiltProcess(x,x) PAGE:004FA075 jmp loc_4B46C0
如果有的话,就会直接通过函数为新进程创建地址空间,并构建页目录表,页表及物理页的关系
PAGE:004B46AB lea eax, [ebp+SpinLock] PAGE:004B46AE push eax ; SpinLock PAGE:004B46AF push ebx ; int PAGE:004B46B0 push [ebp+var_WorkingSetMinimum] ; int PAGE:004B46B3 call _MmCreateProcessAddressSpace@12 ; MmCreateProcessAddressSpace(x,x,x) PAGE:004B46B8 test al, al PAGE:004B46BA jz loc_52DF97
在对创建的进程成员赋值以后继续调用函数来初始化进程的优先级,页面映射表等等
PAGE:004B46C0 mov eax, 40000h PAGE:004B46C5 lea ecx, [ebx+248h] ; 将Flags的地址赋给ecx PAGE:004B46CB lock or [ecx], eax ; 对ecx地址中保存的内容进程异或操作 PAGE:004B46CE mov eax, [ebp+var_WorkingSetMaximum] PAGE:004B46D1 mov [ebx+214h], eax PAGE:004B46D7 xor eax, eax PAGE:004B46D9 mov al, [ebx+1A8h] ; 将DefaultHardErrorProcessing赋给al PAGE:004B46DF and eax, 0FFFFFF04h PAGE:004B46E4 push eax PAGE:004B46E5 lea eax, [ebp+SpinLock] PAGE:004B46E8 push eax PAGE:004B46E9 push [ebp+var_Affinity] PAGE:004B46EC push 8 PAGE:004B46EE push ebx PAGE:004B46EF call _KeInitializeProcess@20
调用函数来初始化创建的进程的句柄表,如果此时父进程被指定了,父进程的句柄表就会被拷贝到新进程中,句柄表中的每个对象的引用计数都加1
PAGE:004B4725 push ebx PAGE:004B4726 mov eax, [ebp+Flags] PAGE:004B4729 and al, 4 PAGE:004B472B neg al PAGE:004B472D sbb eax, eax PAGE:004B472F and eax, [ebp+var_ParentProcObject] PAGE:004B4732 push eax PAGE:004B4733 call _ObInitProcess@8
调用函数初始化新创建进程地址空间
PAGE:004B474E lea eax, [ebx+1F4h] PAGE:004B4754 push eax ; int PAGE:004B4755 push [ebp+var_SectionObj] ; int PAGE:004B4758 push esi ; int PAGE:004B4759 push ebx ; P PAGE:004B475A call _MmInitializeProcessAddressSpace@16
调用函数将进程对象的系统DLL(NTDLL.DLL)映射到创建的进程空间中
PAGE:004B476C push esi ; int PAGE:004B476D push ebx ; FastMutex PAGE:004B476E call _PspMapSystemDll@
调用函数获得新进程的会话ID以后在调用函数来设置令牌的会话ID
PAGE:004B4781 mov edi, [ebx+0C8h] ; 取出进创建进程的TOKEN PAGE:004B4787 and edi, 0FFFFFFF8h ; 低3位清0 PAGE:004B478A push ebx PAGE:004B478B call _MmGetSessionId@4 ; MmGetSessionId(x) PAGE:004B4790 push eax ; 新进程的会话ID PAGE:004B4791 push edi PAGE:004B4792 call _SeSetSessionIdToken@8
调用函数在全局句柄表PspCidTable中增加进程的句柄并获得进程PID,将返回值赋给新建进程
PAGE:004B4797 mov [ebp+var_NewEProcess], ebx PAGE:004B479A mov [ebp+var_6C], esi PAGE:004B479D lea eax, [ebp+var_NewEProcess] PAGE:004B47A0 push eax PAGE:004B47A1 push _PspCidTable PAGE:004B47A7 call _ExCreateHandle@8 PAGE:004B47AC mov [ebx+84h], eax ; 将返回值赋给新进程的UniqueProcessId PAGE:004B47B2 cmp eax, esi PAGE:004B47B4 jz loc_52DF97
对句柄表所指向对象中的UniqueProcessId进程赋值,随后调用函数设置访问状态
PAGE:004B47BA mov ecx, [ebx+0C4h] ; 将进程句柄表赋值给ecx PAGE:004B47C0 mov [ecx+8], eax ; 将返回值赋给句柄表对象中的UniqueProcessId PAGE:004B47C3 xor ecx, ecx PAGE:004B47C5 call @SeDetailedAuditingWithToken@4 ; SeDetailedAuditingWithToken(x) PAGE:004B47CA test al, al PAGE:004B47CC jnz loc_52DFA1
初始化局部变量为0以后,调用函数将新建对象的PEB赋值为0
PAGE:004B47F2 xor eax, eax PAGE:004B47F4 lea edi, [ebp+var_InitPeb] PAGE:004B47F7 stosd ; 将局部变量InitPeb进行初始化为0 PAGE:004B4805 lea eax, [ebx+1B0h] ; 取出新建进程的PEB地址赋给eax PAGE:004B480B push eax ; int PAGE:004B480C lea eax, [ebp+var_InitPeb] PAGE:004B480F push eax ; int PAGE:004B4810 push ebx ; 新建进程的EPROCESS PAGE:004B4811 call _MmCreatePeb@12
将新建进程的ActiveProcessLinks插入到全局链表PsActiveProcessHead尾部中
PAGE:004B4836 lea eax, [ebx+88h] ; 取出新建进程的ActiveProcessLinks地址赋给eax PAGE:004B483C mov ecx, _PsActiveProcessHeadPlus4 ; 取出全局变量_PsActiveProcessHead+4的地址中的内容赋给ecx PAGE:004B4842 mov [eax+LIST_ENTRY.Flink], offset _PsActiveProcessHead PAGE:004B4848 mov [eax+LIST_ENTRY.Blink], ecx PAGE:004B484B mov [ecx+LIST_ENTRY.Flink], eax
调用SetCreateAccessStateEx设置状态访问以后,调用ObInsertObject将创建的对象插入到句柄表中
PAGE:004B4881 mov ecx, _PsProcessType PAGE:004B4887 add ecx, 68h PAGE:004B488A push ecx ; GenericMapping PAGE:004B488B push [ebp+AccessMask] ; AccessMask PAGE:004B488E lea ecx, [ebp+var_B8] PAGE:004B4894 push ecx ; int PAGE:004B4895 lea ecx, [ebp+AccessState] PAGE:004B489B push ecx ; int PAGE:004B489C push eax ; int PAGE:004B489D push esi ; int PAGE:004B489E call _SeCreateAccessStateEx@24 PAGE:004B48AD lea eax, [ebp+ObjHandle] PAGE:004B48B0 push eax ; Handle PAGE:004B48B1 push esi ; NewObject PAGE:004B48B2 push 1 ; ObjectPointerBias PAGE:004B48B4 push [ebp+AccessMask] ; DesiredAccess PAGE:004B48B7 lea eax, [ebp+AccessState] PAGE:004B48BD push eax ; PACCESS_STATE PAGE:004B48BE push ebx ; Object PAGE:004B48BF call _ObInsertObject@2
最后设置了进程的访问权限和创建时间以后,就把新建进程的句柄赋值到输出参数ProcessHandle中,从而创建者可以获得新进程的句柄。
PAGE:004B49A6 mov eax, [ebp+ProcessHandle] PAGE:004B49A9 mov ecx, [ebp+ObjHandle] PAGE:004B49AC mov [eax], ecx
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界