欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

(66)全局句柄表,遍历全局句柄表

发布时间:2025/3/21 编程问答 42 豆豆
生活随笔 收集整理的这篇文章主要介绍了 (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`00000000

81fb13c9 低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)全局句柄表,遍历全局句柄表的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。