A和W后缀代表ANSI和UNICODE版本。早期,Windows为了兼容之前ANSI版本,为了推广UNICODE版本,所以做出了两套API.
注意,并不是所有Windows API都有*A和*W两套,只有API参数包含"字符串"才可能有两个版本。
CreateProcess只是一个宏,在UNICODE版本是*W, ANSI是*A.
后者多了参数hToken, 可指定在此token对应的安全上下文,默认创建的进程是不可交互的,且不可接受输入。
UAC就会使用CreateProcessAsUser做权限管控。
kernel32.dll --> KernelBase.dll(自Vista之后新增) --> ntdll.dll
WinDbg attach到cmd.exe, cmd.exe里创建应用程序触发创建进程的断点。
XP系统是会Kernel32 CreateProcess(A/W)进入到ntdll NtCreateProcess.
Win 10变成kernel32放存根, KERNELBASE具体实现参数检查、环境创建并调入ntdll并进入系统调用,改用NtCreateUserProcess, 弃用了NtCreateProcess.
和裸调系统调用类似,syscall或者int 2Eh完成进入内核SSDT.
0:000> u ntdll!NtCreateUserProcess
ntdll!NtCreateUserProcess:
00007ffd`30b6e650 4c8bd1 mov r10,rcx
00007ffd`30b6e653 b8c8000000 mov eax,0C8h
00007ffd`30b6e658 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
00007ffd`30b6e660 7503 jne ntdll!NtCreateUserProcess+0x15 (00007ffd`30b6e665)
00007ffd`30b6e662 0f05 syscall
00007ffd`30b6e664 c3 ret
00007ffd`30b6e665 cd2e int 2Eh
00007ffd`30b6e667 c3 ret
它与NtCreateProcess差在系统服务号eax不同,其他都类似。
0:000> u ntdll!NtCreateProcess
ntdll!NtCreateProcess:
00007ffd`30b6e470 4c8bd1 mov r10,rcx
00007ffd`30b6e473 b8b9000000 mov eax,0B9h
00007ffd`30b6e478 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
00007ffd`30b6e480 7503 jne ntdll!NtCreateProcess+0x15 (00007ffd`30b6e485)
00007ffd`30b6e482 0f05 syscall
00007ffd`30b6e484 c3 ret
00007ffd`30b6e485 cd2e int 2Eh
00007ffd`30b6e487 c3 ret
注意看:
test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
根据SharedUserData + 0x308和1比较,如果是0,用int, 否则用syscall.
Windows NT+开始, 从2000到Win10都有此结构。目的为了让内核把频繁访问的信息,比如时间等,用全局一致的共享结构给用户空间,减少频繁系统调用带来的性能消耗。
:000> dt _KUSER_SHARED_DATA 00000000`7ffe0308
ntdll!_KUSER_SHARED_DATA+0x000 TickCountLowDeprecated : 0+0x004 TickCountMultiplier : 0+0x008 InterruptTime : _KSYSTEM_TIME+0x014 SystemTime : _KSYSTEM_TIME............+0x308 SystemCall : 0 // 此处是0+0x30c Reserved2 : 0+0x310 SystemCallPad : [2] 0
WRK _KUSER_SHARED_DATA结构体
首先CreateProcess根据UNICODE定义与否,选择是否调用CreateProcessW.
编译器直接插入了stub, 将调入转入到kernel32.
ReactOS: dll/win32/kernel32/client/proc.c File Reference + IDA win10 DLL
a. 验证参数合法
参数creationFlags同时有DETEACHED_PROCESS和NEW_CONSOLE是非法的。
进程优先级Idle/Nromal等状态从此参数获取,这里只列举部分参数,源代码里面有很多检查。
b. 搜索可执行文件,并加载
注意,DosPath to NtPath转换类似于:
C:\\WINDOWS\\exefile --> \\Device\\HarddiskVolume1\\WINDOWS\\exefile
内核不认识C盘字符:"C:\\", 它是方便用户查阅的符号链接。
c. 调用NtOpenFile打开可执行文件并创建section
创建进程映射空间,为加载做准备(参考: NtCreateSection function).
d. 确认Subsystem是GUI或CUI, 且MajorVersion和MinVersion符合预期
这是为什么VS设置错了subsystem(version)程序不能启动的原因。
e. 调用ntdll创建进程
Win10: NtCreateUserProcess
f. 创建主线程
注意,如果参数带CREATE_SUSPENED, 线程默认是不会执行的。
内核创建完进程和线程后,如何执行到真正的用户层线程代码?
参考:Windows内核--代码只有main函数, 进程怎么创建的? (2.1)
让内核处理过多的参数检查,和流程处理并不是很好的做法,反而加重了内核的负担。可以在用户层发现错误就返回,比进入内核检查到错误再返回更高效。