相比于Linux中的进程管理,即创建一个线程的同时创建一个进程。而Windows中却不是这样,它是先创建一个进程作为容器,然后创建第一个线程,也就是对于CreateProcess而言,它先调用NtCreateProcess创建进程,然后调用NtCreateThread创建进程的第一个线程。
一些重要的宏如下
#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实现
其流程大概是
用户空间最重要的自然是可执行映像,在进行NtCreateProcess之前,调用者已经为目标映像创建好一个Section,即文件映射区,现在通过NtCreateProcess将它映射到用户空间中,由于此时空间基本为空,根据PE文件的头部信息,我们总能加载到期望地址上去。
当然除了重要的EXE映像,还要许多重要的DLL文件,例如ntdll.dll,这是一个非常重要的DLL,系统调用的时候也需要用到这个DLL。Windows内核在初始化阶段首次需要这个DLL的时候为其创建文件映射区,以后凡是用到这个DLL只要映射这个文件映射区即可。ntdll的映射是由PspCreateProcess->PspMapSystemDll完成的。
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的建立是通过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会填充到那个位置。并且对于进程而言不存在堆栈问题,堆栈是针对于线程而言!
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;
}
上述是初始化参数块的过程。流程是:
由于分配时是从低往高扫描的,而用户空间最低地址是0x10000,所以分配的位置总是在0x10000处,所以从0x10000处开始的第一个64KB是环境变量块,即Environment。第二个64KB是参数块。
下一步就是创建新进程的第一个线程了。首先总结回顾下进程建立的主要点
第一个线程的建立,设计到堆栈和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;
}
这里主要做了三步
/*
* 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流程
对于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通过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,是用于供新进程发送调试信息和异常信息的两个端口。
//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;
}
流程如下
至此进程就创建完毕,当然进程只是个容器的作用,系统自然是无法调度进程的,所以创建进程之后要通过另一个系统调用NtCreateThread来为该新进程创建线程。