整理CreateProcessW

前言:

经过好几次对CreateProcess的学习,之前总是学到一半就放弃,学一半就放弃,这次总算坚持了下来 -、-。

这次学习主要参考三个资料:

1.Windows2000源代码

2.毛德操老师的《Windows内核情景分析》

3.一个大佬的博客:https://bbs.pediy.com/thread-114611.htm

三个资料讲的大体相同,但是有些许差别,有不同我主要都是按照源代码来解释了。(大佬们可以自己在Win10上写一个CreateProcess,然后反汇编调试一发。。。。弱鸡就飘过了。。。)

 

 

正文:

WIN32API函数CreateProcess用来创建一个新的进程和它的主线程,这个新进程运行指定的可执行文件。      

创建进程的过程就是构建一个环境,这个环境包含了很多的机制 (比如自我保护, 与外界通信等等)。 构建这个环境需要两种“人”来协调完成(用户态和内核态),他们各有分工,其中用户态提供原料(提供创建的那些参数), 内核态负责来构建这个环境,由于环境是由内核态构建的,因此他持有这个环境的控制权, 而用户由于提供了原料, 因此他具有使用权。 内核态开始构建环境中的基础设施(进程对象,等等),在构建完基础设施后,内核态通知用户态基础设施构建已经完成,是否需要继续构建其他设施,于是用户态通知内核态继续构建一条通道(既创建线程),方便两边的通信,当用户态接收到线程创建完毕的信息后,便可以开始使用这个环境(投入生产),以后缺啥补啥。

大家都知道,很久之前没有线程,进程做了一切工作,但是后来MicroSoft发现这并不行,就又搞了线程出来,从此进程成为系统进行资源分配和调度的一个独立单位,而线程成为进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位。(就是进程拿着钱而线程去花+.+)所以创建进程时就不仅要创建进程,还要创建线程。

分两部分说明CreatProcess的原理:第一部分为进程,第二部分为线程。

一.进程

1.1Ring3层

 

1.2Ring0层

 

 

二:创建线程:

Ring3 + Ring0

 

进程创建详细步骤(Windows内核情景分析):

第一阶段:打开目标映像文件
首先打开映像文件,再为其(分配)创建一个“section”即文件映射区,创建文件映射区的目的当然是要把映像文件的内容映射到这个区间,不过首先要检查已经打开的目标文件是否为一个合格的exe映像,此外,还要查询“注册表”中的这个路径:HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options。如果上述路径下有以目标映像文件的文件名和扩展名为“键”的表项,例如"Image.exe",而表项中又有名为"Debugger"的值,那么这个值(一个字符串)就替换了原来的目标文件名,变成新的目标映像名,并重新执行上述的第一阶段操作。如果黑客或某个木马程序设法在注册表中加上了这么一个表项,则用户以为是启动了进程A,而实际启动的却是B。

第二阶段:创建内核中的进程对象
所谓创建内核中的进程对象,实际上就是创建以EPROCESS为核心的相关数据结构,这就是系统调用NtCreateProcess()要做的事情,我们已在前面看过其代码了,主要包括:
1.分配并设置EPROCESS数据结构。
2.其他相关的数据结构的设置,例如句柄表。
3.为目标进程创建初始的地址空间。
4.对目标进程的“内核进程块”KPROCESS进行初始化,这是EPROCESS的一部分。
5.将目标进程的可执行映像映射到其自身的用户空间。
6.将系统DLL的映像映射到目标进程的用户空间。//ntdll.dll映射到目标进程的用户空间
7.设置好目标进程的“进程环境快”PEB
8.映射其他需要映射到用户空间的数据结构,例如与“当地语言支持”即NLS有关的数据结构。
9.完成EPROCESS创建,将其挂入进程队列并插入创建者的句柄表。

第三阶段:创建初始线程
进程只是个容器,实际的运行实体是里面的线程。所以下一步就是创建目标进程的初始线程,即其第一个线程。
与EPROCESS相对应,线程的数据结构是ETHREAD,其第一个成分则是KTHREAD数据结构,称为Tcb。此外,就像进程有“进程环境块”PEB一样,线程也有“线程环境块”TEB,KTHREAD结构中有个指针指向其存在于用户空间的TEB。PEB在用户空间的位置是固定的,PEB下方就是TEB,进程中有几个线程就有几个TEB,每个TEB占一个4Kb的页面。NtCreateThread():
1.创建和设置目标线程的ETHREAD数据结构。
2.在目标进程的用户空间创建并设置目标线程的TEB。
3.将目标线程在用户空间的起始地址设置成指向Kernel32.dll中的BaseProcessStartThunk()或BaseThreadStartupThunk(),前者用于进程中的第一个线程,后者用于随后的线程。用户程序在调用NtCreateThread()时也要提供一个用户级的起始函数(地址),BaseProcessStartThunk()和BaseThreadStartThunk()在完成初始化时会调用这个起始函数。
4.设置目标线程的KTHREAD数据结构并为其分配堆栈。特别的,将其上下文中的断点(返回点)设置成指向内核中的一段程序KiThreadStartup,使得该线程一旦被调度运行时就从这里开始执行。
5.如果登记了每当创建线程时就应加以调用的“通知”函数,就调用这些函数。

第四阶段:通知Windows子系统
当初在设计的时候还是考虑了对不同“平台”的支持,即在同一个内核的基础上配以不同的外围软件,形成不同的应用软件运行环境,微软称之为“子系统”。创建Windows进程时还要通知csrss,因为它担负着对所有Windows进程进行管理的责任,另一方面,csrss在接到通知以后就会在屏幕上显示那个沙漏状的光标(如果这是个有窗口的进程的话)。注意这里向csrss发出通知的是CreateProcess()调用者,而不是新创建出来的进程(它还没有开始运行)。

至此CreateProcess()的操作已经完成,CreateProcess()的调用者从该函数返回,就回到了应用程序或更高层的DLL中。这四个阶段都是立足于创建者进程的用户空间,在整个过程中进程了多次系统调用,每次系统调用完成后都回到了用户空间中。现在虽然创建者进程已经从库函数CreateProcess()返回了,子进程中的线程却尚未开始运行,他的运行还需要经历下面的第五第六阶段。

第五阶段:启动起始线程
新创建的线程未必是可以被立即调度运行的,因为用户可能在创建时把标志位CREATE_SUSPENDED设成了1。如果那样的话,就需要等待别的线程通过系统调用“恢复(Resume)”其运行资格以后才可以被调度运行。否则就是一经创建便可以被调度运行了。至于什么时候才会真正被调度运行,则要看优先级等条件了。而一旦被调度运行,那就是以新建进程的身份在运行,与CreateProcess()的调用者无关了。
当(用户空间)进程的第一个线程首次受调度运行时,由于其(系统空间)堆栈内容的设置,首先执行的是KiThreadStartup()。这段程序把目标线程的运行级别从DPC级降低到APC级,然后调用内核函数PspUserThreadStartup()。
最后PspUserThreadStartup()把用户空间ntdll.dll中的函数LdrInitializeThunk()作为APC函数挂入APC队列,再企图“返回”到用户空间。
于是此时的CPU将两次进入用户空间。第一次是因为APC请求的存在而进入用户空间,执行APC函数LdrInitializeThunk(),执行完毕仍回到系统控剑。然后,第二次进入用户空间才正式“返回”到用户空间。返回到Kernel32.dll中的BaseProcessStartThunk()或BaseThreadStartupThunk(),对于进程中的第一个线程是前者,对于后来的线程则是后者。至于用户程序所提供的(线程)入口,则是作为参数提供给这两个函数的,这两个函数都会使用该指针调用有用户提供的入口函数。
第六阶段:用户控件的初始化和DLL的连接
用户空间的初始化和DLL的动态连接是由APC函数LdrInitializeThunk()在用户空间完成的。LdrInitializeThunk()是ntdll.dll的一个函数。在此之前ntdll.dll已经被映射到了用户空间,但是其他的DLL尚未装入,应用软件与DLL之前也尚未建立动态连接。函数LdrInitializeThunk()在映像中的位置是预定的,所以在进入这个函数之前并不需要连接。

CreateProcessW源代码(含注释)

BOOL
WINAPI
CreateProcessW(
    LPCWSTR lpApplicationName,
    LPWSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    LPSTARTUPINFOW lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
    )

/*++

Routine Description:

    A process and thread object are created and a handle opened to each
    object using CreateProcess.  Note that WinExec and LoadModule are
    still supported, but are implemented as a call to CreateProcess.

Arguments:

    lpApplicationName - Supplies an optional pointer to a null terminated
        character string that contains the name of the image file to
        execute.  This is a fully qualified DOS path name.  If not
        specified, then the image file name is the first whitespace
        delimited token on the command line.

    lpCommandLine - Supplies a null terminated character string that
        contains the command line for the application to be executed.
        The entire command line is made available to the new process
        using GetCommandLine.  If the lpApplicationName parameter was
        not specified, then the first token of the command line
        specifies file name of the application (note that this token
        begins at the beginning of the command line and ends at the
        first "white space" character).  If the file name does not
        contain an extension (the presence of a "."), then .EXE is
        assumed.  If the file name does not contain a directory path,
        Windows will search for the executable file in:

          - The current directory

          - The windows directory

          - The windows system directory

          - The directories listed in the path environment variable

        This parameter is optional onlu if the lpApplicationName
        parameter is specified.  In this case the command line the
        application receives will be the application name.

    lpProcessAttributes - An optional parameter that may be used to
        specify the attributes of the new process.  If the parameter is
        not specified, then the process is created without a security
        descriptor, and the resulting handle is not inherited on process
        creation:

        SECURITY_ATTRIBUTES Structure:

        DWORD nLength - Specifies the length of this structure.  Must be
            set to sizeof( SECURITY_ATTRUBUTES ).

        LPVOID lpSecurityDescriptor - Points to a security descriptor for
            the object (must be NULL for Win32, used on NT/Win32). The
            security descriptor controls the sharing of an object.

        BOOL bInheritHandle - Supplies a flag that indicates whether
            or not the returned handle is to be inherited by a new
            process during process creation.  A value of TRUE
            indicates that the new process will inherit the handle.

    lpThreadAttributes - An optional parameter that may be used to specify
        the attributes of the new thread.  If the parameter is not
        specified, then the thread is created without a security
        descriptor, and the resulting handle is not inherited on
        process creation.

    dwCreationFlags - Supplies additional flags that control the creation
        of the process.

        dwCreationFlags Flags:

        DEBUG_PROCESS - If this flag bit is set, then the creating
            process is treated as a debugger, and the process being
            created is created as a debugee.  All debug events occuring
            in the debugee are reported to the debugger.  If this bit is
            clear, but the calling process is a debugee, then the
            process becomes a debugee of the calling processes debugger.
            If this bit is clear and the calling processes is not a
            debugee then no debug related actions occur.

        DEBUG_ONLY_THIS_PROCESS - If this flag is set, then the
            DEBUG_PROCESS flag bit must also be set.  The calling
            process is is treated as a debugger, and the new process is
            created as its debuggee.  If the new process creates
            additional processes, no debug related activities (with
            respect to the debugger) occur.

        CREATE_SUSPENDED - The process is created, but the initial thread
            of the process remains suspended. The creator can resume this
            thread using ResumeThread. Until this is done, code in the
            process will not execute.

        CREATE_UNICODE_ENVIRONMENT - If set, the environment pointer
            points to a Unicode environment block.  Otherwise, the
            block is ANSI (actually OEM.)

    bInheritHandles - Supplies a flag that specifies whether or not the
        new process is to inherit handles to objects visible to the
        calling process.  A value of TRUE causes handles to be inherited
        by the new process.  If TRUE was specified, then for each handle
        visible to the calling process, if the handle was created with
        the inherit handle option, the handle is inherited to the new
        process.  The handle has the same granted access in the new
        process as it has in the calling process, and the value of the
        handle is the same.

    lpEnvironment - An optional parameter, that if specified, supplies a
        pointer to an environment block.  If the parameter is not
        specified, the environment block of the current process is used.
        This environment block is made available to the new process
        using GetEnvironmentStrings.

    lpCurrentDirectory - An optional parameter, that if specified,
        supplies a string representing the current drive and directory
        for the new process.  The string must be a fully qualified
        pathname that includes a drive letter.  If the parameter is not
        specified, then the new process is created with the same current
        drive and directory as the calling process.  This option is
        provided primarily for shells that want to start an application
        and specify its initial drive and working directory.

    lpStartupInfo - Supplies information that specified how the
        applications window is to be shown. This structure is described
        in the Win32 User Interface API Book.

    lpProcessInformation - Returns identification information about the
        new process.

    PROCESS_INFORMATION Structure:

        HANDLE hProcess - Returns a handle to the newly created process.
            Through the handle, all operations on process objects are
            allowed.

        HANDLE hThread - Returns a handle to the newly created thread.
            Through the handle, all operations on thread objects are
            allowed.

        DWORD dwProcessId - Returns a global process id that may be used
            to identify a process.  The value is valid from the time the
            process is created until the time the process is terminated.

        DWORD dwThreadId - Returns a global thread id that may be used
            to identify a thread.  The value is valid from the time the
            thread is created until the time the thread is terminated.

Return Value:

    TRUE - The operation was successful

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES Obja;
    POBJECT_ATTRIBUTES pObja;
    HANDLE ProcessHandle, ThreadHandle, VdmWaitHandle = NULL;
    HANDLE FileHandle, SectionHandle;
    CLIENT_ID ClientId;
    UNICODE_STRING PathName;
    IO_STATUS_BLOCK IoStatusBlock;
    BOOLEAN TranslationStatus;
    RTL_RELATIVE_NAME RelativeName;
    PVOID FreeBuffer;
    LPWSTR NameBuffer;
    LPWSTR WhiteScan;
    ULONG Length,i;
    PROCESS_BASIC_INFORMATION ProcessInfo;
    SECTION_IMAGE_INFORMATION ImageInformation;
    NTSTATUS StackStatus;
    BOOLEAN bStatus;
    INITIAL_TEB InitialTeb;
    CONTEXT ThreadContext;
    PPEB Peb;
    BASE_API_MSG m;
    PBASE_CREATEPROCESS_MSG a= (PBASE_CREATEPROCESS_MSG)&m.u.CreateProcess;
    PBASE_CHECKVDM_MSG b= (PBASE_CHECKVDM_MSG)&m.u.CheckVDM;
    PWCH TempNull = NULL;
    WCHAR TempChar;
    UNICODE_STRING VdmNameString;
    PVOID BaseAddress;
    ULONG VdmReserve;
    SIZE_T BigVdmReserve;
    ULONG iTask=0;
    LPWSTR CurdirBuffer, CurdirFilePart;
    DWORD CurdirLength,CurdirLength2;
    ULONG VDMCreationState=0;
    ULONG VdmBinaryType = 0;
    UNICODE_STRING  SubSysCommandLine;
    PIMAGE_NT_HEADERS NtHeaders;
    DWORD dwNoWindow = (dwCreationFlags & CREATE_NO_WINDOW);
    ANSI_STRING AnsiStringVDMEnv;
    UNICODE_STRING UnicodeStringVDMEnv;
    WCHAR ImageFileDebuggerCommand[ 64 ];
    LPWSTR QuotedBuffer;
    BOOLEAN QuoteInsert;
    BOOLEAN QuoteCmdLine = FALSE;
    BOOLEAN QuoteFound;
    BOOLEAN SearchRetry;
    BOOLEAN IsWowBinary = FALSE;
    STARTUPINFOW StartupInfo;
    DWORD LastError;
    DWORD fileattr;
    PROCESS_PRIORITY_CLASS PriClass;
    PVOID State;
#if defined(BUILD_WOW6432) || defined(_WIN64)
    LPCWSTR lpOriginalApplicationName = lpApplicationName;
    LPWSTR lpOriginalCommandLine = lpCommandLine;
#endif

#if defined(WX86) || defined(_AXP64_)
    HANDLE Wx86Info = NULL;
#endif

#if defined WX86
    BOOLEAN UseKnownWx86Dll;
    UseKnownWx86Dll = NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll;
    NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
#endif


    RtlZeroMemory(lpProcessInformation,sizeof(*lpProcessInformation));

    // Private VDM flag should be ignored; Its meant for internal use only.
    dwCreationFlags &= (ULONG)~CREATE_NO_WINDOW;//首先屏蔽CREATE_NO_WINDOW标志

    //
    // CREATE_WITH_USERPROFILE is the new Create Flag that is used
    // only by CreateProcessWithLogonW.  If this flags ends up getting
    // passed to CreateProcess, we must reject it.
    //
    if (dwCreationFlags & CREATE_WITH_USERPROFILE ) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
        }

    if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
        (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) { //参考 MSDN,DETACHED_PROCESS|CREATE_NEW_CONSOLE这个组合是不合法的

        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
        }

    AnsiStringVDMEnv.Buffer = NULL;
    UnicodeStringVDMEnv.Buffer = NULL;

    //
    // the lowest specified priority class is used.
    //
    //判断优先级,判断顺序依次为IDLE_PRIORITY_CLASS,NORMAL_PRIORITY_CLASS,HIGH_PRIORITY_CLASS,REALTIME_PRIORITY_CLASS
    //只要满足其中一个优先级,就跳过其他优先级的判断,如果都不满足,将权限级置为0
    if (dwCreationFlags & IDLE_PRIORITY_CLASS ) {
        PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
        }
    else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS ) {
        PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
        }
    else if (dwCreationFlags & NORMAL_PRIORITY_CLASS ) {
        PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
        }
    else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS ) {
        PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
        }
    else if (dwCreationFlags & HIGH_PRIORITY_CLASS ) {
        PriClass.PriorityClass =  PROCESS_PRIORITY_CLASS_HIGH;
        }
    else if (dwCreationFlags & REALTIME_PRIORITY_CLASS ) {
        if ( BasepIsRealtimeAllowed(FALSE) ) {
            PriClass.PriorityClass =  PROCESS_PRIORITY_CLASS_REALTIME;
            }
        else {
            PriClass.PriorityClass =  PROCESS_PRIORITY_CLASS_HIGH;
            }
        }
    else {
        PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_UNKNOWN;
        }
    PriClass.Foreground = FALSE;

    dwCreationFlags = (dwCreationFlags & ~PRIORITY_CLASS_MASK );//过滤掉优先级的标志位,然后再判断是什么创建标志

    //
    // Default separate/shared VDM option if not explicitly specified.
    //
    //CREATE_SEPARATE_WOW_VDM 和CREATE_SHARED_WOW_VDM只适用于Windows NT
    if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) {
        if (dwCreationFlags & CREATE_SHARED_WOW_VDM) {
            SetLastError(ERROR_INVALID_PARAMETER);

            return FALSE;
            }
        }
    else
    if ((dwCreationFlags & CREATE_SHARED_WOW_VDM) == 0) {
        if (BaseStaticServerData->DefaultSeparateVDM) {
            dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
            }
        }

    if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) == 0) {
        //
        // If the creator is running inside a job object, always
        // set SEPERATE_WOW_VDM so the VDM is part of the job.
        //
        JOBOBJECT_BASIC_UI_RESTRICTIONS UiRestrictions;

        Status = NtQueryInformationJobObject(NULL,
                                             JobObjectBasicUIRestrictions,
                                             &UiRestrictions,
                                             sizeof(UiRestrictions),
                                             NULL);
        if (Status != STATUS_ACCESS_DENIED) {
            //
            // Anything other than STATUS_ACCESS_DENIED indicates the
            // current process is inside a job.
            //
            dwCreationFlags = (dwCreationFlags & (~CREATE_SHARED_WOW_VDM)) |
                                  CREATE_SEPARATE_WOW_VDM;
            }
        }


    //
    //  If ANSI environment, convert to Unicode
    //
    //判断lpEnvironment是否为空,如果不为空,将Ansi字符串转换为UNICODE_STRING,为空的话跳过这一步。
    //
    if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
        PUCHAR s;
        STRING Ansi;
        UNICODE_STRING Unicode;
        MEMORY_BASIC_INFORMATION MemoryInformation;

        Ansi.Buffer = s = lpEnvironment;
        while (*s || *(s+1))            // find end of block
            s++;

        Ansi.Length = (USHORT)(s - Ansi.Buffer) + 1;
        Ansi.MaximumLength = Ansi.Length + 1;
        MemoryInformation.RegionSize = Ansi.MaximumLength * sizeof(WCHAR);
        Unicode.Buffer = NULL;
        //给Unicode申请空间
        Status = NtAllocateVirtualMemory( NtCurrentProcess(),
                                          &Unicode.Buffer,
                                          0,
                                          &MemoryInformation.RegionSize,
                                          MEM_COMMIT,
                                          PAGE_READWRITE
                                        );
        if (!NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);

            return FALSE;
            }

        Unicode.MaximumLength = (USHORT)MemoryInformation.RegionSize;
        Status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, FALSE);//Ansi->UNICODE_STRING
        if (!NT_SUCCESS(Status) ) {
            NtFreeVirtualMemory( NtCurrentProcess(),
                                 &Unicode.Buffer,
                                 &MemoryInformation.RegionSize,
                                 MEM_RELEASE
                               );
            BaseSetLastNTError(Status);

            return FALSE;
            }
        lpEnvironment = Unicode.Buffer;
        }

    FileHandle = NULL;
    SectionHandle = NULL;
    ProcessHandle = NULL;
    ThreadHandle = NULL;
    FreeBuffer = NULL;
    NameBuffer = NULL;
    VdmNameString.Buffer = NULL;
    BaseAddress = (PVOID)1;
    VdmReserve = 0;
    CurdirBuffer = NULL;
    CurdirFilePart = NULL;
    SubSysCommandLine.Buffer = NULL;
    QuoteFound = FALSE;
    QuoteInsert = FALSE;
    QuotedBuffer = NULL;

    try {

        //
        // Make a copy of the startup info so we can change it.
        //

        StartupInfo = *lpStartupInfo;

        //
        // STARTF_USEHOTKEY means hStdInput is really the hotkey value.
        // STARTF_HASSHELLDATA means std handles are used for shell-private
        // data.  This flag is used if an icon is passed to ShellExecuteEx.
        // As a result they cannot be specified with STARTF_USESTDHANDLES.
        // Consistent with Win95, USESTDHANDLES is ignored.
        //

        if (StartupInfo.dwFlags & STARTF_USESTDHANDLES &&
            StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) {

            StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
            }

VdmRetry:
        LastError = 0;
        SearchRetry = TRUE;
        QuoteInsert = FALSE;
        QuoteCmdLine = FALSE;
        if (!ARGUMENT_PRESENT( lpApplicationName )) {
            //lpApplicationName为空,大部分情况都为空
            //在这种情况下,可执行模块的名字必须处于lpCommandLine参数的最前面并由空格符与后面的字符分开

            //
            // Locate the image
            //

            // forgot to free NameBuffer before goto VdmRetry???
            ASSERT(NameBuffer == NULL);

            NameBuffer = RtlAllocateHeap( RtlProcessHeap(),
                                          MAKE_TAG( TMP_TAG ),
                                          MAX_PATH * sizeof( WCHAR ));
            if ( !NameBuffer ) {
                BaseSetLastNTError(STATUS_NO_MEMORY);
                return FALSE;
                }
            lpApplicationName = lpCommandLine;//从命令行中获取应用名
            TempNull = (PWCH)lpApplicationName;
            WhiteScan = (LPWSTR)lpApplicationName;


            //
            //当CreateProcess解析lpCommandLine字符串时,它会检查字符串中的第一个标记(token),并假记此标记为我们想
            //运行的可执行文件的名称。
            // check for lead quote
            //
            if ( *WhiteScan == L'\"' ) {
                SearchRetry = FALSE;
                WhiteScan++;
                lpApplicationName = WhiteScan;
                while(*WhiteScan) {
                    if ( *WhiteScan == (WCHAR)'\"' ) {
                        TempNull = (PWCH)WhiteScan;
                        QuoteFound = TRUE;
                        break;
                        }
                    WhiteScan++;
                    TempNull = (PWCH)WhiteScan;
                    }
                }
            else {
retrywsscan:
                lpApplicationName = lpCommandLine;
                while(*WhiteScan) {
                    if ( *WhiteScan == (WCHAR)' ' ||
                         *WhiteScan == (WCHAR)'\t' ) {
                        TempNull = (PWCH)WhiteScan;
                        break;
                        }
                    WhiteScan++;
                    TempNull = (PWCH)WhiteScan;
                    }
                }
            TempChar = *TempNull;
            *TempNull = UNICODE_NULL;

#ifdef WX86

            //
            // Wx86 applications must use x86 version of known exes
            // for compatibility.
            //

            if (UseKnownWx86Dll) {
               LPWSTR KnownName;

               NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;


                //判断lpApplicationName是否为regedit.exe,regsvr32.exe,msiexec.exe
               KnownName = BasepWx86KnownExe(lpApplicationName);
               if (KnownName) {
                  lpApplicationName = KnownName;
                  }
               }
#endif

            //寻找.exe,如果lpApplicationName不包含扩展名,默认为.exe
            //并且如果文件名不包含一个完整的路径,会按以下顺序搜索可执行文件:
            //1.主调进程.exe文件所在的目录
            //2.主调进程的当前目录
            //3.windows系统目录,即GetSystemDiretory返回的System32子文件夹
            //4.windows目录
            //5.path环境变量中列出的目录
            Length = SearchPathW(
                        NULL,
                        lpApplicationName,
                        (PWSTR)L".exe",
                        MAX_PATH,
                        NameBuffer,
                        NULL
                        )*2;

            if (Length != 0 && Length < MAX_PATH * sizeof( WCHAR )) {
                //
                // SearchPathW worked, but file might be a directory
                // if this happens, we need to keep trying
                //路径是一个文件夹
                //
                fileattr = GetFileAttributesW(NameBuffer);
                if ( fileattr != 0xffffffff &&
                     (fileattr & FILE_ATTRIBUTE_DIRECTORY) ) {
                    Length = 0;
                } else {
                    Length++;
                    Length++;
                }
            }

            if ( !Length || Length >= MAX_PATH<<1 ) {

                //
                // If we search pathed, then return file not found.
                // otherwise, try to be more specific.
                //没有找到
                //
                RTL_PATH_TYPE PathType;
                HANDLE hFile;

                //判断应用的状态
                PathType = RtlDetermineDosPathNameType_U(lpApplicationName);//?????不懂
                if ( PathType != RtlPathTypeRelative ) {
                    //不是活跃状态

                    //
                    // The failed open should set get last error properly.
                    //

                    hFile = CreateFileW(
                                lpApplicationName,
                                GENERIC_READ,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL
                                );
                    if ( hFile != INVALID_HANDLE_VALUE ) {
                        CloseHandle(hFile);
                        BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
                        }
                    }
                else {
                    BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
                    }

                //
                // remember initial last error value for the retry scan path
                //

                if ( LastError ) {
                    SetLastError(LastError);
                    }
                else {
                    LastError = GetLastError();
                    }

                //
                // restore the command line
                //

                *TempNull = TempChar;
                lpApplicationName = NameBuffer;

                //
                // If we still have command line left, then keep going
                // the point is to march through the command line looking
                // for whitespace so we can try to find an image name
                // launches of things like:
                // c:\word 95\winword.exe /embedding -automation
                // require this. Our first iteration will stop at c:\word, our next
                // will stop at c:\word 95\winword.exe
                //
                if (*WhiteScan && SearchRetry) {
                    WhiteScan++;
                    TempNull = WhiteScan;
                    QuoteInsert = TRUE;
                    QuoteFound = TRUE;
                    goto retrywsscan;
                }

                return FALSE;
                }
            //
            // restore the command line
            //

            *TempNull = TempChar;
            lpApplicationName = NameBuffer;
            }
        else
        if (!ARGUMENT_PRESENT( lpCommandLine ) || *lpCommandLine == UNICODE_NULL ) {
            QuoteCmdLine = TRUE;
            lpCommandLine = (LPWSTR)lpApplicationName;
            }


#ifdef WX86

       //
       // Wx86 applications must use x86 version of known exes
       // for compatibility.
       //

       if (UseKnownWx86Dll) {
           LPWSTR KnownName;

           NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;

           KnownName = BasepWx86KnownExe(lpApplicationName);
           if (KnownName) {

               RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
               NameBuffer = KnownName;
               lpApplicationName = KnownName;
               }
           }

#endif


        //
        // Translate to an NT name.
        //将DOS路径转换为NT路径,由于用户给定的路径一般都是DOS路径,而内核需要的是NT路径,因此需要转换一下
        //

        TranslationStatus = RtlDosPathNameToNtPathName_U(
                                lpApplicationName,
                                &PathName,
                                NULL,
                                &RelativeName
                                );

        if ( !TranslationStatus ) {
            SetLastError(ERROR_PATH_NOT_FOUND);

            return FALSE;
            }

        // forgot to free FreeBuffer before goto VdmRetry????
        ASSERT(FreeBuffer == NULL);
        FreeBuffer = PathName.Buffer;

        if ( RelativeName.RelativeName.Length ) {
            PathName = *(PUNICODE_STRING)&RelativeName.RelativeName;
            }
        else {
            RelativeName.ContainingDirectory = NULL;
            }

        InitializeObjectAttributes(
            &Obja,
            &PathName,
            OBJ_CASE_INSENSITIVE,
            RelativeName.ContainingDirectory,
            NULL
            );

        //
        // Open the file for execute access
        //获得文件句柄
        //

        Status = NtOpenFile(
                    &FileHandle,
                    SYNCHRONIZE | FILE_EXECUTE,
                    &Obja,
                    &IoStatusBlock,
                    FILE_SHARE_READ | FILE_SHARE_DELETE,
                    FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
                    );
        if (!NT_SUCCESS(Status) ) {

            //
            // if we failed, see if this is a device. If it is a device,
            // then just return invalid image format
            //

            if ( RtlIsDosDeviceName_U((PWSTR)lpApplicationName) ) {
                SetLastError(ERROR_BAD_DEVICE);
                }
            else {
                BaseSetLastNTError(Status);
                }

            return FALSE;
            }

        //
        // If no desktop has been specified, use the caller's
        // desktop.
        //

        if (StartupInfo.lpDesktop == NULL) {
            StartupInfo.lpDesktop =
                    (LPWSTR)((PRTL_USER_PROCESS_PARAMETERS)NtCurrentPeb()->
                        ProcessParameters)->DesktopInfo.Buffer;
            }

        //
        // Create a section object backed by the file
        //得到内存区对象句柄
        //

        Status = NtCreateSection(
                    &SectionHandle,
                    SECTION_ALL_ACCESS,
                    NULL,
                    NULL,
                    PAGE_EXECUTE,
                    SEC_IMAGE,
                    FileHandle
                    );


        NtClose(FileHandle);
        FileHandle = NULL;



        //
        // App Certification DLL
        //

       if (NT_SUCCESS(Status)) {
            //BasepIsProcessAllowed该函数用来判断应用程序名是否在授权文件列表中
            //函数实现调用了NtOpenKey函数打开了注册表中的HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options键
          Status = BasepIsProcessAllowed(lpApplicationName);

          if (!NT_SUCCESS(Status)) {
            BaseSetLastNTError(Status);
            return FALSE;
          }

       }



        if (!NT_SUCCESS(Status)) {
            //得到SectionHandle失败
            switch (Status) {
                // 16 bit OS/2 exe
                case STATUS_INVALID_IMAGE_NE_FORMAT:
#ifdef i386
                //
                // Use OS/2 if x86 (OS/2 not supported on risc),
                //    and CreationFlags don't have forcedos bit
                //    and Registry didn't specify ForceDos
                //
                // else execute as a DOS bound app.
                //
                //

                if (!(dwCreationFlags & CREATE_FORCEDOS) &&
                    !BaseStaticServerData->ForceDos)
                  {

                    if ( !BuildSubSysCommandLine( L"OS2 /P ",
                                                  lpApplicationName,
                                                  lpCommandLine,
                                                  &SubSysCommandLine
                                                ) ) {
                        return FALSE;
                        }

                    lpCommandLine = SubSysCommandLine.Buffer;

                    lpApplicationName = NULL;

                    RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
                    FreeBuffer = NULL;
                    RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
                    NameBuffer = NULL;
                    goto VdmRetry;
                    }
#endif
                    // Falls into Dos case, so that stub message will be
                    // printed, and bound apps will run w/o OS/2 subsytem

                // Dos .exe or .com

                case STATUS_INVALID_IMAGE_PROTECT:
                case STATUS_INVALID_IMAGE_NOT_MZ:
ForceDos:
                    {
                    ULONG BinarySubType;

                    BinarySubType = BINARY_TYPE_DOS_EXE;
                    if (Status == STATUS_INVALID_IMAGE_PROTECT   ||
                        Status == STATUS_INVALID_IMAGE_NE_FORMAT ||
                       (BinarySubType = BaseIsDosApplication(&PathName,Status)) )
                       {
                        VdmBinaryType = BINARY_TYPE_DOS;

                        // create the environment before going to the
                        // server. This was done becuase we want NTVDM
                        // to have the new environment when it gets
                        // created.
                        if (!BaseCreateVDMEnvironment(
                                    lpEnvironment,
                                    &AnsiStringVDMEnv,
                                    &UnicodeStringVDMEnv
                                    ))
                            return FALSE;

                        if(!BaseCheckVDM(VdmBinaryType | BinarySubType,
                                         lpApplicationName,
                                         lpCommandLine,
                                         lpCurrentDirectory,
                                         &AnsiStringVDMEnv,
                                         &m,
                                         &iTask,
                                         dwCreationFlags,
                                         &StartupInfo
                                         ))
                            return FALSE;


                        // Check the return value from the server
                        switch (b->VDMState & VDM_STATE_MASK){
                            case VDM_NOT_PRESENT:
                                // mark this so the server can undo
                                // creation if something goes wrong.
                                // We marked it "partitially created" because
                                // the NTVDM has yet not been fully created.
                                // a call to UpdateVdmEntry to update
                                // process handle will signal the NTVDM
                                // process completed creation
                                VDMCreationState = VDM_PARTIALLY_CREATED;
                                // fail the call if NTVDM process is being
                                // created DETACHED.
                                // note that, we let it go if NTVDM process
                                // is already running.
                                if (dwCreationFlags & DETACHED_PROCESS) {
                                    SetLastError(ERROR_ACCESS_DENIED);
                                    return FALSE;
                                    }
                                if (!BaseGetVdmConfigInfo(lpCommandLine,
                                                          iTask,
                                                          VdmBinaryType,
                                                          &VdmNameString,
                                                          &VdmReserve
                                                          ))
                                   {
                                    BaseSetLastNTError(Status);
                                    return FALSE;
                                    }

                                lpCommandLine = VdmNameString.Buffer;
                                lpApplicationName = NULL;

                                break;

                            case VDM_PRESENT_NOT_READY:
                                SetLastError (ERROR_NOT_READY);
                                return FALSE;

                            case VDM_PRESENT_AND_READY:
                                VDMCreationState = VDM_BEING_REUSED;
                                VdmWaitHandle = b->WaitObjectForParent;
                                break;
                            }
                         RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
                         FreeBuffer = NULL;
                         RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
                         NameBuffer = NULL;
                         VdmReserve--;               // we reserve from addr 1
                         if(VdmWaitHandle)
                            goto VdmExists;
                         else{
                            bInheritHandles = FALSE;
                            if (lpEnvironment &&
                                !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)){
                                RtlDestroyEnvironment(lpEnvironment);
                                }
                            lpEnvironment = UnicodeStringVDMEnv.Buffer;
                            goto VdmRetry;
                            }
                        }
                    else {

                        //
                        //  must be a .bat or .cmd file
                        //

                        static PWCHAR CmdPrefix = L"cmd /c ";
                        PWCHAR NewCommandLine;
                        ULONG Length;
                        PWCHAR Last4 = &PathName.Buffer[PathName.Length / sizeof( WCHAR )-4];

                        if ( PathName.Length < 8 ) {
                            SetLastError(ERROR_BAD_EXE_FORMAT);
                            return FALSE;
                            }

                        if (_wcsnicmp( Last4, L".bat", 4 ) && _wcsnicmp( Last4, L".cmd", 4 )) {
                            SetLastError(ERROR_BAD_EXE_FORMAT);
                            return FALSE;
                        }

                        Length = wcslen( CmdPrefix )
                                 + (QuoteCmdLine || QuoteFound )
                                 + wcslen( lpCommandLine )
                                 + (QuoteCmdLine || QuoteFound)
                                 + 1;

                        NewCommandLine = RtlAllocateHeap( RtlProcessHeap( ),
                                                          MAKE_TAG( TMP_TAG ),
                                                          Length * sizeof( WCHAR ) );

                        if (NewCommandLine == NULL) {
                            BaseSetLastNTError(STATUS_NO_MEMORY);
                            return FALSE;
                        }

                        wcscpy( NewCommandLine, CmdPrefix );
                        if (QuoteCmdLine || QuoteFound) {
                            wcscat( NewCommandLine, L"\"" );
                        }
                        wcscat( NewCommandLine, lpCommandLine );
                        if (QuoteCmdLine || QuoteFound) {
                            wcscat( NewCommandLine, L"\"" );
                        }

                        RtlInitUnicodeString( &SubSysCommandLine, NewCommandLine );

                        lpCommandLine = SubSysCommandLine.Buffer;

                        lpApplicationName = NULL;

                        RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
                        FreeBuffer = NULL;
                        RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
                        NameBuffer = NULL;
                        goto VdmRetry;

                        }

                    }

                // 16 bit windows exe
                case STATUS_INVALID_IMAGE_WIN_16:
#if defined(BUILD_WOW6432) || defined(_WIN64)
                   if (lpOriginalApplicationName == NULL) {
                       // pass in the part of the command line after the exe name
                       // including whitespace
                       lpCommandLine = ((*TempNull == '\"') ? TempNull + 1 : TempNull);
                   } else {
                       lpCommandLine = lpOriginalCommandLine;
                   }
                   return NtVdm64CreateProcess(lpOriginalApplicationName == NULL,
                                               lpApplicationName,             // this is now the real file name we've loaded
                                               lpCommandLine,
                                               lpProcessAttributes,
                                               lpThreadAttributes,
                                               bInheritHandles,
                                               (dwCreationFlags & ~CREATE_UNICODE_ENVIRONMENT),  // the environment has already been converted to unicode
                                               lpEnvironment,
                                               lpCurrentDirectory,
                                               lpStartupInfo,
                                               lpProcessInformation
                                               );
#endif
                   if (dwCreationFlags & CREATE_FORCEDOS) {
                       goto ForceDos;
                       }

                    IsWowBinary = TRUE;
                    if (!BaseCreateVDMEnvironment(lpEnvironment,
                                                  &AnsiStringVDMEnv,
                                                  &UnicodeStringVDMEnv
                                                  ))
                       {
                        return FALSE;
                        }



RetrySepWow:
                    VdmBinaryType = dwCreationFlags & CREATE_SEPARATE_WOW_VDM
                                     ? BINARY_TYPE_SEPWOW : BINARY_TYPE_WIN16;

                    if (!BaseCheckVDM(VdmBinaryType,
                                      lpApplicationName,
                                      lpCommandLine,
                                      lpCurrentDirectory,
                                      &AnsiStringVDMEnv,
                                      &m,
                                      &iTask,
                                      dwCreationFlags,
                                      &StartupInfo
                                      ))
                       {
                        //
                        // If we failed with access denied, caller may not
                        // be allowed allowed to access the shared wow's
                        // desktop, so retry as a separate wow
                        //
                        if (VdmBinaryType == BINARY_TYPE_WIN16 &&
                            GetLastError() == ERROR_ACCESS_DENIED)
                          {
                           dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
                           }
                        else {
                           return FALSE;
                           }
                        goto RetrySepWow;
                        }


                    // Check the return value from the server
                    switch (b->VDMState & VDM_STATE_MASK){
                        case VDM_NOT_PRESENT:
                            // mark this so the server can undo
                            // creation if something goes wrong.
                            // We marked it "partitially created" because
                            // the NTVDM has yet not been fully created.
                            // a call to UpdateVdmEntry to update
                            // process handle will signal the NTVDM
                            // process completed creation

                            VDMCreationState = VDM_PARTIALLY_CREATED;

                            if (!BaseGetVdmConfigInfo(
                                    lpCommandLine,
                                    iTask,
                                    VdmBinaryType,
                                    &VdmNameString,
                                    &VdmReserve
                                    ))
                               {
                                BaseSetLastNTError(Status);
                                return FALSE;
                                }

                            lpCommandLine = VdmNameString.Buffer;
                            lpApplicationName = NULL;


                            //
                            // Wow must have a hidden console
                            // Throw away DETACHED_PROCESS flag which isn't
                            // meaningful for Win16 apps.
                            //

                            dwCreationFlags |= CREATE_NO_WINDOW;
                            dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);


                            //
                            // We're starting a WOW VDM, turn on feedback unless
                            // the creator passed STARTF_FORCEOFFFEEDBACK.
                            //

                            StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;

                            break;

                        case VDM_PRESENT_NOT_READY:
                            SetLastError (ERROR_NOT_READY);
                            return FALSE;

                        case VDM_PRESENT_AND_READY:
                            VDMCreationState = VDM_BEING_REUSED;
                            VdmWaitHandle = b->WaitObjectForParent;
                            break;
                        }

                    RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
                    FreeBuffer = NULL;
                    RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
                    NameBuffer = NULL;
                    VdmReserve--;               // we reserve from addr 1
                    if(VdmWaitHandle)
                        goto VdmExists;
                    else {
                        bInheritHandles = FALSE;
                        // replace the environment with ours
                        if (lpEnvironment &&
                            !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) {
                            RtlDestroyEnvironment(lpEnvironment);
                            }
                        lpEnvironment = UnicodeStringVDMEnv.Buffer;
                        goto VdmRetry;
                        }


                default :
                    SetLastError(ERROR_BAD_EXE_FORMAT);
                    return FALSE;
            }
        }

        //
        // Make sure only WOW apps can have the CREATE_SEPARATE_WOW_VDM flag.
        //

        if (!IsWowBinary && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) {
            dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
        }

        //
        //上面皆为错误处理
        // Query the section to determine the stack parameters and
        // image entrypoint.
        //返回得到节的基本信息(节基地址,大小,属性)
        //

        Status = NtQuerySection(
                    SectionHandle,
                    SectionImageInformation,
                    &ImageInformation,
                    sizeof( ImageInformation ),
                    NULL
                    );

        if (!NT_SUCCESS( Status )) {
            BaseSetLastNTError(Status);
            RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
            FreeBuffer = NULL;
            return FALSE;
            }

        if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) {
            SetLastError(ERROR_BAD_EXE_FORMAT);
            RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
            FreeBuffer = NULL;
            return FALSE;
            }

        ImageFileDebuggerCommand[ 0 ] = UNICODE_NULL;
        if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
            NtCurrentPeb()->ReadImageFileExecOptions
           ) 
           {
            LdrQueryImageFileExecutionOptions( &PathName,
                                               L"Debugger",
                                               REG_SZ,
                                               ImageFileDebuggerCommand,
                                               sizeof( ImageFileDebuggerCommand ),
                                               NULL
                                             );
            }



        if ((ImageInformation.Machine < USER_SHARED_DATA->ImageNumberLow) ||
            (ImageInformation.Machine > USER_SHARED_DATA->ImageNumberHigh)) {
#if defined(WX86) || defined(_AXP64_)
            if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386)
               {
                Wx86Info = (HANDLE)UIntToPtr(sizeof(WX86TIB));
                }
            else
#endif // WX86
#if defined(_AXP64_)
            if (ImageInformation.Machine == IMAGE_FILE_MACHINE_ALPHA) {
               // Fall through since this is a valid machine type.
                }
             else
#elif defined(_IA64_)
            if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) {
               // Fall through since this is a valid machine type.
                }
             else
#endif // _AXP64_
#if defined(BUILD_WOW6432)
            // 32-bit kernel32.dll on NT64 can run 64-bit binaries
#if defined(_ALPHA_)
            if (ImageInformation.Machine == IMAGE_FILE_MACHINE_ALPHA) {
               // Fall through since this is a valid machine type.
                }
             else
#elif defined(_X86_)
            if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) {
               // Fall through since this is a valid machine type.
                }
             else
#endif  // ALPHA or IA64
#endif  // BUILD_WOW6432
                {
                ULONG_PTR ErrorParameters[2];
                ULONG ErrorResponse;

                ErrorResponse = ResponseOk;
                ErrorParameters[0] = (ULONG_PTR)&PathName;

                NtRaiseHardError( STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
                                  1,
                                  1,
                                  ErrorParameters,
                                  OptionOk,
                                  &ErrorResponse
                                );
                if ( NtCurrentPeb()->ImageSubsystemMajorVersion <= 3 ) {
                    SetLastError(ERROR_BAD_EXE_FORMAT);
                    }
                else {
                    SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
                    }
                RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
                FreeBuffer = NULL;
                return FALSE;
                }
            }

        RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
        FreeBuffer = NULL;
        if ( ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI &&
             ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI ) {

            // POSIX exe

            NtClose(SectionHandle);
            SectionHandle = NULL;

            if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_POSIX_CUI ) {

                if ( !BuildSubSysCommandLine( L"POSIX /P ",
                                              lpApplicationName,
                                              lpCommandLine,
                                              &SubSysCommandLine
                                            ) ) {
                    return FALSE;
                }

                lpCommandLine = SubSysCommandLine.Buffer;

                lpApplicationName = NULL;
                RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
                NameBuffer = NULL;
                goto VdmRetry;
                }
            else {
                SetLastError(ERROR_CHILD_NOT_COMPLETE);
                return FALSE;
                }
            }
        else {
            //判断镜像文件版本是否合法
            if (!BasepIsImageVersionOk( ImageInformation.SubSystemMajorVersion,
                                        ImageInformation.SubSystemMinorVersion) ) {
                SetLastError(ERROR_BAD_EXE_FORMAT);
                return FALSE;
                }
            }

        if (ImageFileDebuggerCommand[ 0 ] != UNICODE_NULL) {
            USHORT n;

            n = (USHORT)wcslen( lpCommandLine );
            if (n == 0) {
                lpCommandLine = (LPWSTR)lpApplicationName;
                n = (USHORT)wcslen( lpCommandLine );
                }

            n += wcslen( ImageFileDebuggerCommand ) + 1 + 2;//一个空格两个'\0'
            n *= sizeof( WCHAR );

            SubSysCommandLine.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), n );
            SubSysCommandLine.Length = 0;
            SubSysCommandLine.MaximumLength = n;
            RtlAppendUnicodeToString( &SubSysCommandLine, ImageFileDebuggerCommand );
            RtlAppendUnicodeToString( &SubSysCommandLine, L" " );
            RtlAppendUnicodeToString( &SubSysCommandLine, (PWSTR)lpCommandLine );
#if DBG
            DbgPrint( "BASE: Calling debugger with '%wZ'\n", &SubSysCommandLine );
#endif
            lpCommandLine = SubSysCommandLine.Buffer;
            lpApplicationName = NULL;

            NtClose(SectionHandle);
            SectionHandle = NULL;
            RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
            NameBuffer = NULL;
            goto VdmRetry;
            }

        //
        // Create the process object
        //
        //将安全属性结构为NT对象属性结构(得到了对象属性)
        pObja = BaseFormatObjectAttributes(&Obja,lpProcessAttributes,NULL);

        if (dwCreationFlags & CREATE_BREAKAWAY_FROM_JOB ) {
            SectionHandle = (HANDLE)( (UINT_PTR)SectionHandle | 1);
            }

        Status = NtCreateProcess(
                    &ProcessHandle,
                    PROCESS_ALL_ACCESS,
                    pObja,
                    NtCurrentProcess(),
                    (BOOLEAN)bInheritHandles,
                    SectionHandle,
                    NULL,
                    NULL
                    );
        if ( !NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            return FALSE;
            }

        //
        // NtCreateProcess will set to normal OR inherit if parent is IDLE or Below
        // only override if a mask is given during the create.
        //

        if ( PriClass.PriorityClass != PROCESS_PRIORITY_CLASS_UNKNOWN ) {
            State = NULL;
            if ( PriClass.PriorityClass ==  PROCESS_PRIORITY_CLASS_REALTIME ) {
                State = BasepIsRealtimeAllowed(TRUE);
                }
            //设置进程的优先级和默认处理模式
            Status = NtSetInformationProcess(
                        ProcessHandle,
                        ProcessPriorityClass,
                        (PVOID)&PriClass,
                        sizeof(PriClass)
                        );
            if ( State ) {
                BasepReleasePrivilege( State );
                }

            if ( !NT_SUCCESS(Status) ) {
                BaseSetLastNTError(Status);
                return FALSE;
                }
            }

        NtClose(SectionHandle);
        SectionHandle = NULL;

        if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) {
            UINT NewMode;
            NewMode = SEM_FAILCRITICALERRORS;
            NtSetInformationProcess(
                ProcessHandle,
                ProcessDefaultHardErrorMode,
                (PVOID) &NewMode,
                sizeof(NewMode)
                );
            }

        //
        // If the process is being created for a VDM call the server with
        // process handle.
        //

        if (VdmBinaryType) {
            VdmWaitHandle = ProcessHandle;
            if (!BaseUpdateVDMEntry(UPDATE_VDM_PROCESS_HANDLE,
                                    &VdmWaitHandle,
                                    iTask,
                                    VdmBinaryType
                                    ))
               {
                //make sure we don't close the handle twice --
                //(VdmWaitHandle == ProcessHandle) if we don't do this.
                VdmWaitHandle = NULL;
                return FALSE;
                }

            //
            // For Sep wow the VdmWaitHandle = NULL (there is none!)
            //

            VDMCreationState |= VDM_FULLY_CREATED;
            }

        //
        // if we're a detached priority, we don't have the focus, so
        // don't create with boosted priority.
        //

        if (dwCreationFlags & DETACHED_PROCESS) {
            KPRIORITY SetBasePriority;

            SetBasePriority = (KPRIORITY)NORMAL_BASE_PRIORITY;
            Status =  NtSetInformationProcess(ProcessHandle,
                                              ProcessBasePriority,
                                              (PVOID) &SetBasePriority,
                                              sizeof(SetBasePriority)
                                              );
            ASSERT(NT_SUCCESS(Status));
        }

#if defined(i386) || defined(_IA64_)
        //
        // Reserve memory in the new process' address space if necessary
        // (for vdms). This is required only for x86 system.
        //

    if ( VdmReserve ) {
            BigVdmReserve = VdmReserve;
            Status = NtAllocateVirtualMemory(
                        ProcessHandle,
                        &BaseAddress,
                        0L,
                        &BigVdmReserve,
                        MEM_RESERVE,
                        PAGE_EXECUTE_READWRITE
                        );
            if ( !NT_SUCCESS(Status) ){
                BaseSetLastNTError(Status);
                return FALSE;
            }
    }
#endif

        //
        // Determine the location of the
        // processes PEB.
        //

        Status = NtQueryInformationProcess(
                    ProcessHandle,
                    ProcessBasicInformation,
                    &ProcessInfo,
                    sizeof( ProcessInfo ),
                    NULL
                    );
        if ( !NT_SUCCESS( Status ) ) {
            BaseSetLastNTError(Status);
            return FALSE;
            }

        Peb = ProcessInfo.PebBaseAddress;

        //
        // Push the parameters into the address space of the new process
        //

        if ( ARGUMENT_PRESENT(lpCurrentDirectory) ) {
            CurdirBuffer = RtlAllocateHeap( RtlProcessHeap(),
                                            MAKE_TAG( TMP_TAG ),
                                            (MAX_PATH + 1) * sizeof( WCHAR ) );
            if ( !CurdirBuffer ) {
                BaseSetLastNTError(STATUS_NO_MEMORY);
                return FALSE;
                }
            CurdirLength2 = GetFullPathNameW(
                                lpCurrentDirectory,
                                MAX_PATH,
                                CurdirBuffer,
                                &CurdirFilePart
                                );
            if ( CurdirLength2 > MAX_PATH ) {
                SetLastError(ERROR_DIRECTORY);
                return FALSE;
                }

            //
            // now make sure the directory exists
            //

            CurdirLength = GetFileAttributesW(CurdirBuffer);
            if ( (CurdirLength == 0xffffffff) ||
                 !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY) ) {
                SetLastError(ERROR_DIRECTORY);
                return FALSE;
                }
            }


        if ( QuoteInsert || QuoteCmdLine) {
            QuotedBuffer = RtlAllocateHeap(RtlProcessHeap(),0,wcslen(lpCommandLine)*2+6);

            if ( QuotedBuffer ) {
                wcscpy(QuotedBuffer,L"\"");

                if ( QuoteInsert ) {
                    TempChar = *TempNull;
                    *TempNull = UNICODE_NULL;
                    }

                wcscat(QuotedBuffer,lpCommandLine);
                wcscat(QuotedBuffer,L"\"");

                if ( QuoteInsert ) {
                    *TempNull = TempChar;
                    wcscat(QuotedBuffer,TempNull);
                    }

                }
            else {
                if ( QuoteInsert ) {
                    QuoteInsert = FALSE;
                    }
                if ( QuoteCmdLine ) {
                    QuoteCmdLine = FALSE;
                    }
                }
            }


        //其中实现中调用了RtlCreateProcessParameters来创建进程参数, 
        //该函数对RTL_USER_PROCESS_PARAMETERS结构中的字符串域的地址改为相对的偏移量。
        if (!BasePushProcessParameters(
                ProcessHandle,
                Peb,
                lpApplicationName,
                CurdirBuffer,
                QuoteInsert || QuoteCmdLine ? QuotedBuffer : lpCommandLine,
                lpEnvironment,
                &StartupInfo,
                dwCreationFlags | dwNoWindow,
                bInheritHandles,
                IsWowBinary ? IMAGE_SUBSYSTEM_WINDOWS_GUI : 0
                ) ) {
            return FALSE;
            }


        RtlFreeUnicodeString(&VdmNameString);
        VdmNameString.Buffer = NULL;

        //
        // Stuff in the standard handles if needed
        //
        if (!VdmBinaryType &&
            !bInheritHandles &&
            !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
            !(dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE | CREATE_NO_WINDOW)) &&
            ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI
           ) {
            PRTL_USER_PROCESS_PARAMETERS ParametersInNewProcess;

            Status = NtReadVirtualMemory( ProcessHandle,
                                          &Peb->ProcessParameters,
                                          &ParametersInNewProcess,
                                          sizeof( ParametersInNewProcess ),
                                          NULL
                                        );
            if (NT_SUCCESS( Status )) {
                if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardInput )) {
                    StuffStdHandle( ProcessHandle,
                                    NtCurrentPeb()->ProcessParameters->StandardInput,
                                    &ParametersInNewProcess->StandardInput
                                  );
                    }
                if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardOutput )) {
                    StuffStdHandle( ProcessHandle,
                                    NtCurrentPeb()->ProcessParameters->StandardOutput,
                                    &ParametersInNewProcess->StandardOutput
                                  );
                    }
                if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardError )) {
                    StuffStdHandle( ProcessHandle,
                                    NtCurrentPeb()->ProcessParameters->StandardError,
                                    &ParametersInNewProcess->StandardError
                                  );
                    }
                }
            }

        //
        // Create the thread...
        //

        //
        // Allocate a stack for this thread in the address space of the target
        // process.
        //

        StackStatus = BaseCreateStack(
                        ProcessHandle,
                        ImageInformation.CommittedStackSize,
                        (ImageInformation.MaximumStackSize < 256*1024) ? 256*1024 : ImageInformation.MaximumStackSize,//256k?????
                        &InitialTeb
                        );

        if ( !NT_SUCCESS(StackStatus) ) {
            BaseSetLastNTError(StackStatus);
            return FALSE;
            }


        //
        // Create an initial context for the new thread.
        //
        //初始化线程上下文
        //在BaseInitializeContext函数的实现中会判断参数上下文类型是否为1,如果不为1, 指定用户空间的线程启动函数为BaseProcessStartThunk(进程的第一个线程调用这个)
        //否则为BaseThreadStartThunk(普通线程),然后CALL OEP。其中BaseProcessStartThunk函数会调用BaseProcessStart,这个函数就是调用OEP所在函数的上层函数
        BaseInitializeContext(
            &ThreadContext,
            Peb,
            ImageInformation.TransferAddress,
            InitialTeb.StackBase,
            BaseContextTypeProcess
            );


        //
        // Create the actual thread object
        //

        //格式化对象(以便传递给NtCreateThread)
        pObja = BaseFormatObjectAttributes(&Obja,lpThreadAttributes,NULL);

        Status = NtCreateThread(
                    &ThreadHandle,
                    THREAD_ALL_ACCESS,
                    pObja,
                    ProcessHandle,
                    &ClientId,
                    &ThreadContext,
                    &InitialTeb,
                    TRUE
                    );

        if (!NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            return FALSE;
            }

        //
        // From here on out, do not modify the address space of the
        // new process.  WOW64's implementation of NtCreateThread()
        // reshuffles the new process' address space if the current
        // process is 32-bit and the new process is 64-bit.
        //
#if DBG
        Peb = NULL;
#endif

#if defined(WX86) || defined(_AXP64_)

        //
        // if this is a Wx86 Process, setup for a Wx86 emulated Thread
        //

        if (Wx86Info) {

            //
            // create a WX86Tib and initialize it's Teb->Vdm.
            //
            Status = BaseCreateWx86Tib(ProcessHandle,
                                       ThreadHandle,
                                       (ULONG)((ULONG_PTR)ImageInformation.TransferAddress),
                                       (ULONG)ImageInformation.CommittedStackSize,
                                       (ULONG)ImageInformation.MaximumStackSize,
                                       TRUE
                                       );

            if (!NT_SUCCESS(Status)) {
                BaseSetLastNTError(Status);
                return( FALSE );
                }


            //
            // Mark Process as WX86
            //
            Status = NtSetInformationProcess (ProcessHandle,
                                              ProcessWx86Information,
                                              &Wx86Info,
                                              sizeof(Wx86Info)
                                              );

            if (!NT_SUCCESS(Status)) {
                BaseSetLastNTError(Status);
                return( FALSE );
                }
            }
#endif


        //
        // Call the Windows server to let it know about the
        // process.
        //

        a->ProcessHandle = ProcessHandle;
        a->ThreadHandle = ThreadHandle;
        a->ClientId = ClientId;
        a->CreationFlags = dwCreationFlags;

        if ( dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) ) {
            Status = DbgUiConnectToDbg();
            if ( !NT_SUCCESS(Status) ) {
                NtTerminateProcess(ProcessHandle, Status);
                BaseSetLastNTError(Status);
                return FALSE;
                }
            a->DebuggerClientId = NtCurrentTeb()->ClientId;
            }
        else {
            a->DebuggerClientId.UniqueProcess = NULL;
            a->DebuggerClientId.UniqueThread = NULL;
            }

        //
        // Set the 2 bit if a gui app is starting. The window manager needs to
        // know this so it can synchronize the startup of this app
        // (WaitForInputIdle api). This info is passed using the process
        // handle tag bits.  The 1 bit asks the window manager to turn on
        // or turn off the application start cursor (hourglass/pointer).
        //
        // When starting a WOW process, lie and tell UserSrv NTVDM.EXE is a GUI
        // process.  We also turn on bit 0x8 so that UserSrv can ignore the
        // UserNotifyConsoleApplication call made by the console during startup.
        //

        if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI ||
             IsWowBinary ) {

            a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 2);

            //
            // If the creating process is a GUI app, turn on the app. start cursor
            // by default.  This can be overridden by STARTF_FORCEOFFFEEDBACK.
            //

            NtHeaders = RtlImageNtHeader((PVOID)GetModuleHandle(NULL));
            if ( NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI )
                a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1);

            }


        //
        // If feedback is forced on, turn it on. If forced off, turn it off.
        // Off overrides on.
        //

        if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
            a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1);
        if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
            a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle & ~1);

        a->VdmBinaryType = VdmBinaryType; // just tell server the truth

        if (VdmBinaryType){
           a->hVDM    = iTask ? 0 : NtCurrentPeb()->ProcessParameters->ConsoleHandle;
           a->VdmTask = iTask;
        }

#if defined(BUILD_WOW6432)
        m.ReturnValue = CsrBasepCreateProcess(a);
#else
        CsrClientCallServer( (PCSR_API_MSG)&m,
                             NULL,
                             CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
                                                  BasepCreateProcess
                                                ),
                             sizeof( *a )
                           );
#endif

        if (!NT_SUCCESS((NTSTATUS)m.ReturnValue)) {
            BaseSetLastNTError((NTSTATUS)m.ReturnValue);
            NtTerminateProcess(ProcessHandle, (NTSTATUS)m.ReturnValue);
            return FALSE;
            }


        if (!( dwCreationFlags & CREATE_SUSPENDED) ) {
            NtResumeThread(ThreadHandle,&i);
            }

VdmExists:
        bStatus = TRUE;
        if (VDMCreationState)
            VDMCreationState |= VDM_CREATION_SUCCESSFUL;

        try {
            if (VdmWaitHandle) {

                //
                // tag Shared WOW VDM handles so that wait for input idle has a
                // chance to work.  Shared WOW VDM "process" handles are actually
                // event handles,  Separate WOW VDM handles are real process
                // handles. Also mark DOS handles with 0x1 so WaitForInputIdle
                // has a way to distinguish DOS apps and not block forever.
                //

                if (VdmBinaryType == BINARY_TYPE_WIN16)  {
                    lpProcessInformation->hProcess =
                            (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x2);

                    //
                    // Shared WOW doesn't always start a process, so
                    // we don't have a process ID or thread ID to
                    // return if the VDM already existed.
                    //
                    // Separate WOW doesn't hit this codepath
                    // (no VdmWaitHandle).
                    //

                    if (VDMCreationState & VDM_BEING_REUSED) {
                        ClientId.UniqueProcess = 0;
                        ClientId.UniqueThread = 0;
                        }

                    }
                else  {
                    lpProcessInformation->hProcess =
                            (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x1);
                    }


                //
                // Close the ProcessHandle, since we are returning the
                // VdmProcessHandle instead.
                //

                if (ProcessHandle != NULL)
                    NtClose(ProcessHandle);
                }
            else{
                lpProcessInformation->hProcess = ProcessHandle;
                }

            lpProcessInformation->hThread = ThreadHandle;
            lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
            lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
            ProcessHandle = NULL;
            ThreadHandle = NULL;
            }
        except ( EXCEPTION_EXECUTE_HANDLER ) {
            NtClose( ProcessHandle );
            NtClose( ThreadHandle );
            ProcessHandle = NULL;
            ThreadHandle = NULL;
            if (VDMCreationState)
                VDMCreationState &= ~VDM_CREATION_SUCCESSFUL;
            }
        }
    finally {
        if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
            RtlDestroyEnvironment(lpEnvironment);
            lpEnvironment = NULL;
            }
        RtlFreeHeap(RtlProcessHeap(), 0,QuotedBuffer);
        RtlFreeHeap(RtlProcessHeap(), 0,NameBuffer);
        RtlFreeHeap(RtlProcessHeap(), 0,CurdirBuffer);
        RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
        if ( FileHandle ) {
            NtClose(FileHandle);
            }
        if ( SectionHandle ) {
            NtClose(SectionHandle);
            }
        if ( ThreadHandle ) {
            NtTerminateProcess(ProcessHandle,STATUS_SUCCESS);
            NtClose(ThreadHandle);
            }
        if ( ProcessHandle ) {
            NtClose(ProcessHandle);
            }
        RtlFreeUnicodeString(&VdmNameString);
        RtlFreeUnicodeString(&SubSysCommandLine);
        if (AnsiStringVDMEnv.Buffer || UnicodeStringVDMEnv.Buffer)
            BaseDestroyVDMEnvironment(&AnsiStringVDMEnv, &UnicodeStringVDMEnv);

        if (VDMCreationState && !(VDMCreationState & VDM_CREATION_SUCCESSFUL)){
            BaseUpdateVDMEntry (
                UPDATE_VDM_UNDO_CREATION,
                (HANDLE *)&iTask,
                VDMCreationState,
                VdmBinaryType
                );
            if(VdmWaitHandle) {
                NtClose(VdmWaitHandle);
                }
            }
        }

    if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
        RtlDestroyEnvironment(lpEnvironment);
        }
    return bStatus;
}
View Code

 NtCreateProcess与PspCreateProcess源代码:

NTSTATUS
NtCreateProcess(
    OUT PHANDLE ProcessHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
    IN HANDLE ParentProcess,
    IN BOOLEAN InheritObjectTable,
    IN HANDLE SectionHandle OPTIONAL,
    IN HANDLE DebugPort OPTIONAL,
    IN HANDLE ExceptionPort OPTIONAL
    )

/*++

Routine Description:

    This routine creates a process object.

Arguments:

    ProcessHandle - Returns the handle for the new process.

    DesiredAccess - Supplies the desired access modes to the new process.

    ObjectAttributes - Supplies the object attributes of the new process.
    .
    .
    .

Return Value:

    TBD

--*/

{
    NTSTATUS st;

    PAGED_CODE();

    if ( KeGetPreviousMode() != KernelMode ) {

        //
        // Probe all arguments
        //

        try {
            ProbeForWriteHandle(ProcessHandle);
        } except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode();
        }
    }
    
    //判断父进程是否为空,不为空调用PspCreateProcess,为空返回失败
    if ( ARGUMENT_PRESENT(ParentProcess) ) {
        st = PspCreateProcess(
                ProcessHandle,
                DesiredAccess,
                ObjectAttributes,
                ParentProcess,
                InheritObjectTable,
                SectionHandle,
                DebugPort,
                ExceptionPort
                );
    } else {
        st = STATUS_INVALID_PARAMETER;
    }

    return st;
}



NTSTATUS
PspCreateProcess(
    OUT PHANDLE ProcessHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
    IN HANDLE ParentProcess OPTIONAL,
    IN BOOLEAN InheritObjectTable,
    IN HANDLE SectionHandle OPTIONAL,
    IN HANDLE DebugPort OPTIONAL,
    IN HANDLE ExceptionPort OPTIONAL
    )

/*++

Routine Description:

    This routine creates and initializes a process object.  It implements the
    foundation for NtCreateProcess and for system initialization process
    creation.

Arguments:

    ProcessHandle - Returns the handle for the new process.

    DesiredAccess - Supplies the desired access modes to the new process.

    ObjectAttributes - Supplies the object attributes of the new process.

    ParentProcess - Supplies a handle to the process' parent process.  If this
                    parameter is not specified, then the process has no parent
                    and is created using the system address space.

    SectionHandle - Supplies a handle to a section object to be used to create
                    the process' address space.  If this parameter is not
                    specified, then the address space is simply a clone of the
                    parent process' address space.

    DebugPort - Supplies a handle to a port object that will be used as the
                process' debug port.

    ExceptionPort - Supplies a handle to a port object that will be used as the
                    process' exception port.

Return Value:

    TBD

--*/

{
    NTSTATUS st;
    PEPROCESS Process;
    PEPROCESS Parent;
    KAFFINITY Affinity;
    KPRIORITY BasePriority;
    PVOID SectionToMap;
    PVOID ExceptionPortObject;
    PVOID DebugPortObject;
    ULONG WorkingSetMinimum, WorkingSetMaximum;
    HANDLE LocalProcessHandle;
    KPROCESSOR_MODE PreviousMode;
    HANDLE NewSection;
    NTSTATUS DuplicateStatus;
    INITIAL_PEB InitialPeb;
    BOOLEAN CreatePeb;
    ULONG_PTR DirectoryTableBase[2];
    BOOLEAN AccessCheck;
    BOOLEAN MemoryAllocated;
    PSECURITY_DESCRIPTOR SecurityDescriptor;
    SECURITY_SUBJECT_CONTEXT SubjectContext;
    NTSTATUS accesst;
    NTSTATUS savedst;
    BOOLEAN BreakAwayRequested;
    PUNICODE_STRING AuditName = NULL ;

    PAGED_CODE();

    BreakAwayRequested = FALSE;
    CreatePeb = FALSE;
    DirectoryTableBase[0] = 0;
    DirectoryTableBase[1] = 0;
    PreviousMode = KeGetPreviousMode();//保存当前线程运行的前一个模式

    //
    // Parent
    //

    if (ARGUMENT_PRESENT(ParentProcess) ) {
        st = ObReferenceObjectByHandle(//得到父进程对象指针
                ParentProcess,
                PROCESS_CREATE_PROCESS,
                PsProcessType,
                PreviousMode,
                (PVOID *)&Parent,
                NULL
                );
        if ( !NT_SUCCESS(st) ) {
            return st;
        }

        //
        // Until CSR understands priority class, don't
        // inherit base priority. This just makes things
        // worse !
        //

        BasePriority = (KPRIORITY) NORMAL_BASE_PRIORITY;

        //
        //BasePriority = Parent->Pcb.BasePriority;
        //

        Affinity = Parent->Pcb.Affinity;

        WorkingSetMinimum = PsMinimumWorkingSet;              // FIXFIX
        WorkingSetMaximum = PsMaximumWorkingSet;


    } else {

        Parent = NULL;
        Affinity = KeActiveProcessors;
        BasePriority = (KPRIORITY) NORMAL_BASE_PRIORITY;

        WorkingSetMinimum = PsMinimumWorkingSet;              // FIXFIX
        WorkingSetMaximum = PsMaximumWorkingSet;
    }

    //
    // Section
    //

    if (ARGUMENT_PRESENT(SectionHandle) ) {

        //
        // Use Object manager tag bits to indicate that breakaway is
        // desired
        //

        if ( (UINT_PTR)SectionHandle & 1 ) {
            BreakAwayRequested = TRUE;
            }

        st = ObReferenceObjectByHandle(//得到SectionHandle(然后将节区对象指针赋值给新进程EPROCESS的相应域中)
                SectionHandle,
                SECTION_MAP_EXECUTE,
                MmSectionObjectType,
                PreviousMode,
                (PVOID *)&SectionToMap,
                NULL
                );
        if ( !NT_SUCCESS(st) ) {
            if (Parent) {
                ObDereferenceObject(Parent);
            }
            return st;
        }
    } else {
        SectionToMap = NULL;
    }

    //
    // DebugPort
    //

    if (ARGUMENT_PRESENT(DebugPort) ) {
        st = ObReferenceObjectByHandle (
                DebugPort,
                0,
                LpcPortObjectType,
                KeGetPreviousMode(),
                (PVOID *)&DebugPortObject,
                NULL
                );
        if ( !NT_SUCCESS(st) ) {
            if (Parent) {
                ObDereferenceObject(Parent);
            }
            if (SectionToMap) {
                ObDereferenceObject(SectionToMap);
            }
            return st;
        }
    } else {
        DebugPortObject = NULL;
    }

    //
    // ExceptionPort
    //

    if (ARGUMENT_PRESENT(ExceptionPort) ) {
        st = ObReferenceObjectByHandle (
                ExceptionPort,
                0,
                LpcPortObjectType,
                KeGetPreviousMode(),
                (PVOID *)&ExceptionPortObject,
                NULL
                );
        if ( !NT_SUCCESS(st) ) {
            if (Parent) {
                ObDereferenceObject(Parent);
            }
            if (SectionToMap) {
                ObDereferenceObject(SectionToMap);
            }
            if (DebugPortObject) {
                ObDereferenceObject(DebugPortObject);
            }

            return st;
        }
    } else {
        ExceptionPortObject = NULL;
    }

    //创建新进程对象并将对象内容初始化为0
    st = ObCreateObject(
           KeGetPreviousMode(),
           PsProcessType,
           ObjectAttributes,
           KeGetPreviousMode(),
           NULL,
           (ULONG) sizeof(EPROCESS),
           0,
           0,
           (PVOID *)&Process
           );
    if ( !NT_SUCCESS( st ) ) {
        if (Parent) {
            ObDereferenceObject(Parent);
        }
        if (SectionToMap) {
            ObDereferenceObject(SectionToMap);
        }
        if (DebugPortObject) {
            ObDereferenceObject(DebugPortObject);
        }
        if (ExceptionPortObject) {
            ObDereferenceObject(ExceptionPortObject);
        }
        return st;
    }

    //
    // The process object is created set to NULL. Errors
    // That occur after this step cause the process delete
    // routine to be entered.
    //
    // Teardown actions that occur in the process delete routine
    // do not need to be performed inline.
    //

    RtlZeroMemory(Process,sizeof(EPROCESS));

    InitializeListHead(&Process->ThreadListHead);
    Process->CreateProcessReported = FALSE;
    Process->DebugPort = DebugPortObject;
    Process->ExceptionPort = ExceptionPortObject;


    PspInheritQuota(Process,Parent);
    ObInheritDeviceMap(Process,Parent);

    if ( Parent ) {
        Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;
        Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
        Process->SessionId = Parent->SessionId;

    } else {
        Process->DefaultHardErrorProcessing = 1;
        Process->InheritedFromUniqueProcessId = NULL;
    }

    Process->ExitStatus = STATUS_PENDING;
    Process->LockCount = 1;
    Process->LockOwner = NULL;
    KeInitializeEvent(&Process->LockEvent, SynchronizationEvent, FALSE);

    //
    //  Initialize the security fields of the process
    //  The parent may be null exactly once (during system init).
    //  Thereafter, a parent is always required so that we have a
    //  security context to duplicate for the new process.
    //

    st = PspInitializeProcessSecurity( Parent, Process );//设置新进程的安全属性,主要是设置新进程的安全令牌对象(从父进程拷贝)

    if (!NT_SUCCESS(st)) {


        if ( Parent ) {
            ObDereferenceObject(Parent);
        }

        if (SectionToMap) {
            ObDereferenceObject(SectionToMap);
        }

        ObDereferenceObject(Process);
        return st;
    }

    //
    // Clone parent's object table.
    // If no parent (booting) then use the current object table created in
    // ObInitSystem.
    //

    if (Parent) {


        //
        // Calculate address space
        //
        //      If Parent == PspInitialSystem
        //

        //为新进程创建地址空间,并构建页目录表,页表及物理页的关系(参考Windows内核情景分析)
        if (!MmCreateProcessAddressSpace(WorkingSetMinimum,
                                         Process,
                                         &DirectoryTableBase[0])) {

            ObDereferenceObject(Parent);
            if (SectionToMap) {
                ObDereferenceObject(SectionToMap);
            }

            PspDeleteProcessSecurity( Process );

            ObDereferenceObject(Process);
            return STATUS_INSUFFICIENT_RESOURCES;
        }

    } else {

        Process->ObjectTable = PsGetCurrentProcess()->ObjectTable;

        DirectoryTableBase[0] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[0];
        DirectoryTableBase[1] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[1];

        //
        // Initialize the Working Set Mutex and address creation mutex
        // for this "hand built" process.
        // Normally, the call the MmInitializeAddressSpace initializes the
        // working set mutex, however, in this case, we have already initialized
        // the address space and we are now creating a second process using
        // the address space of the idle thread.
        //

        //KeInitializeMutant(&Process->WorkingSetLock, FALSE);
        ExInitializeFastMutex(&Process->WorkingSetLock);

        ExInitializeFastMutex(&Process->AddressCreationLock);

        KeInitializeSpinLock (&Process->HyperSpaceLock);

        //
        // Initialize virtual address descriptor root.
        //

        ASSERT (Process->VadRoot == NULL);
        Process->Vm.WorkingSetSize = PsGetCurrentProcess()->Vm.WorkingSetSize;
        KeQuerySystemTime(&Process->Vm.LastTrimTime);
        Process->Vm.VmWorkingSetList = MmWorkingSetList;
    }

    Process->Vm.MaximumWorkingSetSize = WorkingSetMaximum;

    //初始化新进程对象中内核对象,优先级,亲和性,页目录表物理地址帧号
    KeInitializeProcess(
        &Process->Pcb,
        BasePriority,
        Affinity,
        &DirectoryTableBase[0],
        (BOOLEAN)(Process->DefaultHardErrorProcessing & PROCESS_HARDERROR_ALIGNMENT_BIT)
        );
    Process->Pcb.ThreadQuantum = PspForegroundQuantum[0];
    Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;

    if (Parent) {

        //
        // this used to happen in basesrv\srvtask.c
        //
        if ( Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
             Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL ) {
             Process->PriorityClass = Parent->PriorityClass;
             }
        //
        // if address space creation worked, then when going through
        // delete, we will attach. Of course, attaching means that the kprocess
        // must be initialized, so we delay the object stuff till here.

        //初始化新进程对象的表,如果父进程被指定,父进程的对象表拷贝到新进程中, 对象表 的每个对象中HandleCount域都+1
        st = ObInitProcess(InheritObjectTable ? Parent : (PEPROCESS)NULL,Process);

        if (!NT_SUCCESS(st)) {

            ObDereferenceObject(Parent);
            if (SectionToMap) {
                ObDereferenceObject(SectionToMap);
            }

            PspDeleteProcessSecurity( Process );
            ObDereferenceObject(Process);
            return st;
        }
    }

    st = STATUS_SUCCESS;
    savedst = STATUS_SUCCESS;

    //
    // Initialize the process address space
    // The address space has four possibilities
    //
    //      1 - Boot Process. Address space is initialized during
    //          MmInit. Parent is not specified.
    //
    //      2 - System Process. Address space is a virgin address
    //          space that only maps system space. Process is same
    //          as PspInitialSystemProcess.
    //
    //      3 - User Process (Cloned Address Space). Address space
    //          is cloned from the specified process.
    //
    //      4 - User Process (New Image Address Space). Address space
    //          is initialized so that it maps the specified section.
    //

    if ( SectionToMap ) {
        //
        // User Process (New Image Address Space). Don't specify Process to
        // clone, just SectionToMap.
        //
        
        //初始化进程地址空间(该函数的实现中调用了KiAttachProcess函数实现进程的切换,以及初始化EPROCESS中的部分域和PFN,工作集列表等
        st = MmInitializeProcessAddressSpace(
                Process,
                NULL,
                SectionToMap,
                &AuditName
                );

        ObDereferenceObject(SectionToMap);
        ObInitProcess2(Process);

        if ( NT_SUCCESS(st) ) {

            //
            // In order to support relocating executables, the proper status
            // (STATUS_IMAGE_NOT_AT_BASE) must be returned, so save it here.
            //

            savedst = st;
            //映射指定进程的节区对象(映射第一个DLL)
            st = PspMapSystemDll(Process,NULL);
        }

        CreatePeb = TRUE;

        goto insert_process;
    }

    if ( Parent ) {

        if ( Parent != PsInitialSystemProcess ) {

            Process->SectionBaseAddress = Parent->SectionBaseAddress;

            //
            // User Process ( Cloned Address Space ).  Don't specify section to
            // map, just Process to clone.
            //

            st = MmInitializeProcessAddressSpace(
                    Process,
                    Parent,
                    NULL,
                    NULL
                    );

            CreatePeb = TRUE;

        } else {

            //
            // System Process.  Don't specify Process to clone or section to map
            //

            st = MmInitializeProcessAddressSpace(
                    Process,
                    NULL,
                    NULL,
                    NULL
                    );
        }
    }

insert_process:

    //
    // If MmInitializeProcessAddressSpace was NOT successful, then
    // dereference and exit.
    //

    if ( !NT_SUCCESS(st) ) {

        if (Parent) {
            ObDereferenceObject(Parent);
        }

        KeAttachProcess(&Process->Pcb);
        ObKillProcess(FALSE, Process);
        KeDetachProcess();

        PspDeleteProcessSecurity( Process );
        ObDereferenceObject(Process);
        return st;
    }

    //
    // Reference count of process is not biased here. Each thread in the
    // process bias the reference count when they are created.
    //

    //插入一个对象到当前进程的句柄表,并返回该对象的句柄值
    st = ObInsertObject(
            Process,
            NULL,
            DesiredAccess,
            0,
            (PVOID *)NULL,
            &LocalProcessHandle
            );


    if ( !NT_SUCCESS(st) ) {
        if (Parent) {
            ObDereferenceObject(Parent);
        }
        return st;
    }

    //
    // See if the parent has a job. If so reference the job
    // and add the process in.
    //

    if ( Parent && Parent->Job && !(Parent->Job->LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK) ) {

        if ( BreakAwayRequested ) {
            if ( !(Parent->Job->LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) ) {
                st = STATUS_ACCESS_DENIED;
                if (Parent) {
                    ObDereferenceObject(Parent);
                    }
                ZwClose(LocalProcessHandle);
                return st;
                }
            }
        else {
            ObReferenceObject(Parent->Job);
            Process->Job = Parent->Job;
            st = PspAddProcessToJob(Process->Job,Process);
            if ( !NT_SUCCESS(st) ) {
                if (Parent) {
                    ObDereferenceObject(Parent);
                    }
                ZwClose(LocalProcessHandle);
                return st;
                }
            }
        }

    PsSetProcessPriorityByClass(Process,PsProcessPriorityBackground);

    ExAcquireFastMutex(&PspActiveProcessMutex);
    InsertTailList(&PsActiveProcessHead,&Process->ActiveProcessLinks);
    ExReleaseFastMutex(&PspActiveProcessMutex);

    if (Parent && CreatePeb ) {

        //
        // For processes created w/ a section,
        // a new "virgin" PEB is created. Otherwise,
        // for forked processes, uses inherited PEB
        // with an updated mutant.

        RtlZeroMemory(&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant));
        InitialPeb.Mutant = (HANDLE)(-1);
        if ( SectionToMap ) {

            try {
                //创建PEB(将线程切换到目标进程后,创建PEB结构及初始化部分域,将映射镜像文件中IMAGE_DIRECTORY_LOAD_CONFIG节)
                Process->Peb = MmCreatePeb(Process,&InitialPeb);
            } except(EXCEPTION_EXECUTE_HANDLER) {
                ObDereferenceObject(Parent);
                ZwClose(LocalProcessHandle);
                return GetExceptionCode();
            }

        } else {

            InitialPeb.InheritedAddressSpace = TRUE;

            Process->Peb = Parent->Peb;

            ZwWriteVirtualMemory(
                LocalProcessHandle,
                Process->Peb,
                &InitialPeb,
                sizeof(INITIAL_PEB),
                NULL
                );
        }

        //
        // The new process should have a handle to its
        // section. The section is either from the specified
        // section, or the section of its parent.
        //

        if ( ARGUMENT_PRESENT(SectionHandle) ) {
            DuplicateStatus = ZwDuplicateObject(
                                NtCurrentProcess(),
                                SectionHandle,
                                LocalProcessHandle,
                                &NewSection,
                                0L,
                                0L,
                                DUPLICATE_SAME_ACCESS
                                );
        } else {

            DuplicateStatus = ZwDuplicateObject(
                                ParentProcess,
                                Parent->SectionHandle,
                                LocalProcessHandle,
                                &NewSection,
                                0L,
                                0L,
                                DUPLICATE_SAME_ACCESS
                                );
        }

        if ( NT_SUCCESS(DuplicateStatus) ) {
            Process->SectionHandle = NewSection;
        }

        ObDereferenceObject(Parent);
    }

    if ( Parent && ParentProcess != PspInitialSystemProcessHandle ) {

        st = ObGetObjectSecurity(
                Process,
                &SecurityDescriptor,
                &MemoryAllocated
                );
        if ( !NT_SUCCESS(st) ) {
            ZwClose(LocalProcessHandle);
            return st;
            }

        //
        // Compute the subject security context
        //

        SubjectContext.ProcessAuditId = Process;
        SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
        SubjectContext.ClientToken = NULL;
        AccessCheck = SeAccessCheck(
                        SecurityDescriptor,
                        &SubjectContext,
                        FALSE,
                        MAXIMUM_ALLOWED,
                        0,
                        NULL,
                        &PsProcessType->TypeInfo.GenericMapping,
                        PreviousMode,
                        &Process->GrantedAccess,
                        &accesst
                        );
        PsDereferencePrimaryToken(SubjectContext.PrimaryToken);
        ObReleaseObjectSecurity(
            SecurityDescriptor,
            MemoryAllocated
            );

        if ( !AccessCheck ) {
            Process->GrantedAccess = 0;
            }

        //
        // It does not make any sense to create a process that can not
        // do anything to itself
        //

        Process->GrantedAccess |= (PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS | PROCESS_SET_INFORMATION);

    } else {
        Process->GrantedAccess = PROCESS_ALL_ACCESS;
    }

    if ( SeDetailedAuditing ) {

        SeAuditProcessCreation( Process, Parent, AuditName );
    } 

    KeQuerySystemTime(&Process->CreateTime);

    try {
        *ProcessHandle = LocalProcessHandle;
    } except(EXCEPTION_EXECUTE_HANDLER) {
        return st;
    }

    if (savedst != STATUS_SUCCESS) {
        st = savedst;
    }

    return st;

}
View Code

 

posted @ 2018-01-21 14:38  WhiteLearner  阅读(2115)  评论(0编辑  收藏  举报