控制程式碼表篇——總結與提升

寂靜的羽夏發表於2022-02-05

寫在前面

  此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。

  看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。


? 華麗的分割線 ?


小節

  本篇的知識挺簡單的,就介紹了兩個表:程式控制程式碼表和全域性控制程式碼表。程式控制程式碼表歸屬程式私有,每一個程式一個控制程式碼表。上一篇的課後思考題答案先不急的給你,下面我們來擴充套件一下控制程式碼表的相關知識,你在來看看你寫的程式碼。

控制程式碼表結構體擴充套件

  如下是WRK裡面的控制程式碼表結構,重點是看註釋,介紹擴充套件一下我講解基礎沒講到知識點:

typedef struct _HANDLE_TABLE {

    //
    //  A pointer to the top level handle table tree node.
    //

    ULONG_PTR TableCode;

    //
    //  The process who is being charged quota for this handle table and a
    //  unique process id to use in our callbacks
    //

    struct _EPROCESS *QuotaProcess;
    HANDLE UniqueProcessId;


    //
    // These locks are used for table expansion and preventing the A-B-A problem
    // on handle allocate.
    //

#define HANDLE_TABLE_LOCKS 4

    EX_PUSH_LOCK HandleTableLock[HANDLE_TABLE_LOCKS];

    //
    //  The list of global handle tables.  This field is protected by a global
    //  lock.
    //

    LIST_ENTRY HandleTableList;

    //
    // Define a field to block on if a handle is found locked.
    //
    EX_PUSH_LOCK HandleContentionEvent;

    //
    // Debug info. Only allocated if we are debugging handles
    //
    PHANDLE_TRACE_DEBUG_INFO DebugInfo;

    //
    //  The number of pages for additional info.
    //  This counter is used to improve the performance
    //  in ExGetHandleInfo
    //
    LONG ExtraInfoPages;

    //
    //  This is a singly linked list of free table entries.  We don't actually
    //  use pointers, but have each store the index of the next free entry
    //  in the list.  The list is managed as a lifo list.  We also keep track
    //  of the next index that we have to allocate pool to hold.
    //

    ULONG FirstFree;

    //
    // We free handles to this list when handle debugging is on or if we see
    // that a thread has this handles bucket lock held. The allows us to delay reuse
    // of handles to get a better chance of catching offenders
    //

    ULONG LastFree;

    //
    // This is the next handle index needing a pool allocation. Its also used as a bound
    // for good handles.
    //

    ULONG NextHandleNeedingPool;

    //
    //  The number of handle table entries in use.
    //

    LONG HandleCount;

    //
    // Define a flags field
    //
    union {
        ULONG Flags;

        //
        // For optimization we reuse handle values quickly. This can be a problem for
        // some usages of handles and makes debugging a little harder. If this
        // bit is set then we always use FIFO handle allocation.
        //
        BOOLEAN StrictFIFO : 1;
    };

} HANDLE_TABLE, *PHANDLE_TABLE;

  然後我再把WinDbg獲取的結構體拿出來:

kd> dt _HANDLE_TABLE
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : Uint4B
   +0x004 QuotaProcess     : Ptr32 _EPROCESS
   +0x008 UniqueProcessId  : Ptr32 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO
   +0x02c ExtraInfoPages   : Int4B
   +0x030 FirstFree        : Uint4B
   +0x034 LastFree         : Uint4B
   +0x038 NextHandleNeedingPool : Uint4B
   +0x03c HandleCount      : Int4B
   +0x040 Flags            : Uint4B
   +0x040 StrictFIFO       : Pos 0, 1 Bit

  可以說是一模一樣的,編制作業系統時為了保持相容性,結構體的每一個成員的含義是一樣的。
  有沒有注意到HandleCount這個成員,這個是我們做思考題的一個坑,就是我知道遍歷有效控制程式碼的次數,就得需要這個值,其實也可以不用,因為控制程式碼是按物理頁進行的。當然,如何獲取一個程式的控制程式碼數可以通過ObGetProcessHandleCount這個函式。這個函式是未匯出的,如下是我整理好的虛擬碼:

int __stdcall ObGetProcessHandleCount(PEPROCESS Process)
{
  _HANDLE_TABLE *objtable; // eax
  int handleCount; // esi

  objtable = ObReferenceProcessHandleTable(Process);
  if ( !objtable )
    return 0;
  handleCount = objtable->HandleCount;
  ObDereferenceProcessHandleTable(Process);
  return handleCount;
}

_HANDLE_TABLE *__stdcall ObReferenceProcessHandleTable(_EPROCESS *Process)
{
  _HANDLE_TABLE *objtable; // edi

  objtable = 0;
  if ( ExAcquireRundownProtection(&Process->RundownProtect) )
  {
    objtable = Process->ObjectTable;
    if ( !objtable )
      ExReleaseRundownProtection(&Process->RundownProtect);
  }
  return objtable;
}

void __stdcall ObDereferenceProcessHandleTable(_EPROCESS *eprocess)
{
  ExReleaseRundownProtection(&eprocess->RundownProtect);
}

ObReferenceObjectByHandle 淺析

  我們來看看作業系統是如何使用控制程式碼的,定位到我們熟悉的函式ObReferenceObjectByHandle

NTSTATUS __stdcall ObReferenceObjectByHandle(HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, PVOID *Object, POBJECT_HANDLE_INFORMATION HandleInformation)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  currentThread = KeGetCurrentThread();
  attachedProcess = currentThread;
  *Object = 0;
  if ( Handle >= 0 )
  {
    objHandleTable = currentThread->Tcb.ApcState.Process->ObjectTable;
  }
  else
  {
    if ( Handle == -1 )
    {
      if ( ObjectType == PsProcessType || !ObjectType )
      {
        attachedProcess = currentThread->Tcb.ApcState.Process;
        if ( (~attachedProcess->GrantedAccess & DesiredAccess) == 0 || !AccessMode )
        {
          if ( HandleInformation )
          {
            HandleInformation->GrantedAccess = attachedProcess->GrantedAccess;
            HandleInformation->HandleAttributes = 0;
          }
          if ( ObpTraceEnabled )
            ObpPushStackInfo(&attachedProcess[-1u].584, 1);
          ++attachedProcess[-1u].Flags;
LABEL_12:
          *Object = attachedProcess;
          return 0;
        }
        return STATUS_ACCESS_DENIED;
      }
      return STATUS_OBJECT_TYPE_MISMATCH;
    }
    if ( Handle == -2 )
    {
      if ( ObjectType == PsThreadType || !ObjectType )
      {
        if ( (~currentThread->GrantedAccess & DesiredAccess) == 0 || !AccessMode )
        {
          if ( HandleInformation )
          {
            HandleInformation->GrantedAccess = currentThread->GrantedAccess;
            HandleInformation->HandleAttributes = 0;
          }
          if ( ObpTraceEnabled )
            ObpPushStackInfo(&currentThread[-1].ReadClusterSize, 1);
          ++attachedProcess[-1].Flags;
          goto LABEL_12;
        }
        return STATUS_ACCESS_DENIED;
      }
      return STATUS_OBJECT_TYPE_MISMATCH;
    }
    if ( AccessMode )
      return STATUS_INVALID_HANDLE;
    Handle = (Handle ^ 0x80000000);
    objHandleTable = ::ObpKernelHandleTable;
  }
  --attachedProcess->WorkingSetLock.Contention;
  ObpKernelHandleTable = objHandleTable;
  handleEntry = ExMapHandleToPointerEx(objHandleTable, Handle, AccessMode);
  if ( handleEntry )
  {
    v12 = (handleEntry->InfoTable & 0xFFFFFFF8);
    BackTraceHash = v12;
    if ( v12[1].Object == ObjectType || !ObjectType )
    {
      if ( (NtGlobalFlag & 0x2000) != 0 )
      {
        if ( AccessMode || HandleInformation )
        {
          v14 = ObpTranslateGrantedAccessIndex(handleEntry->GrantedAccessIndex);
          v12 = BackTraceHash;
          v15 = v14;
        }
        else
        {
          v15 = Handle;
        }
      }
      else
      {
        v15 = ~ObpAccessProtectCloseBit & handleEntry->GrantedAccess;
      }
      if ( (~v15 & DesiredAccess) == 0 || !AccessMode )
      {
        if ( *(ObpKernelHandleTable + 44) )
        {
          v16 = ExpGetHandleInfo(ObpKernelHandleTable, Handle, 1);
          v12 = BackTraceHash;
          ObjectTypea = v16;
        }
        else
        {
          ObjectTypea = 0;
        }
        if ( HandleInformation )
        {
          HandleInformation->GrantedAccess = v15;
          if ( (ObpAccessProtectCloseBit & handleEntry->GrantedAccess) != 0 )
            v17 = handleEntry->ObAttributes & 6 | 1;
          else
            v17 = handleEntry->ObAttributes & 6;
          HandleInformation->HandleAttributes = v17;
        }
        if ( (handleEntry->Object & 4) != 0
          && ObjectTypea
          && ObjectTypea->Mutex.SystemResourcesList.Flink
          && DesiredAccess
          && AccessMode )
        {
          ObpAuditObjectAccess(Handle, ObjectTypea, v12[1].ObAttributes + 64, DesiredAccess);
          v12 = BackTraceHash;
        }
        if ( ObpTraceEnabled )
          ObpPushStackInfo(v12, 1);
        ++BackTraceHash->ObAttributes;
        ExUnlockHandleTableEntry(ObpKernelHandleTable, handleEntry);
        v19 = attachedProcess->WorkingSetLock.Contention++ == 4294967295;
        if ( v19 && attachedProcess->Pcb.ActiveProcessors != &attachedProcess->Pcb.ActiveProcessors )
        {
          LOBYTE(v18) = 1;
          BYTE1(attachedProcess->Pcb.SwapListEntry.Next) = 1;
          HalRequestSoftwareInterrupt(v18);
        }
        *Object = &BackTraceHash[3];
        return 0;
      }
      v13 = STATUS_ACCESS_DENIED;
    }
    else
    {
      v13 = STATUS_OBJECT_TYPE_MISMATCH;
    }
    ExUnlockHandleTableEntry(ObpKernelHandleTable, handleEntry);
  }
  else
  {
    v13 = STATUS_INVALID_HANDLE;
  }
  v19 = attachedProcess->WorkingSetLock.Contention++ == 4294967295;
  if ( v19 && attachedProcess->Pcb.ActiveProcessors != &attachedProcess->Pcb.ActiveProcessors )
  {
    LOBYTE(v10) = 1;
    BYTE1(attachedProcess->Pcb.SwapListEntry.Next) = 1;
    HalRequestSoftwareInterrupt(v10);
  }
  return v13;
}

  當然上面的翻譯的虛擬碼也是不準確的,有些函式我不熟悉,逆向不太明白,如下是我逆向的結果,由於彙編太長了,請點選摺疊觀看:

? 點選檢視程式碼 ?
; NTSTATUS __stdcall ObReferenceObjectByHandle(HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, PVOID *Object, POBJECT_HANDLE_INFORMATION HandleInformation)
                public _ObReferenceObjectByHandle@24
_ObReferenceObjectByHandle@24 proc near ; CODE XREF: IopUnloadDriver(x,x)+1B8↑p
                                        ; MmCreateSection(x,x,x,x,x,x,x,x)+221↑p ...

ObpKernelHandleTable= dword ptr -8
OBJHeader       = dword ptr -4
Handle          = dword ptr  8
DesiredAccess   = dword ptr  0Ch
ObjectType      = dword ptr  10h
AccessMode      = byte ptr  14h
Object          = dword ptr  18h
HandleInformation= dword ptr  1Ch

                mov     edi, edi
                push    ebp
                mov     ebp, esp
                push    ecx
                push    ecx
                push    ebx
                push    esi
                push    edi
                mov     eax, large fs:_KPCR.PrcbData.CurrentThread
                mov     edi, [ebp+Object]
                xor     ebx, ebx        ; EBX = 0
                cmp     [ebp+Handle], ebx ; CMP Handle , 0
                mov     esi, eax        ; ESI = CurrentThread
                mov     [edi], ebx
                jge     NotFakeHandle   ; >=0 JMP
                cmp     [ebp+Handle], -1
                jnz     short CheckIsThreadFake ; != -1 JMP
                mov     eax, [ebp+ObjectType]
                cmp     eax, _PsProcessType ; 判斷是否為 Process 型別
                jz      short CurrentProcessHandle ; 是的話跳走
                cmp     eax, ebx
                jnz     short loc_4D9B4F

CurrentProcessHandle:                   ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+31↑j
                mov     esi, [esi+_KTHREAD.ApcState.Process]
                mov     ecx, [esi+_EPROCESS.GrantedAccess]
                mov     eax, ecx        ; 至此 esi = AttachedProcess , eax = ecx = GrantedAccess
                not     eax             ; 按位取反 Access
                test    [ebp+DesiredAccess], eax ; 如果是0的話,說明一致
                jz      short DesiredIsGranted ; 一致的話跳走
                cmp     [ebp+AccessMode], KernelMode
                jnz     short IsUserMode

DesiredIsGranted:                       ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+47↑j
                mov     eax, [ebp+HandleInformation]
                cmp     eax, ebx        ; CMP HandleInformation , 0
                lea     edx, [esi-18h]  ; _OBJECT_HEADER
                mov     [ebp+OBJHeader], edx
                jz      short HandleInformationIsNULL
                mov     [eax+_OBJECT_HANDLE_INFORMATION.GrantedAccess], ecx
                mov     [eax+_OBJECT_HANDLE_INFORMATION.HandleAttributes], ebx

HandleInformationIsNULL:                ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+5A↑j
                cmp     _ObpTraceEnabled, 0
                jz      short ObpTraceEnabledIsFalse
                push    1               ; char
                push    edx             ; BackTraceHash
                call    _ObpPushStackInfo@8 ; ObpPushStackInfo(x,x)

ObpTraceEnabledIsFalse:                 ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+68↑j
                mov     eax, 1
                mov     ecx, [ebp+OBJHeader]
                xadd    [ecx+_OBJECT_HEADER.PointerCount], eax ; 引用計數加1

loc_4D9B33:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+F0↓j
                mov     [edi], esi
                jmp     loc_4D9D25
; ---------------------------------------------------------------------------

CheckIsThreadFake:                      ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+26↑j
                cmp     [ebp+Handle], -2
                jnz     short NotCurrentThread ; Handle != -2 跳走
                mov     eax, [ebp+ObjectType]
                cmp     eax, _PsThreadType
                jz      short IsThreadType
                cmp     eax, ebx        ; cmp eax , 0
                jz      short IsThreadType

loc_4D9B4F:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+35↑j
                mov     eax, STATUS_OBJECT_TYPE_MISMATCH
                jmp     ProcEnding
; ---------------------------------------------------------------------------

IsThreadType:                           ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+93↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+97↑j
                mov     ecx, [esi+_ETHREAD.GrantedAccess]
                mov     eax, ecx
                not     eax
                test    [ebp+DesiredAccess], eax
                jz      short DesiredIsGranted_0
                cmp     [ebp+AccessMode], KernelMode
                jz      short DesiredIsGranted_0

IsUserMode:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+4D↑j
                mov     eax, STATUS_ACCESS_DENIED
                jmp     ProcEnding
; ---------------------------------------------------------------------------

DesiredIsGranted_0:                     ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+B0↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+B6↑j
                mov     eax, [ebp+HandleInformation]
                cmp     eax, ebx        ; CMP HandleInformation , 0
                lea     edx, [esi-18h]  ; _OBJECT_HEADER
                mov     [ebp+OBJHeader], edx
                jz      short loc_4D9B8A
                mov     [eax+_OBJECT_HANDLE_INFORMATION.GrantedAccess], ecx
                mov     [eax+_OBJECT_HANDLE_INFORMATION.HandleAttributes], ebx

loc_4D9B8A:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+CD↑j
                cmp     _ObpTraceEnabled, 0
                jz      short ObpTraceEnabledIsFalse_0
                push    1               ; char
                push    edx             ; BackTraceHash
                call    _ObpPushStackInfo@8 ; ObpPushStackInfo(x,x)

ObpTraceEnabledIsFalse_0:               ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+DB↑j
                mov     eax, 1
                mov     ecx, [ebp+OBJHeader]
                xadd    [ecx+_OBJECT_HEADER.PointerCount], eax ; 引用計數加1
                jmp     short loc_4D9B33
; ---------------------------------------------------------------------------

NotCurrentThread:                       ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+88↑j
                cmp     [ebp+AccessMode], KernelMode
                jnz     short IsUserMode_1
                xor     [ebp+Handle], 80000000h
                mov     eax, _ObpKernelHandleTable
                jmp     short loc_4D9BCF
; ---------------------------------------------------------------------------

IsUserMode_1:                           ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+F6↑j
                mov     eax, STATUS_INVALID_HANDLE
                jmp     ProcEnding
; ---------------------------------------------------------------------------

NotFakeHandle:                          ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1C↑j
                mov     eax, [esi+_KTHREAD.ApcState.Process]
                mov     eax, [eax+_EPROCESS.ObjectTable]

loc_4D9BCF:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+104↑j
                push    dword ptr [ebp+AccessMode] ; char
                dec     [esi+_ETHREAD.Tcb.KernelApcDisable]
                push    [ebp+Handle]    ; BugCheckParameter1
                mov     [ebp+ObpKernelHandleTable], eax
                push    eax             ; BugCheckParameter2
                call    _ExMapHandleToPointerEx@12 ; ExMapHandleToPointerEx(x,x,x)
                mov     edi, eax
                cmp     edi, ebx
                jz      InvalidHandleProc
                mov     edx, [edi+_HANDLE_TABLE_ENTRY.___u0.InfoTable]
                mov     eax, [ebp+ObjectType]
                and     edx, 0FFFFFFF8h
                cmp     [edx+8], eax
                mov     [ebp+OBJHeader], edx
                jz      short loc_4D9C09
                cmp     eax, ebx
                jz      short loc_4D9C09
                mov     ebx, STATUS_OBJECT_TYPE_MISMATCH
                jmp     short ErrorProc
; ---------------------------------------------------------------------------

loc_4D9C09:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+146↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+14A↑j
                test    byte ptr _NtGlobalFlag+1, 20h
                jz      short loc_4D9C30
                cmp     [ebp+AccessMode], KernelMode
                jnz     short IsUserMode_0
                cmp     [ebp+HandleInformation], ebx
                jz      short loc_4D9C3E

IsUserMode_0:                           ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+160↑j
                xor     eax, eax
                mov     ax, [edi+4]
                push    eax
                call    _ObpTranslateGrantedAccessIndex@4 ; ObpTranslateGrantedAccessIndex(x)
                mov     edx, [ebp+OBJHeader]
                mov     ebx, eax
                jmp     short loc_4D9C41
; ---------------------------------------------------------------------------

loc_4D9C30:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+15A↑j
                mov     eax, _ObpAccessProtectCloseBit
                mov     ebx, [edi+4]
                not     eax
                and     ebx, eax
                jmp     short loc_4D9C41
; ---------------------------------------------------------------------------

loc_4D9C3E:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+165↑j
                mov     ebx, [ebp+Handle]

loc_4D9C41:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+178↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+186↑j
                mov     eax, ebx
                not     eax
                test    [ebp+DesiredAccess], eax
                jz      short loc_4D9C63
                cmp     [ebp+AccessMode], 0
                jz      short loc_4D9C63
                mov     ebx, STATUS_ACCESS_DENIED

ErrorProc:                              ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+151↑j
                push    edi             ; BugCheckParameter2
                push    [ebp+ObpKernelHandleTable] ; int
                call    _ExUnlockHandleTableEntry@8 ; ExUnlockHandleTableEntry(x,x)
                jmp     loc_4D9D2E
; ---------------------------------------------------------------------------

loc_4D9C63:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+192↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+198↑j
                mov     eax, [ebp+ObpKernelHandleTable]
                cmp     dword ptr [eax+2Ch], 0
                jz      short loc_4D9C7F
                push    1
                push    [ebp+Handle]
                push    eax
                call    _ExpGetHandleInfo@12 ; ExpGetHandleInfo(x,x,x)
                mov     edx, [ebp+OBJHeader]
                mov     [ebp+ObjectType], eax
                jmp     short loc_4D9C83
; ---------------------------------------------------------------------------

loc_4D9C7F:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1B4↑j
                and     [ebp+ObjectType], 0

loc_4D9C83:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1C7↑j
                mov     eax, [ebp+HandleInformation]
                test    eax, eax
                jz      short loc_4D9CA7
                mov     [eax+4], ebx
                mov     ecx, _ObpAccessProtectCloseBit
                test    [edi+4], ecx
                mov     ecx, [edi]
                jz      short loc_4D9CA2
                and     ecx, 6
                or      ecx, 1
                jmp     short loc_4D9CA5
; ---------------------------------------------------------------------------

loc_4D9CA2:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1E2↑j
                and     ecx, 6

loc_4D9CA5:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1EA↑j
                mov     [eax], ecx

loc_4D9CA7:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1D2↑j
                test    byte ptr [edi], 4
                jz      short loc_4D9CDA
                mov     eax, [ebp+ObjectType]
                test    eax, eax
                jz      short loc_4D9CDA
                cmp     dword ptr [eax], 0
                jz      short loc_4D9CDA
                cmp     [ebp+DesiredAccess], 0
                jz      short loc_4D9CDA
                cmp     [ebp+AccessMode], 0
                jz      short loc_4D9CDA
                push    [ebp+DesiredAccess]
                mov     ecx, [edx+8]
                add     ecx, 40h ; '@'
                push    ecx
                push    eax
                push    [ebp+Handle]
                call    _ObpAuditObjectAccess@16 ; ObpAuditObjectAccess(x,x,x,x)
                mov     edx, [ebp+OBJHeader]

loc_4D9CDA:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1F4↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+1FB↑j ...
                cmp     _ObpTraceEnabled, 0
                jz      short loc_4D9CEB
                push    1               ; char
                push    edx             ; BackTraceHash
                call    _ObpPushStackInfo@8 ; ObpPushStackInfo(x,x)

loc_4D9CEB:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+22B↑j
                mov     eax, 1
                mov     ecx, [ebp+OBJHeader]
                xadd    [ecx+_OBJECT_HANDLE_INFORMATION.HandleAttributes], eax
                push    edi             ; BugCheckParameter2
                push    [ebp+ObpKernelHandleTable] ; int
                call    _ExUnlockHandleTableEntry@8 ; ExUnlockHandleTableEntry(x,x)
                inc     [esi+_ETHREAD.Tcb.KernelApcDisable]
                jnz     short MovObject
                lea     eax, [esi+_ETHREAD.Tcb.ApcState]
                cmp     [eax+_KAPC_STATE.ApcListHead.Flink], eax
                jz      short MovObject
                mov     cl, 1
                mov     byte ptr [esi+49h], 1
                call    ds:__imp_@HalRequestSoftwareInterrupt@4 ; HalRequestSoftwareInterrupt(x)

MovObject:                              ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+24F↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+256↑j
                mov     eax, [ebp+OBJHeader]
                mov     ecx, [ebp+Object]
                add     eax, 18h
                mov     [ecx], eax

loc_4D9D25:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+7F↑j
                xor     eax, eax
                jmp     short ProcEnding
; ---------------------------------------------------------------------------

InvalidHandleProc:                      ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+132↑j
                mov     ebx, STATUS_INVALID_HANDLE

loc_4D9D2E:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1A8↑j
                inc     [esi+_ETHREAD.Tcb.KernelApcDisable]
                jnz     short loc_4D9D49
                lea     eax, [esi+_ETHREAD.Tcb.ApcState]
                cmp     [eax+_KAPC_STATE.ApcListHead.Flink], eax
                jz      short loc_4D9D49
                mov     cl, 1
                mov     [esi+_ETHREAD.Tcb.ApcState.KernelApcPending], 1
                call    ds:__imp_@HalRequestSoftwareInterrupt@4 ; HalRequestSoftwareInterrupt(x)

loc_4D9D49:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+27E↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+285↑j
                mov     eax, ebx

ProcEnding:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+9E↑j
                                        ; ObReferenceObjectByHandle(x,x,x,x,x,x)+BD↑j ...
                pop     edi
                pop     esi
                pop     ebx
                leave
                retn    18h
_ObReferenceObjectByHandle@24 endp

  為了方便講解,我就用虛擬碼,具體細節請參考我的彙編註釋。我們把注意力放到如下程式碼上:

  if ( Handle == -1 )
    {
      if ( ObjectType == PsProcessType || !ObjectType )
      {
        attachedProcess = currentThread->Tcb.ApcState.Process;
        if ( (~attachedProcess->GrantedAccess & DesiredAccess) == 0 || !AccessMode )
        {
          if ( HandleInformation )
          {
            HandleInformation->GrantedAccess = attachedProcess->GrantedAccess;
            HandleInformation->HandleAttributes = 0;
          }
          if ( ObpTraceEnabled )
            ObpPushStackInfo(&attachedProcess[-1u].584, 1);
          ++attachedProcess[-1u].Flags;
LABEL_12:
          *Object = attachedProcess;
          return 0;
        }
        return STATUS_ACCESS_DENIED;
      }
      return STATUS_OBJECT_TYPE_MISMATCH;
    }
    if ( Handle == -2 )
    {
      if ( ObjectType == PsThreadType || !ObjectType )
      {
        if ( (~currentThread->GrantedAccess & DesiredAccess) == 0 || !AccessMode )
        {
          if ( HandleInformation )
          {
            HandleInformation->GrantedAccess = currentThread->GrantedAccess;
            HandleInformation->HandleAttributes = 0;
          }
          if ( ObpTraceEnabled )
            ObpPushStackInfo(&currentThread[-1].ReadClusterSize, 1);
          ++attachedProcess[-1].Flags;
          goto LABEL_12;
        }
        return STATUS_ACCESS_DENIED;
      }
      return STATUS_OBJECT_TYPE_MISMATCH;
    }

  看到函式會提前判斷控制程式碼的值是否為負數,再判斷是否是-1還是-2。通過虛擬碼可知,如果是-1就是獲取了當前程式的結構體,如果是-2就會找當前執行緒的結構體,這個值被稱之為偽控制程式碼。
  我敢說只要你在三環與程式執行緒相關打交道的時候,肯定用到過偽控制程式碼。用沒用過GetCurrentProcessGetCurrentThread這倆函式?

; HANDLE __stdcall GetCurrentProcess()
                public _GetCurrentProcess@0
_GetCurrentProcess@0 proc near          ; CODE XREF: UnhandledExceptionFilter(x)+81↓p
                                        ; UnhandledExceptionFilter(x)+69E↓p ...
                or      eax, 0FFFFFFFFh
                retn
_GetCurrentProcess@0 endp

; HANDLE __stdcall GetCurrentThread()
                public _GetCurrentThread@0
_GetCurrentThread@0 proc near           ; DATA XREF: .text:off_7C802654↑o
                push    0FFFFFFFEh
                pop     eax
                retn
_GetCurrentThread@0 endp

  上面的函式是不是挺弱智的,為啥我必須呼叫這樣的函式獲取偽控制程式碼呢,直接一個巨集定義不就行了?為什麼不能用CloseHandle關閉偽控制程式碼是不是明白了?

  好,下面我繼續接著擴充套件。當我們的控制程式碼為負值時,作業系統就會使用ObpKernelHandleTable這個表,這個是通過逆向得到的結果。我們使用WinDbg看看裡面有什麼東西:

kd> dd ObpKernelHandleTable
8055a7d8  e1001cc8 00000000 00000000 00000000
8055a7e8  00000000 00000000 00000000 00000000
8055a7f8  00000000 00000000 00000000 00000000
8055a808  00000000 00000000 00000000 00000000
8055a818  00000000 00000000 00000000 00000000
8055a828  00000000 00000000 00000000 00000000
8055a838  00000000 00000000 00000000 00000000
8055a848  00000000 00000000 00000000 00000000
kd> dt _HANDLE_TABLE e1001cc8
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : 0xe1002000
   +0x004 QuotaProcess     : (null) 
   +0x008 UniqueProcessId  : 0x00000004 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY [ 0xe100f384 - 0x8055c448 ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : (null) 
   +0x02c ExtraInfoPages   : 0n0
   +0x030 FirstFree        : 0x64c
   +0x034 LastFree         : 0
   +0x038 NextHandleNeedingPool : 0x800
   +0x03c HandleCount      : 0n221
   +0x040 Flags            : 0
   +0x040 StrictFIFO       : 0y0
kd> dq 0xe1002000
ReadVirtual: e1002000 not properly sign extended
e1002000  fffffffe`00000000 001f0fff`89fb09e9
e1002010  00000000`89fb0329 000f003f`e13c9119
e1002020  00000000`e1011449 00020019`e13d6731
e1002030  00020019`e13d2791 00020019`e101f421
e1002040  0002001f`e13de479 00020019`e13c3079
e1002050  00020019`e13d1419 0002001f`e13bee51
e1002060  00020019`e13d84d1 001f0003`89fa7a11
e1002070  00000040`00000000 000000a8`00000000

  關於該函式的擴充套件,就這些。

控制程式碼表擴充套件

  為了方便大家進行學習控制程式碼表,我們給出一個小結論,我在WinDbg找到了一個程式的控制程式碼表,如下所示:

kd> dq 0xe10ec000
e10ec000  fffffffe`00000000 00000000`00000000
e10ec010  00000004`00000000 00000008`00000000
e10ec020  0000000c`00000000 00000010`00000000
e10ec030  00000014`00000000 00000018`00000000
e10ec040  0000001c`00000000 00000020`00000000
e10ec050  00000024`00000000 00000028`00000000
e10ec060  0000002c`00000000 00000030`00000000
e10ec070  00000034`00000000 00000038`00000000

  每一個QWORD,發現了什麼規律了嗎?每一個專案展示的前半部分是索引,後面就是我們對應的結構體地址,每一個索引都是4的倍數。
  其實每一個控制程式碼都是一個結構體,名為_HANDLE_TABLE_ENTRY,我們可以看一下它的結構:

kd> dt _HANDLE_TABLE_ENTRY 
nt!_HANDLE_TABLE_ENTRY
   +0x000 Object           : Ptr32 Void
   +0x000 ObAttributes     : Uint4B
   +0x000 InfoTable        : Ptr32 _HANDLE_TABLE_ENTRY_INFO
   +0x000 Value            : Uint4B
   +0x004 GrantedAccess    : Uint4B
   +0x004 GrantedAccessIndex : Uint2B
   +0x006 CreatorBackTraceIndex : Uint2B
   +0x004 NextFreeTableEntry : Int4B

  可以看出,這是個用共用體複雜巢狀出的結構體,我們可以看看WRK的定義:

typedef struct _HANDLE_TABLE_ENTRY {

    //
    //  The pointer to the object overloaded with three ob attributes bits in
    //  the lower order and the high bit to denote locked or unlocked entries
    //

    union {

        PVOID Object;

        ULONG ObAttributes;

        PHANDLE_TABLE_ENTRY_INFO InfoTable;

        ULONG_PTR Value;
    };

    //
    //  This field either contains the granted access mask for the handle or an
    //  ob variation that also stores the same information.  Or in the case of
    //  a free entry the field stores the index for the next free entry in the
    //  free list.  This is like a FAT chain, and is used instead of pointers
    //  to make table duplication easier, because the entries can just be
    //  copied without needing to modify pointers.
    //

    union {

        union {

            ACCESS_MASK GrantedAccess;

            struct {

                USHORT GrantedAccessIndex;
                USHORT CreatorBackTraceIndex;
            };
        };

        LONG NextFreeTableEntry;
    };

} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;

  目前掌握的細節已經在程式控制程式碼表裡解釋了,也就擴充套件這些。如想更好的掌握這些細節,推薦潘愛民的《Windows核心原理與實現》,注意這本書基於WRK,僅供參考。

PsLookupProcessByProcessId 淺析

  對於作業系統,它是如何實現通過控制程式碼查詢對應的結構體地址呢?我們可以通過以逆向PsLookupProcessByProcessId為例子進行:

NTSTATUS __stdcall PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process)
{
  _KTHREAD *v2; // esi
  struct _EPROCESS **v3; // eax
  int v4; // ecx
  ULONG_PTR v5; // ebx
  struct _EPROCESS *v6; // edi
  bool v7; // zf
  HANDLE ProcessIda; // [esp+10h] [ebp+8h]

  v2 = KeGetCurrentThread();
  --v2->KernelApcDisable;
  v3 = ExMapHandleToPointer(PspCidTable, ProcessId);
  v5 = v3;
  ProcessIda = STATUS_INVALID_PARAMETER;
  if ( v3 )
  {
    v6 = *v3;
    if ( (*v3)->Pcb.Header.Type == 3 && v6->GrantedAccess && ObReferenceObjectSafe(*v3) )
    {
      ProcessIda = 0;
      *Process = &v6->Pcb;
    }
    ExUnlockHandleTableEntry(PspCidTable, v5);
  }
  v7 = v2->KernelApcDisable++ == -1;
  if ( v7 && v2->ApcState.ApcListHead[0].Flink != &v2->ApcState )
  {
    LOBYTE(v4) = 1;
    v2->ApcState.KernelApcPending = 1;
    HalRequestSoftwareInterrupt(v4);
  }
  return ProcessIda;
}

  我們可以看出這個函式功能主要是由ExMapHandleToPointer實現的,點選去看看:

_EPROCESS *__stdcall ExMapHandleToPointer(int pspcidtable, int processid)
{
  _EPROCESS *eprocess; // eax
  _EPROCESS *eprocess_1; // esi
  int v5; // ebx
  _EPROCESS *v8; // eax
  int *v9; // esi
  ULONG v10; // eax
  int v11; // [esp+4h] [ebp-8h]
  _EPROCESS *v12; // [esp+8h] [ebp-4h]
  _DWORD *pspcidtablea; // [esp+14h] [ebp+8h]

  if ( (processid & 0x7FC) == 0 )
    return 0;
  eprocess = ExpLookupHandleTableEntry(pspcidtable, processid);
  eprocess_1 = eprocess;
  if ( eprocess )
  {
    v12 = eprocess;
    while ( 1 )
    {
      v5 = *&eprocess_1->Pcb.Header.Type;
      v11 = *&eprocess_1->Pcb.Header.Type;
      if ( (*&eprocess_1->Pcb.Header.Type & 1) != 0 )
      {
        _ECX = v12;
        _EDX = v5 - 1;
        __asm { cmpxchg [ecx], edx }
        if ( v11 == v5 )
          return eprocess_1;
      }
      else if ( !v5 )
      {
        break;
      }
      ExpBlockOnLockedHandleEntry(pspcidtable, eprocess_1);
    }
  }
  pspcidtablea = *(pspcidtable + 40);
  if ( pspcidtablea )
  {
    v8 = (*pspcidtablea)++;
    v9 = &pspcidtablea[20 * ((v8 + 1) & 0xFFF) + 1];
    *v9 = *&KeGetCurrentThread()[1].DebugActive;
    v9[2] = processid;
    v9[3] = 3;
    v10 = RtlWalkFrameChain(&pspcidtablea[20 * ((v8 + 1) & 0xFFF) + 5], 0x10u, 0);
    RtlWalkFrameChain(&v9[v10 + 4], 16 - v10, 1u);
  }
  return 0;
}

  這個函式是由ExpLookupHandleTableEntry實現的,繼續:

unsigned int __stdcall ExpLookupHandleTableEntry(_HANDLE_TABLE *pspcidtable, int processid)
{
  unsigned int index; // eax
  int pageCount; // ecx
  _HANDLE_TABLE *tablebase; // esi
  int base; // ecx
  unsigned int processindex; // [esp+Ch] [ebp+Ch]

  processindex = processid & 0xFFFFFFFC;
  index = processindex >> 2;
  if ( processindex >= pspcidtable->NextHandleNeedingPool )
    return 0;
  pageCount = pspcidtable->TableCode & 3;
  tablebase = (pspcidtable->TableCode & 0xFFFFFFFC);
  if ( !pageCount )
    return tablebase + 8 * index;
  if ( pageCount == 1 )
  {
    base = *(&tablebase->TableCode + (processindex >> 11));
  }
  else
  {
    index = (processindex >> 2) - (processindex >> 21 << 19);
    base = *(*(&tablebase->TableCode + (processindex >> 21)) + 4 * (index >> 9));
  }
  return base + 8 * (index & 0x1FF);
}

  這個就是作業系統實現的偽C程式碼了,上面的pageCount就是控制程式碼表的級數,我們重點分析一下它是怎樣查詢的:

pageCount 值為 0

  tablebase就是我們的真正的控制程式碼表專案的地址,這個很簡單,正如其實現:tablebase + 8 * index
  你可能會有疑問,為什麼有這條程式碼:processindex = processid & 0xFFFFFFFC;這個程式碼就是去掉餘數,保證這個就是4的倍數。processindex >> 2就是我們之前所謂的CID / 4。剩下的應該沒有理解上的難度了。

pageCount 值為 1

  我們之前介紹過控制程式碼表是按物理頁的,如果是一級,那麼第一層一個專案可以儲存4 * 1024 / 8個控制程式碼,也就是 29,由於還要除以4,所以就是 211,我們的Base算出來了,那麼真正的index還沒有計算,但是作業系統是這樣算的:base + 8 * (index & 0x1FF)
  為什麼index & 0x1FF就是我們的真正的索引呢?0x200十進位制為512,也就是級數為1的一個專案儲存的控制程式碼數,我只需取出餘數即可,由於這個是二進位制,除數又是2,我們就可以用與運算進行替代。

pageCount 值為 2

  這個更復雜一些,但思想是統一的。如果值為2,第一層一個專案儲存的專案為4096 / 4 * 4096 / 8,也就是 219,第二層一個專案就是29,第一層的base就是*(&tablebase->TableCode + (processindex >> 21),只需要再找第二層就是真正的Base了,也就是所謂的4 * (index >> 9)偏移。
  剩下就是Index了,是不是對於這個程式碼非常懵:(processindex >> 2) - (processindex >> 21 << 19)
  上面的就是為了重用上一種情況的公式,把它轉換到第二層,作用就是看看第一層(級數為2的層數)剝削後剩下的控制程式碼個數。如果讀懂這一塊,就沒問題了,如果實在看不懂,我換一種寫法:index - index >> 19 << 19,這樣是不是明白了?

遍歷全域性控制程式碼表練習參考

  有了上面一大波鋪墊,那麼我們就開始了寫上一篇的思考題參考了:

? 點選檢視程式碼 ?
#include <ntddk.h>  

typedef struct _HANDLE_TABLE {
    ULONG_PTR TableCode;
    struct _EPROCESS* QuotaProcess;
    HANDLE UniqueProcessId;

#define HANDLE_TABLE_LOCKS 4

    EX_PUSH_LOCK HandleTableLock[HANDLE_TABLE_LOCKS];
    LIST_ENTRY HandleTableList;
    EX_PUSH_LOCK HandleContentionEvent;
    int* DebugInfo;
    LONG ExtraInfoPages;
    ULONG FirstFree;
    ULONG LastFree;
    ULONG NextHandleNeedingPool;
    LONG HandleCount;
    union {
        ULONG Flags;
        BOOLEAN StrictFIFO : 1;
    };

} HANDLE_TABLE, * PHANDLE_TABLE;

unsigned int* __stdcall ExpLookupHandleTableEntry(struct _HANDLE_TABLE* pspcidtable, int cid)
{
    unsigned int index;
    int pageCount; 
    int tablebase; 
    int base; 
    unsigned int processindex; 

    processindex = cid & 0xFFFFFFFC;
    index = processindex >> 2;
    if (processindex >= pspcidtable->NextHandleNeedingPool)
        return 0;
    pageCount = pspcidtable->TableCode & 3;
    tablebase = (pspcidtable->TableCode & 0xFFFFFFFC);
    if (!pageCount)
        return *(int*)(tablebase + 8 * index);
    if (pageCount == 1)
    {
        base = *(int*)(tablebase + (processindex >> 11));
    }
    else
    {
        index = (processindex >> 2) - (processindex >> 21 << 19);
        base = *((char*)*(int*)(tablebase + (processindex >> 21)) + 4 * (index >> 9));
    }
    return *(int*)(base + 8 * (index & 0x1FF));
}

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

UNICODE_STRING process, thread;

void ShowInfo(int* TableCode, int cid)
{
    int* addr = ((int)ExpLookupHandleTableEntry(TableCode, cid) & ~3);
    if (addr && MmIsAddressValid(addr))
    {
        int* type = addr[-4];

        if (!type)
        {
            return;
        }

        PUNICODE_STRING str = &type[16];

        if (!RtlCompareUnicodeString(str, &process, FALSE))
        {
            UCHAR* imgname = &addr[93];
            DbgPrint("PID : %d , Type : %wZ , Name : %s\n", cid, str, imgname);
            return;
        }

        if (!RtlCompareUnicodeString(str, &thread, FALSE))
        {
            DbgPrint("TID : %d , Type : %wZ\n", cid, str);
            return;
        }
    }
}


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

    RtlInitUnicodeString(&process, L"Process");
    RtlInitUnicodeString(&thread, L"Thread");

    UNICODE_STRING PsLookupProcessThreadByCid;
    RtlInitUnicodeString(&PsLookupProcessThreadByCid, L"PsLookupProcessThreadByCid");
    UCHAR* f = MmGetSystemRoutineAddress(&PsLookupProcessThreadByCid);

    DWORD32* PspCidTable = *(DWORD32*)(f + 32);

    UINT32 TableCode = *PspCidTable;

    switch (TableCode & 3)
    {
        case 0:
            for (int i = 0; i < 512; i++)
            {
                ShowInfo(TableCode, 4 * i);
            }
            break;

        case 1:
            for (int i = 0; i < 512 * 1024; i++)
            {
                ShowInfo(TableCode, 4 * i);
            }
            break;
        case 2:
            for (int i = 0; i < 512 * 1024 * 1024; i++)
            {
                ShowInfo(TableCode, 4 * i);
            }
            break;
    }

    return STATUS_SUCCESS;
}

  效果如下圖所示:

控制程式碼表篇——總結與提升

  到最後,我們看看WRK是如何寫的ExpLookupHandleTableEntry程式碼:

? 點選檢視程式碼 ?
PHANDLE_TABLE_ENTRY
ExpLookupHandleTableEntry (
    IN PHANDLE_TABLE HandleTable,
    IN EXHANDLE tHandle
    )

/*++

Routine Description:

    This routine looks up and returns the table entry for the
    specified handle value.

Arguments:

    HandleTable - Supplies the handle table being queried

    tHandle - Supplies the handle value being queried

Return Value:

    Returns a pointer to the corresponding table entry for the input
        handle.  Or NULL if the handle value is invalid (i.e., too large
        for the tables current allocation.

--*/

{
    ULONG_PTR i,j,k;
    ULONG_PTR CapturedTable;
    ULONG TableLevel;
    PHANDLE_TABLE_ENTRY Entry = NULL;
    EXHANDLE Handle;

    PUCHAR TableLevel1;
    PUCHAR TableLevel2;
    PUCHAR TableLevel3;

    ULONG_PTR MaxHandle;

    PAGED_CODE();


    //
    // Extract the handle index
    //
    Handle = tHandle;

    Handle.TagBits = 0;

    MaxHandle = *(volatile ULONG *) &HandleTable->NextHandleNeedingPool;

    //
    // See if this can be a valid handle given the table levels.
    //
    if (Handle.Value >= MaxHandle) {
        return NULL;        
    }

    //
    // Now fetch the table address and level bits. We must preserve the
    // ordering here.
    //
    CapturedTable = *(volatile ULONG_PTR *) &HandleTable->TableCode;

    //
    //  we need to capture the current table. This routine is lock free
    //  so another thread may change the table at HandleTable->TableCode
    //

    TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
    CapturedTable = CapturedTable - TableLevel;

    //
    //  The lookup code depends on number of levels we have
    //

    switch (TableLevel) {
        
        case 0:
            
            //
            //  We have a simple index into the array, for a single level
            //  handle table
            //


            TableLevel1 = (PUCHAR) CapturedTable;

            //
            // The index for this level is already scaled by a factor of 4. Take advantage of this
            //

            Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[Handle.Value *
                                                       (sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];

            break;
        
        case 1:
            
            //
            //  we have a 2 level handle table. We need to get the upper index
            //  and lower index into the array
            //


            TableLevel2 = (PUCHAR) CapturedTable;

            i = Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);

            Handle.Value -= i;
            j = Handle.Value / ((LOWLEVEL_COUNT * HANDLE_VALUE_INC) / sizeof (PHANDLE_TABLE_ENTRY));

            TableLevel1 =  (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel2[j];
            Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[i * (sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];

            break;
        
        case 2:
            
            //
            //  We have here a three level handle table.
            //


            TableLevel3 = (PUCHAR) CapturedTable;

            i = Handle.Value  % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);

            Handle.Value -= i;

            k = Handle.Value / ((LOWLEVEL_COUNT * HANDLE_VALUE_INC) / sizeof (PHANDLE_TABLE_ENTRY));

            j = k % (MIDLEVEL_COUNT * sizeof (PHANDLE_TABLE_ENTRY));

            k -= j;

            k /= MIDLEVEL_COUNT;


            TableLevel2 = (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel3[k];
            TableLevel1 = (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel2[j];
            Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[i * (sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];

            break;

        default :
            _assume (0);
    }

    return Entry;
}

OBJECT_HEADER 擴充套件

  這裡我們來介紹一些該結構體的擴充套件知識,你可以通過這些內容運用於對抗技術。先看看其結構體:

kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int4B
   +0x004 HandleCount      : Int4B
   +0x004 NextToFree       : Ptr32 Void
   +0x008 Type             : Ptr32 _OBJECT_TYPE
   +0x00c NameInfoOffset   : UChar
   +0x00d HandleInfoOffset : UChar
   +0x00e QuotaInfoOffset  : UChar
   +0x00f Flags            : UChar
   +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : Ptr32 Void
   +0x014 SecurityDescriptor : Ptr32 Void
   +0x018 Body             : _QUAD

  我們把關注點放到Type中,之前我們也涉及過,用來判斷該物件體的型別,再看一看其結構:

kd> dt _OBJECT_TYPE
ntdll!_OBJECT_TYPE
   +0x000 Mutex            : _ERESOURCE
   +0x038 TypeList         : _LIST_ENTRY
   +0x040 Name             : _UNICODE_STRING
   +0x048 DefaultObject    : Ptr32 Void
   +0x04c Index            : Uint4B
   +0x050 TotalNumberOfObjects : Uint4B
   +0x054 TotalNumberOfHandles : Uint4B
   +0x058 HighWaterNumberOfObjects : Uint4B
   +0x05c HighWaterNumberOfHandles : Uint4B
   +0x060 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x0ac Key              : Uint4B
   +0x0b0 ObjectLocks      : [4] _ERESOURCE

  然後看到TypeInfo這個成員,這個是一個結構體,dt一下:

kd> dt _OBJECT_TYPE_INITIALIZER
ntdll!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : Uint2B
   +0x002 UseDefaultObject : UChar
   +0x003 CaseInsensitive  : UChar
   +0x004 InvalidAttributes : Uint4B
   +0x008 GenericMapping   : _GENERIC_MAPPING
   +0x018 ValidAccessMask  : Uint4B
   +0x01c SecurityRequired : UChar
   +0x01d MaintainHandleCount : UChar
   +0x01e MaintainTypeList : UChar
   +0x020 PoolType         : _POOL_TYPE
   +0x024 DefaultPagedPoolCharge : Uint4B
   +0x028 DefaultNonPagedPoolCharge : Uint4B
   +0x02c DumpProcedure    : Ptr32     void 
   +0x030 OpenProcedure    : Ptr32     long 
   +0x034 CloseProcedure   : Ptr32     void 
   +0x038 DeleteProcedure  : Ptr32     void 
   +0x03c ParseProcedure   : Ptr32     long 
   +0x040 SecurityProcedure : Ptr32     long 
   +0x044 QueryNameProcedure : Ptr32     long 
   +0x048 OkayToCloseProcedure : Ptr32     unsigned char 

  我們把注意力放到後面幾個成員,也就是用來回撥函式的地方:

   +0x02c DumpProcedure    : Ptr32     void 
   +0x030 OpenProcedure    : Ptr32     long 
   +0x034 CloseProcedure   : Ptr32     void 
   +0x038 DeleteProcedure  : Ptr32     void 
   +0x03c ParseProcedure   : Ptr32     long 
   +0x040 SecurityProcedure : Ptr32     long 
   +0x044 QueryNameProcedure : Ptr32     long 
   +0x048 OkayToCloseProcedure : Ptr32     unsigned char 

  如果我們把函式地址掛到上面,當呼叫相應的核心函式的時候,就就會回撥該函式,用WRK程式碼片段來進行示例:

VOID
ObpDecrementHandleCount (
    PEPROCESS Process,
    POBJECT_HEADER ObjectHeader,
    POBJECT_TYPE ObjectType,
    ACCESS_MASK GrantedAccess
    )
{
    POBJECT_HEADER_HANDLE_INFO HandleInfo;
    POBJECT_HANDLE_COUNT_DATABASE HandleCountDataBase;
    POBJECT_HANDLE_COUNT_ENTRY HandleCountEntry;
    PVOID Object;
    ULONG CountEntries;
    ULONG ProcessHandleCount;
    ULONG_PTR SystemHandleCount;
    LOGICAL HandleCountIsZero;

    PAGED_CODE();


    Object = (PVOID)&ObjectHeader->Body;

    ProcessHandleCount = 0;

    ObpLockObject( ObjectHeader );

    SystemHandleCount = ObjectHeader->HandleCount;

    HandleCountIsZero = ObpDecrHandleCount( ObjectHeader );

    if ( HandleCountIsZero &&
        (ObjectHeader->Flags & OB_FLAG_EXCLUSIVE_OBJECT)) {

        OBJECT_HEADER_TO_QUOTA_INFO_EXISTS( ObjectHeader )->ExclusiveProcess = NULL;
    }

    if (ObjectType->TypeInfo.MaintainHandleCount) {

        HandleInfo = OBJECT_HEADER_TO_HANDLE_INFO_EXISTS( ObjectHeader );

        if (ObjectHeader->Flags & OB_FLAG_SINGLE_HANDLE_ENTRY) {

            ASSERT(HandleInfo->SingleEntry.Process == Process);
            ASSERT(HandleInfo->SingleEntry.HandleCount > 0);

            ProcessHandleCount = HandleInfo->SingleEntry.HandleCount--;
            HandleCountEntry = &HandleInfo->SingleEntry;

        } else {

            HandleCountDataBase = HandleInfo->HandleCountDataBase;

            if (HandleCountDataBase != NULL) {

                CountEntries = HandleCountDataBase->CountEntries;
                HandleCountEntry = &HandleCountDataBase->HandleCountEntries[ 0 ];

                while (CountEntries) {

                    if ((HandleCountEntry->HandleCount != 0) &&
                        (HandleCountEntry->Process == Process)) {

                        ProcessHandleCount = HandleCountEntry->HandleCount--;

                        break;
                    }

                    HandleCountEntry++;
                    CountEntries--;
                }
            }
            else {
                HandleCountEntry = NULL;
            }
        }

        if (ProcessHandleCount == 1) {

            HandleCountEntry->Process = NULL;
            HandleCountEntry->HandleCount = 0;
        }
    }

    ObpUnlockObject( ObjectHeader );

    if (ObjectType->TypeInfo.CloseProcedure) {

#if DBG
        KIRQL SaveIrql;
#endif

        ObpBeginTypeSpecificCallOut( SaveIrql );

        (*ObjectType->TypeInfo.CloseProcedure)( Process,
                                                Object,
                                                GrantedAccess,
                                                ProcessHandleCount,
                                                SystemHandleCount );

        ObpEndTypeSpecificCallOut( SaveIrql, "Close", ObjectType, Object );

    }

    ObpDeleteNameCheck( Object );

    InterlockedDecrement((PLONG)&ObjectType->TotalNumberOfHandles);

    return;
}

  為了突出主題,我把註釋給刪掉了,重點看這塊程式碼:

    if (ObjectType->TypeInfo.CloseProcedure) {

#if DBG
        KIRQL SaveIrql;
#endif

        ObpBeginTypeSpecificCallOut( SaveIrql );

        (*ObjectType->TypeInfo.CloseProcedure)( Process,
                                                Object,
                                                GrantedAccess,
                                                ProcessHandleCount,
                                                SystemHandleCount );

        ObpEndTypeSpecificCallOut( SaveIrql, "Close", ObjectType, Object );

    }

  明白這塊地方的作用了吧?其他的我就不逐個列舉了。

下一篇

  羽夏看Win系統核心—— APC 篇

相關文章