📄
字号:
/* Initialize the process object attributes */
if(lpProcessAttributes != NULL)
{
if(lpProcessAttributes->bInheritHandle)
{
ProcAttributes |= OBJ_INHERIT;
}
ProcSecurity = lpProcessAttributes->lpSecurityDescriptor;
}
InitializeObjectAttributes(&ProcObjectAttributes, NULL,
ProcAttributes, NULL, ProcSecurity);
/* initialize the process priority class structure */
PriorityClass.Foreground = FALSE;
if(dwCreationFlags & IDLE_PRIORITY_CLASS)
{
PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
}
else if(dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
{
PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
}
else if . . . . . .
. . . . . .
/* Create a new process */
Status = NtCreateProcess(&hProcess, PROCESS_ALL_ACCESS,
&ProcObjectAttributes, NtCurrentProcess(),
bInheritHandles, hSection, NULL, NULL);
. . . . . .[/code]
为目标映像创建的Section对象中含有许多来自目标映像PE头部的信息,可以通过ZwQuerySection()、即NtQuerySection()询问、获取这些信息。所获取的信息在数据结构Sii中,这是个SECTION_IMAGE_INFORMATION数据结构,其Subsystem字段表明了映像的模式。一个PE映像可以是GUI模式的、面向“视窗”和图像的应用,也可以是“控制台”、即CUI模式的面向命令行和字符的应用。但是二者必居其一,否则就错了。
然后,这里对一个局部量的数据结构PriorityClass进行了一些设置,设置的依据来自调用参数dwCreationFlags中的一些标志位
接着就是对NtCreateProcess()的调用了。不过刚才的PriorityClass并不用于进程的创建,而是用于进程创建之后,这里只是先作好准备。
[code][CreateProcessW() > NtCreateProcess()]
NTSTATUS STDCALL
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)
{
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
if(PreviousMode != KernelMode)
{
_SEH_TRY _SEH_END;
}
if(ParentProcess == NULL)
{
Status = STATUS_INVALID_PARAMETER;
}
else
{
Status = PspCreateProcess(ProcessHandle, DesiredAccess, ObjectAttributes,
ParentProcess, InheritObjectTable, SectionHandle,
DebugPort, ExceptionPort);
}
return Status;
}[/code]
显然,NtCreateProcess()只是PspCreateProcess()的包装,其作用主要是把有关的参数从用户空间拷贝到内核中,并进行一些参数合理性的检查。这里的参数DesiredAccess、ObjectAttributes、和InheritObjectTable与一般创建对象时所用的大致相同。参数ParentProcess则是父进程的Handle,一般这就是当前进程,但也可以不是,也就是说当前进程可以为别的进程创建子进程。另一个参数SectionHandle是一个共享内存区的Handle,这个共享内存区代表着目标映像文件。最后是两个进程间通信端口DebugPort和ExceptionPort的Handle。顾名思义,这两个端口是供新建进程发送调试信息和异常处理信息的端口,是可以为所有进程共用的系统资源。
下面我们看PspCreateProcess()的代码:
[code][CreateProcessW() > NtCreateProcess() > PspCreateProcess()]
NTSTATUS
PspCreateProcess(OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess OPTIONAL, IN BOOLEAN InheritObjectTable,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL)
{
. . . . . .
PreviousMode = ExGetPreviousMode();
. . . . . .
if(ParentProcess != NULL)
{
Status = ObReferenceObjectByHandle(ParentProcess,
PROCESS_CREATE_PROCESS, PsProcessType,
PreviousMode, (PVOID*)&pParentProcess, NULL);
. . . . . .
}
else
{
pParentProcess = NULL;
}
. . . . . .
if (SectionHandle != NULL)
{
Status = ObReferenceObjectByHandle(SectionHandle,
0, MmSectionObjectType, PreviousMode,
(PVOID*)&SectionObject, NULL);
. . . . . .
}
Status = ObCreateObject(PreviousMode, PsProcessType, ObjectAttributes,
PreviousMode, NULL, sizeof(EPROCESS), 0, 0, (PVOID*)&Process);
. . . . . .
KProcess = &Process->Pcb;
RtlZeroMemory(Process, sizeof(EPROCESS));
Status = PsCreateCidHandle(Process, PsProcessType, &Process->UniqueProcessId);
. . . . . .
Process->DebugPort = pDebugPort;
Process->ExceptionPort = pExceptionPort;
. . . . . .
KeInitializeDispatcherHeader(&KProcess->DispatcherHeader,
ProcessObject, sizeof(EPROCESS), FALSE);
/* Inherit parent process's affinity. */
if(pParentProcess != NULL)
{
KProcess->Affinity = pParentProcess->Pcb.Affinity;
Process->InheritedFromUniqueProcessId = pParentProcess->UniqueProcessId;
Process->SessionId = pParentProcess->SessionId;
}
else
{
KProcess->Affinity = KeActiveProcessors;
}
KProcess->BasePriority = PROCESS_PRIO_NORMAL;
KProcess->IopmOffset = 0xffff;
KProcess->LdtDescriptor[0] = 0;
KProcess->LdtDescriptor[1] = 0;
InitializeListHead(&KProcess->ThreadListHead);
KProcess->ThreadQuantum = 6;
KProcess->AutoAlignment = 0;
MmInitializeAddressSpace(Process, &Process->AddressSpace);
ObCreateHandleTable(pParentProcess, InheritObjectTable, Process);
MmCopyMmInfo(pParentProcess ? pParentProcess : PsInitialSystemProcess, Process);
KeInitializeEvent(&Process->LockEvent, SynchronizationEvent, FALSE);
Process->LockCount = 0;
Process->LockOwner = NULL;
Process->Win32WindowStation = (HANDLE)0;
ExAcquireFastMutex(&PspActiveProcessMutex);
InsertTailList(&PsActiveProcessHead, &Process->ProcessListEntry);
InitializeListHead(&Process->ThreadListHead);
ExReleaseFastMutex(&PspActiveProcessMutex);
ExInitializeFastMutex(&Process->TebLock);
Process->Pcb.State = PROCESS_STATE_ACTIVE;
/* Now we have created the process proper */
MmLockAddressSpace(&Process->AddressSpace);
/* Protect the highest 64KB of the process address space */
BaseAddress = (PVOID)MmUserProbeAddress;
Status = MmCreateMemoryArea(Process, &Process->AddressSpace,
MEMORY_AREA_NO_ACCESS,
&BaseAddress, 0x10000, PAGE_NOACCESS,
&MemoryArea, FALSE, FALSE, BoundaryAddressMultiple);
. . . . . .
/* Protect the lowest 64KB of the process address space */
#if 0
BaseAddress = (PVOID)0x00000000;
Status = MmCreateMemoryArea(Process, &Process->AddressSpace,
MEMORY_AREA_NO_ACCESS,
&BaseAddress, 0x10000, PAGE_NOACCESS,
&MemoryArea, FALSE, FALSE, BoundaryAddressMultiple);
. . . . . .
#endif
/* Protect the 60KB above the shared user page */
BaseAddress = (char*)USER_SHARED_DATA + PAGE_SIZE;
Status = MmCreateMemoryArea(Process, &Process->AddressSpace,
MEMORY_AREA_NO_ACCESS,
&BaseAddress, 0x10000 - PAGE_SIZE, PAGE_NOACCESS,
&MemoryArea, FALSE, FALSE, BoundaryAddressMultiple);
. . . . . .
/* Create the shared data page */
BaseAddress = (PVOID)USER_SHARED_DATA;
Status = MmCreateMemoryArea(Process, &Process->AddressSpace,
MEMORY_AREA_SHARED_DATA,
&BaseAddress, PAGE_SIZE, PAGE_READONLY,
&MemoryArea, FALSE, FALSE, BoundaryAddressMultiple);
MmUnlockAddressSpace(&Process->AddressSpace);
. . . . . .
#if 1
/* FIXME - the handle should be created after all things are initialized, NOT HERE! */
Status = ObInsertObject ((PVOID)Process, NULL, DesiredAccess, 0, NULL, &hProcess);
. . . . . .
#endif[/code]
对于新建的进程而言,其父进程是至关重要的,因为它可能需要从父进程继承许多资源和性质、例如已经打开的对象等等。所以,这里先根据父进程的Handle获取指向其EPROCESS数据结构的指针。当然,已经创建并打开的Section对象也是至关重要,因为它代表着目标映像,所以也要根据其Handle获取指向其SECTION_OBJECT数据结构的指针。
接着就通过ObCreateObject()创建新进程的进程对象。当然,对象的类型是PsProcessType。我们知道,进程对象的数据结构是EPROCESS,其头部是KPROCESS,而KPROCESS的头部则是DISPATCHER_HEADER。代码中通过KeInitializeDispatcherHeader()对此进行了初始化。进程对象也是“可等待”对象,可以作为等待的目标。
读者要注意区分“对象”和“已打开对象”。“对象”是实体,是归系统所有的;而“已打开对象”则只是一个进程对某个对象的访问权和上下文。就像因为对于文件对象的管理而需要一个有组织的“文件系统”一样,系统对于进程和线程也需要有组织的管理。为此,系统为每个进程和线程都创建一个CID对象,加以统一管理。CID是“客户身份号(Client ID)”的缩写,一般由进程号和线程号两部分构成。系统有个CID表PspCidTable,类似于每个进程的已打开对象表。
有了进程对象以后,下一步就是通过MmInitializeAddressSpace()为它创建一个用户空间。所谓一个地址空间,实际上就是一套数据结构、就是一个“账本”。我们在这里就不深入到具体的代码中去了,熟悉Linux存储管理的读者应该不难理解此中的机理。
接着的ObCreateHandleTable()为新建进程创建其打开对象表,这很简单。而InsertTailList()则把新建进程的数据结构挂入系统的进程队列。注意这并不意味着这个进程从此就有可能被调度运行,因为在Windows中受调度运行的是线程而不是进程。
现在我们已经有了新建进程的进程对象和一个空白的用户空间,下面就要对其用户空间进行一些初步的“城市规划”了,这就是下面的一连串MmCreateMemoryArea()。这些调用的形式都是一样,只是参数不同,特别是变量BaseAddress的值各不相同。以代码中的第一次调用为例,此时的BaseAddress设置成另一个变量MmUserProbeAddress的值,据查这个变量的值为0x7fff0000。我们知道,Windows把用户空间与系统空间之间的边界划在0x80000000。所以,0x7fff0000是在边界下方64KB的地方。而另一个参数表示所需的区间大小为0x10000,那就是64KB。至于区间的类型是MEMORY_AREA_NO_ACCESS,访问模式为PAGE_NOACCESS。显然,这是要建立一个“无人区”。还有几个就留给读者自己看了,这些还只是属于“城市规划”,下面才是“基本建设”。
上面最后的ObInsertObject()把新建的进程对象插入当前进程的打开对象表。对于当前进程,新建的进程对象当然是已经打开的对象。
我们继续往下看“基本建设”:
[code][CreateProcessW() > NtCreateProcess() > PspCreateProcess()]
/* FIXME - Map ntdll */
Status = LdrpMapSystemDll(hProcess, &LdrStartupAddr);
/* FIXME - hProcess shouldn't be available at this point! */
. . . . . .
/* Map the process image */
if (SectionObject != NULL)
{
ULONG ViewSize = 0;
Status = MmMapViewOfSection(SectionObject, Process, (PVOID*)&ImageBase,
0, ViewSize, NULL, &ViewSize, 0,
MEM_COMMIT, PAGE_READWRITE);
ObDereferenceObject(SectionObject);
. . . . . .
}
if(pParentProcess != NULL)
{
/*
* Duplicate the token
*/
Status = SepInitializeNewProcess(Process, pParentProcess);
. . . . . .
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -