前言: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; }
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; }