(66)全局句柄表,遍历全局句柄表
一、回顧
前面的課程我們學習了進程的句柄表,全局句柄表和進程句柄表非常像,只有一些小區別。
這節課的課后作業我先給出來:
編寫程序,通過全局句柄表PsdCidTable,遍歷所有進程(包括隱藏進程)。
打印全局句柄表中內核對象的所有類型
一、需要解決的問題:
1、如何通過找到全局句柄表?
2、如何判斷是否是進程?
二、有用的系統函數:MmGetSystemRoutineAddress
這個函數用來得到導出函數的地址,優點是:
1、不會被IAT Hook影響(從內核模塊導出表中找函數地址的)
2、有些內核函數雖然導出了 但并沒有函數說明,無法直接使用
我對題目要求稍作修改:遍歷全局句柄表,如果是進程,就打印 EPROCESS 和 鏡像名,如果是線程,就打印 ETHREAD。
二、全局句柄表 PsdCidTable
全局變量 PspCidTable 存儲了全局句柄表 _HANDLE_TABLE 的地址:
kd> dd PspCidTable 8055b260 e1000838 00000002 00000000 00000000 8055b270 00000000 00000000 00000000 00000000 8055b280 00000000 00000000 00000000 00000000 8055b290 00000000 00000000 00000000 00000000 8055b2a0 00000000 00000000 00000000 00000000 8055b2b0 00000000 00000000 00000000 00000000 8055b2c0 00000000 00000000 00000000 00000000 8055b2d0 00000000 00000000 00000000 00000000kd> dt e1000838 _HANDLE_TABLE nt!_HANDLE_TABLE+0x000 TableCode : 0xe1003000+0x004 QuotaProcess : (null) +0x008 UniqueProcessId : (null) +0x00c HandleTableLock : [4] _EX_PUSH_LOCK+0x01c HandleTableList : _LIST_ENTRY [ 0xe1000854 - 0xe1000854 ]+0x024 HandleContentionEvent : _EX_PUSH_LOCK+0x028 DebugInfo : (null) +0x02c ExtraInfoPages : 0n0+0x030 FirstFree : 0x4fc+0x034 LastFree : 0x4d8+0x038 NextHandleNeedingPool : 0x800+0x03c HandleCount : 0n346+0x040 Flags : 1+0x040 StrictFIFO : 0y1全局句柄表存儲了所有 EPROCESS 和 ETHREAD.和進程的句柄表不同,全局句柄表項低32位指向的就是內核對象,而非 OBJECT_HEADER.
除此之外,和進程句柄表就沒什么不同了,結構也是可以分為1,2,3級,這里 0xe1003000 低位是0,就只有一級。
我們平時用的PID就可以用來索引全局句柄表,下面我們隨便找一個進程:
計算器的PID是1840,轉成16進制是 0x730 . 0x730 / 4 = 0x1cc, 所以句柄表項的地址就是 0xe1003000 + 1cc * 8
kd> dq 0xe1003000 + 1cc * 8 e1003e60 00000000`81fb13c9 000005f0`00000000 e1003e70 00000000`81e5eb39 00000000`81c87021 e1003e80 00000000`81c00da9 00000000`82114279 e1003e90 00000000`81c46da9 00000000`81fe45a1 e1003ea0 00000000`820eca29 00000000`81c32da9 e1003eb0 00000000`81b3c5e1 00000000`81c09231 e1003ec0 00000000`81b79231 00000000`81b79a79 e1003ed0 00000000`81c46561 000005a0`0000000081fb13c9 低2位清零就是 EPROCESS 的地址:
kd> dt _EPROCESS 81fb13c8 nt!_EPROCESS ...+0x170 Session : 0xf8bc8000 Void+0x174 ImageFileName : [16] "calc.exe"+0x184 JobLinks : _LIST_ENTRY [ 0x0 - 0x0 ]+0x18c LockedPagesList : (null) +0x190 ThreadListHead : _LIST_ENTRY [ 0x81f92cb4 - 0x81f92cb4 ]+0x198 SecurityPort : (null) ...😒🤣
三、遍歷全局句柄表
老師說全局句柄表里只存 EPROCESS 和 ETHREAD,我們編程遍歷這個表,判斷是線程還是進程,打印不同的信息:
運行結果:二級句柄表
驅動
//#include <ntddk.h> //#include <ntstatus.h> #include <ntifs.h>//----------------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------------typedef struct _LDR_DATA_TABLE_ENTRY {LIST_ENTRY InLoadOrderLinks;LIST_ENTRY InMemoryOrderLinks;LIST_ENTRY InInitializationOrderLinks;PVOID DllBase;PVOID EntryPoint;ULONG SizeOfImage;UNICODE_STRING FullDllName;UNICODE_STRING BaseDllName;ULONG Flags;UINT16 LoadCount;UINT16 TlsIndex;LIST_ENTRY HashLinks;PVOID SectionPointer;ULONG CheckSum;ULONG TimeDateStamp;PVOID LoadedImports;PVOID EntryPointActivationContext;PVOID PatchInformation; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_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;typedef struct _OBJECT_TYPE {ERESOURCE Mutex;LIST_ENTRY TypeList;UNICODE_STRING Name; // Copy from object header for convenience // PVOID DefaultObject; // ULONG Index; // ULONG TotalNumberOfObjects; // ULONG TotalNumberOfHandles; // ULONG HighWaterNumberOfObjects; // ULONG HighWaterNumberOfHandles; // OBJECT_TYPE_INITIALIZER TypeInfo; //#ifdef POOL_TAGGING // ULONG Key; //#endif //POOL_TAGGING // ERESOURCE ObjectLocks[ OBJECT_LOCK_COUNT ]; } OBJECT_TYPE, *POBJECT_TYPE;typedef struct _OBJECT_HEADER {LONG PointerCount;union {LONG HandleCount;PVOID NextToFree;};POBJECT_TYPE Type;UCHAR NameInfoOffset;UCHAR HandleInfoOffset;UCHAR QuotaInfoOffset;UCHAR Flags;union {//POBJECT_CREATE_INFORMATION ObjectCreateInfo;PVOID ObjectCreateInfo;PVOID QuotaBlockCharged;};PSECURITY_DESCRIPTOR SecurityDescriptor;QUAD Body; } OBJECT_HEADER, *POBJECT_HEADER;//----------------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------------VOID DriverUnload(PDRIVER_OBJECT pDriver); NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path);//----------------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------------ULONG PspCidTable;// 驅動入口 NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path) {typedef HANDLE_TABLE_ENTRY *L1P;typedef volatile L1P *L2P;typedef volatile L2P *L3P;int i,j,k;ULONG TableCode;ULONG TableLevel;L1P TableLevel1;L2P TableLevel2;L3P TableLevel3;UNICODE_STRING ProcessString;UNICODE_STRING ThreadString;ULONG HandleAddr;PEPROCESS pEprocess;PCHAR ImageFileName;POBJECT_HEADER pObjectHeader;// 使用 MmGetSystemRoutineAddress 動態獲取函數地址可以防 IAT hook// 此處偷懶不弄,除非加錢PspCidTable = **(PULONG*)((ULONG)PsLookupProcessByProcessId + 26);//DbgPrint("PspCidTable = %x\n",PspCidTable);TableCode = *(PULONG)PspCidTable;//DbgPrint("%x\n", TableCode);TableLevel = TableCode & 0x03; // 句柄表等級TableCode = TableCode & ~0x03; // 清除等級標志位DbgPrint("TableLevel = %x\n",TableLevel);DbgPrint("TableCode = %x\n",TableCode);RtlInitUnicodeString(&ProcessString, L"Process");RtlInitUnicodeString(&ThreadString, L"Thread");// 要測試這個程序,可以創建一個進程,進程創建512個線程,// 這樣全局句柄表的結構就是二級的,就會進入 case 1// 如果想測試 case 2,要創建大于 1024 * 512 個內核對象switch(TableLevel){case 0:{ DbgPrint("一級句柄表...\n");TableLevel1 = (L1P) TableCode;for (i = 0; i < 512; i++){if ( MmIsAddressValid(TableLevel1[i].Object)){//DbgPrint("%x\n",TableLevel1[i].Object);HandleAddr = ((ULONG)(TableLevel1[i].Object) & ~0x03);pObjectHeader = (POBJECT_HEADER)(HandleAddr - 0x18);if(RtlCompareUnicodeString(&pObjectHeader->Type->Name, &ProcessString, TRUE) == 0){//DbgPrint("EPROCESS: %x\n", HandleAddr);pEprocess = (PEPROCESS)HandleAddr;ImageFileName = (PCHAR)pEprocess + 0x174;DbgPrint("進程鏡像名:%s\n", ImageFileName);}else if (RtlCompareUnicodeString(&pObjectHeader->Type->Name, &ThreadString, TRUE) == 0){pEprocess = (PEPROCESS)*(PULONG)(HandleAddr+0x220);ImageFileName = (PCHAR)pEprocess + 0x174;DbgPrint("----ETHREAD: %x, 所屬進程:%s\n", HandleAddr, ImageFileName);}else{DbgPrint("既不是線程也不是進程 0x%x\n", HandleAddr); // 應該是不可能的...因為全局句柄表只存進程和線程}}}break;}case 1:{DbgPrint("二級句柄表...\n");TableLevel2 = (L2P) TableCode;for (i = 0; i < 1024; i++){if (MmIsAddressValid((PVOID)((PULONG)TableLevel2)[i])){for (j = 0; j < 512; j++){if ( MmIsAddressValid(TableLevel2[i][j].Object)){HandleAddr = ((ULONG)(TableLevel2[i][j].Object) & ~0x03);pObjectHeader = (POBJECT_HEADER)(HandleAddr - 0x18);if(RtlCompareUnicodeString(&pObjectHeader->Type->Name, &ProcessString, TRUE) == 0){//DbgPrint("EPROCESS: %x\n", HandleAddr);pEprocess = (PEPROCESS)HandleAddr;ImageFileName = (PCHAR)pEprocess + 0x174;DbgPrint("進程鏡像名:%s\n", ImageFileName);}else if (RtlCompareUnicodeString(&pObjectHeader->Type->Name, &ThreadString, TRUE) == 0){pEprocess = (PEPROCESS)*(PULONG)(HandleAddr+0x220);ImageFileName = (PCHAR)pEprocess + 0x174;DbgPrint("----ETHREAD: %x, 所屬進程:%s\n", HandleAddr, ImageFileName);}else{DbgPrint("既不是線程也不是進程 0x%x\n", HandleAddr); // 應該是不可能的...因為全局句柄表只存進程和線程}} }}}break;}case 2:{DbgPrint("三級句柄表...\n");TableLevel3 = (L3P) TableCode;for (i = 0; i < 1024; i++){if (MmIsAddressValid((PVOID)((PULONG)TableLevel3)[i])){for (j = 0; j < 1024; j++){if (MmIsAddressValid((PVOID)((PULONG*)TableLevel3)[i][j])){for (k = 0; k < 512; k++){if ( MmIsAddressValid(TableLevel3[i][j][k].Object)){HandleAddr = ((ULONG)(TableLevel3[i][j][k].Object) & ~0x03);pObjectHeader = (POBJECT_HEADER)(HandleAddr - 0x18);if(RtlCompareUnicodeString(&pObjectHeader->Type->Name, &ProcessString, TRUE) == 0){//DbgPrint("EPROCESS: %x\n", HandleAddr);pEprocess = (PEPROCESS)HandleAddr;ImageFileName = (PCHAR)pEprocess + 0x174;DbgPrint("進程鏡像名:%s\n", ImageFileName);}else if (RtlCompareUnicodeString(&pObjectHeader->Type->Name, &ThreadString, TRUE) == 0){pEprocess = (PEPROCESS)*(PULONG)(HandleAddr+0x220);ImageFileName = (PCHAR)pEprocess + 0x174;DbgPrint("----ETHREAD: %x, 所屬進程:%s\n", HandleAddr, ImageFileName);}else{DbgPrint("既不是線程也不是進程 0x%x\n", HandleAddr); // 應該是不可能的...因為全局句柄表只存進程和線程}} }}}}}break;}}pDriver->DriverUnload = DriverUnload;return STATUS_SUCCESS; }// 卸載驅動 VOID DriverUnload(PDRIVER_OBJECT pDriver) {DbgPrint("Driver unloaded.\n"); }ULONG GetHandleFromTable(ULONG TableCode, ULONG Handle) {return 0; }附測試程序:
如果要檢驗三種級別的代碼是否正確,一個辦法是起一個程序,創建很多線程:
// 創建線程.cpp : 定義控制臺應用程序的入口點。 //#include "stdafx.h" #include <Windows.h>DWORD WINAPI MyThread(LPVOID p) {int i = 0;while (++i){Sleep(1000);printf("%d\n", i);}return 0; }int _tmain(int argc, _TCHAR* argv[]) {for (int i = 0; i < 1000 * 513; i++){CreateThread(0,0,MyThread,0,0,0);}getchar();return 0; }總結
以上是生活随笔為你收集整理的(66)全局句柄表,遍历全局句柄表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (65)如何根据句柄从二级、三级结构句柄
- 下一篇: (67)多核同步,lock 总线锁 ,自