⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄

📁 兼容内核漫谈 适合想将Windows上的程序移植到其它平台上的朋友研究查看
💻
📖 第 1 页 / 共 5 页
字号:
   /* 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 + -