The NT Handle Table


The NT OS uses objects to represent and access kernel abstractions and system resources, including files, events, devices and ports. An object is accessed by the user via the per process handle table. Opening an object results in adding a pointer to the object to the handle table. Then returned handle value is, essentially, an index in the table.

The table is stored in system memory and cannot be directly read or written by the user. The user must use the handle to reference the object. When an object is opened, and a handle created, it is opened with certain rights according to the user's request and limited by the user's privilege. The object's ACL's are referenced to decide whether requested rights will be granted, but once granted, those rights are stored alongside the object pointer in the handle table. The currently granted rights belong to that handle and cannot be revoked.

Handles are always multiples of four. I don't know why, since handle table entries are eight bytes each. Each active entry is the DWORD pair:

    (object pointer, granted access mask).
Free entries are tied together with a doubly-linked list.

How to manually read the handle table

Use !process to get the address of the Object Table, also known as the Handle Table. Get the DWORD at offset 0x0c from this value. This is the base of the handle table. Take the handle value, multiply by 2 and use it as an offset into the handle table. The DWORD at this offset is a pointer to the object header. Add 0x18 to the header to get a pointer to the object suitable for an argument to !object.

Since the handle table is in kernel space, to use this from NTSD, break to the kernel using !kpb. This will break into the kernel while in NTSD's process context, not the context of the process you are trying to debug. You must use !process _process_cid_ to get the correct Handle Table for the process you are actually debugging.


0:025> g

eax=0014d300 ebx=0014c368 ecx=00000002 edx=0014c368 esi=0014c340 edi=0014a720
eip=4ef83797 esp=0465fddc ebp=0465fe40 iopl=0         nv up ei ng nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000296
4ef83797 ff15f841fc4e                               ds:0023:4efc41f8=77f681b4
                         call dword ptr [rpcrt4!_imp__NtReplyPort (4efc41f8)]
0:025> dd esp
0465fddc  000000e8 0465fe68 0014a720 0014c340
0465fdec  00000000 77f9b7f8 ffffffff 0014a750
0465fdfc  77f64d02 0465fd04 00000010 0465fe98
0465fe0c  00000009 00000010 0014d3c8 0014cc40
0465fe1c  0465fdf0 00000000 4ef81012 00000000
0465fe2c  00000041 00150b78 4ef81066 00000003
0465fe3c  0014d3c0 0465ff90 4ef883c9 0014a720
0465fe4c  0465fe68 00000000 0014d300 001996c8

0:025> !kbp

*                                                                             *
*   You are seeing this message because you pressed the SysRq/PrintScreen     *
*   key on your test machine's keyboard.                                      *
*                                                                             *
*                                                                             *
*                   THIS IS NOT A BUG OR A SYSTEM CRASH                       *
*                                                                             *
* If you did not intend to break into the debugger, press the "g" key, then   *
* press the "Enter" key now.  This message might immediately reappear.  If it *
* does, press "g" and "Enter" again.                                          *
*                                                                             *
801356ec cc               int     3
kd> !process 0 0
PROCESS 80725940  LogonId: 0  Cid: 0002  Peb: 00000000  ParentCid: 0000
    DirBase: 00030000  ObjectTable: 80724f28  TableSize: 398.
    Image: System

[lines deleted]

PROCESS 805e7020  LogonId: 0  Cid: 0066  Peb: 7ffdf000  ParentCid: 002b
    DirBase: 02e60000  ObjectTable: 8067ff28  TableSize: 286.
    Image: TERMSRV.EXE

[lines deleted]

kd> dd  8067ff28
8067ff28  00000000 00000000 00000000 e11ae708
8067ff38  e11af000 000000fe 805e7020 00000066
8067ff48  00000010 805d9d2c 805f092c 00040201
8067ff58  00000000 8067ff5c 8067ff5c 00050005
8067ff68  00000000 8067ff6c 8067ff6c 7fffffff
8067ff78  00000000 00000000 04000003 43444d31
8067ff88  805e8928 806003a8 8122e974 8122e974
8067ff98  fdaafbe0 fdaafbe0 03010001 6274624f
kd> dd e11ae708
e11ae708  e11aebb0 e11aea68 e113acb8 000f001f
e11ae718  805e81a8 00100003 8066e5f8 00000003
e11ae728  805d7888 001f0003 805e80d2 00100020
e11ae738  80649a81 000f016e 80649020 000f00cf
e11ae748  80649a80 000f016e e12c15c8 000f003f
e11ae758  e1240d18 000f001f 805e3009 001f0003
e11ae768  8066e7f8 000f000f e12d6639 000f0001
e11ae778  8065fa28 00100000 805e47a8 00100002
kd> dd e11ae708+1d0
e11ae8d8  e12f7d38 000f0001 8059ea48 001f0003
e11ae8e8  83f65650 00120089 8052ea68 001f03ff
e11ae8f8  e12ec498 000f0001 e11aeb80 e11aec80
e11ae908  805864c8 001f0003 e1315018 0000000c
e11ae918  80593608 001f0001 e11aea68 e11aeb80
e11ae928  805946e8 001f0001 80592b48 001f03ff
e11ae938  e1310ab8 000f0001 80592da8 001f03ff
e11ae948  805935d8 000f03ff 805946b8 000f03ff
kd> !object  e12f7d50
Object: e12f7d50  Type: (806f2760) Port
    ObjectHeader: e12f7d38
    HandleCount: 1  PointerCount: 1
kd> g