| 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         ;如果相等,说明创建标志不合法
 经过屏蔽标志位后,判断dwCreationFlags中是否包含CREATE_NEW_CONSOLE | DETACHED_PROCESS的组合, 如果包含它们的组合,参考MSDN上, 存在这种组合是不合法的, 因此跳转到错误处理中, 该错误处理例程中,将57h与teb-> LastErrorValue想比较,如果不相等,就更新LastErrorValue的值为57h, 实际上GetLastError() 函数的返回的错误码就是从teb-> LastErrorValue获取的。
 
 3.  在2中说到dwCreationFlags中也包含优先级类型的组合, 接着就判断优先级,判断的顺序依次是IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, HIGH_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS, 只要满足其中一个优先级,就跳过其他优先级的判断,如果都不满足, 将权限级置为0.
 
 (当满足IDLE_PRIORITY_CLASS时),置优先级标志为1.
 (当满足NORMAL_PRIORITY_CLASS时),置优先级标志为2.
 (当满足HIGH_PRIORITY_CLASS时),置优先级标志3.
 (当满足REALTIME_PRIORITY_CLASS时),由于该优先级别很高,比操作系统优先级还高(参考MSDN),作了如下操作, 申请堆空间 -->打开一个令牌对象与线程关联,并返回一个句柄可用于访问该令牌。-->调整优先级令牌。 置优先级标志4。
 
 
 复制代码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
4.  继续判断dwCreationFlag的情况,首先在dwCreationFlag过滤掉表示优先级的标志位,然后在判断是什么创建标志。
 
 复制代码
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
6. 判断lpEnvironment是否为空, 如果不为空,将Ansi字符串转换为UNICODE_STRING, 为空的话跳过这一步,接着调用RtlDosPathNameToNtPathName_U函数将DOS路径转换为NT路径,由于用户给定的路径一般都是DOS路径,而内核需要的是NT路径,因此需要转换一下。
 
 7.  接着调用NtOpenFile()得到文件句柄
 
 复制代码
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)
接着通过NtOpenFile得到的handle接着调用了NtCreateSectiond函数得到内存区对象句柄
 
 复制代码
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)
接着调用BasepIsProcessAllowed函数, 该函数用来判断应用程序名是否在授权文件列表中,函数实现调用了NtOpenKey函数打开了注册表中的[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\ CurrentVersion\Image File Execution Options键, 这个键跟以前流行的镜像劫持技术有关。
 
 在得到内存区对象句柄后调用了NtQuerySection函数,返回后得到节的基本信息(节基地址,大小,属性)
 
 复制代码
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 ; 将区对象作为镜像执行文件来查询信息
然后判断创建标志中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,如果包含该标志,判断PEB->ReadImageFileExecOptions域是否为0,如果不为0, 调用LdrQueryImageFileExecutionOptions函数查询该信息,如果不包含该标志,也用调用LdrQueryImageFileExecutionOptions函数。
 
 
 下面检查镜像文件的部分信息的有效性:
 
 复制代码
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
接着调用函数BasepIsImageVersionOk判断镜像文件版本是否合法
 
 复制代码
movzx eax, [ebp+pImageFileMinorVersion] ; 子系统次版本号
push  eax
movzx eax, [ebp+pImageFileMajorVersion] ; phSection->GpValue
push  eax                   ; 子系统的主版本号
call  _BasepIsImageVersionOk@8 ; 判断镜像文件的版本是否合法。
然后加载advapi32.dll并获得CreateProcessAsUserSecure函数的地址:
 
 复制代码
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     ; 获得指定模块中指定函数的地址
获得函数CreateProcessAsUserSecure地址,不过后面没有发现调用这个函数。
 
 然后调用BaseFormatObjectAttributes将安全属性结构格式为NT对象属性结构(得到了对象属性)。 接着调用了_DbgUiConnectToDbg在实现通过调用NtCreateDebugObject函数来创建调试对象,调用DbgUiGetThreadDebugObject来获得调试对象(作为参数传递到0环)。
 
 复制代码
call  _DbgUiConnectToDbg@0  ; 实现调用中NtCreateDebugObject
mov   [ebp+nReturnStatus], eax
cmp   eax, ebx
jl    loc_7C82DF58
call  _DbgUiGetThreadDebugObject@0 ; 获得线程调试对象teb->0xF24,
mov   [ebp+phandleOfDebugObj], eax
最后调用NtCreateProcessEx函数完成3环的创建过程
 
 复制代码
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)
 |