PspCidTable存放着系統中全部的進程和線程對象,其索引也就是進程ID(PID)或線程ID(TID).先經過它來看看windbg裏的HANDLE_TABLE結構:html
能夠看到地址 0x83f41bc4中存放的內容是 0x 8da010a8,這是系統的_HANDLE_TABLE的結構。spa
好了,如今windbg是獲得HANDLE_TABLE結構了,仍是要代碼實現的。這裏只簡單用一下加偏移:線程
//system進程的eprocess地址3d
PEPROCESS EProcess = (PEPROCESS)0x86aee798;指針
#define _HANDLE_TABLE_OFFSET_EPROCESS 0x0f4code
HandleTable = (PHANDLE_TABLE)(*((ULONG*)((UINT8*)EProcess + _HANDLE_TABLE_OFFSET_EPROCESS)));orm
HANDLE_TABLE這裏有兩個須要關注的內容,第一個是TableCode,另外一個是NextHandleNeedingPool.htm
TableCode成員,能夠認爲它是一個指向句柄表的地址,其中這個數值的低2位表示的是句柄表的層數,因此咱們實際獲得的句柄表的地址是要掩掉低2位的,也就是 TableCode&& 0xFFFFFFF8(TableCode&~0x3) ,其中低兩位 爲0時表示 1層索引,爲1時表示2層索引,爲2時表示3層索引,最後咱們索引到的是一個_HANDLE_TABLE_ENTRY的結構,這個結構裏面有咱們要的_EPROCESS對象地址。對象
借某位博主圖一用(圖片源自:http://www.cnblogs.com/ck1020/p/5897460.html)blog
這個圖能夠說是很是清晰了(有一處小錯誤,TableCode低兩位爲2是三級索引,不過瑕不掩瑜吶)
對於每個索引表大小都爲1頁 4KB,其中一級表存放的是8Byte的_HANDLE_TABLE_ENTRY的結構,因此每個1級表就只能存放512個項;
2級表存放的是1級表的地址(4Byte)那麼每個2級表可以存放4KB/4B = 1024個1級表的地址,若是存在3級表的話,至關龐大的數目了。
回到上面能夠看到TableCode爲0x8f71001, 低2位爲01 ,能夠知道當前是2層索引結構,其中句柄表的地址爲0x8f71000,經過命令 dd 0x0x8f71000看到對應的2級索引表:
能夠看到就只有兩項,也就是說有兩個1級表,那麼當前的句柄表可以容納512*2=1024個句柄.
經過地址0x8da04000訪問到第一個1級索引表來看看 :
裏面是不少8字節的_HANDLE_TABLE_ENTRY,查看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;
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;
經過這個結構能夠看出HANDLE_TABLE_ENTRY 8Byte的前4Byte是一個Object對象,這也就是咱們要找的_EPROCESS指針,可是須要注意的是,句柄表中的Object指針的低3位則是有另外意義的:
①第0位OBJ_PROTECT_CLOSE,表示調用者是否容許關閉該句柄;
②第1位OBJ_ INHERIT,指示該進程所建立的子進程是否能夠繼承該句柄,便是否將該句柄項拷貝到子進程的句柄表中;
③第2位OBJ_ AUDIT_OBJECT_ CLOSE。指示關閉該對象時是否產生一個審計事件。
因此咱們在使用該指針的時候要掩掉低3Bit
也就是說對於Object=86ae88a9應該變爲ObjectHeader=0x86aee799& 0xFFFFFFF8 (0x86aee799&~0x07)&~0x07 = 0x86aee798,這纔是須要的_EPROCESS 的地址;
說了這麼多,咱們的代碼也來實現實現:
TableCode = HandleTable->TableCode;
TableCode = (ULONG)TableCode & 0xFFFFFFFC; //去掉低兩位
#define _SPECIAL_PURPOSE 8
//越過特殊用途8字節到第一個HandleTableEntry
HandleTableEntry = (PHANDLE_TABLE_ENTRY)((ULONG*)((UINT8*)TableCode + _SPECIAL_PURPOSE));
//去掉低3位掩碼標誌,轉換爲對象(體)指針
//EPROCESS地址
PVOID ObjectHeader = (PVOID)((ULONG)(HandleTableEntry->Object) & 0xFFFFFFF8);
Windbg繼續開進,驗證一下0x86aee798,這個地址的對象類型:
(在寫這個程序代碼的時候,就已經WINDBG查看了system進程的EPROCESS地址寫死到了代碼中,回頭看看,正是這個0x86aee798!!!!)
一個Process對象,
Bingo!
代碼無誤,代接下來循環讀取HandleTableEntry結構,打印出對象頭和對象體:
NTSTATUS EnumTable0(PVOID TableCode) { PHANDLE_TABLE_ENTRY HandleTableEntry = NULL; ULONG i = 0; //越過特殊用途8字節到第一個HandleTableEntry HandleTableEntry = (PHANDLE_TABLE_ENTRY)((ULONG*)((UINT8*)TableCode + _SPECIAL_PURPOSE)); for (i = 0; i<_MAX; i++) { if (MmIsAddressValid((PVOID)HandleTableEntry)) //判斷該虛擬內存是否合法 { //去掉低3位掩碼標誌,轉換爲對象(體)指針 //EPROCESS地址 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)) //這裏應當判斷對象是否合法 { DbgPrint("Object:%p\r\n", ObjectBody); __ObjectCount++; } } } HandleTableEntry++; //結構體指針++ 一加一個結構體 } return STATUS_SUCCESS; }
接下來回頭談談_HANDLE_TABLE中的一個叫作NextHandleNeedingPool的成員:
這個成員描述了下次句柄表增加的時,起始的句柄值(別忘了句柄是以4爲步長增加的),上面的分析咱們知道系統有2個2級索引那麼最多能描述512*2=1024個_HANDLE_ENTRY,也就是說最大能表示的句柄值爲1024*4=4096=0x1000,由於是從0x00開始的,因此當前的索引表狀態可以描述的最大句柄上限爲0x1000,這個值也就是下次句柄表擴展的起始句柄值.
最後來驗證一下經過PspCidTable和進程的PID找到進程對應的_EPROCESS:
以audiodg.exe做爲檢驗對象。
它的PID爲1048(十進制),應當位於第1048/4=262 個表項,咱們每個1級索引表能容納512個表項,PID爲1048應該在第1個1級索引的第262=0x106個表項(每一個表項8Byte)
BINGO!
最後的最後總結一下流程:
(1)(WINDBG )獲取到PspCidTable的地址,根據Tablecode低2位判斷句柄表的層數。
(2)遍歷句柄表:只有一級句柄表纔是_HANDLE_TABLE_ENTRY(8字節),二級和三級都是指針(4字節),每個表都是1頁(4KB)大小,
(3)獲取到Object以後,能夠經過ObjectHeader的TypeIndex看看是否是Process.
(4)HANDLE_TABLE_ENTRY 8Byte的前4Byte是一個Object對象,去掉低三位纔是纔是須要的_EPROCESS 對象的地址,_EPROCESS 對象偏移0x18處是相對於對象頭的對象體。
源代碼:
ScanProcessHandleTable.c
#include "ScanProcessHandleTable.h" #define _HANDLE_TABLE_OFFSET_EPROCESS 0x0f4 #define _SPECIAL_PURPOSE 8 #define _MAX 511 #define _BODY_OFFSET_OBJECT_HEADER 0x18 ULONG64 __ObjectCount = 0; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) { UNREFERENCED_PARAMETER(RegisterPath); NTSTATUS Status = STATUS_SUCCESS; //system進程的eprocess地址 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; //去掉低兩位 Flag = (ULONG)(HandleTable->TableCode) & 0x03; //00 01 10 11 switch (Flag) { case 0: { EnumTable0(TableCode); break; } case 1: { EnumTable1(TableCode); break; } case 2: { EnumTable2(TableCode); break; } case 3: { EnumTable3(TableCode); break; } } } NTSTATUS EnumTable0(PVOID TableCode) { PHANDLE_TABLE_ENTRY HandleTableEntry = NULL; ULONG i = 0; //越過特殊用途8字節到第一個HandleTableEntry HandleTableEntry = (PHANDLE_TABLE_ENTRY)((ULONG*)((UINT8*)TableCode + _SPECIAL_PURPOSE)); for (i = 0; i<_MAX; i++) { if (MmIsAddressValid((PVOID)HandleTableEntry)) //判斷該虛擬內存是否合法 { //去掉低3位掩碼標誌,轉換爲對象(體)指針 //EPROCESS地址 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)) //這裏應當判斷對象是否合法 { DbgPrint("Object:%p\r\n", ObjectBody); __ObjectCount++; } } } HandleTableEntry++; //結構體指針++ 一加一個結構體 } 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); DbgPrint("DriverUnload()\r\n"); }
ScanProcessHandleTable.h
#pragma once #include <ntifs.h> typedef struct _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; typedef struct _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);