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