*[标题]:CreateProcess进程创建的内核跟踪分析
*[作者]:gz1X [gz1x(at)tom(dot)com]
*[来自]:中国黑客联盟 [CHU]
*[正文]:
[实现流程]
——————————————————————
1.打开需要执行的文件(.exe);
2.创建执行体进程对象; //<-------重点
3.创建初始线程; //<-------重点
4.通知WIN32子系统设置新进程和线程;
5.启动执行体开始执行;
6.完成地址空间的初始化,开始执行程序。
[阶段一]
——————————————————————
当我们调用CreateProcess函数时,需要指定一个文件映象,一般是.exe文件。
CreateProcess第一件事就是打开这个文件,为它创建一个文件映射对象。但是并没有真正映射到内存中。
这里需要注意的是,能创建文件映射对象并不代表文件是有效的win32文件,dll文件也能被成功打开。
显然,如果是dll,CreateProcess将失败。
现在我们来验证,反汇编kernel32.dll,可以看到:
.text:7C802367 CreateProcessA proc near ; CODE XREF: LoadModule+126p
//..此处省略
.text:7C80238C push 0
.text:7C80238E call CreateProcessInternalA
.text:7C802393 pop ebp
.text:7C802394 retn 28h
.text:7C802394 CreateProcessA endp
再接着看CreateProcessInternalA函数,跟进去可以看到这里:
.text:7C81DEC2 call CreateProcessInternalW
在往里跟,进CreateProcessInternalW的领空,走过:
.text:7C8197A8 call ds:NtQueryInformationJobObject
到这里:
.text:7C8197D9 cmp [eax], bl
.text:7C8197DB jz loc_7C818CE6
接下来就是关键代码了,挑重点的函数调用列举出来:
.text:7C818D2C call ds:NtAllocateVirtualMemory //设置内存映象空间;
.text:7C818E6B call ds:RtlDosPathNameToNtPathName_U //将DOS文件名转换成NT文件名;
.text:7C818F3A call esi ; NtOpenFile //打开.exe文件;
.text:7C818F71 call ds:NtCreateSection //建立文件映射对象;
.text:7C819098 call ds:NtQuerySection //取得文件映射对象的相关信息;
.text:7C8190E4 call LdrQueryImageFileExecutionOptions
检查是否是一个POSIX文件,是则运行POSIX.EXE;
.text:7C819238 call ds:NtCreateProcessEx
调用系统函数创建进程执行体。
大致上从函数名再结合上面给出的解释,很容易能猜到各个函数实现的功能,由于各个函数都是未公开的,详细函数声明您可以到:
http://undocumented.ntinternals.net/
[阶段二] -0
——————————————————————
为了方便说明,我们先通过Windbg把当前进程的EPROCESS块结构了解清楚:
0:000> 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 //<-------win32子系统进程块
+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
[阶段二] -1
——————————————————————
第二步创建执行体进程对象,大致上包括以下几个方面:
1.设置EPROCESS块;
2.创建初始进程空间地址;
3.创建内核进程块;
4.决定进程地址空间的设置;
5.设置PEB;
6.完成执行体进程对象的设置。
这几项大都是内核实现的,详细的过程可以参考书目[Inside Windows NT],这里只把一些关键的东西挑出来。
那么我们反汇编ntoskrnl.exe,找到:
PAGE:004AAE82 __stdcall NtCreateProcessEx(x, x, x, x, x, x, x, x, x) proc near
PAGE:004AAE82 ; CODE XREF: NtCreateProcess(x,x,x,x,x,x,x,x)+3Dp
PAGE:004AAE82 ; DATA XREF: .text:0040B768o
//...
PAGE:004AAEDF call PspCreateProcess(x,x,x,x,x,x,x,x,x)
//...
PAGE:004AAEE9 retn 24h
PAGE:004AAEE9 __stdcall NtCreateProcessEx(x, x, x, x, x, x, x, x, x) endp
好,下面就是重头戏了。跟进PspCreateProcess:
PAGE:004AB2CB push esi ; HandleInformation
PAGE:004AB2CC lea eax, [ebp+Object]
PAGE:004AB2CF push eax ; Object
PAGE:004AB2D0 push dword ptr [ebp+PreviousMode] ; AccessMode
PAGE:004AB2D3 push _PsProcessType ; ObjectType
PAGE:004AB2D9 push 80h ; DesiredAccess
PAGE:004AB2DE push [ebp+Handle] ; Handle
PAGE:004AB2E1 call ObReferenceObjectByHandle(x,x,x,x,x,x)
这次调用ObReferenceObjectByHandle作用主要是设置父进程,为后面新进程的创建做好准备。
PAGE:004AB300 mov [ebp+var_64], eax
PAGE:004AB303 mov eax, ds:_PsMinimumWorkingSet
PAGE:004AB308 mov [ebp+var_60], eax
PAGE:004AB30B mov eax, ds:_PsMaximumWorkingSet
这几句就是在设置进程的最小和最大工作集了。
PAGE:004AB32E call ObCreateObject(x,x,x,x,x,x,x,x,x)
为对象表头和对象体分配内存,并初始化对象参数。
PAGE:004AB363 call PspInheritQuota(x,x)
PAGE:004AB36C call ObInheritDeviceMap(x,x)
这句设定进程配额块和DosDevices,将新进程的配额块移动到父进程配额块地址处;
在此之后,将执行以下操作:
Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;
Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
Process->SessionId = Parent->SessionId;
PAGE:004AB3B3 call ObReferenceObjectByHandle(x,x,x,x,x,x)
创建页表项以映射初始页面;
PAGE:004AB3E4 push edi
PAGE:004AB3E5 push ebx
PAGE:004AB3E6 call DbgkCopyProcessDebugPort(x,x)
设置调试端口,如果有必要,也会设置异常端口;
PAGE:004AB402 call PspInitializeProcessSecurity(x,x)
设置访问令牌,新的进程将继承父进程的配置;
PAGE:004AB424 call MmCreateProcessAddressSpace(x,x,x)
创建初始进程地址空间;
PAGE:004AB460 call KeInitializeProcess(x,x,x,x,x)
PAGE:004AB465 mov al, _PspForegroundQuantum
创建内核进程块,,对KPROCESS块进行设置,_PspForegroundQuantum中包含时间片的初始值;
PAGE:004AB4A4 call ObInitProcess(x,x)
进程地址空间一旦建立,接下来就是attach上去,不过在此之前,先需要对KPROCESS进行初始化;
PAGE:004AB4CB call MmInitializeProcessAddressSpace(x,x,x,x)
初始化地址空间,有四种模式,包括:
Boot Process,System Process,Cloned User Process ,New User Process;
PAGE:004AB4DF call PspMapSystemDll(x,x)
支持重定向,有必要的话将ntdll.dll、语言支持表映射到进程中;
PAGE:004AB512 push _PspCidTable
PAGE:004AB518 call ExCreateHandle(x,x)
为进程分配一个CID标识,在句柄表中设置CID;
PAGE:004AB582 call MmCreatePeb(x,x,x)
创建PEB,为PEB分配一个页面,并初始化;
它将继续调用_MiCreatePebOrTeb,详细信息可以反汇编__stdcall MmCreatePeb(x, x, x)。
PAGE:004AB5A2 call ExAcquireFastMutexUnsafe(x)
PAGE:004AB5B3 mov dword ptr [eax], offset _PsActiveProcessHead
PAGE:004AB5BE mov dword_4896DC, eax
PAGE:004AB5C3 mov ecx, offset _PspActiveProcessMutex
PAGE:004AB5C8 call ExReleaseFastMutexUnsafe(x)
把新进程块插入到活动进程表的末端(_PsActiveProcessHead);
PAGE:004AB60F call SeCreateAccessStateEx(x,x,x,x,x,x)
创建AccessState结构体;
PAGE:004AB630 call ObInsertObject(x,x,x,x,x,x)
将新进程的句柄返回给调用函数CreateProcess,然后返回到用户态继续;
再接下去是检查控制权限安全性,然后取得进程创建时间,不再多做讨论。
[阶段三]
——————————————————————
我们返回到用户态,kernel32.dll里接着走:
.text:7C82C968 call ds:NtSetInformationProcess
设置对象属性,枚举值_PROCESSINFOCLASS描述了信息类;
.text:7C819BB0 call ds:NtQueryInformationProcess
这里取得ProcessBasicInfo的信息,参考ntddk.h:
typedef struct _PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PPEB PebBaseAddress;
KAFFINITY AffinityMask;
KPRIORITY BasePriority;
ULONG UniqueProcessId;
ULONG InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
值PPEB PebBaseAddress是关键,指向PEB结构的地址。
之后将执行:Peb = ProcessInfo.PebBaseAddress;获取了之后,将开始调整堆栈;
先不管堆栈怎么建立,继续...
在NtCreateThread之前,.text:7C819D2B loc_7C819D2B:
.text:7C819D3F call sub_7C81021C
跟进去,可以看到:
.text:7C81024B call ds:RtlImageNtHeader
.text:7C8102DF mov ebx, ds:NtAllocateVirtualMemory
//...
.text:7C810356 call ebx ; NtAllocateVirtualMemory
.text:7C810385 call ds:NtProtectVirtualMemory
好象在对某些值进行设置调整,并试图在进程产生的地址空间中设置内存;
记住这里,我们将在不久之后讨论这个“好象在对某些值进行设置调整,并试图...”;
==========================================================================
那么是什么值呢?堆栈又怎么调整?
我们不妨从核心态来了解相关的信息,之后我们再从用户态还原出这些函数:
进入ntoskrnl.exe,我们看NTSTATUS __stdcall RtlCreateUserProcess:
INIT:005CABC6 __stdcall RtlCreateUserProcess(x, x, x, x, x, x, x, x, x, x) proc near
//...
INIT:005CABF4 call RtlpOpenImageFile(x,x,x,x)
同样是打开文件映象;
INIT:005CAC18 call ZwCreateSection(x,x,x,x,x,x,x)
INIT:005CAC50 call RtlGetNtGlobalFlags()
INIT:005CAC87 call ZwCreateProcess(x,x,x,x,x,x,x,x)
和ring3状态下基本类似,创建进程;
INIT:005CACA0 call ZwQuerySection(x,x,x,x,x)
INIT:005CACBA call ZwQueryInformationProcess(x,x,x,x,x)
正如我们这里的第三步,为创建线程做好准备;
INIT:005CAD8E call ZwAllocateVirtualMemory(x,x,x,x,x,x)
INIT:005CADAC call ZwWriteVirtualMemory(x,x,x,x,x)
INIT:005CAE42 call RtlCreateUserThread(x,x,x,x,x,x,x,x,x,x)
分配空间,将参数压入,到此为止,线程创建完毕;
现在我们就跟进RtlCreateUserThread,看看到底做了什么:
PAGE:004EA78C __stdcall RtlCreateUserThread(x, x, x, x, x, x, x, x, x, x) proc near
//...
PAGE:004EA7DE call RtlpCreateStack(x,x,x,x,x)
PAGE:004EA804 call RtlInitializeContext(x,x,x,x,x)
PAGE:004EA863 call ZwCreateThread(x,x,x,x,x,x,x,x)
原来是创建了堆栈,并初始化线程上下文,然后调用ZwCreateThread正式创建线程;
===========================================================================
再返回到用户态,接着上面的NtQueryInformationProcess往下看:
.text:7C819BF5 call ds:RtlAllocateHeap
.text:7C819C1F call GetFullPathNameW
.text:7C819C33 call GetFileAttributesW
将参数压入新进程地址空间,检查路径,确认路径存在;
.text:7C819CE7 call sub_7C81A2CE
.text:7C819CFB call ds:RtlFreeUnicodeString
这里是个重点,在RtlFreeUnicodeString前调用了一个函数,是什么?我们跟进去...
当我们试图跟踪的时候,发现了什么?一大堆的调用类似:
mov esi, ds:RtlInitUnicodeString
call esi ; RtlInitUnicodeString
但是幸运的是我们发现了这个函数调用:
.text:7C81A454 call ds:RtlCreateProcessParameters
这个函数的作用是将进程的环境信息放进RTL_USER_PROCESS_PARAMETERS缓冲区中;
并在之后看到了内存操作函数:
.text:7C81A4EF call ds:NtAllocateVirtualMemory
.text:7C81A516 mov esi, ds:NtWriteVirtualMemory
.text:7C81A51C call esi ; NtWriteVirtualMemory
它们将设置诸如环境块(EnvironmentBlock)、进程信息块(ProcessParameterBlock)、PEB等;
OK,这些是什么?组合起来,这就是著名的_BasePushProcessParameters,多么美妙!
它的作用是将诸如exe文件路径、命令行参数、路径、窗口信息等压栈,并调整由进程产生的地址空间;
好,我们回到前面,不是有个莫名其妙的“好象在对某些值进行设置调整,并试图...”么?
得到了_BasePushProcessParameters,就不难知道,它就是同样著名的_BaseCreateStack,哈!!
跳回到.text:7C819D2B loc_7C819D2B,也就是_BaseCreateStack处,往下看:
.text:7C819D6C call sub_7C8103A3
跟进去,是一些值的设定,但是同样幸运的是,我们看到了这个:
.text:7C8103F4 mov dword ptr [eax+0C0h], 3000h
想起些什么了么?对,就是 Context->EFlags = 0x3000;
那么很容易推断,这个call就是_BaseInitializeContext;
好了,对应于核心态,所有的用户态堆栈函数全部还原出来了,我们整理下思路:
NtQueryInformationProcess之后进行堆栈操作,先后执行了
_BasePushProcessParameters、_BaseCreateStack、_BaseInitializeContext;
万事具备,接下来就只差线程的创建了:
.text:7C819DCD call ds:NtCreateThread
而实际上,我们查看函数原型:
NTSYSAPI
NTSTATUS
NTAPI
NtCreateThread(
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle,
OUT PCLIENT_ID ClientID,
IN PCONTEXT Context, /* see _BaseInitializeContext */
IN StackInformation* StackInfo, /* see _BaseCreateStack */
IN BOOLEAN CreateSuspended /* ==1 */
);
相应的参数结构就是由还原出的堆栈函数进行填充的;
不过目前这个线程还是挂起的,当所有的设定完成后,我们将用NtResumeThread让它工作。
[阶段四]
[阶段五]
——————————————————————
引用Gloomy在《研究CreateProcess》中最后的“陈词”:
“终于,在对PE映象的SubSystem主要域的数据进行处理之后,通过LPC转到Win32服务。进程应该只在Win32子系统下创建。”
在所有必要的执行体进程和线程对象创建以后,kernerl32.dll发送信息给Win32子系统,设置新的进程和线程。
接着call ds:NtCreateThread,继续我们的旅程:
.text:7C819EA2 push 98h
.text:7C819EA7 push 10000h
.text:7C819EAC push dword ptr [ebp-690h]
.text:7C819EB2 lea eax, [ebp-398h]
.text:7C819EB8 push eax
.text:7C819EB9 call ds:CsrClientCallServer
当然,在此之前有必要检查和设置一些标志,否则NtTerminateProcess;
我们来看需要传递哪些信息:
1.进程和线程句柄 2.创建标志中的项 3.进程创建者的ID 4.是否属于Win32程序
然后调用ds:CsrClientCallServer发送给Win32子系统,向CSRSS注册新的进程和线程;
子系统要完成的操作就不再废话,参考资料5。
.text:7C819EF5 lea eax, [ebp-6ACh]
.text:7C819EFB push eax
.text:7C819EFC push dword ptr [ebp-67Ch]
.text:7C819F02 call ds:NtResumeThread
完成了所有的进程环境设置,分配了线程资源,调用NtResumeThread开始初始线程的执行;
[阶段六]
——————————————————————
NtCreateThread()在MmCreateTeb()之后KeInitThread(),然后KeStartThread()等等。
KeInitThread()启动后将初始化线程上下文,标记Thread->State = Initialized,然后:
PspUserThreadStartup(),它将调用KiInitializeUserApc()为线程设置用户态的APC。
而APC的初始化需要LdrInitializeThunk(),我们从它入手,完成进程和线程的最后的设置。
.text:7C941616 ; START OF FUNCTION CHUNK FOR LdrInitializeThunk
//...
检查LdrpProcessInitialized是否为0,是则调用LdrpInitializeProcess();
如果已经初始化完毕,则调用LdrpInitializeThread();
.text:7C941634 call LdrpInitializeProcess //SUB_7C941ABC
好,我们跟进去,看到:
.text:7C941ABC LdrpInitializeProcess proc near ; CODE XREF: LdrInitializeThunk+204B6p
.text:7C941B44 call RtlImageNtHeader
检查ProcessParameters中的文件映射名;
.text:7C941B8D call RtlNormalizeProcessParams
在注册表中搜索ImageFileExecutionOptions,如下:
ProcessParameters= RtlNormalizeProcessParams(Peb->ProcessParameters)
.text:7C941BD1 call RtlInitNlsTables
.text:7C941BDA call RtlResetRtlTranslations
初始化TLS和FLS的进程数据结构;
text:7C941BE9 call RtlImageDirectoryEntryToData
将进程信息关联到映象文件;
//...
.text:7C941D99 call RtlInitializeCriticalSection
设置临界区;
.text:7C941E44 call RtlCreateHeap
两次调用RtlCreateHeap,设置为:
ProcessHeap= RtlCreateHeap()
LdrpHeap= RtlCreateHeap()
.text:7C941E74 call RtlInitializeAtomPackage
//...
之后将设置dll搜索路径,获取路径,初始化装载的模块;
然后调用LdrpWalkImportDescriptor()遍历IDT,并装载关联的DLL;LdrpInitializeTls()之后所有模块装载完毕;
此时检查PEB.BeingDebugged是否被置位,是则调用DbgBreakPoint()通知调试器。
最后调用LdrpRunInitializeRoutines()启动例程初始化所有的DLL。
接着转到线程初始化,完成收尾:
LdrpInitializeThread()中调用LdrpCallInitRoutine(DLL_THREAD_ATTACH)唤醒DLL初始化例程;
如果存在TLS,则调用LdrpCallTlsInitializers(DLL_THREAD_ATTACH)进行初始化。
到此,所有的工作都完成了。由于设置了陷阱框架,当线程在核心态运行的陷阱被取消,映象将回到用户态开始执行。
*[参考资料]:
——————————————————————
1. 《反汇编CreateProcess》 董岩,Gloomy
2. Windows NT Source Code
3. Kernel32.dll, Ntdll.dll, ntoskrnl.exe
4. http://undocumented.ntinternals.net/
5. 《Inside Windows NT》 David A.Solomon
CreateProcess进程创建的内核跟踪分析
最新推荐文章于 2023-09-13 13:45:04 发布