【安全报告】揭秘创建进程时ebx为什么指向peb的答案
一、背景
為了確保被替換后的進程能順利執行不崩潰,需要獲取原進程各種上下文,并修改被替換后的新進程上下文,其中在原進程被掛起還沒開始執行的時候,需要將eax指向新oep,而ebx指向新peb,而為什么這樣設置的原因卻很少有人提及。為此,在經過查閱了一定的資料與簡單的分析后,我們可以找到答案。
二、具體分析
先拋出結論,這里的eax與ebx屬于線程上下文信息,在一個PE文件開始被運行的過程中,主線程上下文初始化過程是在進程已經創建完成,而主線程還沒創建的階段發生的,下面是具體更詳細的分析:
首先我們需要對進程的創建有一個大概的認識,在ring3下創建進程API無非是CreateProcessA/W,但是無論調用哪一個,最終都會將相關參數轉化為Unicode字符串,并最終調用CreateProcessInternalW,因此以下將主要分析CreateProcessInternalW,而在xp和win7下,它具體實現又有一些不一樣的地方。
2.1 XP下執行流程
在xp下,它大概分為四個部分,分別是ring3下創建進程,ring0下創建進程,ring3下創建線程,ring0下創建線程,以NtCreateProcessEx為分界線,NtCreateProcessEx之前為ring3下創建進程主要流程。
2.1.1 Ring3下創建進程
如果lpApplicationName不為空直接調用RtlDosPathNameToNtPathName_U函數將DOS路徑(C:\WINDOWS\XXX)轉換為NT路徑(\Device\HarddiskVolume1\WINDOWS\XXX),為空則會解析lpCommandLine,主要按照’”’引號,’ ’空格,’\t’制表符作為分隔符進行解析并獲取相應的PE文件,然后將DOS路徑轉換為NT路徑。
4. 調用NtOpenFile得到文件句柄,調用了NtCreateSectiond函數得到內存區對象句柄。
調用BasepIsProcessAllowed函數, 該函數用來判斷應用程序名是否在授權文件列表中。
之后會經過一大段的函數進行各種校驗,再得到內存區對象句柄后調用NtQuerySection函數,返回后得到節的基本信息(節基地址,大小,屬性),并判斷創建標志中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,如果不包含該標志,則判斷PEB->ReadImageFileExecOptions域是否為0,如果包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,或者不包含該標志但ReadImageFileExecOptions域不為0, 調用LdrQueryImageFileExecutionOptions函數查詢該信息。
之后會經過一大段的函數進行各種校驗,再得到內存區對象句柄后調用NtQuerySection函數,返回后得到節的基本信息(節基地址,大小,屬性),并判斷創建標志中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,如果不包含該標志,則判斷PEB->ReadImageFileExecOptions域是否為0,如果包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,或者不包含該標志但ReadImageFileExecOptions域不為0, 調用LdrQueryImageFileExecutionOptions函數查詢該信息。
檢查鏡像文件的部分信息的有效性,并調用函數BasepIsImageVersionOk判斷鏡像文件版本是否合法。
加載advapi32.dll并獲得CreateProcessAsUserSecure函數的地址。
調用BaseFormatObjectAttributes將安全屬性結構格式為NT對象屬性結構(得到了對象屬性),接著調用了_DbgUiConnectToDbg在實現通過調用NtCreateDebugObject函數來創建調試對象,調用DbgUiGetThreadDebugObject來獲得調試對象(作為參數傳遞到0環)。
最后調用NtCreateProcessEx函數。
2.1.2 Ring0下創建進程
NtCreateProcessEx內為ring0下創建進程主要流程。
判斷父進程是否存在,若不存在則退出,否則,調用PspCreateProcess。
在PspCreateProcess中,保存當前線程運行的前一個模式,通過KTHREAD->PreviousMode可以得到前一個模式,并判斷創建標志是否包含除DEBUG_PROCESS, DEBUG_ONLY_THIS_PROCESS,CREATE_SUSPENDED的標志之外其它標志, 如果包含其他的標志,則報錯退出。
通過參數ParentProcess調用ObReferenceObjectByHandle函數得到父進程對象的指針。
判斷參數 JobMemberLevel是否為0, 如果不為0,接著判斷父進程的EPROCESS->Job是否為0,如果JobMemberLevel不為為0且EPROCESS->Job為0,則返回無效參數錯誤后退出該函數;否則的話,將父進程對象中的屬性保存到局部變量中。
調用ObCreateObject函數創建新進程對象并將對象內容初始化為0,然后從父進程繼承配額信息(PspInheritQuot)和設備位圖信息(ObInheritDeviceMap),將父進程對象中的部分域給新進程。
判斷參數SectionHandle是否為0,若不為0,調用ObReferenceObjectByHandle函數得到區對象指針,然后將區對象指針賦值給新進程EPROCESS的相應域。
接著就判斷參數DebugPort是否為0,若不為0,調用ObReferenceObjectByHandle函數通過調試對象句柄得到調試對象指針,否則調用DbgkCopyProcessDebugPort函數從父進程拷貝DebugPort給新進程。
判斷參數ExceptionPort是否為0,若不為0,調用ObReferenceObjectByHandle函數通過異常端口對象句柄得到異常端口對象指針。
接著調用PspInitializeProcessSecurity函數來設置新進程的安全屬性, 主要是設置新進程的安全令牌對象。該函數會調用SeSubProcessToken函數來設置新進程對象的令牌對象。
接著調用MmCreateProcessAddressSpace為新進程創建地址空間,并構建頁目錄表、頁表及物理頁的關系。
調用KeInitializeProcess函數初始化新進程對象中內核對象、優先級、親和性、頁目錄表物理地址幀號。
調用ObInitProcess函數來初始化新進程對象的表。
調用MmInitializeProcessAddressSpace函數初始化進程地址空間,該函數的實現中調用了KiAttachProcess函數來實現進程的切換(將當前線程掛靠到新進程中),以及初始化EPROCESS中的部分域和PFN、工作集列表等。
調用PspMapSystemDll函數映射新進程對象的系統DLL(即NTDLL,映射第一個DLL),該函數會調用MmMapViewOfSection映射節區,而MmMapViewOfSection會調用MiMapViewOfImageSection函數將DLL作為鏡像映射。
接著調用MmGetSessionId函數獲得指定進程的會話ID,然后調用SeSetSessionIdToken函數設置令牌的會話ID,之后再調用ExCreateHandle函數在PspCidTable中添加一項(PID)。
調用MmCreatePeb為新進程創建PEB,該函數首先通過調用KeAttachProcess函數將當前線程切換到新進程對象,然后通過MmMapViewOfSection函數將NLS節區映射到新進程的地址空間中,隨后調用MiCreatePebOrTeb創建PEB/TEB。
在MiCreatePebOrTeb函數中,該函數首先會通過ExAllocatePoolWithTag來申請0x34大小的空間,接著通過MiFindEmptyAddressRangeDownTree函數在VAD樹中查找一塊未被使用的地址空間范圍,并返回該范圍的起始地址,最后通過MiInsertVad函數將申請的地址空間插入到VAD樹中。
在創建PEB結構后,初始化PEB中部分域的值(鏡像基地址,操作系統編譯號等域),最后調用KeDetachProcess函數使線程回到原來的線程中。截止此步驟,PEB創建完成。
同時觀察也可以發現,這里也解析了包括Nt頭、擴展頭、擴展頭魔術字效驗等關鍵PE結構信息,聯想到之前分析流程也處理了一部分PE結構,可以猜測早期的PE文件結構逆向可能也是通過逆向進程創建過程,即逆向CreateProcess API來實現的。
接下來我們回到CreateProcessInternalW,以NtCreateThread為分界線,NtCreateProcessEx之后到NtCreateThread之前為ring3下創建線程流程,而NtCreateThread內則是ring0下創建線程流程,經過分析發現,我們所需要尋找的線程上下文設置其實就在ring3下創建線程流程內。
總結
以上是生活随笔為你收集整理的【安全报告】揭秘创建进程时ebx为什么指向peb的答案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Web安全】JSP内存马研究
- 下一篇: 【Web安全】先进技术WebSocket