1. 程式人生 > >程序建立過程分析NtCreateProcess-NtCreateProcessEx-PspCreateProcess

程序建立過程分析NtCreateProcess-NtCreateProcessEx-PspCreateProcess

轉載自:http://www.blogfshare.com/createprocess-analyse.html

在核心中,windows建立一個程序的過程是從NtCreateProcess函式開始的。找到這個函式,發現它只是簡單地對引數稍作處理,然後把建立程序的任務交給NtCreateProcessEx函式。

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);
}

所以我們來看NtCreateProcessEx函式的流程。它也只是簡單地檢查ProcessHandle引數代表的控制代碼是否可寫,ParentProcess是否不為空,這是個必需的引數,不能為NULL,並且呼叫者必須對該程序具有PROCESS_CREATE_PROCESS訪問許可權。真正的建立工作交給PspCreateProcess函式。

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
    )

{
    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;
}

下面來看看PspCreateProcess 的執行流程。

1.   if (ARGUMENT_PRESENT (ParentProcess)) {

       //如果ParentProcess不為空,則通過ObReferenceObjectByHandle 獲得父程序物件的EPROCESS指標,放在區域性變數Parent。
       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設定。

       Affinity = Parent->Pcb.Affinity;

       新程序的工作集最大/最小值被初始化為全域性變數。
       WorkingSetMinimum = PsMinimumWorkingSet;
       WorkingSetMaximum = PsMaximumWorkingSet;

   } else {

       Parent = NULL;

       //Affinity設定為全域性變數KeActiveProcessors,系統中當前的可用處理器。
       Affinity = KeActiveProcessors;
       WorkingSetMinimum = PsMinimumWorkingSet;
       WorkingSetMaximum = PsMaximumWorkingSet;
   }

2.呼叫ObCreateObject 建立一個型別為PsProcessType的核心物件,置於區域性變數Process中,物件體為EPROCESS

Status = ObCreateObject (PreviousMode,
                            PsProcessType,
                            ObjectAttributes,
                            PreviousMode,
                            NULL,
                            sizeof (EPROCESS),
                            0,
                            0,
                            &Process);

   if (!NT_SUCCESS (Status)) {
       goto exit_and_deref_parent;
   }

3.把Process置0,並初始化部分成員。

RtlZeroMemory (Process, sizeof(EPROCESS));

//初始化程序的引用計數
    ExInitializeRundownProtection (&Process->RundownProtect);

//初始化程序鎖
    PspInitializeProcessLock (Process);

//初始化程序的執行緒連結串列
    InitializeListHead (&Process->ThreadListHead);

#if defined(_WIN64)

    if (Flags & PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE) {
        PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE);
    }

#endif

//繼承資源配額

    PspInheritQuota (Process, Parent);

//繼承父程序的裝置點陣圖
    ObInheritDeviceMap (Process, Parent);
    if (Parent != NULL) {

//繼承父程序
        Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;

//儲存父程序PID
        Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;

    } else {
        Process->DefaultHardErrorProcessing = PROCESS_HARDERROR_DEFAULT;
        Process->InheritedFromUniqueProcessId = NULL;
    }

4.判斷SectionHandle是否為空

if (ARGUMENT_PRESENT (SectionHandle)) {

   //不為空,ObReferenceObjectByHandle 獲取記憶體區物件的指標
    Status = ObReferenceObjectByHandle (SectionHandle,
                                        SECTION_MAP_EXECUTE,
                                        MmSectionObjectType,
                                        PreviousMode,
                                        &SectionObject,
                                        NULL);
    if (!NT_SUCCESS (Status)) {
        goto exit_and_deref;
    }

} else {
    SectionObject = NULL;

//父程序不為系統程序
    if (Parent != PsInitialSystemProcess) {

//acquire run-down protection on a shared object so the caller can safely access the object

        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;
        }
    }
}

Process->SectionObject = SectionObject;

新程序物件的記憶體區物件已經完成初始化。

5.初始化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);
        }
    }

6.初始化ExceptionPort引數。

if (ARGUMENT_PRESENT (ExceptionPort)) {
        Status = ObReferenceObjectByHandle (ExceptionPort,
                                            0,
                                            LpcPortObjectType,
                                            PreviousMode,
                                            &ExceptionPortObject,
                                            NULL);

        if (!NT_SUCCESS (Status)) {
            goto exit_and_deref;
        }

        Process->ExceptionPort = ExceptionPortObject;
    }

    Process->ExitStatus = STATUS_PENDING;

7.建立地址空間。

if (Parent != NULL) {
    //建立一個全新的地址空間

    if (!MmCreateProcessAddressSpace (WorkingSetMinimum,
                                      Process,
                                      &DirectoryTableBase[0])) {

        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto exit_and_deref;
    }

} 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;

8.初始化新程序物件的基本優先順序、Affinity、程序頁表目錄和超空間的頁幀號。

KeInitializeProcess (&Process->Pcb,
                        NORMAL_BASE_PRIORITY,
                        Affinity,
                        &DirectoryTableBase[0],
                        (BOOLEAN)(Process->DefaultHardErrorProcessing & PROCESS_HARDERROR_ALIGNMENT_BIT));

9.初始化新程序的安全屬性。

Status = PspInitializeProcessSecurity (Parent, Process);
   if (!NT_SUCCESS (Status)) {
       goto exit_and_deref;
   }

10.設定新程序優先順序。

Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
   if (Parent != NULL) {
       if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
           Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL) {

//拷貝父程序的優先順序
           Process->PriorityClass = Parent->PriorityClass;
       }
       //如果引數中包含了控制代碼繼承標誌,則把父程序控制代碼表中凡是具有繼承屬性的物件拷貝到新程序控制代碼表中。

       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;

11.初始化新程序的地址空間

if (SectionHandle != NULL) {

//新程序有新的可執行映像記憶體區物件,根據指定的記憶體區物件來初始化程序地址空間。

       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) {
       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);
         

           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 {

          //系統程序,不指定記憶體區和父程序,直接初始化,同樣拷貝父程序的映像名稱到新程序物件的資料結構中。

           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’);

           if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
               RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
                              sizeof(OBJECT_NAME_INFORMATION));
           } else {
               Status = STATUS_INSUFFICIENT_RESOURCES;
               goto exit_and_deref;
           }
       }
   }

還有第四種可能性,即沒有指定映像記憶體區物件,也沒有指定父程序。這對應於系統的引導程序(後蛻化成空閒程序),它的地址空間是在MmInitSystem執行過程中初始化的,由MiInitMachineDependent函式呼叫MmInitializeProcessAddressSpace來完成。

12.建立程序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);

13.審計建立行為

if (SeDetailedAuditingWithToken (NULL)) {
       SeAuditProcessCreation (Process);
   }

14.如果父程序屬於一個作業物件,則也加入到父程序所在的作業中。

if (Parent) {
        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;
            }
        }
    }

15.對於通過映像記憶體區來建立程序的情形,建立一個PEB,對於程序拷貝的情況,則使用繼承的PEB。

if (Parent && CreatePeb) {

        RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant));

        InitialPeb.Mutant = (HANDLE)(-1);
        InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages;
            
        if (SectionHandle != NULL) {
            Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb);
            if (!NT_SUCCESS (Status)) {
                Process->Peb = NULL;
                goto exit_and_deref;
            }

            Peb =  Process->Peb;

        } else {
            SIZE_T BytesCopied;

            InitialPeb.InheritedAddressSpace = TRUE;
            Process->Peb = Parent->Peb;
            MmCopyVirtualMemory (CurrentProcess,
                                 &InitialPeb,
                                 Process,
                                 Process->Peb,
                                 sizeof (INITIAL_PEB),
                                 KernelMode,
                                 &BytesCopied);

#if defined(_WIN64)
            if (Process->Wow64Process != NULL) {
                
                RtlZeroMemory (&InitialPeb32, FIELD_OFFSET(INITIAL_PEB32, Mutant));
                InitialPeb32.Mutant = -1;
                InitialPeb32.InheritedAddressSpace = TRUE;
                InitialPeb32.ImageUsesLargePages = (BOOLEAN) UseLargePages;

                MmCopyVirtualMemory (CurrentProcess,
                                     &InitialPeb32,
                                     Process,
                                     Process->Wow64Process->Wow64,
                                     sizeof (INITIAL_PEB32),
                                     KernelMode,
                                     &BytesCopied);
            }
#endif

        }
    }

    Peb = Process->Peb;

16.把新程序加入全域性的程序連結串列中。PsActiveProcessHead

PspLockProcessList (CurrentThread);
   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;
       }
   }

17.把新程序物件插入到當前程序的控制代碼表中。

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;
    }

18.計算新程序的優先順序和時限重置值,設定記憶體優先順序。

ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE);

   BasePriority = PspComputeQuantumAndPriority(Process,
                                               PsProcessPriorityBackground,
                                               &QuantumReset);

   Process->Pcb.BasePriority = (SCHAR)BasePriority;
   Process->Pcb.QuantumReset = QuantumReset;

19。設定程序的訪問許可權,GrantedAccess。由於新程序已經被加入到控制代碼表中,所以它現在能夠被終止了(PROCESS_TERMINATE)。有父程序,但不是PsInitialialSystemProcess,首先執行訪問檢查,然後計算程序的訪問許可權。如果是PsInitialialSystemProcess的子程序,則授予所有的訪問許可權。

Process->GrantedAccess = PROCESS_TERMINATE;
   if (Parent && Parent != PsInitialSystemProcess) {
       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 |