Windows 進程創建完整過程(除去細節)
當前流程是分析WinXP x86得到的,在最新版本Windows上不一定正確,但是可以做一個參考,
由于我這里符號并不全,所以導致我這里有些東西看到的可能是錯誤的,誤導了我,然后我就做了個錯誤的記錄,
有緣人如果看到的話,可以幫我指正一下,我會很高興。
工作挺忙的,三天的業余時間,哎,個人水平問題吧,還是沒有辦法詳細地分析出完整套路,算是個簡要分析吧。
?
差點忘記了,我這里分析的文件是 kernel32.dll 和 ntoskrnl.exe
?
?
1:入口 CreateProcessW
當我們在應用層調用CreateProcessW的時候,參數稍作整理,會直接調用到?CreateProcessInternalW
(CreateProcessA,會調用 CreateProcessInternalA,然后整理了參數也會調用到CreateProcessInternalW)
?
?
2:CreateProcessInternalW
首先,開場是大概是2000來行的匯編指令,判斷進程創建參數,以及文件安全屬性等等亂七八糟沒用的,
?
然后是?NtOpenFile?NtCreateSection 一系列的函數,獲取文件句柄和section句柄
?
中間經過一系列的文件類型判斷,有效性判斷,及屬性判斷,
?
調用?NtQuerySection 獲取段屬性,
?
判斷是否需要Debug方式啟動,并且做對應的設置,
?
加載?advapi32.dll ,然后獲取?CreateProcessAsUserSecure 函數地址,但是并沒有使用它,
?
很可能是根據這個函數是否存在,來判斷當前操作系統版本,后面直接調用?NtQuerySystemInformation 來獲取操作系統信息,參數很奇怪,0x47,可能就是特殊的情況,
(有空再看)
?
中間判斷了一個位,然后調用了DBG相關的函數,(可能是判斷DBG相關吧)
?
最后就是一個Nt函數,NtCreateProcessEx,進入內核了,(也可能是個Zw函數)
?
?
3:NtCreateProcessEx
上一部分,調用ntdll 的ZwCreateProcessEx/NtCreateProcessEx 之后,
進入內核,走SSDT表,第48項,進入NtCreateProcessEx,
?
判斷當前執行模式,內核模式的話,就往下走,否則,改了模式往下走
調用PspCreateProcessEx,
?
1)獲取父進程信息,
并且繼承父進程的執行位置
?
2)給子進程創建一個EPROCESS,然后初步設置它
?
3)初始化子進程的線程列表
?
4)增加引用計數,并且繼承父進程的QuotaBlock,如果父進程存在的話,否則用系統默認的
?
5)繼承父進程的DeviceMap,如果有父進程,否則用系統默認的
?
6)如果父進程存在,那么再繼承父進程的屬性
?
7)如果參數Section句柄存在,那么就獲取對象,并且保存,后面需要用,保存到進程中
這個Section,實際上是上面應用層CreateProcessInternalW函數創建的那個Section
?
8)如果Debug端口存在,那么就獲取它的對象,如果不存在,則從父進程繼承
上面的第一個jnz跳轉到下面,下面的最后一個jmp回到上面的cmp處
?
9)初始化PrimaryToken
?
10)中間繼續初始化各種亂七八糟東西,包括
初始化進程地址空間
如果當前進程EProcess不存在,就用另外一種方法,初始化進程空間
PCB
優先級組,因為后面直接就用EPROCESS了,所以我推斷這里可能同時也初始化了EPROCESS剩下的部分
?
11)如果 section存在,那么attach到子進程,
在很深的位置先循環調用了N次?MiMapViewOfPhysicalSection 函數來映射物理內存地址,然后
初始化section,根據section展開文件到子進程
這里有else,但是一般來說,不會走到else里面,因為如果是有效PE,那么section肯定存在,這是應用層已經找到的,然后轉化成的內核對象,
然后向自己內部再影射一個子進程模塊的內存,映射了之后,就釋放掉,只是判斷是否成功,如果成功,什么都不干,不成功就返回到label_48
(走到else里面也是同樣的操作,只不過目標section不存在,那么就只能映射自己的)
?
///中間有一塊這段代碼,個人能力問題,無法看出這里是做什么的,因為結構體識別可能是有一定的問題
///?
?
12)初始化PEB
PEB
實際上這里有點問題,v74在前面的時候,是必須 hSection 存在的時候,才會 ==1,也就是說必須hSection存在,才會進入外層if
但是外層if進入了之后,反過來說,就是hSection肯定存在,肯定不會走else,但是為什么它這么寫
?
13)初始化APC?不知道是否真的是這樣,但是清理APC的功能,這里是第一次使用
然后判斷APC對列里面有沒有APC,如果有的話,觸發一個軟中斷,去運行它
?
14)做一個 AccessState ,然后使用它把子進程EPROCESS放到進程句柄表中,返回一個句柄
然后AccessState就沒用了,釋放掉
最后設置一下進程優先級組
?
15)接近尾聲,這里獲取當前進程允許的訪問權限位
?
16)收尾
設置進程創建時間,然后返回進程句柄,減掉自己的引用計數,防止泄露
?
?
4:CreateProcessInternalW,回到應用層了
沿著第二部分的NtCreateProcessEx繼續來看,這里已經回到應用層了,
繼續往下,并且這里拿到了一個 新進程的句柄,也就是第一個參數
?
一大片代碼,設置進程優先級和處理模式相關
NtSetInformationProcess
NtAllocateVirtualMemory
申請了一塊內存,然后好像也沒用過,
創建命令行參數,
初始化stdin stdout stderr
BasePushProcessParameters
就是直接dup出來,然后給對面進程用
給目標進程創建一個棧
給線程構建上下文
然后,主線程就可以跑了
(上下文構建中,其實有個小細節)
其實在?BaseInitializeContext 函數中,根據第五個參數,會判斷走哪個啟動函數
如上,進程啟動,最后一個參數,寫死為0,則會走最下面的?BaseProcessStartThunk 函數
而?BaseProcessStartThunk 內部還調用了。。。
再往里看,就是這樣
通過對比另外兩個函數,可以推斷,這里應該就是走主線程 _tmainCRTStartup / wWinMainCRTStartup 位置的了
?
?
5:NtCreateThread 開始主線程部分
應用層陷入內核,走SSDT,到了驅動里面的NtCreateThread,
進來還是模式校驗,
然后走
?
1)獲取進程對象
?
2)創建ETHREAD
?
3)在進程句柄表中創建線程的handle
?
4)創建TEB
初始化TEB
?
5)中間使用了超級大篇幅來初始化這個ETHREAD
初始化了之后,這一塊,就是準備開跑了,
進程的活動線程數++,然后插入列表,然后啟動線程,
?
6)直到這里,這里是進程創建回調,注意哦,這里是在父進程里面調用的
?
7)判斷作業是否在工作,所在進程是否在作業中,如果在的話,特殊處理,讓他去完成,并且清理APC
?
8)壓軸戲,線程創建回調
?
9)兩個回調結束之后,似乎就沒什么好做得了,把線程對象插入句柄表
這里的插入和前面的創建不是一個東西,那個ExCreateHandle 是創建全局句柄表,
這里是插入進程句柄表
?
10)后面就是枯燥乏味的收尾工作
寫時間戳,寫訪問權限,解對象引用,
再清理一次APC,
然后把線程句柄返回,
?
11)補充一下吧
正常結束時,這里實際上是有個收尾的小工作的,
這里面KeReadyThread 是關鍵操作,它把 ETHREAD 放到了進程 KPROCESS 的?ReadyListHead 里面,
這樣應該就可以swapcontext了,
其實它內部還有眾多關鍵函數,如?KiSetSwapEvent ,看名字和內部實現,似乎就是搶搶占時間片去了。
?
我所關心的整個部分,實際上就是,
進程、線程創建回調的觸發時機,實際上觸發的進程上下文都在父進程中,觸發時機都在PspCreateThread中,
因為實際工作中,我們能用到的部分,也就這里了
?
?
6:回到應用層
這里直接判斷返回值,有問題的話,直接錯誤返回,
沒有問題的情況下,那么繼續往下做判斷
?
1)通知Csrss
由于我這里符號不全,我想下面的ExitStatus 應該是 Csrss返回的吧。
一旦有問題,立刻退出
?
2)指派進程到一個作業中
?
3)后面直到最后,通篇都是整理內存,釋放空間了,
基本上就沒有干別的活,
最后才是函數返回。
?
這樣,整體流程結束。
?
?
補充一下,鏡像加載回調的位置吧,
這個,實際上,鏡像加載回調被調用的時候,已經和鏡像沒什么關系了,
而且鏡像早早已經被鋪到內存中了
在InitThread之前,是初始化TEB部分,這里初始化了TEB之后,設置了一個回調函數
創建應用層線程PspUserThreadStartup
進入這個函數里面,可以看到
有這樣一個判斷,由于我實在沒有找到那個6代表什么,可能是2|4,但是我沒找到,所以無法知道它是什么,
進入函數之后,里面會進行一次鏡像加載回調狀態的判斷,如果非隱藏狀態,那么再經過一個函數,
就會到這里,聯系外面的函數,可以清晰地看出,這里就是鏡像加載回調的調用位置,
總地來說,由于鏡像加載回調有可能是異步調用的,所以無法確切地知道它的位置,
但是它的位置實際上是在前兩個回調之后的(其實已知,這些都是廢話)
?
?
?
重點:
在CreateProcess 函數中,會開辟子進程的進程空間,
然后同時會map子進程的主模塊到進程空間中,這時不會調用鏡像加載回調和進程創建回調,
到了CreateThread 里面,整理了線程各種信息之后,
會先調用進程創建回調,
然后調用線程創建回調,
這時,當前進程上下文還是在父進程中,
最后,當線程跑起來之后,
第一個回調會被觸發,就是主模塊的鏡像加載回調,
這時剩下的就是其他模塊的鏡像加載回調了,
后面就不是很重要了,前面這個流程應該是最重要的。