整理CreateProcessW

前言:shell

通過好幾回對CreateProcess的學習,以前老是學到一半就放棄,學一半就放棄,此次總算堅持了下來 -、-。windows

此次學習主要參考三個資料:api

1.Windows2000源代碼安全

2.毛德操老師的《Windows內核情景分析》數據結構

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

三個資料講的大致相同,可是有些許差異,有不一樣我主要都是按照源代碼來解釋了。(大佬們能夠本身在Win10上寫一個CreateProcess,而後反彙編調試一發。。。。弱雞就飄過了。。。)less

 

 

正文:ide

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
相關文章
相關標籤/搜索