Handle table - process handle table

Write in front

  this series is written word by word, including examples and experimental screenshots. Due to the complexity of the system kernel, there may be errors or incompleteness. If there are errors, criticism and correction are welcome. This tutorial will be updated for a long time. If you have any good suggestions, welcome feedback. Code words are not easy. If this article is helpful to you, if you have spare money, you can reward and support my creation. If you want to reprint, please attach my reprint information to the back of the article and state my personal information and my blog address, but you must notify me in advance.

If you look from the middle, please read it carefully Yu Xia's view of Win system kernel -- a brief introduction , easy to learn this tutorial.

  before reading this tutorial, ask a few questions. Have you prepared the basic knowledge? Have you learned the protection mode? Have you learned the system call? Have you finished your exercise? If not, don't continue.

🔒 Gorgeous dividing line 🔒

handle

  handle is a kernel object. When a process creates or opens a kernel object, it will get a handle through which the kernel object can be accessed.

HANDLE g_hMutex = ::CreateMutex( NULL , FALSE, "XYZ");
HANDLE g_hMutex = ::OpenMutex( MUTEX_ALL_ACCESSFALSE, "XYZ");
HANDLE g_hEvent = ::CreateEvent( NULL, TRUE, FALSE, NULL);
HANDLE g_hThread = ::CreateThread( NULL, 0, Proc,NULL, 0, NULL);

  so where is the handle? Why does the operating system use this handle? The purpose of the handle is to avoid directly modifying the kernel object in the application layer, and the handle is an index. Through this index, I can easily find the address of the corresponding kernel object structure in the kernel.
  note that I emphasize here that the handle is for the 3-ring, not for the kernel. So when writing drivers, don't do anything fancy. For all Windows API s involving handles, once they reach the real function implementation part, they immediately use ObReferenceObjectByHandle to convert it into a real pointer to the kernel object. For example, due to the length, only the beginning part is listed:

; int __stdcall PspCreateProcess(int, ACCESS_MASK DesiredAccess, int, HANDLE Handle, int, HANDLE, HANDLE, HANDLE, int)
_PspCreateProcess@36 proc near          ; CODE XREF: NtCreateProcessEx(x,x,x,x,x,x,x,x,x)+72↓p
                                        ; PsCreateSystemProcess(x,x,x)+1B↓p ...

; __unwind { // __SEH_prolog
                push    11Ch
                push    offset stru_402EB0
                call    __SEH_prolog
                mov     eax, large fs:124h
                mov     [ebp+var_84], eax
                mov     cl, [eax+140h]
                mov     [ebp+AccessMode], cl
                mov     eax, [eax+44h]
                mov     [ebp+RunRef], eax
                xor     esi, esi
                mov     [ebp+var_1D], 0
                mov     [ebp+var_48], esi
                mov     [ebp+var_44], esi
                test    [ebp+arg_10], 0FFFFFFF0h
                jnz     short loc_4EFB0B
                cmp     [ebp+Handle], esi
                jz      short loc_4EFB1A
                push    esi             ; HandleInformation
                lea     eax, [ebp+Object]
                push    eax             ; Object
                push    dword ptr [ebp+AccessMode] ; AccessMode
                push    _PsProcessType  ; ObjectType
                push    80h ; '€'       ; DesiredAccess
                push    [ebp+Handle]    ; Handle
                call    _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x)
                mov     ecx, [ebp+Object] ; Object
                mov     [ebp+var_1C], ecx
                cmp     eax, esi
                jl      loc_4F01FB
                cmp     [ebp+arg_20], esi
                jz      short loc_4EFB15
                cmp     [ecx+134h], esi
                jnz     short loc_4EFB15
                call    @ObfDereferenceObject@4 ; ObfDereferenceObject(x)

loc_4EFB0B:                             ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+3D↑j
                mov     eax, 0C000000Dh
                jmp     loc_4F01FB
; ---------------------------------------------------------------------------

  the following is Microsoft's official explanation of ObReferenceObjectByHandle:

The ObReferenceObjectByHandle routine provides access validation on the object handle, and, if access can be granted, returns the corresponding pointer to the object's body.

Handle table

    the above describes what handles are. Let's introduce the handle table. Without special instructions, the handle table we are talking about is the process handle table. So where is the handle table? Look at the following structure:

kd> dt _EPROCESS
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER
   +0x078 ExitTime         : _LARGE_INTEGER
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY
   +0x090 QuotaUsage       : [3] Uint4B
   +0x09c QuotaPeak        : [3] Uint4B
   +0x0a8 CommitCharge     : Uint4B
   +0x0ac PeakVirtualSize  : Uint4B
   +0x0b0 VirtualSize      : Uint4B
   +0x0b4 SessionProcessLinks : _LIST_ENTRY
   +0x0bc DebugPort        : Ptr32 Void
   +0x0c0 ExceptionPort    : Ptr32 Void
   +0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE

  we have contacted this structure before. Because it is very large, only part of it is shown. In 0xc4, this offset is where the handle table is stored. Let's take a look at its structure:

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

   TableCode is the so-called handle table, but the handle table has a structure. Let's take a look at its structure:

   ①: there are two bytes in this block. The high-order byte is used for the SetHandleInformation function. For example, if it is written in the following form, this position will be written to 0x02:

`SetHandleInformation(Handle,HANDLE_FLAG_PROTECT_FROM_CLOSE,HANDLE_FLAG_PROTECT_FROM_CLOSE);`

  HANDLE_ FLAG_ PROTECT_ FROM_ The value of the close macro is 0x00000002, the lowest byte is taken, and the final ① is 0x0200.
   ②: This is the access mask, which is used for the OpenProcess function. The specific stored value is the value of the first parameter of the function.
   ③ and ④ are four bytes in total, in which bit0-bit2 stores the attributes of the handle, in which bit2 and bit0 are 0 and 1 by default; bit1 indicates whether the handle can be inherited. The second parameter of OpenProcess is related to bit1,
bit31-bit3 is the specific address of the kernel object stored in the kernel.

Handle table structure

   the structure of the handle table is still complex. The specific structure is as follows:

   TableCode can be seen from the above figure, indicating the level of the handle table. If the lower two bits are 0, the real handle is stored in the handle table pointed to; If the lower two bits are 1, each member of the first handle table pointed to is an address, and each address points to each real handle table, and so on.

This section exercises

The answers in this section will be explained in the next section. Be sure to read the next explanation after completing this section. Don't be lazy. Experiment is a shortcut to learn this tutorial.

   as the saying goes, you can only speak without practicing the fake skill. The following are the relevant exercises in this section. If you don't do well in the exercise, don't look at the next tutorial. If you don't do the exercise, it's easy to get mixed up. At first, you still understand, and then you really don't understand at all. There are not many exercises in this section. Please complete it with quality and quantity.

one ️⃣ When I open a kernel object with a function, if I use CloseHandle, will the kernel object be destroyed? Suppose I open an existing process with OpenProcess, but after opening, the process is closed. Does the kernel object still exist?
two ️⃣ Use the loop to open a kernel object 100 times to analyze the structure of the handle table; Then open a kernel object 1000 times and continue to analyze the above operations.

Next

  handle table -- global handle table

Added by twistisking on Tue, 25 Jan 2022 05:11:21 +0200