📄
字号:
else
{
/* FIXME */
}
. . . . . .
Status = PsCreatePeb(hProcess, Process, ImageBase);
/* FIXME - hProcess shouldn't be available at this point! */
. . . . . .
/*
* Maybe send a message to the creator process's debugger
*/
PspRunCreateProcessNotifyRoutine s(Process, TRUE);
. . . . . .
return Status;
}[/code]
读者想必知道,LdrpMapSystemDll()的作用就是把ntdll.dll的映像映射到目标进程的用户空间,还包括获取其LdrInitializeThunk()、KiUserApcDispatcher()等函数的入口地址,具体的代码这里就不看了。接着的MmMapViewOfSection()则把目标EXE映像也映射到目标进程的用户空间。至于PsCreatePeb(),当然是创建PEB。PEB在用户空间的位置是0x7FFDF000。
最后的PspRunCreateProcessNotifyRoutine s()依次调用预先设置在一个函数指针数组PiProcessNotifyRoutine[]中的函数,向有关方面发出已经创建了一个进程的通知。不过在ReactOS的0.2.6版中似乎还没有谁来设置这些函数,所以还只是空操作。
倒是PsCreatePeb()还需要补充几句,因为从代码中看它还搞了点“副业”,就是把一个用于NLS的Section对象NlsSectionObject所代表的映像也映射到了目标进程的用户空间。这是因为PEB中的一些字段本来就与所用的语言文字有关。NLS是“National Language Support”即“本国语言支持”的缩写,主要就是不同的文字输入法。不过对此恐怕得要另作研究、另行撰文介绍,否则这儿就离题太远了。
至此,作为一个对象的新建进程已经创建,并且已经挂入进程队列。不过,由NtCreateProcess()创建的新建进程在某些方面还是空白,有些手续也尚未完成,还是一个半成品,需要由创建者进一步将其变为成品并完成有关的手续,主要包括:
? l 设置新建进程的调度优先级。
? l 建立新建进程的“进程参数块”PPB。
? l 让新建进程从父进程继承允许遗传的已打开对象的Handle,把这些Handle复制给新建进程。
? l 向csrss发出已经创建了一个进程的通知。
? l 把PPB中的数据写入新建进程的PEB。
下面我们回到CreateProcessW()的代码。
[code][CreateProcessW()]
Status = NtSetInformationProcess(hProcess, ProcessPriorityClass,
&PriorityClass, sizeof(PROCESS_PRIORITY_CLASS));
. . . . . .
/* Create the PPB */
RtlCreateProcessParameters(&Ppb, &ImagePathName_U, NULL,
lpCurrentDirectory ? &CurrentDirectory_U : NULL,
&CommandLine_U, lpEnvironment, NULL, NULL, NULL,
lpStartupInfo && lpStartupInfo->lpReserved2 ?
&RuntimeInfo_U : NULL);
. . . . . .
/*
* Translate some handles for the new process
*/
if (Ppb->CurrentDirectoryHandle)
{
Status = NtDuplicateObject (NtCurrentProcess(),Ppb->CurrentDirectoryHandle,
hProcess, &Ppb->CurrentDirectoryHandle, 0, TRUE,
DUPLICATE_SAME_ACCESS);
}
/* Close the section */
NtClose(hSection);
/* Get some information about the process */
NtQueryInformationProcess(hProcess,
ProcessBasicInformation,
&ProcessBasicInfo,
sizeof(ProcessBasicInfo),
&retlen);
DPRINT("ProcessBasicInfo.UniqueProcessId 0x%x\n",
ProcessBasicInfo.UniqueProcessId);
lpProcessInformation->dwProcessId = (DWORD)ProcessBasicInfo.UniqueProcessId;
/* Tell the csrss server we are creating a new process */
CsrRequest.Type = CSRSS_CREATE_PROCESS;
CsrRequest.Data.CreateProcessRequest.NewProcessId =
ProcessBasicInfo.UniqueProcessId;
if (Sii.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)
{
/* Do not create a console for GUI applications */
dwCreationFlags &= ~CREATE_NEW_CONSOLE;
dwCreationFlags |= DETACHED_PROCESS;
}
else if (Sii.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
{
if (NULL == Ppb->hConsole)
{
dwCreationFlags |= CREATE_NEW_CONSOLE;
}
}
CsrRequest.Data.CreateProcessRequest.Flags = dwCreationFlags;
CsrRequest.Data.CreateProcessRequest.CtrlDispatcher = ConsoleControlDispatcher;
Status = CsrClientCallServer(&CsrRequest, &CsrReply,
sizeof(CSRSS_API_REQUEST), sizeof(CSRSS_API_REPLY));
. . . . . .
Ppb->hConsole = CsrReply.Data.CreateProcessReply.Console;
InputSet = FALSE;
OutputSet = FALSE;
ErrorSet = FALSE;
/* Set the child console handles */
/* First check if handles were passed in startup info */
if (lpStartupInfo && (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES))
{
if (lpStartupInfo->hStdInput)
{
Ppb->hStdInput = lpStartupInfo->hStdInput;
InputSet = TRUE;
InputDup = TRUE;
}
if (lpStartupInfo->hStdOutput)
{
Ppb->hStdOutput = lpStartupInfo->hStdOutput;
OutputSet = TRUE;
OutputDup = TRUE;
}
if (lpStartupInfo->hStdError)
{
Ppb->hStdError = lpStartupInfo->hStdError;
ErrorSet = TRUE;
ErrorDup = TRUE;
}
}
/* Check if new console was created, use it for input and output if not overridden */
if (0 != (dwCreationFlags & CREATE_NEW_CONSOLE)
&& NT_SUCCESS(Status) && NT_SUCCESS(CsrReply.Status))
{
if (! InputSet)
{
Ppb->hStdInput = CsrReply.Data.CreateProcessReply.InputHandle;
InputSet = TRUE;
InputDup = FALSE;
}
if (! OutputSet)
{
Ppb->hStdOutput = CsrReply.Data.CreateProcessReply.OutputHandle;
OutputSet = TRUE;
OutputDup = FALSE;
}
if (! ErrorSet)
{
Ppb->hStdError = CsrReply.Data.CreateProcessReply.OutputHandle;
ErrorSet = TRUE;
ErrorDup = FALSE;
}
}
/* Use existing handles otherwise */
if (! InputSet)
{
Ppb->hStdInput = NtCurrentPeb()->ProcessParameters->hStdInput;
InputDup = TRUE;
}
if (! OutputSet)
{
Ppb->hStdOutput = NtCurrentPeb()->ProcessParameters->hStdOutput;
OutputDup = TRUE;
}
if (! ErrorSet)
{
Ppb->hStdError = NtCurrentPeb()->ProcessParameters->hStdError;
ErrorDup = TRUE;
}
/* Now duplicate handles if required */
if (InputDup)
{
if (IsConsoleHandle(Ppb->hStdInput))
{
Ppb->hStdInput = CsrReply.Data.CreateProcessReply.InputHandle;
}
else
{
DPRINT("Duplicate input handle\n");
Status = NtDuplicateObject (NtCurrentProcess(), Ppb->hStdInput,
hProcess, &Ppb->hStdInput,
0, TRUE, DUPLICATE_SAME_ACCESS);
. . . . . .
}
}
if (OutputDup)
{
if (IsConsoleHandle(Ppb->hStdOutput))
{
Ppb->hStdOutput = CsrReply.Data.CreateProcessReply.OutputHandle;
}
else
{
DPRINT("Duplicate output handle\n");
Status = NtDuplicateObject (NtCurrentProcess(), Ppb->hStdOutput,
hProcess, &Ppb->hStdOutput,
0, TRUE, DUPLICATE_SAME_ACCESS);
. . . . . .
}
}
. . . . . .
/* Initialize some other fields in the PPB */
if (lpStartupInfo)
{
Ppb->dwFlags = lpStartupInfo->dwFlags;
if (Ppb->dwFlags & STARTF_USESHOWWINDOW)
{
Ppb->wShowWindow = lpStartupInfo->wShowWindow;
}
else
{
Ppb->wShowWindow = SW_SHOWDEFAULT;
}
Ppb->dwX = lpStartupInfo->dwX;
Ppb->dwY = lpStartupInfo->dwY;
Ppb->dwXSize = lpStartupInfo->dwXSize;
Ppb->dwYSize = lpStartupInfo->dwYSize;
Ppb->dwFillAttribute = lpStartupInfo->dwFillAttribute;
}
else
{
Ppb->Flags = 0;
}
/* Create Process Environment Block */
DPRINT("Creating peb\n");
KlInitPeb(hProcess, Ppb, &ImageBaseAddress, Sii.Subsystem);
RtlDestroyProcessParameters (Ppb);[/code]
前面已经准备下了一个数据结构PriorityClass,这里的NtSetInformationProcess()就把与调度优先级有关的信息设置到目标进程对象中去。
此外,前面已经创建了PEB,并对其进行了最基本的设置,但是PEB是个不小的数据结构。特别地,PEB中还有个指针ProcessParameters,指向一个“进程参数块”PPB、即RTL_USER_PROCESS_PARAMETERS数据结构。这PPB也是在用户空间的,虽然是个独立存在的数据结构,逻辑上却可以看作是PEB的一部分。这里先通过RtlCreateProcessParameters()根据有关的信息在(创建者的用户空间)为新建进程创建起一个PPB的副本,经修改补充以后再通过KlInitPeb()写入新建进程的用户空间、并与其PEB建立起连接。
下面陆续有一些对NtDuplicateObject()的调用。这就是用于跨进程复制Handle的系统调用,目的在于让新建进程继承其父进程的一些已打开的对象。不过这里只是复制了当前目录、标准输入、标准输出这三个对象,加上对标准出错信息通道的有条件复制(这里的代码中已略去),并且是从当前进程、而不是从父进程复制的,这样处理之正确与否待考。
还有件事,就是向csrss报告新进程的创建,相当于“报户口”吧,这是由CsrClientCallServer()完成的。这些操作的结果有些也要记录在PPB中,所以对KlInitPeb()的调用是在完成了所有这些操作之后。
至此,新进程的创建已经完成了。
在Windows中,进程并非一个实际受调度运行的实体,也没有执行的上下文,而只是一个让线程赖以存身的框架。所以光创建进程而不创建其第一个线程、即其主线程,是没有意义的。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -