有了以前的對進程和線程對象的學習的鋪墊後,咱們如今能夠開始學習windows下的進程建立過程了,我將嘗試着從源代碼的層次來分析在windows下建立一個進程都要涉及到哪些步驟,都要涉及到哪些數據結構。php
1. 相關閱讀材料css
《windows 內核原理與分析》 --- 潘愛民web
《深刻解析windows操做系統(第4版,中文版)》算法
http://bbs.pediy.com/showthread.php?p=819417#post819417 看雪上的精華貼編程
http://undoc.airesoft.co.uk/ 查閱windows未公開的函數的網站windows
因爲進程的建立能夠在ring3模式下,也能夠在ring0模式下,因此咱們分兩個部分來分別說明!!
一 . Ring3進程建立流程api
1. 簡介安全
當一個應用程序調用某個進程建立函數,好比CreateProcess、CreateProcessAsUser、CreateProcessWithTokenW、CreateProcessWithLogonW時,一個windows進程就被建立起來了。bash
建立一個windows進程的過程,是由操做系統的三個部分執行一些列步驟來完成的(以後會詳細介紹):數據結構
1. 客戶方的windows庫Kernel32.dll 2. windows執行體 3. windows子系統進程(Csrss.exe)
因爲windows是多環境子系統的體系結構,所以,建立一個windows執行體進程對象(其餘的子系統也可使用),與建立一個windows進程的工做是分離的。
也就是說windows在建立進程的過程當中有兩大類的工做要作:
1. windows系統加入的語義 2. 執行體/內核層對象等的建立
下面歸納了一下在利用windows的CreateProcess函數來建立一個進程時所涉及的主要階段:
1. 打開將要在該進程中執行的映像文件(.exe) 2. 建立windows執行體進程對象 3. 建立初始線程(棧、執行環境、windows執行體線程對象) 4. 通知windows子系統新進程建立了,因此它能夠爲新進程和線程作好準備 5. 開始執行初始線程(除非指定了CREATE_SUSPENDING標誌) 6. 在新進程和線程的環境中,完成地址空間中的初始化(好比加載必要的DLL),並開始執行程序
在開始學習進程建立的詳細過程以前,有幾點是咱們要注意的:
1. 在CreateProcess中,新進程的優先級類別是在CreationFlags參數總由單獨的位來決定的,所以,咱們能夠爲單個CreateProcess調用多個優先級類別。
windows經過選取"最低優先級類別集合",來解決爲進程分配優先級類別的問題 BOOL WINAPI CreateProcess( _In_opt_ LPCTSTR lpApplicationName, _Inout_opt_ LPTSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCTSTR lpCurrentDirectory, _In_ LPSTARTUPINFO lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation ); 2. 若是建立新進程時沒有指定優先級類別,那麼,它的優先級類別默認被設置爲Normal(進程/線程優先級的問題參考 學習筆記(2)),除非建立線程的優先級類別是Idle或者Below Normal,
在這種狀況下,新進程的優先級類別與建立進程時指定的的優先級相同 3. 若是爲新進程指定了Real-Time優先級類別,可是該進程的調用者沒有"Increase Scheduling Priority(增長調度優先級)"特權,則使用High優先級類別。換句話說,CreateProcess
不會僅僅由於調用者沒有足夠的特權來建立Real-Time優先級類別的進程而失敗,而是自動"下降"一點,新進程只是沒有Real-Time那麼高的優先級而已 4. 全部的窗口都與桌面有關聯,從操做系統的角度理解,桌面是一個工做區的圖形表示。若是在CreateProcess中沒有指定桌面,那麼,該進程就與調用者的當前桌面關聯在一塊兒
2. 詳細分析(CreateProcess)
階段一: 打開將要被執行的映像
從最開始的地方,那就是咱們點擊鼠標或者在命令行中輸入可執行程序的路徑來進行所謂的"打開"程序。
在CreateProcess中的第一個階段是: 先找到適當的windows映像(.exe文件),它將運行由調用者指定的可執行文件(.exe)。而後建立一個內存區對象,以便稍後將它映射到新進程的地址空間中。若是沒有指定映像名稱,則該命令行的第一個符號(被定義成命令行字符串中第一個空格或製表符以前的、合乎文件規範的那部分)被用做映像文件名。
在windows XP和windows Server 2003上,CreateProcess會首先檢查機器上的"軟件限制策略"是否容許該映像被運行起來(《深刻解析windows操做系統(第4版)》8.6軟件限制策略)
若是指定的可執行文件是一個windows.exe類型的文件,那麼,它可被直接使用。若是它不是一個windows.exe(例如MS-DOS、Win1六、POSIX應用程序),那麼CreateProcess經過一系列的步驟來找到一個windows支持映像(support image),以便運行它。這個過程是必須的,由於"非windows"應用程序不能直接被運行,相反,windows使用少數幾個特定的"支持映像"中的某一個,由它負責實際運行這一個"非windows"程序。
例如:
1. 若是你試圖運行一個POSIX應用程序,那麼CreateProcess把它識別出來,並改變該映像,改爲能夠在windows可執行文件Posix.exe上運行的新映像。 2. 若是你試圖運行一個MS-DOS或者Win16可執行文件,那麼,被運行的映像變成了windows的可執行文件Ntvdm.exe。
總之,咱們不能直接建立一個非windows進程的進程,若是windows不能找到一種方法把要激活的映像解析成一個windows進程,那麼CreateProcess就會"失敗"。
(這張圖以輻射狀展現windows對可執行文件的類型判斷並自動進行映像轉換)
(這就是爲何咱們運行windows的bash腳本.bat時彈出的是cmd的窗口的緣由)
階段一屬於操做系統對的工做,和CreateProcess代碼自己的關係並非很大,可是倒是操做系統很重要的一步
階段二: 檢查和轉換工做
在CreateProcessA函數中調用了CreateProcessInternalA,調用該函數時,增長了兩個參數(一頭一尾加了個零參數),在該函數裏面也沒看到對這兩個參數的引用,而是直接傳遞給CreateProcessInternalW函數。
IDA->Kernel32.dll:
BOOL __stdcall CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return CreateProcessInternalA( 0, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation, 0); }
OD->動態調試(C代碼會在文章的最後給出):
進入CreateProcessInternalA的代碼中,發現了大量的代碼,下面請容許我一口氣把代碼都貼出來,由於我以爲說什麼都不如原始的windows源代碼來的實在,咱們一行一行地來分析這些代碼的邏輯,我會盡個人能力解析代碼的意思:
int __stdcall CreateProcessInternalA(int var_0, int lpApplicationName, int lpCommandLine, int lpProcessAttributes, int lpThreadAttributes,
int bInheritHandles, int dwCreationFlags, int lpEnvironment, int lpCurrentDirectory, int lpStartupInfo, int lpProcessInformation,
int var_0_) { int result; // eax@2 int v13; // eax@7 int v14; // eax@10 int v15; // eax@17 int v16; // eax@19 int v17; // eax@21 int v18; // eax@24 int v19; // eax@26 int v20; // eax@30 int v21; // eax@32 signed int v22; // [sp-4h] [bp-B8h]@20 char v23; // [sp+14h] [bp-A0h]@15 int v24; // [sp+18h] [bp-9Ch]@15 unsigned __int32 v25; // [sp+1Ch] [bp-98h]@32 char Dst; // [sp+20h] [bp-94h]@3 int v27; // [sp+24h] [bp-90h]@3 int v28; // [sp+28h] [bp-8Ch]@3 int v29; // [sp+2Ch] [bp-88h]@3 char *v30; // [sp+68h] [bp-4Ch]@15 int v31; // [sp+6Ch] [bp-48h]@21 char v32; // [sp+70h] [bp-44h]@4 int v33; // [sp+74h] [bp-40h]@3 char v34; // [sp+78h] [bp-3Ch]@6 int v35; // [sp+7Ch] [bp-38h]@3 char v36; // [sp+80h] [bp-34h]@2 int v37; // [sp+84h] [bp-30h]@10 int v38; // [sp+88h] [bp-2Ch]@11 char v39; // [sp+8Ch] [bp-28h]@21 __int16 v40; // [sp+8Eh] [bp-26h]@19 int v41; // [sp+90h] [bp-24h]@21 unsigned __int16 v42; // [sp+94h] [bp-20h]@16 CPPEH_RECORD ms_exc; // [sp+9Ch] [bp-18h]@3 if ( !lpCommandLine ) { v37 = 0; v30 = &v23; v24 = 0; LABEL_3: v33 = 0; v35 = 0; _memmove(&Dst, (const void *)lpStartupInfo, 0x44u); v27 = 0; v28 = 0; v29 = 0; ms_exc.disabled = 1; if ( lpApplicationName && !Basep8BitStringToDynamicUnicodeString(&v32, lpApplicationName) || lpCurrentDirectory && !Basep8BitStringToDynamicUnicodeString(&v34, lpCurrentDirectory) ) goto LABEL_14; v13 = *(_DWORD *)(lpStartupInfo + 4); if ( v13 ) { ms_exc.disabled = 2; RtlInitAnsiString(&v42, v13); ms_exc.disabled = 1; if ( (_BYTE)NlsMbCodePageTag ) LOWORD(v15) = RtlxAnsiStringToUnicodeSize(&v42); else v15 = 2 * v42 + 2; v40 = v15; v16 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), BaseDllTag, (unsigned __int16)v15); v27 = v16; if ( !v16 ) goto LABEL_20; v41 = v16; v17 = RtlAnsiStringToUnicodeString(&v39, &v42, 0); v31 = v17; if ( v17 < 0 ) goto LABEL_34; } if ( *(_DWORD *)(lpStartupInfo + 8) ) { RtlInitAnsiString(&v42, *(_DWORD *)(lpStartupInfo + 8)); if ( (_BYTE)NlsMbCodePageTag ) LOWORD(v18) = RtlxAnsiStringToUnicodeSize(&v42); else v18 = 2 * v42 + 2; v40 = v18; v19 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), BaseDllTag, (unsigned __int16)v18); v28 = v19; if ( !v19 ) goto LABEL_20; v41 = v19; v17 = RtlAnsiStringToUnicodeString(&v39, &v42, 0); v31 = v17; if ( v17 < 0 ) { LABEL_34: v22 = v17; goto LABEL_35; } } if ( !*(_DWORD *)(lpStartupInfo + 12) ) { LABEL_10: ms_exc.disabled = 0; v14 = v37; if ( !v37 ) v14 = *((_DWORD *)v30 + 1); v38 = CreateProcessInternalW( var_0, v33, v14, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, v35, &Dst, lpProcessInformation, var_0_); goto LABEL_12; } RtlInitAnsiString(&v42, *(_DWORD *)(lpStartupInfo + 12)); if ( (_BYTE)NlsMbCodePageTag ) LOWORD(v20) = RtlxAnsiStringToUnicodeSize(&v42); else v20 = 2 * v42 + 2; v40 = v20; v25 = __readfsdword(24); v21 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v25 + 48) + 24), BaseDllTag, (unsigned __int16)v20); v29 = v21; if ( v21 ) { v41 = v21; v17 = RtlAnsiStringToUnicodeString(&v39, &v42, 0); v31 = v17; if ( v17 >= 0 ) goto LABEL_10; goto LABEL_34; } LABEL_20: v22 = -1073741801; LABEL_35: BaseSetLastNTError(v22); LABEL_14: v38 = 0; ms_exc.disabled = 0; LABEL_12: ms_exc.disabled = -1; RtlFreeUnicodeString(&v36); RtlFreeUnicodeString(&v32); RtlFreeUnicodeString(&v34); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), 0, v27); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), 0, v28); return RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), 0, v29); } result = Basep8BitStringToDynamicUnicodeString(&v36, lpCommandLine); if ( result ) goto LABEL_3; return result; }
1. 函數的開始首先是一大段的臨時棧區變量的申請。因爲是IDA反編譯出來的,名字都是這個遞增名字,其中有一個結構體變量CPPEH_RECORD注意一下:
CPPEH_RECORD struc ; (sizeof=0x18, standard type) old_esp dd ? exc_ptr dd ? ; offset prev_er dd ? ; offset handler dd ? ; offset msEH_ptr dd ? ; offset disabled dd ? CPPEH_RECORD ends
2. 判斷lpCommandLine(就是咱們輸入的notepad.exe這個命令)是否爲空。即判斷是否爲0,不爲0則將lpCommandLine初始化爲UNICODE字符串
if ( !lpCommandLine ) { ... } result = Basep8BitStringToDynamicUnicodeString(&lpCommandLine_UNICODE, lpCommandLine); if ( result ) goto LABEL_3; ...
這裏的Basep8BitStringToDynamicUnicodeString()函數是windows的未公開undocumented函數(在文章的開頭有給出網址),它的定義以下:
BOOL WINAPI Basep8BitStringToDynamicUnicodeString (
PUNICODE_STRING pConvertedStr,
LPCSTR pszAnsiStr
)
接着繼續檢查參數(lpApplicationName / lpCurrentDirectory),咱們跟隨代碼來到LABEL_3:
1. 先將lpStartupInfor的內容拷貝到一個局部變量lpStartupInfo_buf 2. 而後判斷參數lpApplicationName是否爲0,不爲0則參數轉換爲UNICODE字符串 3. 爲0則跳過轉換繼續判斷參數lpCurrentDirectory是否爲0(思考"與運算符"和"或運算符"在彙編代碼中的表現形式,即"與運算符"的提早退出性,"或運算符"的並行檢查性),
不爲0則參數轉換爲UNICODE字符串 4. lpApplicationName和lpCurrentDirectory中只有至少有一個不爲0,則這個if判斷成立,繼續檢查參數
LABEL_3: .. _memmove(&lpStartupInfo_buf, (const void *)lpStartupInfo, 0x44u); .. if ( lpApplicationName && !Basep8BitStringToDynamicUnicodeString(&v32, lpApplicationName) || lpCurrentDirectory && !Basep8BitStringToDynamicUnicodeString(&v34, lpCurrentDirectory) ) ..
3. 繼續檢查參數(lpStartupInfo.lpReserved / lpStartupInfo.lpDesktop / lpStartupInfo.lpTitle),接着判斷STARTUPINFOA.lpReserved域是否爲0,若是不爲0,則申請了一個堆空間並將STARTUPINFOA.lpReserved的ASCII字符串轉換成了UNICODE,接下來判斷lpStartupInfo.lpDesktop、lpStartupInfo.lpTitle。代碼模式都是同樣的:
if ( STARTUPINFOA.lpReserved ) //STARTUPINFOA.lpReserved { ms_exc.disabled = 2; RtlInitAnsiString(&STARTUPINFOA.lpReserved_buf, STARTUPINFOA.lpReserved); ms_exc.disabled = 1; if ( (_BYTE)NlsMbCodePageTag ) LOWORD(STARTUPINFOA.lpReserved_buf_len) = RtlxAnsiStringToUnicodeSize(&STARTUPINFOA.lpReserved_buf); else STARTUPINFOA.lpReserved_buf_len = 2 * STARTUPINFOA.lpReserved_buf + 2; STARTUPINFOA.lpReserved_buf_len_ = STARTUPINFOA.lpReserved_buf_len; v16 = RtlAllocateHeap( *(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), BaseDllTag, (unsigned __int16)STARTUPINFOA.lpReserved_buf_len); v27 = v16; if ( !v16 ) goto LABEL_20; v41 = v16; v17 = RtlAnsiStringToUnicodeString(&v39, &STARTUPINFOA.lpReserved_buf, 0); v31 = v17; if ( v17 < 0 ) goto LABEL_34; } if ( *(_DWORD *)(lpStartupInfo + 8) ) //STARTUPINFOA.lpDesktop { RtlInitAnsiString(&STARTUPINFOA.lpReserved_buf, *(_DWORD *)(lpStartupInfo + 8)); if ( (_BYTE)NlsMbCodePageTag ) LOWORD(v18) = RtlxAnsiStringToUnicodeSize(&STARTUPINFOA.lpReserved_buf); else v18 = 2 * STARTUPINFOA.lpReserved_buf + 2; STARTUPINFOA.lpReserved_buf_len_ = v18; v19 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), BaseDllTag, (unsigned __int16)v18); v28 = v19; if ( !v19 ) goto LABEL_20; v41 = v19; v17 = RtlAnsiStringToUnicodeString(&v39, &STARTUPINFOA.lpReserved_buf, 0); v31 = v17; if ( v17 < 0 ) { LABEL_34: v22 = v17; goto LABEL_35; } } if ( !*(_DWORD *)(lpStartupInfo + 12) ) //STARTUPINFOA.lpTitle { ...
這裏附上STARTUPINFOA數據結構的定義,這個數據結構負責在建立進程的時候給操做系統傳遞必要的和這個進程相關的信息,很是重要
typedef struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO;
4. 這些參數都判斷完以後,就開始調用CreateProcessInternalW(),注意,此次傳入的參數所有都是UNICODE字符串了,也就是符合NT式的標準,咱們知道,windows在NT系統之後把全部的API(例如CreateProcessInternalW)實現都以UNICODE實現了,而保留本來的ASCII版本的API(例如CreateProcessInternalA)只是在中間作了一個"轉階層",最終都必定要調用UNICODE版本的API,從這個例子咱們能夠清除地看到這點。
... v38 = CreateProcessInternalW( var_0, v33, v14, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, v35, &lpStartupInfo_buf, lpProcessInformation, var_0_); ...
看上面的步驟你們應該知道了其實CreateProcessInternalA函數只是對字符串參數或者結構體中包含字符串類型的域的做了檢查和轉換工做,而後就調用了下層函數
階段三: CreateProcessInternalW(),ring3階段進程建立流程
在開始分析以前,先明確一點:
咱們如今還處在ring3層,也就是用戶模式的代碼空間中,咱們以前和咱們接下來分析的代碼在kernel32.dll中是能夠逆向出源代碼的,到了ring0層咱們就無法直接逆向出源代碼了,
心理必定要明確這一點,由於進程的建立從大的分類來看是分ring3(用戶模式)和ring0(內核模式)的。
咱們從CreateProcessInternalA()進入到了kernel32.dll中的關於進程建立的更底層的函數CreateProcessInternalW(),接下來分析這個函數的源代碼。
請原諒我又貼出一大段"雜亂"的代碼,我思考了一下,爲了保持思路的完整性,仍是把原生的完整代碼貼出來,而後再進行代碼邏輯的分析,因此可能會形成朋友們看的不舒服,請多多包涵了。
int __stdcall CreateProcessInternalW(int var_0, size_t lpApplicationName, wchar_t *lpCommandLine, int lpProcessAttributes, int lpThreadAttributes,
int bInheritHandles, int dwCreationFlags, int lpEnvironment, const WCHAR *lpCurrentDirectory, const void *lpStartupInfo,
int lpProcessInformation, int var_0_) { unsigned __int16 v12; // ax@22 int v13; // eax@22 int v14; // esi@23 size_t v15; // edi@33 int v16; // eax@34 int v17; // edi@39 int v18; // eax@40 signed int v19; // esi@42 int v20; // eax@47 int v21; // eax@50 signed int v22; // edi@55 int v23; // eax@55 HMODULE v24; // eax@65 HMODULE v25; // esi@65 signed int v26; // esi@76 int v27; // eax@76 int v28; // ecx@81 int v29; // eax@2 int v30; // eax@17 int v31; // edi@89 DWORD v32; // eax@91 int v33; // ecx@97 signed int v34; // eax@101 int v35; // eax@121 int v36; // esi@123 void (__stdcall *v37)(_DWORD, _DWORD, _DWORD); // esi@127 int *v38; // esi@131 int v39; // edi@131 int v40; // eax@132 int v41; // eax@135 int v42; // ecx@136 int v43; // esi@138 int result; // eax@154 int *v45; // esi@161 int v46; // edi@161 int v47; // ecx@161 int v48; // edx@162 int v49; // ecx@163 int v50; // eax@165 int v51; // esi@167 HMODULE v52; // eax@182 int v53; // eax@182 signed int v54; // esi@198 unsigned int v55; // eax@201 const wchar_t *v56; // edi@201 int v57; // edi@206 size_t v58; // eax@209 int v59; // eax@209 wchar_t *v60; // esi@209 wchar_t *v61; // edi@226 DWORD v62; // eax@259 int v63; // esi@259 DWORD v64; // eax@261 size_t v65; // eax@270 wchar_t v66; // ax@272 HANDLE v67; // eax@297 int *v68; // eax@329 int v69; // ecx@329 int v70; // ecx@330 size_t v71; // eax@358 int v72; // esi@358 unsigned __int32 v73; // eax@377 unsigned __int32 v74; // esi@377 size_t v75; // eax@377 wchar_t *v76; // eax@377 const wchar_t *v77; // edi@378 int v78; // eax@393 int v79; // eax@398 int v80; // esi@398 signed int v81; // [sp-4h] [bp-A28h]@173 signed int v82; // [sp-4h] [bp-A28h]@179 DWORD v83; // [sp-4h] [bp-A28h]@277 int v84; // [sp-4h] [bp-A28h]@289 signed int v85; // [sp-4h] [bp-A28h]@235 int v86; // [sp+18h] [bp-A0Ch]@22 unsigned __int32 v87; // [sp+28h] [bp-9FCh]@394 char v88; // [sp+2Ch] [bp-9F8h]@14 int v89; // [sp+30h] [bp-9F4h]@182 int v90; // [sp+34h] [bp-9F0h]@47 int v91; // [sp+38h] [bp-9ECh]@103 unsigned __int32 v92; // [sp+3Ch] [bp-9E8h]@140 unsigned __int32 v93; // [sp+40h] [bp-9E4h]@192 unsigned __int32 v94; // [sp+44h] [bp-9E0h]@171 unsigned __int32 v95; // [sp+48h] [bp-9DCh]@191 unsigned __int32 v96; // [sp+4Ch] [bp-9D8h]@409 unsigned __int32 v97; // [sp+50h] [bp-9D4h]@241 unsigned int v98; // [sp+54h] [bp-9D0h]@201 unsigned __int32 v99; // [sp+58h] [bp-9CCh]@190 unsigned __int32 v100; // [sp+5Ch] [bp-9C8h]@309 unsigned __int32 v101; // [sp+60h] [bp-9C4h]@247 HANDLE v102; // [sp+64h] [bp-9C0h]@297 unsigned __int32 v103; // [sp+68h] [bp-9BCh]@189 unsigned __int32 v104; // [sp+6Ch] [bp-9B8h]@220 unsigned __int32 v105; // [sp+70h] [bp-9B4h]@418 unsigned __int32 v106; // [sp+74h] [bp-9B0h]@144 unsigned __int32 v107; // [sp+78h] [bp-9ACh]@144 unsigned __int32 v108; // [sp+7Ch] [bp-9A8h]@377 unsigned int v109; // [sp+80h] [bp-9A4h]@209 DWORD v110; // [sp+84h] [bp-9A0h]@91 int v111; // [sp+88h] [bp-99Ch]@50 DWORD v112; // [sp+8Ch] [bp-998h]@90 unsigned __int32 v113; // [sp+90h] [bp-994h]@310 unsigned __int32 v114; // [sp+94h] [bp-990h]@89 unsigned __int32 v115; // [sp+98h] [bp-98Ch]@84 unsigned __int32 v116; // [sp+9Ch] [bp-988h]@158 int v117; // [sp+A0h] [bp-984h]@34 HMODULE v118; // [sp+A4h] [bp-980h]@65 int v119; // [sp+A8h] [bp-97Ch]@248 unsigned __int32 v120; // [sp+ACh] [bp-978h]@358 unsigned __int32 v121; // [sp+B0h] [bp-974h]@269 unsigned __int32 v122; // [sp+B4h] [bp-970h]@358 unsigned __int32 v123; // [sp+B8h] [bp-96Ch]@231 unsigned __int32 v124; // [sp+BCh] [bp-968h]@358 unsigned __int32 v125; // [sp+C0h] [bp-964h]@20 unsigned __int32 v126; // [sp+C4h] [bp-960h]@352 unsigned __int32 v127; // [sp+C8h] [bp-95Ch]@144 int v128; // [sp+CCh] [bp-958h]@209 unsigned __int32 v129; // [sp+D0h] [bp-954h]@144 unsigned __int32 v130; // [sp+D4h] [bp-950h]@209 unsigned __int32 v131; // [sp+D8h] [bp-94Ch]@181 char v132; // [sp+DCh] [bp-948h]@411 int v133; // [sp+E0h] [bp-944h]@411 DWORD v134; // [sp+E4h] [bp-940h]@261 char *v135; // [sp+E8h] [bp-93Ch]@224 char *v136; // [sp+ECh] [bp-938h]@224 char *v137; // [sp+F0h] [bp-934h]@224 char *v138; // [sp+F4h] [bp-930h]@1 char *v139; // [sp+F8h] [bp-92Ch]@1 char *v140; // [sp+FCh] [bp-928h]@1 char *v141; // [sp+100h] [bp-924h]@1 char *v142; // [sp+104h] [bp-920h]@1 int *v143; // [sp+108h] [bp-91Ch]@409 const void *v144; // [sp+110h] [bp-914h]@1 int v145; // [sp+114h] [bp-910h]@193 int v146; // [sp+118h] [bp-90Ch]@1 size_t v147; // [sp+11Ch] [bp-908h]@209 int v148; // [sp+120h] [bp-904h]@1 int v149; // [sp+124h] [bp-900h]@76 int v150; // [sp+128h] [bp-8FCh]@163 int *v151; // [sp+12Ch] [bp-8F8h]@86 int v152; // [sp+130h] [bp-8F4h]@198 int v153; // [sp+134h] [bp-8F0h]@81 int v154; // [sp+138h] [bp-8ECh]@22 int v155; // [sp+13Ch] [bp-8E8h]@17 int v156; // [sp+140h] [bp-8E4h]@1 char v157; // [sp+144h] [bp-8E0h]@23 __int16 v158; // [sp+146h] [bp-8DEh]@23 int v159; // [sp+148h] [bp-8DCh]@22 int v160; // [sp+14Ch] [bp-8D8h]@1 int v161; // [sp+150h] [bp-8D4h]@208 int v162; // [sp+154h] [bp-8D0h]@1 char v163[4]; // [sp+158h] [bp-8CCh]@1 int v164; // [sp+15Ch] [bp-8C8h]@1 char v165; // [sp+163h] [bp-8C1h]@33 int v166; // [sp+164h] [bp-8C0h]@1 int v167; // [sp+168h] [bp-8BCh]@232 int v168; // [sp+16Ch] [bp-8B8h]@409 int v169; // [sp+170h] [bp-8B4h]@1 int v170; // [sp+174h] [bp-8B0h]@25 DWORD dwErrCode; // [sp+178h] [bp-8ACh]@30 int v172; // [sp+17Ch] [bp-8A8h]@259 int v173; // [sp+180h] [bp-8A4h]@161 int v174; // [sp+184h] [bp-8A0h]@25 int v175; // [sp+18Ch] [bp-898h]@38 int v176; // [sp+1B0h] [bp-874h]@25 LPCWSTR lpFileName; // [sp+1C8h] [bp-85Ch]@1 size_t v178; // [sp+1CCh] [bp-858h]@356 wchar_t v179; // [sp+1D0h] [bp-854h]@252 wchar_t *v180; // [sp+1D4h] [bp-850h]@226 int v181; // [sp+1D8h] [bp-84Ch]@1 int v182; // [sp+1DCh] [bp-848h]@88 int v183; // [sp+1E0h] [bp-844h]@1 int v184; // [sp+1E4h] [bp-840h]@1 int v185; // [sp+1E8h] [bp-83Ch]@1 int v186; // [sp+1ECh] [bp-838h]@70 int v187; // [sp+1F0h] [bp-834h]@55 unsigned int v188; // [sp+1F8h] [bp-82Ch]@101 int v189; // [sp+1FCh] [bp-828h]@103 int v190; // [sp+200h] [bp-824h]@61 unsigned __int16 v191; // [sp+204h] [bp-820h]@63 unsigned __int16 v192; // [sp+206h] [bp-81Eh]@63 char v193; // [sp+20Dh] [bp-817h]@56 unsigned __int16 v194; // [sp+210h] [bp-814h]@59 int v195; // [sp+220h] [bp-804h]@72 wchar_t *Dest; // [sp+224h] [bp-800h]@25 int v197; // [sp+228h] [bp-7FCh]@1 int v198; // [sp+22Ch] [bp-7F8h]@25 char v199; // [sp+230h] [bp-7F4h]@87 int v200; // [sp+234h] [bp-7F0h]@88 wchar_t *v201; // [sp+248h] [bp-7DCh]@1 LPWSTR Str1; // [sp+24Ch] [bp-7D8h]@25 char v203; // [sp+253h] [bp-7D1h]@30 int v204; // [sp+254h] [bp-7D0h]@1 int v205; // [sp+258h] [bp-7CCh]@37 int v206; // [sp+25Ch] [bp-7C8h]@37 int *v207; // [sp+260h] [bp-7C4h]@37 int v208; // [sp+264h] [bp-7C0h]@37 int v209; // [sp+268h] [bp-7BCh]@37 int v210; // [sp+26Ch] [bp-7B8h]@37 char v211; // [sp+270h] [bp-7B4h]@103 int v212; // [sp+278h] [bp-7ACh]@104 int i; // [sp+284h] [bp-7A0h]@81 char v214; // [sp+28Ah] [bp-79Ah]@122 char v215; // [sp+28Bh] [bp-799h]@1 int v216; // [sp+28Ch] [bp-798h]@188 int v217; // [sp+290h] [bp-794h]@1 int v218; // [sp+294h] [bp-790h]@1 int v219; // [sp+298h] [bp-78Ch]@1 int v220; // [sp+29Ch] [bp-788h]@1 int v221; // [sp+2A0h] [bp-784h]@1 int v222; // [sp+2A4h] [bp-780h]@1 int v223; // [sp+2A8h] [bp-77Ch]@1 int v224; // [sp+2ACh] [bp-778h]@1 int v225; // [sp+2B0h] [bp-774h]@86 int v226; // [sp+2B4h] [bp-770h]@375 int v227; // [sp+2B8h] [bp-76Ch]@375 int v228; // [sp+2BCh] [bp-768h]@375 int v229; // [sp+2C0h] [bp-764h]@1 int v230; // [sp+2C4h] [bp-760h]@1 int v231; // [sp+2C8h] [bp-75Ch]@1 int v232; // [sp+2CCh] [bp-758h]@1 int v233; // [sp+2D0h] [bp-754h]@1 int v234; // [sp+2D4h] [bp-750h]@1 char v235; // [sp+2DBh] [bp-749h]@1 int v236; // [sp+2DCh] [bp-748h]@37 int v237; // [sp+2E0h] [bp-744h]@33 int v238; // [sp+2E4h] [bp-740h]@176 int v239; // [sp+2E8h] [bp-73Ch]@36 int v240; // [sp+2ECh] [bp-738h]@1 int v241; // [sp+2F0h] [bp-734h]@1 int v242; // [sp+2F4h] [bp-730h]@1 int v243; // [sp+2F8h] [bp-72Ch]@1 int v244; // [sp+2FCh] [bp-728h]@1 int v245; // [sp+300h] [bp-724h]@1 wchar_t *Source; // [sp+304h] [bp-720h]@1 size_t Size; // [sp+308h] [bp-71Ch]@1 char v248; // [sp+30Eh] [bp-716h]@25 char v249; // [sp+30Fh] [bp-715h]@1 char v250; // [sp+310h] [bp-714h]@37 char v251; // [sp+31Bh] [bp-709h]@30 char *v252; // [sp+31Ch] [bp-708h]@1 char *v253; // [sp+320h] [bp-704h]@1 char *v254; // [sp+324h] [bp-700h]@1 char *v255; // [sp+328h] [bp-6FCh]@1 int *v256; // [sp+32Ch] [bp-6F8h]@1 int *v257; // [sp+330h] [bp-6F4h]@1 int v258; // [sp+334h] [bp-6F0h]@1 int v259; // [sp+338h] [bp-6ECh]@35 int v260; // [sp+33Ch] [bp-6E8h]@106 int v261; // [sp+340h] [bp-6E4h]@107 char v262; // [sp+344h] [bp-6E0h]@324 int v263; // [sp+348h] [bp-6DCh]@3 char v264; // [sp+34Ch] [bp-6D8h]@324 int v265; // [sp+350h] [bp-6D4h]@3 char v266; // [sp+354h] [bp-6D0h]@99 wchar_t *v267; // [sp+358h] [bp-6CCh]@25 int v268; // [sp+35Ch] [bp-6C8h]@1 int v269; // [sp+360h] [bp-6C4h]@181 __int16 v270; // [sp+364h] [bp-6C0h]@154 __int16 v271; // [sp+366h] [bp-6BEh]@358 wchar_t *v272; // [sp+368h] [bp-6BCh]@25 int v273; // [sp+36Ch] [bp-6B8h]@33 int v274; // [sp+370h] [bp-6B4h]@34 char v275; // [sp+377h] [bp-6ADh]@30 char v276; // [sp+378h] [bp-6ACh]@120 char v277; // [sp+37Ch] [bp-6A8h]@67 int v278; // [sp+380h] [bp-6A4h]@370 LPWSTR FilePart; // [sp+384h] [bp-6A0h]@25 int v280; // [sp+388h] [bp-69Ch]@25 int v281; // [sp+38Ch] [bp-698h]@1 int v282; // [sp+390h] [bp-694h]@1 int v283; // [sp+394h] [bp-690h]@1 int v284; // [sp+398h] [bp-68Ch]@1 int v285; // [sp+39Ch] [bp-688h]@1 int v286; // [sp+3A0h] [bp-684h]@25 int v287; // [sp+3A4h] [bp-680h]@1 int v288; // [sp+3A8h] [bp-67Ch]@25 int v289; // [sp+3ACh] [bp-678h]@25 int v290; // [sp+3B0h] [bp-674h]@1 int v291; // [sp+3B4h] [bp-670h]@25 int v292; // [sp+3B8h] [bp-66Ch]@25 char v293; // [sp+3BCh] [bp-668h]@10 char v294; // [sp+3BDh] [bp-667h]@9 char v295; // [sp+3C0h] [bp-664h]@104 char v296; // [sp+68Ch] [bp-398h]@115 int v297; // [sp+6ACh] [bp-378h]@116 int v298; // [sp+6B4h] [bp-370h]@107 int v299; // [sp+6B8h] [bp-36Ch]@107 int v300; // [sp+6BCh] [bp-368h]@107 int v301; // [sp+6C0h] [bp-364h]@107 int v302; // [sp+6C4h] [bp-360h]@109 int v303; // [sp+6C8h] [bp-35Ch]@109 int v304; // [sp+6CCh] [bp-358h]@109 int v305; // [sp+6D0h] [bp-354h]@113 int v306; // [sp+6D4h] [bp-350h]@395 int v307; // [sp+6D8h] [bp-34Ch]@395 int v308; // [sp+6DCh] [bp-348h]@1 char v309; // [sp+6E8h] [bp-33Ch]@224 char v310; // [sp+710h] [bp-314h]@224 char v311; // [sp+716h] [bp-30Eh]@329 char v312; // [sp+734h] [bp-2F0h]@224 __int64 v313; // [sp+73Ch] [bp-2E8h]@107 signed __int16 v314; // [sp+744h] [bp-2E0h]@108 char v315; // [sp+74Ch] [bp-2D8h]@1 int v316; // [sp+760h] [bp-2C4h]@81 char v317; // [sp+770h] [bp-2B4h]@1 int v318; // [sp+784h] [bp-2A0h]@81 char v319; // [sp+794h] [bp-290h]@1 int v320; // [sp+7A8h] [bp-27Ch]@81 char v321; // [sp+7B8h] [bp-26Ch]@1 int v322; // [sp+7CCh] [bp-258h]@81 char v323; // [sp+7DCh] [bp-248h]@1 int v324; // [sp+7F0h] [bp-234h]@81 __int16 v325; // [sp+800h] [bp-224h]@57 CPPEH_RECORD ms_exc; // [sp+A0Ch] [bp-18h]@25 int v327; // [sp+A44h] [bp+20h]@2 v185 = var_0; Size = lpApplicationName; Source = lpCommandLine; v169 = lpProcessAttributes; v164 = lpThreadAttributes; v234 = lpEnvironment; lpFileName = lpCurrentDirectory; v144 = lpStartupInfo; v166 = lpProcessInformation; v148 = var_0_; v290 = 0; v201 = 0; v287 = 0; v233 = 0; v204 = 0; v160 = 0; v146 = dwCreationFlags & 0x8000000; v235 = 0; v162 = 0; v284 = 0; v282 = 0; v249 = 0; v285 = 0; v281 = 0; v283 = 0; v181 = 0; v138 = &v317; v139 = &v321; v140 = &v323; v141 = &v315; v142 = &v319; v229 = 0; v230 = 0; v231 = 0; v232 = 0; v221 = 0; v222 = 0; v223 = 0; v224 = 0; v256 = &v268; v257 = &v258; v252 = &v317; v253 = &v315; v254 = &v321; v255 = &v319; v217 = 0; v218 = 0; v219 = 0; v220 = 0; v197 = 0; v156 = 0; v184 = 0; v183 = 0; *(_DWORD *)v163 = 0; v243 = 0; v244 = 0; v245 = 0; v240 = 0; v241 = 0; v242 = 0; v215 = 0; memset(&v308, 0, 0x60u); *(_DWORD *)lpProcessInformation = 0; *(_DWORD *)(lpProcessInformation + 4) = 0; *(_DWORD *)(lpProcessInformation + 8) = 0; *(_DWORD *)(lpProcessInformation + 12) = 0; if ( var_0_ ) *(_DWORD *)var_0_ = 0; v29 = dwCreationFlags & 0xF7FFFFFF; v327 = v29; if ( (v29 & 0x18) == 24 ) goto LABEL_279; v265 = 0; v263 = 0; if ( v29 & 0x40 ) { v294 = 1; } else { if ( BYTE1(v29) & 0x40 ) { v294 = 5; } else { if ( v29 & 0x20 ) { v294 = 2; } else { if ( SBYTE1(v29) < 0 ) { v294 = 6; } else { if ( (char)v29 < 0 ) { v294 = 3; } else { if ( BYTE1(v29) & 1 ) v294 = (BasepIsRealtimeAllowed(0) != 0) + 3; else v294 = 0; } } } } } v293 = 0; LOWORD(v327) = v327 & 0x3E1F; if ( v327 & 0x800 ) { if ( !(v327 & 0x1000) ) goto LABEL_13; LABEL_279: SetLastError(0x57u); return 0; } if ( !(v327 & 0x1000) && *(_BYTE *)(BaseStaticServerData + 6644) ) v327 |= 0x800u; LABEL_13: if ( !(v327 & 0x800) && NtQueryInformationJobObject(0, 4, &v88, 4, 0) != -1073741790 ) v327 = v327 & 0xFFFFEFFF | 0x800; if ( !v234 || BYTE1(v327) & 4 ) goto LABEL_25; v30 = v234; v155 = v234; while ( *(_BYTE *)v30 || *(_BYTE *)(v30 + 1) ) ++v30; LOWORD(v154) = v30 - (_WORD)v234 + 1; v12 = v30 - (_WORD)v234 + 2; HIWORD(v154) = v12; v86 = 2 * v12; v159 = 0; v13 = NtAllocateVirtualMemory(-1, &v159, 0, &v86, 4096, 4); if ( v13 < 0 ) { v84 = v13; LABEL_290: BaseSetLastNTError(v84); return 0; } v158 = v86; v14 = RtlAnsiStringToUnicodeString(&v157, &v154, 0); if ( v14 < 0 ) { NtFreeVirtualMemory(-1, &v159, &v86, 32768); v84 = v14; goto LABEL_290; } v234 = v159; LABEL_25: v289 = 0; v291 = 0; v292 = 0; v288 = 0; v198 = 0; Str1 = 0; v267 = 0; v280 = 1; v286 = 0; v170 = 0; FilePart = 0; v272 = 0; v248 = 0; Dest = 0; ms_exc.disabled = 0; memcpy(&v174, v144, 0x44u); if ( BYTE1(v176) & 1 && BYTE1(v176) & 6 ) BYTE1(v176) &= 0xFEu; while ( 1 ) { if ( Str1 ) { v123 = __readfsdword(24); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v123 + 48) + 24), 0, Str1); Str1 = 0; } if ( v198 ) { v104 = __readfsdword(24); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v104 + 48) + 24), 0, v198); v198 = 0; } if ( v289 ) { NtClose(v289); v289 = 0; } dwErrCode = 0; v203 = 1; v251 = 0; v275 = 0; if ( Size ) { if ( !Source || !*Source ) { v275 = 1; Source = (wchar_t *)Size; } goto LABEL_33; } v121 = __readfsdword(24); Str1 = (LPWSTR)RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v121 + 48) + 24), BaseDllTag, 520); if ( !Str1 ) goto LABEL_293; v65 = (size_t)Source; Size = (size_t)Source; v201 = Source; v61 = Source; v180 = Source; if ( *Source != 34 ) goto LABEL_271; v203 = 0; v61 = Source + 1; v180 = Source + 1; Size = (size_t)(Source + 1); while ( *v61 ) { if ( *v61 == 34 ) { v201 = v61; v248 = 1; break; } ++v61; v180 = v61; v201 = v61; } LABEL_259: v179 = *v201; *v201 = 0; v62 = SearchPathW(0, (LPCWSTR)Size, L".exe", 0x104u, Str1, 0); v63 = 2 * v62; v172 = 2 * v62; if ( 2 * v62 && (unsigned int)v63 < 0x208 ) { v64 = GetFileAttributesW(Str1); v134 = v64; if ( v64 != -1 && v64 & 0x10 ) { v63 = 0; } else { v172 = v63 + 1; v63 += 2; } v172 = v63; } if ( !v63 || (unsigned int)v63 >= 0x208 ) { v119 = RtlDetermineDosPathNameType_U(Size); if ( v119 == 5 ) goto LABEL_249; v67 = CreateFileW((LPCWSTR)Size, 0x80000000u, 3u, 0, 3u, 0x80u, 0); v102 = v67; if ( v67 != (HANDLE)-1 ) { CloseHandle(v67); LABEL_249: BaseSetLastNTError(-1073741772); } if ( dwErrCode ) SetLastError(dwErrCode); else dwErrCode = GetLastError(); *v201 = v179; Size = (size_t)Str1; if ( !*v61 || !v203 ) goto LABEL_173; ++v61; v180 = v61; v201 = v61; v251 = 1; v248 = 1; v65 = (size_t)Source; LABEL_271: Size = v65; while ( 1 ) { v66 = *v61; if ( !*v61 ) goto LABEL_259; if ( v66 == 32 || v66 == 9 ) { v201 = v61; goto LABEL_259; } ++v61; v180 = v61; v201 = v61; } } *v201 = v179; Size = (size_t)Str1; if ( BasepIsSetupInvokedByWinLogon(Str1) && !(BYTE3(v327) & 0x80) ) BYTE3(v327) |= 0x80u; LABEL_33: v15 = Size; v165 = RtlDosPathNameToNtPathName_U(Size, &v273, 0, &v237); if ( !v165 ) { v83 = 3; goto LABEL_278; } v198 = v274; RtlInitUnicodeString(&v268, v15); v16 = RtlDetermineDosPathNameType_U(v15); v117 = v16; if ( v16 != 2 && v16 != 1 ) { if ( !v197 ) { v94 = __readfsdword(24); v197 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v94 + 48) + 24), 0, 522); if ( !v197 ) { v83 = 8; goto LABEL_278; } } RtlGetFullPathName_U(v15, 522, v197, 0); RtlInitUnicodeString(&v268, v197); } v258 = v273; v259 = v274; if ( (_WORD)v237 ) { v273 = v237; v274 = v238; } else { v239 = 0; } v205 = 24; v206 = v239; v208 = 64; v207 = &v273; v209 = 0; v210 = 0; v236 = NtOpenFile(&v289, 1048737, &v205, &v250, 5, 96); if ( v236 < 0 ) { v236 = NtOpenFile(&v289, 1048608, &v205, &v250, 5, 96); if ( v236 < 0 ) break; } if ( !v175 ) { v115 = __readfsdword(24); v175 = *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v115 + 48) + 16) + 124); } v17 = NtCreateSection(&v291, 983071, 0, 0, 16, 16777216, v289); v236 = v17; if ( v17 < 0 ) goto LABEL_425; v18 = BasepIsProcessAllowed(Size); v17 = v18; v236 = v18; if ( v18 < 0 ) { BaseSetLastNTError(v18); NtClose(v291); goto LABEL_173; } if ( BYTE1(v327) & 0x20 && *(_BYTE *)(BaseStaticServerData + 6645) ) { BYTE1(v327) &= 0xCFu; v19 = 2048; v327 |= 0x800u; v17 = -1073741519; v236 = -1073741519; v160 = 1; NtClose(v291); v291 = 0; } else { LABEL_425: v19 = 2048; } if ( !v249 ) { if ( v17 >= 0 || v17 == -1073741521 && !BaseIsDosApplication(&v273, -1073741521) ) { if ( v284 ) { v100 = __readfsdword(24); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v100 + 48) + 24), 0, v284); v284 = 0; } if ( v285 ) { v113 = __readfsdword(24); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v113 + 48) + 24), 0, v285); v285 = 0; } v20 = BasepCheckBadapp(v289, v274, v234, &v284, &v282, &v285, &v281); v90 = v20; if ( v20 < 0 ) { if ( v20 == -1073741790 ) SetLastError(0x4C7u); else BaseSetLastNTError(v20); if ( v291 ) { NtClose(v291); v291 = 0; } goto LABEL_173; } } if ( !v249 && !(BYTE3(v327) & 2) ) { v21 = BasepCheckWinSaferRestrictions(v185, Size, v289, &v156, &v183, &v184); v111 = v21; if ( v21 == -1 ) { SetLastError(0x4ECu); } else { if ( v21 >= 0 ) goto LABEL_52; BaseSetLastNTError(v21); } v214 = 0; goto LABEL_126; } } LABEL_52: if ( v17 >= 0 ) goto LABEL_53; if ( v17 == -1073741541 ) goto LABEL_198; if ( v17 <= -1073741522 ) goto LABEL_277; if ( v17 <= -1073741520 ) { LABEL_198: v54 = 1; v152 = 1; if ( v17 != -1073741520 ) { if ( v17 != -1073741541 ) { v54 = BaseIsDosApplication(&v273, v17); v152 = v54; if ( !v54 ) { v55 = (unsigned int)(unsigned __int16)v273 >> 1; v56 = (const wchar_t *)(v274 + 2 * v55 - 8); v98 = v274 + 2 * v55 - 8; if ( (unsigned __int16)v273 < 8u || __wcsnicmp((const wchar_t *)(v274 + 2 * v55 - 8), L".bat", 4u) && __wcsnicmp(v56, L".cmd", 4u) ) goto LABEL_277; v57 = v275 || v248; if ( v275 || (v161 = 0, v248) ) v161 = 1; v147 = _wcslen(Source); v58 = _wcslen(Str); v109 = v161 + v147 + v58 + v57 + 1; v130 = __readfsdword(24); v59 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v130 + 48) + 24), BaseDllTag, 2 * v109); v60 = (wchar_t *)v59; v128 = v59; if ( !v59 ) goto LABEL_293; _wcscpy((wchar_t *)v59, Str); if ( v275 || v248 ) _wcscat(v60, L"\""); _wcscat(v60, Source); if ( v275 || v248 ) _wcscat(v60, L"\""); RtlInitUnicodeString(&v270, v60); LABEL_215: Source = v272; Size = 0; goto LABEL_216; } } } v204 = 16; if ( !BaseCreateVDMEnvironment(v234, &v264, &v262) || !BaseCheckVDM(v54 | 0x10, Size, Source, lpFileName, &v264, &v296, &v287, v327, &v174) ) goto LABEL_173; v68 = &v298; v69 = (v311 & 7) - 1; if ( (v311 & 7) == 1 ) { v233 = 1; if ( v327 & 8 ) { v83 = 5; goto LABEL_278; } if ( !BaseGetVdmConfigInfo(Source, v287, 16, &v266, &v286) ) goto LABEL_344; Source = v267; Size = 0; LABEL_346: v35 = v290; goto LABEL_347; } goto LABEL_330; } if ( v17 == -1073741519 ) { if ( BYTE1(v327) & 0x20 ) goto LABEL_198; v235 = 1; if ( !BaseCreateVDMEnvironment(v234, &v264, &v262) ) goto LABEL_173; while ( 1 ) { v204 = (v19 & v327) != 0 ? 64 : 32; if ( BaseCheckVDM((v19 & v327) != 0 ? 64 : 32, Size, Source, lpFileName, &v264, &v296, &v287, v327, &v174) ) break; if ( v204 != 32 || GetLastError() != 5 ) goto LABEL_173; v327 |= v19; } v68 = &v298; v69 = (v311 & 7) - 1; if ( (v311 & 7) == 1 ) { v233 = 1; if ( v160 ) v286 = 1; if ( !BaseGetVdmConfigInfo(Source, v287, v204, &v266, &v286) ) { LABEL_344: v82 = v17; goto LABEL_180; } Source = v267; Size = 0; BYTE3(v327) |= 8u; v327 &= 0xFFFFFFE7u; v176 |= 0x40u; goto LABEL_346; } LABEL_330: v70 = v69 - 1; if ( !v70 ) { v83 = 21; goto LABEL_278; } if ( v70 != 2 ) goto LABEL_346; v233 = 4; v35 = v68[3]; v290 = v35; LABEL_347: --v286; if ( v35 ) goto LABEL_122; bInheritHandles = 0; if ( v234 && !(BYTE1(v327) & 4) ) RtlDestroyEnvironment(v234); v234 = v263; LABEL_216: v249 = 1; } else { if ( v17 != -1073741209 ) goto LABEL_277; SetLastError(0x10FEu); LABEL_53: if ( !v235 && v19 & v327 ) BYTE1(v327) &= 0xF7u; v22 = 1; v23 = NtQuerySection(v291, 1, &v187, 48, 0); v236 = v23; if ( v23 < 0 ) goto LABEL_242; if ( v193 & 0x20 ) goto LABEL_277; v325 = 0; if ( !(v327 & 3) || (v126 = __readfsdword(24), *(_BYTE *)(*(_DWORD *)(v126 + 48) + 1)) ) LdrQueryImageFileExecutionOptions(&v273, L"Debugger", 1, &v325, 520, 0); if ( v194 < v7FFE002C || v194 > v7FFE002E ) { v168 = 6; v143 = &v273; NtRaiseHardError(1073741859, 1, 1, &v143, 1, &v168); v96 = __readfsdword(24); if ( *(_DWORD *)(*(_DWORD *)(v96 + 48) + 184) <= 3u ) LABEL_277: v83 = 193; else v83 = 216; goto LABEL_278; } if ( v190 != 2 && v190 != 3 ) { NtClose(v291); v291 = 0; if ( v190 != 7 ) { v83 = 129; goto LABEL_278; } if ( !BuildSubSysCommandLine(L"POSIX /P ", Size, Source, &v270) ) goto LABEL_173; goto LABEL_215; } if ( !BasepIsImageVersionOk(v192, v191) ) goto LABEL_277; if ( !v325 ) { v24 = LoadLibraryA("advapi32.dll"); v25 = v24; v118 = v24; if ( v24 ) { if ( GetProcAddress(v24, "CreateProcessAsUserSecure") ) { v236 = NtQuerySystemInformation(71, &v277, 4, 0); if ( !v236 ) v215 = 1; } FreeLibrary(v25); } v186 = BaseFormatObjectAttributes(&v205, v169, 0); if ( v215 && v185 && v169 ) { v243 = *(_DWORD *)v169; v244 = *(_DWORD *)(v169 + 4); v245 = *(_DWORD *)(v169 + 8); v244 = 0; v186 = BaseFormatObjectAttributes(&v205, &v243, 0); v22 = 1; } v195 = 0; if ( BYTE3(v327) & 1 ) v195 = v22; if ( v327 & 3 ) { v23 = DbgUiConnectToDbg(); v236 = v23; if ( v23 < 0 ) goto LABEL_242; v162 = DbgUiGetThreadDebugObject(); if ( v327 & 2 ) v195 |= 2u; } if ( bInheritHandles ) v195 |= 4u; v149 = v194 == 332 ? v284 : 0; v26 = -1; v27 = NtCreateProcessEx(&v292, 2035711, v186, -1, v195, v291, v162, 0, v156); v236 = v27; if ( v27 < 0 ) goto LABEL_426; if ( v294 ) { v167 = 0; if ( v294 == 4 ) v167 = BasepIsRealtimeAllowed(v22); v236 = NtSetInformationProcess(v292, 18, &v293, 2); if ( v167 ) BasepReleasePrivilege(v167); if ( v236 < 0 ) { v85 = v236; LABEL_363: BaseSetLastNTError(v85); LABEL_364: v81 = v26; goto LABEL_174; } } if ( BYTE3(v327) & 4 ) { v145 = v22; NtSetInformationProcess(v292, 12, &v145, 4); } if ( v204 ) { v290 = v292; if ( !BaseUpdateVDMEntry(v22, &v290, v287, v204) ) { v290 = 0; goto LABEL_364; } v233 |= 2u; } if ( v286 ) { v278 = v286; v27 = NtAllocateVirtualMemory(v292, &v280, 0, &v278, 8192, 64); v236 = v27; if ( v27 < 0 ) goto LABEL_426; } v318 = (unsigned __int16)v268 + 20; v322 = (unsigned __int16)v268 + 16; v324 = (unsigned __int16)v268 + 2; v316 = (unsigned __int16)v258 + 20; v320 = (unsigned __int16)v258 + 16; v28 = 0; v153 = 0; for ( i = 0; i != 5; ++i ) { v28 += *((_DWORD *)(&v138)[4 * i] + 5); v153 = v28; } v116 = __readfsdword(24); v181 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v116 + 48) + 24), 0, v28); if ( !v181 ) { v85 = -1073741801; goto LABEL_363; } i = 0; while ( i != 5 ) { v45 = (int *)&(&v138)[4 * i]; v46 = *v45; v47 = *(_DWORD *)(*v45 + 20); v173 = *(_DWORD *)(*v45 + 20); if ( i ) v48 = *(_DWORD *)(*(v45 - 1) + 8) + *(_DWORD *)(*(v45 - 1) + 20); else v48 = v181; v150 = v48; v49 = v47 & 0xFFFFFFFE; v173 = v49; if ( (unsigned int)v49 > 0xFFFE ) { v49 = 65534; v173 = 65534; } if ( (unsigned int)v49 < 2 ) { v48 = v46 + 32; v150 = v46 + 32; v49 = 2; v173 = 2; } v50 = *v45; *(_DWORD *)(*v45 + 8) = v48; *(_DWORD *)(v50 + 16) = v49; *(_DWORD *)(v50 + 12) = v48; *(_DWORD *)(v50 + 20) = v49; *(_DWORD *)(v50 + 4) = v48; if ( v48 ) **(_WORD **)(v46 + 4) = 0; v51 = *v45; *(_WORD *)v51 = 0; *(_WORD *)(v51 + 2) = v49; ++i; v26 = -1; } v230 = v292; v229 = v289; v231 = v291; if ( v285 ) { v225 = v268; v226 = v269; v227 = v285; v228 = v281; } v151 = &v308; v27 = BasepSxsCreateProcessCsrMessage( v285 != 0 ? (int)&v225 : 0, 0, &v252, &v221, &v256, &v229, &v254, &v217, &v323, &v308); v236 = v27; if ( v27 < 0 || (v27 = NtQueryInformationProcess(v292, 0, &v199, 24, 0), v236 = v27, v27 < 0) ) { LABEL_426: v85 = v27; goto LABEL_363; } v182 = v200; if ( lpFileName ) { v114 = __readfsdword(24); v31 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v114 + 48) + 24), BaseDllTag, 522); v170 = v31; if ( !v31 ) { LABEL_293: v82 = -1073741801; goto LABEL_180; } v112 = GetFullPathNameW(lpFileName, 0x104u, (LPWSTR)v31, &FilePart); if ( v112 > 0x104 || (v32 = GetFileAttributesW((LPCWSTR)v31), v110 = v32, v32 == -1) || !(v32 & 0x10) ) { v83 = 267; goto LABEL_278; } } if ( v251 || v275 ) { v73 = __readfsdword(24); v74 = v73; v108 = v73; v75 = _wcslen(Source); v76 = (wchar_t *)RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v74 + 48) + 24), 0, 2 * v75 + 6); Dest = v76; if ( v76 ) { _wcscpy(v76, L"\""); v77 = v201; if ( v251 ) { v179 = *v201; *v201 = 0; } _wcscat(Dest, Source); _wcscat(Dest, L"\""); if ( v251 ) { *v77 = v179; _wcscat(Dest, v77); } } else { if ( v251 ) v251 = 0; if ( v275 ) v275 = 0; } } if ( *(_BYTE *)v151 & 1 ) *(_DWORD *)v163 |= 1u; if ( v251 || (v33 = (int)Source, v275) ) v33 = (int)Dest; if ( !BasePushProcessParameters( v163[0], v292, v182, (LPCWSTR)Size, v170, v33, v234, (int)&v174, v327 | v146, bInheritHandles, v235 != 0 ? 2 : 0, v149, v282) ) goto LABEL_173; RtlFreeUnicodeString(&v266); v267 = 0; if ( !v204 ) { if ( !bInheritHandles ) { if ( !(BYTE1(v176) & 1) ) { if ( !(v327 & 0x8000018) ) { if ( v190 == 3 ) { v236 = NtReadVirtualMemory(v292, v182 + 16, &v216, 4, 0); if ( v236 >= 0 ) { v103 = __readfsdword(24); if ( (*(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v103 + 48) + 16) + 24) & 0x10000003) != 3 ) { v101 = __readfsdword(24); StuffStdHandle(v292, *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v101 + 48) + 16) + 24), v216 + 24); } v99 = __readfsdword(24); if ( (*(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v99 + 48) + 16) + 28) & 0x10000003) != 3 ) { v97 = __readfsdword(24); StuffStdHandle(v292, *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v97 + 48) + 16) + 28), v216 + 28); } v95 = __readfsdword(24); if ( (*(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v95 + 48) + 16) + 32) & 0x10000003) != 3 ) { v93 = __readfsdword(24); StuffStdHandle(v292, *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v93 + 48) + 16) + 32), v216 + 32); } } } } } } } v34 = 262144; if ( v188 >= 0x40000 ) v34 = v188; v23 = BaseCreateStack(v292, v189, v34, &v211); v91 = v23; if ( v23 < 0 ) goto LABEL_242; BaseInitializeContext(&v295, v182, v187, v212, 0); v186 = BaseFormatObjectAttributes(&v205, v164, 0); if ( v215 && v185 && v164 ) { v240 = *(_DWORD *)v164; v241 = *(_DWORD *)(v164 + 4); v242 = *(_DWORD *)(v164 + 8); v241 = 0; v186 = BaseFormatObjectAttributes(&v205, &v240, 0); } v23 = NtCreateThread(&v288, 2032639, v186, v292, &v260, &v295, &v211, 1); v236 = v23; if ( v23 < 0 ) goto LABEL_242; v313 = v182; v298 = v292; v299 = v288; v300 = v260; v301 = v261; switch ( v194 ) { case 0x14Cu: v314 = 0; break; case 0x200u: v314 = 6; break; case 0x8664u: v314 = 9; break; default: DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n", v194); v314 = -1; break; } v304 = v327 & 0xFFFFFFFC; v302 = 0; v303 = 0; if ( v190 == 2 || v235 ) { v298 |= 2u; v52 = GetModuleHandleA(0); v53 = RtlImageNtHeader(v52); v89 = v53; if ( v53 ) { if ( *(_WORD *)(v53 + 92) == 2 ) v298 |= 1u; } } if ( v176 & 0x40 ) v298 |= 1u; if ( v176 & 0x80 ) v298 &= 0xFFFFFFFEu; v305 = v204; if ( v204 ) { if ( v287 ) { v78 = 0; } else { v87 = __readfsdword(24); v78 = *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v87 + 48) + 16) + 16); } v307 = v78; v306 = v287; } memcpy(&v298, &v298, 0x98u); if ( v308 ) { v135 = &v309; v136 = &v310; v137 = &v312; v23 = CsrCaptureMessageMultiUnicodeStringsInPlace(&v283, 3, &v135); v236 = v23; if ( v23 < 0 ) { LABEL_242: v82 = v23; LABEL_180: BaseSetLastNTError(v82); goto LABEL_173; } } CsrClientCallServer(&v296, v283, 65536, 152); if ( v283 ) { CsrFreeCaptureBuffer(v283); v283 = 0; } if ( v297 < 0 ) { BaseSetLastNTError(v297); NtTerminateProcess(v292, v297); goto LABEL_173; } if ( v183 ) { if ( !v185 ) { v79 = BasepReplaceProcessThreadTokens(v183, v292, v288); v80 = v79; v236 = v79; if ( v79 < 0 ) { NtTerminateProcess(v292, v79); v82 = v80; goto LABEL_180; } } } if ( v184 ) { v236 = NtAssignProcessToJobObject(v184, v292); if ( v236 < 0 ) { NtTerminateProcess(v292, -1073741790); LABEL_179: v82 = v236; goto LABEL_180; } } if ( !(v327 & 4) ) NtResumeThread(v288, &v276); v35 = v290; LABEL_122: v214 = 1; if ( v233 ) v233 |= 8u; ms_exc.disabled = 1; v36 = v166; if ( v35 ) { if ( v204 == 32 ) { *(_DWORD *)v166 = v35 | 2; if ( v233 & 4 ) { v260 = 0; v261 = 0; } } else { *(_DWORD *)v166 = v35 | 1; } if ( v292 ) NtClose(v292); } else { *(_DWORD *)v166 = v292; } *(_DWORD *)(v36 + 4) = v288; *(_DWORD *)(v36 + 8) = v260; *(_DWORD *)(v36 + 12) = v261; v292 = 0; v288 = 0; ms_exc.disabled = 0; LABEL_126: ms_exc.disabled = -1; if ( v197 ) { v269 = 0; v268 = 0; v131 = __readfsdword(24); v37 = RtlFreeHeap; RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v131 + 48) + 24), 0, v197); v197 = 0; } else { v37 = RtlFreeHeap; } if ( !v204 ) { BasepSxsCloseHandles(&v221); BasepSxsCloseHandles(&v217); if ( v181 ) { i = 0; do { v38 = (int *)&(&v138)[4 * i]; v39 = *v38; if ( *v38 ) { v40 = v39 + 8; if ( v39 != -8 && *(_DWORD *)v40 ) { if ( *(_DWORD *)(v39 + 8) != *(_DWORD *)(v39 + 12) ) { v133 = *(_DWORD *)v40; RtlFreeUnicodeString(&v132); } v41 = *v38; *(_DWORD *)(*v38 + 8) = *(_DWORD *)(*v38 + 12); *(_DWORD *)(v41 + 16) = *(_DWORD *)(v41 + 20); } v42 = *(_DWORD *)(*v38 + 12); *(_DWORD *)(*v38 + 4) = v42; if ( v42 ) **(_WORD **)(v39 + 4) = 0; v43 = *v38; *(_WORD *)v43 = 0; *(_WORD *)(v43 + 2) = *(_WORD *)(v43 + 20); } ++i; } while ( i != 5 ); v92 = __readfsdword(24); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v92 + 48) + 24), 0, v181); v37 = RtlFreeHeap; } } if ( v234 && !(BYTE1(v327) & 4) ) { RtlDestroyEnvironment(v234); v234 = 0; } v129 = __readfsdword(24); v37(*(_DWORD *)(*(_DWORD *)(v129 + 48) + 24), 0, Dest); v107 = __readfsdword(24); v37(*(_DWORD *)(*(_DWORD *)(v107 + 48) + 24), 0, Str1); v127 = __readfsdword(24); v37(*(_DWORD *)(*(_DWORD *)(v127 + 48) + 24), 0, v170); v106 = __readfsdword(24); v37(*(_DWORD *)(*(_DWORD *)(v106 + 48) + 24), 0, v198); if ( v289 ) NtClose(v289); if ( v291 ) NtClose(v291); if ( v288 ) { NtTerminateProcess(v292, 0); NtClose(v288); } if ( v292 ) NtClose(v292); if ( v184 ) NtClose(v184); if ( v183 ) { if ( v185 ) *(_DWORD *)v148 = v183; else NtClose(v183); } if ( v284 ) { v125 = __readfsdword(24); v37(*(_DWORD *)(*(_DWORD *)(v125 + 48) + 24), 0, v284); } if ( v285 ) { v105 = __readfsdword(24); v37(*(_DWORD *)(*(_DWORD *)(v105 + 48) + 24), 0, v285); } RtlFreeUnicodeString(&v266); result = RtlFreeUnicodeString(&v270); if ( v265 || v263 ) result = BaseDestroyVDMEnvironment(&v264, &v262); if ( v233 ) { if ( !(v233 & 8) ) { result = BaseUpdateVDMEntry(0, &v287, v233, v204); if ( v290 ) result = NtClose(v290); } } return result; } v178 = _wcslen(Source); if ( !v178 ) { Source = (wchar_t *)Size; v178 = _wcslen((const wchar_t *)Size); } v71 = _wcslen((const wchar_t *)&v325); v72 = 2 * (v178 + v71 + 3); v178 = v72; v124 = __readfsdword(24); v272 = (wchar_t *)RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v124 + 48) + 24), BaseDllTag, v72); v270 = 0; v271 = v72; RtlAppendUnicodeToString(&v270, &v325); RtlAppendUnicodeToString(&v270, L" "); RtlAppendUnicodeToString(&v270, Source); Source = v272; Size = 0; NtClose(v291); v291 = 0; v122 = __readfsdword(24); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v122 + 48) + 24), 0, Str1); Str1 = 0; v120 = __readfsdword(24); RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v120 + 48) + 24), 0, v198); v198 = 0; } } if ( !RtlIsDosDeviceName_U(v15) ) goto LABEL_179; v83 = 1200; LABEL_278: SetLastError(v83); LABEL_173: v81 = -1; LABEL_174: _local_unwind2(&ms_exc.prev_er, v81); return 0; }
1. 函數的一開始是一大段臨時變量聲明,而後將從CreateProcessInternalA傳過來的參數保存到局部變量中。
... v185 = var_0; Size = lpApplicationName; Source = lpCommandLine; v169 = lpProcessAttributes; v164 = lpThreadAttributes; v234 = lpEnvironment; lpFileName = lpCurrentDirectory; v144 = lpStartupInfo; v166 = lpProcessInformation; v148 = var_0_; v290 = 0; v201 = 0; v287 = 0; v233 = 0; v204 = 0; v160 = 0; v146 = dwCreationFlags & 0x8000000; ...
Ps: 插個題外話: v146 = dwCreationFlags & 0x8000000;這句話的意思是判斷dwCreationFlags的最高位是否是1,這裏使用了"與運算符"來達到目的
2. 對lpProcessInformation字段進行初始化賦值,lpProcessInformation字段是咱們傳入的一個數據結構的引用,操做系統在建立完進程以後,會在這個數據結構中填入關於"此次"建立的進程的一些相關信息
*(_DWORD *) lpProcessInformation = 0; *(_DWORD *)(lpProcessInformation + 4) = 0; *(_DWORD *)(lpProcessInformation + 8) = 0; *(_DWORD *)(lpProcessInformation + 12) = 0;
A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.
關於這個數據結構的定義以下:
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
3. dwCreationFlags字段的檢測和判斷
dwCreationFlags 的值至少由一個標誌組合成(一些建立標誌和優先級類型)。
3.1 首先屏蔽 CREATE_NO_WINDOW 標誌,代碼以下:
v29 = dwCreationFlags & 0xF7FFFFFF; v327 = v29;
由於: CREATE_NO_WINDOW = 0x08000000,因此,爲了將這個宏表明的0x08000000給置0,因此採用了0xF7FFFFFF,這也是一種"位操做"的思想。
3.2 判斷dwCreationFlags中的非法位組合
通過屏蔽標誌位後,判斷dwCreationFlags中是否包含CREATE_NEW_CONSOLE | DETACHED_PROCESS的組合, 若是包含它們的組合(參考MSDN上的說明), 存在這種組合是不合法的, 所以跳轉到錯誤處理中:
if ( (v29 & 0x18) == 24 ) //24 = DETACHED_PROCESS | CREATE_NEW_CONSOLE goto LABEL_279;
咱們繼續跟蹤錯誤代碼 LABEL_279:
LABEL_279: SetLastError(0x57u); return 0;
繼續跟進SetLastError()函數.
void __stdcall SetLastError(DWORD dwErrCode) { unsigned __int32 v1; // edi@1 v1 = __readfsdword(24); if ( g_dwLastErrorToBreakOn && dwErrCode == g_dwLastErrorToBreakOn ) DbgBreakPoint(); if ( *(_DWORD *)(v1 + 52) != dwErrCode ) *(_DWORD *)(v1 + 52) = dwErrCode; }
將57h與Teb->LastErrorValue想比較,若是不相等,就更新LastErrorValue的值爲57h, 實際上GetLastError() 函數的返回的錯誤碼就是從Teb->LastErrorValue獲取的。
3.3 判斷dwCreationFlags中的優先級類型的組合
在一開始提到判斷dwCreationFlags中包含了"建立標誌"和"優先級",接下來代碼先判斷優先級,咱們接着分析:
.text:7C8199A6 mov [ebp+var_6D4], ebx .text:7C8199AC mov [ebp+var_6DC], ebx .text:7C8199B2 test al, 40h .text:7C8199B4 jnz IsIdlePriority //優先級爲IDLE_PRIORITY_CLASS .text:7C8199B4 .text:7C8199BA test ah, 40h .text:7C8199BD jnz loc_7C84278E .text:7C8199BD .text:7C8199C3 test al, 20h .text:7C8199C5 jnz IsNormalPriority //優先級爲NORMAL_PRIORITY_CLASS .text:7C8199C5 .text:7C8199CB test ah, ah .text:7C8199CD js loc_7C84279A .text:7C8199CD .text:7C8199D3 test al, al .text:7C8199D5 js IsHighPriotity //優先級爲HIGH_PRIORITY_CLASS .text:7C8199D5 .text:7C8199DB test ah, 1 .text:7C8199DE jnz IsRealTimePriority //優先級爲REALTIME_PRIORITY_CLASS
判斷的順序依次是
IDLE_PRIORITY_CLASS -> NORMAL_PRIORITY_CLASS -> HIGH_PRIORITY_CLASS -> REALTIME_PRIORITY_CLASS
只要知足其中一個優先級,就跳過其餘優先級的判斷,若是都不知足, 將權限級置爲0.
(當知足IDLE_PRIORITY_CLASS時),置優先級標誌爲1. (當知足NORMAL_PRIORITY_CLASS時),置優先級標誌爲2. (當知足HIGH_PRIORITY_CLASS時),置優先級標誌3. (當知足REALTIME_PRIORITY_CLASS時),因爲該優先級別很高,比操做系統優先級還高(參考MSDN),做了以下操做, 申請堆空間 -->打開一個令牌對象與線程關聯,並返回一個句柄可用於訪問
該令牌。-->調整優先級令牌。 置優先級標誌4。
這裏回想一點咱們以前說的關於進程建立時若是指定了太高的優先級時,操做系統是怎麼處理的:
3. 若是爲新進程指定了Real-Time優先級類別,可是該進程的調用者沒有"Increase Scheduling Priority(增長調度優先級)"特權,則使用High優先級類別。換句話說,CreateProcess 不會僅僅由於調用者沒有足夠的特權來建立Real-Time優先級類別的進程而失敗,而是自動"下降"一點,新進程只是沒有Real-Time那麼高的優先級而已
3.4 判斷dwCreationFlags中的建立標誌的組合
繼續判斷dwCreationFlag的狀況,首先在dwCreationFlag過濾掉表示優先級的標誌位,而後在判斷是什麼建立標誌。用與運算符把全部不屬於建立標誌的位都置0
LOWORD(dwCreationFlagsa) = dwCreationFlagsa & 0x3E1F;
判斷CREATE_SEPARATE_WOW_VDM / CREATE_SHARED_WOW_VDM(這兩個位是用來表示16bit的windows程序)
.text:7C8199F6 mov edi, 800h .text:7C8199FB mov esi, 1000h .text:7C819A00 test [ebp+dwCreationFlags], edi .text:7C819A03 jnz loc_7C8427CA //dwCreationFlag = CREATE_SEPARATE_WOW_VDM .text:7C819A03 .text:7C819A09 test [ebp+dwCreationFlags], esi .text:7C819A0C jnz short loc_7C819A1F //dwCreationFlag = CREATE_SHARED_WOW_VDM .text:7C819A0C .text:7C819A0E mov eax, _BaseStaticServerData .text:7C819A13 cmp [eax+19F4h], bl .text:7C819A19 jnz loc_7C8427D4
4. 判斷lpEnvironment,和可執行程序的路徑相關的處理
判斷lpEnvironment是否爲空, 若是不爲空,將lpEnvironment對應的ANSI字符串轉換爲UNICODE_STRING, 爲空的話跳過這一步,接着調用RtlDosPathNameToNtPathName_U函數將DOS路徑轉換爲NT路徑,因爲用戶給定的路徑通常都是DOS路徑,而內核須要的是NT路徑,所以須要轉換一下。
if ( !lpEnvironment_ || BYTE1(dwCreationFlagsa) & 4 ) //判斷lpEnvironment是否爲空 { ... v13 = NtAllocateVirtualMemory(-1, &v161, 0, &v88, 4096, 4); //申請存儲UNICODE的空間 ... v14 = RtlAnsiStringToUnicodeString(&v159, &v156, 0); //將ANSI轉換爲UNICODE if ( v14 < 0 ) { NtFreeVirtualMemory(-1, &v161, &v88, 32768); //釋放臨時的存儲空間 .. } .. goto LABEL_33; ... LABEL_33: v15 = Size; v167 = RtlDosPathNameToNtPathName_U(Size, &v275, 0, &v239); //將DOS路徑轉換爲NT路徑
這個關鍵函數的聲明以下:
NTSTATUS NtAllocateVirtualMemory( __in HANDLE ProcessHandle, __inout PVOID *BaseAddress, __in ULONG_PTR ZeroBits, __inout PSIZE_T RegionSize, __in ULONG AllocationType, __in ULONG Protect );
NTSTATUS RtlAnsiStringToUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
IN PANSI_STRING SourceString,
IN BOOLEAN AllocateDestinationString
);
RtlDosPathNameToNtPathName_U是一個windows未公開的函數,若是咱們本身須要使用,須要本身在頭文件中定義
typedef NTSTATUS (*DOSPATH_TO_NTPATH)( IN PCWSTR DosPathName, OUT PUNICODE_STRING NtPathName, OUT PWSTR* FilePathInNtPathName OPTIONAL, OUT PRELATIVE_NAME* RelativeName OPTIONAL ); DOSPATH_TO_NTPATH RtlDosPathNameToNtPathName_U;
5. 打開文件映像
5.1 接着調用NtOpenFile()獲得文件句柄,咱們先來看一下這個函數的聲明:
NTSTATUS NtOpenFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, //指望的訪問屬性 IN POBJECT_ATTRIBUTES ObjectAttributes, //對象屬性 OUT PIO_STATUS_BLOCK IoStatusBlock, //I/0狀態塊 IN ULONG ShareAccess, //共享模式 IN ULONG OpenOptions //打開選項 );
v238 = NtOpenFile(&v291, 1048737, &v207, &v252, 5, 96); if ( v238 < 0 ) { v238 = NtOpenFile(&v291, 1048608, &v207, &v252, 5, 96); if ( v238 < 0 ) break; }
5.2 建立程序的內存區
接着經過NtOpenFile獲得的handle接着調用了NtCreateSectiond函數獲得內存區對象句柄,即咱們常說的進程用戶空間的虛擬地址空間,在這一步完成建立(事實上,這裏用建立這個詞不是很是恰當,由於進程的內存空間一直4GB,這裏作的實際是得到這個內存區對象的句柄,以方便咱們以後使用)。
NTSTATUS NtCreateSection( OUT PHANDLE SectionHandle, //指向內存區對象的指針(傳出參數) IN ACCESS_MASK DesiredAccess, //訪問掩碼 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, //對象屬性 IN PLARGE_INTEGER MaximumSize OPTIONAL, //SETION大小 IN ULONG SectionPageProtection, //內存區頁面保護屬性 IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL //文件句柄 );
v17 = NtCreateSection(&v293, 983071, 0, 0, 16, 16777216, v291); v238 = v17; if ( v17 < 0 ) goto LABEL_425;
6. 檢查windows程序運行受權策略
接着調用BasepIsProcessAllowed函數該函數用來判斷應用程序名是否在受權文件列表中,函數實現調用了NtOpenKey函數打開了註冊表中的HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options鍵
v18 = BasepIsProcessAllowed(Size); v17 = v18; v238 = v18; if ( v18 < 0 ) { BaseSetLastNTError(v18); NtClose(v293); goto LABEL_173; }
輸入gpedit.msc->計算機配置->windows設置->安全設置->軟件限制策略->其餘規則中能夠設置咱們的"軟件運行策略"
7. 獲取進程內存區對象的基本信息
在獲得進程對應的內存區對象句柄後調用了NtQuerySection函數,返回後獲得節的基本信息(節基地址,大小,屬性),中間的那一大段代碼我們能夠忽略,我通讀以後,發現只是一些可有可無的內存區的申請,釋放操做,還有和前面說的win16的VDM相關的代碼(在win32下不會執行),因此能夠掠過。
咱們直接來到NtQuerySection函數這個代碼塊,先來看看這個函數的聲明:
//將內存區對象做爲"鏡像執行"文件來查詢信息 typedef DWORD ( WINAPI* NTQUERYSECTION)( HANDLE, //內存區句柄 ULONG, //edi = 1 = SectionImageInformation PVOID, //接受內存區信息Buffer ULONG, //內存區信息的長度 PULONG //接受返回的大小 ); NTQUERYSECTION NtQuerySection;
v23 = NtQuerySection(v293, 1, &v189, 48, 0); v238 = v23; if ( v23 < 0 ) goto LABEL_242;
8. 判斷映像文件(剛纔加載到進程內存對象區域中的映像文件)信息的有效性
8.1 而後判斷建立標誌中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS
若是包含該標誌,判斷PEB->ReadImageFileExecOptions域是否爲0,若是不爲0, 調用LdrQueryImageFileExecutionOptions函數查詢該信息,若是不包含該標誌,也用調用LdrQueryImageFileExecutionOptions函數。
if ( !(dwCreationFlagsa & 3) || (v128 = __readfsdword(24), *(_BYTE *)(*(_DWORD *)(v128 + 48) + 1)) ) //DEBUG_PROCESS OR DEBUG_ONLY_THIS_PROCESS = 3 LdrQueryImageFileExecutionOptions(&v275, L"Debugger", 1, &v327, 520, 0);
8.2 下面檢查鏡像文件的部分信息的有效性
if ( MachineType < v7FFE002C || MachineType > v7FFE002E ) { //MachineType爲機器類型, .. } if ( subsystem_machineType != 2 && subsystem_machineType != 3 ) { //子系統版本號 2: 控制檯 3: GUI .. if ( subsystem_machineType != 7 ) { .. } if ( !BuildSubSysCommandLine(L"POSIX /P ", Size, Source, &v272) ) goto LABEL_173; goto LABEL_215; }
接着調用函數BasepIsImageVersionOk判斷鏡像文件版本是否合法
if ( !BasepIsImageVersionOk(subsystem_major_type, subsystem_minor_type) ) //subsystem_major_type: 子系統的主版本號 //subsystem_minor_type: 子系統的次版本號 goto LABEL_277;
9. 加載advapi32.dll
若是建立標誌中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS(即當前處在調試模式中),就載advapi32.dll並得到CreateProcessAsUserSecure函數的地址:
if ( !v327 ) { v24 = LoadLibraryA("advapi32.dll"); .. if ( v24 ) { if ( GetProcAddress(v24, "CreateProcessAsUserSecure") ) { v238 = NtQuerySystemInformation(71, &v279, 4, 0); .. } FreeLibrary(v25); }
(Ps: 進程分析的過程很是之繁雜的瑣碎,很容易讓你陷入代碼的細節而不能自拔,小瀚建議朋友們每分析一段時間後就翻到前面去仔細看看"進程建立流程"的概覽,不斷地提醒本身同時從宏觀
和微觀的角度去看待這個過程,這樣,不至於迷失在windows的代碼的海洋中,本身也能夠準備一張進程建立的縱覽圖,補充着看)
10. NT對象屬性的建立
而後調用BaseFormatObjectAttributes將安全屬性結構(關於進程安全、權限方面的信息,例如普通應用程序若是想要刪除systrem32\calc.exe就必須利用這個字段來提高權限到TrustedInstaller(win7下))格式爲NT對象屬性結構(獲得了對象屬性)。
typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
..
v188 = BaseFormatObjectAttributes(&v207, v171, 0);
..
接着調用了_DbgUiConnectToDbg在實現經過調用NtCreateDebugObject函數來建立調試對象,調用DbgUiGetThreadDebugObject來得到調試對象(做爲參數傳遞到0環)。
11. 建立調試對象
接着調用了_DbgUiConnectToDbg在實現經過調用NtCreateDebugObject函數來建立調試對象,而後調用DbgUiGetThreadDebugObject來得到調試對象(做爲參數傳遞到0環)。
if ( dwCreationFlagsa & 3 ) { v23 = DbgUiConnectToDbg(); v238 = v23; if ( v23 < 0 ) goto LABEL_242; v164 = DbgUiGetThreadDebugObject(); ... }
12. 最後一步,準備進行模式穿越
最後調用NtCreateProcessEx函數完成3環的建立過程,咱們先來看看NtCreateProcessEx的聲明:
NTSYSAPI NTSTATUS NTAPI NtCreateProcessEx( OUT PHANDLE ProcessHandle, //保留進程句柄值(傳出參數) IN ACCESS_MASK DesiredAccess, //(訪問掩碼) IN POBJECT_ATTRIBUTES ObjectAttributes, //對象屬性 IN HANDLE InheritFromProcessHandle, //父進程句柄(FFFFFFFF) IN BOOLEAN InheritHandles, //建立標誌 IN HANDLE SectionHandle OPTIONAL, //內存區對象句柄 IN HANDLE DebugPort OPTIONAL, //調試對象句柄 IN HANDLE ExceptionPort OPTIONAL, //異常端口對象句柄 IN HANDLE Unknown //做業級別 );
v27 = NtCreateProcessEx(&v294, 2035711, v188, -1, v197, v293, v164, 0, v158);
這裏要注意一點,這個函數NtCreateProcessEx是一個ntdll.dll導出的存根函數,它是咱們從用戶模式穿越進內核層的一條"通道"
到了這一步,咱們和ring3的"緣分""暫時"盡了,注意,是暫時盡了,也就是說,咱們的代碼在進入內核層以後,還有回到用戶層一次
繼續跟進NtCreateProcessEx這個函數,咱們就要進入內核層的代碼了。因此這裏是一個分界點。
下面附上函數的流程圖:
階段四: 建立進程的"執行體進程對象",(Ring0從這裏開始)
在開始分析ring0層的進程建立過程以前,咱們作一個承上啓下的總結
1. 以前在ring3層,CreateProcess已經打開了一個有效的windows可執行文件,而且建立了一個內存區對象,以便稍後將它映射到新的進程地址空間中 2. 接下來,ring3的代碼會調用NtCreateProcessEx,來建立一個"windows執行體進程對象",以運行該"進程映像"。
建立執行體進程對象(EPROCESS)(過程當中天然也包含了建立內核層進程對象KPROCESS)的大體過程以下:
1. 創建起EPROCESS塊 2. 建立初始的進程地址空間 3. 初始化內核進程塊(KPROCESS) 4. 結束進程地址空間的建立過程(包括初始化工做集列表和虛擬地址空間描述符,以及將映像映射到地址空間中) 5. 創建PEB 6. 完成執行體進程對象的建立過程
創建起EPROCESS
咱們知道,在ring3的最後一次調用中,代碼調用了NtCreateProcessEx()這個函數,從這個函數之後,代碼進入了內核的範疇。因此咱們從這個函數開始逐行分析
下面貼出NtCreateProcessEx()函數源代碼,它位於WRK的 base\ntos\ps\create.c 文件中
NTSTATUS NtCreateProcessEx __out PHANDLE ProcessHandle, //輸出參數 __in ACCESS_MASK DesiredAccess, //對新進程的訪問權限 __in_opt POBJECT_ATTRIBUTES ObjectAttributes, //進程對象的屬性 __in HANDLE ParentProcess, //指向父進程的句柄, __in ULONG Flags, //建立標誌 __in_opt HANDLE SectionHandle, //該進程的內存區對象 __in_opt HANDLE DebugPort, //新進程的調試端口 __in_opt HANDLE ExceptionPort, //新進程的異常端口 __in ULONG JobMemberLevel //新進程在一個job集中的級別 ) { NTSTATUS Status; PAGED_CODE(); if (KeGetPreviousMode() != KernelMode) { try { ProbeForWriteHandle (ProcessHandle); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode (); } } if (ARGUMENT_PRESENT (ParentProcess)) { Status = PspCreateProcess (ProcessHandle, DesiredAccess, ObjectAttributes, ParentProcess, Flags, SectionHandle, DebugPort, ExceptionPort, JobMemberLevel); } else { Status = STATUS_INVALID_PARAMETER; } return Status; }
首先對NtCreateProcessEx的參數作一個說明:
1. ProcessHandle: 一個輸出參數,若是該進程建立成功,則它包含了所建立的進程的句柄,windows中這種帶引用的傳出參數的編程很是常見 2. DesiredAccess: 包含了對新進程的訪問權限 3. ObjectAttributes: 這是一個可選的"指針參數(便可覺得NULL)",它指定了進程對象的屬性 4. ParentProcess: 指向父進程的句柄,這是一個必需的參數,不能爲NULL,而且調用者必須對該進程具備"PROCESS_CREATE_PROCESS"(這是FLAGS中的一個屬性) 5. Flags: 這是建立標誌,其中有一個標誌"PROCESS_CREATE_FLAGS_INHERIT_HANDLES"很是關鍵,代表新進程的"對象句柄表"是否要複製父進程的句柄表,或者初始設置爲空。 6. SectionHandle: 這是一個可選的句柄,指向一個內存區對象,表明了該進程的映像文件,調用者對於此內存區對象必須具備"SECTION_MAP_EXECUTE"訪問權限。 7. DebugPort: 這是一個可選的句柄,指向一個端口對象,若是此句柄參數不爲NULL,則此端口被賦值爲新進程的調試端口,不然,新進程沒有調試端口 8. ExceptionPort: 這是一個可選的端口,指向一個端口對象,若是此句柄參數不爲NULL,則此端口被賦值爲新進程的異常端口,不然,新進程沒有異常端口。調用者對於異常端口對象必須具備 PORT_WRITE和PORT_READ訪問權限 9. JobMemberLevel: 指定了要建立的進程在一個job集中的級別
NtCreateProcessEx的代碼先判斷了線程的前一個模式是否是"內核態(KernelMode)",即判斷這個建立請求是從內核態來的仍是從用戶態來的,咱們如今分析的是從ring3建立進程的流程,因此這裏"線程的前一個模式"是"用戶態"。
接着代碼會檢查ProcessHandle參數表明的句柄是否可寫,而後才把真正的建立工做交給PspCreateProcess函數。
if (KeGetPreviousMode() != KernelMode) { //判斷了線程的前一個模式是否是"內核態(KernelMode)" try { ProbeForWriteHandle (ProcessHandle); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode (); } } if (ARGUMENT_PRESENT (ParentProcess)) { //這是真正的建立進程的函數 Status = PspCreateProcess ...
接下來順藤摸瓜,咱們繼續跟進代碼,代碼來到了PspCreateProcess ()這個函數。它位於 base\ntos\ps\create.c 中。
在開始分析PspCreateProcess 以前,要一個知識點要注意:
PspCreateProcess在windows中會被三個函數調用,它們是NtCreateProcessEx、PsCreateSystemProcess、PsInitPhase 1. PsInitPhase()是在系統初始化的早期被調用的,它建立的進程(即System進程)的句柄保存在全局變量PspInitialSystemProcessHandle中,進程對象存放於全局變量
PsInitialSystemProcess中 2. PsCreateSystemProcess()可用於建立系統進程對象,它建立的進程都是PsInitialSystemProcess的子進程 3. NtCreateProcessEx()即咱們接下來要分析的,同時這個NtCreateProcessEx也有可能會從ring3或ring0發出,文章的最後總結會再次說起這點 因此,PspCreateProcess()函數負責建立系統中的"全部"進程(注意,是全部),包括System進程
接下來結合WRK的源代碼來逐行分析此函數的流程,爲了保持說明的完整性,我依然弱智地貼出完整代碼,此次,由於代碼實在太長了,我選擇直接在代碼中添加註釋進行說明。當中涉及的數據結構的成員域在以前的EPROCESS/KPROCESS的學習筆記中都有介紹,請朋友結合起來看:
NTSTATUS PspCreateProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ParentProcess OPTIONAL, IN ULONG Flags, IN HANDLE SectionHandle OPTIONAL, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL, IN ULONG JobMemberLevel ) { NTSTATUS Status; PEPROCESS Process; PEPROCESS CurrentProcess; PEPROCESS Parent; PETHREAD CurrentThread; KAFFINITY Affinity; KPRIORITY BasePriority; PVOID SectionObject; PVOID ExceptionPortObject; PVOID DebugPortObject; ULONG WorkingSetMinimum, WorkingSetMaximum; HANDLE LocalProcessHandle; KPROCESSOR_MODE PreviousMode; INITIAL_PEB InitialPeb; BOOLEAN CreatePeb; ULONG_PTR DirectoryTableBase[2]; BOOLEAN AccessCheck; BOOLEAN MemoryAllocated; PSECURITY_DESCRIPTOR SecurityDescriptor; SECURITY_SUBJECT_CONTEXT SubjectContext; NTSTATUS accesst; NTSTATUS SavedStatus; ULONG ImageFileNameSize; HANDLE_TABLE_ENTRY CidEntry; PEJOB Job; PPEB Peb; AUX_ACCESS_DATA AuxData; PACCESS_STATE AccessState; ACCESS_STATE LocalAccessState; BOOLEAN UseLargePages; SCHAR QuantumReset; PAGED_CODE(); CurrentThread = PsGetCurrentThread (); PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb); CurrentProcess = PsGetCurrentProcessByThread (CurrentThread); CreatePeb = FALSE; UseLargePages = FALSE; DirectoryTableBase[0] = 0; DirectoryTableBase[1] = 0; Peb = NULL; if (Flags&~PROCESS_CREATE_FLAGS_LEGAL_MASK) { return STATUS_INVALID_PARAMETER; } /* 若是父進程句柄不爲NULL,則經過ObReferenceObjectByHandle得到父進程對象的EPROCESS指針,放在Parent局部變量中 */ if (ARGUMENT_PRESENT (ParentProcess)) { Status = ObReferenceObjectByHandle (ParentProcess, PROCESS_CREATE_PROCESS, PsProcessType, PreviousMode, &Parent, NULL); if (!NT_SUCCESS (Status)) { return Status; } if (JobMemberLevel != 0 && Parent->Job == NULL) { ObDereferenceObject (Parent); return STATUS_INVALID_PARAMETER; } /* 得到父進程的Affinity(CPU親和性)設置,新進程工做集最大/最小值被初始化爲全局變量PsMaximumWorkingSet和PsMinimumWorkingSet */ Affinity = Parent->Pcb.Affinity; WorkingSetMinimum = PsMinimumWorkingSet; WorkingSetMaximum = PsMaximumWorkingSet; } else { /* 若是父進程句柄爲NULL,則Affinity設置爲全局變量KeActiveProcessors,即系統中當前的可用處理器。此時由於新進程對象還沒有被建立,因此這些設置都保存在局部變量中 */ Parent = NULL; Affinity = KeActiveProcessors; WorkingSetMinimum = PsMinimumWorkingSet; WorkingSetMaximum = PsMaximumWorkingSet; } /* 調用ObCreateObject函數,建立一個類型爲PsProcessType的"內核對象",並置於局部變量Process中,這裏所謂的PsProcessType類型的內核對象指的就是"EPROCESS",
即這一步開始建立了一個執行體進程對象 */ Status = ObCreateObject (PreviousMode, PsProcessType, ObjectAttributes, PreviousMode, NULL, sizeof (EPROCESS), 0, 0, &Process); if (!NT_SUCCESS (Status)) { goto exit_and_deref_parent; } /* 把Process(EPROCESS)對象中的全部域置爲0,而後初始化(或者直接繼承父進程的對應成員域)其中部分紅員(RundownProtect、ProcessLock、ThreadListHead、QuotaUsage、
QuotaPeak、QuotaBlock、DeviceMap) */ RtlZeroMemory (Process, sizeof(EPROCESS)); ExInitializeRundownProtection (&Process->RundownProtect); PspInitializeProcessLock (Process); InitializeListHead (&Process->ThreadListHead); PspInheritQuota (Process, Parent); ObInheritDeviceMap (Process, Parent); /* 若是父進程句柄不爲NULL,則將新進程的Process(EPROCESS)繼承父進程的DefaultHardErrorProcessing(默認的硬件錯誤處理)、
InheritedFromUniqueProcessId(父進程的PID) */ if (Parent != NULL) { Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing; Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId; } else { Process->DefaultHardErrorProcessing = PROCESS_HARDERROR_DEFAULT; Process->InheritedFromUniqueProcessId = NULL; } /* 檢查內存區句柄參數SectionHandle,對於系統進程,此參數爲NULL,此時,除非父進程爲PsInitialSystemProcess(System進程),不然內存區對象繼承自父進程,而且不得爲NULL。 若是此參數不爲NULL,則利用此句柄參數調用ObReferenceObjectByHandle得到內存區對象的指針。因此,新進程對象的內存區對象已經完成初始化。 Ps: 小瀚建議朋友看到這裏翻回去看看"階段四: 執行體進程建立"的整體概覽步驟,從宏觀和微觀的角度不斷加深映像,不要一會兒就陷入了代碼中。經過整體路線,咱們知道下一步基本
是要建立"進程內核對象KPROCESS"了,基本的代碼模式咱們也能猜想出來了 */ if (ARGUMENT_PRESENT (SectionHandle)) { Status = ObReferenceObjectByHandle (SectionHandle, SECTION_MAP_EXECUTE, MmSectionObjectType, PreviousMode, &SectionObject, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } else { SectionObject = NULL; if (Parent != PsInitialSystemProcess) { if (ExAcquireRundownProtection (&Parent->RundownProtect)) { SectionObject = Parent->SectionObject; if (SectionObject != NULL) { ObReferenceObject (SectionObject); } ExReleaseRundownProtection (&Parent->RundownProtect); } if (SectionObject == NULL) { Status = STATUS_PROCESS_IS_TERMINATING; goto exit_and_deref; } } } /* 完成新進程對象的內存區對象初始化爲新進程的EPROCESS成員域賦值 */ Process->SectionObject = SectionObject; /* 根據DebugPort參數來初始化新進程對象的DebugPort(調試端口)成員域 */ if (ARGUMENT_PRESENT (DebugPort)) { Status = ObReferenceObjectByHandle (DebugPort, DEBUG_PROCESS_ASSIGN, DbgkDebugObjectType, PreviousMode, &DebugPortObject, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } Process->DebugPort = DebugPortObject; if (Flags&PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT) { PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT); } } else { if (Parent != NULL) { DbgkCopyProcessDebugPort (Process, Parent); } } /* 根據ExceptionPort參數來初始化新進程對象的ExceptionPort(異常端口)成員域 */ if (ARGUMENT_PRESENT (ExceptionPort)) { Status = ObReferenceObjectByHandle (ExceptionPort, 0, LpcPortObjectType, PreviousMode, &ExceptionPortObject, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } Process->ExceptionPort = ExceptionPortObject; } /* 設置新進程的退出狀態,這裏設置爲STATUS_PENDING表示還沒有完成,正在處理ing */ Process->ExitStatus = STATUS_PENDING; /* 若是指定的父進程不爲NULL,則建立一個全新的地址空間(最小工做集大小) */ if (Parent != NULL) { if (!MmCreateProcessAddressSpace (WorkingSetMinimum, Process, &DirectoryTableBase[0])) { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } } /* 若是指定的父進程爲NULL(即系統進程,系統進程是沒有父進程的說法的),讓新進程的句柄表指向當前進程(CurrentProcess)的句柄表,而且利用空閒線程的地址空間來初始化新進程
的地址空間 */ else { Process->ObjectTable = CurrentProcess->ObjectTable; Status = MmInitializeHandBuiltProcess (Process, &DirectoryTableBase[0]); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_HAS_ADDRESS_SPACE); Process->Vm.MaximumWorkingSetSize = WorkingSetMaximum; /* 調用KeInitializeProcess函數來初始化新進程內核對象(KPROCESS)的"基本優先級(BasePriority)"、Affinity(CPU親和性)、進程頁面目錄(DirectoryTableBase)和超空間的
頁幀號 Ps: 再次回顧階段四的"總路線圖",咱們如今到了建立KPROCESS的那一步了 */ KeInitializeProcess (&Process->Pcb, NORMAL_BASE_PRIORITY, Affinity, &DirectoryTableBase[0], (BOOLEAN)(Process->DefaultHardErrorProcessing & PROCESS_HARDERROR_ALIGNMENT_BIT)); /* 經過PspInitializeProcessSecurity函數初始化新進程的安全屬性,主要是從父進程複製一個令牌 */ Status = PspInitializeProcessSecurity (Parent, Process); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } /* 設置新進程的優先級類別: PROCESS_PRIORITY_CLASS_NORMAL(普通級別) */ Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; /* 若是父進程句柄不爲NULL,則複製父進程的優先級,這裏注意一個細節,就是要判斷一下指定的優先級是否超過了IDLE,由於Real-Time的優先級普通進程是不被容許建立的 */ if (Parent != NULL) { if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE || Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL) { Process->PriorityClass = Parent->PriorityClass; } /* 初始化新進程的句柄表,若Flags參數中包含了句柄繼承標誌,則把父進程句柄表中凡有繼承屬性的對象拷貝到新進程的句柄表中(回想句柄表的知識: 即便是複製,同一個對象的
句柄在不一樣的進程空間中是不一樣的) */ Status = ObInitProcess ((Flags&PROCESS_CREATE_FLAGS_INHERIT_HANDLES) ? Parent : NULL, Process); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } else { Status = MmInitializeHandBuiltProcess2 (Process); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } Status = STATUS_SUCCESS; SavedStatus = STATUS_SUCCESS; /* 接下來開始初始化新進程的進程地址空間 Ps: 再次回到總路線圖,咱們如今已經到了"結束進程地址空間的建立過程(包括初始化工做集列表和虛擬地址空間描述符,以及將映像映射到地址空間中)"這一步,作到心中有數。 */ /* 接下來初始化新進程的進程地址空間,有三種可能性 */ if (SectionHandle != NULL) { /* 1. 新進程有新的可執行映像內存區對象SectionObject,調用MmInitializeProcessAddressSpace函數,根據指定的內存區對象來初始化進程地址空間 */ Status = MmInitializeProcessAddressSpace (Process, NULL, SectionObject, &Flags, &(Process->SeAuditProcessCreationInfo.ImageFileName)); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } SavedStatus = Status; CreatePeb = TRUE; UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE); } else if (Parent != NULL) { /* 2. 沒有指定映像內存區對象SectionObject,但父進程也並不是PsInitialSystemProcess(非NULL)。也調用MmInitializeProcessAddressSpace函數,但根據父進程來初始化
進程地址空間。 */ if (Parent != PsInitialSystemProcess) { Process->SectionBaseAddress = Parent->SectionBaseAddress; Status = MmInitializeProcessAddressSpace (Process, Parent, NULL, &Flags, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } CreatePeb = TRUE; UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE); /* 而且把父進程的映像名稱ImageFileName字符串拷貝到新進程對象的數據結構中 */ if (Parent->SeAuditProcessCreationInfo.ImageFileName != NULL) { ImageFileNameSize = sizeof(OBJECT_NAME_INFORMATION) + Parent->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength; Process->SeAuditProcessCreationInfo.ImageFileName = ExAllocatePoolWithTag (PagedPool, ImageFileNameSize, 'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) { RtlCopyMemory (Process->SeAuditProcessCreationInfo.ImageFileName, Parent->SeAuditProcessCreationInfo.ImageFileName, ImageFileNameSize); Process->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer = (PUSHORT)(((PUCHAR) Process->SeAuditProcessCreationInfo.ImageFileName) + sizeof(UNICODE_STRING)); } else { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } } } else { /* 3. 沒有指定映像內存區對象SectionObject,但指定了PsInitialSystemProcess(System進程)做爲父進程。這對應於系統進程的情形。
調用MmInitializeProcessAddressSpace,不指定內存區和父進程,直接初始化。 */ Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS; Status = MmInitializeProcessAddressSpace (Process, NULL, NULL, &Flags, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } Process->SeAuditProcessCreationInfo.ImageFileName = ExAllocatePoolWithTag (PagedPool, sizeof(OBJECT_NAME_INFORMATION), 'aPeS'); /* 一樣地,把父進程的映像名稱字符串ImageFileName拷貝到新進程對象的數據結構中 */ if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) { RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName, sizeof(OBJECT_NAME_INFORMATION)); } else { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } } /* 實際上還有第四種可能性,即沒有指定映像內存區對象SectionObject,也沒有指定父進程。這對應於系統的引導進程(後蛻化成空閒進程),它的地址空間是在MmInitSystem執行過程
中初始化的,由MiInitMachineDependent函數調用MmInitializeProcessAddressSpace來完成 (這是潘老師在書上說的,能夠我在翻閱了WRK和ReactOS的源代碼後也沒有找到與這段描述對應的源代碼,個人是windows確實實現了,可是WRK中沒有提供) */ } /* 建立進程ID。利用ExCreateHandle函數在CID句柄表中建立一個進程ID項 */ CidEntry.Object = Process; CidEntry.GrantedAccess = 0; Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry); if (Process->UniqueProcessId == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } ExSetHandleTableOwner (Process->ObjectTable, Process->UniqueProcessId); /* 對此次進程建立行爲進行審計 */ if (SeDetailedAuditingWithToken (NULL)) { SeAuditProcessCreation (Process); } if (Parent) { /* 若是父進程屬於一個做業對象,則也加入到父進程所在的做業中,調用PspAddProcessToJob完成操做,注意這裏對做業的最大容納進程數有要求 */ Job = Parent->Job; if (Job != NULL && !(Job->LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) { if (Flags&PROCESS_CREATE_FLAGS_BREAKAWAY) { if (!(Job->LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK)) { Status = STATUS_ACCESS_DENIED; } else { Status = STATUS_SUCCESS; } } else { Status = PspGetJobFromSet (Job, JobMemberLevel, &Process->Job); if (NT_SUCCESS (Status)) { PACCESS_TOKEN Token, NewToken; Job = Process->Job; Status = PspAddProcessToJob (Job, Process); Token = Job->Token; if (Token != NULL) { Status = SeSubProcessToken (Token, &NewToken, FALSE, Job->SessionId); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } SeAssignPrimaryToken (Process, NewToken); ObDereferenceObject (NewToken); } } } if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } } /* 開始建立Peb,建立Peb分爲兩種狀況,建立進程時創建Peb、複製進程時創建Peb Ps: 再次翻閱總路線圖,咱們如今來到了"創建PEB"這一步 */ if (Parent && CreatePeb) { RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant)); InitialPeb.Mutant = (HANDLE)(-1); InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages; if (SectionHandle != NULL) { /* 1. 對於經過映像內存區對象SectionObject來建立進程的情形,調用MmCreatePeb來建立一個Peb,建立數據結構的基本模式都是相似的:
申請空間->初始化剛纔申請的空間->爲數據結構中的指定成員域賦值 */ Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb); if (!NT_SUCCESS (Status)) { Process->Peb = NULL; goto exit_and_deref; } Peb = Process->Peb; } else { /* 2. 對於進程拷貝(fork)的情形,則使用從父進程繼承而來的Peb(從這裏也能夠明白爲何說子進程繼承了大部分的父進程的代碼空間,從源代碼上能夠找到理論依據) */ SIZE_T BytesCopied; InitialPeb.InheritedAddressSpace = TRUE; Process->Peb = Parent->Peb; /* 直接複製父進程的Peb */ MmCopyVirtualMemory (CurrentProcess, &InitialPeb, Process, Process->Peb, sizeof (INITIAL_PEB), KernelMode, &BytesCopied); } } Peb = Process->Peb; /* 能夠看到,在內核中對這種雙鏈表的操做進行"加鎖"是很常見的編程作法,爲了保證一致性 */ PspLockProcessList (CurrentThread); /* 把新進程加入到全局的進程鏈表PsActiveProcessHead中,從數據結構的角度理解就是把Process->ActiveProcessLinks這個鏈表項插入一個雙鏈表中(頭尾斷鏈再從新結合) Ps: 回想利用PsActiveProcessHead進行內核中進程枚舉的思路 */ InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks); PspUnlockProcessList (CurrentThread); AccessState = NULL; if (!PsUseImpersonationToken) { AccessState = &LocalAccessState; Status = SeCreateAccessStateEx (NULL, (Parent == NULL || Parent != PsInitialSystemProcess)? PsGetCurrentProcessByThread (CurrentThread) : PsInitialSystemProcess, AccessState, &AuxData, DesiredAccess, &PsProcessType->TypeInfo.GenericMapping); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } Status = ObInsertObject (Process, AccessState, DesiredAccess, 1, // bias the refcnt by one for future process manipulations NULL, &LocalProcessHandle); if (AccessState != NULL) { SeDeleteAccessState (AccessState); } if (!NT_SUCCESS (Status)) { goto exit_and_deref_parent; } ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE); /* 調用PspComputeQuantumAndPriority計算新進程的"基本優先級(BasePriority)"和"時限重置值(QuantumReset)" */ BasePriority = PspComputeQuantumAndPriority(Process, PsProcessPriorityBackground, &QuantumReset); /* 對新進程的KPROCESS賦值基本優先級和時限重置值 */ Process->Pcb.BasePriority = (SCHAR)BasePriority; Process->Pcb.QuantumReset = QuantumReset; /* 設置新進程的"內存優先級(進程的訪問權限)GrantedAccess"。由於新進程已經被加入到句柄表中了,因此它如今可以被終止了(即具備了PROCESS_TERMINATE的權限,即它有終止的權利) */ Process->GrantedAccess = PROCESS_TERMINATE; if (Parent && Parent != PsInitialSystemProcess) { /* 對於有父進程但父進程不是PsInitialSystemProcess(System系統進程)的進程,首先調用SeAccessCheck執行訪問檢查 */ Status = ObGetObjectSecurity (Process, &SecurityDescriptor, &MemoryAllocated); if (!NT_SUCCESS (Status)) { ObCloseHandle (LocalProcessHandle, PreviousMode); goto exit_and_deref; } 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); PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken); ObReleaseObjectSecurity (SecurityDescriptor, MemoryAllocated); if (!AccessCheck) { Process->GrantedAccess = 0; } /* 爲進程授予訪問權限 */ 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 | STANDARD_RIGHTS_ALL | PROCESS_SET_QUOTA); } else { /* 若是是PsInitialSystemProcess的子進程,則授予全部訪問權限"PROCESS_ALL_ACCESS" */ Process->GrantedAccess = PROCESS_ALL_ACCESS; } /* 設置進程的建立時間 */ KeQuerySystemTime (&Process->CreateTime); try { if (Peb != NULL && CurrentThread->Tcb.Teb != NULL) { ((PTEB)(CurrentThread->Tcb.Teb))->NtTib.ArbitraryUserPointer = Peb; } /* 把新進程的句柄賦值到輸出參數"ProcessHandle"中,從而使建立者(調用者)能夠得到新進程的句柄 */ *ProcessHandle = LocalProcessHandle; } except (EXCEPTION_EXECUTE_HANDLER) { NOTHING; } if (SavedStatus != STATUS_SUCCESS) { Status = SavedStatus; } exit_and_deref: ObDereferenceObject (Process); exit_and_deref_parent: if (Parent != NULL) { ObDereferenceObject (Parent); } return Status; }
這是一個漫長的過程,可是代碼邏輯是很清晰的,咱們在看完源代碼後再次作一個"稍微"詳細一點的總結:
1. 分配並初始化windows EPROCESS執行體進程塊 2. 從父進程處繼承獲得進程的親和性掩碼 3. 新進程的最小和最大工做集尺寸被分別設置爲PsMinimumWorkingSet和PsMaximumWorkingSet 4. 將新進程的配額塊設置爲其父進程配額塊的地址,而且遞增父進程配額塊的引用計數 5. 繼承windows的設備名字空間(包括驅動器字母的定義、COM端口等等) 6. 將父進程的進程ID保存在新進程對象的InheritedFromUniqueProcessId域中 7. 建立新進程的主訪問令牌(父進程主令牌的副本)。新進程繼承了其父進程的安全輪廓(安全描述符),若是經過CreateProcessAsUser來爲新進程指定一個不一樣的訪問令牌,
則該令牌將在後面被正確地改過來 8. 初始化新進程句柄表,若是父進程已經被設置了繼承句柄標誌,那麼,從父進程的句柄表中將任何可繼承的句柄拷貝到新進程中 9. 將新進程的退出狀態設置爲STATUS_PENDING 10. 建立和初始新進程的地址空間 11. 建立內核進程塊(KPROCESS) 12. 結束進程地址空間的建立過程 13. 創建Peb 14. 完成執行體進程對象的建立過程(設置返回值,開啓審計等)
至此,這就是一個"進程對象(EPROCESS/KPROCESS)"的初始化過程了。然而,通過PspCreateProcess函數以後,新建的進程中並無任何線程對象,因此,它如今仍是一個死的進程空間,也就是說,其中的代碼並沒被"真正"運行起來。因此,咱們接下來討論線程對象的的建立和初始化。
回到以前留下的一個問題,咱們在分析Kernel32.dll中的NtCreateProcessEx()的時候,咱們提到,那個時候是"暫時"離開ring3,穿越進ring0去建立進程對象(EPROCESS/KPROCESS)。
到了這裏,能夠解釋這句話的意思了,咱們的內核代碼在建立完進程相關的結構後,就會退出內核模式,穿越回ring3用戶模式。繼續執行kernel32.dll中的代碼邏輯。
階段五: 建立線程的內核對象
建立完進程對象後,代碼從內核模式穿越會用戶模式,咱們在kernel32.dll中的NtCreateProcessEx()代碼處繼續往下翻:
1. 處理下建立進程後的殘餘工做
在建立進程返回後,此時EPROCESS,PEB結構已經建立,進程地址空間也已經建立並初始化了。接下處理下建立進程後的殘餘工做,調用NtSetInformationProcess函數設置進程的優先級和默認處理模式.
NTSYSAPI NTSTATUS NTAPI NtSetInformationProcess( IN HANDLE ProcessHandle, //進程句柄 IN PROCESS_INFORMATION_CLASS ProcessInformationClass, //進程信息類型索引(18 = ProcessPriorityClass) IN PVOID ProcessInformation, //進程信息 IN ULONG ProcessInformationLength //進程信息的的大小 );
.. v238 = NtSetInformationProcess(v294, 18, &v295, 2); ..
2. 構建線程的環境
接下來要作的就是建立線程, 不過在在此以前還要構建線程的環境,調用BaseCreateStack函數建立棧:
NTSTATUS WINAPI BaseCreateStack ( HANDLE hProcess, //進程句柄 SIZE_T StackReserve, //棧大小 SIZE_T StackCommit, //棧的最大值 PINITIAL_TEB InitialTeb //初始TEB );
... v23 = BaseCreateStack(v294, v191, v36, &v213); ...
接着調用BaseInitializeContext初始化線程上下文, 而後調用BaseFormatObjectAttributes函數格式化對象(以便傳遞給NtCreateThread)
.... BaseInitializeContext(&v297, v184, v189, v214, 0); v188 = BaseFormatObjectAttributes(&v207, v166, 0); ...
3. 最後調用NtCreateThread建立線程
NTSYSAPI NTSTATUS NTAPI NtCreateThread( OUT PHANDLE ThreadHandle, //線程句柄指針 IN ACCESS_MASK DesiredAccess, //訪問掩碼 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, //對象屬性 IN HANDLE ProcessHandle, //進程句柄 OUT PCLIENT_ID ClientId, //客戶端ID結構指針 IN PCONTEXT ThreadContext, //線程上下文結構指針 IN PINITIAL_TEB InitialTeb, //初始化TEB IN BOOLEAN CreateSuspended //是否建立後掛起(默認是掛起的,題外話: 這就是APC進程注入的原理所在了) );
//kernel32.dll .. v23 = NtCreateThread(&v290, 2032639, v188, v294, &v262, &v297, &v213, 1); ...
執行了一小段以後,又再次調用NtCreateThread穿越進內核模式,此次的目的是建立線程對象。
相似於進程的建立過程,線程的建立過程是從NtCreateThread()開始的, 它的源代碼位於 base\ntos\ps\create.c 中,我仍是決定直接在代碼中插入註釋,逐行的分析源代碼
NTSTATUS NtCreateThread( __out PHANDLE ThreadHandle, __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in HANDLE ProcessHandle, __out PCLIENT_ID ClientId, __in PCONTEXT ThreadContext, __in PINITIAL_TEB InitialTeb, __in BOOLEAN CreateSuspended ) { NTSTATUS Status; INITIAL_TEB CapturedInitialTeb; PAGED_CODE(); try { if (KeGetPreviousMode () != KernelMode) { ProbeForWriteHandle (ThreadHandle); if (ARGUMENT_PRESENT (ClientId)) { ProbeForWriteSmallStructure (ClientId, sizeof (CLIENT_ID), sizeof (ULONG)); } if (ARGUMENT_PRESENT (ThreadContext) ) { ProbeForReadSmallStructure (ThreadContext, sizeof (CONTEXT), CONTEXT_ALIGN); } else { return STATUS_INVALID_PARAMETER; } ProbeForReadSmallStructure (InitialTeb, sizeof (InitialTeb->OldInitialTeb), sizeof (ULONG)); } CapturedInitialTeb.OldInitialTeb = InitialTeb->OldInitialTeb; if (CapturedInitialTeb.OldInitialTeb.OldStackBase == NULL && CapturedInitialTeb.OldInitialTeb.OldStackLimit == NULL) { CapturedInitialTeb = *InitialTeb; } } except (ExSystemExceptionFilter ()) { return GetExceptionCode (); } Status = PspCreateThread (ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, NULL, ClientId, ThreadContext, &CapturedInitialTeb, CreateSuspended, NULL, NULL); return Status; }
NtCreateThread()所作的事情很簡單:
1. 對非內核模式傳遞過來的調用,檢查幾個參數是否可寫(輸出參數TheadHandle、ClientId、輸入參數ThreadContext、InitialTeb) 2. 處理InitialTeb參數,將它放到局部變量CapturedInitialTeb中 3. 調用真正建立線程的函數CreateSuspended()。參數原封不動的傳過去,並增長了幾個參數
PspCreateThread函數的原型以下:
NTSTATUS PspCreateThread( OUT PHANDLE ThreadHandle, //輸出參數: 新線程的句柄 IN ACCESS_MASK DesiredAccess, //新線程的訪問權限 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, //新線程對象的屬性 IN HANDLE ProcessHandle, //線程所屬的進程的句柄 IN PEPROCESS ProcessPointer, //所屬進程的EPROCESS對象,此參數僅當建立系統線程時纔會指向全局PsInitialSystemProcess OUT PCLIENT_ID ClientId OPTIONAL, //指向新線程的CLIENT_ID結構 IN PCONTEXT ThreadContext OPTIONAL, //提供新線程的執行環境(以前建立好的), 它表明了用戶模式線程的初始執行環境 IN PINITIAL_TEB InitialTeb OPTIONAL, //爲新線程Teb提供初始值 IN BOOLEAN CreateSuspended, //指明新線程被建立起來後是否"被掛起"。若是爲TRUE則意味着新線程建立完之後並不當即執行
//之後經過NtResumeThread函數讓它開始運行 IN PKSTART_ROUTINE StartRoutine OPTIONAL, //系統線程啓動函數的地址 IN PVOID StartContext //系統線程啓動函數的執行環境 );
和談到PspCreateProcess相似,咱們在談到PspCreateThread的時候也要注意一個知識點:
1. PspCteateThread函數僅僅被NtCreateThread和PsCreateSystemThread這兩個函數調用,分別用於建立用戶線程和系統線程對象。 2. 在PspCteateThread函數的參數中,ThreadContext和InitialTeb參數針對用戶線程的建立操做,而StartRoutine和StartContext參數則針對系統線程的建立操做
接下來將再次貼出PspCreateThread函數的一大段代碼,我將盡個人能力在代碼插入註釋,來逐行解釋線程建立的過程。
在開始以前,咱們先作一個"路線概覽",以後在分析詳細的代碼的時候咱們須要不斷的回到這個"線路概覽"上來,不斷從宏觀和微觀的角度來看待線程的建立過程。
1. 遞增進程對象中的線程計數值 2. 建立並初始化一個執行體線程塊(ETHREAD) 3. 爲新線程生成一個線程ID 4. 在進程的用戶模式地址空間中創建Teb 5. 用戶模式線程的起始地址被保存在ETHREAD中。對於windows線程,這是系統提供的線程啓動函數,位於Kernel32.dll中(對於進程中的第一個線程是BaseThreadStart,對於其餘的線程則
是BaseThreadStart)。用戶指定的windows啓動地址被保存在ETHREAD塊中的另外一個位置上,於是,系統提供的線程啓動函數能夠調用用戶指定的啓動函數 6. 調用KeInitThread來創建起KTHREAD塊。該線程初始的基本優先級和當前優先級均被設置爲所屬進程的基本優先級,它的親和性和時限被設置爲進程的親和性和時限。該函數也會設置初始線程
的理想處理器。KeInitThread接下來爲該線程分配一個內核棧(注意和用戶線程棧區分),而且爲它初始化與機器相關的硬件環境,包括執行環境、陷阱和異常幀。該線程的執行環境也被創建
起來,於是在內核模式下此線程可在KiThreadStartup中啓動起來。最後,KeInitThread將該線程的狀態設置爲"已初始化",並返回到PspCreateThread 7. 調用任何已等級的系統全局範圍的線程來建立通知例程 8. 該線程的訪問令牌被設置爲指向進程的訪問令牌,而後作一次訪問檢查,以肯定調用者是否有權建立該線程。若是你是在本地進程中建立一個線程,那麼這一檢查將老是成功,可是,若是你是在
另外一個進程中建立一個"遠程線程",而且建立線程的進程沒有""調試特權,那麼這一訪問檢查可能會失敗 9. 最後,該線程作好執行準備
接下來貼上源代碼,但願朋友們能拿起手邊的書本,資料,配合着耐心讀完:
NTSTATUS PspCreateThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ProcessHandle, IN PEPROCESS ProcessPointer, OUT PCLIENT_ID ClientId OPTIONAL, IN PCONTEXT ThreadContext OPTIONAL, IN PINITIAL_TEB InitialTeb OPTIONAL, IN BOOLEAN CreateSuspended, IN PKSTART_ROUTINE StartRoutine OPTIONAL, IN PVOID StartContext ) { HANDLE_TABLE_ENTRY CidEntry; NTSTATUS Status; PETHREAD Thread; PETHREAD CurrentThread; PEPROCESS Process; PTEB Teb; KPROCESSOR_MODE PreviousMode; HANDLE LocalThreadHandle; BOOLEAN AccessCheck; BOOLEAN MemoryAllocated; PSECURITY_DESCRIPTOR SecurityDescriptor; SECURITY_SUBJECT_CONTEXT SubjectContext; NTSTATUS accesst; LARGE_INTEGER CreateTime; ULONG OldActiveThreads; PEJOB Job; AUX_ACCESS_DATA AuxData; PACCESS_STATE AccessState; ACCESS_STATE LocalAccessState; PAGED_CODE(); /* 首先得到當前線程對象,以及獲取這次建立操做來自於內核模式仍是用戶模式(PreviousMode) */ CurrentThread = PsGetCurrentThread(); if (StartRoutine != NULL) { PreviousMode = KernelMode; } else { PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb); } Teb = NULL; Thread = NULL; Process = NULL; /* 根據進程句柄參數ProcessHandle得到相應的進程對象,放到局部變量Process中 Ps: 觀察總路線圖,咱們如今處於"遞增進程對象中的線程計數值" */ if (ProcessHandle != NULL) { Status = ObReferenceObjectByHandle (ProcessHandle, PROCESS_CREATE_THREAD, PsProcessType, PreviousMode, &Process, NULL); } else { if (StartRoutine != NULL) { ObReferenceObject (ProcessPointer); Process = ProcessPointer; Status = STATUS_SUCCESS; } else { Status = STATUS_INVALID_HANDLE; } } if (!NT_SUCCESS (Status)) { return Status; } if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess)) { ObDereferenceObject (Process); return STATUS_INVALID_HANDLE; } /* 調用ObCreateObject函數建立一個線程對象ETHREAD */ Status = ObCreateObject (PreviousMode, PsThreadType, ObjectAttributes, PreviousMode, NULL, sizeof(ETHREAD), 0, 0, &Thread); if (!NT_SUCCESS (Status)) { ObDereferenceObject (Process); return Status; } /* 把整個ETHREAD結構清零 */ RtlZeroMemory (Thread, sizeof (ETHREAD)); /* 對ETHREAD的一些基本的域進行初始化(RundownProtect、ThreadsProcess(指向由進程句柄參數解析出來的EPROCESS對象)、Cid(包括UniqueProcess和UniqueThread成員)) 這裏Cid.UniqueProcess是從Process對象中來的,而Cid.UniqueThread則是經過調用ExCreateHandle()函數在CID句柄表中建立一個句柄表項而得到的 */ ExInitializeRundownProtection (&Thread->RundownProtect); Thread->ThreadsProcess = Process; Thread->Cid.UniqueProcess = Process->UniqueProcessId; CidEntry.Object = Thread; CidEntry.GrantedAccess = 0; Thread->Cid.UniqueThread = ExCreateHandle (PspCidTable, &CidEntry); if (Thread->Cid.UniqueThread == NULL) { ObDereferenceObject (Thread); return (STATUS_INSUFFICIENT_RESOURCES); } /* 繼續初始化新線程對象ETHREAD結構中的一些域(ReadClusterSize、LpcReplySemaphore、LpcReplyChain、IrpList、PostBlockList、ThreadLock(線程鎖成員)、
ActiveTimerListLock、ActiveTimerListHead) */ Thread->ReadClusterSize = MmReadClusterSize; KeInitializeSemaphore (&Thread->LpcReplySemaphore, 0L, 1L); InitializeListHead (&Thread->LpcReplyChain); InitializeListHead (&Thread->IrpList); InitializeListHead (&Thread->PostBlockList); PspInitializeThreadLock (Thread); KeInitializeSpinLock (&Thread->ActiveTimerListLock); InitializeListHead (&Thread->ActiveTimerListHead); /* 得到"進程"的RundownProtect鎖,以免在建立過程當中該進程被停掉(rundown)。直到該線程被插入到進程的線程鏈表中(經過KeStartThread函數)以後,PspCreateThread此釋放該鎖 */ if (!ExAcquireRundownProtection (&Process->RundownProtect)) { ObDereferenceObject (Thread); return STATUS_PROCESS_IS_TERMINATING; } if (ARGUMENT_PRESENT (ThreadContext)) { /* 若是ThreadContext非NULL,則這次建立的是用戶模式線程,因而調用MmCreateTeb建立一個Teb,並用InitialTeb進行初始化 */ Status = MmCreateTeb (Process, InitialTeb, &Thread->Cid, &Teb); if (!NT_SUCCESS (Status)) { ExReleaseRundownProtection (&Process->RundownProtect); ObDereferenceObject (Thread); return Status; } try { /* 利用ThreadContext中的程序指針(EIP寄存器)來設置線程的啓動地址StartAddress,而且將ThreadContext中的EAX寄存器設置到線程的Win32StartAddress域 */ Thread->StartAddress = (PVOID)CONTEXT_TO_PROGRAM_COUNTER(ThreadContext); } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } /* 調用KeInitThread函數,根據進程對象中的信息來初始化新線程內核對象(KTHREAD)的一些屬性(包括跟同步相關的各個域: 同步頭(Header)、WaitBlock初始化、
線程的系統服務表(SSDT ServiceTable域)、與APC、定時器相關的域、線程的內核棧的初始化等等)。最後,KeInitThread函數根據所提供的參數信息調用
KiInitializeContextThread以便完成與特定處理器相關的執行環境的初始化。所以,當這個函數執行完後,新線程的狀態是"已初始化(Initialized)" Ps: 回顧總路線圖,咱們如今已經到了"建立並初始化一個執行體線程塊(ETHREAD)"這一步了 */ if (NT_SUCCESS (Status)) { Status = KeInitThread (&Thread->Tcb, NULL, PspUserThreadStartup, (PKSTART_ROUTINE)NULL, Thread->StartAddress, ThreadContext, Teb, &Process->Pcb); } } else { /* 不然,則是系統線程。首先在CrossThreadFlags標誌中設置系統線程位。而後將線程的啓動地址設置爲StartRoutine參數。最後一樣地調用KeInitThread函數,
因此,KeInitThread函數既能夠被用來初始化用戶模式線程,也能夠被用來初始化系線程 */ Teb = NULL; PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_SYSTEM); Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine; Status = KeInitThread (&Thread->Tcb, NULL, PspSystemThreadStartup, StartRoutine, StartContext, NULL, NULL, &Process->Pcb); } if (!NT_SUCCESS (Status)) { if (Teb != NULL) { MmDeleteTeb(Process, Teb); } ExReleaseRundownProtection (&Process->RundownProtect); ObDereferenceObject (Thread); return Status; } /* 鎖住進程,並確保此進程並非在退出或終止過程當中 */ PspLockProcessExclusive (Process, CurrentThread); if ((Process->Flags&PS_PROCESS_FLAGS_PROCESS_DELETE) != 0 || (((CurrentThread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) != 0) && (ThreadContext != NULL) && (THREAD_TO_PROCESS(CurrentThread) == Process))) { PspUnlockProcessExclusive (Process, CurrentThread); KeUninitThread (&Thread->Tcb); if (Teb != NULL) { MmDeleteTeb(Process, Teb); } ExReleaseRundownProtection (&Process->RundownProtect); ObDereferenceObject(Thread); return STATUS_PROCESS_IS_TERMINATING; } /* 而後進程的活動線程數加1,而且將新線程加入到進程的線程鏈表中。 */ OldActiveThreads = Process->ActiveThreads++; InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry); /* 調用KeStartThread,初始化KTHREAD剩餘的域(和調度相關的域: 優先級(BasePriority)、實現設置(Quantum)、處理器親和性(Affinity)等等)。通過這一步的處理,新線程就能夠
開始運行了 Ps: 在PspCreateThread和KeStartThread這兩個函數中,咱們均可以看到InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);這樣的調用,這是分別
在執行體層和內核層維護線程和進程的關係(線程從屬進程)即EPROCESS->ETHREAD、KPROCESS->KTHREAD。 */ KeStartThread (&Thread->Tcb); PspUnlockProcessExclusive (Process, CurrentThread); ExReleaseRundownProtection (&Process->RundownProtect); /* 若這是進程中的第一個線程,則觸發該進程的建立通知 */ if (OldActiveThreads == 0) { PERFINFO_PROCESS_CREATE (Process); if (PspCreateProcessNotifyRoutineCount != 0) { ULONG i; PEX_CALLBACK_ROUTINE_BLOCK CallBack; PCREATE_PROCESS_NOTIFY_ROUTINE Rtn; for (i=0; i<PSP_MAX_CREATE_PROCESS_NOTIFY; i++) { CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]); if (CallBack != NULL) { Rtn = (PCREATE_PROCESS_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack); Rtn (Process->InheritedFromUniqueProcessId, Process->UniqueProcessId, TRUE); ExDereferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i], CallBack); } } } } /* 若是新線程所屬的進程在一個做業中,則須要作特定的處理 */ Job = Process->Job; if (Job != NULL && Job->CompletionPort && !(Process->JobStatus & (PS_JOB_STATUS_NOT_REALLY_ACTIVE|PS_JOB_STATUS_NEW_PROCESS_REPORTED))) { PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NEW_PROCESS_REPORTED); KeEnterCriticalRegionThread (&CurrentThread->Tcb); ExAcquireResourceSharedLite (&Job->JobLock, TRUE); if (Job->CompletionPort != NULL) { IoSetIoCompletion (Job->CompletionPort, Job->CompletionKey, (PVOID)Process->UniqueProcessId, STATUS_SUCCESS, JOB_OBJECT_MSG_NEW_PROCESS, FALSE); } ExReleaseResourceLite (&Job->JobLock); KeLeaveCriticalRegionThread (&CurrentThread->Tcb); } PERFINFO_THREAD_CREATE(Thread, InitialTeb); /* 通知哪些接收線程建立事件的出調例程(callout routine),即以前綁定在這個線程建立事件的回調函數的例程 */ if (PspCreateThreadNotifyRoutineCount != 0) { ULONG i; PEX_CALLBACK_ROUTINE_BLOCK CallBack; PCREATE_THREAD_NOTIFY_ROUTINE Rtn; for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { CallBack = ExReferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i]); if (CallBack != NULL) { Rtn = (PCREATE_THREAD_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack); Rtn (Thread->Cid.UniqueProcess, Thread->Cid.UniqueThread, TRUE); ExDereferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i], CallBack); } } } /* 線程對象的引用計數加2: 一個針對當前的建立操做,另外一個針對要返回的線程句柄 */ ObReferenceObjectEx (Thread, 2); /* 若是CreateSuspended參數指示新線程當即被掛起,則調用KeSuspendThread掛起新線程 */ if (CreateSuspended) { try { KeSuspendThread (&Thread->Tcb); } except ((GetExceptionCode () == STATUS_SUSPEND_COUNT_EXCEEDED)? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { } if (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) { KeForceResumeThread (&Thread->Tcb); } } /* 根據指定的指望訪問權限,調用SeCreateAccessStateEx()函數建立一個訪問狀態結構(ACCESS_STATE) */ AccessState = NULL; if (!PsUseImpersonationToken) { AccessState = &LocalAccessState; Status = SeCreateAccessStateEx (NULL, ARGUMENT_PRESENT (ThreadContext)?PsGetCurrentProcessByThread (CurrentThread) : Process, AccessState, &AuxData, DesiredAccess, &PsThreadType->TypeInfo.GenericMapping); if (!NT_SUCCESS (Status)) { PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) { (VOID) KeResumeThread (&Thread->Tcb); } KeReadyThread (&Thread->Tcb); ObDereferenceObjectEx (Thread, 2); return Status; } } /* 調用ObInsertObject函數把新線程對象插入到當前進程的句柄表中 */ Status = ObInsertObject (Thread, AccessState, DesiredAccess, 0, NULL, &LocalThreadHandle); if (AccessState != NULL) { SeDeleteAccessState (AccessState); } if (!NT_SUCCESS (Status)) { /* ObInsertObject調用若是不成功,則終止新線程 */ PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) { KeResumeThread (&Thread->Tcb); } } else { /* ObInsertObject調用成功,設置好輸入參數ThreadHandle和ClienId,準備返回 */ try { *ThreadHandle = LocalThreadHandle; if (ARGUMENT_PRESENT (ClientId)) { *ClientId = Thread->Cid; } } except(EXCEPTION_EXECUTE_HANDLER) { PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) { (VOID) KeResumeThread (&Thread->Tcb); } KeReadyThread (&Thread->Tcb); ObDereferenceObject (Thread); ObCloseHandle (LocalThreadHandle, PreviousMode); return GetExceptionCode(); } } /* 設置新線程的建立時間 */ KeQuerySystemTime(&CreateTime); ASSERT ((CreateTime.HighPart & 0xf0000000) == 0); PS_SET_THREAD_CREATE_TIME(Thread, CreateTime); if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == 0) { Status = ObGetObjectSecurity (Thread, &SecurityDescriptor, &MemoryAllocated); if (!NT_SUCCESS (Status)) { PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) { KeResumeThread(&Thread->Tcb); } KeReadyThread (&Thread->Tcb); ObDereferenceObject (Thread); ObCloseHandle (LocalThreadHandle, PreviousMode); return Status; } SubjectContext.ProcessAuditId = Process; SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process); SubjectContext.ClientToken = NULL; /* 設置新線程的訪問權限: GrantedAccess域 */ AccessCheck = SeAccessCheck (SecurityDescriptor, &SubjectContext, FALSE, MAXIMUM_ALLOWED, 0, NULL, &PsThreadType->TypeInfo.GenericMapping, PreviousMode, &Thread->GrantedAccess, &accesst); PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken); ObReleaseObjectSecurity (SecurityDescriptor, MemoryAllocated); if (!AccessCheck) { Thread->GrantedAccess = 0; } Thread->GrantedAccess |= (THREAD_TERMINATE | THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION); } else { Thread->GrantedAccess = THREAD_ALL_ACCESS; } /* 最後,調用KeReadyThread函數,調用KeReadyThread函數將線程加入到進程對象中的線程就緒隊列中(kprocess->ReadyListHead)
他使新線程進入"就緒(ready)狀態",準備立刻執行(就緒態只是代表它有機會得到CPU的調度,並非指馬上就能執行)。 或者,若此時進程被內存調度換出了內存,則新線程的狀態爲"轉移(transition)",以等待換入內存後再執行 */ KeReadyThread (&Thread->Tcb); /* 引用計數減1,當前操做完成,返回 */ ObDereferenceObject (Thread); return Status; }
線程建立完成後調用CsrClientCallServer通知CRSS 線程建立成功,因此這個通知過程,咱們詳細說明一下:
到這個點上,全部必要的執行體進程對象和執行體線程對象都已經被建立出來了。接下來kernel32.dll給windows"子系統CSRSS"發送一個消息,從而它能夠進一步創建起新進程和線程,
該消息包含如下信息: 1. 進程和線程的句柄 2. 建立標誌中的各個項目 3. 該進程的建立者ID 4. 指示該進程是否屬於一個windows應用程序的標誌(Csrss能夠肯定是否要顯示啓動光標) 當windows子系統Csrss接收到此消息時,它執行如下12個步驟 1. CreateProcess複製一份該進程和線程的句柄。在這一步,進程和線程的使用計數從1(在建立時設置的)增長到2 2. 若是進程的優先級和類別沒有指定的話,則CreateProcess自動進行優先級類別設置 3. 分配Csrss進程塊 4. 新進程的異常端口被設置爲windows子系統的通用功能端口,這樣,當該進程中發生異常時,windows子系統將會收到一個消息 5. 若是該進程正在被調試(即它被附載到一個調試器進程)的話,則該進程的調試端口被設置爲windows子系統的通用功能端口。這一設置能夠保證,windows將把在新進程中發生的調試事件
(好比線程建立和刪除、異常、等等)做爲消息發送給windows子系統in個,從而它能夠把這些事件分發給新進程的調試器進程 6. 分配並初始化Csrss線程塊 7. CreateProcess把該線程插入到進程的線程列表中 8. 遞增該會話中的進程計數值 9. 進程的停機級別被設置爲0x280,這是進程默認停機級別 10. 將新進程塊插入到windows子系統範圍內的進程的列表中 11. windows子系統的內核模式部分所用到的,針對每一個進程的數據結構(W32PROCESS結構)被分配並初始化 12. 顯示應用程序啓動光標。即沙漏箭頭。這是windows提醒用戶的方式: "我正在啓動某些東西"
代碼調用NtRequestWaitReplyPort()來等待迴應,最後調用NtResumeThread()函數恢復線程的執行。
在以上調用完後,會調用內核中線程的啓動函數KiThreadStartup, 該函數的實現中調用了PspUserThreadStartup,該函數初始化用戶APC,將LdrInitializeThunk函數做爲APC函數掛入 APC隊列中,最後調用KiDeliverApc發生APC, 經過中斷返回3環(注意,這裏又再次回到ring3)。 //這裏插個題外話,在一個進程注入的技術中談到的APC注入指的就是這一步了,咱們經過註冊"進程建立"回調函數往"遠程進程"的APC隊列中插入了DLL文件,即插入了遠程進程的APC隊列中, 那麼操做系統在這一步就會逐個執行APC隊列中的任務,一樣也把咱們注入的DLL給執行了
當PspUserThreadStartup返回到KiThreadStartup中後,它從內核模式中返回,切換到3環KiUserApcDispatcher,實現中調用LdrInitializeThunk來加載須要的DLL,而且用DLL_PROCESS_ATTACH功能代碼來調用DLL的入口點
KiUserApcDispatcher lea edi, [esp+arg_C] pop eax call eax //LdrInitializeThunk push 1 push edi call _ZwContinue@8 ;加載完DLL後,調用該函數繼續返回到內核
切換到內核後做部分操做後,再次回到3環(注意,這裏又再次回到ring3,這一階段來回次數較多,放慢速度理解一下),
調用用戶空間的線程入口函數BaseProcessStart(Kernel32.dll中的BaseProcessStart函數), 該函數在3環中BaseInitializeContext中有指定。 這個時候,線程就從以前指定的入口點代碼處開始執行起來了。固然,它要根據優先級遵循CPU的調度,分配到了時間才能執行。
//上面說了這一大段,整理一下流程: 1. KiThreadStartup下降IRQL到APC_LEVEL 2. 而後調用系統初始的線程函數PspUserThreadStartup 3. PspUserThreadStartup把一個用戶模式的APC插入到線程的用戶APC隊列中,此APC例程是在全局變量PspSystemDll中指定的,指向ntdll.dll的LdrInitializeThunk函數 4. PspUserThreadStartup函數返回以後,KiThreadStartup函數返回到用戶模式,此時,PspUserThreadStartup插入的APC被交付,因而LdrInitializeThunk函數被調用,
這是映像加載器(image loader)的初始化函數。LdrInitializeThunk函數完成加載器、堆管理器等初始化工做,而後加載必要的DLL,而且調用這些DLL的入口函數。最後,
當LdrInitializeThunk返回到用戶模式APC分發器時,該線程開始在用戶模式下執行,調用應用程序指定的線程啓動函數,此啓動函數的地址已經在APC交付時備被壓入到用戶棧中 5. 至此,進程已經完成創建起來了,開始執行用戶空間的代碼
附上線程建立的流程圖
至此,階段5的線程建立也分析結束了,此後線程就正常的運行起來了,至於以後在代碼中是否要加載DLL仍是別的功能,那和具體的代碼邏輯有關,CPU以線程爲調度單位根據CPU調度算法輪詢地執行線程中的代碼邏輯。
這裏還有一個問題,咱們在文章的最開始說過: 這個ring3層的進程建立分析,即這個進程建立的發起源地是ring3。那若是要從ring0開始建立進程呢?答案是95%幾乎同樣,惟一不同的的最開始的入口,咱們在內核層建立進程調用的NtCreateProcess(),這也是一個"轉接層"函數
NTSTATUS NtCreateProcess( __out PHANDLE ProcessHandle, __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in HANDLE ParentProcess, __in BOOLEAN InheritObjectTable, __in_opt HANDLE SectionHandle, __in_opt HANDLE DebugPort, __in_opt HANDLE ExceptionPort ) { ULONG Flags = 0; if ((ULONG_PTR)SectionHandle & 1) { Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY; } if ((ULONG_PTR) DebugPort & 1) { Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT; } if (InheritObjectTable) { Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES; } return NtCreateProcessEx (ProcessHandle, DesiredAccess, ObjectAttributes OPTIONAL, ParentProcess, Flags, SectionHandle, DebugPort, ExceptionPort, 0); }
函數的源代碼位於 base\ntos\ps\create.c 中,它知識簡單地對參數稍做處理,而後把建立進程得分任務交給NtCreateProcessEx函數,以後的流程就和咱們以前分析的如出一轍了。
ring3建立進程和ring0建立進程的大體流程是:
1.1. ring3: CreateProcessA->CreateProcessInternalA->CreateProcessInternalW->NtCreateProcessEx->PspCreateProcess->NtCreateThread->PspCreateThread-> KiThreadStartup->PspUserThreadStartup->LdrInitializeThunk->BaseProcessStart //或 //CreateProcessA()只是起來轉接層的做用 1.1 ring3: CreateProcess->CreateProcessInternalW->NtCreateProcessEx->PspCreateProcess->->NtCreateThread->PspCreateThread->KiThreadStartup-> PspUserThreadStartup->LdrInitializeThunk->BaseProcessStart
2. ring0: NtCreateProcess->NtCreateProcessEx->PspCreateProcss->->NtCreateThread->PspCreateThread->KiThreadStartup->PspUserThreadStartup-> LdrInitializeThunk->BaseProcessStart
至此,windows中建立進程/線程的所有流程咱們都分析清楚了,如今咱們能夠理解了windows系統中一個用戶進程的整個建立過程,雖然有一部分工做是由windows子系統來完成的,可是從操做系統內核的角度,咱們依然能夠清晰地看到,windows爲了支持進程和線程的概念,是如何以對象的方式來管理它們,並建立和初始化進程和線程,使它們變成真正能夠工做的功能實體。
本文也到此結束,若有不對的地方,懇請朋友們指正,共同窗習