继续接着上篇,这一篇说下句柄表,关于handletable,其实也已经相当科普了~基础知识还是学习下吧.
继续引用下V大的思路.
3.通过pspCidTable获得进程表c
4.通过handletablelisthead获得进程表d
5.通过csrss的handletable用2种方法枚举获得进程表e和f
6.通过扫描当前进程的handletable获得进程表g
在解释如何通过这些思路枚举进程的时候,我们还是先来熟悉下句柄表吧.
在这里推荐阅读教主的<<Windows句柄表格式>>
在Windows中,每一个进程都有一个句柄表(handletable),
在EPROCESS进程对象中,+0xc4,就能找到句柄表指针.
lkd> dt _eprocess 899af020
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER 0x1cb5694`beedc756
+0x078 ExitTime : _LARGE_INTEGER 0x0
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : 0x000014f4
+0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x805648b8 - 0x89b5e6a8 ]
+0x090 QuotaUsage : [3] 0x1068
+0x09c QuotaPeak : [3] 0x1068
+0x0a8 CommitCharge : 0x716
+0x0ac PeakVirtualSize : 0x37f4000
+0x0b0 VirtualSize : 0x37cd000
+0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0xba5dc014 - 0x89b5e6d4 ]
+0x0bc DebugPort : (null)
+0x0c0 ExceptionPort : 0xe2206aa0
+0x0c4 ObjectTable : 0xe6766350 _HANDLE_TABLE
而句柄表是一个_HANDLE_TABLE 结构.下面就来看看这个结构的成员.
lkd> dt _HANDLE_TABLE 0xe6766350
nt!_HANDLE_TABLE
+0x000 TableCode : 0xe1e64000 //指向句柄表的存储结构,真正意义上的动态三层句柄表.
+0x004 QuotaProcess : 0x899af020 _EPROCESS // 所属进程的EPRCESS,这里是枚举进程所需要的~
+0x008 UniqueProcessId : 0x000014f4 // 所属进程的PID
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK // 句柄表锁,仅在句柄表扩展时使用, HANDLE_TALBE_LOCKS = 4
+0x01c HandleTableList : _LIST_ENTRY [ 0x80565ba8 - 0xe4330bcc ] // 句柄表的双链.将系统范围内所有的句柄表链接在一起,这是枚举进程所需要的,
//其实和 ActiveList 是同一个道理,隐藏也是一样的.而这一个双链结构的链表头就是HandleTableListHead,那么表g和表d的实现就很容易了~
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages : 0
+0x030 FirstFree : 0x180
+0x034 LastFree : 0
+0x038 NextHandleNeedingPool : 0x800 // 下一次句柄表扩展的起始句柄索引.其实也就是当前句柄表池的上界.
+0x03c HandleCount : 93 //正在使用的句柄表数量.
+0x040 Flags : 0
+0x040 StrictFIFO : 0y0
通过上面的分析,表g和表d的实现其实已经呼之欲出了:使用PsGetCurrentProcess()获得当前的EPROCESS结构pCurrentProcess;
然后pHandleTable=pCurrentProcess + 0xc4;然后获得HandleTable结构.再然后遍历那个_List_Entry,就能遍历所有进程的Handle_Table,
然后就获得所有进程的EPROCESS,从而实现对进程的枚举.
![ContractedBlock.gif](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
{
PLIST_ENTRY HanTableListHead = NULL;
NTSTATUS nResult;
PLIST_ENTRY CurrTable = NULL;
PEPROCESS PEprocess = NULL;
PLIST_ENTRY HandleTableList = NULL;
ULONG ulNeededSize, uLoop, uKernelSta,NtoskrnlLast;
PMODULE_LIST pModuleList = NULL;
// 获取内核模块基址
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
{
return STATUS_UNSUCCESSFUL;
}
ZwQuerySystemInformation(SystemModuleInformation, & ulNeededSize, 0 , & ulNeededSize);
pModuleList = (PMODULE_LIST)ExAllocatePoolWithTag(NonPagedPool, ulNeededSize, ' han ' );
nResult = ZwQuerySystemInformation(SystemModuleInformation, pModuleList, ulNeededSize, NULL);
if (NT_SUCCESS(nResult))
{ // ntoskrnl is always first there
uKernelSta = (ULONG) pModuleList -> a_Modules[ 0 ].p_Base;
NtoskrnlLast = (ULONG) pModuleList -> a_Modules[ 0 ].p_Base + (ULONG) pModuleList -> a_Modules[ 0 ].d_Size;
}
ExFreePoolWithTag(pModuleList, ' han ' );
// 获取Handletablelisthead地址
PHANDLE_TABLE HandleTable =* (PHANDLE_TABLE * )((ULONG)PsGetCurrentProcess() + HandleTableOffset);
HandleTableList = (PLIST_ENTRY)((ULONG)HandleTable + HandleTableListOffset);
for (CurrTable = HandleTableList -> Flink;CurrTable != HandleTableList;CurrTable = CurrTable -> Flink)
{
if ((ULONG)CurrTable > uKernelSta && (ULONG)CurrTable < NtoskrnlLast)
{
HanTableListHead = CurrTable;
break ;
}
}
// 开始枚举
for (CurrTable = HanTableListHead -> Flink;
CurrTable != HanTableListHead;
CurrTable = CurrTable -> Flink)
{
PEprocess = * (PEPROCESS * )((PUCHAR)CurrTable - HandleTableListOffset + QuotaProcessOffset);
if (PEprocess)
{
FindAndCheckProcess(FALSE,(ULONG)PEprocess);
}
}
return STATUS_SUCCESS;
}
通过扫描当前进程的handletable获得进程表g
通过handletablelisthead获得进程表d
枚举的思路还是和上面一样,关键是要获取handletablelisthead的地址.
要找到HeadleTableListHead,我们要注意到HandleTableListHead是一个全局的内核变量,
因此它一定是在内核文件的某一个段(Section)里面,并且HandleTableList的其他成员是在动态分配的内存中,
所以总是受到内核地址空间的限制。根据这些,我们需要得到任何一个进程的HandleTable的指针,
然后遍历链表直到找到定位在这个内核地址空间的成员,那么这个成员就是HandleTableListHead了.
![ContractedBlock.gif](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
NTSTATUS FromCruentProEnumProcess()
{
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
{
return STATUS_UNSUCCESSFUL;
}
PEPROCESS PCurrentProc = IoGetCurrentProcess();
PLIST_ENTRY CurrList = NULL;
PEPROCESS PEprocess,Ptemp = PCurrentProc;
ULONG Phandle;
PLIST_ENTRY PCurrentHandleList = NULL;
// __
// HandleTableOffset = 0xc4
// HandleTableListOffset = 0x1c
// QuotaProcessOffset = 0x04
do
{
Phandle = (ULONG)( * (PULONG)((ULONG)PCurrentProc + HandleTableOffset));
PCurrentHandleList = (PLIST_ENTRY)(Phandle + HandleTableListOffset);
CurrList = PCurrentHandleList -> Flink;
// __
PEprocess = * (PEPROCESS * )((PUCHAR)CurrList - HandleTableListOffset + QuotaProcessOffset);
if (PEprocess)
{
// FindAndCheckProcess()函数检测进程的有效性..
FindAndCheckProcess(FALSE,(ULONG)PEprocess);
}
else
{
return STATUS_UNSUCCESSFUL;
}
// ULONG Phandle=(ULONG)(*(PULONG)((ULONG)PCurrentProc+HandleTableOffset));
PCurrentProc = PEprocess;
} while (PCurrentProc != Ptemp);
return STATUS_SUCCESS;
}