[note]枚举进程之二(句柄表分析篇)

继续接着上篇,这一篇说下句柄表,关于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,从而实现对进程的枚举.

 

 

代码
NTSTATUS HandletablelistheadEnumProcess()
{
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了. 

 

代码
//通过自身的HANDLETABLE枚举进程
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;

}

 

 

posted @ 2010-09-18 03:23  Tbit  阅读(1632)  评论(0)    收藏  举报