Windows进程与线程---1

概述

相比于Linux中的进程管理,即创建一个线程的同时创建一个进程。而Windows中却不是这样,它是先创建一个进程作为容器,然后创建第一个线程,也就是对于CreateProcess而言,它先调用NtCreateProcess创建进程,然后调用NtCreateThread创建进程的第一个线程。

Windows进程的用户空间

一些重要的宏如下

#define MM_HIGHEST_USER_ADDRESS	*MmHighestUserAddress
#define MM_USER_PROBE_ADDRESS	*MmUserProbeAddress
#define MM_LOWEST_USER_ADDRESS 	0x10000

对于MmHighestUserAddress和MmUserProbeAddress而言,他们是在系统初始化的时候被赋值的,即MmUserProbeAddress其值为MmSystemRangeStart-0x10000,而MmHighestUserAddress为MmUserProbeAddress-1,也就是严格来讲,从0x8000-0000开始处的下沿64kb也是属于用户不可访问的范围,并且之前我们在系统调用中提到过有一个SharedUserData是一块“飞地”的存在,它虽然在系统内核空间中,但却是可以访问到的。其大小也是64kb,位置是在0xffdf0000
Windows进程与线程---1_第1张图片

用户空间格局的创建

用户空间的创建及其大的格局基本上是通过内核函数MmCreateProcessAddressSpace实现
其流程大概是

  • 调用MmInitializeAddressSpace初始化MADDRESS_SPACE结构
  • 通过调用CreateMemoryArea将0x80000000下沿64kb设置成PAGE_NOACCESS属性
  • 通过调用CreateMemoryArea将SharedUserData所在页面设置为PAGE_EXECUTE_READ属性,所以实际上只有一个页面的大小的飞地,其余部分设置成PAGE_NOACCESS
  • 通过MmMapViewOfSection将该可执行映像装入用户空间,其起始地址正是ImageBase,接着初始化一些域 例如文件名 并且填充到EPROCESS

用户空间最重要的自然是可执行映像,在进行NtCreateProcess之前,调用者已经为目标映像创建好一个Section,即文件映射区,现在通过NtCreateProcess将它映射到用户空间中,由于此时空间基本为空,根据PE文件的头部信息,我们总能加载到期望地址上去。
当然除了重要的EXE映像,还要许多重要的DLL文件,例如ntdll.dll,这是一个非常重要的DLL,系统调用的时候也需要用到这个DLL。Windows内核在初始化阶段首次需要这个DLL的时候为其创建文件映射区,以后凡是用到这个DLL只要映射这个文件映射区即可。ntdll的映射是由PspCreateProcess->PspMapSystemDll完成的。

ntdll的加载
NTSTATUS
NTAPI
INIT_FUNCTION
PspMapSystemDll(IN PEPROCESS Process,
                IN PVOID *DllBase,
                IN BOOLEAN UseLargePages)
{
    NTSTATUS Status;
    LARGE_INTEGER Offset = {{0, 0}};
    SIZE_T ViewSize = 0;
    PVOID ImageBase = 0;
    
    /* Map the System DLL */
    Status = MmMapViewOfSection(PspSystemDllSection,    //调用MmMapViewOfSection将ntdll映射到ImageBase处 类型是可读写
                                Process,
                                (PVOID*)&ImageBase,
                                0,
                                0,
                                &Offset,
                                &ViewSize,
                                ViewShare,
                                0,
                                PAGE_READWRITE);
    if (Status != STATUS_SUCCESS)
    {
        /* Normalize status code */
        Status = STATUS_CONFLICTING_ADDRESSES;
    }
    
    /* Write the image base and return status */
    if (DllBase) *DllBase = ImageBase;  //DllBase是ntdll的加载基址
    return Status;
}

这里的ntdll加载进内存是通过MmMapViewOfSection将该共享文件对象映射进内存中,这里也就是用户空间里。

PEB的建立

PEB的建立是通过PspCreateProcess->MmCreatePeb

NTSTATUS
NTAPI
MmCreatePeb(IN PEPROCESS Process,
            IN PINITIAL_PEB InitialPeb,
            OUT PPEB *BasePeb)
{
    PPEB Peb = NULL;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize = 0;
    PVOID TableBase = NULL;
    PIMAGE_NT_HEADERS NtHeaders;
    PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
    NTSTATUS Status;
    USHORT Characteristics;
    KAFFINITY ProcessAffinityMask = 0;
    SectionOffset.QuadPart = (ULONGLONG)0;
    *BasePeb = NULL;

    //
    // Attach to Process
    //
    KeAttachProcess(&Process->Pcb);

    //
    // Map NLS Tables
    //
    Status = MmMapViewOfSection(ExpNlsSectionPointer,   //映射NLS码表
                                (PEPROCESS)Process,
                                &TableBase,
                                0,
                                0,
                                &SectionOffset,
                                &ViewSize,
                                ViewShare,
                                MEM_TOP_DOWN,
                                PAGE_READONLY);
    DPRINT("NLS Tables at: %p\n", TableBase);
    if (!NT_SUCCESS(Status))
    {
        /* Cleanup and exit */
        KeDetachProcess();
        return Status;
    }

    //
    // Allocate the PEB
    //
    Status = MiCreatePebOrTeb(Process, sizeof(PEB), (PULONG_PTR)&Peb);  //为Peb建立空间的主要函数
    DPRINT("PEB at: %p\n", Peb);
    if (!NT_SUCCESS(Status))
    {
        /* Cleanup and exit */
        KeDetachProcess();
        return Status;
    }

    //
    // Use SEH in case we can't load the PEB
    //
    _SEH2_TRY
    {
        //
        // Initialize the PEB
        //
        RtlZeroMemory(Peb, sizeof(PEB));

        //
        // Set up data
        //
        Peb->ImageBaseAddress = Process->SectionBaseAddress;        //EPROCESS的SectionBaseAddress赋值给了ImageBaseAddress
        Peb->InheritedAddressSpace = InitialPeb->InheritedAddressSpace; //对peb进行适当的初始化
        Peb->Mutant = InitialPeb->Mutant;
        Peb->ImageUsesLargePages = InitialPeb->ImageUsesLargePages;

        //
        // NLS
        //
        Peb->AnsiCodePageData = (PCHAR)TableBase + ExpAnsiCodePageDataOffset;
        Peb->OemCodePageData = (PCHAR)TableBase + ExpOemCodePageDataOffset;
        Peb->UnicodeCaseTableData = (PCHAR)TableBase + ExpUnicodeCaseTableDataOffset;

        //
        // Default Version Data (could get changed below)
        //
        Peb->OSMajorVersion = NtMajorVersion;   //这些是从SharedUserData中获取的 根据上面的注释 是只读无法更改的
        Peb->OSMinorVersion = NtMinorVersion;
        Peb->OSBuildNumber = (USHORT)(NtBuildNumber & 0x3FFF);
        Peb->OSPlatformId = 2; /* VER_PLATFORM_WIN32_NT */
        Peb->OSCSDVersion = (USHORT)CmNtCSDVersion;

        //
        // Heap and Debug Data
        //
        Peb->NumberOfProcessors = KeNumberProcessors;   //KeNumberProcessors即处理器的数量也给了Peb
        Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL); //这里给反调试做了依据!!! BeingDebugged是根据DebugPort而来的
        Peb->NtGlobalFlag = NtGlobalFlag;
        /*Peb->HeapSegmentReserve = MmHeapSegmentReserve;
         Peb->HeapSegmentCommit = MmHeapSegmentCommit;
         Peb->HeapDeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
         Peb->HeapDeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
         Peb->CriticalSectionTimeout = MmCriticalSectionTimeout;
         Peb->MinimumStackCommit = MmMinimumStackCommitInBytes;
         */
        Peb->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB)) / sizeof(PVOID);  //堆的最大数量
        Peb->ProcessHeaps = (PVOID*)(Peb + 1);  //可以看到Windows的堆指针是紧邻Peb之后的 并且是堆的一个指针数组

        //
        // Session ID
        //
        if (Process->Session) Peb->SessionId = 0; // MmGetSessionId(Process);
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        //
        // Fail
        //
        KeDetachProcess();
        _SEH2_YIELD(return _SEH2_GetExceptionCode());
    }
    _SEH2_END;

    //
    // Use SEH in case we can't load the image
    //
    _SEH2_TRY
    {
        //
        // Get NT Headers
        //
        NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress);    //获取Pe映像头
        Characteristics = NtHeaders->FileHeader.Characteristics;    //获取Pe映像信息
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        //
        // Fail
        //
        KeDetachProcess();
        _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT);
    }
    _SEH2_END;

    //
    // Parse the headers
    //
    if (NtHeaders)
    {
        //
        // Use SEH in case we can't load the headers
        //
        _SEH2_TRY
        {
            //
            // Get the Image Config Data too
            //
            ImageConfigData = RtlImageDirectoryEntryToData(Peb->ImageBaseAddress,
                                                           TRUE,
                                                           IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
                                                           (PULONG)&ViewSize);
            if (ImageConfigData)
            {
                //
                // Probe it
                //
                ProbeForRead(ImageConfigData,
                             sizeof(IMAGE_LOAD_CONFIG_DIRECTORY),
                             sizeof(ULONG));
            }

            //
            // Write subsystem data
            //
            Peb->ImageSubsystem = NtHeaders->OptionalHeader.Subsystem;
            Peb->ImageSubsystemMajorVersion = NtHeaders->OptionalHeader.MajorSubsystemVersion;
            Peb->ImageSubsystemMinorVersion = NtHeaders->OptionalHeader.MinorSubsystemVersion;

            //
            // Check for version data
            //
            if (NtHeaders->OptionalHeader.Win32VersionValue)
            {
                //
                // Extract values and write them
                //
                Peb->OSMajorVersion = NtHeaders->OptionalHeader.Win32VersionValue & 0xFF;
                Peb->OSMinorVersion = (NtHeaders->OptionalHeader.Win32VersionValue >> 8) & 0xFF;
                Peb->OSBuildNumber = (NtHeaders->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF;
                Peb->OSPlatformId = (NtHeaders->OptionalHeader.Win32VersionValue >> 30) ^ 2;

                /* Process CSD version override */
                if ((ImageConfigData) && (ImageConfigData->CSDVersion))
                {
                    /* Take the value from the image configuration directory */
                    Peb->OSCSDVersion = ImageConfigData->CSDVersion;
                }
            }

            /* Process optional process affinity mask override */
            if ((ImageConfigData) && (ImageConfigData->ProcessAffinityMask))
            {
                /* Take the value from the image configuration directory */
                ProcessAffinityMask = ImageConfigData->ProcessAffinityMask;
            }

            //
            // Check if this is a UP image
            if (Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY)
            {
                //
                // Force it to use CPU 0
                //
                /* FIXME: this should use the MmRotatingUniprocessorNumber */
                Peb->ImageProcessAffinityMask = 0;
            }
            else
            {
                //
                // Whatever was configured
                //
                Peb->ImageProcessAffinityMask = ProcessAffinityMask;
            }
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            //
            // Fail
            //
            KeDetachProcess();
            _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT);
        }
        _SEH2_END;
    }

    //
    // Detach from the Process
    //
    KeDetachProcess();  //撤销挂靠
    *BasePeb = Peb;     
    return STATUS_SUCCESS;
}

这里首先映射NLS码表,这个是关于编码的,我们并不关心,我们的重心放到MiCreatePebOrTeb,这个函数为Peb建立了空间。
接着分配好空间就是对Peb进行初始化,首先是ImageBase的从EPROCESS到Peb的一个转移。然后是比较重要的就是Peb的BeingDebugged的来源是EPROCESS中的DebugPort域,当被调试的时候,DebugPort的值为-1,此时!=null,所以BeingDbugged的值就是这么来的。另外比较重要的就是Heap,它最大数量就是(PAGE_SIZE - sizeof(PEB)) / sizeof(PVOID),并且堆数组的起始地址是ProcessHeaps,它的定义是**(PVOID*)(Peb + 1)**,也就是紧邻在Peb之后,是Windows堆的指针数组。

顺便看一下MiCreatePebOrTeb
Windows进程与线程---1_第2张图片其核心是一个循环,对于peb而言,它总能在第一次循环的时候就将其数据结构安装到期望地址上。对于之后的Teb而言,每次是第2,3,4…循环之后能成功安装,这样多线程的Teb建立问题就不需要思虑了,即使某个线程结束了其生命,下一次新的线程建立teb会填充到那个位置。并且对于进程而言不存在堆栈问题,堆栈是针对于线程而言!

ProcessParameters的生成

PEB中有个字段ProcessParameters,这个字段指向的是本进程的"参数块",注意,进程的建立也是存在参数的!!!
是一个RTL_USER_PROCESS_PARAMETERS结构。
其设置是由BasepInitializeEnvironment处理的

[CreateProcessW->CreateProcessInternalW->BasepInitializeEnvironment]
NTSTATUS
WINAPI
BasepInitializeEnvironment(HANDLE ProcessHandle,
                           PPEB Peb,
                           LPWSTR ApplicationPathName,
                           LPWSTR lpCurrentDirectory,
                           LPWSTR lpCommandLine,
                           PWSTR lpEnvironment,
                           SIZE_T EnvSize,
                           LPSTARTUPINFOW StartupInfo,
                           DWORD CreationFlags,
                           BOOL InheritHandles)
{
        WCHAR FullPath[MAX_PATH];
        LPWSTR Remaining;
        LPWSTR DllPathString;
        PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
        PRTL_USER_PROCESS_PARAMETERS RemoteParameters = NULL;
        UNICODE_STRING DllPath, ImageName, CommandLine, CurrentDirectory;
        UINT RetVal;
        NTSTATUS Status;
        PWCHAR ScanChar;
        ULONG EnviroSize;
        SIZE_T Size;
        UNICODE_STRING Desktop, Shell, Runtime, Title;
        PPEB OurPeb = NtCurrentPeb();
        PWSTR Environment = lpEnvironment;
 
        DPRINT("BasepInitializeEnvironment\n");
 
        /* Get the full path name */
        RetVal = GetFullPathNameW(ApplicationPathName,
                MAX_PATH,
                FullPath,
                &Remaining);
        DPRINT("ApplicationPathName: %S, FullPath: %S\n", ApplicationPathName,
                FullPath);
 
        /* Get the DLL Path */
        DllPathString = BasepGetDllPath(FullPath, Environment); //获取dll路径
 
        /* Initialize Strings */
        RtlInitUnicodeString(&DllPath, DllPathString);
        RtlInitUnicodeString(&ImageName, FullPath); //获取可执行映像路径
        RtlInitUnicodeString(&CommandLine, lpCommandLine);  //获取命令行参数
        RtlInitUnicodeString(&CurrentDirectory, lpCurrentDirectory);    //获取当前目录
 
        /* Initialize more Strings from the Startup Info */
        //这是对启动参数信息的复制 主要有视窗位置 标题等
        if (StartupInfo->lpDesktop)
        {
                RtlInitUnicodeString(&Desktop, StartupInfo->lpDesktop);
        }
        else
        {
                RtlInitUnicodeString(&Desktop, L"");
        }
        if (StartupInfo->lpReserved)
        {
                RtlInitUnicodeString(&Shell, StartupInfo->lpReserved);
        }
        else
        {
                RtlInitUnicodeString(&Shell, L"");
        }
        if (StartupInfo->lpTitle)
        {
                RtlInitUnicodeString(&Title, StartupInfo->lpTitle);
        }
        else
        {
                RtlInitUnicodeString(&Title, L"");
        }
 
        /* This one is special because the length can differ */
        Runtime.Buffer = (LPWSTR)StartupInfo->lpReserved2;
        Runtime.MaximumLength = Runtime.Length = StartupInfo->cbReserved2;
 
        /* Create the Parameter Block */
        DPRINT("Creating Process Parameters: %wZ %wZ %wZ %wZ %wZ %wZ %wZ\n",
                &ImageName, &DllPath, &CommandLine, &Desktop, &Title, &Shell,
                &Runtime);
        Status = RtlCreateProcessParameters(&ProcessParameters, &ImageName, &DllPath, lpCurrentDirectory ? &CurrentDirectory : NULL, &CommandLine, Environment, &Title, &Desktop, &Shell, &Runtime);//RtlCreateProcessParameters为ProcessParameters分配空间 并且将Environment进行填充
 
        if (!NT_SUCCESS(Status))
        {
                DPRINT1("Failed to create process parameters!\n");
                return Status;
        }
 
        /* Check if we got an environment. If not, use ours */
        //下面是获取环境变量块的信息
        if (Environment)
        {
                /* Save pointer and start lookup */
                Environment = ScanChar = ProcessParameters->Environment;
        }
        else
        {
                /* Save pointer and start lookup */
                Environment = ScanChar = OurPeb->ProcessParameters->Environment;
        }
 
        /* Find the environment size */
        if (ScanChar)
        {
                if (EnvSize && Environment == lpEnvironment)
                {
                        /* its a converted ansi environment, bypass the length calculation */
                        EnviroSize = EnvSize;
                }
                else
                {
                        while (*ScanChar)
                        {
                                ScanChar += wcslen(ScanChar) + 1;
                        }
 
                        /* Calculate the size of the block */
                        if (ScanChar == Environment)
                        {
                                EnviroSize = 2 * sizeof(WCHAR);
                        }
                        else
                        {
                                EnviroSize = (ULONG)((ULONG_PTR)ScanChar - (ULONG_PTR)Environment + sizeof(WCHAR));
                        }
                }
                DPRINT("EnvironmentSize %ld\n", EnviroSize);
 
                /* Allocate and Initialize new Environment Block */
                Size = EnviroSize;
                ProcessParameters->Environment = NULL;
                Status = NtAllocateVirtualMemory(ProcessHandle, //为参数块中的环境变量域分配一个环境变量块大小
                        (PVOID*)&ProcessParameters->Environment,
                        0,
                        &Size,
                        MEM_COMMIT,
                        PAGE_READWRITE);
                if (!NT_SUCCESS(Status))
                {
                        DPRINT1("Failed to allocate Environment Block\n");
                        return(Status);
                }
 
                /* Write the Environment Block */
                NtWriteVirtualMemory(ProcessHandle,             //将得到的环境变量块信息存入到参数块中
                        ProcessParameters->Environment,
                        Environment,
                        EnviroSize,
                        NULL);
        }
 
        /* Write new parameters */
        //继续为参数块设置一些未初始化的信息
        ProcessParameters->StartingX = StartupInfo->dwX;
        ProcessParameters->StartingY = StartupInfo->dwY;
        ProcessParameters->CountX = StartupInfo->dwXSize;
        ProcessParameters->CountY = StartupInfo->dwYSize;
        ProcessParameters->CountCharsX = StartupInfo->dwXCountChars;
        ProcessParameters->CountCharsY = StartupInfo->dwYCountChars;
        ProcessParameters->FillAttribute = StartupInfo->dwFillAttribute;
        ProcessParameters->WindowFlags = StartupInfo->dwFlags;
        ProcessParameters->ShowWindowFlags = StartupInfo->wShowWindow;
 
        /* Write the handles only if we have to */
        if (StartupInfo->dwFlags & STARTF_USESTDHANDLES)    //设置三个标准通道
        {
                DPRINT("Using Standard Handles\n");
                ProcessParameters->StandardInput = StartupInfo->hStdInput;
                ProcessParameters->StandardOutput = StartupInfo->hStdOutput;
                ProcessParameters->StandardError = StartupInfo->hStdError;
        }
 
        /* Use Special Flags for ConDllInitialize in Kernel32 */
        //设置创建标志值 例如无视窗 控制台应用程序
        if (CreationFlags & DETACHED_PROCESS)
        {
                ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS;
        }
        else if (CreationFlags & CREATE_NO_WINDOW)
        {
                ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
        }
        else if (CreationFlags & CREATE_NEW_CONSOLE)
        {
                ProcessParameters->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE;
        }
        else
        {
                /* Inherit our Console Handle */
                ProcessParameters->ConsoleHandle = OurPeb->ProcessParameters->ConsoleHandle;    //遗传父进程的句柄
 
                /* Is the shell trampling on our Handles? */
                if (!(StartupInfo->dwFlags &
                        (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
                {
                        /* Use handles from PEB, if inheriting or they are console */
                        DPRINT("Copying handles from parent\n");
                        BasepCopyHandles(ProcessParameters, OurPeb->ProcessParameters, InheritHandles);//将三个标准通道的句柄复制到参数块中
                }
        }
 
        /* Also set the Console Flag */
        if (CreationFlags & CREATE_NEW_PROCESS_GROUP)
        {
                ProcessParameters->ConsoleFlags = 1;
        }
 
        /* Allocate memory for the parameter block */
        Size = ProcessParameters->Length;
        Status = NtAllocateVirtualMemory(ProcessHandle,//在新创建的用户空间中分配参数所需要的空间
                (PVOID*)&RemoteParameters,
                0,
                &Size,
                MEM_COMMIT,
                PAGE_READWRITE);
        if (!NT_SUCCESS(Status))
        {
                DPRINT1("Failed to allocate Parameters Block\n");
                return(Status);
        }
 
        /* Set the allocated size */
        ProcessParameters->MaximumLength = Size;
 
        /* Handle some Parameter Flags */
        ProcessParameters->ConsoleFlags = (CreationFlags & CREATE_NEW_PROCESS_GROUP);
        ProcessParameters->Flags |= (CreationFlags & PROFILE_USER) ?
RTL_USER_PROCESS_PARAMETERS_PROFILE_USER : 0;
        ProcessParameters->Flags |= (CreationFlags & PROFILE_KERNEL) ?
RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL : 0;
        ProcessParameters->Flags |= (CreationFlags & PROFILE_SERVER) ?
RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER : 0;
        ProcessParameters->Flags |= (NtCurrentPeb()->ProcessParameters->Flags &
                RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS);
 
        /* Write the Parameter Block */
        Status = NtWriteVirtualMemory(ProcessHandle,    //将填充好的参数块填充到新进程中
                RemoteParameters,
                ProcessParameters,
                ProcessParameters->Length,
                NULL);
 
        /* Write the PEB Pointer */
        Status = NtWriteVirtualMemory(ProcessHandle,    //参数块指针填充到peb中
                &Peb->ProcessParameters,
                &RemoteParameters,
                sizeof(PVOID),
                NULL);
 
        /* Cleanup */
        RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath.Buffer);
        RtlDestroyProcessParameters(ProcessParameters);
 
        DPRINT("Completed\n");
        return STATUS_SUCCESS;
}
        

NTSTATUS NTAPI
RtlCreateProcessParameters(PRTL_USER_PROCESS_PARAMETERS *ProcessParameters,
			   PUNICODE_STRING ImagePathName,
			   PUNICODE_STRING DllPath,
			   PUNICODE_STRING CurrentDirectory,
			   PUNICODE_STRING CommandLine,
			   PWSTR Environment,
			   PUNICODE_STRING WindowTitle,
			   PUNICODE_STRING DesktopInfo,
			   PUNICODE_STRING ShellInfo,
			   PUNICODE_STRING RuntimeData)
{
   PRTL_USER_PROCESS_PARAMETERS Param = NULL;
   ULONG Length = 0;
   PWCHAR Dest;
   UNICODE_STRING EmptyString;
   HANDLE CurrentDirectoryHandle;
   HANDLE ConsoleHandle;
   ULONG ConsoleFlags;

   DPRINT ("RtlCreateProcessParameters\n");

   RtlAcquirePebLock();

   EmptyString.Length = 0;
   EmptyString.MaximumLength = sizeof(WCHAR);
   EmptyString.Buffer = L"";

   if (RtlpGetMode() == UserMode)   //对于UserMode而言 从当前的进程的peb中获取相关信息
     {
	if (DllPath == NULL)
	  DllPath = &NtCurrentPeb()->ProcessParameters->DllPath;
	if (Environment == NULL)
	  Environment  = NtCurrentPeb()->ProcessParameters->Environment;
	if (CurrentDirectory == NULL)
	  CurrentDirectory = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
	CurrentDirectoryHandle = NtCurrentPeb()->ProcessParameters->CurrentDirectory.Handle;
	ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
	ConsoleFlags = NtCurrentPeb()->ProcessParameters->ConsoleFlags;
     }
   else //若处于内核空间 则不存在进程这一说法 全部赋值为空
     {
	if (DllPath == NULL)
	  DllPath = &EmptyString;
	if (CurrentDirectory == NULL)
	  CurrentDirectory = &EmptyString;
	CurrentDirectoryHandle = NULL;
	ConsoleHandle = NULL;
	ConsoleFlags = 0;
     }

   if (CommandLine == NULL)
     CommandLine = &EmptyString;
   if (WindowTitle == NULL)
     WindowTitle = &EmptyString;
   if (DesktopInfo == NULL)
     DesktopInfo = &EmptyString;
   if (ShellInfo == NULL)
     ShellInfo = &EmptyString;
   if (RuntimeData == NULL)
     RuntimeData = &EmptyString;

   /* size of process parameter block */    //计算要分配的长度
   Length = sizeof(RTL_USER_PROCESS_PARAMETERS);

   /* size of current directory buffer */
   Length += (MAX_PATH * sizeof(WCHAR));

   /* add string lengths */
   Length += ALIGN(DllPath->MaximumLength, sizeof(ULONG));
   Length += ALIGN(ImagePathName->Length + sizeof(WCHAR), sizeof(ULONG));
   Length += ALIGN(CommandLine->Length + sizeof(WCHAR), sizeof(ULONG));
   Length += ALIGN(WindowTitle->MaximumLength, sizeof(ULONG));
   Length += ALIGN(DesktopInfo->MaximumLength, sizeof(ULONG));
   Length += ALIGN(ShellInfo->MaximumLength, sizeof(ULONG));
   Length += ALIGN(RuntimeData->MaximumLength, sizeof(ULONG));

   /* Calculate the required block size */
   Param = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Length);//分配一个Length空间除了本身的RTL_USER_PROCESS_PARAMETERS空间 还外加了
   if (!Param)
     {
	RtlReleasePebLock();
	return STATUS_INSUFFICIENT_RESOURCES;
     }

   DPRINT ("Process parameters allocated\n");

   //初始化一些必要的参数
   Param->MaximumLength = Length;
   Param->Length = Length;
   Param->Flags = RTL_USER_PROCESS_PARAMETERS_NORMALIZED;
   Param->Environment = Environment;
   Param->CurrentDirectory.Handle = CurrentDirectoryHandle;
   Param->ConsoleHandle = ConsoleHandle;
   Param->ConsoleFlags = ConsoleFlags;

   Dest = (PWCHAR)(((PBYTE)Param) + sizeof(RTL_USER_PROCESS_PARAMETERS));

   /* copy current directory */
   RtlpCopyParameterString(&Dest,
			   &Param->CurrentDirectory.DosPath,
			   CurrentDirectory,
			   MAX_PATH * sizeof(WCHAR));

   /* make sure the current directory has a trailing backslash */
   if (Param->CurrentDirectory.DosPath.Length > 0)
     {
	ULONG Length;

	Length = Param->CurrentDirectory.DosPath.Length / sizeof(WCHAR);
	if (Param->CurrentDirectory.DosPath.Buffer[Length-1] != L'\\')
	  {
	     Param->CurrentDirectory.DosPath.Buffer[Length] = L'\\';
	     Param->CurrentDirectory.DosPath.Buffer[Length + 1] = 0;
	     Param->CurrentDirectory.DosPath.Length += sizeof(WCHAR);
	  }
     }

   /* copy dll path */
   RtlpCopyParameterString(&Dest,
			   &Param->DllPath,
			   DllPath,
			   0);

   /* copy image path name */
   RtlpCopyParameterString(&Dest,
			   &Param->ImagePathName,
			   ImagePathName,
			   ImagePathName->Length + sizeof(WCHAR));

   /* copy command line */
   RtlpCopyParameterString(&Dest,
			   &Param->CommandLine,
			   CommandLine,
			   CommandLine->Length + sizeof(WCHAR));

   /* copy title */
   RtlpCopyParameterString(&Dest,
			   &Param->WindowTitle,
			   WindowTitle,
			   0);

   /* copy desktop */
   RtlpCopyParameterString(&Dest,
			   &Param->DesktopInfo,
			   DesktopInfo,
			   0);

   /* copy shell info */
   RtlpCopyParameterString(&Dest,
			   &Param->ShellInfo,
			   ShellInfo,
			   0);

   /* copy runtime info */
   RtlpCopyParameterString(&Dest,
			   &Param->RuntimeData,
			   RuntimeData,
			   0);

   RtlDeNormalizeProcessParams(Param);
   *ProcessParameters = Param;
   RtlReleasePebLock();

   return STATUS_SUCCESS;
}



上述是初始化参数块的过程。流程是:

  • 先是在本进程初始化一些参数块的必要参数
  • 接着调用RtlCreateProcessParameters在 本进程中 为参数块分配一个空间 并将初始化参数放入ProcessParameters中
  • 然后对于另一部分带有附加参数的环境块,函数也会将Environment分配空间然后存放到ProcessParameters中去。
  • 值得注意的是,我们刚才所说的是操作是在当前进程中进行的,我们最终还是要落实到新进程中,所以需要在新进程中开辟一块空间来存放参数块。

由于分配时是从低往高扫描的,而用户空间最低地址是0x10000,所以分配的位置总是在0x10000处,所以从0x10000处开始的第一个64KB是环境变量块,即Environment。第二个64KB是参数块。

线程建立

下一步就是创建新进程的第一个线程了。首先总结回顾下进程建立的主要点

  • 划分用地 即0x0—0x10000, 0x7fff-f000—0x8000-0000,及除了内核空间SharedUserData中的一个页面外其他都是NOACCESS的区域,是不允许用户空间访问这些虚拟地址的。
  • Section映射进来,这里也就是最重要的可执行文件映像,由于系统将其作为文件共享对象映射进用户空间,这一步也就是对用户空间的使用
  • 然后导入进必要的DLL,我们之前以"系统DLL"举例过了,ntdll.dll也是作为Section对象映射到用户空间的高地址处。
  • 接着是进程必要的参数,如Peb的建立和它本身的初始化,以及ProcessParameters的建立,由于ProcessParameters的特殊和庞大,所以单独用了一个函数来进行初始化,至此新进程对象框架就建立好了

第一个线程的建立,设计到堆栈TEB的建立,先看下函数BasepCreateFirstThread,由创建者进程在其用户空间执行

CreateProcessW -> CreateProcessInternalW -> BasepInitializeFirstThead
/*
 * Creates the first Thread in a Proces
 */
HANDLE
WINAPI
BasepCreateFirstThread(HANDLE ProcessHandle,    //这里是新进程的进程句柄
                       LPSECURITY_ATTRIBUTES lpThreadAttributes,
                       PSECTION_IMAGE_INFORMATION SectionImageInfo,
                       PCLIENT_ID ClientId)
{
    OBJECT_ATTRIBUTES LocalObjectAttributes;
    POBJECT_ATTRIBUTES ObjectAttributes;
    CONTEXT Context;
    INITIAL_TEB InitialTeb;
    NTSTATUS Status;
    HANDLE hThread;

    DPRINT("BasepCreateFirstThread. hProcess: %lx\n", ProcessHandle);

    /* Create the Thread's Stack */
    BaseCreateStack(ProcessHandle,      //在新创建的进程空间里创建一个堆栈
                     SectionImageInfo->MaximumStackSize,
                     SectionImageInfo->CommittedStackSize,
                     &InitialTeb);

    /* Create the Thread's Context */
    BaseInitializeContext(&Context,     //创建线程的初始化上下文
                           NtCurrentPeb(),
                           SectionImageInfo->TransferAddress,
                           InitialTeb.StackBase,
                           0);

    /* Convert the thread attributes */
    ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
                                                    lpThreadAttributes,
                                                    NULL);

    /* Create the Kernel Thread Object */
    Status = NtCreateThread(&hThread,   //在准备好堆栈和线程上下文后调用NtCreateThread创建第一个线程
                            THREAD_ALL_ACCESS,
                            ObjectAttributes,
                            ProcessHandle,
                            ClientId,
                            &Context,
                            &InitialTeb,
                            TRUE);
    if (!NT_SUCCESS(Status))
    {
        return NULL;
    }

    Status = BasepNotifyCsrOfThread(hThread, ClientId);
    if (!NT_SUCCESS(Status))
    {
        ASSERT(FALSE);
    }

    /* Success */
    return hThread;
}

这里主要做了三步

  • BasepCreateStack 为新线程准备一个堆栈 之所以准备是因为需要线程也需要压栈参数,存取变量等等。
  • BasepInitializeContext 初始化其上下文
  • NtCreateThread 这里才是真正的创建线程
  • 最后返回创建的线程对象的句柄给调用者

BasepCreateStack

/*
 * Creates a stack for a thread or fiber
 */
NTSTATUS
WINAPI
BasepCreateStack(HANDLE hProcess,
                 SIZE_T StackReserve,
                 SIZE_T StackCommit,
                 PINITIAL_TEB InitialTeb)
{
    NTSTATUS Status;
    SYSTEM_BASIC_INFORMATION SystemBasicInfo;
    PIMAGE_NT_HEADERS Headers;
    ULONG_PTR Stack = 0;
    BOOLEAN UseGuard = FALSE;
    
    DPRINT("BasepCreateStack (hProcess: %lx, Max: %lx, Current: %lx)\n",
            hProcess, StackReserve, StackCommit);
    
    /* Get some memory information */
    Status = NtQuerySystemInformation(SystemBasicInformation,   //获取系统基本信息
                                      &SystemBasicInfo,
                                      sizeof(SYSTEM_BASIC_INFORMATION),
                                      NULL);
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("Failure to query system info\n");
        return Status;
    }
    
    /* Use the Image Settings if we are dealing with the current Process */
    if (hProcess == NtCurrentProcess()) //若是为当前进程创建栈
    {
        /* Get the Image Headers */
        Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);   //获取Nt映像头
        
        /* If we didn't get the parameters, find them ourselves */
        StackReserve = (StackReserve) ? //是否选用系统的堆栈保留大小
                        StackReserve : Headers->OptionalHeader.SizeOfStackReserve;
        StackCommit = (StackCommit) ? //是否选用系统的堆栈提交大小
                       StackCommit : Headers->OptionalHeader.SizeOfStackCommit;
    }
    else//若不是为当前进程创建栈
    {
        /* Use the System Settings if needed */
        StackReserve = (StackReserve) ? StackReserve :
                                        SystemBasicInfo.AllocationGranularity;
        StackCommit = (StackCommit) ? StackCommit : SystemBasicInfo.PageSize;
    }
    
    /* Align everything to Page Size */
    //对齐
    StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity);
    StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize);
    #if 1 // FIXME: Remove once Guard Page support is here
    StackCommit = StackReserve;
    #endif
    DPRINT("StackReserve: %lx, StackCommit: %lx\n", StackReserve, StackCommit);
    
    /* Reserve memory for the stack */
    Status = ZwAllocateVirtualMemory(hProcess,  //保留一块区间
                                     (PVOID*)&Stack,
                                     0,
                                     &StackReserve,
                                     MEM_RESERVE,
                                     PAGE_READWRITE);
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("Failure to reserve stack\n");
        return Status;
    }
    
    /* Now set up some basic Initial TEB Parameters */
    // 初始化一些Teb的信息 包括分配的栈顶和栈底
    InitialTeb->AllocatedStackBase = (PVOID)Stack;  //栈底
    InitialTeb->StackBase = (PVOID)(Stack + StackReserve);  //栈顶
    InitialTeb->PreviousStackBase = NULL;
    InitialTeb->PreviousStackLimit = NULL;
    
    /* Update the Stack Position */
    Stack += StackReserve - StackCommit;//更新栈的位置
    
    /* Check if we will need a guard page */
    //分配一个guard page用于实现自动堆栈检查
    if (StackReserve > StackCommit)
    {
        Stack -= SystemBasicInfo.PageSize;
        StackCommit += SystemBasicInfo.PageSize;
        UseGuard = TRUE;
    }
    
    /* Allocate memory for the stack */
    Status = ZwAllocateVirtualMemory(hProcess,  //提交指定页面
                                     (PVOID*)&Stack,
                                     0,
                                     &StackCommit,
                                     MEM_COMMIT,
                                     PAGE_READWRITE);
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("Failure to allocate stack\n");
        return Status;
    }
    
    /* Now set the current Stack Limit */
    //将提交信息挡刀InitialTeb中
    InitialTeb->StackLimit = (PVOID)Stack;
    
    /* Create a guard page */
    //若使用gurad page
    if (UseGuard)
    {
        SIZE_T GuardPageSize = SystemBasicInfo.PageSize;
        ULONG Dummy;
        
        /* Attempt maximum space possible */        
        Status = ZwProtectVirtualMemory(hProcess,//更改一个页面为PAGE_GUARD
                                        (PVOID*)&Stack,
                                        &GuardPageSize,
                                        PAGE_GUARD | PAGE_READWRITE,
                                        &Dummy);
        if (!NT_SUCCESS(Status))
        {
            DPRINT1("Failure to create guard page\n");
            return Status;
        }
        
        /* Update the Stack Limit keeping in mind the Guard Page */
        //更改保存到InitialTeb中
        InitialTeb->StackLimit = (PVOID)((ULONG_PTR)InitialTeb->StackLimit - GuardPageSize);
    }
    
    /* We are done! */
    return STATUS_SUCCESS;
}

创建stack流程

  • 首先调用NtQuerySystemInformation获取系统基本信息,用于创建获取默认的堆栈提交保留大小。
  • 根据传进的参数决定是用默认堆栈大小还是指定堆栈大小,然后先分配提交的空间
  • 根据是否启用一个guard page决定是否多提交一个页大小,并将相关信息保留到InitialTeb中
  • 根据提交大小提交栈,并将相关信息保留到InitialTeb中
  • 根据是否启用guard page决定是否更改栈底的一个页面为PAGE_GUARD属性

NtCreateThread

对于BaseInitializeContext与我们当前关心的用户格局并无关联,所以暂时跳过去。
接下来看下NtCreateThread为第一个线程创建TEB的流程
而NtCreateThread则是通过MmCreateTeb为线程建立Teb的的

//CreateProcessW->CreateProcessInternalW->BasepCreateFirstThread->NtCreateThread->PspCreateThread->MmCreateTeb

NTSTATUS
NTAPI
MmCreateTeb(IN PEPROCESS Process,
            IN PCLIENT_ID ClientId,
            IN PINITIAL_TEB InitialTeb,
            OUT PTEB *BaseTeb)
{
    PTEB Teb;
    NTSTATUS Status = STATUS_SUCCESS;
    *BaseTeb = NULL;

    //
    // Attach to Target
    //
    KeAttachProcess(&Process->Pcb); //先挂靠到创建的新进程上

    //
    // Allocate the TEB
    //
    Status = MiCreatePebOrTeb(Process, sizeof(TEB), (PULONG_PTR)&Teb);  //创建一个Teb 这里用的是While循环 从指定的高地址处每次减少一个页面大小 然后看是否合适 由于这是第一个 所以一第二次循环即可满足要求

    ASSERT(NT_SUCCESS(Status));

    //
    // Use SEH in case we can't load the TEB
    //
    _SEH2_TRY
    {
        //
        // Initialize the PEB
        //
        RtlZeroMemory(Teb, sizeof(TEB));

        //
        // Set TIB Data
        // 初始化Teb的异常处理链 及 自身的地址
        Teb->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
        Teb->NtTib.Self = (PNT_TIB)Teb;

        //
        // Identify this as an OS/2 V3.0 ("Cruiser") TIB
        //
        Teb->NtTib.Version = 30 << 8;

        //
        // Set TEB Data
        // 初始化其他属性
        Teb->ClientId = *ClientId;
        Teb->RealClientId = *ClientId;
        Teb->ProcessEnvironmentBlock = Process->Peb;
        Teb->CurrentLocale = PsDefaultThreadLocaleId;

        //
        // Check if we have a grandparent TEB
        // 将先前的InitialTeb的属性复制到Teb中
        if ((InitialTeb->PreviousStackBase == NULL) &&
            (InitialTeb->PreviousStackLimit == NULL))
        {
            //
            // Use initial TEB values
            //
            Teb->NtTib.StackBase = InitialTeb->StackBase;   //栈顶
            Teb->NtTib.StackLimit = InitialTeb->StackLimit; //栈底
            Teb->DeallocationStack = InitialTeb->AllocatedStackBase;
        }
        else
        {
            //
            // Use grandparent TEB values
            //
            Teb->NtTib.StackBase = InitialTeb->PreviousStackBase;
            Teb->NtTib.StackLimit = InitialTeb->PreviousStackLimit;
        }

        //
        // Initialize the static unicode string
        // 
        Teb->StaticUnicodeString.MaximumLength = sizeof(Teb->StaticUnicodeBuffer);
        Teb->StaticUnicodeString.Buffer = Teb->StaticUnicodeBuffer;
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        //
        // Get error code
        //
        Status = _SEH2_GetExceptionCode();
    }
    _SEH2_END;

    //
    // Return
    //
    KeDetachProcess();  //撤销挂靠
    *BaseTeb = Teb;     //将创建的Teb返回
    return Status;
}

这里跟之前MmCreatePeb很类似,也是先通过调用MiCreatePebOrTeb从高地址开始分配Teb,由于这是第一个线程,所以两次循环即可实现第一个线程的Teb的建立,接着自然就是对Teb的一个初始化,其中用到了之前的InitialTeb的信息,将其提取出来放到Teb中。

用户空间的格局图简略如下
Windows进程与线程---1_第3张图片

NtCreateProcess

Windows通过NtCreateProcess来建立新进程

/*
 * @implemented
 */
NTSTATUS
NTAPI
NtCreateProcess(OUT PHANDLE ProcessHandle,
                IN ACCESS_MASK DesiredAccess,
                IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
                IN HANDLE ParentProcess,
                IN BOOLEAN InheritObjectTable,
                IN HANDLE SectionHandle OPTIONAL,
                IN HANDLE DebugPort OPTIONAL,
                IN HANDLE ExceptionPort OPTIONAL)
{
    ULONG Flags = 0;
    PSTRACE(PS_PROCESS_DEBUG,
            "Parent: %p Attributes: %p\n", ParentProcess, ObjectAttributes);

    /* Set new-style flags */
    if ((ULONG)SectionHandle & 1) Flags = PS_REQUEST_BREAKAWAY;
    if ((ULONG)DebugPort & 1) Flags |= PS_NO_DEBUG_INHERIT;
    if (InheritObjectTable) Flags |= PS_INHERIT_HANDLES;

    /* Call the new API */
    return NtCreateProcessEx(ProcessHandle, //实际上是调用了NtCreateProcessEx
                             DesiredAccess,
                             ObjectAttributes,
                             ParentProcess,
                             Flags,
                             SectionHandle,
                             DebugPort,
                             ExceptionPort,
                             FALSE);
}

可以看到这里其实是下发给了NtCreateProcessEx,这里的Ex多了一个InJob参数,表明所创建的进程是否属于某个任务

/*
 * @implemented
 */
NTSTATUS
NTAPI
NtCreateProcessEx(OUT PHANDLE ProcessHandle,
                  IN ACCESS_MASK DesiredAccess,
                  IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
                  IN HANDLE ParentProcess,
                  IN ULONG Flags,
                  IN HANDLE SectionHandle OPTIONAL,
                  IN HANDLE DebugPort OPTIONAL,
                  IN HANDLE ExceptionPort OPTIONAL,
                  IN BOOLEAN InJob)
{
    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
    NTSTATUS Status;
    PAGED_CODE();
    PSTRACE(PS_PROCESS_DEBUG,
            "ParentProcess: %p Flags: %lx\n", ParentProcess, Flags);

    /* Check if we came from user mode */
    if (PreviousMode != KernelMode)
    {
        _SEH2_TRY
        {
            /* Probe process handle */
            ProbeForWriteHandle(ProcessHandle);//测试句柄是否可写
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            /* Return the exception code */
            _SEH2_YIELD(return _SEH2_GetExceptionCode());
        }
        _SEH2_END;
    }

    /* Make sure there's a parent process */
    if (!ParentProcess)
    {
        /* Can't create System Processes like this */
        Status = STATUS_INVALID_PARAMETER;
    }
    else
    {
        /* Create a user Process */
        Status = PspCreateProcess(ProcessHandle,    //发送给PspCreateProcess
                                  DesiredAccess,
                                  ObjectAttributes,
                                  ParentProcess,
                                  Flags,
                                  SectionHandle,
                                  DebugPort,
                                  ExceptionPort,
                                  InJob);
    }

    /* Return Status */
    return Status;
}

这里NtCreateProcessEx先检查句柄是否可写,若可写则又下放给了PspCreateProcess。
这里的SectionHandle是文件映射区的句柄,代表着目标映像文件。最后是DebugPort和ExceptionPort,是用于供新进程发送调试信息异常信息的两个端口。

PspCreateProcess

//NtCreateProcess->NtCreateProcessEx->PspCreateProcess
NTSTATUS
NTAPI
PspCreateProcess(OUT PHANDLE ProcessHandle,
                 IN ACCESS_MASK DesiredAccess,
                 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
                 IN HANDLE ParentProcess OPTIONAL,
                 IN ULONG Flags,
                 IN HANDLE SectionHandle OPTIONAL,
                 IN HANDLE DebugPort OPTIONAL,
                 IN HANDLE ExceptionPort OPTIONAL,
                 IN BOOLEAN InJob)
{
    HANDLE hProcess;
    PEPROCESS Process, Parent;
    PVOID ExceptionPortObject;
    PDEBUG_OBJECT DebugObject;
    PSECTION_OBJECT SectionObject;
    NTSTATUS Status, AccessStatus;
    ULONG_PTR DirectoryTableBase[2] = {0,0};
    KAFFINITY Affinity;
    HANDLE_TABLE_ENTRY CidEntry;
    PETHREAD CurrentThread = PsGetCurrentThread();
    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
    PEPROCESS CurrentProcess = PsGetCurrentProcess();
    ULONG MinWs, MaxWs;
    ACCESS_STATE LocalAccessState;
    PACCESS_STATE AccessState = &LocalAccessState;
    AUX_ACCESS_DATA AuxData;
    UCHAR Quantum;
    BOOLEAN Result, SdAllocated;
    PSECURITY_DESCRIPTOR SecurityDescriptor;
    SECURITY_SUBJECT_CONTEXT SubjectContext;
    BOOLEAN NeedsPeb = FALSE;
    INITIAL_PEB InitialPeb;
    PAGED_CODE();
    PSTRACE(PS_PROCESS_DEBUG,
            "ProcessHandle: %p Parent: %p\n", ProcessHandle, ParentProcess);

    /* Validate flags */
    if (Flags & ~PS_ALL_FLAGS) return STATUS_INVALID_PARAMETER;

    /* Check for parent */
    if (ParentProcess)  //若存在父进程
    {
        /* Reference it */
        Status = ObReferenceObjectByHandle(ParentProcess,   //获取父进程对象 即EPROCESS
                                           PROCESS_CREATE_PROCESS,
                                           PsProcessType,
                                           PreviousMode,
                                           (PVOID*)&Parent,
                                           NULL);
        if (!NT_SUCCESS(Status)) return Status;

        /* If this process should be in a job but the parent isn't */
        if ((InJob) && (!Parent->Job))
        {
            /* This is illegal. Dereference the parent and fail */
            ObDereferenceObject(Parent);
            return STATUS_INVALID_PARAMETER;
        }

        /* Inherit Parent process's Affinity. */
        Affinity = Parent->Pcb.Affinity;
    }
    else
    {
        /* We have no parent */
        Parent = NULL;
        Affinity = KeActiveProcessors;
    }

    /* Save working set data */
    MinWs = PsMinimumWorkingSet;
    MaxWs = PsMaximumWorkingSet;

    /* Create the Object */
    Status = ObCreateObject(PreviousMode,       //创建一个PsProcessType类型的对象,即EPROCESS对象
                            PsProcessType,
                            ObjectAttributes,
                            PreviousMode,
                            NULL,
                            sizeof(EPROCESS),
                            0,
                            0,
                            (PVOID*)&Process);
    if (!NT_SUCCESS(Status)) goto Cleanup;

    /* Clean up the Object */
    RtlZeroMemory(Process, sizeof(EPROCESS));   //清零操作

    /* Initialize pushlock and rundown protection */
    ExInitializeRundownProtection(&Process->RundownProtect);
    Process->ProcessLock.Value = 0;

    /* Setup the Thread List Head */
    InitializeListHead(&Process->ThreadListHead);   //初始化线程队列

    /* Set up the Quota Block from the Parent */
    PspInheritQuota(Process, Parent);               //继承配额信息

    /* Set up Dos Device Map from the Parent */
    ObInheritDeviceMap(Parent, Process);            //继承设备位图信息

    /* Check if we have a parent */
    if (Parent)
    {
        /* Inherit PID and Hard Error Processing */
        Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
        Process->DefaultHardErrorProcessing = Parent->
                                              DefaultHardErrorProcessing;
    }
    else
    {
        /* Use default hard error processing */
        Process->DefaultHardErrorProcessing = TRUE;
    }

    /* Check for a section handle */
    if (SectionHandle)
    {
        /* Get a pointer to it */
        Status = ObReferenceObjectByHandle(SectionHandle,
                                           SECTION_MAP_EXECUTE,
                                           MmSectionObjectType,
                                           PreviousMode,
                                           (PVOID*)&SectionObject,
                                           NULL);
        if (!NT_SUCCESS(Status)) goto CleanupWithRef;
    }
    else
    {
        /* Assume no section object */
        SectionObject = NULL;

        /* Is the parent the initial process? */
        if (Parent != PsInitialSystemProcess)
        {
            /* It's not, so acquire the process rundown */
            if (ExAcquireRundownProtection(&Parent->RundownProtect))
            {
                /* If the parent has a section, use it */
                SectionObject = Parent->SectionObject;
                if (SectionObject) ObReferenceObject(SectionObject);

                /* Release process rundown */
                ExReleaseRundownProtection(&Parent->RundownProtect);
            }

            /* If we don't have a section object */
            if (!SectionObject)
            {
                /* Then the process is in termination, so fail */
                Status = STATUS_PROCESS_IS_TERMINATING;
                goto CleanupWithRef;
            }
        }
    }

    /* Save the pointer to the section object */
    Process->SectionObject = SectionObject;

    /* Check for the debug port */
    if (DebugPort)  //若存在调试端口
    {
        /* Reference it */
        Status = ObReferenceObjectByHandle(DebugPort,
                                           DEBUG_OBJECT_ADD_REMOVE_PROCESS,
                                           DbgkDebugObjectType,
                                           PreviousMode,
                                           (PVOID*)&DebugObject,
                                           NULL);
        if (!NT_SUCCESS(Status)) goto CleanupWithRef;

        /* Save the debug object */
        Process->DebugPort = DebugObject;

        /* Check if the caller doesn't want the debug stuff inherited */
        if (Flags & PS_NO_DEBUG_INHERIT)
        {
            /* Set the process flag */
            InterlockedOr((PLONG)&Process->Flags, PSF_NO_DEBUG_INHERIT_BIT);
        }
    }
    else   //否则拷贝父进程端口
    {
        /* Do we have a parent? Copy his debug port */
        if (Parent) DbgkCopyProcessDebugPort(Process, Parent);
    }

    /* Now check for an exception port */
    if (ExceptionPort)  //若存在异常端口
    {
        /* Reference it */
        Status = ObReferenceObjectByHandle(ExceptionPort,
                                           PORT_ALL_ACCESS,
                                           LpcPortObjectType,
                                           PreviousMode,
                                           (PVOID*)&ExceptionPortObject,
                                           NULL);
        if (!NT_SUCCESS(Status)) goto CleanupWithRef;

        /* Save the exception port */
        Process->ExceptionPort = ExceptionPortObject;
    }

    /* Save the pointer to the section object */
    Process->SectionObject = SectionObject;

    /* Set default exit code */
    Process->ExitStatus = STATUS_PENDING;

    /* Check if this is the initial process being built */
    if (Parent)
    {
        /* Create the address space for the child */
        if (!MmCreateProcessAddressSpace(MinWs,
                                         Process,
                                         DirectoryTableBase))
        {
            /* Failed */
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto CleanupWithRef;
        }
    }
    else
    {
        /* Otherwise, we are the boot process, we're already semi-initialized */
        Process->ObjectTable = CurrentProcess->ObjectTable;
        Status = MmInitializeHandBuiltProcess(Process, DirectoryTableBase);
        if (!NT_SUCCESS(Status)) goto CleanupWithRef;
    }

    /* We now have an address space */
    InterlockedOr((PLONG)&Process->Flags, PSF_HAS_ADDRESS_SPACE_BIT);

    /* Set the maximum WS */
    Process->Vm.MaximumWorkingSetSize = MaxWs;

    /* Now initialize the Kernel Process */
    KeInitializeProcess(&Process->Pcb,
                        PROCESS_PRIORITY_NORMAL,
                        Affinity,
                        DirectoryTableBase,
                        (BOOLEAN)(Process->DefaultHardErrorProcessing & 4));

    /* Duplicate Parent Token */
    Status = PspInitializeProcessSecurity(Process, Parent);
    if (!NT_SUCCESS(Status)) goto CleanupWithRef;

    /* Set default priority class */
    Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;

    /* Check if we have a parent */
    if (Parent)
    {
        /* Check our priority class */
        if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
            Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL)
        {
            /* Normalize it */
            Process->PriorityClass = Parent->PriorityClass;
        }

        /* Initialize object manager for the process */
        Status = ObInitProcess(Flags & PS_INHERIT_HANDLES ? Parent : NULL,
                               Process);
        if (!NT_SUCCESS(Status)) goto CleanupWithRef;
    }
    else
    {
        /* Do the second part of the boot process memory setup */
        Status = MmInitializeHandBuiltProcess2(Process);
        if (!NT_SUCCESS(Status)) goto CleanupWithRef;
    }

    /* Set success for now */
    Status = STATUS_SUCCESS;

    /* Check if this is a real user-mode process */
    if (SectionHandle)
    {
        /* Initialize the address space */
        Status = MmInitializeProcessAddressSpace(Process,
                                                 NULL,
                                                 SectionObject,
                                                 &Flags,
                                                 &Process->
                                                 SeAuditProcessCreationInfo.
                                                 ImageFileName);
        if (!NT_SUCCESS(Status)) goto CleanupWithRef;

        //
        // We need a PEB
        //
        NeedsPeb = TRUE;
    }
    else if (Parent)
    {
        /* Check if this is a child of the system process */
        if (Parent != PsInitialSystemProcess)
        {
            //
            // We need a PEB
            //
            NeedsPeb = TRUE;

            /* This is a clone! */
            ASSERTMSG("No support for cloning yet\n", FALSE);
        }
        else
        {
            /* This is the initial system process */
            Flags &= ~PS_LARGE_PAGES;
            Status = MmInitializeProcessAddressSpace(Process,
                                                     NULL,
                                                     NULL,
                                                     &Flags,
                                                     NULL);
            if (!NT_SUCCESS(Status)) goto CleanupWithRef;

            /* Create a dummy image file name */
            Process->SeAuditProcessCreationInfo.ImageFileName =
                ExAllocatePoolWithTag(PagedPool,
                                      sizeof(OBJECT_NAME_INFORMATION),
                                      'aPeS');
            if (!Process->SeAuditProcessCreationInfo.ImageFileName)
            {
                /* Fail */
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto CleanupWithRef;
            }

            /* Zero it out */
            RtlZeroMemory(Process->SeAuditProcessCreationInfo.ImageFileName,
                          sizeof(OBJECT_NAME_INFORMATION));
        }
    }

#if MI_TRACE_PFNS
    /* Copy the process name now that we have it */
    memcpy(MiGetPfnEntry(Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT)->ProcessName, Process->ImageFileName, 16);
    if (Process->Pcb.DirectoryTableBase[1]) memcpy(MiGetPfnEntry(Process->Pcb.DirectoryTableBase[1] >> PAGE_SHIFT)->ProcessName, Process->ImageFileName, 16);
    if (Process->WorkingSetPage) memcpy(MiGetPfnEntry(Process->WorkingSetPage)->ProcessName, Process->ImageFileName, 16);
#endif

    /* Check if we have a section object and map the system DLL */
    if (SectionObject) PspMapSystemDll(Process, NULL, FALSE);   //映射系统DLL至新进程的空间

    /* Create a handle for the Process */
    CidEntry.Object = Process;
    CidEntry.GrantedAccess = 0;
    Process->UniqueProcessId = ExCreateHandle(PspCidTable, &CidEntry);  //根据PspCidTable 即进程句柄表生成唯一的PID标识
    if (!Process->UniqueProcessId)
    {
        /* Fail */
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto CleanupWithRef;
    }

    /* Set the handle table PID */
    Process->ObjectTable->UniqueProcessId = Process->UniqueProcessId;

    /* Check if we need to audit */
    if (SeDetailedAuditingWithToken(NULL)) SeAuditProcessCreate(Process);

    /* Check if the parent had a job */
    if ((Parent) && (Parent->Job))
    {
        /* FIXME: We need to insert this process */
        DPRINT1("Jobs not yet supported\n");
        ASSERT(FALSE);
    }

    /* Create PEB only for User-Mode Processes */
    if ((Parent) && (NeedsPeb))
    {
        //
        // Set up the initial PEB
        //
        RtlZeroMemory(&InitialPeb, sizeof(INITIAL_PEB));
        InitialPeb.Mutant = (HANDLE)-1;
        InitialPeb.ImageUsesLargePages = 0; // FIXME: Not yet supported

        //
        // Create it only if we have an image section
        //
        if (SectionHandle)
        {
            //
            // Create it
            //
            Status = MmCreatePeb(Process, &InitialPeb, &Process->Peb);  //创建peb结构
            if (!NT_SUCCESS(Status)) goto CleanupWithRef;
        }
        else
        {
            //
            // We have to clone it
            //
            ASSERTMSG("No support for cloning yet\n", FALSE);
        }

    }

    /* The process can now be activated */
    KeAcquireGuardedMutex(&PspActiveProcessMutex);
    InsertTailList(&PsActiveProcessHead, &Process->ActiveProcessLinks);  //插入到对象目录与本进程的句柄表中,从而通过该进程对象,返回该进程对象的句柄->hProcess
    KeReleaseGuardedMutex(&PspActiveProcessMutex);

    /* Create an access state */
    Status = SeCreateAccessStateEx(CurrentThread,
                                   ((Parent) &&
                                   (Parent == PsInitialSystemProcess)) ?
                                    Parent : CurrentProcess,
                                   &LocalAccessState,
                                   &AuxData,
                                   DesiredAccess,
                                   &PsProcessType->TypeInfo.GenericMapping);
    if (!NT_SUCCESS(Status)) goto CleanupWithRef;

    /* Insert the Process into the Object Directory */
    Status = ObInsertObject(Process,        //插入到对象目录中和句柄表中 并返回句柄
                            AccessState,
                            DesiredAccess,
                            1,
                            NULL,
                            &hProcess);

    /* Free the access state */
    if (AccessState) SeDeleteAccessState(AccessState);

    /* Cleanup on failure */
    if (!NT_SUCCESS(Status)) goto Cleanup;

    /* Compute Quantum and Priority */
    ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE);
    Process->Pcb.BasePriority =
        (SCHAR)PspComputeQuantumAndPriority(Process,    //计算并设置进程的优先级 以及 时间片
                                            PsProcessPriorityBackground,
                                            &Quantum);
    Process->Pcb.QuantumReset = Quantum;

    /* Check if we have a parent other then the initial system process */
    Process->GrantedAccess = PROCESS_TERMINATE;
    if ((Parent) && (Parent != PsInitialSystemProcess))
    {
        /* Get the process's SD */
        // 获取安全描述符
        Status = ObGetObjectSecurity(Process,
                                     &SecurityDescriptor,
                                     &SdAllocated);
        if (!NT_SUCCESS(Status))
        {
            /* We failed, close the handle and clean up */
            ObCloseHandle(hProcess, PreviousMode);
            goto CleanupWithRef;
        }

        /* Create the subject context */
        SubjectContext.ProcessAuditId = Process;
        SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
        SubjectContext.ClientToken = NULL;

        /* Do the access check */
        Result = SeAccessCheck(SecurityDescriptor,  //根据SD设置子进程所允许的访问权限
                               &SubjectContext,
                               FALSE,
                               MAXIMUM_ALLOWED,
                               0,
                               NULL,
                               &PsProcessType->TypeInfo.GenericMapping,
                               PreviousMode,
                               &Process->GrantedAccess,
                               &AccessStatus);

        /* Dereference the token and let go the SD */
        ObFastDereferenceObject(&Process->Token,
                                SubjectContext.PrimaryToken);
        ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);

        /* Remove access if it failed */
        if (!Result) Process->GrantedAccess = 0;

        /* Give the process some basic access */
        Process->GrantedAccess |= (PROCESS_VM_OPERATION |   //对于子进程而言 这些操作是进程所必需的一些基本操作 所以即使安全检查出错 这些标志也会被设置
                                   PROCESS_VM_READ |
                                   PROCESS_VM_WRITE |
                                   PROCESS_QUERY_INFORMATION |
                                   PROCESS_TERMINATE |
                                   PROCESS_CREATE_THREAD |
                                   PROCESS_DUP_HANDLE |
                                   PROCESS_CREATE_PROCESS |
                                   PROCESS_SET_INFORMATION |
                                   STANDARD_RIGHTS_ALL |
                                   PROCESS_SET_QUOTA);
    }
    else
    {
        /* Set full granted access */
        Process->GrantedAccess = PROCESS_ALL_ACCESS;
    }

    /* Set the Creation Time */
    KeQuerySystemTime(&Process->CreateTime);    //设置进程被创建的时间

    /* Protect against bad user-mode pointer */
    _SEH2_TRY
    {
        /* Save the process handle */
       *ProcessHandle = hProcess;
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        /* Get the exception code */
       Status = _SEH2_GetExceptionCode();
    }
    _SEH2_END;

    /* Run the Notification Routines */
    PspRunCreateProcessNotifyRoutines(Process, TRUE);

    /* If 12 processes have been created, enough of user-mode is ready */
    if (++ProcessCount == 12) Ki386PerfEnd();

CleanupWithRef:
    /*
     * Dereference the process. For failures, kills the process and does
     * cleanup present in PspDeleteProcess. For success, kills the extra
     * reference added by ObInsertObject.
     */
    ObDereferenceObject(Process);

Cleanup:
    /* Dereference the parent */
    if (Parent) ObDereferenceObject(Parent);

    /* Return status to caller */
    return Status;
}

流程如下

  • 调用ObCreateObject创建一个PsProcessType类型的对象,即EPROCESS对象
  • 继承配额信息,设备位图信息
  • 若父进程存在,复制父进程的PID和DefaultHardErrorProcessing
  • 若存在可执行共享映射区对象句柄 根据句柄获取到该共享文件对象 然后复制到该进程对象中
  • 若存在DebugPort端口和ExceptionPort端口,则获取对象并存放到进程对象中。
  • 为新进程创建句柄表 实际上是遗传调用者的句柄表
  • 调用MmCopyMmInfo 创建新进程的页面映射表,复制系统空间的映射至页面映射表中
  • 调用MmCreateProcessAddressSpace安排进程空间布局,以及将可执行映像载入到新进程空间中
  • 调用KeInitializeProcess进一步的初始化,主要有WaitListHead结构,即进程也是可等待对象,页面映射表物理基址DirectoryBase,并初始化挂入本进程的线程队列
  • 映射系统DLL至新进程的用户空间
  • 通过ExCreateHandle生成句柄,其值就被当做是PID值。根据PspCidTable 即进程句柄表生成唯一的PID标识
  • 创建Peb
  • 插入到PsActiveProcessHead链表中,这个链表记录着所有被创建的进程,即插入到进程队列中。
  • 通过ObInsertObject插入到对象目录与本进程的句柄表中,从而通过该进程对象,能最终返回该进程对象的句柄->hProcess
  • 调用PspComputeQuantumAndPriority计算出子进程的优先级时间片 这两个参数决定了系统的客观性能,并且参数PsProcessPriorityBackground表明进程一开始是作为后台进程工作的
  • 最后则是获取SD并设置安全属性和grantedAccess 即子进程所允许的权限

至此进程就创建完毕,当然进程只是个容器的作用,系统自然是无法调度进程的,所以创建进程之后要通过另一个系统调用NtCreateThread来为该新进程创建线程。

你可能感兴趣的:(Windows操作系统)