标 题: 【原创】CreateProcess流程分析。
作 者: pyq逍遥
时 间: 2010-06-03,16:26:42
链 接: http://bbs.pediy.com/showthread.php?p=819417#post819417
工具: IDA5.2, WinDbg, Ollydbg
平台: Windows XP xp3
这个项目是本人在科锐和同学一起完成的项目,各有分工,我的任务是分析CreateProcess从用户态到内核态的整体流程框架(部分函数的具体实现由其他同学的完成)。
声明:由于本人能力有限,文中必然有错漏之处,恳请读者不吝赐教.(第一次写文章)
这里为了方便大家查阅,给出其他同学分析的链接:
CreateProcess函数的整体流程分析:http://bbs.pediy.com/showthread.php?p=819417#post819417
NtCreateProcessEx的分析:http://bbs.pediy.com/showthread.php?t=114958
MmInitializeProcessAddressSpace的分析:http://bbs.pediy.com/showthread.php?t=114956
内核创建进程之创建节区分析:http://bbs.pediy.com/showthread.php?t=114943
线程创建流程分析:http://bbs.pediy.com/showthread.php?t=114929
异常分发:http://bbs.pediy.com/showthread.php?t=114945
进程句柄表分析:http://bbs.pediy.com/showthread.php?t=114820
ObCreateObject函数的分析:http://bbs.pediy.com/showthread.php?t=114730
ObInsertObject函数的分析:http://bbs.pediy.com/showthread.php?t=114734
以下是我的分析:
目录:
一:创建进程的大体流程。
二:3环进程的创建分析。
三:0环创建进程的分析。
四:3环创建线程的分析。
五:0环创建线程的分析。
一. 创建进程的大体流程:
创建进程的过程就是构建一个环境,这个环境包含了很多的机制 (比如自我保护, 与外界通信等等)。 构建这个环境需要两种“人”来协调完成(用户态和内核态),他们各有分工,其中用户态提供原料(提供创建的那些参数), 内核态负责来构建这个环境,由于环境是由内核态构建的,因此他持有这个环境的控制权, 而用户由于提供了原料, 因此他具有使用权。 内核态开始构建环境中的基础设施(进程对象,等等),在构建完基础设施后,内核态通知用户态基础设施构建已经完成,是否需要继续构建其他设施,于是用户态通知内核态继续构建一条通道(既创建线程),方便两边的通信,当用户态接收到线程创建完毕的信息后,便可以开始使用这个环境(投入生产),以后缺啥补啥。
二. 3环进程的创建分析:
CreateProcesssA的分析:
在CreateProcessA函数中调用了CreateProcessInternalA,调用该函数时,增加了两个参数(一头一尾加了个零参数),在该函数里面也没看到对这两个参数的引用,而是直接传递给CreateProcessInternalW函数。
在CreateProcessInternalA中,首先检查了参数 lpCmdLine, 判断是否为0,不为0则将lpCmdLine初始化为UNICODE字符串, 为0则做了几个局部变量赋值后跳过初始化为UNICODE字符串。接着继续检查参数,先将lpStartupInfor的内容拷贝到一个局部变量, 然后判断参数lpApplicationName是否为0,不为0则参数转换为UNICODE字符串,为0则跳过转换继续判断参数lpCurrentDirectory是否为0,这个和参数lpAppLicationName的判断逻辑是一样的。接着判断STARTUPINFOA.lpReserved域是否为0(MSDN上说在调用CreateProcess前,要把lpReserved设置为NULL ,不过我试了下,不为0也没问题。),该域不为0则和其他字符串参数一样做UNICODE的转换,后面还判断STARTUPINFOA中的几个字符串的域,不为0的都作了下转换。最后调用了CreateProcesssW函数, 看上面的步骤大家应该知道了其实CreateProcessInternalA函数只是对字符串参数或者结构体中包含字符串类型的域的作了检查和转换工作,然后就调用了下层函数。(附图)
分析CreateProcesssW的大概流程:
1. 将参数 保存到局部变量中。
2. dwCreationFlags 的值至少由一个标志组合成(一些创建标志和优先级类型),首先屏蔽 CREATE_NO_WINDOW 标志,代码如下:
mov eax, [ebp+20h] ; ebp+20h = dwCreationFlags
and eax, 0F7FFFFFFh ; 屏蔽CREATE_NO_WINDOW标志。
mov [ebp+20h], eax
mov ecx, eax
and ecx, 18h ;18h = DETACHED_PROCESS | CREATE_NEW_CONSOLE
cmp cl, 18h
jz loc_7C8427DE ;如果相等,说明创建标志不合法
mov eax, [ebp+20h] ; ebp+20h = dwCreationFlags
and eax, 0F7FFFFFFh ; 屏蔽CREATE_NO_WINDOW标志
mov [ebp+20h], eax
mov ecx, eax
and ecx, 18h
cmp cl, 18h ; 判断dwCreationFlags 是否为CREATE_NEW_CONSOLE |DETACHED_PROCESS
jz loc_7C8427DE ; 标志组合不合法, 进行错误处理并退出
mov [ebp+var_6D4], ebx
mov [ebp+var_6DC], ebx
test al, 40h
jnz IsIdlePriority ; 优先级为IDLE_PRIORITY_CLASS
test ah, 40h
jnz loc_7C8427F6
test al, 20h
jnz IsNormalPriority ; 优先级为NORMAL_PRIORITY_CLASS
test ah, ah
js loc_7C842802
test al, al
js IsHighPriotity ; 优先级为HIGH_PRIORITY_CLASS
test ah, 1
jnz IsRealTimePriority ; 优先级为REALTIME_PRIORITY_CLASS
mov [ebp+var_668], bl
and word ptr [ebp+dwCreateFlag], 3E1Fh ; 屏蔽权限级表示的位
mov edi, 800h
mov esi, 1000h
test [ebp+dwCreateFlag], edi
jnz loc_7C842832 ; dwCreationFlag = CREATE_SEPARATE_WOW_VDM
test [ebp+dwCreateFlag], esi
jnz short loc_7C819A33 ; dwCreationFlag = CREATE_SHARED_WOW_VDM
mov eax, _BaseStaticServerData
cmp [eax+19F4h], bl
jnz loc_7C84283C
push 60h ;打开选项
push 5 ;共享模式
lea eax, [ebp+pIoStatusBlock]
push eax ; I/0状态块
lea eax, [ebp+pObjAttribute]
push eax ; 对象属性
push 1000A1h
;SYNCHRONIZE |FILE_ATTRIBUTE_NORMAL|
;FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY
lea eax, [ebp+pHandleOfFile]
push eax ; 文件句柄
mov esi, ds:__imp__NtOpenFile@24 ; NtOpenFile(x,x,x,x,x,x)
call esi ; NtOpenFile(x,x,x,x,x,x) ; NtOpenFile(x,x,x,x,x,x)
push [ebp+pHandleOfFile] ; 文件句柄
push 1000000h
push 10h ; 内存区页面保护属性
push ebx ; SETION大小
push ebx ; 对象属性
push 0F001Fh ; 访问掩码
lea eax, [ebp+pSectionHandle]
push eax ; 指向内存区对象的指针(传出参数)
call ds:__imp__NtCreateSection@28 ; NtCreateSection(x,x,x,x,x,x,x)
push ebx ; 接受返回的大小
push 30h ; 内存区信息的长度
lea eax, [ebp+OepAddress]
push eax ; 接受内存区信息Buffer
xor edi, edi
inc edi
push edi ; edi = 1 = SectionImageInformation
push [ebp+pSectionHandle] ; 内存区句柄
call ds:__imp__NtQuerySection@20 ; 将区对象作为镜像执行文件来查询信息
mov ax, [ebp+MachineType] ; MachineType为机器类型,
cmp ax, ds:7FFE002Ch ; ds:7FFE002Ch中的内容为0x014c
jb loc_7C84329E
cmp ax, ds:7FFE002Eh
ja loc_7C84329E
cmp dword ptr [ebp+ SubSystemVersion], 2
jz short loc_7C8191C4 ; 子系统次版本号
cmp dword ptr [ebp+ SubSystemVersion], 3 ; 子系统的比较版本(PE信息中包含)
jnz loc_7C842D0C
movzx eax, [ebp+pImageFileMinorVersion] ; 子系统次版本号
push eax
movzx eax, [ebp+pImageFileMajorVersion] ; phSection->GpValue
push eax ; 子系统的主版本号
call _BasepIsImageVersionOk@8 ; 判断镜像文件的版本是否合法。
push offset LibFileName ; "advapi32.dll"
call _LoadLibraryA@4 ; LoadLibraryA(x)
mov esi, eax
mov [ebp+phModuleOfAdvapi32.dll], esi ; 保存模块基地址
cmp esi, ebx
jz short loc_7C81923C
push offset ProcName ; "CreateProcessAsUserSecure"
push esi ; hModule
call _GetProcAddress@8 ; 获得指定模块中指定函数的地址
call _DbgUiConnectToDbg@0 ; 实现调用中NtCreateDebugObject
mov [ebp+nReturnStatus], eax
cmp eax, ebx
jl loc_7C82DF58
call _DbgUiGetThreadDebugObject@0 ; 获得线程调试对象teb->0xF24,
mov [ebp+phandleOfDebugObj], eax
push [ebp+JobLevel] ; 作业级别
push ebx ; 异常端口对象句柄
push [ebp+phandleOfDebugObj] ; 调试对象句柄
push [ebp+pSectionHandle] ; 内存区对象句柄
push [ebp+CreateFlag] ; 创建标志
or esi, 0FFFFFFFFh
push esi ; 父进程句柄(FFFFFFFF)
push [ebp+ObjAttibute] ; 对象属性
push 1F0FFFh ; (访问掩码)
lea eax, [ebp+pHandleOfProcess]
push eax ; 保留进程句柄值(传出参数)
call ds:__imp__NtCreateProcessEx@36 ; 创建进程(x,x,x,x,x,x,x,x,x)
mov eax, large fs:124h ; eax保存了指向KTHREAD结构的指针
mov [ebp+pKthread], eax
mov cl, [eax+_KTHREAD.PreviousMode] ; 线程的前一个模式
mov [ebp+PreviousMode], cl
push esi ; HandleInformation
lea eax, [ebp+pParentObject] ; eax 为EPROCESS局部变量的地址(作传出参数)
push eax ; Object
push dword ptr [ebp+PreviousMode] ; AccessMode
push _PsProcessType ; ObjectType
push 80h ; DesiredAccess
push [ebp+ParentProcessHandle] ; Handle
call _ObReferenceObjectByHandle@24 ; 通过句柄得到父进程对象指针
lea eax, [ebp+pNewProcessObj]
push eax ; 新对象指针
push esi ; int
push esi ; 对象的大小
push 260h ; paseContext
push esi ; int
push dword ptr [ebp+PreviousMode] ; int
push [ebp+ObjectAttributes] ; 对象属性
push _PsProcessType ; 对象类型
push dword ptr [ebp+PreviousMode] ; 处理器模式
call _ObCreateObject@36 ; 创建进程对象
mov edi, eax
cmp edi, esi
jl loc_4AAB6B ; 判断ObCreateObject函数调用是否成功
mov ecx, 98h
xor eax, eax
mov ebx, [ebp+pNewProcessObj]
mov edi, ebx
rep stosd ; 将新建对象初始化为0
cmp [ebp+SectionHandle], esi
jz loc_4F449F ; 判断区对象句柄是否为0
push esi ; HandleInformation
eax, [ebp+pSectionObj]
push eax ; 内存区对象指针
push dword ptr [ebp+PreviousMode] ; 处理器模式
push _MmSectionObjectType ; 对象类型
push 8 ; 访问掩码
push [ebp+SectionHandle] ; 内存去句柄
call _ObReferenceObjectByHandle@24 ; 通过区对象句柄得到区对象指针
mov eax, [ebp+HandleInformation]
mov [ebx+_EPROCESS.SectionObject], eax ; pNewProcessObject->SectionObject = 内存区对象指针
cmp [ebp+DebugPort], esi
jnz IsDebugger ; 判断参数DebugPort是否为0
IsDebugger:(DebugPort不为0时)
push esi ; HandleInformation
lea eax, [ebp+pDebugObj]
push eax ; 新调试对象指针
push dword ptr [ebp+PreviousMode] ; 处理器模式
push _DbgkDebugObjectType ; 对象类型
push 2 ; 访问模式
push [ebp+DebugPort] ; 调试端口句柄
call _ObReferenceObjectByHandle@24 ; 通过调试对象句柄得到调试对象指针
push ebx ; 新进程对象
push [ebp+ParentObj] ; 父进程对象
call _PspInitializeProcessSecurity@8 ; 初始进程的安全属性(设置新进程对象的令牌对象)
push 1 ; TOKEN是否为活动
lea eax, [ebp+8]
push eax ; eax指向父进程对象的指针
push edi ; 父进程对象中的令牌对象
call _SeSubProcessToken@12 ; 将父进程的令牌对象赋值给新进程对象
lea eax, [ebp+pDirTableBase]
push eax ; 页目录表基地指针
push ebx ; 新进程对象
push [ebp+MinimumWorkingSet] ; 最小工作集
call _MmCreateProcessAddressSpace@12 ; 创建进程地址空间(并构建页目录表,页表 及物理页的关系)
push eax
lea eax, [ebp+pDirTableBase]
push eax ; 页目录表基地址
push [ebp+Affinity] ; 亲和性
push 8 ; 进程基本优先级
push ebx ; 新进程对象
call _KeInitializeProcess@20 ;
push ebx ; 新进程对象
mov eax, [ebp+CreateFlag]
and al, 4
neg al
sbb eax, eax
and eax, [ebp+ParentObj]
push eax ; 父进程对象
call _ObInitProcess@8 ; 初始化新进程对象的对象表(如果父进程被指定,父进程的对象表拷贝到新进程中, 对象表 的每个对象中HandleCount域都+1)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!