我是靠谱客的博主 魁梧巨人,最近开发中收集的这篇文章主要介绍 pspCidTable,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


一.                        pspCidTable概念及内核调试
-----------------------------------------------------


pspCidTable是内核未导出的HANDLE_TALBE结构,它保存着所有进程和线程对象的指针。
只要能遍历这个PspCidTable句柄表,就可以遍历到系统的所有进程,包括所有隐藏进程,除非你抹掉pspCidTable...

结构如下:


typedef struct _HANDLE_TABLE
{
   ULONG           Flags;
   LONG             HandleCount;
   PHANDLE_TABLE_ENTRY **Table;
   PEPROCESS         QuotaProcess;
   HANDLE           UniqueProcessId;
   LONG             FirstFreeTableEntry;
   LONG             NextIndexNeedingPool;
   ERESOURCE         HandleTableLock;
   LIST_ENTRY         HandleTableList;
   KEVENT           HandleContentionEvent;
} HANDLE_TABLE , *PHANDLE_TABLE ;


我们利用调试器先从内核里找到这张表:


kd> version
Windows XP Kernel Version 2600 (Service Pack 2) UP Free x86 compatible
Built by: 2600.xpsp_sp2_rtm.040803-2158
Kernel base = 0x804d7000 PsLoadedModuleList = 0x8055ab20
//...
dbgeng:   image 6.7.0005.1, built Wed Jun 20 11:50:35 2007
//...


kd> dd pspCidTAble
80560ce0   e10018c8 00000002 00000000 00000000
80560cf0   00000000 00000000 00000000 00000000
80560d00   00000000 00000000 00000000 00000000
80560d10   00000000 00000000 00000000 00000000


kd> dt _HANDLE_TABLE e10018c8
nt!_HANDLE_TABLE
   +0x000 TableCode         : 0xe1003000
   +0x004 QuotaProcess     : (null)
   +0x008 UniqueProcessId   : (null)
   +0x00c HandleTableLock   : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList   : _LIST_ENTRY [ 0xe10018e4 - 0xe10018e4 ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo         : (null)
   +0x02c ExtraInfoPages   : 0
   +0x030 FirstFree         : 0x770
   +0x034 LastFree         : 0x764
   +0x038 NextHandleNeedingPool : 0x800
   +0x03c HandleCount       : 266
   +0x040 Flags             : 1
   +0x040 StrictFIFO       : 0y1


注意NextHandleNeedingPool : 0x800,句柄表依靠NextHandleNeedingPool来计数的,
凡是大于0x800(2048)的部分就放入第二张表中.所以当CID(PID)号大于0x800时,
就需要查找第二张表了.例如要定位CID等于0x844的句柄表位置,
0xe1003800 + ( 0x844 - 0x800 ) * 2 这个偏移就是CID为0x844的句柄项了.


kd> dd e1003000
e1003000   00000000 fffffffe 81fc9a01 00000000
e1003010   81fc9789 00000000 81fc9341 00000000
e1003020   81fc8021 00000000 81fc8da9 00000000
e1003030   81fc8b31 00000000 81fc88b9 00000000
e1003040   81fc8641 00000000 81fc83c9 00000000
e1003050   81fc7021 00000000 81fc7da9 00000000
e1003060   81fc7b31 00000000 81fc78b9 00000000
e1003070   81fc7641 00000000 81fc73c9 00000000

kd> !object 81fc9a01
Object: 81fc9a01   Type: (0081fc90)
     ObjectHeader: 81fc99e9 (old version)
     HandleCount: 1073741824   PointerCount: 33554432

kd> !object 81fc9a00
Object: 81fc9a00   Type: (81fc9040) Process
     ObjectHeader: 81fc99e8 (old version)
     HandleCount: 2   PointerCount: 56

81fc9a00的进程是System...以后的都是其线程。

kd> !object 81fc73c9
Object: 81fc73c9   Type: (0081fc9e)
     ObjectHeader: 81fc73b1 (old version)
     HandleCount: 1879048192   PointerCount: 0

kd> !object 81fc73c8
Object: 81fc73c8   Type: (81fc9e70) Thread
     ObjectHeader: 81fc73b0 (old version)
     HandleCount: 0   PointerCount: 1

kd> !thread 81fc73c8
THREAD 81fc73c8   Cid 0004.003c   Teb: 00000000 Win32Thread: 00000000 WAIT: (WrQueue) UserMode Non-Alertable
     80561b7c   Unknown
Not impersonating
DeviceMap                 e10001e8
Owning Process             81fc9a00       Image:         System
Wait Start TickCount       16940           Ticks: 20342 (0:00:03:23.712)
Context Switch Count       418          
UserTime                   00:00:00.000
KernelTime                 00:00:03.124
//....

我们可以根据PID查找进程对象.HANDLE_TABLE的Entry地址+PID*2 。
在此之前,你可以用:

kd>!process 0 0
枚举所有的进程,然后再根据PID来查找验证。
我们以calc.exe PID738为例:


kd> dd e1003000+738*2
e1003e70   81f15da1 00000000 81ef26e1 00000000
e1003e80   81d1f021 00000000 00000000 00000124
e1003e90   00000000 00000744 00000000 00000150
e1003ea0   00000000 0000075c 00000000 00000750
e1003eb0   00000000 00000760 00000000 00000758
e1003ec0   00000000 00000764 00000000 00000768
e1003ed0   00000000 0000076c 00000000 00000774
e1003ee0   00000000 0000077c 00000000 00000770

kd> !object 81f15da1
Object: 81f15da1   Type: (0081fc90)
     ObjectHeader: 81f15d89 (old version)
     HandleCount: 1073741824   PointerCount: 33554432

kd> !object 81f15da0
Object: 81f15da0   Type: (81fc9040) Process
     ObjectHeader: 81f15d88 (old version)
     HandleCount: 2   PointerCount: 15

kd> !process 81f15da0
PROCESS 81f15da0   SessionId: 0   Cid: 0738     Peb: 7ffd5000   ParentCid: 03ec
     DirBase: 0ddf4000   ObjectTable: e1a222f8   HandleCount:   26.
     Image: calc.exe
//...


二.     获取pspCidTable的方法
-----------------------------------------------------
罗嗦几句,在得到pspCidTable后,我们可以有很多方法来实现枚举。
比如:
1.利用未导出的ExEnumHandleTable函数 ;
2.直接自己获取PHANDLE_TABLE_ENTRY等,然后PsLookupProcessByProcessId ;
(这里一直有误导人的意思,其实我本意是让大家自己实现这些个函数...)

回归正题:

1.在PsLookupProcessByProcessId函数中搜索特征串定位 PspCidTalbe.
这个方法也是本人用的方法,代码如下:


void GetPspCidTable()
{
     PUCHAR cPtr;
     unsigned char * pOpcode;
     ULONG Length;

     for (cPtr = (PUCHAR)PsLookupProcessByProcessId;
         cPtr < (PUCHAR)PsLookupProcessByProcessId + PAGE_SIZE;
         cPtr += Length)
     {
         Length = SizeOfCode(cPtr, &pOpcode);     //credit to LDasm.c by Ms-Rem

         if (!Length) break;

         if (*(PUSHORT)cPtr == 0x35FF && *(pOpcode + 6) == 0xE8)
         {
             pPspCidTable = **(PVOID **)(pOpcode + 2);
             break;
         }
     }
}

其中,全局变量:
PHANDLE_TABLE pPspCidTable = NULL;

当然,PsLookupProcessThreadByCid里也是一样的。



2.利用KDDEBUGGER_DATA(32/64)结构得到pspCidTable.
流程是这样的: KdEnableDebugger->KdInitSystem->KdDebuggerDataBlock/KDDEBUGGER_DATA(32) ->PspCidTable
当然也是内存字节搜索,比如006A006Ah,05C6006ah,0e801h,4742444bh等等。注意平台间的差异性.
当然简单点的方法是加 DebugGetKdVersionBlock参数用NtSystemDebugControl获取KdVersionBlock,再转到KdDebuggerDataBlock.
Windbg的SDK里wdbgexts.h中有相关的结构定义:

typedef struct _KDDEBUGGER_DATA32 {
     DBGKD_DEBUG_DATA_HEADER32 Header;
     ULONG   KernBase;
     ULONG   BreakpointWithStatus;       // address of breakpoint
     ULONG   SavedContext;
     USHORT   ThCallbackStack;             // offset in thread data
     USHORT   NextCallback;               // saved pointer to next callback frame
     USHORT   FramePointer;               // saved frame pointer
     USHORT   PaeEnabled:1;
     ULONG   KiCallUserMode;             // kernel routine
     ULONG   KeUserCallbackDispatcher;   // address in ntdll

     ULONG   PsLoadedModuleList;
     ULONG   PsActiveProcessHead;
     ULONG   PspCidTable;             // <--------- What we need!!
     //...
     ULONG   MmLoadedUserImageList;
} KDDEBUGGER_DATA32, *PKDDEBUGGER_DATA32;



三.     几种进程检测方法的对比
-----------------------------------------------------
1.既然要检测隐藏进程,那就先写个简单的隐藏吧。

typedef struct ServiceDescriptorEntry {
         unsigned int *ServiceTableBase;
         unsigned int *ServiceCounterTableBase; //Used only in checked build
         unsigned int NumberOfServices;
         unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;

__declspec(dllimport)   ServiceDescriptorTableEntry_t KeServiceDescriptorTable;

嗯,ssdt,hook掉ZwQuerySystemInformation。网上代码多如牛毛,就不多浪费时间现眼了。
if(0 == memcmp(SystemProcesses->ProcessName.Buffer, L"services.exe", 24))
把services.exe藏起来吧...



2.枚举进程,用户态下最多的(包括taskmgr)就是ZwQuerySystemInformation了。
真是哪壶不开提哪壶啊,哈哈...

     ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, pBuffer,
                         cbBuffer, NULL);
  
     PSYSTEM_PROCESS_INFORMATION pInfo =
         (PSYSTEM_PROCESS_INFORMATION)pBuffer;
  
     for (;;)
     {
         printf("ProcessID: %d (%ls)/n", pInfo->ProcessId,
             pInfo->ProcessName.Buffer);
      
         if (pInfo->NextEntryDelta == 0)
             break;
  
         pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo)
             + pInfo->NextEntryDelta);
     }

分析结果:
================================================
ProcessID: 0 ((null))
ProcessID: 4 (System)
ProcessID: 312 (smss.exe)
ProcessID: 404 (csrss.exe)
ProcessID: 428 (winlogon.exe)
ProcessID: 492 (lsass.exe)
ProcessID: 636 (svchost.exe)
ProcessID: 704 (svchost.exe)
ProcessID: 740 (svchost.exe)
ProcessID: 780 (svchost.exe)
ProcessID: 1004 (explorer.exe)
ProcessID: 1024 (vmsrvc.exe)
ProcessID: 1152 (vpcmap.exe)
ProcessID: 1320 (vmusrvc.exe)
ProcessID: 1512 (svchost.exe)
ProcessID: 1816 (InstDrv.exe)
ProcessID: 1836 (notepad.exe)
ProcessID: 1848 (calc.exe)
ProcessID: 200 (dv.exe)
ProcessID: 344 (InstDrv.exe)
ProcessID: 356 (enumprocsnt.exe)
==================================================
没有services.exe...



3.该我们的 get PspCidTable to ExEnumHandleTable 出马了。
为了方便,代码做了很多简略处理,EnumHandleCallback没帖出来,大致就是自己walk list,实际的代码中你可以充分利用
ExEnumHandleTable

我写的代码只为演示PspCidTable,walk list就不繁琐的贴上来了。凑合着看吧 :-)

ULONG GetFunctionAddr(IN PCWSTR FunctionName)
{
     UNICODE_STRING UniCodeFunctionName;
     RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
         return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName );
}



NTSTATUS
DriverEntry( IN PDRIVER_OBJECT   DriverObject,
     IN PUNICODE_STRING RegistryPath )
{
     HANDLE h;  

     DbgPrint("DriverEntry Loading.../n");
     DriverObject->DriverUnload = DriverUnload;  
  
     ExEnumHandleTable = (EXENUMHANDLETABLE)GetFunctionAddr(L"ExEnumHandleTable");
     if ( ExEnumHandleTable == NULL )
         {
         DbgPrint("Get ExEnumHandleTable Addr Error!!");
         return STATUS_DEVICE_CONFIGURATION_ERROR;
     }
     DbgPrint("Address of ExEnumHandleTable:%x/n",ExEnumHandleTable);

     GetPspCidTable();
     DbgPrint("CidTable:%x/n",pPspCidTable);
  
     if (!ExEnumHandleTable(pPspCidTable, EnumHandleCallback, NULL, &h ))
     {
         DbgPrint( "HandleTable Found: %lx./n", h );
     }
     else
     {
         DbgPrint( "HandleTable not found./n");
     }      
  
     return STATUS_SUCCESS;
}


分析结果:
==================================================
Address of ExEnumHandleTable:805a4274
CidTable:e10018c8
PID:   0 NAME: Idle
PID:   4 NAME: System
PID: 200 NAME: dv.exe        
PID: 312 NAME: smss.exe      
PID: 344 NAME: InstDrv.exe  
PID: 356 NAME: enumprocsnt.exe
PID: 404 NAME: csrss.exe    
PID: 428 NAME: winlogon.exe  
PID: 480 NAME: services.exe  
PID: 492 NAME: lsass.exe    
PID: 636 NAME: svchost.exe  
PID: 704 NAME: svchost.exe  
PID: 740 NAME: svchost.exe  
PID: 780 NAME: svchost.exe  
PID:1004 NAME: explorer.exe  
PID:1024 NAME: vmsrvc.exe    
PID:1152 NAME: vpcmap.exe    
PID:1320 NAME: vmusrvc.exe  
PID:1512 NAME: svchost.exe  
PID:1816 NAME: InstDrv.exe  
PID:1836 NAME: notepad.exe  
PID:1848 NAME: calc.exe      
Handle Found: bb40.
=====================================================
看起来蛮强大,连死进程也能读出来...   :-)



4.ring3下的间接pspCidTable,看代码:

void main()
{  
     for (int i=0;i<=65535;i+=4)     //实际上,取到0x4e1c就够了
     {
         if(OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,i)!= 0)
         {
             printf("ProcessID: %d/n",i);
             CloseHandle(&i);
         }
     }  
  
     getchar();  
}

很猥琐的代码,哈哈...
想得到更多信息,自己#include "psapi.h",把lib库link上去,然后:
EnumProcessModules,GetModuleFileNameEx,GetProcessImageFileName等等吧...
至于想比较出哪些是隐藏进程或者恶意进程,EnumProcesses或者CreateToolhelp32Snapshot,
得到用户态下的一张表对比就知道了。


分析结果:
=============================================
ProcessID: 4
ProcessID: 200
ProcessID: 312
ProcessID: 344
ProcessID: 356
ProcessID: 428
ProcessID: 480
ProcessID: 492
ProcessID: 636
ProcessID: 740
ProcessID: 852
ProcessID: 1004
ProcessID: 1024
ProcessID: 1152
ProcessID: 1320
ProcessID: 1836
ProcessID: 1848
==============================================
ProcessID: 480,嗯,是services.exe...

其实原理很简单,OpenProcess->NtOpenProcess->PsLookupProcessByProcessId,
反汇编PsLookupProcessByProcessId,有这样一段代码:
mov     eax, large fs:124h
push     [ebp+ProcessId]
mov     esi, eax
dec     dword ptr [esi+0D4h]
push     PspCidTable
call     ExMapHandleToPointer

还原出来是这样的:
PHANDLE_TABLE_ENTRY CidEntry;
CidEntry = ExMapHandleToPointer(PspCidTable, ProcessId);

像PsLookupProcessThreadByCid,PspExitThread,PspCreateThread等也有类似的代码,
但是由于入函数太深,查找不方便。

-----------------------------------
综合起来看,pspCidTable枚举进程还是很成功的,一般的进程隐藏技术都过不了它。 :-)



四.     anti-pspCidTable技术及其他
-----------------------------------------------------

写这篇的原因仅仅是先前有人在maillist上问起,这个技术并不神秘,想anti它很简单,抹掉pspCidTable就行了,
扫描内存得到pspCidTable,然后对应EPROCESS进行分析,找到要隐藏的进程,将相关信息置成0,安放一个进程notify routine。
其他检测技术,比如用activelist,handletable,handletablelisthead获得进程表,
挂钩KiSwapContext,KiService,CreateProcessNotifyRoutine等等...
隐藏进程的方法多种多样,也有很多高深的技术,未公开的技术,谁叫RK和ARK技术总是相生相克的呢...
:-)
本文章转自http://hi.baidu.com/gz1x/blog/item/d99aeefa4d1c92ddb48f31b9.html

最后

以上就是魁梧巨人为你收集整理的 pspCidTable的全部内容,希望文章能够帮你解决 pspCidTable所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(39)

评论列表共有 0 条评论

立即
投稿
返回
顶部