句柄表篇——总结与提升

博客 分享
0 297
张三
张三 2022-02-05 21:55:00
悬赏:0 积分 收藏

句柄表篇——总结与提升

句柄表篇之总结与提升,扩展一些有关句柄表的知识和结构体。

写在前面

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

你如果是从中间插过来看的,请仔细阅读 羽夏看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_TABLEntdll!_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 -8OBJHeader       = dword ptr -4Handle          = dword ptr  8DesiredAccess   = dword ptr  0ChObjectType      = dword ptr  10hAccessMode      = byte ptr  14hObject          = dword ptr  18hHandleInformation= 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_4D9B4FCurrentProcessHandle:                   ; 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 IsUserModeDesiredIsGranted:                       ; 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], ebxHandleInformationIsNULL:                ; 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 ; 引用计数加1loc_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 IsThreadTypeloc_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_0IsUserMode:                             ; 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], ebxloc_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_4D9C3EIsUserMode_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_DENIEDErrorProc:                              ; 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], 0loc_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, 6loc_4D9CA5:                             ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1EA↑j                mov     [eax], ecxloc_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], eaxloc_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_HANDLEloc_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, ebxProcEnding:                             ; 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 ObpKernelHandleTable8055a7d8  e1001cc8 00000000 00000000 000000008055a7e8  00000000 00000000 00000000 000000008055a7f8  00000000 00000000 00000000 000000008055a808  00000000 00000000 00000000 000000008055a818  00000000 00000000 00000000 000000008055a828  00000000 00000000 00000000 000000008055a838  00000000 00000000 00000000 000000008055a848  00000000 00000000 00000000 00000000kd> dt _HANDLE_TABLE e1001cc8ntdll!_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       : 0y0kd> dq 0xe1002000ReadVirtual: e1002000 not properly sign extendede1002000  fffffffe`00000000 001f0fff`89fb09e9e1002010  00000000`89fb0329 000f003f`e13c9119e1002020  00000000`e1011449 00020019`e13d6731e1002030  00020019`e13d2791 00020019`e101f421e1002040  0002001f`e13de479 00020019`e13c3079e1002050  00020019`e13d1419 0002001f`e13bee51e1002060  00020019`e13d84d1 001f0003`89fa7a11e1002070  00000040`00000000 000000a8`00000000

??关于该函数的扩展,就这些。

句柄表扩展

??为了方便大家进行学习句柄表,我们给出一个小结论,我在WinDbg找到了一个进程的句柄表,如下所示:

kd> dq 0xe10ec000e10ec000  fffffffe`00000000 00000000`00000000e10ec010  00000004`00000000 00000008`00000000e10ec020  0000000c`00000000 00000010`00000000e10ec030  00000014`00000000 00000018`00000000e10ec040  0000001c`00000000 00000020`00000000e10ec050  00000024`00000000 00000028`00000000e10ec060  0000002c`00000000 00000030`00000000e10ec070  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_ENTRYExpLookupHandleTableEntry (    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 queriedReturn 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_HEADERnt!_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_TYPEntdll!_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_INITIALIZERntdll!_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代码片段来进行示例:

VOIDObpDecrementHandleCount (    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 篇

知识共享许可协议
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15864867.html
posted @ 2022-02-05 21:24 寂静的羽夏 阅读(4) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员