前言:
經過好幾次對CreateProcess的學習,之前總是學到一半就放棄,學一半就放棄,這次總算堅持了下來 -、-。
這次學習主要參考三個資料:
1.Windows2000源代碼
2.毛德操老師的《Windows內核情景分析》
3.一個大佬的博客:https://bbs.pediy.com/thread-114611.htm
三個資料講的大體相同,但是有些許差別,有不同我主要都是按照源代碼來解釋了。(大佬們可以自己在Win10上寫一個CreateProcess,然后反匯編調試一發。。。。弱雞就飄過了。。。)
正文:
WIN32API函數CreateProcess用來創建一個新的進程和它的主線程,這個新進程運行指定的可執行文件。
創建進程的過程就是構建一個環境,這個環境包含了很多的機制 (比如自我保護, 與外界通信等等)。 構建這個環境需要兩種“人”來協調完成(用戶態和內核態),他們各有分工,其中用戶態提供原料(提供創建的那些參數), 內核態負責來構建這個環境,由於環境是由內核態構建的,因此他持有這個環境的控制權, 而用戶由於提供了原料, 因此他具有使用權。 內核態開始構建環境中的基礎設施(進程對象,等等),在構建完基礎設施后,內核態通知用戶態基礎設施構建已經完成,是否需要繼續構建其他設施,於是用戶態通知內核態繼續構建一條通道(既創建線程),方便兩邊的通信,當用戶態接收到線程創建完畢的信息后,便可以開始使用這個環境(投入生產),以后缺啥補啥。
大家都知道,很久之前沒有線程,進程做了一切工作,但是后來MicroSoft發現這並不行,就又搞了線程出來,從此進程成為系統進行資源分配和調度的一個獨立單位,而線程成為進程的一個實體,是CPU調度和分派的基本單位,是比進程更小的能獨立運行的基本單位。(就是進程拿着錢而線程去花+.+)所以創建進程時就不僅要創建進程,還要創建線程。
分兩部分說明CreatProcess的原理:第一部分為進程,第二部分為線程。
一.進程
1.1Ring3層
 
1.2Ring0層
 
二:創建線程:
Ring3 + Ring0

進程創建詳細步驟(Windows內核情景分析):
第一階段:打開目標映像文件
首先打開映像文件,再為其(分配)創建一個“section”即文件映射區,創建文件映射區的目的當然是要把映像文件的內容映射到這個區間,不過首先要檢查已經打開的目標文件是否為一個合格的exe映像,此外,還要查詢“注冊表”中的這個路徑:HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options。如果上述路徑下有以目標映像文件的文件名和擴展名為“鍵”的表項,例如"Image.exe",而表項中又有名為"Debugger"的值,那么這個值(一個字符串)就替換了原來的目標文件名,變成新的目標映像名,並重新執行上述的第一階段操作。如果黑客或某個木馬程序設法在注冊表中加上了這么一個表項,則用戶以為是啟動了進程A,而實際啟動的卻是B。
第二階段:創建內核中的進程對象
所謂創建內核中的進程對象,實際上就是創建以EPROCESS為核心的相關數據結構,這就是系統調用NtCreateProcess()要做的事情,我們已在前面看過其代碼了,主要包括:
1.分配並設置EPROCESS數據結構。
2.其他相關的數據結構的設置,例如句柄表。
3.為目標進程創建初始的地址空間。
4.對目標進程的“內核進程塊”KPROCESS進行初始化,這是EPROCESS的一部分。
5.將目標進程的可執行映像映射到其自身的用戶空間。
6.將系統DLL的映像映射到目標進程的用戶空間。//ntdll.dll映射到目標進程的用戶空間
7.設置好目標進程的“進程環境快”PEB
8.映射其他需要映射到用戶空間的數據結構,例如與“當地語言支持”即NLS有關的數據結構。
9.完成EPROCESS創建,將其掛入進程隊列並插入創建者的句柄表。
第三階段:創建初始線程
進程只是個容器,實際的運行實體是里面的線程。所以下一步就是創建目標進程的初始線程,即其第一個線程。
與EPROCESS相對應,線程的數據結構是ETHREAD,其第一個成分則是KTHREAD數據結構,稱為Tcb。此外,就像進程有“進程環境塊”PEB一樣,線程也有“線程環境塊”TEB,KTHREAD結構中有個指針指向其存在於用戶空間的TEB。PEB在用戶空間的位置是固定的,PEB下方就是TEB,進程中有幾個線程就有幾個TEB,每個TEB占一個4Kb的頁面。NtCreateThread():
1.創建和設置目標線程的ETHREAD數據結構。
2.在目標進程的用戶空間創建並設置目標線程的TEB。
3.將目標線程在用戶空間的起始地址設置成指向Kernel32.dll中的BaseProcessStartThunk()或BaseThreadStartupThunk(),前者用於進程中的第一個線程,后者用於隨后的線程。用戶程序在調用NtCreateThread()時也要提供一個用戶級的起始函數(地址),BaseProcessStartThunk()和BaseThreadStartThunk()在完成初始化時會調用這個起始函數。
4.設置目標線程的KTHREAD數據結構並為其分配堆棧。特別的,將其上下文中的斷點(返回點)設置成指向內核中的一段程序KiThreadStartup,使得該線程一旦被調度運行時就從這里開始執行。
5.如果登記了每當創建線程時就應加以調用的“通知”函數,就調用這些函數。
第四階段:通知Windows子系統
當初在設計的時候還是考慮了對不同“平台”的支持,即在同一個內核的基礎上配以不同的外圍軟件,形成不同的應用軟件運行環境,微軟稱之為“子系統”。創建Windows進程時還要通知csrss,因為它擔負着對所有Windows進程進行管理的責任,另一方面,csrss在接到通知以后就會在屏幕上顯示那個沙漏狀的光標(如果這是個有窗口的進程的話)。注意這里向csrss發出通知的是CreateProcess()調用者,而不是新創建出來的進程(它還沒有開始運行)。
至此CreateProcess()的操作已經完成,CreateProcess()的調用者從該函數返回,就回到了應用程序或更高層的DLL中。這四個階段都是立足於創建者進程的用戶空間,在整個過程中進程了多次系統調用,每次系統調用完成后都回到了用戶空間中。現在雖然創建者進程已經從庫函數CreateProcess()返回了,子進程中的線程卻尚未開始運行,他的運行還需要經歷下面的第五第六階段。
第五階段:啟動起始線程
新創建的線程未必是可以被立即調度運行的,因為用戶可能在創建時把標志位CREATE_SUSPENDED設成了1。如果那樣的話,就需要等待別的線程通過系統調用“恢復(Resume)”其運行資格以后才可以被調度運行。否則就是一經創建便可以被調度運行了。至於什么時候才會真正被調度運行,則要看優先級等條件了。而一旦被調度運行,那就是以新建進程的身份在運行,與CreateProcess()的調用者無關了。
當(用戶空間)進程的第一個線程首次受調度運行時,由於其(系統空間)堆棧內容的設置,首先執行的是KiThreadStartup()。這段程序把目標線程的運行級別從DPC級降低到APC級,然后調用內核函數PspUserThreadStartup()。
最后PspUserThreadStartup()把用戶空間ntdll.dll中的函數LdrInitializeThunk()作為APC函數掛入APC隊列,再企圖“返回”到用戶空間。
於是此時的CPU將兩次進入用戶空間。第一次是因為APC請求的存在而進入用戶空間,執行APC函數LdrInitializeThunk(),執行完畢仍回到系統控劍。然后,第二次進入用戶空間才正式“返回”到用戶空間。返回到Kernel32.dll中的BaseProcessStartThunk()或BaseThreadStartupThunk(),對於進程中的第一個線程是前者,對於后來的線程則是后者。至於用戶程序所提供的(線程)入口,則是作為參數提供給這兩個函數的,這兩個函數都會使用該指針調用有用戶提供的入口函數。
第六階段:用戶控件的初始化和DLL的連接
用戶空間的初始化和DLL的動態連接是由APC函數LdrInitializeThunk()在用戶空間完成的。LdrInitializeThunk()是ntdll.dll的一個函數。在此之前ntdll.dll已經被映射到了用戶空間,但是其他的DLL尚未裝入,應用軟件與DLL之前也尚未建立動態連接。函數LdrInitializeThunk()在映像中的位置是預定的,所以在進入這個函數之前並不需要連接。
CreateProcessW源代碼(含注釋)
 
          
         BOOL WINAPI CreateProcessW( LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) /*++ Routine Description: A process and thread object are created and a handle opened to each object using CreateProcess. Note that WinExec and LoadModule are still supported, but are implemented as a call to CreateProcess. Arguments: lpApplicationName - Supplies an optional pointer to a null terminated character string that contains the name of the image file to execute. This is a fully qualified DOS path name. If not specified, then the image file name is the first whitespace delimited token on the command line. lpCommandLine - Supplies a null terminated character string that contains the command line for the application to be executed. The entire command line is made available to the new process using GetCommandLine. If the lpApplicationName parameter was not specified, then the first token of the command line specifies file name of the application (note that this token begins at the beginning of the command line and ends at the first "white space" character). If the file name does not contain an extension (the presence of a "."), then .EXE is assumed. If the file name does not contain a directory path, Windows will search for the executable file in: - The current directory - The windows directory - The windows system directory - The directories listed in the path environment variable This parameter is optional onlu if the lpApplicationName parameter is specified. In this case the command line the application receives will be the application name. lpProcessAttributes - An optional parameter that may be used to specify the attributes of the new process. If the parameter is not specified, then the process is created without a security descriptor, and the resulting handle is not inherited on process creation: SECURITY_ATTRIBUTES Structure: DWORD nLength - Specifies the length of this structure. Must be set to sizeof( SECURITY_ATTRUBUTES ). LPVOID lpSecurityDescriptor - Points to a security descriptor for the object (must be NULL for Win32, used on NT/Win32). The security descriptor controls the sharing of an object. BOOL bInheritHandle - Supplies a flag that indicates whether or not the returned handle is to be inherited by a new process during process creation. A value of TRUE indicates that the new process will inherit the handle. lpThreadAttributes - An optional parameter that may be used to specify the attributes of the new thread. If the parameter is not specified, then the thread is created without a security descriptor, and the resulting handle is not inherited on process creation. dwCreationFlags - Supplies additional flags that control the creation of the process. dwCreationFlags Flags: DEBUG_PROCESS - If this flag bit is set, then the creating process is treated as a debugger, and the process being created is created as a debugee. All debug events occuring in the debugee are reported to the debugger. If this bit is clear, but the calling process is a debugee, then the process becomes a debugee of the calling processes debugger. If this bit is clear and the calling processes is not a debugee then no debug related actions occur. DEBUG_ONLY_THIS_PROCESS - If this flag is set, then the DEBUG_PROCESS flag bit must also be set. The calling process is is treated as a debugger, and the new process is created as its debuggee. If the new process creates additional processes, no debug related activities (with respect to the debugger) occur. CREATE_SUSPENDED - The process is created, but the initial thread of the process remains suspended. The creator can resume this thread using ResumeThread. Until this is done, code in the process will not execute. CREATE_UNICODE_ENVIRONMENT - If set, the environment pointer points to a Unicode environment block. Otherwise, the block is ANSI (actually OEM.) bInheritHandles - Supplies a flag that specifies whether or not the new process is to inherit handles to objects visible to the calling process. A value of TRUE causes handles to be inherited by the new process. If TRUE was specified, then for each handle visible to the calling process, if the handle was created with the inherit handle option, the handle is inherited to the new process. The handle has the same granted access in the new process as it has in the calling process, and the value of the handle is the same. lpEnvironment - An optional parameter, that if specified, supplies a pointer to an environment block. If the parameter is not specified, the environment block of the current process is used. This environment block is made available to the new process using GetEnvironmentStrings. lpCurrentDirectory - An optional parameter, that if specified, supplies a string representing the current drive and directory for the new process. The string must be a fully qualified pathname that includes a drive letter. If the parameter is not specified, then the new process is created with the same current drive and directory as the calling process. This option is provided primarily for shells that want to start an application and specify its initial drive and working directory. lpStartupInfo - Supplies information that specified how the applications window is to be shown. This structure is described in the Win32 User Interface API Book. lpProcessInformation - Returns identification information about the new process. PROCESS_INFORMATION Structure: HANDLE hProcess - Returns a handle to the newly created process. Through the handle, all operations on process objects are allowed. HANDLE hThread - Returns a handle to the newly created thread. Through the handle, all operations on thread objects are allowed. DWORD dwProcessId - Returns a global process id that may be used to identify a process. The value is valid from the time the process is created until the time the process is terminated. DWORD dwThreadId - Returns a global thread id that may be used to identify a thread. The value is valid from the time the thread is created until the time the thread is terminated. Return Value: TRUE - The operation was successful FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; POBJECT_ATTRIBUTES pObja; HANDLE ProcessHandle, ThreadHandle, VdmWaitHandle = NULL; HANDLE FileHandle, SectionHandle; CLIENT_ID ClientId; UNICODE_STRING PathName; IO_STATUS_BLOCK IoStatusBlock; BOOLEAN TranslationStatus; RTL_RELATIVE_NAME RelativeName; PVOID FreeBuffer; LPWSTR NameBuffer; LPWSTR WhiteScan; ULONG Length,i; PROCESS_BASIC_INFORMATION ProcessInfo; SECTION_IMAGE_INFORMATION ImageInformation; NTSTATUS StackStatus; BOOLEAN bStatus; INITIAL_TEB InitialTeb; CONTEXT ThreadContext; PPEB Peb; BASE_API_MSG m; PBASE_CREATEPROCESS_MSG a= (PBASE_CREATEPROCESS_MSG)&m.u.CreateProcess; PBASE_CHECKVDM_MSG b= (PBASE_CHECKVDM_MSG)&m.u.CheckVDM; PWCH TempNull = NULL; WCHAR TempChar; UNICODE_STRING VdmNameString; PVOID BaseAddress; ULONG VdmReserve; SIZE_T BigVdmReserve; ULONG iTask=0; LPWSTR CurdirBuffer, CurdirFilePart; DWORD CurdirLength,CurdirLength2; ULONG VDMCreationState=0; ULONG VdmBinaryType = 0; UNICODE_STRING SubSysCommandLine; PIMAGE_NT_HEADERS NtHeaders; DWORD dwNoWindow = (dwCreationFlags & CREATE_NO_WINDOW); ANSI_STRING AnsiStringVDMEnv; UNICODE_STRING UnicodeStringVDMEnv; WCHAR ImageFileDebuggerCommand[ 64 ]; LPWSTR QuotedBuffer; BOOLEAN QuoteInsert; BOOLEAN QuoteCmdLine = FALSE; BOOLEAN QuoteFound; BOOLEAN SearchRetry; BOOLEAN IsWowBinary = FALSE; STARTUPINFOW StartupInfo; DWORD LastError; DWORD fileattr; PROCESS_PRIORITY_CLASS PriClass; PVOID State; #if defined(BUILD_WOW6432) || defined(_WIN64) LPCWSTR lpOriginalApplicationName = lpApplicationName; LPWSTR lpOriginalCommandLine = lpCommandLine; #endif #if defined(WX86) || defined(_AXP64_) HANDLE Wx86Info = NULL; #endif #if defined WX86 BOOLEAN UseKnownWx86Dll; UseKnownWx86Dll = NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll; NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE; #endif RtlZeroMemory(lpProcessInformation,sizeof(*lpProcessInformation)); // Private VDM flag should be ignored; Its meant for internal use only. dwCreationFlags &= (ULONG)~CREATE_NO_WINDOW;//首先屏蔽CREATE_NO_WINDOW標志 // // CREATE_WITH_USERPROFILE is the new Create Flag that is used // only by CreateProcessWithLogonW. If this flags ends up getting // passed to CreateProcess, we must reject it. // if (dwCreationFlags & CREATE_WITH_USERPROFILE ) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) == (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) { //參考 MSDN,DETACHED_PROCESS|CREATE_NEW_CONSOLE這個組合是不合法的 SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } AnsiStringVDMEnv.Buffer = NULL; UnicodeStringVDMEnv.Buffer = NULL; // // the lowest specified priority class is used. // //判斷優先級,判斷順序依次為IDLE_PRIORITY_CLASS,NORMAL_PRIORITY_CLASS,HIGH_PRIORITY_CLASS,REALTIME_PRIORITY_CLASS //只要滿足其中一個優先級,就跳過其他優先級的判斷,如果都不滿足,將權限級置為0 if (dwCreationFlags & IDLE_PRIORITY_CLASS ) { PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE; } else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS ) { PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; } else if (dwCreationFlags & NORMAL_PRIORITY_CLASS ) { PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; } else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS ) { PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; } else if (dwCreationFlags & HIGH_PRIORITY_CLASS ) { PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; } else if (dwCreationFlags & REALTIME_PRIORITY_CLASS ) { if ( BasepIsRealtimeAllowed(FALSE) ) { PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME; } else { PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; } } else { PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_UNKNOWN; } PriClass.Foreground = FALSE; dwCreationFlags = (dwCreationFlags & ~PRIORITY_CLASS_MASK );//過濾掉優先級的標志位,然后再判斷是什么創建標志 // // Default separate/shared VDM option if not explicitly specified. // //CREATE_SEPARATE_WOW_VDM 和CREATE_SHARED_WOW_VDM只適用於Windows NT if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) { if (dwCreationFlags & CREATE_SHARED_WOW_VDM) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } } else if ((dwCreationFlags & CREATE_SHARED_WOW_VDM) == 0) { if (BaseStaticServerData->DefaultSeparateVDM) { dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; } } if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) == 0) { // // If the creator is running inside a job object, always // set SEPERATE_WOW_VDM so the VDM is part of the job. // JOBOBJECT_BASIC_UI_RESTRICTIONS UiRestrictions; Status = NtQueryInformationJobObject(NULL, JobObjectBasicUIRestrictions, &UiRestrictions, sizeof(UiRestrictions), NULL); if (Status != STATUS_ACCESS_DENIED) { // // Anything other than STATUS_ACCESS_DENIED indicates the // current process is inside a job. // dwCreationFlags = (dwCreationFlags & (~CREATE_SHARED_WOW_VDM)) | CREATE_SEPARATE_WOW_VDM; } } // // If ANSI environment, convert to Unicode // //判斷lpEnvironment是否為空,如果不為空,將Ansi字符串轉換為UNICODE_STRING,為空的話跳過這一步。 // if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) { PUCHAR s; STRING Ansi; UNICODE_STRING Unicode; MEMORY_BASIC_INFORMATION MemoryInformation; Ansi.Buffer = s = lpEnvironment; while (*s || *(s+1)) // find end of block s++; Ansi.Length = (USHORT)(s - Ansi.Buffer) + 1; Ansi.MaximumLength = Ansi.Length + 1; MemoryInformation.RegionSize = Ansi.MaximumLength * sizeof(WCHAR); Unicode.Buffer = NULL; //給Unicode申請空間 Status = NtAllocateVirtualMemory( NtCurrentProcess(), &Unicode.Buffer, 0, &MemoryInformation.RegionSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); return FALSE; } Unicode.MaximumLength = (USHORT)MemoryInformation.RegionSize; Status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, FALSE);//Ansi->UNICODE_STRING if (!NT_SUCCESS(Status) ) { NtFreeVirtualMemory( NtCurrentProcess(), &Unicode.Buffer, &MemoryInformation.RegionSize, MEM_RELEASE ); BaseSetLastNTError(Status); return FALSE; } lpEnvironment = Unicode.Buffer; } FileHandle = NULL; SectionHandle = NULL; ProcessHandle = NULL; ThreadHandle = NULL; FreeBuffer = NULL; NameBuffer = NULL; VdmNameString.Buffer = NULL; BaseAddress = (PVOID)1; VdmReserve = 0; CurdirBuffer = NULL; CurdirFilePart = NULL; SubSysCommandLine.Buffer = NULL; QuoteFound = FALSE; QuoteInsert = FALSE; QuotedBuffer = NULL; try { // // Make a copy of the startup info so we can change it. // StartupInfo = *lpStartupInfo; // // STARTF_USEHOTKEY means hStdInput is really the hotkey value. // STARTF_HASSHELLDATA means std handles are used for shell-private // data. This flag is used if an icon is passed to ShellExecuteEx. // As a result they cannot be specified with STARTF_USESTDHANDLES. // Consistent with Win95, USESTDHANDLES is ignored. // if (StartupInfo.dwFlags & STARTF_USESTDHANDLES && StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) { StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES; } VdmRetry: LastError = 0; SearchRetry = TRUE; QuoteInsert = FALSE; QuoteCmdLine = FALSE; if (!ARGUMENT_PRESENT( lpApplicationName )) { //lpApplicationName為空,大部分情況都為空 //在這種情況下,可執行模塊的名字必須處於lpCommandLine參數的最前面並由空格符與后面的字符分開 // // Locate the image // // forgot to free NameBuffer before goto VdmRetry??? ASSERT(NameBuffer == NULL); NameBuffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), MAX_PATH * sizeof( WCHAR )); if ( !NameBuffer ) { BaseSetLastNTError(STATUS_NO_MEMORY); return FALSE; } lpApplicationName = lpCommandLine;//從命令行中獲取應用名 TempNull = (PWCH)lpApplicationName; WhiteScan = (LPWSTR)lpApplicationName; // //當CreateProcess解析lpCommandLine字符串時,它會檢查字符串中的第一個標記(token),並假記此標記為我們想 //運行的可執行文件的名稱。 // check for lead quote // if ( *WhiteScan == L'\"' ) { SearchRetry = FALSE; WhiteScan++; lpApplicationName = WhiteScan; while(*WhiteScan) { if ( *WhiteScan == (WCHAR)'\"' ) { TempNull = (PWCH)WhiteScan; QuoteFound = TRUE; break; } WhiteScan++; TempNull = (PWCH)WhiteScan; } } else { retrywsscan: lpApplicationName = lpCommandLine; while(*WhiteScan) { if ( *WhiteScan == (WCHAR)' ' || *WhiteScan == (WCHAR)'\t' ) { TempNull = (PWCH)WhiteScan; break; } WhiteScan++; TempNull = (PWCH)WhiteScan; } } TempChar = *TempNull; *TempNull = UNICODE_NULL; #ifdef WX86 // // Wx86 applications must use x86 version of known exes // for compatibility. // if (UseKnownWx86Dll) { LPWSTR KnownName; NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE; //判斷lpApplicationName是否為regedit.exe,regsvr32.exe,msiexec.exe KnownName = BasepWx86KnownExe(lpApplicationName); if (KnownName) { lpApplicationName = KnownName; } } #endif //尋找.exe,如果lpApplicationName不包含擴展名,默認為.exe //並且如果文件名不包含一個完整的路徑,會按以下順序搜索可執行文件: //1.主調進程.exe文件所在的目錄 //2.主調進程的當前目錄 //3.windows系統目錄,即GetSystemDiretory返回的System32子文件夾 //4.windows目錄 //5.path環境變量中列出的目錄 Length = SearchPathW( NULL, lpApplicationName, (PWSTR)L".exe", MAX_PATH, NameBuffer, NULL )*2; if (Length != 0 && Length < MAX_PATH * sizeof( WCHAR )) { // // SearchPathW worked, but file might be a directory // if this happens, we need to keep trying //路徑是一個文件夾 // fileattr = GetFileAttributesW(NameBuffer); if ( fileattr != 0xffffffff && (fileattr & FILE_ATTRIBUTE_DIRECTORY) ) { Length = 0; } else { Length++; Length++; } } if ( !Length || Length >= MAX_PATH<<1 ) { // // If we search pathed, then return file not found. // otherwise, try to be more specific. //沒有找到 // RTL_PATH_TYPE PathType; HANDLE hFile; //判斷應用的狀態 PathType = RtlDetermineDosPathNameType_U(lpApplicationName);//?????不懂 if ( PathType != RtlPathTypeRelative ) { //不是活躍狀態 // // The failed open should set get last error properly. // hFile = CreateFileW( lpApplicationName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( hFile != INVALID_HANDLE_VALUE ) { CloseHandle(hFile); BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); } } else { BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); } // // remember initial last error value for the retry scan path // if ( LastError ) { SetLastError(LastError); } else { LastError = GetLastError(); } // // restore the command line // *TempNull = TempChar; lpApplicationName = NameBuffer; // // If we still have command line left, then keep going // the point is to march through the command line looking // for whitespace so we can try to find an image name // launches of things like: // c:\word 95\winword.exe /embedding -automation // require this. Our first iteration will stop at c:\word, our next // will stop at c:\word 95\winword.exe // if (*WhiteScan && SearchRetry) { WhiteScan++; TempNull = WhiteScan; QuoteInsert = TRUE; QuoteFound = TRUE; goto retrywsscan; } return FALSE; } // // restore the command line // *TempNull = TempChar; lpApplicationName = NameBuffer; } else if (!ARGUMENT_PRESENT( lpCommandLine ) || *lpCommandLine == UNICODE_NULL ) { QuoteCmdLine = TRUE; lpCommandLine = (LPWSTR)lpApplicationName; } #ifdef WX86 // // Wx86 applications must use x86 version of known exes // for compatibility. // if (UseKnownWx86Dll) { LPWSTR KnownName; NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE; KnownName = BasepWx86KnownExe(lpApplicationName); if (KnownName) { RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); NameBuffer = KnownName; lpApplicationName = KnownName; } } #endif // // Translate to an NT name. //將DOS路徑轉換為NT路徑,由於用戶給定的路徑一般都是DOS路徑,而內核需要的是NT路徑,因此需要轉換一下 // TranslationStatus = RtlDosPathNameToNtPathName_U( lpApplicationName, &PathName, NULL, &RelativeName ); if ( !TranslationStatus ) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } // forgot to free FreeBuffer before goto VdmRetry???? ASSERT(FreeBuffer == NULL); FreeBuffer = PathName.Buffer; if ( RelativeName.RelativeName.Length ) { PathName = *(PUNICODE_STRING)&RelativeName.RelativeName; } else { RelativeName.ContainingDirectory = NULL; } InitializeObjectAttributes( &Obja, &PathName, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL ); // // Open the file for execute access //獲得文件句柄 // Status = NtOpenFile( &FileHandle, SYNCHRONIZE | FILE_EXECUTE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ); if (!NT_SUCCESS(Status) ) { // // if we failed, see if this is a device. If it is a device, // then just return invalid image format // if ( RtlIsDosDeviceName_U((PWSTR)lpApplicationName) ) { SetLastError(ERROR_BAD_DEVICE); } else { BaseSetLastNTError(Status); } return FALSE; } // // If no desktop has been specified, use the caller's // desktop. // if (StartupInfo.lpDesktop == NULL) { StartupInfo.lpDesktop = (LPWSTR)((PRTL_USER_PROCESS_PARAMETERS)NtCurrentPeb()-> ProcessParameters)->DesktopInfo.Buffer; } // // Create a section object backed by the file //得到內存區對象句柄 // Status = NtCreateSection( &SectionHandle, SECTION_ALL_ACCESS, NULL, NULL, PAGE_EXECUTE, SEC_IMAGE, FileHandle ); NtClose(FileHandle); FileHandle = NULL; // // App Certification DLL // if (NT_SUCCESS(Status)) { //BasepIsProcessAllowed該函數用來判斷應用程序名是否在授權文件列表中 //函數實現調用了NtOpenKey函數打開了注冊表中的HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options鍵 Status = BasepIsProcessAllowed(lpApplicationName); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return FALSE; } } if (!NT_SUCCESS(Status)) { //得到SectionHandle失敗 switch (Status) { // 16 bit OS/2 exe case STATUS_INVALID_IMAGE_NE_FORMAT: #ifdef i386 // // Use OS/2 if x86 (OS/2 not supported on risc), // and CreationFlags don't have forcedos bit // and Registry didn't specify ForceDos // // else execute as a DOS bound app. // // if (!(dwCreationFlags & CREATE_FORCEDOS) && !BaseStaticServerData->ForceDos) { if ( !BuildSubSysCommandLine( L"OS2 /P ", lpApplicationName, lpCommandLine, &SubSysCommandLine ) ) { return FALSE; } lpCommandLine = SubSysCommandLine.Buffer; lpApplicationName = NULL; RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); FreeBuffer = NULL; RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); NameBuffer = NULL; goto VdmRetry; } #endif // Falls into Dos case, so that stub message will be // printed, and bound apps will run w/o OS/2 subsytem // Dos .exe or .com case STATUS_INVALID_IMAGE_PROTECT: case STATUS_INVALID_IMAGE_NOT_MZ: ForceDos: { ULONG BinarySubType; BinarySubType = BINARY_TYPE_DOS_EXE; if (Status == STATUS_INVALID_IMAGE_PROTECT || Status == STATUS_INVALID_IMAGE_NE_FORMAT || (BinarySubType = BaseIsDosApplication(&PathName,Status)) ) { VdmBinaryType = BINARY_TYPE_DOS; // create the environment before going to the // server. This was done becuase we want NTVDM // to have the new environment when it gets // created. if (!BaseCreateVDMEnvironment( lpEnvironment, &AnsiStringVDMEnv, &UnicodeStringVDMEnv )) return FALSE; if(!BaseCheckVDM(VdmBinaryType | BinarySubType, lpApplicationName, lpCommandLine, lpCurrentDirectory, &AnsiStringVDMEnv, &m, &iTask, dwCreationFlags, &StartupInfo )) return FALSE; // Check the return value from the server switch (b->VDMState & VDM_STATE_MASK){ case VDM_NOT_PRESENT: // mark this so the server can undo // creation if something goes wrong. // We marked it "partitially created" because // the NTVDM has yet not been fully created. // a call to UpdateVdmEntry to update // process handle will signal the NTVDM // process completed creation VDMCreationState = VDM_PARTIALLY_CREATED; // fail the call if NTVDM process is being // created DETACHED. // note that, we let it go if NTVDM process // is already running. if (dwCreationFlags & DETACHED_PROCESS) { SetLastError(ERROR_ACCESS_DENIED); return FALSE; } if (!BaseGetVdmConfigInfo(lpCommandLine, iTask, VdmBinaryType, &VdmNameString, &VdmReserve )) { BaseSetLastNTError(Status); return FALSE; } lpCommandLine = VdmNameString.Buffer; lpApplicationName = NULL; break; case VDM_PRESENT_NOT_READY: SetLastError (ERROR_NOT_READY); return FALSE; case VDM_PRESENT_AND_READY: VDMCreationState = VDM_BEING_REUSED; VdmWaitHandle = b->WaitObjectForParent; break; } RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); FreeBuffer = NULL; RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); NameBuffer = NULL; VdmReserve--; // we reserve from addr 1 if(VdmWaitHandle) goto VdmExists; else{ bInheritHandles = FALSE; if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)){ RtlDestroyEnvironment(lpEnvironment); } lpEnvironment = UnicodeStringVDMEnv.Buffer; goto VdmRetry; } } else { // // must be a .bat or .cmd file // static PWCHAR CmdPrefix = L"cmd /c "; PWCHAR NewCommandLine; ULONG Length; PWCHAR Last4 = &PathName.Buffer[PathName.Length / sizeof( WCHAR )-4]; if ( PathName.Length < 8 ) { SetLastError(ERROR_BAD_EXE_FORMAT); return FALSE; } if (_wcsnicmp( Last4, L".bat", 4 ) && _wcsnicmp( Last4, L".cmd", 4 )) { SetLastError(ERROR_BAD_EXE_FORMAT); return FALSE; } Length = wcslen( CmdPrefix ) + (QuoteCmdLine || QuoteFound ) + wcslen( lpCommandLine ) + (QuoteCmdLine || QuoteFound) + 1; NewCommandLine = RtlAllocateHeap( RtlProcessHeap( ), MAKE_TAG( TMP_TAG ), Length * sizeof( WCHAR ) ); if (NewCommandLine == NULL) { BaseSetLastNTError(STATUS_NO_MEMORY); return FALSE; } wcscpy( NewCommandLine, CmdPrefix ); if (QuoteCmdLine || QuoteFound) { wcscat( NewCommandLine, L"\"" ); } wcscat( NewCommandLine, lpCommandLine ); if (QuoteCmdLine || QuoteFound) { wcscat( NewCommandLine, L"\"" ); } RtlInitUnicodeString( &SubSysCommandLine, NewCommandLine ); lpCommandLine = SubSysCommandLine.Buffer; lpApplicationName = NULL; RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); FreeBuffer = NULL; RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); NameBuffer = NULL; goto VdmRetry; } } // 16 bit windows exe case STATUS_INVALID_IMAGE_WIN_16: #if defined(BUILD_WOW6432) || defined(_WIN64) if (lpOriginalApplicationName == NULL) { // pass in the part of the command line after the exe name // including whitespace lpCommandLine = ((*TempNull == '\"') ? TempNull + 1 : TempNull); } else { lpCommandLine = lpOriginalCommandLine; } return NtVdm64CreateProcess(lpOriginalApplicationName == NULL, lpApplicationName, // this is now the real file name we've loaded lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, (dwCreationFlags & ~CREATE_UNICODE_ENVIRONMENT), // the environment has already been converted to unicode lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation ); #endif if (dwCreationFlags & CREATE_FORCEDOS) { goto ForceDos; } IsWowBinary = TRUE; if (!BaseCreateVDMEnvironment(lpEnvironment, &AnsiStringVDMEnv, &UnicodeStringVDMEnv )) { return FALSE; } RetrySepWow: VdmBinaryType = dwCreationFlags & CREATE_SEPARATE_WOW_VDM ? BINARY_TYPE_SEPWOW : BINARY_TYPE_WIN16; if (!BaseCheckVDM(VdmBinaryType, lpApplicationName, lpCommandLine, lpCurrentDirectory, &AnsiStringVDMEnv, &m, &iTask, dwCreationFlags, &StartupInfo )) { // // If we failed with access denied, caller may not // be allowed allowed to access the shared wow's // desktop, so retry as a separate wow // if (VdmBinaryType == BINARY_TYPE_WIN16 && GetLastError() == ERROR_ACCESS_DENIED) { dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; } else { return FALSE; } goto RetrySepWow; } // Check the return value from the server switch (b->VDMState & VDM_STATE_MASK){ case VDM_NOT_PRESENT: // mark this so the server can undo // creation if something goes wrong. // We marked it "partitially created" because // the NTVDM has yet not been fully created. // a call to UpdateVdmEntry to update // process handle will signal the NTVDM // process completed creation VDMCreationState = VDM_PARTIALLY_CREATED; if (!BaseGetVdmConfigInfo( lpCommandLine, iTask, VdmBinaryType, &VdmNameString, &VdmReserve )) { BaseSetLastNTError(Status); return FALSE; } lpCommandLine = VdmNameString.Buffer; lpApplicationName = NULL; // // Wow must have a hidden console // Throw away DETACHED_PROCESS flag which isn't // meaningful for Win16 apps. // dwCreationFlags |= CREATE_NO_WINDOW; dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS); // // We're starting a WOW VDM, turn on feedback unless // the creator passed STARTF_FORCEOFFFEEDBACK. // StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK; break; case VDM_PRESENT_NOT_READY: SetLastError (ERROR_NOT_READY); return FALSE; case VDM_PRESENT_AND_READY: VDMCreationState = VDM_BEING_REUSED; VdmWaitHandle = b->WaitObjectForParent; break; } RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); FreeBuffer = NULL; RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); NameBuffer = NULL; VdmReserve--; // we reserve from addr 1 if(VdmWaitHandle) goto VdmExists; else { bInheritHandles = FALSE; // replace the environment with ours if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) { RtlDestroyEnvironment(lpEnvironment); } lpEnvironment = UnicodeStringVDMEnv.Buffer; goto VdmRetry; } default : SetLastError(ERROR_BAD_EXE_FORMAT); return FALSE; } } // // Make sure only WOW apps can have the CREATE_SEPARATE_WOW_VDM flag. // if (!IsWowBinary && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) { dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM; } // //上面皆為錯誤處理 // Query the section to determine the stack parameters and // image entrypoint. //返回得到節的基本信息(節基地址,大小,屬性) // Status = NtQuerySection( SectionHandle, SectionImageInformation, &ImageInformation, sizeof( ImageInformation ), NULL ); if (!NT_SUCCESS( Status )) { BaseSetLastNTError(Status); RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); FreeBuffer = NULL; return FALSE; } if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) { SetLastError(ERROR_BAD_EXE_FORMAT); RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); FreeBuffer = NULL; return FALSE; } ImageFileDebuggerCommand[ 0 ] = UNICODE_NULL; if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) || NtCurrentPeb()->ReadImageFileExecOptions ) { LdrQueryImageFileExecutionOptions( &PathName, L"Debugger", REG_SZ, ImageFileDebuggerCommand, sizeof( ImageFileDebuggerCommand ), NULL ); } if ((ImageInformation.Machine < USER_SHARED_DATA->ImageNumberLow) || (ImageInformation.Machine > USER_SHARED_DATA->ImageNumberHigh)) { #if defined(WX86) || defined(_AXP64_) if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) { Wx86Info = (HANDLE)UIntToPtr(sizeof(WX86TIB)); } else #endif // WX86 #if defined(_AXP64_) if (ImageInformation.Machine == IMAGE_FILE_MACHINE_ALPHA) { // Fall through since this is a valid machine type. } else #elif defined(_IA64_) if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) { // Fall through since this is a valid machine type. } else #endif // _AXP64_ #if defined(BUILD_WOW6432) // 32-bit kernel32.dll on NT64 can run 64-bit binaries #if defined(_ALPHA_) if (ImageInformation.Machine == IMAGE_FILE_MACHINE_ALPHA) { // Fall through since this is a valid machine type. } else #elif defined(_X86_) if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) { // Fall through since this is a valid machine type. } else #endif // ALPHA or IA64 #endif // BUILD_WOW6432 { ULONG_PTR ErrorParameters[2]; ULONG ErrorResponse; ErrorResponse = ResponseOk; ErrorParameters[0] = (ULONG_PTR)&PathName; NtRaiseHardError( STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, 1, 1, ErrorParameters, OptionOk, &ErrorResponse ); if ( NtCurrentPeb()->ImageSubsystemMajorVersion <= 3 ) { SetLastError(ERROR_BAD_EXE_FORMAT); } else { SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH); } RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); FreeBuffer = NULL; return FALSE; } } RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); FreeBuffer = NULL; if ( ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI && ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI ) { // POSIX exe NtClose(SectionHandle); SectionHandle = NULL; if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_POSIX_CUI ) { if ( !BuildSubSysCommandLine( L"POSIX /P ", lpApplicationName, lpCommandLine, &SubSysCommandLine ) ) { return FALSE; } lpCommandLine = SubSysCommandLine.Buffer; lpApplicationName = NULL; RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); NameBuffer = NULL; goto VdmRetry; } else { SetLastError(ERROR_CHILD_NOT_COMPLETE); return FALSE; } } else { //判斷鏡像文件版本是否合法 if (!BasepIsImageVersionOk( ImageInformation.SubSystemMajorVersion, ImageInformation.SubSystemMinorVersion) ) { SetLastError(ERROR_BAD_EXE_FORMAT); return FALSE; } } if (ImageFileDebuggerCommand[ 0 ] != UNICODE_NULL) { USHORT n; n = (USHORT)wcslen( lpCommandLine ); if (n == 0) { lpCommandLine = (LPWSTR)lpApplicationName; n = (USHORT)wcslen( lpCommandLine ); } n += wcslen( ImageFileDebuggerCommand ) + 1 + 2;//一個空格兩個'\0' n *= sizeof( WCHAR ); SubSysCommandLine.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), n ); SubSysCommandLine.Length = 0; SubSysCommandLine.MaximumLength = n; RtlAppendUnicodeToString( &SubSysCommandLine, ImageFileDebuggerCommand ); RtlAppendUnicodeToString( &SubSysCommandLine, L" " ); RtlAppendUnicodeToString( &SubSysCommandLine, (PWSTR)lpCommandLine ); #if DBG DbgPrint( "BASE: Calling debugger with '%wZ'\n", &SubSysCommandLine ); #endif lpCommandLine = SubSysCommandLine.Buffer; lpApplicationName = NULL; NtClose(SectionHandle); SectionHandle = NULL; RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); NameBuffer = NULL; goto VdmRetry; } // // Create the process object // //將安全屬性結構為NT對象屬性結構(得到了對象屬性) pObja = BaseFormatObjectAttributes(&Obja,lpProcessAttributes,NULL); if (dwCreationFlags & CREATE_BREAKAWAY_FROM_JOB ) { SectionHandle = (HANDLE)( (UINT_PTR)SectionHandle | 1); } Status = NtCreateProcess( &ProcessHandle, PROCESS_ALL_ACCESS, pObja, NtCurrentProcess(), (BOOLEAN)bInheritHandles, SectionHandle, NULL, NULL ); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); return FALSE; } // // NtCreateProcess will set to normal OR inherit if parent is IDLE or Below // only override if a mask is given during the create. // if ( PriClass.PriorityClass != PROCESS_PRIORITY_CLASS_UNKNOWN ) { State = NULL; if ( PriClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME ) { State = BasepIsRealtimeAllowed(TRUE); } //設置進程的優先級和默認處理模式 Status = NtSetInformationProcess( ProcessHandle, ProcessPriorityClass, (PVOID)&PriClass, sizeof(PriClass) ); if ( State ) { BasepReleasePrivilege( State ); } if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); return FALSE; } } NtClose(SectionHandle); SectionHandle = NULL; if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) { UINT NewMode; NewMode = SEM_FAILCRITICALERRORS; NtSetInformationProcess( ProcessHandle, ProcessDefaultHardErrorMode, (PVOID) &NewMode, sizeof(NewMode) ); } // // If the process is being created for a VDM call the server with // process handle. // if (VdmBinaryType) { VdmWaitHandle = ProcessHandle; if (!BaseUpdateVDMEntry(UPDATE_VDM_PROCESS_HANDLE, &VdmWaitHandle, iTask, VdmBinaryType )) { //make sure we don't close the handle twice -- //(VdmWaitHandle == ProcessHandle) if we don't do this. VdmWaitHandle = NULL; return FALSE; } // // For Sep wow the VdmWaitHandle = NULL (there is none!) // VDMCreationState |= VDM_FULLY_CREATED; } // // if we're a detached priority, we don't have the focus, so // don't create with boosted priority. // if (dwCreationFlags & DETACHED_PROCESS) { KPRIORITY SetBasePriority; SetBasePriority = (KPRIORITY)NORMAL_BASE_PRIORITY; Status = NtSetInformationProcess(ProcessHandle, ProcessBasePriority, (PVOID) &SetBasePriority, sizeof(SetBasePriority) ); ASSERT(NT_SUCCESS(Status)); } #if defined(i386) || defined(_IA64_) // // Reserve memory in the new process' address space if necessary // (for vdms). This is required only for x86 system. // if ( VdmReserve ) { BigVdmReserve = VdmReserve; Status = NtAllocateVirtualMemory( ProcessHandle, &BaseAddress, 0L, &BigVdmReserve, MEM_RESERVE, PAGE_EXECUTE_READWRITE ); if ( !NT_SUCCESS(Status) ){ BaseSetLastNTError(Status); return FALSE; } } #endif // // Determine the location of the // processes PEB. // Status = NtQueryInformationProcess( ProcessHandle, ProcessBasicInformation, &ProcessInfo, sizeof( ProcessInfo ), NULL ); if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); return FALSE; } Peb = ProcessInfo.PebBaseAddress; // // Push the parameters into the address space of the new process // if ( ARGUMENT_PRESENT(lpCurrentDirectory) ) { CurdirBuffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (MAX_PATH + 1) * sizeof( WCHAR ) ); if ( !CurdirBuffer ) { BaseSetLastNTError(STATUS_NO_MEMORY); return FALSE; } CurdirLength2 = GetFullPathNameW( lpCurrentDirectory, MAX_PATH, CurdirBuffer, &CurdirFilePart ); if ( CurdirLength2 > MAX_PATH ) { SetLastError(ERROR_DIRECTORY); return FALSE; } // // now make sure the directory exists // CurdirLength = GetFileAttributesW(CurdirBuffer); if ( (CurdirLength == 0xffffffff) || !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY) ) { SetLastError(ERROR_DIRECTORY); return FALSE; } } if ( QuoteInsert || QuoteCmdLine) { QuotedBuffer = RtlAllocateHeap(RtlProcessHeap(),0,wcslen(lpCommandLine)*2+6); if ( QuotedBuffer ) { wcscpy(QuotedBuffer,L"\""); if ( QuoteInsert ) { TempChar = *TempNull; *TempNull = UNICODE_NULL; } wcscat(QuotedBuffer,lpCommandLine); wcscat(QuotedBuffer,L"\""); if ( QuoteInsert ) { *TempNull = TempChar; wcscat(QuotedBuffer,TempNull); } } else { if ( QuoteInsert ) { QuoteInsert = FALSE; } if ( QuoteCmdLine ) { QuoteCmdLine = FALSE; } } } //其中實現中調用了RtlCreateProcessParameters來創建進程參數, //該函數對RTL_USER_PROCESS_PARAMETERS結構中的字符串域的地址改為相對的偏移量。 if (!BasePushProcessParameters( ProcessHandle, Peb, lpApplicationName, CurdirBuffer, QuoteInsert || QuoteCmdLine ? QuotedBuffer : lpCommandLine, lpEnvironment, &StartupInfo, dwCreationFlags | dwNoWindow, bInheritHandles, IsWowBinary ? IMAGE_SUBSYSTEM_WINDOWS_GUI : 0 ) ) { return FALSE; } RtlFreeUnicodeString(&VdmNameString); VdmNameString.Buffer = NULL; // // Stuff in the standard handles if needed // if (!VdmBinaryType && !bInheritHandles && !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) && !(dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE | CREATE_NO_WINDOW)) && ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI ) { PRTL_USER_PROCESS_PARAMETERS ParametersInNewProcess; Status = NtReadVirtualMemory( ProcessHandle, &Peb->ProcessParameters, &ParametersInNewProcess, sizeof( ParametersInNewProcess ), NULL ); if (NT_SUCCESS( Status )) { if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardInput )) { StuffStdHandle( ProcessHandle, NtCurrentPeb()->ProcessParameters->StandardInput, &ParametersInNewProcess->StandardInput ); } if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardOutput )) { StuffStdHandle( ProcessHandle, NtCurrentPeb()->ProcessParameters->StandardOutput, &ParametersInNewProcess->StandardOutput ); } if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardError )) { StuffStdHandle( ProcessHandle, NtCurrentPeb()->ProcessParameters->StandardError, &ParametersInNewProcess->StandardError ); } } } // // Create the thread... // // // Allocate a stack for this thread in the address space of the target // process. // StackStatus = BaseCreateStack( ProcessHandle, ImageInformation.CommittedStackSize, (ImageInformation.MaximumStackSize < 256*1024) ? 256*1024 : ImageInformation.MaximumStackSize,//256k????? &InitialTeb ); if ( !NT_SUCCESS(StackStatus) ) { BaseSetLastNTError(StackStatus); return FALSE; } // // Create an initial context for the new thread. // //初始化線程上下文 //在BaseInitializeContext函數的實現中會判斷參數上下文類型是否為1,如果不為1, 指定用戶空間的線程啟動函數為BaseProcessStartThunk(進程的第一個線程調用這個) //否則為BaseThreadStartThunk(普通線程),然后CALL OEP。其中BaseProcessStartThunk函數會調用BaseProcessStart,這個函數就是調用OEP所在函數的上層函數 BaseInitializeContext( &ThreadContext, Peb, ImageInformation.TransferAddress, InitialTeb.StackBase, BaseContextTypeProcess ); // // Create the actual thread object // //格式化對象(以便傳遞給NtCreateThread) pObja = BaseFormatObjectAttributes(&Obja,lpThreadAttributes,NULL); Status = NtCreateThread( &ThreadHandle, THREAD_ALL_ACCESS, pObja, ProcessHandle, &ClientId, &ThreadContext, &InitialTeb, TRUE ); if (!NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); return FALSE; } // // From here on out, do not modify the address space of the // new process. WOW64's implementation of NtCreateThread() // reshuffles the new process' address space if the current // process is 32-bit and the new process is 64-bit. // #if DBG Peb = NULL; #endif #if defined(WX86) || defined(_AXP64_) // // if this is a Wx86 Process, setup for a Wx86 emulated Thread // if (Wx86Info) { // // create a WX86Tib and initialize it's Teb->Vdm. // Status = BaseCreateWx86Tib(ProcessHandle, ThreadHandle, (ULONG)((ULONG_PTR)ImageInformation.TransferAddress), (ULONG)ImageInformation.CommittedStackSize, (ULONG)ImageInformation.MaximumStackSize, TRUE ); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return( FALSE ); } // // Mark Process as WX86 // Status = NtSetInformationProcess (ProcessHandle, ProcessWx86Information, &Wx86Info, sizeof(Wx86Info) ); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return( FALSE ); } } #endif // // Call the Windows server to let it know about the // process. // a->ProcessHandle = ProcessHandle; a->ThreadHandle = ThreadHandle; a->ClientId = ClientId; a->CreationFlags = dwCreationFlags; if ( dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) ) { Status = DbgUiConnectToDbg(); if ( !NT_SUCCESS(Status) ) { NtTerminateProcess(ProcessHandle, Status); BaseSetLastNTError(Status); return FALSE; } a->DebuggerClientId = NtCurrentTeb()->ClientId; } else { a->DebuggerClientId.UniqueProcess = NULL; a->DebuggerClientId.UniqueThread = NULL; } // // Set the 2 bit if a gui app is starting. The window manager needs to // know this so it can synchronize the startup of this app // (WaitForInputIdle api). This info is passed using the process // handle tag bits. The 1 bit asks the window manager to turn on // or turn off the application start cursor (hourglass/pointer). // // When starting a WOW process, lie and tell UserSrv NTVDM.EXE is a GUI // process. We also turn on bit 0x8 so that UserSrv can ignore the // UserNotifyConsoleApplication call made by the console during startup. // if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI || IsWowBinary ) { a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 2); // // If the creating process is a GUI app, turn on the app. start cursor // by default. This can be overridden by STARTF_FORCEOFFFEEDBACK. // NtHeaders = RtlImageNtHeader((PVOID)GetModuleHandle(NULL)); if ( NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ) a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1); } // // If feedback is forced on, turn it on. If forced off, turn it off. // Off overrides on. // if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK) a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1); if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK) a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle & ~1); a->VdmBinaryType = VdmBinaryType; // just tell server the truth if (VdmBinaryType){ a->hVDM = iTask ? 0 : NtCurrentPeb()->ProcessParameters->ConsoleHandle; a->VdmTask = iTask; } #if defined(BUILD_WOW6432) m.ReturnValue = CsrBasepCreateProcess(a); #else CsrClientCallServer( (PCSR_API_MSG)&m, NULL, CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX, BasepCreateProcess ), sizeof( *a ) ); #endif if (!NT_SUCCESS((NTSTATUS)m.ReturnValue)) { BaseSetLastNTError((NTSTATUS)m.ReturnValue); NtTerminateProcess(ProcessHandle, (NTSTATUS)m.ReturnValue); return FALSE; } if (!( dwCreationFlags & CREATE_SUSPENDED) ) { NtResumeThread(ThreadHandle,&i); } VdmExists: bStatus = TRUE; if (VDMCreationState) VDMCreationState |= VDM_CREATION_SUCCESSFUL; try { if (VdmWaitHandle) { // // tag Shared WOW VDM handles so that wait for input idle has a // chance to work. Shared WOW VDM "process" handles are actually // event handles, Separate WOW VDM handles are real process // handles. Also mark DOS handles with 0x1 so WaitForInputIdle // has a way to distinguish DOS apps and not block forever. // if (VdmBinaryType == BINARY_TYPE_WIN16) { lpProcessInformation->hProcess = (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x2); // // Shared WOW doesn't always start a process, so // we don't have a process ID or thread ID to // return if the VDM already existed. // // Separate WOW doesn't hit this codepath // (no VdmWaitHandle). // if (VDMCreationState & VDM_BEING_REUSED) { ClientId.UniqueProcess = 0; ClientId.UniqueThread = 0; } } else { lpProcessInformation->hProcess = (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x1); } // // Close the ProcessHandle, since we are returning the // VdmProcessHandle instead. // if (ProcessHandle != NULL) NtClose(ProcessHandle); } else{ lpProcessInformation->hProcess = ProcessHandle; } lpProcessInformation->hThread = ThreadHandle; lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess); lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread); ProcessHandle = NULL; ThreadHandle = NULL; } except ( EXCEPTION_EXECUTE_HANDLER ) { NtClose( ProcessHandle ); NtClose( ThreadHandle ); ProcessHandle = NULL; ThreadHandle = NULL; if (VDMCreationState) VDMCreationState &= ~VDM_CREATION_SUCCESSFUL; } } finally { if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) { RtlDestroyEnvironment(lpEnvironment); lpEnvironment = NULL; } RtlFreeHeap(RtlProcessHeap(), 0,QuotedBuffer); RtlFreeHeap(RtlProcessHeap(), 0,NameBuffer); RtlFreeHeap(RtlProcessHeap(), 0,CurdirBuffer); RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); if ( FileHandle ) { NtClose(FileHandle); } if ( SectionHandle ) { NtClose(SectionHandle); } if ( ThreadHandle ) { NtTerminateProcess(ProcessHandle,STATUS_SUCCESS); NtClose(ThreadHandle); } if ( ProcessHandle ) { NtClose(ProcessHandle); } RtlFreeUnicodeString(&VdmNameString); RtlFreeUnicodeString(&SubSysCommandLine); if (AnsiStringVDMEnv.Buffer || UnicodeStringVDMEnv.Buffer) BaseDestroyVDMEnvironment(&AnsiStringVDMEnv, &UnicodeStringVDMEnv); if (VDMCreationState && !(VDMCreationState & VDM_CREATION_SUCCESSFUL)){ BaseUpdateVDMEntry ( UPDATE_VDM_UNDO_CREATION, (HANDLE *)&iTask, VDMCreationState, VdmBinaryType ); if(VdmWaitHandle) { NtClose(VdmWaitHandle); } } } if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) { RtlDestroyEnvironment(lpEnvironment); } return bStatus; }
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; }
