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)
复制代码 |