进程线程篇——总结与提升(下)

写在前面

  此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

  看此教程之前,问几个问题,基础知识储备好了吗?保护模式篇学会了吗?练习做完了吗?没有的话就不要继续了。


🔒 华丽的分割线 🔒


进程创建浅析

  同理上面的浅析,我同样使用伪代码。进程创建浅析是为了知道内核是怎样创建进程的流程,具体细节请自行挖掘,我们定位到其内核函数NtCreateProcess

NTSTATUS __stdcall NtCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, BOOLEAN InheritObjectTable, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort)
{
  ULONG v8; // eax

  v8 = (SectionHandle & 1) != 0;
  if ( (DebugPort & 1) != 0 )
    v8 |= 2u;
  if ( InheritObjectTable )
    v8 |= 4u;
  return NtCreateProcessEx(
           ProcessHandle,
           DesiredAccess,
           ObjectAttributes,
           ParentProcess,
           v8,
           SectionHandle,
           DebugPort,
           ExceptionPort,
           0);
}

  这个内核函数又会调用NtCreateProcessEx实现功能,我们点进去看看:

NTSTATUS __stdcall NtCreateProcessEx(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort, BOOLEAN InJob)
{
  PHANDLE v9; // ecx
  NTSTATUS result; // eax

  if ( KeGetCurrentThread()->PreviousMode )
  {
    v9 = ProcessHandle;
    if ( ProcessHandle >= MmUserProbeAddress )
      *MmUserProbeAddress = 0;
    *ProcessHandle = *ProcessHandle;
  }
  else
  {
    v9 = ProcessHandle;
  }
  if ( ParentProcess )
    result = PspCreateProcess(
               v9,
               DesiredAccess,
               ObjectAttributes,
               ParentProcess,
               Flags,
               SectionHandle,
               DebugPort,
               ExceptionPort,
               InJob);
  else
    result = STATUS_INVALID_PARAMETER;
  return result;
}

  这个函数又会调用PspCreateProcess实现创建进程的任务,继续点击去看看:

🔒 点击查看伪代码 🔒
NTSTATUS __stdcall PspCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort, BOOLEAN InJob)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v41 = KeGetCurrentThread();
  AccessMode[0] = v41->PreviousMode;
  process = v41->ApcState.Process;
  v64 = 0;
  a4[0] = 0;
  a4[1] = 0;
  if ( (Flags & 0xFFFFFFF0) != 0 )
    return STATUS_INVALID_PARAMETER;
  if ( ParentProcess )
  {
    result = ObReferenceObjectByHandle(ParentProcess, 0x80u, PsProcessType, AccessMode[0], &eprocess, 0);
    ParentProcess_1 = eprocess;
    if ( result < 0 )
      return result;
    if ( InJob && !eprocess->Job )
    {
      ObfDereferenceObject(eprocess);
      return STATUS_INVALID_PARAMETER;
    }
    ActiveProcessors = eprocess->Pcb.Affinity;
  }
  else
  {
    ParentProcess_1 = 0;
    ActiveProcessors = KeActiveProcessors;
  }
  a3 = ActiveProcessors;
  *NewIrql = PsMinimumWorkingSet;
  v47 = PsMaximumWorkingSet;
  v11 = ObCreateObject(AccessMode[0], PsProcessType, ObjectAttributes, *AccessMode, 0, 608, 0, 0, &Process);
  if ( v11 < 0 )
    goto LABEL_97;
  NewProcess = Process;
  memset(Process, 0, 0x260u);
  NewProcess->RundownProtect.Count = 0;
  NewProcess->ProcessLock.Value = 0;
  InitializeListHead(&NewProcess->ThreadListHead);
  PspInheritQuota(&NewProcess->Pcb, &ParentProcess_1->Pcb);
  ObInheritDeviceMap(NewProcess, ParentProcess_1);
  v13 = ParentProcess_1;
  if ( ParentProcess_1 )
  {
    NewProcess->DefaultHardErrorProcessing = ParentProcess_1->DefaultHardErrorProcessing;
    NewProcess->InheritedFromUniqueProcessId = v13->UniqueProcessId;
  }
  else
  {
    NewProcess->DefaultHardErrorProcessing = 1;
    NewProcess->InheritedFromUniqueProcessId = 0;
  }
  if ( SectionHandle )
  {
    v14 = ObReferenceObjectByHandle(SectionHandle, 8u, MmSectionObjectType, AccessMode[0], &Object, 0);
    v61 = Object;
    v11 = v14;
    if ( v14 < 0 )
      goto LABEL_96;
    v13 = ParentProcess_1;
  }
  else
  {
    v61 = 0;
    if ( v13 != PsInitialSystemProcess )
    {
      if ( ExAcquireRundownProtection(&v13->RundownProtect) )
      {
        v15 = v13->SectionObject;
        v61 = v15;
        if ( v15 )
          ObfReferenceObject(v15);
        ExReleaseRundownProtection(&v13->RundownProtect);
      }
      if ( !v61 )
      {
        v11 = STATUS_PROCESS_IS_TERMINATING;
        goto LABEL_96;
      }
    }
  }
  NewProcess->SectionObject = v61;
  if ( DebugPort )
  {
    v11 = ObReferenceObjectByHandle(DebugPort, 2u, DbgkDebugObjectType, AccessMode[0], &v44, 0);
    if ( v11 < 0 )
      goto LABEL_96;
    NewProcess->DebugPort = v44;
    if ( (Flags & 2) != 0 )
      _InterlockedOr(&NewProcess->584, 2u);
  }
  else if ( v13 )
  {
    DbgkCopyProcessDebugPort(NewProcess, v13);
  }
  if ( ExceptionPort )
  {
    v11 = ObReferenceObjectByHandle(ExceptionPort, 0, LpcPortObjectType, AccessMode[0], &v45, 0);
    if ( v11 < 0 )
      goto LABEL_96;
    NewProcess->ExceptionPort = v45;
  }
  NewProcess->ExitStatus = STATUS_PENDING;
  v11 = PspInitializeProcessSecurity(&ParentProcess_1->Pcb, NewProcess);
  if ( v11 < 0 )
    goto LABEL_96;
  v16 = ParentProcess_1;
  if ( ParentProcess_1 )
  {
    if ( !MmCreateProcessAddressSpace(*NewIrql, NewProcess, a4) )
      goto LABEL_59;
  }
  else
  {
    NewProcess->ObjectTable = process->ObjectTable;
    MmInitializeHandBuiltProcess(NewProcess, a4);
  }
  _InterlockedOr(&NewProcess->584, 0x40000u);
  NewProcess->Vm.MaximumWorkingSetSize = v47;
  KeInitializeProcess(&NewProcess->Pcb, 8, a3, a4, NewProcess->DefaultHardErrorProcessing & 4);
  NewProcess->Pcb.ThreadQuantum = PspForegroundQuantum;
  NewProcess->PriorityClass = 2;
  if ( v16 )
  {
    v17 = v16->PriorityClass;
    if ( v17 == 1 || v17 == 5 )
      NewProcess->PriorityClass = v17;
    v18 = ObInitProcess((Flags & 4) != 0 ? ParentProcess_1 : 0, NewProcess);
  }
  else
  {
    v18 = MmInitializeHandBuiltProcess2(&NewProcess->Pcb);
  }
  v11 = v18;
  if ( v18 < 0 )
    goto LABEL_96;
  v58 = 0;
  if ( SectionHandle )
  {
    v19 = MmInitializeProcessAddressSpace(&NewProcess->Pcb, 0, v61, &NewProcess->SeAuditProcessCreationInfo);
    v11 = v19;
    if ( v19 < 0 )
      goto LABEL_96;
    v58 = v19;
    v11 = PspMapSystemDll(&NewProcess->Pcb, 0);
    if ( v11 < 0 )
      goto LABEL_96;
    v64 = 1;
    goto LABEL_58;
  }
  v20 = ParentProcess_1;
  if ( !ParentProcess_1 )
    goto LABEL_58;
  if ( ParentProcess_1 == PsInitialSystemProcess )
  {
    v11 = MmInitializeProcessAddressSpace(&NewProcess->Pcb, 0, 0, 0);
    if ( v11 >= 0 )
    {
      v24 = ExAllocatePoolWithTag(PagedPool, 8u, 0x61506553u);
      NewProcess->SeAuditProcessCreationInfo.ImageFileName = v24;
      if ( v24 )
      {
        *&v24->Name.Length = 0;
        v24->Name.Buffer = 0;
        goto LABEL_58;
      }
      goto LABEL_59;
    }
LABEL_96:
    ObfDereferenceObject(NewProcess);
    goto LABEL_97;
  }
  NewProcess->SectionBaseAddress = ParentProcess_1->SectionBaseAddress;
  v11 = MmInitializeProcessAddressSpace(&NewProcess->Pcb, v20, 0, 0);
  v64 = 1;
  if ( v11 < 0 )
    goto LABEL_96;
  v21 = ParentProcess_1->SeAuditProcessCreationInfo.ImageFileName;
  if ( v21 )
  {
    v22 = v21->Name.MaximumLength + 8;
    v23 = ExAllocatePoolWithTag(PagedPool, v22, 0x61506553u);
    NewProcess->SeAuditProcessCreationInfo.ImageFileName = v23;
    if ( v23 )
    {
      qmemcpy(v23, ParentProcess_1->SeAuditProcessCreationInfo.ImageFileName, v22);
      NewProcess->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer = &NewProcess->SeAuditProcessCreationInfo.ImageFileName[1].Name.Length;
      goto LABEL_58;
    }
LABEL_59:
    v11 = STATUS_INSUFFICIENT_RESOURCES;
    goto LABEL_96;
  }
LABEL_58:
  v25 = (NewProcess->Token.Value & 0xFFFFFFF8);
  v26 = MmGetSessionId(NewProcess);
  SeSetSessionIdToken(v25, v26);
  v46[0] = NewProcess;
  v46[1] = 0;
  v27 = ExCreateHandle(PspCidTable, v46);
  NewProcess->UniqueProcessId = v27;
  if ( !v27 )
    goto LABEL_59;
  NewProcess->ObjectTable->UniqueProcessId = v27;
  if ( SeDetailedAuditingWithToken(0) )
    SeAuditProcessCreation(&NewProcess->Pcb);
  if ( ParentProcess_1 )
  {
    v28 = ParentProcess_1->Job;
    if ( v28 )
    {
      if ( (v28->LimitFlags & 0x1000) == 0 )
      {
        if ( (Flags & 1) != 0 )
        {
          v11 = (v28->LimitFlags & 0x800) != 0 ? 0 : STATUS_ACCESS_DENIED;
        }
        else
        {
          v11 = PspGetJobFromSet(v28, InJob, &NewProcess->Job);
          if ( v11 < 0 )
            goto LABEL_96;
          v43 = NewProcess->Job;
          v11 = PspAddProcessToJob(v43, &NewProcess->Pcb);
          v29 = v43->Token;
          if ( v29 )
          {
            v11 = SeSubProcessToken(v29, &v50, 0);
            if ( v11 < 0 )
              goto LABEL_96;
            SeAssignPrimaryToken(&NewProcess->Pcb, v50);
            ObfDereferenceObject(v50);
          }
        }
        if ( v11 < 0 )
          goto LABEL_96;
      }
    }
  }
  if ( ParentProcess_1 && v64 )
  {
    BaseAddress[0] = 0;
    BaseAddress[1] = -1;
    if ( SectionHandle )
    {
      v11 = MmCreatePeb(&NewProcess->Pcb, BaseAddress, &NewProcess->Peb);
      if ( v11 < 0 )
      {
        NewProcess->Peb = 0;
        goto LABEL_96;
      }
    }
    else
    {
      LOBYTE(BaseAddress[0]) = 1;
      v30 = ParentProcess_1->Peb;
      NewProcess->Peb = v30;
      MmCopyVirtualMemory(&process->Pcb, BaseAddress, &NewProcess->Pcb, v30, 8u, 0, &a7a);
    }
  }
  v31 = v41;
  --v41->KernelApcDisable;
  ExAcquireFastMutexUnsafe(&PspActiveProcessMutex);
  v32 = dword_48315C;
  NewProcess->ActiveProcessLinks.Flink = &PsActiveProcessHead;
  NewProcess->ActiveProcessLinks.Blink = v32;
  v32->Flink = &NewProcess->ActiveProcessLinks;
  dword_48315C = &NewProcess->ActiveProcessLinks;
  ExReleaseFastMutexUnsafe(&PspActiveProcessMutex);
  v34 = (*(v31 + 212))++ == -1;
  if ( v34 && *(v31 + 52) != v31 + 52 )
  {
    *(v31 + 73) = 1;
    LOBYTE(v33) = 1;
    HalRequestSoftwareInterrupt(v33);
  }
  if ( !ParentProcess_1 || (v35 = PsInitialSystemProcess, ParentProcess_1 != PsInitialSystemProcess) )
    v35 = *(v31 + 68);
  v11 = SeCreateAccessStateEx(0, v35, &PassedAccessState, v37, DesiredAccess, (PsProcessType + 26));
  if ( v11 < 0 )
    goto LABEL_96;
  v11 = ObInsertObject(NewProcess, &PassedAccessState, DesiredAccess, 1u, 0, &Handle);
  v53 = v11;
  SeDeleteAccessState(&PassedAccessState);
  if ( v11 >= 0 )
  {
    NewProcess->GrantedAccess = 1;
    PsSetProcessPriorityByClass(NewProcess, 0);
    if ( !ParentProcess_1 || ParentProcess_1 == PsInitialSystemProcess )
    {
      NewProcess->GrantedAccess = 2035711;
    }
    else
    {
      v11 = ObGetObjectSecurity(NewProcess, &SecurityDescriptor, MemoryAllocated);
      v53 = v11;
      if ( v11 < 0 )
      {
        ObCloseHandle(Handle, AccessMode[0]);
        goto LABEL_96;
      }
      SubjectSecurityContext.ProcessAuditId = NewProcess;
      SubjectSecurityContext.PrimaryToken = PsReferencePrimaryToken(&NewProcess->Pcb);
      SubjectSecurityContext.ClientToken = 0;
      v62 = SeAccessCheck(
              SecurityDescriptor,
              &SubjectSecurityContext,
              0,
              0x2000000u,
              0,
              0,
              (PsProcessType + 26),
              AccessMode[0],
              &NewProcess->GrantedAccess,
              &AccessStatus);
      ObFastDereferenceObject(&NewProcess->Token, SubjectSecurityContext.PrimaryToken);
      ObReleaseObjectSecurity(SecurityDescriptor, MemoryAllocated[0]);
      if ( !v62 )
        NewProcess->GrantedAccess = 0;
      NewProcess->GrantedAccess |= 0x1F07FBu;
    }
    KeQuerySystemTime(&NewProcess->CreateTime);
    *ProcessHandle = Handle;
    ms_exc.registration.TryLevel = -1;
    if ( v58 )
      v11 = v58;
    goto LABEL_96;
  }
LABEL_97:
  if ( ParentProcess_1 )
    ObfDereferenceObject(ParentProcess_1);
  return v11;
}

  我们都知道,创建进程的时候一定会创建一个线程,被称之为主线程。上面都是初始化进程结构体,创建TEB等操作,线程在哪里创建的呢?是因为这个是在3环调用的,我们来看看3环长啥样子:

BOOL __stdcall CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
  return CreateProcessInternalW(
           0,
           lpApplicationName,
           lpCommandLine,
           lpProcessAttributes,
           lpThreadAttributes,
           bInheritHandles,
           dwCreationFlags,
           lpEnvironment,
           lpCurrentDirectory,
           lpStartupInfo,
           lpProcessInformation,
           0);
}

  这个函数又调用了CreateProcessInternalW这个函数,我们来继续点击去看看,创建线程的函数就在这里面,但是为了节省篇幅,我们就给出了局部代码:

BaseInitializeContext(&ThreadContext, v157, SectionInformation, UserStack.StackBase, 0);
v161 = BaseFormatObjectAttributes(&ObjectAttributes, v141, 0);
if ( v184 && v160 && v141 )
{
  DirectoryInfo.CurDirRef = v141->nLength;
  v200 = v141->lpSecurityDescriptor;
  v201 = v141->bInheritHandle;
  v200 = 0;
  v161 = BaseFormatObjectAttributes(&ObjectAttributes, &DirectoryInfo.CurDirRef, 0);
}
v20 = NtCreateThread(
        &ThreadHandle,
        0x1F03FFu,
        v161,
        ProcessHandle,
        &ClientId,
        &ThreadContext,
        &UserStack,
        1u);

进程结束浅析

  结束函数没啥可分析了,给出如下IDA伪代码:

NTSTATUS __stdcall NtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v2 = KeGetCurrentThread();
  v3 = v2;
  v8 = v2->ApcState.Process;
  if ( ProcessHandle )
  {
    v11 = 1;
  }
  else
  {
    ProcessHandle = -1;
    v11 = 0;
  }
  LOBYTE(AccessMode) = v2->PreviousMode;
  result = ObReferenceObjectByHandle(ProcessHandle, 1u, PsProcessType, AccessMode, &AccessMode, 0);
  v5 = AccessMode;
  v6 = AccessMode;
  if ( result >= 0 )
  {
    ProcessHandlea = &AccessMode[146];
    if ( (AccessMode[146].Count & 0x2000) != 0 )
      PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n", AccessMode, &AccessMode[93]);
    RunRef = v5 + 32;
    if ( ExAcquireRundownProtection(v5 + 32) )
    {
      if ( v11 )
        _InterlockedOr(ProcessHandlea, 8u);
      ProcessHandleb = 290;
      v7 = PsGetNextProcessThread(v6, 0);
      if ( v7 )
      {
        ProcessHandleb = 0;
        do
        {
          if ( v7 != v3 )
            PspTerminateThreadByPointer(v7, ExitStatus);
          v7 = PsGetNextProcessThread(v6, v7);
        }
        while ( v7 );
      }
      ExReleaseRundownProtection(RunRef);
      if ( v6 == v8 )
      {
        if ( v11 )
        {
          ObfDereferenceObject(v6);
          PspTerminateThreadByPointer(v3, ExitStatus);
        }
      }
      else if ( ExitStatus == DBG_TERMINATE_PROCESS )
      {
        DbgkClearProcessDebugObject(v6, 0);
      }
      if ( ProcessHandleb == 290 || v6[1].ThreadListHead.Flink && v11 )
      {
        ObClearProcessHandleTable(v6);
        ProcessHandleb = 0;
      }
      ObfDereferenceObject(v6);
      result = ProcessHandleb;
    }
    else
    {
      ObfDereferenceObject(v5);
      result = STATUS_PROCESS_IS_TERMINATING;
    }
  }
  return result;
}

  可以说,结束进程,也就是把它的所有的线程全部干掉,删掉进程相关记录,进程就被杀死了。

进程线程结构体扩展

  之前介绍进程线程相关结构体的时候主要介绍关键的成员,但是有些成员是在说明信息的时候还是比较重要的,或者没啥用处做个记录的,这里再重新补充一下,仅供了解:

KPROCESS

  其结构体如下所示:

kd> dt _KPROCESS
ntdll!_KPROCESS
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 ProfileListHead  : _LIST_ENTRY
   +0x018 DirectoryTableBase : [2] Uint4B
   +0x020 LdtDescriptor    : _KGDTENTRY
   +0x028 Int21Descriptor  : _KIDTENTRY
   +0x030 IopmOffset       : Uint2B
   +0x032 Iopl             : UChar
   +0x033 Unused           : UChar
   +0x034 ActiveProcessors : Uint4B
   +0x038 KernelTime       : Uint4B
   +0x03c UserTime         : Uint4B
   +0x040 ReadyListHead    : _LIST_ENTRY
   +0x048 SwapListEntry    : _SINGLE_LIST_ENTRY
   +0x04c VdmTrapcHandler  : Ptr32 Void
   +0x050 ThreadListHead   : _LIST_ENTRY
   +0x058 ProcessLock      : Uint4B
   +0x05c Affinity         : Uint4B
   +0x060 StackCount       : Uint2B
   +0x062 BasePriority     : Char
   +0x063 ThreadQuantum    : Char
   +0x064 AutoAlignment    : UChar
   +0x065 State            : UChar
   +0x066 ThreadSeed       : UChar
   +0x067 DisableBoost     : UChar
   +0x068 PowerState       : UChar
   +0x069 DisableQuantum   : UChar
   +0x06a IdealNode        : UChar
   +0x06b Flags            : _KEXECUTE_OPTIONS
   +0x06b ExecuteOptions   : UChar

ProfileListHead

  性能分析相关,一般操作系统会自动处理,如下图所示,没啥用处:

ActiveProcessors

  当前活动的处理器,表示在哪个核运行。

ReadyListHead

  该进程处于就绪状态的所有进程链表。

SwapListEntry

  被交换到磁盘上的进程的链表,如果进程被交换出去就会挂到这里。

ThreadListHead

  当前进程下的所有线程的链表。

ProcessLock

  进程锁,用于同步,防止被同时修改用的,给操作系统用的。

ThreadQuantum

  线程默认的时间碎片。

State

  表示进程交换到磁盘和内存的状态。

ThreadSeed

  指示Affinity陈述的亲核性最理想亲的核。

EPROCESS

  其结构体如下所示:

kd> dt _EPROCESS
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER
   +0x078 ExitTime         : _LARGE_INTEGER
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY
   +0x090 QuotaUsage       : [3] Uint4B
   +0x09c QuotaPeak        : [3] Uint4B
   +0x0a8 CommitCharge     : Uint4B
   +0x0ac PeakVirtualSize  : Uint4B
   +0x0b0 VirtualSize      : Uint4B
   +0x0b4 SessionProcessLinks : _LIST_ENTRY
   +0x0bc DebugPort        : Ptr32 Void
   +0x0c0 ExceptionPort    : Ptr32 Void
   +0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE
   +0x0c8 Token            : _EX_FAST_REF
   +0x0cc WorkingSetLock   : _FAST_MUTEX
   +0x0ec WorkingSetPage   : Uint4B
   +0x0f0 AddressCreationLock : _FAST_MUTEX
   +0x110 HyperSpaceLock   : Uint4B
   +0x114 ForkInProgress   : Ptr32 _ETHREAD
   +0x118 HardwareTrigger  : Uint4B
   +0x11c VadRoot          : Ptr32 Void
   +0x120 VadHint          : Ptr32 Void
   +0x124 CloneRoot        : Ptr32 Void
   +0x128 NumberOfPrivatePages : Uint4B
   +0x12c NumberOfLockedPages : Uint4B
   +0x130 Win32Process     : Ptr32 Void
   +0x134 Job              : Ptr32 _EJOB
   +0x138 SectionObject    : Ptr32 Void
   +0x13c SectionBaseAddress : Ptr32 Void
   +0x140 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK
   +0x144 WorkingSetWatch  : Ptr32 _PAGEFAULT_HISTORY
   +0x148 Win32WindowStation : Ptr32 Void
   +0x14c InheritedFromUniqueProcessId : Ptr32 Void
   +0x150 LdtInformation   : Ptr32 Void
   +0x154 VadFreeHint      : Ptr32 Void
   +0x158 VdmObjects       : Ptr32 Void
   +0x15c DeviceMap        : Ptr32 Void
   +0x160 PhysicalVadList  : _LIST_ENTRY
   +0x168 PageDirectoryPte : _HARDWARE_PTE_X86
   +0x168 Filler           : Uint8B
   +0x170 Session          : Ptr32 Void
   +0x174 ImageFileName    : [16] UChar
   +0x184 JobLinks         : _LIST_ENTRY
   +0x18c LockedPagesList  : Ptr32 Void
   +0x190 ThreadListHead   : _LIST_ENTRY
   +0x198 SecurityPort     : Ptr32 Void
   +0x19c PaeTop           : Ptr32 Void
   +0x1a0 ActiveThreads    : Uint4B
   +0x1a4 GrantedAccess    : Uint4B
   +0x1a8 DefaultHardErrorProcessing : Uint4B
   +0x1ac LastThreadExitStatus : Int4B
   +0x1b0 Peb              : Ptr32 _PEB
   +0x1b4 PrefetchTrace    : _EX_FAST_REF
   +0x1b8 ReadOperationCount : _LARGE_INTEGER
   +0x1c0 WriteOperationCount : _LARGE_INTEGER
   +0x1c8 OtherOperationCount : _LARGE_INTEGER
   +0x1d0 ReadTransferCount : _LARGE_INTEGER
   +0x1d8 WriteTransferCount : _LARGE_INTEGER
   +0x1e0 OtherTransferCount : _LARGE_INTEGER
   +0x1e8 CommitChargeLimit : Uint4B
   +0x1ec CommitChargePeak : Uint4B
   +0x1f0 AweInfo          : Ptr32 Void
   +0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   +0x1f8 Vm               : _MMSUPPORT
   +0x238 LastFaultCount   : Uint4B
   +0x23c ModifiedPageCount : Uint4B
   +0x240 NumberOfVads     : Uint4B
   +0x244 JobStatus        : Uint4B
   +0x248 Flags            : Uint4B
   +0x248 CreateReported   : Pos 0, 1 Bit
   +0x248 NoDebugInherit   : Pos 1, 1 Bit
   +0x248 ProcessExiting   : Pos 2, 1 Bit
   +0x248 ProcessDelete    : Pos 3, 1 Bit
   +0x248 Wow64SplitPages  : Pos 4, 1 Bit
   +0x248 VmDeleted        : Pos 5, 1 Bit
   +0x248 OutswapEnabled   : Pos 6, 1 Bit
   +0x248 Outswapped       : Pos 7, 1 Bit
   +0x248 ForkFailed       : Pos 8, 1 Bit
   +0x248 HasPhysicalVad   : Pos 9, 1 Bit
   +0x248 AddressSpaceInitialized : Pos 10, 2 Bits
   +0x248 SetTimerResolution : Pos 12, 1 Bit
   +0x248 BreakOnTermination : Pos 13, 1 Bit
   +0x248 SessionCreationUnderway : Pos 14, 1 Bit
   +0x248 WriteWatch       : Pos 15, 1 Bit
   +0x248 ProcessInSession : Pos 16, 1 Bit
   +0x248 OverrideAddressSpace : Pos 17, 1 Bit
   +0x248 HasAddressSpace  : Pos 18, 1 Bit
   +0x248 LaunchPrefetched : Pos 19, 1 Bit
   +0x248 InjectInpageErrors : Pos 20, 1 Bit
   +0x248 VmTopDown        : Pos 21, 1 Bit
   +0x248 Unused3          : Pos 22, 1 Bit
   +0x248 Unused4          : Pos 23, 1 Bit
   +0x248 VdmAllowed       : Pos 24, 1 Bit
   +0x248 Unused           : Pos 25, 5 Bits
   +0x248 Unused1          : Pos 30, 1 Bit
   +0x248 Unused2          : Pos 31, 1 Bit
   +0x24c ExitStatus       : Int4B
   +0x250 NextPageColor    : Uint2B
   +0x252 SubSystemMinorVersion : UChar
   +0x253 SubSystemMajorVersion : UChar
   +0x252 SubSystemVersion : Uint2B
   +0x254 PriorityClass    : UChar
   +0x255 WorkingSetAcquiredUnsafe : UChar
   +0x258 Cookie           : Uint4B

RundownProtect

  进程停运保护,可以防止他人杀死进程。

  进程的会话子系统相关。

Token

  该进程的令牌,与安全相关。

InheritedFromUniqueProcessId

  指示被谁创建该进程的pid,这东西比较有用,可以找到父进程。

SeAuditProcessCreationInfo

  通过这个可以获取进程的全路径,我们举个例子:

kd> dt _EPROCESS 89a9b648
ntdll!_EPROCESS
   ……
   +0x1f0 AweInfo          : (null) 
   +0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   +0x1f8 Vm               : _MMSUPPORT
   ……

kd> dx -id 0,0,805539a0 -r1 (*((ntdll!_SE_AUDIT_PROCESS_CREATION_INFO *)0x89a9b83c))
(*((ntdll!_SE_AUDIT_PROCESS_CREATION_INFO *)0x89a9b83c))                 [Type: _SE_AUDIT_PROCESS_CREATION_INFO]
    [+0x000] ImageFileName    : 0x89cc8a18 [Type: _OBJECT_NAME_INFORMATION *]
kd> dx -id 0,0,805539a0 -r1 ((ntdll!_OBJECT_NAME_INFORMATION *)0x89cc8a18)
((ntdll!_OBJECT_NAME_INFORMATION *)0x89cc8a18)                 : 0x89cc8a18 [Type: _OBJECT_NAME_INFORMATION *]
    [+0x000] Name             : "\Device\HarddiskVolume1\Program Files\PalmInput\Extensions\Guard\2.6.0.49\PalmInputGuard.exe" [Type: _UNICODE_STRING]

Flags

  指示进程的状态,比较有用。

SubSystemMinorVersion / SubSystemMajorVersion

  指示支持的子系统版本,在PE文件的信息中是有描述的。

ExitStatus

  进程退出状态。

ReadOperationCount

  调用ReadFile的次数。

WriteOperationCount

  调用WriteFile的次数。

OtherOperationCount

  调用其他与IO读写文件相关的API次数。

KTHREAD

  其结构体如下所示:

kd> dt _KTHREAD
ntdll!_KTHREAD
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 MutantListHead   : _LIST_ENTRY
   +0x018 InitialStack     : Ptr32 Void
   +0x01c StackLimit       : Ptr32 Void
   +0x020 Teb              : Ptr32 Void
   +0x024 TlsArray         : Ptr32 Void
   +0x028 KernelStack      : Ptr32 Void
   +0x02c DebugActive      : UChar
   +0x02d State            : UChar
   +0x02e Alerted          : [2] UChar
   +0x030 Iopl             : UChar
   +0x031 NpxState         : UChar
   +0x032 Saturation       : Char
   +0x033 Priority         : Char
   +0x034 ApcState         : _KAPC_STATE
   +0x04c ContextSwitches  : Uint4B
   +0x050 IdleSwapBlock    : UChar
   +0x051 Spare0           : [3] UChar
   +0x054 WaitStatus       : Int4B
   +0x058 WaitIrql         : UChar
   +0x059 WaitMode         : Char
   +0x05a WaitNext         : UChar
   +0x05b WaitReason       : UChar
   +0x05c WaitBlockList    : Ptr32 _KWAIT_BLOCK
   +0x060 WaitListEntry    : _LIST_ENTRY
   +0x060 SwapListEntry    : _SINGLE_LIST_ENTRY
   +0x068 WaitTime         : Uint4B
   +0x06c BasePriority     : Char
   +0x06d DecrementCount   : UChar
   +0x06e PriorityDecrement : Char
   +0x06f Quantum          : Char
   +0x070 WaitBlock        : [4] _KWAIT_BLOCK
   +0x0d0 LegoData         : Ptr32 Void
   +0x0d4 KernelApcDisable : Uint4B
   +0x0d8 UserAffinity     : Uint4B
   +0x0dc SystemAffinityActive : UChar
   +0x0dd PowerState       : UChar
   +0x0de NpxIrql          : UChar
   +0x0df InitialNode      : UChar
   +0x0e0 ServiceTable     : Ptr32 Void
   +0x0e4 Queue            : Ptr32 _KQUEUE
   +0x0e8 ApcQueueLock     : Uint4B
   +0x0f0 Timer            : _KTIMER
   +0x118 QueueListEntry   : _LIST_ENTRY
   +0x120 SoftAffinity     : Uint4B
   +0x124 Affinity         : Uint4B
   +0x128 Preempted        : UChar
   +0x129 ProcessReadyQueue : UChar
   +0x12a KernelStackResident : UChar
   +0x12b NextProcessor    : UChar
   +0x12c CallbackStack    : Ptr32 Void
   +0x130 Win32Thread      : Ptr32 Void
   +0x134 TrapFrame        : Ptr32 _KTRAP_FRAME
   +0x138 ApcStatePointer  : [2] Ptr32 _KAPC_STATE
   +0x140 PreviousMode     : Char
   +0x141 EnableStackSwap  : UChar
   +0x142 LargeStack       : UChar
   +0x143 ResourceIndex    : UChar
   +0x144 KernelTime       : Uint4B
   +0x148 UserTime         : Uint4B
   +0x14c SavedApcState    : _KAPC_STATE
   +0x164 Alertable        : UChar
   +0x165 ApcStateIndex    : UChar
   +0x166 ApcQueueable     : UChar
   +0x167 AutoAlignment    : UChar
   +0x168 StackBase        : Ptr32 Void
   +0x16c SuspendApc       : _KAPC
   +0x19c SuspendSemaphore : _KSEMAPHORE
   +0x1b0 ThreadListEntry  : _LIST_ENTRY
   +0x1b8 FreezeCount      : Char
   +0x1b9 SuspendCount     : Char
   +0x1ba IdealProcessor   : UChar
   +0x1bb DisableBoost     : UChar

MutantListHead

  互斥体链表相关,具体细节在同步篇进行讲解。

Alertable

  指示线程是否被唤醒,具体细节在APC篇进行讲解。

Priority

  线程优先级。

EnableStackSwap

  指示是否能将堆栈最为文件交换

ETHREAD

  结构体如下所示:

kd> dt _ETHREAD
ntdll!_ETHREAD
   +0x000 Tcb              : _KTHREAD
   +0x1c0 CreateTime       : _LARGE_INTEGER
   +0x1c0 NestedFaultCount : Pos 0, 2 Bits
   +0x1c0 ApcNeeded        : Pos 2, 1 Bit
   +0x1c8 ExitTime         : _LARGE_INTEGER
   +0x1c8 LpcReplyChain    : _LIST_ENTRY
   +0x1c8 KeyedWaitChain   : _LIST_ENTRY
   +0x1d0 ExitStatus       : Int4B
   +0x1d0 OfsChain         : Ptr32 Void
   +0x1d4 PostBlockList    : _LIST_ENTRY
   +0x1dc TerminationPort  : Ptr32 _TERMINATION_PORT
   +0x1dc ReaperLink       : Ptr32 _ETHREAD
   +0x1dc KeyedWaitValue   : Ptr32 Void
   +0x1e0 ActiveTimerListLock : Uint4B
   +0x1e4 ActiveTimerListHead : _LIST_ENTRY
   +0x1ec Cid              : _CLIENT_ID
   +0x1f4 LpcReplySemaphore : _KSEMAPHORE
   +0x1f4 KeyedWaitSemaphore : _KSEMAPHORE
   +0x208 LpcReplyMessage  : Ptr32 Void
   +0x208 LpcWaitingOnPort : Ptr32 Void
   +0x20c ImpersonationInfo : Ptr32 _PS_IMPERSONATION_INFORMATION
   +0x210 IrpList          : _LIST_ENTRY
   +0x218 TopLevelIrp      : Uint4B
   +0x21c DeviceToVerify   : Ptr32 _DEVICE_OBJECT
   +0x220 ThreadsProcess   : Ptr32 _EPROCESS
   +0x224 StartAddress     : Ptr32 Void
   +0x228 Win32StartAddress : Ptr32 Void
   +0x228 LpcReceivedMessageId : Uint4B
   +0x22c ThreadListEntry  : _LIST_ENTRY
   +0x234 RundownProtect   : _EX_RUNDOWN_REF
   +0x238 ThreadLock       : _EX_PUSH_LOCK
   +0x23c LpcReplyMessageId : Uint4B
   +0x240 ReadClusterSize  : Uint4B
   +0x244 GrantedAccess    : Uint4B
   +0x248 CrossThreadFlags : Uint4B
   +0x248 Terminated       : Pos 0, 1 Bit
   +0x248 DeadThread       : Pos 1, 1 Bit
   +0x248 HideFromDebugger : Pos 2, 1 Bit
   +0x248 ActiveImpersonationInfo : Pos 3, 1 Bit
   +0x248 SystemThread     : Pos 4, 1 Bit
   +0x248 HardErrorsAreDisabled : Pos 5, 1 Bit
   +0x248 BreakOnTermination : Pos 6, 1 Bit
   +0x248 SkipCreationMsg  : Pos 7, 1 Bit
   +0x248 SkipTerminationMsg : Pos 8, 1 Bit
   +0x24c SameThreadPassiveFlags : Uint4B
   +0x24c ActiveExWorker   : Pos 0, 1 Bit
   +0x24c ExWorkerCanWaitUser : Pos 1, 1 Bit
   +0x24c MemoryMaker      : Pos 2, 1 Bit
   +0x250 SameThreadApcFlags : Uint4B
   +0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit
   +0x250 LpcExitThreadCalled : Pos 1, 1 Bit
   +0x250 AddressSpaceOwner : Pos 2, 1 Bit
   +0x254 ForwardClusterOnly : UChar
   +0x255 DisablePageFaultClustering : UChar

CrossThreadFlags

  表示线程的状态和身份,可以设置状态为系统线程可以使普通权限杀不死。

线程创建浅析

  之前我们分析创建进程的时候,是通过NtCreateThread这个函数进行创建主线程,我们来看看里面有什么:

NTSTATUS __stdcall NtCreateThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PCLIENT_ID ClientId, PCONTEXT ThreadContext, PINITIAL_TEB UserStack, BOOLEAN CreateSuspended)
{
  struct _INITIAL_TEB *v8; // eax
  int *v9; // ebx
  int v11; // ecx
  int v12[6]; // [esp+Ch] [ebp-38h] BYREF
  _KTHREAD *v13; // [esp+24h] [ebp-20h]
  CPPEH_RECORD ms_exc; // [esp+2Ch] [ebp-18h]

  ms_exc.registration.TryLevel = 0;
  v13 = KeGetCurrentThread();
  if ( v13->PreviousMode )
  {
    if ( ThreadHandle >= MmUserProbeAddress )
      *MmUserProbeAddress = 0;
    *ThreadHandle = *ThreadHandle;
    if ( ClientId )
    {
      v12[5] = ClientId;
      if ( ClientId >= MmUserProbeAddress )
        *MmUserProbeAddress = 0;
      if ( (ClientId & 3) != 0 )
        ExRaiseDatatypeMisalignment();
      LOBYTE(ClientId->UniqueProcess) = ClientId->UniqueProcess;
      LOBYTE(ClientId->UniqueThread) = ClientId->UniqueThread;
    }
    if ( !ThreadContext )
    {
      ms_exc.registration.TryLevel = -1;
      return STATUS_INVALID_PARAMETER;
    }
    if ( (ThreadContext & 3) != 0 )
      ExRaiseDatatypeMisalignment();
    v8 = MmUserProbeAddress;
    if ( ThreadContext >= MmUserProbeAddress )
    {
      *MmUserProbeAddress = 0;
      v8 = MmUserProbeAddress;
    }
    v9 = UserStack;
    if ( (UserStack & 3) != 0 )
      ExRaiseDatatypeMisalignment();
    if ( UserStack >= v8 )
      v8->PreviousStackBase = 0;
  }
  else
  {
    v9 = UserStack;
  }
  v12[0] = *v9;
  v11 = v9[1];
  v12[1] = v11;
  if ( !v12[0] && !v11 )
    qmemcpy(v12, v9, 0x14u);
  ms_exc.registration.TryLevel = -1;
  return PspCreateThread(
           ThreadHandle,
           DesiredAccess,
           ObjectAttributes,
           ProcessHandle,
           0,
           ClientId,
           ThreadContext,
           v12,
           CreateSuspended,
           0,
           0);
}

  浏览一遍,发现最终是通过PspCreateThread这个函数进行创建线程实现,点击去看看:

🔒 点击查看伪代码 🔒
MACRO_STATUS __stdcall PspCreateThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PVOID a5, PCLIENT_ID ClientId, PCONTEXT ThreadContext, int a8, BOOLEAN CreateSuspended, int a10, int a11)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  CurrentThread = KeGetCurrentThread();
  CurrentThread_1 = CurrentThread;
  if ( a10 )
    AccessMode[0] = 0;
  else
    AccessMode[0] = CurrentThread->PreviousMode;
  v70 = 0;
  v12 = 0;
  v56 = 0;
  if ( ProcessHandle )
  {
    result = ObReferenceObjectByHandle(ProcessHandle, 2u, PsProcessType, AccessMode[0], &Object, 0);
    v12 = Object;
    v56 = Object;
  }
  else if ( a10 )
  {
    v12 = a5;
    ObfReferenceObject(a5);
    v56 = a5;
    result = STATUS_WAIT_0;
  }
  else
  {
    result = STATUS_INVALID_HANDLE;
  }
  if ( result >= STATUS_WAIT_0 )
  {
    if ( AccessMode[0] && v12 == PsInitialSystemProcess )
    {
      v14 = STATUS_INVALID_HANDLE;
LABEL_15:
      ObfDereferenceObject(v12);
      return v14;
    }
    v15 = ObCreateObject(AccessMode[0], PsThreadType, ObjectAttributes, *AccessMode, 0, 600, 0, 0, &v59);
    if ( v15 < 0 )
    {
      v14 = v15;
      goto LABEL_15;
    }
    thread = v59;
    memset(v59, 0, 0x258u);
    thread->RundownProtect.Count = 0;
    thread->ThreadsProcess = v12;
    thread->Cid.UniqueProcess = v12->UniqueProcessId;
    v60[0] = thread;
    v60[1] = 0;
    v17 = ExCreateHandle(PspCidTable, v60);
    thread->Cid.UniqueThread = v17;
    if ( !v17 )
    {
      v18 = STATUS_INSUFFICIENT_RESOURCES;
LABEL_38:
      ObfDereferenceObject(thread);
      return v18;
    }
    thread->ReadClusterSize = MmReadClusterSize;
    KeInitializeSemaphore(&thread->LpcReplySemaphore, 0, 1);
    thread->ExitTime.HighPart = &thread->456;
    thread->ExitTime.LowPart = &thread->456;
    thread->IrpList.Blink = &thread->IrpList;
    thread->IrpList.Flink = &thread->IrpList;
    thread->PostBlockList.Blink = &thread->PostBlockList;
    thread->PostBlockList.Flink = &thread->PostBlockList;
    thread->ThreadLock.Value = 0;
    KeInitializeSpinLock(&thread->ActiveTimerListLock);
    thread->ActiveTimerListHead.Blink = &thread->ActiveTimerListHead;
    thread->ActiveTimerListHead.Flink = &thread->ActiveTimerListHead;
    v45 = &v12->RundownProtect;
    if ( !ExAcquireRundownProtection(&v12->RundownProtect) )
    {
      v18 = STATUS_PROCESS_IS_TERMINATING;
      goto LABEL_38;
    }
    if ( ThreadContext )
    {
      v18 = MmCreateTeb(&v12->Pcb, a8, &thread->Cid, &v70);
      if ( v18 < 0 )
      {
        v19 = v45;
LABEL_37:
        ExReleaseRundownProtection(v19);
        goto LABEL_38;
      }
      ms_exc.registration.TryLevel = 0;
      thread->StartAddress = ThreadContext->Eip;
      thread->LpcReceivedMessageId = ThreadContext->Eax;
      ms_exc.registration.TryLevel = -1;
      v20 = KeInitThread(thread, 0, PspUserThreadStartup, 0, thread->StartAddress, ThreadContext, v70, v12);
    }
    else
    {
      v70 = 0;
      _InterlockedOr(&thread->584, 0x10u);
      thread->StartAddress = a10;
      v20 = KeInitThread(thread, 0, PspSystemThreadStartup, a10, a11, 0, 0, v12);
    }
    v18 = v20;
    if ( v20 < 0 )
    {
      if ( v70 )
      {
        MmDeleteTeb(&v12->Pcb, v70);
        thread->Tcb.Teb = 0;
      }
LABEL_36:
      v19 = &v12->RundownProtect;
      goto LABEL_37;
    }
    v21 = CurrentThread_1;
    --CurrentThread_1->KernelApcDisable;
    v53 = &v12->ProcessLock;
    _ECX = &v12->ProcessLock;
    _EDX = 2;
    __asm { cmpxchg [ecx], edx }
    if ( (v12->Flags & 8) != 0 )
    {
      v50 = &v12->ProcessLock;
      _ECX = &v12->ProcessLock;
      _EDX = 0;
      __asm { cmpxchg [ecx], edx }
      v26 = (*(v21 + 212))++ == -1;
      if ( v26 && *(v21 + 52) != v21 + 52 )
      {
        *(v21 + 73) = 1;
        LOBYTE(_ECX) = 1;
        HalRequestSoftwareInterrupt(_ECX);
      }
      KeUninitThread(thread);
      if ( v70 )
        MmDeleteTeb(&v12->Pcb, v70);
      v18 = STATUS_PROCESS_IS_TERMINATING;
      goto LABEL_36;
    }
    v27 = v12->ActiveThreads;
    v12->ActiveThreads = v27 + 1;
    v28 = v12->ThreadListHead.Blink;
    thread->ThreadListEntry.Flink = &v12->ThreadListHead;
    thread->ThreadListEntry.Blink = v28;
    v28->Flink = &thread->ThreadListEntry;
    v12->ThreadListHead.Blink = &thread->ThreadListEntry;
    KeStartThread(thread);
    v48 = &v12->ProcessLock;
    _ECX = &v12->ProcessLock;
    _EDX = 0;
    __asm { cmpxchg [ecx], edx }
    v31 = CurrentThread_1;
    v26 = CurrentThread_1->KernelApcDisable++ == -1;
    if ( v26 && *(v31 + 52) != v31 + 52 )
    {
      CurrentThread_1->ApcState.KernelApcPending = 1;
      LOBYTE(_ECX) = 1;
      HalRequestSoftwareInterrupt(_ECX);
    }
    ExReleaseRundownProtection(&v12->RundownProtect);
    if ( !v27 )
    {
      WmiTraceProcess(&v12->Pcb);
      if ( PspCreateProcessNotifyRoutineCount )
      {
        v58 = &PspCreateProcessNotifyRoutine;
        v51 = 8;
        do
        {
          v32 = ExReferenceCallBackBlock(v58);
          v33 = v32;
          if ( v32 )
          {
            v34 = ExGetCallBackBlockRoutine(v32);
            v34(v12->InheritedFromUniqueProcessId, v12->UniqueProcessId, 1);
            ExDereferenceCallBackBlock(v58, v33);
          }
          v58 += 4;
          --v51;
        }
        while ( v51 );
      }
    }
    v35 = v12->Job;
    if ( v35 && *(v35 + 196) && (v12->JobStatus & 5) == 0 )
    {
      _InterlockedOr(&v12->JobStatus, 4u);
      --CurrentThread_1->KernelApcDisable;
      Resource = (v35 + 32);
      ExAcquireResourceSharedLite((v35 + 32), 1u);
      v36 = *(v35 + 196);
      if ( v36 )
        IoSetIoCompletion(v36, *(v35 + 200), v12->UniqueProcessId, 0, 6, 0);
      ExReleaseResourceLite(Resource);
      v37 = CurrentThread_1;
      v26 = CurrentThread_1->KernelApcDisable++ == -1;
      if ( v26 && *(v37 + 52) != v37 + 52 )
      {
        *(v37 + 73) = 1;
        LOBYTE(v37) = 1;
        HalRequestSoftwareInterrupt(v37);
      }
    }
    WmiTraceThread(thread, a8, 1);
    if ( PspCreateThreadNotifyRoutineCount )
    {
      v57 = &PspCreateThreadNotifyRoutine;
      v49 = 8;
      do
      {
        v38 = ExReferenceCallBackBlock(v57);
        RunRef = v38;
        if ( v38 )
        {
          v39 = ExGetCallBackBlockRoutine(v38);
          v39(thread->Cid.UniqueProcess, thread->Cid.UniqueThread, 1);
          ExDereferenceCallBackBlock(v57, RunRef);
        }
        v57 += 4;
        --v49;
      }
      while ( v49 );
    }
    ObReferenceObjectEx(thread, 2);
    if ( ThreadContext )
    {
      P = ExAllocatePoolWithTag(NonPagedPool, 0x30u, 'aCsP');
      if ( !P )
      {
        _InterlockedOr(&thread->584, 2u);
LABEL_64:
        v18 = STATUS_INSUFFICIENT_RESOURCES;
LABEL_77:
        KeReadyThread(thread);
        ObDereferenceObjectEx(thread, 2);
        return v18;
      }
      KeInitializeApc(P, &thread->Tcb, OriginalApcEnvironment, IopDeallocateApc, 0, dword_598B5C, 1, 0);
      if ( !KeInsertQueueApc(P, BaseAddress, 0, 0) )
      {
        _InterlockedOr(&thread->584, 2u);
        ExFreePoolWithTag(P, 0);
        goto LABEL_64;
      }
    }
    if ( CreateSuspended )
    {
      ms_exc.registration.TryLevel = 1;
      KeSuspendThread(&thread->Tcb);
      ms_exc.registration.TryLevel = -1;
      if ( (thread->CrossThreadFlags & 1) != 0 )
        KeForceResumeThread(&thread->Tcb);
    }
    if ( ThreadContext )
      v40 = &CurrentThread_1->ApcState.Process->Pcb;
    else
      v40 = &v12->Pcb;
    v63 = SeCreateAccessStateEx(0, v40, &PassedAccessState, v44, DesiredAccess, (PsThreadType + 26));
    if ( v63 < 0 )
    {
      _InterlockedOr(&thread->584, 2u);
      if ( CreateSuspended )
        KeResumeThread(thread);
      v18 = v63;
      goto LABEL_77;
    }
    v63 = ObInsertObject(thread, &PassedAccessState, DesiredAccess, 0, 0, &Handle);
    SeDeleteAccessState(&PassedAccessState);
    if ( v63 >= 0 )
    {
      ms_exc.registration.TryLevel = 2;
      *ThreadHandle = Handle;
      if ( ClientId )
        *ClientId = thread->Cid;
      ms_exc.registration.TryLevel = -1;
    }
    else
    {
      _InterlockedOr(&thread->584, 2u);
      if ( CreateSuspended )
        KeResumeThread(thread);
    }
    KeQuerySystemTime(&CurrentTime);
    v41 = CurrentTime.QuadPart >> 29;
    thread->CreateTime.LowPart = 8 * CurrentTime.LowPart;
    thread->CreateTime.HighPart = v41;
    if ( (thread->CrossThreadFlags & 2) != 0 )
    {
      thread->GrantedAccess = 2032639;
    }
    else
    {
      v63 = ObGetObjectSecurity(thread, &SecurityDescriptor, MemoryAllocated);
      if ( v63 < 0 )
      {
        _InterlockedOr(&thread->584, 2u);
        if ( CreateSuspended )
          KeResumeThread(thread);
        KeReadyThread(thread);
        ObfDereferenceObject(thread);
        ObCloseHandle(Handle, AccessMode[0]);
        goto LABEL_95;
      }
      SubjectSecurityContext.ProcessAuditId = v12;
      SubjectSecurityContext.PrimaryToken = PsReferencePrimaryToken(&v12->Pcb);
      SubjectSecurityContext.ClientToken = 0;
      v42 = &thread->GrantedAccess;
      v64 = SeAccessCheck(
              SecurityDescriptor,
              &SubjectSecurityContext,
              0,
              0x2000000u,
              0,
              0,
              (PsThreadType + 26),
              AccessMode[0],
              &thread->GrantedAccess,
              &AccessStatus);
      ObFastDereferenceObject(&v12->Token, SubjectSecurityContext.PrimaryToken);
      ObReleaseObjectSecurity(SecurityDescriptor, MemoryAllocated[0]);
      if ( !v64 )
        *v42 = 0;
      *v42 |= 0x61u;
    }
    KeReadyThread(thread);
    ObfDereferenceObject(thread);
LABEL_95:
    result = v63;
  }
  return result;
}

  流程和进程线程创建了流程套路几乎一样,初始化线程结构体,创建TEB,插入结构体,但里面有APC相关的知识,具体细节就不再赘述了。

DPC

  DPC是什么?它的英文全称为Deferred Procedure Call,即延迟过程调用。它最初作用是设计为中断服务程序的一部分,用来解决中断服务处理时间过长的问题。因为每次触发中断,都会关中断,然后执行中断服务例程。由于关中断了,所以中断服务例程必须短小精悍,不能消耗过多时间,否则会导致系统丢失大量其他中断。但是有的中断,其中断服务例程要做的事情本来就很多,那怎么办?于是,可以在中断服务例程中先执行最紧迫的那部分工作,然后把剩余的相对来说不那么重要的工作移入到DPC函数中去执行。
  每当触发一个中断时,中断服务例程可以在当前CPU中插入一个DPC,当执行完ISR,退出ISR后, CPU就会扫描它的DPC队列,依次执行里面的每个DPC,当执行完DPC后,才又回到当前线程的中断处继续执行。
  既然提到ISR,那么它是什么?它的英文全称为Interrupt Service Routines,即中断服务处理。在Windows中如果有认为不太重要的操作会被打包成一个KDPC结构体,如下所示:

kd> dt _KDPC
ntdll!_KDPC
   +0x000 Type             : Int2B
   +0x002 Number           : UChar
   +0x003 Importance       : UChar
   +0x004 DpcListEntry     : _LIST_ENTRY
   +0x00c DeferredRoutine  : Ptr32     void 
   +0x010 DeferredContext  : Ptr32 Void
   +0x014 SystemArgument1  : Ptr32 Void
   +0x018 SystemArgument2  : Ptr32 Void
   +0x01c Lock             : Ptr32 Uint4B

  打包完毕后,会插入到KPCRBDpcListHead成员中,等待触发调用时机。KPCRB结构如下所示,只展示与DPC相关的成员:

kd> dt _KPRCB
ntdll!_KPRCB
   ……
   +0x4b0 DpcTime          : Uint4B
   +0x4b4 DebugDpcTime     : Uint4B
   ……
   +0x860 DpcListHead      : _LIST_ENTRY
   +0x868 DpcStack         : Ptr32 Void
   +0x86c DpcCount         : Uint4B
   +0x870 DpcQueueDepth    : Uint4B
   +0x874 DpcRoutineActive : Uint4B
   +0x878 DpcInterruptRequested : Uint4B
   +0x87c DpcLastCount     : Uint4B
   +0x880 DpcRequestRate   : Uint4B
   ……
   +0x8a0 DpcLock          : Uint4B
   ……
   +0x8c0 CallDpc          : _KDPC
   ……

  非时钟中断的中断、时钟中断、主动调用API,相关函数会去轮询DPC链表进行回调。对于用户或者驱动创建的DPC,还可以加定时器,到指定时间进行触发,但不会挂到KPCR当中,会挂到时钟任务当中。
  介绍了上面的概念,我们来介绍结构体的相关成员含义:

KDPC

Type

  指明该结构体的类型,值为0x13

Number

  指明属于哪个KPCR

Importance

  优先级,取值0-2,0最低,2最高,优先级越高越优先执行,初始化默认值为1。

DpcListEntry

  DPC链表,和进程线程链表一样,挂到腰上。

DeferredRoutine

  DPC的回调函数地址。

DeferredContext

  回调函数上下文,非必须。

SystemArgument1 / SystemArgument2

  回调函数的参数,非必须。

Lock

  DPC结构体的锁。

DPC 的初始化

  学习DPC,首先我们了解一下它的初始化,我们用IDA定位到该函数:

; void __stdcall KeInitializeDpc(PRKDPC Dpc, PKDEFERRED_ROUTINE DeferredRoutine, PVOID DeferredContext)
                public _KeInitializeDpc@12
_KeInitializeDpc@12 proc near           ; CODE XREF: IopInitializeIrpStackProfiler()+29↑p
                                        ; VdmpDelayInterrupt(x)+26B↓p ...

Dpc             = dword ptr  8
DeferredRoutine = dword ptr  0Ch
DeferredContext = dword ptr  10h

                mov     edi, edi
                push    ebp
                mov     ebp, esp
                mov     eax, [ebp+Dpc]
                mov     ecx, [ebp+DeferredRoutine]
                and     dword ptr [eax+1Ch], 0
                mov     [eax+0Ch], ecx
                mov     ecx, [ebp+DeferredContext]
                mov     word ptr [eax], 13h
                mov     byte ptr [eax+2], 0
                mov     byte ptr [eax+3], 1
                mov     [eax+10h], ecx
                pop     ebp
                retn    0Ch
_KeInitializeDpc@12 endp

  这个函数十分简单,用伪代码展示如下:

void __stdcall KeInitializeDpc(PRKDPC Dpc, PKDEFERRED_ROUTINE DeferredRoutine, PVOID DeferredContext)
{
  Dpc->Lock = 0;
  Dpc->DeferredRoutine = DeferredRoutine;
  Dpc->Type = 19;
  Dpc->Number = 0;
  Dpc->Importance = 1;
  Dpc->DeferredContext = DeferredContext;
}

DPC 的插入

  然后我们来看看DPC的插入流程,由于涉及IRQL提权操作,我们先看一下枚举:

  为了增强可读性,如下是我重命名好的IDA的伪代码:

BOOLEAN __stdcall KeInsertQueueDpc(PRKDPC Dpc, PVOID SystemArgument1, PVOID SystemArgument2)
{
  _KPRCB *kprcb; // esi
  bool Importance; // zf
  _LIST_ENTRY *DpcListHead; // ecx
  _LIST_ENTRY *DpcListEntry; // eax
  _LIST_ENTRY *v9; // edx
  _LIST_ENTRY *v10; // edx
  KIRQL NewIrql; // [esp+Fh] [ebp-1h]

  NewIrql = KfRaiseIrql(0x1Fu);
  kprcb = MEMORY[0xFFDFF020];
  _ECX = &Dpc->Lock;
  _EDX = MEMORY[0xFFDFF020] + 0x8A0;            // DpcLock
  __asm { cmpxchg [ecx], edx }
  ++kprcb->DpcCount;
  ++kprcb->DpcQueueDepth;
  Importance = Dpc->Importance == 2;
  Dpc->SystemArgument1 = SystemArgument1;
  Dpc->SystemArgument2 = SystemArgument2;
  DpcListHead = &kprcb->DpcListHead;
  DpcListEntry = &Dpc->DpcListEntry;
  if ( Importance )
  {
    v9 = DpcListHead->Flink;
    DpcListEntry->Flink = DpcListHead->Flink;
    Dpc->DpcListEntry.Blink = DpcListHead;
    v9->Blink = DpcListEntry;
    DpcListHead->Flink = DpcListEntry;
  }
  else
  {
    v10 = kprcb->DpcListHead.Blink;
    DpcListEntry->Flink = DpcListHead;
    Dpc->DpcListEntry.Blink = v10;
    v10->Flink = DpcListEntry;
    kprcb->DpcListHead.Blink = DpcListEntry;
  }
  if ( !kprcb->DpcRoutineActive
    && !kprcb->DpcInterruptRequested
    && (Dpc->Importance
     || kprcb->DpcQueueDepth >= kprcb->MaximumDpcQueueDepth
     || kprcb->DpcRequestRate < kprcb->MinimumDpcRate) )
  {
    LOBYTE(DpcListHead) = 2;
    kprcb->DpcInterruptRequested = 1;
    HalRequestSoftwareInterrupt(DpcListHead);
  }
  KfLowerIrql(NewIrql);
  return 1;
}

  可以看出,如果DPC的优先级高,就会插到DPC链表的前面,如果低就会插到末尾。在多核的情况下,其伪代码会有所不一样,如下所示:

BOOLEAN __stdcall KeInsertQueueDpc(PRKDPC Dpc, PVOID SystemArgument1, PVOID SystemArgument2)
{
  unsigned __int8 Number; // al
  _KPRCB *kpcrb; // esi
  char v5; // bl
  bool v6; // zf
  _LIST_ENTRY *v7; // ecx
  _LIST_ENTRY *v8; // eax
  _LIST_ENTRY *v9; // edx
  _LIST_ENTRY *v10; // edx
  signed __int32 v12; // [esp+Ch] [ebp-8h]
  KIRQL NewIrql; // [esp+13h] [ebp-1h]

  NewIrql = KfRaiseIrql(0x1Fu);
  Number = Dpc->Number;
  if ( Number < 0x20u )
  {
    v5 = Dpc;
    kpcrb = KeGetPcr()->Prcb;
  }
  else
  {
    kpcrb = KiProcessorBlock[Number];
    v5 = Number - 32;
  }
  KiAcquireSpinLock();
  v12 = _InterlockedCompareExchange(&Dpc->Lock, &kpcrb->DpcLock, 0);
  if ( !v12 )
  {
    ++kpcrb->DpcCount;
    ++kpcrb->DpcQueueDepth;
    v6 = Dpc->Importance == 2;
    Dpc->SystemArgument1 = SystemArgument1;
    Dpc->SystemArgument2 = SystemArgument2;
    v7 = &kpcrb->DpcListHead;
    v8 = &Dpc->DpcListEntry;
    if ( v6 )
    {
      v9 = v7->Flink;
      v8->Flink = v7->Flink;
      Dpc->DpcListEntry.Blink = v7;
      v9->Blink = v8;
      v7->Flink = v8;
    }
    else
    {
      v10 = kpcrb->DpcListHead.Blink;
      v8->Flink = v7;
      Dpc->DpcListEntry.Blink = v10;
      v10->Flink = v8;
      kpcrb->DpcListHead.Blink = v8;
    }
    if ( !kpcrb->DpcRoutineActive && !kpcrb->DpcInterruptRequested )
    {
      if ( kpcrb == KeGetPcr()->Prcb )
      {
        if ( Dpc->Importance
          || kpcrb->DpcQueueDepth >= kpcrb->MaximumDpcQueueDepth
          || kpcrb->DpcRequestRate < kpcrb->MinimumDpcRate )
        {
          LOBYTE(v7) = 2;
          kpcrb->DpcInterruptRequested = 1;
          HalRequestSoftwareInterrupt(v7); //处理 DPC
        }
      }
      else if ( Dpc->Importance == 2 || kpcrb->DpcQueueDepth >= kpcrb->MaximumDpcQueueDepth )
      {
        kpcrb->DpcInterruptRequested = 1;
        KiIpiSend(1 << v5, 2u);
      }
    }
  }
  KiReleaseSpinLock(&kpcrb->DpcLock);
  KfLowerIrql(NewIrql);
  return v12 == 0;
}

  这里提一句,想要获取多核调试环境极其内核文件,必须把虚拟机的配置配置成多核再重新安装操作系统。但是我给的符号是单核的,多核的我也踏破铁鞋也找不到,上面的伪代码一些结构体我是通过单核的生成的,至于函数是参考资料命名的。如果您有多核的符号,希望您能够提供,感谢支持。
  对于多核的情况,具体细节将会在同步篇进行讲解。含有SpinLock函数的这个东西成为自旋锁。发现这份代码和单核的会多一些判断和一个函数KiIpiSend,这个函数作用是通知另一个KPCR执行DPC,了解即可。

DPC 的移除

  对于移除DPC代码很简单,如下是其伪代码:

BOOLEAN __stdcall KeRemoveQueueDpc(PRKDPC Dpc)
{
  unsigned int *Lock; // esi

  _disable();                                   // cli
  Lock = Dpc->Lock;
  if ( Lock )
  {
    --*(Lock - 12);
    RemoveEntryList(&Dpc->DpcListEntry);
    Dpc->Lock = 0;
  }
  _enable();                                    // sti
  return Lock != 0;
}

  对于多核下,代码如下,和单核区别不大,只是加入了自旋锁:

BOOLEAN __stdcall KeRemoveQueueDpc(PRKDPC Dpc)
{
  unsigned int *Lock; // edi

  _disable();
  Lock = Dpc->Lock;
  if ( Lock )
  {
    KiAcquireSpinLock(Dpc->Lock);
    if ( Lock == Dpc->Lock )
    {
      --*(Lock - 12);
      RemoveEntryList(&Dpc->DpcListEntry);
      Dpc->Lock = 0;
    }
    KiReleaseSpinLock(Lock);
  }
  _enable();
  return Lock != 0;
}

DPC 的执行

  执行DPC是通过KiRetireDpcList执行的,而调用该函数的是通过KiDispatchInterruptKiIdleLoop执行的,我们通过伪代码进行简单了解:

int __usercall KiRetireDpcList@<eax>(_KPCR *kpcr@<ebx>, _LIST_ENTRY *DPCListHead@<ebp>)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v13 = 0;
  if ( PPerfGlobalGroupMask && (*(PPerfGlobalGroupMask + 4) & 0x80) != 0 )
    v13 = 1;
  do
  {
    MEMORY[0xFFDFFBC4] = &v11;                  // 这里有错误,原汇编:
                                                // mov ds:0FFDFF994h, esp
                                                // 可能是插件的 Bug
                                                // 指向:DpcRoutineActive
    do
    {
      dpc = DPCListHead->Flink;
      v3 = DPCListHead->Flink->Flink;
      DPCListHead->Flink = v3;
      v3->Blink = DPCListHead;
      dpc = (dpc - 4);                          // 挂到腰上了,把指针指到头部
      DeferredRoutine = dpc->DeferredRoutine;
      SystemArgument1 = *&dpc->SystemArgument1;
      DeferredContext = dpc->DeferredContext;
      dpc_1 = dpc;
      dpc->Lock = 0;
      --kpcr->PrcbData.DpcQueueDepth;
      _enable();
      if ( v13 )                                // 日志性能分析,忽略
      {
        v6 = WmiGetCpuClock(DeferredRoutine);
        DeferredRoutine = v7;
        v11 = v6;
        v12 = v7;
      }
      result = (DeferredRoutine)(dpc_1, DeferredContext, SystemArgument1, HIDWORD(SystemArgument1));// 执行 DPC
      if ( v13 )
        result = PerfInfoLogDpc(v12, v11, SHIDWORD(v11));
      _disable();
    }
    while ( !IsListEmpty(DPCListHead) );
    kpcr->PrcbData.DpcRoutineActive = 0;
    kpcr->PrcbData.DpcInterruptRequested = 0;
  }
  while ( !IsListEmpty(DPCListHead) );
  return result;
}

DPC 的使用

  既然学习了DPC,没有应用肯定不行,我们做几个实验体验一下DPC。我们新建一个驱动项目,怎么配置我就不说了,代码如下:

#include <ntifs.h>
#include <ntddk.h>  

KDPC dpc = { 0 };

VOID DPCRoutine(_In_ struct _KDPC* Dpc, _In_opt_ PVOID DeferredContext,
 _In_opt_ PVOID SystemArgument1, _In_opt_ PVOID SystemArgument2)
{
    DbgPrint("DPC Running……\n");
}

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    DbgPrint("Unloaded Successfully!");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Loaded Successfully!");
    DriverObject->DriverUnload = UnloadDriver;

    KeInitializeDpc(&dpc, DPCRoutine, NULL);
    KeInsertQueueDpc(&dpc, NULL, NULL);

    return STATUS_SUCCESS;
}

  编译然后加载后,就会出现如下结果:

  那么继续搞得高级点,不是驱动或者用户创建的DPC支持定时器吗?我们来搞一下:

#include <ntifs.h>
#include <ntddk.h>  

KDPC dpc = { 0 };
KTIMER timer = { 0 };
LARGE_INTEGER duringtime = { 0 };

VOID DPCRoutine(_In_ struct _KDPC* Dpc, _In_opt_ PVOID DeferredContext,
 _In_opt_ PVOID SystemArgument1, _In_opt_ PVOID SystemArgument2)
{
    DbgPrint("DPC Running……\n");
    KeSetTimer(&timer, duringtime, &dpc);
}

VOID UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    KeCancelTimer(&timer);
    DbgPrint("Unloaded Successfully!");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Loaded Successfully!");
    DriverObject->DriverUnload = UnloadDriver;

    KeInitializeTimer(&timer);
    KeInitializeDpc(&dpc, DPCRoutine, NULL);    
    duringtime.QuadPart = -30 * 1000 * 1000; //负号表示相对时间,此处间隔为3秒
    KeSetTimer(&timer, duringtime, &dpc);

    return STATUS_SUCCESS;
}

  实现的效果如下:

  上面实现的就是DPC定时器。

Windows 架构

  学习系统内核,最好了解一些该系统的系统架构,示意图如下,来自潘爱民的《Windows内核原理与实现》:

  如有兴趣了解其细节,请自行翻找,注意该书是基于WRK的,里面的结构体相关或者代码相关的请以逆向结果为准。

下一篇

  羽夏看Win系统内核——句柄表篇

posted @ 2022-03-04 11:46  寂静的羽夏  阅读(686)  评论(0编辑  收藏  举报