Windows进程与线程---1

本文详细介绍了Windows操作系统中进程和线程的创建过程,包括先创建进程作为容器,再创建线程的步骤。讨论了Windows进程的用户空间格局创建,如PEB的建立、ntdll.dll的加载,以及线程的建立,包括BasepCreateStack、NtCreateThread等关键步骤。同时,文章还阐述了NtCreateProcess在进程创建中的作用,以及如何映射系统DLL到用户空间。
摘要由CSDN通过智能技术生成

概述

相比于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
在这里插入图片描述

用户空间格局的创建

用户空间的创建及其大的格局基本上是通过内核函数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
在这里插入图片描述其核心是一个循环,对于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"");
        }
       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值