• Windows内核--CreateProcess到内核NtCreateProcess(2.3)


    CreateProcessA vs CreateProcessW

            A和W后缀代表ANSI和UNICODE版本。早期,Windows为了兼容之前ANSI版本,为了推广UNICODE版本,所以做出了两套API.

            注意,并不是所有Windows API都有*A和*W两套,只有API参数包含"字符串"才可能有两个版本。

            CreateProcess只是一个宏,在UNICODE版本是*W, ANSI是*A.

                    

    CreateProcessA vs CreateProcessAsUser

            后者多了参数hToken, 可指定在此token对应的安全上下文,默认创建的进程是不可交互的,且不可接受输入。

            UAC就会使用CreateProcessAsUser做权限管控。

    CreateProcess到NtCreateProcess经过哪些DLL?

            kernel32.dll --> KernelBase.dll(自Vista之后新增) --> ntdll.dll

    Callstack (环境: Win10)

            WinDbg attach到cmd.exe,  cmd.exe里创建应用程序触发创建进程的断点。

            

    XP系统是会Kernel32 CreateProcess(A/W)进入到ntdll NtCreateProcess.

    Win 10变成kernel32放存根, KERNELBASE具体实现参数检查、环境创建并调入ntdll并进入系统调用,改用NtCreateUserProcess, 弃用了NtCreateProcess.

    NtCreateUserProcess

            和裸调系统调用类似,syscall或者int 2Eh完成进入内核SSDT.

    1. 0:000> u ntdll!NtCreateUserProcess
    2. ntdll!NtCreateUserProcess:
    3. 00007ffd`30b6e650 4c8bd1 mov r10,rcx
    4. 00007ffd`30b6e653 b8c8000000 mov eax,0C8h
    5. 00007ffd`30b6e658 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
    6. 00007ffd`30b6e660 7503 jne ntdll!NtCreateUserProcess+0x15 (00007ffd`30b6e665)
    7. 00007ffd`30b6e662 0f05 syscall
    8. 00007ffd`30b6e664 c3 ret
    9. 00007ffd`30b6e665 cd2e int 2Eh
    10. 00007ffd`30b6e667 c3 ret

    它与NtCreateProcess差在系统服务号eax不同,其他都类似。

    1. 0:000> u ntdll!NtCreateProcess
    2. ntdll!NtCreateProcess:
    3. 00007ffd`30b6e470 4c8bd1 mov r10,rcx
    4. 00007ffd`30b6e473 b8b9000000 mov eax,0B9h
    5. 00007ffd`30b6e478 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
    6. 00007ffd`30b6e480 7503 jne ntdll!NtCreateProcess+0x15 (00007ffd`30b6e485)
    7. 00007ffd`30b6e482 0f05 syscall
    8. 00007ffd`30b6e484 c3 ret
    9. 00007ffd`30b6e485 cd2e int 2Eh
    10. 00007ffd`30b6e487 c3 ret

    注意看:

        test    byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1

        根据SharedUserData + 0x308和1比较,如果是0,用int, 否则用syscall.

      SharedUserData (_KUSER_SHARED_DATA)

            Windows NT+开始, 从2000到Win10都有此结构。目的为了让内核把频繁访问的信息,比如时间等,用全局一致的共享结构给用户空间,减少频繁系统调用带来的性能消耗。

    1. :000> dt _KUSER_SHARED_DATA 00000000`7ffe0308
    2. ntdll!_KUSER_SHARED_DATA
    3. +0x000 TickCountLowDeprecated : 0
    4. +0x004 TickCountMultiplier : 0
    5. +0x008 InterruptTime : _KSYSTEM_TIME
    6. +0x014 SystemTime : _KSYSTEM_TIME
    7. ......
    8. ......
    9. +0x308 SystemCall : 0 // 此处是0
    10. +0x30c Reserved2 : 0
    11. +0x310 SystemCallPad : [2] 0

      WRK _KUSER_SHARED_DATA结构体

      

    代码调用CreateProcess, 为什么能跑进kernel32 CreateProcessWStub?

            首先CreateProcess根据UNICODE定义与否,选择是否调用CreateProcessW.

            编译器直接插入了stub, 将调入转入到kernel32.

            

    回到CreateProcessInternalW

            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)     

      为什么用户层会有这么多参数检查和流程处理,不移到内核处理?

            让内核处理过多的参数检查,和流程处理并不是很好的做法,反而加重了内核的负担。可以在用户层发现错误就返回,比进入内核检查到错误再返回更高效。

  • 相关阅读:
    flask bootstrap页面json格式化
    寻找数组中最接近目标的数字
    网络安全(黑客)自学
    Docker部署Redis内存数据库
    本地部署大语言模型
    docker安装和优化
    Vue3 + Nodejs 实战 ,文件上传项目--实现图片上传
    14:00面试测试岗,14:06就出来了,问的问题有点变态。。。
    部署jar包到windows服务器,并自动执行启动脚本
    前端跨界面之间的通信解决方案
  • 原文地址:https://blog.csdn.net/cxsjabcabc/article/details/127874075