在 Phate 上看見了 asusp4b533 大大的對於 Handle Tree 的文章後
便想自己實際演練一次並存起來, 當作日後我可以方便的複習
首先每一個 Process 都會有一個叫做 _EPPROCESS 的 Structure
( 當然你可以使用 !process 發現 _EPPROCESS 實際的 Address )
而在這個 _EPPROCESS + 0x0c4 就指向了一個 ObjectTable
其實這個 ObjectTable 也就是 Handle Table
假設 Handle Table 上 的位置是 0xe1a4f178
則我可以 dt _HANDLE_TABLE 0xe1a4f178 得到下面的結果
nt!_HANDLE_TABLE
+0x000 TableCode : 0xe12b5000
+0x004 QuotaProcess : 0x81acd500 _EPROCESS
+0x008 UniqueProcessId : 0x00000880
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY [ 0x8055c4c8 - 0xe1243a5c ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages : 0
+0x030 FirstFree : 0x144
+0x034 LastFree : 0
+0x038 NextHandleNeedingPool : 0x800
+0x03c HandleCount : 79
+0x040 Flags : 0
+0x040 StrictFIFO : 0y0
上面這個結果最重要的就是 TableCode 了, 他代表了一個指向 Handle Tree 的 Address
由於 Alignment 的關係, 實際上的 Handle Tree 位置應該要做一個 addr &= 0xfffffffc 的處理
它的最低兩個 bit 代表了整顆 Tree 總共有幾層 (1 ~ 3 層)
Handle Tree 的三層是這樣定義的9 ]: n: A7 \. }1 ~) \
最低層定義為一個 HANDLE_TABLE_ENTRY 的陣列,共有512個
中間層定義為一個 ULONG 的陣列,共2048個,每個元素存著一個位址,指向一個最低層陣列
最高層也是個 ULONG 陣列,共32個,每個元素存著一個位址,指向一個中間層陣列8 J" S8 Y( f- ^$ `. \ ?
而在 Handle 的 32 bits 裡面
0 ~ 1 的 bit 是忽略的
2 ~ 10 的 bit 儲存著最低層陣列的 Index
11 ~ 20 的 bit 儲存中間層陣列的 Index# c, j9 y3 u& M
21 ~ 25 的 bit 儲存最高層陣列的 Index
如果 TableCode 的低 2 bit 為 0,代表只有一層,那麼 TableCode 便指向最低層陣列
若為 1,那麼便指向中間層陣列! W( B* t/ [2 v" c
若為 2,那麼便指向最高層陣列
從上面得知了我的 Tree Address 應該為 0xe12b5000, 所以再 dt _HANDLE_TABLE_ENTRY 0xe12b5000
則我可以得到下面的結果
nt!_HANDLE_TABLE_ENTRY
+0x000 Object : (null)
+0x000 ObAttributes : 0
+0x000 InfoTable : (null)
+0x000 Value : 0
+0x004 GrantedAccess : 0xfffffffe
+0x004 GrantedAccessIndex : 0xfffe
+0x006 CreatorBackTraceIndex : 0xffff
+0x004 NextFreeTableEntry : -2
可以發現這個是個無效的 Entry, 這是由於 Windows 不使用第一個 Entry
但是這整個 Entry 是在一塊連續的空間上, 所以可以很容易得到第二個 Entry
所以再 dt _HANDLE_TABLE_ENTRY 0xe12b5008, 可以得到
nt!_HANDLE_TABLE_ENTRY
+0x000 Object : 0xe1006fd9
+0x000 ObAttributes : 0xe1006fd9
+0x000 InfoTable : 0xe1006fd9 _HANDLE_TABLE_ENTRY_INFO
+0x000 Value : 0xe1006fd9
+0x004 GrantedAccess : 0xf0003
+0x004 GrantedAccessIndex : 3
+0x006 CreatorBackTraceIndex : 0xf
+0x004 NextFreeTableEntry : 983043
上面的結果顯示了 Object 的位址是 0xe1006fd8 ( 0xe1006fd9 & 0xfffffffc )
再來要加上 0x18 的 Offset 才能取得真正的 Object Body
所以實際上的 Object Body 位址是 0xe1006ff0 ( 加 0x18 的原因看下面 )
nt!_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
所以顯示出這個 Object 來看看 ( !object e1006ff0 ), 得到
Object: e1006ff0 Type: (81bb9e70) KeyedEvent
ObjectHeader: e1006fd8 (old version)
HandleCount: 24 PointerCount: 25
Directory Object: e10015f0 Name: CritSecOutOfMemoryEvent
所以我可以根據 HANDLE 的數值取得一個最低層陣列的 Index
就可以取得最後一個 Object ( 它會跟 !handle 的內容完全一樣 )
下面這是 asusp4b533 大大的 Code
ULONG HighLevelTableIndex;
ULONG MidLevelTableIndex;
ULONG LowLevelTableIndex;
PVOID pHandleTable;
ULONG TableCode;
PULONG pHighLevelTable;
PULONG pMidLevelTable;
PHANDLE_TABLE_ENTRY pLowLevelTable;
ULONG HandleValue;
HighLevelTableIndex=((ULONG)Handle & 0x3e00000)/0x200000;
MidLevelTableIndex=((ULONG)Handle & 0x1ff800)/0x800;
LowLevelTableIndex=((ULONG)Handle & 0x7fc)/0x4;
pHandleTable=(PVOID)*(PULONG)((ULONG)PsInitialSystemProcess+EPROCESS_ObjectTable);
TableCode=*(PULONG)pHandleTable;
switch(TableCode & 3){
case 0:
pLowLevelTable=(PHANDLE_TABLE_ENTRY)(TableCode & 0xfffffffc);
*pObject=(PVOID)(((ULONG)pLowLevelTable[LowLevelTableIndex].Object & 0xfffffff8)-OBJECT_HEADER_OFFSET);
break;
case 1:
pMidLevelTable=(PULONG)(TableCode & 0xfffffffc);
pLowLevelTable=(PHANDLE_TABLE_ENTRY)pMidLevelTable[MidLevelTableIndex];
*pObject=(PVOID)(((ULONG)pLowLevelTable[LowLevelTableIndex].Object & 0xfffffff8)-OBJECT_HEADER_OFFSET);
break;
case 2:
pHighLevelTable=(PULONG)(TableCode & 0xfffffffc);
pMidLevelTable=(PULONG)pHighLevelTable[HighLevelTableIndex];
pLowLevelTable=(PHANDLE_TABLE_ENTRY)pMidLevelTable[MidLevelTableIndex];
*pObject=(PVOID)(((ULONG)pLowLevelTable[LowLevelTableIndex].Object & 0xfffffff8)-OBJECT_HEADER_OFFSET);
break;
default:
return STATUS_UNSUCCESSFUL;
break;
}
return STATUS_SUCCESS;