Windows程序建立的流程分析。
阿新 • • 發佈:2019-02-14
. 建立程序的大體流程:
建立程序的過程就是構建一個環境,這個環境包含了很多的機制 (比如自我保護, 與外界通訊等等)。 構建這個環境需要兩種“人”來協調完成(使用者態和核心態),他們各有分工,其中使用者態提供原料(提供建立的那些引數), 核心態負責來構建這個環境,由於環境是由核心態構建的,因此他持有這個環境的控制權, 而使用者由於提供了原料, 因此他具有使用權。 核心態開始構建環境中的基礎設施(程序物件,等等),在構建完基礎設施後,核心態通知使用者態基礎設施構建已經完成,是否需要繼續構建其他設施,於是使用者態通知核心態繼續構建一條通道(既建立執行緒),方便兩邊的通訊,當用戶態接收到執行緒建立完畢的資訊後,便可以開始使用這個環境(投入生產),以後缺啥補啥 。
二. 3環程序的建立分析:
CreateProcesssA的分析:
在CreateProcessA函式中呼叫了CreateProcessInternalA,呼叫該函式時,增加了兩個引數(一頭一尾加了個零引數),在該函式裡面也沒看到對這兩個引數的引用,而是直接傳遞給CreateProcessInternalW函式。
在CreateProcessInternalA中,首先檢查了引數 lpCmdLine, 判斷是否為0,不為0則將lpCmdLine初始化為UNICODE字串, 為0則做了幾個區域性變數賦值後跳過初始化為UNICODE字串。接著繼續檢查引數,先將lpStartupInfor的內容拷貝到一個區域性變數, 然後判斷引數lpApplicationName是否為0,不為0則引數轉換為UNICODE字串,為0則跳過轉換繼續判斷引數lpCurrentDirectory是否為0,這個和引數lpAppLicationName的判斷邏輯是一樣的。接著判斷STARTUPINFOA.lpReserved域是否為0(MSDN上說在呼叫CreateProcess前,要把lpReserved設定為NULL ,不過我試了下,不為0也沒問題。),該域不為0則和其他字串引數一樣做UNICODE的轉換,後面還判斷STARTUPINFOA中的幾個字串的域,不為0的都作了下轉換。最後呼叫了CreateProcesssW函式, 看上面的步驟大家應該知道了其實CreateProcessInternalA函式只是對字串引數或者結構體中包含字串型別的域的作了檢查和轉換工作,然後就呼叫了下層函式。(附圖)
分析CreateProcesssW的大概流程:
1. 將引數 儲存到區域性變數中。
2. dwCreationFlags 的值至少由一個標誌組合成(一些建立標誌和優先順序型別),首先遮蔽 CREATE_NO_WINDOW 標誌,程式碼如下:
程式碼:
經過遮蔽標誌位後,判斷dwCreationFlags中是否包含CREATE_NEW_CONSOLE | DETACHED_PROCESS的組合, 如果包含它們的組合,參考MSDN上, 存在這種組合是不合法的, 因此跳轉到錯誤處理中, 該錯誤處理例程中,將57h與teb-> LastErrorValue想比較,如果不相等,就更新LastErrorValue的值為57h, 實際上GetLastError() 函式的返回的錯誤碼就是從teb-> LastErrorValue獲取的。
3. 在2中說到dwCreationFlags中也包含優先順序型別的組合, 接著就判斷優先順序,判斷的順序依次是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。
程式碼:
程式碼:
7. 接著呼叫NtOpenFile()得到檔案控制代碼
程式碼:
程式碼:
在得到記憶體區物件控制代碼後呼叫了NtQuerySection函式,返回後得到節的基本資訊(節基地址,大小,屬性)
程式碼:
下面檢查映象檔案的部分資訊的有效性:
程式碼:
程式碼:
程式碼:
然後呼叫BaseFormatObjectAttributes將安全屬性結構格式為NT物件屬性結構(得到了物件屬性)。 接著呼叫了_DbgUiConnectToDbg在實現通過呼叫NtCreateDebugObject函式來建立除錯物件,呼叫DbgUiGetThreadDebugObject來獲得除錯物件(作為引數傳遞到0環)。
程式碼:
程式碼:
三. 0環建立程序的分析:
在NtCreateProcessEx中, 首先是判斷父程序是否存在,如果不存在,就返回一個STATUS_INVALID_PARAMETER錯誤碼(即無效引數),否則呼叫PspCreateProcess, 引數都沒有變,直接傳遞給該函式。
1. 在PspCreateProcess函式中,首先儲存當前執行緒執行的前一個模式(r0/r3), 通過KTHREAD->PreviousMode可以得到前一個模式。
程式碼:
2. 通過引數ParentProcess呼叫ObReferenceObjectByHandle()函式得到父程序物件的指標)
程式碼:
3. 通過ObCreateObject函式建立新程序物件並將物件內容初始化為0.
程式碼:
4. 通過引數SectionHandle呼叫ObReferenceObjectByHandle函式得到區物件指標,當然前提是SectionHandle有效,然後將區物件指標賦值給新程序EPROCESS的相應域中,接著就判斷引數DebugPort是否為0, 當不為0時:
程式碼:
以上部分就是通過引數來得到所需要的物件指標。
5. 接著呼叫PspInitializeProcessSecurity函式來設定新程序的安全屬性, 主要是設定新程序的安全令牌物件(從父程序拷貝):
程式碼:
程式碼:
程式碼:
程式碼:
程式碼:
該函式的實現中呼叫了KiAttachProcess函式來實現程序的切換(將當前執行緒掛靠到新程序中),以及初始化EPROCESS中的部分域和PFN,工作集列表等。
程式碼:
程式碼:
程式碼:
程式碼:
接著呼叫MiCreatePebOrTeb建立PEB/TEB,(該函式通過ExAllocatePoolWithTag來申請_MMVAD結構大小的空間,通過MiFindEmptyAddressRangeDownTree函式在VAD樹中查詢一塊未被使用的地址空間範圍,並返回該範圍的起始地址, 最後通過MiInsertVad函式將申請的地址空間插入到VAD樹中。), 在建立PEB結構後,初始化PEB中部分域的值(映象基地址,作業系統編譯號等域),最後呼叫KeDetachProcess函式使執行緒回到原來的執行緒中。到此建立PEB完成。
程式碼:
程式碼:
建立程序的過程就是構建一個環境,這個環境包含了很多的機制 (比如自我保護, 與外界通訊等等)。 構建這個環境需要兩種“人”來協調完成(使用者態和核心態),他們各有分工,其中使用者態提供原料(提供建立的那些引數), 核心態負責來構建這個環境,由於環境是由核心態構建的,因此他持有這個環境的控制權, 而使用者由於提供了原料, 因此他具有使用權。 核心態開始構建環境中的基礎設施(程序物件,等等),在構建完基礎設施後,核心態通知使用者態基礎設施構建已經完成,是否需要繼續構建其他設施,於是使用者態通知核心態繼續構建一條通道(既建立執行緒),方便兩邊的通訊,當用戶態接收到執行緒建立完畢的資訊後,便可以開始使用這個環境(投入生產),以後缺啥補啥
二. 3環程序的建立分析:
CreateProcesssA的分析:
在CreateProcessA函式中呼叫了CreateProcessInternalA,呼叫該函式時,增加了兩個引數(一頭一尾加了個零引數),在該函式裡面也沒看到對這兩個引數的引用,而是直接傳遞給CreateProcessInternalW函式。
在CreateProcessInternalA中,首先檢查了引數 lpCmdLine, 判斷是否為0,不為0則將lpCmdLine初始化為UNICODE字串, 為0則做了幾個區域性變數賦值後跳過初始化為UNICODE字串。接著繼續檢查引數,先將lpStartupInfor的內容拷貝到一個區域性變數, 然後判斷引數lpApplicationName是否為0,不為0則引數轉換為UNICODE字串,為0則跳過轉換繼續判斷引數lpCurrentDirectory是否為0,這個和引數lpAppLicationName的判斷邏輯是一樣的。接著判斷STARTUPINFOA.lpReserved域是否為0(MSDN上說在呼叫CreateProcess前,要把lpReserved設定為NULL ,不過我試了下,不為0也沒問題。),該域不為0則和其他字串引數一樣做UNICODE的轉換,後面還判斷STARTUPINFOA中的幾個字串的域,不為0的都作了下轉換。最後呼叫了CreateProcesssW函式, 看上面的步驟大家應該知道了其實CreateProcessInternalA函式只是對字串引數或者結構體中包含字串型別的域的作了檢查和轉換工作,然後就呼叫了下層函式。(附圖)
分析CreateProcesssW的大概流程:
1. 將引數 儲存到區域性變數中。
2. dwCreationFlags 的值至少由一個標誌組合成(一些建立標誌和優先順序型別),首先遮蔽 CREATE_NO_WINDOW 標誌,程式碼如下:
程式碼:
mov eax, [ebp+20h] ; ebp+20h = dwCreationFlags and eax, 0F7FFFFFFh ; 遮蔽CREATE_NO_WINDOW標誌。 mov [ebp+20h], eax mov ecx, eax and ecx, 18h ;18h = DETACHED_PROCESS | CREATE_NEW_CONSOLE cmp cl, 18h jz loc_7C8427DE ;如果相等,說明建立標誌不合法
3. 在2中說到dwCreationFlags中也包含優先順序型別的組合, 接著就判斷優先順序,判斷的順序依次是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。
程式碼:
mov eax, [ebp+20h] ; ebp+20h = dwCreationFlags and eax, 0F7FFFFFFh ; 遮蔽CREATE_NO_WINDOW標誌 mov [ebp+20h], eax mov ecx, eax and ecx, 18h cmp cl, 18h ; 判斷dwCreationFlags 是否為CREATE_NEW_CONSOLE |DETACHED_PROCESS jz loc_7C8427DE ; 標誌組合不合法, 進行錯誤處理並退出 mov [ebp+var_6D4], ebx mov [ebp+var_6DC], ebx test al, 40h jnz IsIdlePriority ; 優先順序為IDLE_PRIORITY_CLASS test ah, 40h jnz loc_7C8427F6 test al, 20h jnz IsNormalPriority ; 優先順序為NORMAL_PRIORITY_CLASS test ah, ah js loc_7C842802 test al, al js IsHighPriotity ; 優先順序為HIGH_PRIORITY_CLASS test ah, 1 jnz IsRealTimePriority ; 優先順序為REALTIME_PRIORITY_CLASS4. 繼續判斷dwCreationFlag的情況,首先在dwCreationFlag過濾掉表示優先順序的標誌位,然後在判斷是什麼建立標誌。
程式碼:
mov [ebp+var_668], bl and word ptr [ebp+dwCreateFlag], 3E1Fh ; 遮蔽許可權級表示的位 mov edi, 800h mov esi, 1000h test [ebp+dwCreateFlag], edi jnz loc_7C842832 ; dwCreationFlag = CREATE_SEPARATE_WOW_VDM test [ebp+dwCreateFlag], esi jnz short loc_7C819A33 ; dwCreationFlag = CREATE_SHARED_WOW_VDM mov eax, _BaseStaticServerData cmp [eax+19F4h], bl jnz loc_7C84283C6. 判斷lpEnvironment是否為空, 如果不為空,將Ansi字串轉換為UNICODE_STRING, 為空的話跳過這一步,接著呼叫RtlDosPathNameToNtPathName_U函式將DOS路徑轉換為NT路徑,由於使用者給定的路徑一般都是DOS路徑,而核心需要的是NT路徑,因此需要轉換一下。
7. 接著呼叫NtOpenFile()得到檔案控制代碼
程式碼:
push 60h ;開啟選項 push 5 ;共享模式 lea eax, [ebp+pIoStatusBlock] push eax ; I/0狀態塊 lea eax, [ebp+pObjAttribute] push eax ; 物件屬性 push 1000A1h ;SYNCHRONIZE |FILE_ATTRIBUTE_NORMAL| ;FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY lea eax, [ebp+pHandleOfFile] push eax ; 檔案控制代碼 mov esi, ds:[email protected] ; NtOpenFile(x,x,x,x,x,x) call esi ; NtOpenFile(x,x,x,x,x,x) ; NtOpenFile(x,x,x,x,x,x)接著通過NtOpenFile得到的handle接著呼叫了NtCreateSectiond函式得到記憶體區物件控制代碼
程式碼:
push [ebp+pHandleOfFile] ; 檔案控制代碼 push 1000000h push 10h ; 記憶體區頁面保護屬性 push ebx ; SETION大小 push ebx ; 物件屬性 push 0F001Fh ; 訪問掩碼 lea eax, [ebp+pSectionHandle] push eax ; 指向記憶體區物件的指標(傳出引數) call ds:[email protected] ; NtCreateSection(x,x,x,x,x,x,x)接著呼叫BasepIsProcessAllowed函式, 該函式用來判斷應用程式名是否在授權檔案列表中,函式實現呼叫了NtOpenKey函式打開了登錄檔中的[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\ CurrentVersion\Image File Execution Options鍵, 這個鍵跟以前流行的映象劫持技術有關,但操作真正這麼做的原因不是很清楚,希望知道的朋友交流一下。
在得到記憶體區物件控制代碼後呼叫了NtQuerySection函式,返回後得到節的基本資訊(節基地址,大小,屬性)
程式碼:
push ebx ; 接受返回的大小 push 30h ; 記憶體區資訊的長度 lea eax, [ebp+OepAddress] push eax ; 接受記憶體區資訊Buffer xor edi, edi inc edi push edi ; edi = 1 = SectionImageInformation push [ebp+pSectionHandle] ; 記憶體區控制代碼 call ds:[email protected] ; 將區物件作為映象執行檔案來查詢資訊然後判斷建立標誌中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,如果包含該標誌,判斷PEB->ReadImageFileExecOptions域是否為0,如果不為0, 呼叫LdrQueryImageFileExecutionOptions函式查詢該資訊,如果不包含該標誌,也用呼叫LdrQueryImageFileExecutionOptions函式。
下面檢查映象檔案的部分資訊的有效性:
程式碼:
mov ax, [ebp+MachineType] ; MachineType為機器型別, cmp ax, ds:7FFE002Ch ; ds:7FFE002Ch中的內容為0x014c jb loc_7C84329E cmp ax, ds:7FFE002Eh ja loc_7C84329E cmp dword ptr [ebp+ SubSystemVersion], 2 jz short loc_7C8191C4 ; 子系統次版本號 cmp dword ptr [ebp+ SubSystemVersion], 3 ; 子系統的比較版本(PE資訊中包含) jnz loc_7C842D0C接著呼叫函式BasepIsImageVersionOk判斷映象檔案版本是否合法
程式碼:
movzx eax, [ebp+pImageFileMinorVersion] ; 子系統次版本號 push eax movzx eax, [ebp+pImageFileMajorVersion] ; phSection->GpValue push eax ; 子系統的主版本號 call [email protected] ; 判斷映象檔案的版本是否合法。然後載入advapi32.dll並獲得CreateProcessAsUserSecure函式的地址:
程式碼:
push offset LibFileName ; "advapi32.dll" call [email protected] ; LoadLibraryA(x) mov esi, eax mov [ebp+phModuleOfAdvapi32.dll], esi ; 儲存模組基地址 cmp esi, ebx jz short loc_7C81923C push offset ProcName ; "CreateProcessAsUserSecure" push esi ; hModule call [email protected] ; 獲得指定模組中指定函式的地址獲得函式CreateProcessAsUserSecure地址,不過後面沒有發現呼叫這個函式。
然後呼叫BaseFormatObjectAttributes將安全屬性結構格式為NT物件屬性結構(得到了物件屬性)。 接著呼叫了_DbgUiConnectToDbg在實現通過呼叫NtCreateDebugObject函式來建立除錯物件,呼叫DbgUiGetThreadDebugObject來獲得除錯物件(作為引數傳遞到0環)。
程式碼:
call [email protected] ; 實現呼叫中NtCreateDebugObject mov [ebp+nReturnStatus], eax cmp eax, ebx jl loc_7C82DF58 call [email protected] ; 獲得執行緒除錯物件teb->0xF24, mov [ebp+phandleOfDebugObj], eax最後呼叫NtCreateProcessEx函式完成3環的建立過程
程式碼:
push [ebp+JobLevel] ; 作業級別 push ebx ; 異常埠物件控制代碼 push [ebp+phandleOfDebugObj] ; 除錯物件控制代碼 push [ebp+pSectionHandle] ; 記憶體區物件控制代碼 push [ebp+CreateFlag] ; 建立標誌 or esi, 0FFFFFFFFh push esi ; 父程序控制代碼(FFFFFFFF) push [ebp+ObjAttibute] ; 物件屬性 push 1F0FFFh ; (訪問掩碼) lea eax, [ebp+pHandleOfProcess] push eax ; 保留程序控制代碼值(傳出引數) call ds:[email protected] ; 建立程序(x,x,x,x,x,x,x,x,x)下面附上CreateProcessW函式的流程圖:
三. 0環建立程序的分析:
在NtCreateProcessEx中, 首先是判斷父程序是否存在,如果不存在,就返回一個STATUS_INVALID_PARAMETER錯誤碼(即無效引數),否則呼叫PspCreateProcess, 引數都沒有變,直接傳遞給該函式。
1. 在PspCreateProcess函式中,首先儲存當前執行緒執行的前一個模式(r0/r3), 通過KTHREAD->PreviousMode可以得到前一個模式。
程式碼:
mov eax, large fs:124h ; eax儲存了指向KTHREAD結構的指標 mov [ebp+pKthread], eax mov cl, [eax+_KTHREAD.PreviousMode] ; 執行緒的前一個模式 mov [ebp+PreviousMode], cl接著判斷建立標誌是否包含除DEBUG_PROCESS, DEBUG_ONLY_THIS_PROCESS,CREATE_SUSPENDED的標誌之外標誌, 如果包含其他的標誌,也報一個無效引數錯誤後退出該函式。
2. 通過引數ParentProcess呼叫ObReferenceObjectByHandle()函式得到父程序物件的指標)
程式碼:
push esi ; HandleInformation lea eax, [ebp+pParentObject] ; eax 為EPROCESS區域性變數的地址(作傳出引數) push eax ; Object push dword ptr [ebp+PreviousMode] ; AccessMode push _PsProcessType ; ObjectType push 80h ; DesiredAccess push [ebp+ParentProcessHandle] ; Handle call [email protected] ; 通過控制代碼得到父程序物件指標接著判斷了引數 JobMemberLevel是否為0, 如果為0,判斷父程序的EPROCESS->Job是否為0,如果為0,返回無效引數錯誤後推出該函式。如果不為0,和JobMemberLevel不為同一流程,將父程序物件中的屬性儲存到區域性變數中。
3. 通過ObCreateObject函式建立新程序物件並將物件內容初始化為0.
程式碼:
lea eax, [ebp+pNewProcessObj] push eax ; 新物件指標 push esi ; int push esi ; 物件的大小 push 260h ; paseContext push esi ; int push dword ptr [ebp+PreviousMode] ; int push [ebp+ObjectAttributes] ; 物件屬性 push _PsProcessType ; 物件型別 push dword ptr [ebp+PreviousMode] ; 處理器模式 call [email protected] ; 建立程序物件 mov edi, eax cmp edi, esi jl loc_4AAB6B ; 判斷ObCreateObject函式呼叫是否成功 mov ecx, 98h xor eax, eax mov ebx, [ebp+pNewProcessObj] mov edi, ebx rep stosd ; 將新建物件初始化為0從父程序繼承配額資訊(PspInheritQuot函式完成)和裝置點陣圖資訊(ObInheritDeviceMap函式完成),然後將父程序物件中的部分域給新程序。
4. 通過引數SectionHandle呼叫ObReferenceObjectByHandle函式得到區物件指標,當然前提是SectionHandle有效,然後將區物件指標賦值給新程序EPROCESS的相應域中,接著就判斷引數DebugPort是否為0, 當不為0時:
程式碼:
cmp [ebp+SectionHandle], esi jz loc_4F449F ; 判斷區物件控制代碼是否為0 push esi ; HandleInformation eax, [ebp+pSectionObj] push eax ; 記憶體區物件指標 push dword ptr [ebp+PreviousMode] ; 處理器模式 push _MmSectionObjectType ; 物件型別 push 8 ; 訪問掩碼 push [ebp+SectionHandle] ; 記憶體去控制代碼 call [email protected] ; 通過區物件控制代碼得到區物件指標 mov eax, [ebp+HandleInformation] mov [ebx+_EPROCESS.SectionObject], eax ; pNewProcessObject->SectionObject = 記憶體區物件指標 cmp [ebp+DebugPort], esi jnz IsDebugger ; 判斷引數DebugPort是否為0 IsDebugger:(DebugPort不為0時) push esi ; HandleInformation lea eax, [ebp+pDebugObj] push eax ; 新除錯物件指標 push dword ptr [ebp+PreviousMode] ; 處理器模式 push _DbgkDebugObjectType ; 物件型別 push 2 ; 訪問模式 push [ebp+DebugPort] ; 除錯埠控制代碼 call [email protected] ; 通過除錯物件控制代碼得到除錯物件指標當DebugPort為0時,呼叫DbgkCopyProcessDebugPort函式從父程序拷貝DebugPort給新程序。
以上部分就是通過引數來得到所需要的物件指標。
5. 接著呼叫PspInitializeProcessSecurity函式來設定新程序的安全屬性, 主要是設定新程序的安全令牌物件(從父程序拷貝):
程式碼:
push ebx ; 新程序物件 push [ebp+ParentObj] ; 父程序物件 call [email protected] ; 初始程序的安全屬性(設定新程序物件的令牌物件)其中PspInitializeProcessSecurity函式裡面呼叫了SeSubProcessToken函式來設定新程序物件的令牌物件,這個函式在XP sp3 下和win2k及WRK中不一樣,函式原型為SeSubProcessToken(TOKEN ParentToken, TOKEN NewToken, BOOL MarkAsActive),MarkAsActive引數表示TOKEN是否為活動的,SeSubProcessToken函式比WRK中的少一個引數,比win2k的多一個引數。
程式碼:
push 1 ; TOKEN是否為活動 lea eax, [ebp+8] push eax ; eax指向父程序物件的指標 push edi ; 父程序物件中的令牌物件 call [email protected] ; 將父程序的令牌物件賦值給新程序物件6. 接著呼叫MmCreateProcessAddressSpace為新程序建立地址空間,並構建頁目錄表,頁表 及物理頁的關係。
程式碼:
lea eax, [ebp+pDirTableBase] push eax ; 頁目錄表基地指標 push ebx ; 新程序物件 push [ebp+MinimumWorkingSet] ; 最小工作集 call [email protected] ; 建立程序地址空間(並構建頁目錄表,頁表 及物理頁的關係)之後呼叫KeInitializeProcess函式初始化新程序物件中核心物件,優先順序,親和性,頁目錄表實體地址幀號。
程式碼:
push eax lea eax, [ebp+pDirTableBase] push eax ; 頁目錄表基地址 push [ebp+Affinity] ; 親和性 push 8 ; 程序基本優先順序 push ebx ; 新程序物件 call [email protected] ;接著呼叫ObInitProcess函式來初始化新程序物件的表
程式碼:
push ebx ; 新程序物件 mov eax, [ebp+CreateFlag] and al, 4 neg al sbb eax, eax and eax, [ebp+ParentObj] push eax ; 父程序物件 call [email protected] ; 初始化新程序物件的物件表(如果父程序被指定,父程序的物件表拷貝到新程序中, 物件表 的每個物件中HandleCount域都+1)7. 呼叫MmInitializeProcessAddressSpace函式初始化程序地址空間,這個函式WRK和XP sp3的不一樣,XP sp3的只有4個引數,WRK中有5個引數, 去掉了CreateFlags引數,
該函式的實現中呼叫了KiAttachProcess函式來實現程序的切換(將當前執行緒掛靠到新程序中),以及初始化EPROCESS中的部分域和PFN,工作集列表等。
程式碼:
lea eax, [ebx+_EPROCESS.SeAuditProcessCreationInfo] push eax ; 物件名稱資訊 push [ebp+HandleInformation] ; 對映節資訊 push esi ; 克隆程序 push ebx ; 需要被初始化的新程序物件 call [email protected] ; 這個函式WRK和XP的不一樣,XP的只有4個引數,WRK中有5個引數8. 呼叫PspMapSystemDll函式對映指定程序的區物件(對映第一個DLL):
程式碼:
push esi ; 需要對映DLL的基地址 push ebx ; 新程序物件 call [email protected] ; 對映新程序物件的系統DLL(NTDLL.DLL)該函式的實現中呼叫了MmMapViewOfSection對映節區(呼叫MiMapViewOfImageSection函式將DLL作為映象對映)
程式碼:
lea eax, [ebp+pMapAddress] push eax ; 對映到那個地址 push [ebp+pNewObj] ; 程序物件(對映到那個程序) push ebx ; 系統DLL對映節的地址 call [email protected] ; 對映節區通過MmGetSessionId函式獲得指定程序的會話ID,呼叫SeSetSessionIdToken函式設定令牌的會話ID,接著呼叫ExCreateHandle函式在PspCidTable中新增一項。
程式碼:
mov edi, [ebx+_EPROCESS.Token] ; 取得pNewProcessObject->Token and edi, 0FFFFFFF8h ; 取消Token中的低3位 push ebx ; 新程序物件 call [email protected] ; 返回指定程序的會話ID push eax ; 新程序物件的會話ID push edi ; 新程序令牌物件 call [email protected] ; 設定指定Token->SessionId mov [ebp+var_70], ebx mov [ebp+var_6C], esi lea eax, [ebp+var_70] push eax ; eax = pNewProcessObject push _PspCidTable ; 該表儲存了所有程序和執行緒物件的指標 call [email protected] ; 在PspCidTable中新增一項9. 呼叫MmCreatePeb為新程序建立PEB(後面是大概是實現過程),首先通過呼叫KeAttachProcess函式將當前執行緒切換到新程序物件,然後NLS節區對映到新程序的地址空間中(MmMapViewOfSection),
接著呼叫MiCreatePebOrTeb建立PEB/TEB,(該函式通過ExAllocatePoolWithTag來申請_MMVAD結構大小的空間,通過MiFindEmptyAddressRangeDownTree函式在VAD樹中查詢一塊未被使用的地址空間範圍,並返回該範圍的起始地址, 最後通過MiInsertVad函式將申請的地址空間插入到VAD樹中。), 在建立PEB結構後,初始化PEB中部分域的值(映象基地址,作業系統編譯號等域),最後呼叫KeDetachProcess函式使執行緒回到原來的執行緒中。到此建立PEB完成。
程式碼:
lea eax, [ebx+_EPROCESS.Peb] push eax ; 指向PEB結構的指標(傳出引數) lea eax, [ebp+pInitPeb] push eax ; 初始PEB push ebx ; 新程序物件 call [email protected]; 建立PEB(將執行緒切換到目標程序後,建立PEB結構及初始化部分域, mov esi, [ebp+pNewObj] push esi ; 新程序物件(附加到那個程序) call [email protected] ; KeAttachProcess(x) lea eax, [ebp+pPeb] push eax ; 指向PEB 物件的地址(傳出引數) push 210h ; 建立PEB/TEB的大小 push esi ; 程序物件結構EPROCESS的指標 call [email protected] ; 建立 lea eax, [ebp+Size] push eax ; 資料目錄大小 push 0Ah ; 目錄表項索引 push 1 ; 如果不為0, 作為映象對映 mov eax, [ebp+pPeb] push dword ptr [eax+8] ; ImageBase(映象檔案或者資料檔案的基地址) call [email protected] ; 將一個映象檔案中IMAGE_DIRECTORY_LOAD_CONFIG節, 在該檔案的那個節區,返回該節區物件指標。9. 將新程序物件EPROCESS.ActiveProcessLinks更新為全域性的活動程序連結串列(PsActiveProcessHead), 判斷父程序是否為系統程序,呼叫SeCreateAccessStateEx設定訪問狀態,呼叫ObInsertObject函式將程序物件加入到程序物件的控制代碼表中,(省了幾個函式介紹)KeQuerySystemTime()結束PspCreateProcess的呼叫。
程式碼:
mov ecx, _PsProcessType add ecx, 68h push ecx ; GenericMapping push [ebp+AccessMask] ; 訪問掩碼 lea ecx, [ebp+var_B8] push ecx ; 訪問輔助結構指標PAUX_ACCESS_DATA lea ecx, [ebp+var_12C] push ecx ; 訪問狀態(傳出引數) push eax ; 程序物件 push esi ; 執行緒物件 call [email protected] lea eax, [ebp+pHandle] push eax ; 接受返回新物件的控制代碼 push esi ; 接受返回新物件的的指標 push 1 ; 物件指標計數 push [ebp+AccessMask] ; 訪問掩碼 lea eax, [ebp+AccessState] push eax ; 訪問狀態 push ebx ; 新程序物件 call [email