Pspcidtable holds all process and thread objects in the system, which are either the process ID (PID) or the thread ID (TID). Let's look at the handle_table structure in WinDbg first:
You can see that the content stored in address 0x83f41bc4 is 0x 8da010a8, which is the structure of the system's _handle_table.
Well, now WinDbg is getting handle_table structure, or code implementation. Here is a simple plus offset:
Eprocess address of the system process
Peprocess eprocess = (peprocess) 0x86aee798;
#define _handle_table_offset_eprocess 0X0F4
Handletable = (phandle_table) (* (ulong*) ((uint8*) eprocess + _handle_table_offset_eprocess));
Handle_table There are two things to watch out for, the first one is Tablecode and the other is Nexthandleneedingpool.
The Tablecode member can be thought of as a pointer to the address of the handle table, where the low 2 bits of the value represent the number of layers of the handle table, so the address of the handle table we actually get is to mask the low 2 bits, which is tablecode&& 0XFFFFFFF8 (tablecode&~0x3), where a low two bit is 0, represents a 1-tier index, 1 represents a 2-tier index, 2 represents a 3-tier index, and finally we index a _handle_table_entry structure, This structure has the address of the _eprocess object we want.
Borrow a blogger chart (image from: http://www.cnblogs.com/ck1020/p/5897460.html)
This diagram can be said to be very clear (there is a small error, tablecode low two bits is 2 is a three-level index, but his flaws na)
For each index table size is 1 page 4KB, where the first level table holds the structure of the 8Byte _handle_table_entry, so each 1-level table can only hold 512 items;
Level 2 table is the address of the Level 1 table (4Byte) then each 2-level table can hold the address of 4kb/4b = 1024 1-level table, if there is a 3-level table, it is quite a large number.
Back to see Tablecode as 0x8f71001, low 2 bits of 01, you can know that the current is a 2-layer index structure, where the address of the handle table is 0x8f71000, through the command DD 0x0x8f71000 see the corresponding 2-level index Table:
You can see that there are only two items, that is, there are two level 1 tables, so the current handle table can accommodate 512*2=1024 handles.
Access to the first Level 1 index table with address 0x8da04000 to see:
Inside is a lot of 8 bytes of _handle_table_entry, look at the wrk source to see its structure:
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
A free entry The field stores the ' index for the next ' entry in the
Free list. This is 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;
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
A free entry The field stores the ' index for the next ' entry in the
Free list. This is 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;
This structure shows that the first 4Byte of the Handle_table_entry 8Byte is an object, which is the _eprocess pointer we are looking for, but it is important to note that the lower 3 bits of the object pointer in the handle table have a different meaning:
① No. 0 Bit obj_protect_close, indicating whether the caller is allowed to close the handle;
② the 1th bit, Obj_ INHERIT, indicates whether the child process created by the process can inherit the handle, that is, whether the handle item is copied to the handle table of the child process;
③ 2nd bit obj_ audit_object_ CLOSE. Indicates whether an audit event is generated when the object is closed.
So we're going to have to mask the low 3Bit when we use the pointer.
That is to say, for OBJECT=86AE88A9 should be objectheader=0x86aee799& 0xfffffff8 (0x86aee799&~0x07) &~0x07 = 0x86aee798, This is the address of the _eprocess that is needed;
Having said so much, our code also implements the implementation:
Tablecode = handletable->tablecode;
Tablecode = (ULONG) Tablecode & 0xFFFFFFFC; Remove the low two-bit
#define _special_purpose 8
Over a special purpose 8 bytes to the first Handletableentry
Handletableentry = (phandle_table_entry) ((ulong*) ((uint8*) Tablecode + _special_purpose));
Remove the low 3-bit mask flag and convert to object (body) pointers
Eprocess Address
PVOID Objectheader = (PVOID) ((ULONG) (handletableentry->object) & 0XFFFFFFF8);
WinDbg go ahead and verify the 0x86aee798, the object type of this address:
(in writing this program code, it has been WinDbg to see the eprocess address of the system process to write dead to the code, look back, it is this 0x86aee798!!!! )
A Process object,
Bingo!
The code is correct, substituting the next loop to read the handletableentry structure, print out the object header and object body:
NTSTATUS EnumTable0 (PVOID tablecode) {phandle_table_entry handletableentry = NULL; ULONG i = 0; Over special uses 8 bytes to the first handletableentry Handletableentry = (phandle_table_entry) ((ulong*) ((uint8*) Tablecode + _special_ PURPOSE)); for (i = 0; i<_max; i++) {if (Mmisaddressvalid (PVOID) handletableentry)//Determine if the virtual memory is valid { Remove the low 3-bit mask flag, convert to object (body) pointer//eprocess address PVOID Objectheader = (PVOID) ((ULONG) (handletableentry-> Object) & 0XFFFFFFF8); if (Mmisaddressvalid (Objectheader)) {dbgprint ("objectheader:%p\r\n", Objectheader); PVOID objectbody = (PVOID) ((uint8*) Objectheader + _body_offset_object_header); if (Mmisaddressvalid (objectbody))//This should be judged whether the object is valid {dbgprint ("object:%p\r\n", objec TBody); __objectcount++; }}} handletableentry++; struct pointer + + one plus one knotStructure} return status_success;}
And then we'll talk about a member of _handle_table called Nexthandleneedingpool:
This member describes the handle value of the starting handle when the next handle table grows (do not forget that the handle is increased by 4), and the above analysis we know that the system has 2 2-level indexes that can describe up to 512*2=1024 _handle_entry, which means that the maximum representation of the handle value is 1024*4 =4096=0x1000, because it starts with 0x00, the maximum handle that the current index table state can describe is 0x1000, which is the starting handle value for the next handle table extension.
Finally, verify that the process corresponds to the _eprocess by Pspcidtable and the PID of the process:
Take Audiodg.exe as the test object.
Its PID is 1048 (decimal), should be located in the 1048/4=262 table entries, each of our 1-level index table can accommodate 512 table entries, PID 1048 should be in the 1th Level 1 index of the 262=0x106 table entries (each table entry 8Byte)
Bingo!
Finally, the final summary of the process:
(1) (WINDBG) Gets the address of the pspcidtable, according to the Tablecode low 2 bits to determine the number of layers of the handle table.
(2) Traversal handle table: Only the first level handle table is _handle_table_entry (8 bytes), two and three levels are pointers (4 bytes), each table is 1 page (4KB) size,
(3) After acquiring the object, you can look through the Objectheader typeindex to see if it is a process.
(4) Handle_table_entry 8Byte of the first 4Byte is an object, remove the low three bits is the address of the desired _eprocess object, _eprocess object offset 0x18 is the object body relative to the object header.
Source:
Scanprocesshandletable.c
#include "ScanProcessHandleTable.h" #define _handle_table_offset_eprocess 0x0f4#define _special_purpose 8 #define _MAX 511#define _body_offset_object_header 0x18ulong64 __objectcount = 0; NTSTATUS DriverEntry (Pdriver_object driverobject, punicode_string registerpath) {Unreferenced_parameter ( Registerpath); NTSTATUS Status = eprocess address of the status_success;//system process peprocess eprocess = (peprocess) 0x86aee798; Pdevice_object DeviceObject = Null;driverobject->driverunload = Driverunload; Sescanprocesshandletable (eprocess); return Status;} NTSTATUS sescanprocesshandletable (peprocess eprocess) {NTSTATUS Status = status_unsuccessful; Phandle_table handletable = NULL; PVOID Tablecode = NULL; ULONG Flag = 0;if (eprocess==null) {return Status;} Handletable = (phandle_table) (* ((ulong*) ((uint8*) eprocess + _handle_table_offset_eprocess)); if (handletable==null) {return Status;} Tablecode = handletable->tablecode; Tablecode = (ULONG) Tablecode & 0xFFFFFFFC;Remove the low two-bit Flag = (ULONG) (Handletable->tablecode) & 0x03; 11switch (Flag) {case 0:{ENUMTABLE0 (tablecode); Case 1:{enumtable1 (Tablecode); Case 2:{enumtable2 (Tablecode); Case 3:{enumtable3 (Tablecode); }ntstatus EnumTable0 (PVOID tablecode) {phandle_table_entry handletableentry = NULL; ULONG i = 0;//over special uses 8 bytes to the first handletableentryhandletableentry = (phandle_table_entry) ((ulong*) ((uint8*) Tablecode + _ Special_purpose)); for (i = 0; i<_max; i++) {if (Mmisaddressvalid (PVOID) handletableentry)//Determine if the virtual memory is valid {// Remove the low 3-bit mask flag, convert to object (body) pointer//eprocess address PVOID Objectheader = (PVOID) ((ULONG) (handletableentry->object) & 0XFFFFFFF8) ; if (Mmisaddressvalid (Objectheader)) {dbgprint ("objectheader:%p\r\n", Objectheader); PVOID objectbody = (PVOID) ((uint8*) Objectheader + _body_offset_object_header); if (Mmisaddressvalid (objectbody))// It should be judged whether the object is valid {dbgprint ("object:%p\r\n", objectbody); __objectcount++;}}} handletableentry++; struct pointer + + one plus one structural body}return status_success;}NTSTATUS EnumTable1 (PVOID tablecode) {do{enumtable0 (* (ulong*) tablecode);(uint8*) Tablecode + = sizeof (ULONG);} while (* (ulong*) Tablecode! = 0 && mmisaddressvalid (* (ulong*) tablecode)); return status_success;} NTSTATUS EnumTable2 (PVOID tablecode) {do{enumtable1 (* (ulong*) tablecode);(uint8*) Tablecode + = sizeof (ULONG);} while (* (ulong*) Tablecode! = 0 && mmisaddressvalid (* (ulong*) tablecode)); return status_success;} NTSTATUS EnumTable3 (PVOID tablecode) {do{enumtable2 (* (ulong*) tablecode);(uint8*) Tablecode + = sizeof (ULONG);} while (* (ulong*) Tablecode! = 0 && mmisaddressvalid (* (ulong*) tablecode)); return status_success;} VOID driverunload (Pdriver_object driverobject) {unreferenced_parameter (DriverObject);D bgprint ("DriverUnload () \ r \ n ");}
ScanProcessHandleTable.h
#pragmaOnce#include<ntifs.h>typedefstruct_handle_table {PVOID tablecode; Peprocess quotaprocess; HANDLE Uniqueprocessid; ULONG Handletablelock; List_entry handletablelist; ULONG handlecontentionevent; PVOID Debuginfo; LONG extrainfopages; ULONG Flags; ULONG Firstfreehandle; PVOID Lastfreehandleentry; ULONG Handlecount; ULONG Nexthandleneedingpool; ULONG Handlecounthighwatermark;} Handle_table,*Phandle_table;typedefstruct_handle_table_entry{Union {PVOID Object; ULONG obattributes; PVOID infotable; PVOID Value; }; Union {union {ULONG grantedaccess; struct{USHORT grantedaccessindex; USHORT Creatorbacktraceindex; }; }; ULONG Nextfreetableentry; };} Handle_table_entry,*Phandle_table_entry; NTSTATUS EnumTable0 (PVOID tablecode); NTSTATUS EnumTable1 (PVOID tablecode); NTSTATUS EnumTable2 (PVOID tablecode); NTSTATUS EnumTable3 (PVOID tablecode); NTSTATUS sescanprocesshandletable (peprocess eprocess); VOID driverunload (Pdriver_object driverobject);
Scanning system Handle Table (WIN7 x86) (appendix Source)