一、PspCidTable概述

PspCidTable也是一个句柄表,其格式与普通的句柄表是完全一样的,但它与每个进程私有的句柄表有以下不同:

1.PspCidTable中存放的对象是系统中所有的进程线程对象,其索引就是PID和TID。

2.PspCidTable中存放的直接是对象体(EPROCESS和ETHREAD),而每个进程私有的句柄表则存放的是对象头(OBJECT_HEADER)。

3.PspCidTable是一个独立的句柄表,而每个进程私有的句柄表以一个双链连接起来。
注意访问对象时要掩掉低三位,每个进程私有的句柄表是双链连接起来的,实际上ZwQuerySystemInformation枚举系统句柄时就是走的这条双链,隐藏进程的话,这条链也是要断掉的~~在遍历进程活动链表(ActiveProcessLinks)、DKOM隐藏进程时,还要把隐藏进程的句柄表从链表中摘去。

二、PspCidTable相关调用

1.系统初始化时调用PspInitPhase0()初始化进程管理子系统,此时初始化进程活动链表和PspCidTable。

初始化进程活动链表:
// Initialize active process list head and mutex

InitializeListHead (&PsActiveProcessHead);

初始化PspCidTable:
   // Initialize CID handle table.
   // N.B. The CID handle table is removed from the handle table list so
   //      it will not be enumerated for object handle queries.

PspCidTable = ExCreateHandleTable (NULL);
   if (PspCidTable == NULL) {
       return FALSE;
   }
   // Set PID and TID reuse to strict FIFO. This isn’t absolutely needed but
   // it makes tracking audits easier.
   ExSetHandleTableStrictFIFO (PspCidTable);

ExRemoveHandleTable (PspCidTable);      //使得PspCidTable独立于其它句柄表

2.进程创建时,PspCreateProcess()在PspCidTable中以进程对象创建句柄,视为PID。
    // Create the process ID

CidEntry.Object = Process;
    CidEntry.GrantedAccess = 0;
    Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry);          //进程的PID就是这么创建的
    if (Process->UniqueProcessId == NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto exit_and_deref;
    }

3.线程创建时,PspCreateThread()在PspCidTable中以线程对象创建句柄,视为TID。
   // Assign this thread to the process so that from now on
   // we don’t have to dereference in error paths.
   Thread->ThreadsProcess = Process;

Thread->Cid.UniqueProcess = Process->UniqueProcessId;

CidEntry.Object = Thread;
   CidEntry.GrantedAccess = 0;
   Thread->Cid.UniqueThread = ExCreateHandle (PspCidTable, &CidEntry);

if (Thread->Cid.UniqueThread == NULL) {
       ObDereferenceObject (Thread);
       return (STATUS_INSUFFICIENT_RESOURCES);
   }

可以清楚地知道:PID和TID分别是EPROCESS和ETHREAD对象在PspCidTable这个句柄表中的索引。

4.进程和线程的查询,主要是以下三个函数,按照给定的PID或TID从PspCidTable从查找相应的进线程对象。
PsLookupProcessThreadByCid()
PsLookupProcessByProcessId()
PsLookupThreadByThreadId()
其中有如下调用:
CidEntry = ExMapHandleToPointer(PspCidTable, ProcessId);
CidEntry = ExMapHandleToPointer(PspCidTable, ThreadId);

ExMapHandleToPointer内部仍然是调用ExpLookupHandleTableEntry()根据指定的句柄查找相应的HANDLE_TABEL_ENTRY,从而获取Object。

5.线程退出时,PspThreadDelete()在PspCidTable中销毁句柄。

if (Thread->Cid.UniqueThread != NULL) {
       if (!ExDestroyHandle (PspCidTable, Thread->Cid.UniqueThread, NULL)) {
           KeBugCheck(CID_HANDLE_DELETION);
       }
   }

6.进程退出时,PspProcessDelete()在PspCidTable中销毁句柄。
// Remove the process from the global list
if (Process->ActiveProcessLinks.Flink != NULL) {
       CurrentThread = PsGetCurrentThread ();

PspLockProcessList (CurrentThread);
       RemoveEntryList (&Process->ActiveProcessLinks);
       PspUnlockProcessList (CurrentThread);
   }

if (Process->UniqueProcessId) {
       if (!(ExDestroyHandle (PspCidTable, Process->UniqueProcessId, NULL))) {
           KeBugCheck (CID_HANDLE_DELETION);
       }
   }

如果进线程退出时,销毁句柄却发现句柄不存在造成ExDestroyHandle返回失败,就会蓝屏!!!
所以抹了PspCidTable来隐藏的进程,在退出时必须把进线程对象再放回去。

三、PspCidTable表及结构分析

在windows下所有的资源都是用对象的方式进行管理的,诸如:文件、进程、设备等都是对象。当要访问一个对象的时候,如打开一个文件,系统就会创建一个对象句柄,通过这个句柄可以对这个文件进行各种操作。句柄和对象的联系是通过句柄表来进行的,准确来说一个句柄就是它所对应的对象在句柄表中的索引。通过句柄,可以在句柄表中找到对象的指针,通过指针对对象进行操作。PspCidTable就是这样的一种表,它不属于任何进程,也不连接在系统的句柄表上,通过它可以反问系统的任何对象。遍历PspCidTable句柄表首先要获取PspCidTable表的内存地址。由于进程活动是动态的,一个进程包括多个线程,windows7操作系统的PspCidTable句柄表采用多层表及动态扩展的方法保存进程和线程对象的指针。因此需要分析PspCidTable句柄表的层次结构,写出遍历PspCidTable表检测隐藏进程的算法,最后变成实现自动隐藏进程。

1.获取PspCidTable的地址

上面我们说到PsLookupProcessByProcessId这个函数调用了PspCidTable,所以我们来反汇编一下。

所以我们可以使用0x3d8b和0xe8作为特征码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ULONG GetPspCidTable()
{
    UNICODE_STRING uniPsLookup;
    ULONG psLookbyidAddr;
    ULONG pspCidTableAddr;
    PUCHAR cPtr;
    RtlInitUnicodeString(&uniPsLookup, L"PsLookupProcessByProcessId");
    psLookbyidAddr = (ULONG)MmGetSystemRoutineAddress(&uniPsLookup);

for (;;psLookbyidAddr++)
    {
        //xp => 0x35ff
        if((0x3d8b == (*(PUSHORT)psLookbyidAddr)) && (0xe8 == (*(PUCHAR)(psLookbyidAddr+6))))
        {
            pspCidTableAddr = *(PULONG)(psLookbyidAddr + 2);
            break;
        }
    }

return pspCidTableAddr;
}

2.PspCidTable的层次结构

PspCidTable是一个HANDLE_TALBE结构,当新建一个进程时,对应的会在PspCidTable存在一个该进程和线程对应的HANDLE_TABLE_ENTRY项。在windows7中采用动态扩展的方法,当句柄数少的时候就采用下层表,多的时候才启用中层表或上层表。

句柄表分为三层,下层表是一个HANDLE_TABLE_ENTRY项的索引,整个表共有256个元素,每个元素是一个8个字节长的HANDLE_TABLE_ENTRY项及索引,HANDLE_TABLE_ENTRY项中保存着指向对象的指针,下层表可以看成是进程和线程的稠密索引。中层表共有256个元素,每个元素是4个字节长的指向下层表的入口指针及索引,中层表可以看成是进程和线程的稀疏索引。上层表共有256个元素,每个元素是4个字节长的指向中层表的入口指针及索引,上层表可以看成是中层表的稀疏索引。一个句柄表有一个上层表,一个上层表最多可以有256个中层表的入口指针,每个中层表最多可以有256个下层表的入口指针,每个下层表最多可以有256个进程和线程对象的指针。PspCidTable表可以看成是HANDLE_TBALE_ENTRY项的多级索引。

3.确定PspCidTable的层次

上面我们从windbg中看到PspCidTable的地址为8298aeb4。我们来看看这个地址的内容:

上面87a011d8是HANDLE_TBALE的地址,我们来看看HANDLE_TBALE的结构:

这里的TbaleCode记录这句柄表的地址,如果后两位是00则采用的是一层表,01是两层表,10是三层表。通过上面的TableCode就可以判断这个系统采用了两层表的结构。

如果系统采用了两层或者三层的时候,TableCode就不是句柄表的地址了,把这个值后两位置为0之后,则是一个指向多层表的指针。

来看看这个指针指向的值:

kd> dd 0×92500000
92500000  87a04000 92501000 00000000 00000000

根据上面说的,那么这个87a04000 指向的肯定是一个HANDLE_TBALE_ENTRY的数组。

_HANDLE_TABLE_ENTRY是两个32位的结构体。

一个指向对象的指针                一个是32位的标志

一定要记得,低3位值不是我们需要的,应该置0 。即:value & 0xFFFFFFF8。

kd> !Object 841ce020 
Object: 841ce020  Type: (84133718) Process
    ObjectHeader: 841ce008 (new version)
    HandleCount: 3  PointerCount: 128

而且这个对象就是pid为4的system.exe进程。

我们已经找到了句柄值为0004的内核对象。

OK,冒出了句柄值,那么句柄值是如何被关联起来呢?想下0004,它对应_HANDLE_TABLE_ENTRY表项的低几个啊?

句柄值为0×0000代表是NULL

刚好_HANDLE_TABLE_ENTRY的第0个表项为无效值

句柄值为0×0004有效

刚好指的是_HANDLE_TABLE_ENTRY的第1个表项。

那句柄值为0×0008了?

原来句柄值总是4的倍数。值/4就代表句柄表项数组_HANDLE_TABLE_ENTRY的索引啊

这时,句柄值的低两位永远是0啦,为啥呢?是4的倍数,第2为不就为0?自己算算

0×00,0×04,0×08,0×10,0×14等等的二进制

既然第2位永远为0,那么微软就利用了这两位做一个标志位,用来指示当前句柄值所代表的内核对象到那个表项数组中找到。(表示层次结构)

四、遍历PspCidTable表枚举进程

1.首先找到PspCidTable的地址。

2.然后找到HANDLE_TBALE的地址。

3.根据TableCode来判断层次结构。

4.遍历层次结构来获取对象地址。

5.判断对象类型是否为进程对象。

6.判断进程是否有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
VOID RefreshProcessByPspCidTable()
{
    ULONG PspCidTable = 0;
    ULONG HandleTable = 0;
    ULONG TableCode = 0;
    ULONG flag = 0;
    PEPROCESS    pCsrssEprocess = NULL;

LookupProcessByName("CSRSS.EXE\0", &pCsrssEprocess);
    //这个是通过获取PspCidTable的地址来的。
    PspCidTable = GetPspCidTable();
    HandleTable = *(PULONG)PspCidTable;

//这个是通过直接从csrss.exe进程获取HandleTable
    //HandleTable = *(PULONG)((ULONG)pCsrssEprocess + ObjectTable);

TableCode = *(PULONG)HandleTable;
    flag = TableCode & 3;   //最后两位
    TableCode &= 0xfffffffc;

switch (flag)
    {
    case 0:   //一层表
        BrowerTableL3(TableCode);
        break;
    case 1:  //二层表
        BrowerTableL2(TableCode);
        break;
    case 2:  //三层表
        BrowerTableL1(TableCode);
        break;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
ULONG BrowerTableL3(ULONG TableAddr)
{
    ULONG Object = 0;
    ULONG ItemCount = 511;
    DWORD dwProcessId = 0;
    ULONG flags;

do{
        TableAddr += 8;
        Object = *(PULONG)TableAddr;
        Object = Object & 0xfffffff8;  //Object最后三位置0,Object即为EPROCESS的地址

if(Object == 0)
        {
            continue;
        }

if(!MmIsAddressValid((PVOID)Object))
        {
            continue;
        }

if(GetProcessType() == *(PULONG)ObGetObjectType((PVOID)Object))
        {
            dwProcessId = *(PULONG)(Object + PIDOFFSET);
            if(dwProcessId < 65536 && dwProcessId != 0)
            {
                flags = *(PULONG)(Object + 0x270);
                //flags显示进程没有退出
                if((flags & 0xc) != 0xc)
                {
                    DbgPrint("ProcessId:  %d\r\n",dwProcessId);
                    DbgPrint("ProcessName:  %s\r\n", PsGetProcessImageFileName((PEPROCESS)Object));
                    /*if(dwProcessId == 960)
                    {
                    InterlockedExchangePointer(&((PHANDLE_TABLE_ENTRY)TableAddr)->Object, NULL);
                    }*/
                    //进程退出时会调用ExDestroyHandle()销毁句柄,若找不到就会蓝屏,所以要小心在进程退出的时候恢复
                }
            }
        }
    }while (--ItemCount > 0);

return 0;
}

ULONG BrowerTableL2(ULONG TableAddr)
{
    do{
        BrowerTableL3(*(PULONG)TableAddr);
        TableAddr += 4;
    }while((*(PULONG)TableAddr) != 0);

return 0;
}

ULONG BrowerTableL1(ULONG TableAddr)
{
    do{
        BrowerTableL2(*(PULONG)TableAddr);
        TableAddr += 4;
    }while((*(PULONG)TableAddr) != 0);

return 0;
}

扩展:EPROCESS里的ObjectTable保存的是该进程对象的地址。PspCidTable保存所有句柄的系统对象。

本文链接:http://www.blogfshare.com/details-in-pspcidtbale.html

关于PID遍历PspCidTable表 参考这个 http://blog.csdn.net/whatday/article/details/17189093   表的大小应该是0x800

jpg gai rar

遍历PspCidTable表检测隐藏进程的更多相关文章

  1. Ring0隐藏进程的方法

    第一种在系统调用服务表HOOK ZwQuerySystemInformation函数地址 使用InterlockedExchange函数将ZwQuerySystemInformation在内核导出表K ...

  2. 遍历进程活动链表(ActiveProcessLinks)、DKOM隐藏进程

    1.EPROCESS结构体 EPROCESS块来表示.EPROCESS块中不仅包含了进程相关了很多信息,还有很多指向其他相关结构数据结构的指针.例如每一个进程里面都至少有一个ETHREAD块表示的线程 ...

  3. 隐藏进程中的模块绕过IceSword的检测

    标 题: [原创] 隐藏进程中的模块绕过IceSword的检测 作 者: xPLK 时 间: 2008-06-19,17:59:11 链 接: http://bbs.pediy.com/showthr ...

  4. 【旧文章搬运】再谈隐藏进程中的DLL模块

    原文发表于百度空间,2009-09-17========================================================================== 相当老的话 ...

  5. 修改ActiveProcessLinks链表隐藏进程

    在Windows内核中有一个活动进程链表AcvtivePeorecssList.它是一个双向链表,保存着系统中所有进程的EPROCESS结构.特别地,进程的EPROCESS结构包含一个具有指针成员FL ...

  6. 遍历注册表回调函数(仿PCHunter CmpBack)

    遍历注册表回调函数(仿PCHunter CmpBack) typedef struct _CAPTURE_REGISTRY_MANAGER { PDEVICE_OBJECT deviceObject; ...

  7. mysql存储过程之游标遍历数据表

    原文:mysql存储过程之游标遍历数据表 今天写一个mysql存储过程,根据自己的需求要遍历一个数据表,因为对存储过程用的不多,语法不甚熟悉,加之存储过程没有调试环境,花了不少时间才慢慢弄好,故留个痕 ...

  8. 安全之路 —— 借助DLL进行远程线程注入实现穿墙与隐藏进程

    简介        大多数后门或病毒要想初步实现隐藏进程,即不被像任务管理器这样典型的RING3级进程管理器找到过于明显的不明进程,其中比较著名的方法就是通过远程线程注入的方法注入将恶意进程的DLL文 ...

  9. Servlet会话管理一(URL重写和表单隐藏域)

    会话可以简单的理解为客户端用户打开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器的整个过程称为一个会话.即一个客户端用户和服务器端进行通讯的过程,也是客户端和服务器端之间的数据传 ...

随机推荐

  1. 基于隐马尔科夫模型(HMM)的地图匹配(Map-Matching)算法

    文章目录 1. 1. 摘要 2. 2. Map-Matching(MM)问题 3. 3. 隐马尔科夫模型(HMM) 3.1. 3.1. HMM简述 3.2. 3.2. 基于HMM的Map-Matchi ...

  2. ios 在ios9中 NSNotificationCenter addObserver 不会影响对象释放

    如题,ios9上,  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@&qu ...

  3. printf 的场宽

    这个经常忘记,从百度直到上搜到的,做个记录. 可以在"%"和字母之间的数字表示最大场宽.例如: %3d 表示输出3位整型数, 不够3位右对齐.%9.2f 表示输出场宽为9的浮点数, ...

  4. cxGRID中的字段怎么能以0.00的格式显示

    CXGRID中的字段如何能以0.00的格式显示在CXGRID中如何让字段能以0.00的格式显示,我的字段是FLOAT类型,满意的马上给分! ------解决方案-------------------- ...

  5. nyoj_95_众数问题_map练习

    众数问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 所谓众数,就是对于给定的含有N个元素的多重集合,每个元素在S中出现次数最多的成为该元素的重数, 多重集合S重 ...

  6. ClassLoad的加载过程及分析

    -Xbootclasspath:bootclasspath 让jvm从指定路径(可以是分号分隔的目录.jar.或者zip)中加载bootclass,用来替换jdk的rt.jar:若非必要,一般不会用到 ...

  7. ios二维码生成

    二维码扫描现在已经有很多的库可以使用了,常用的有ZXing和ZBar.如果感兴趣的同学可以自行研究. libqrencode介绍:是一个用C语言编写的用来解析二维条形码(QR Code)的程序库,li ...

  8. Web上的支持的图片格式以及它们之间的区别

    一.GIF(图形交换格式) GIF格式的图片最多只能保存256中颜色,该格式支持透明色,支持动画效果. 二.JPEG(联合图像专家组) JPEG格式不支持透明色及动画,颜色可达1670种. 三.PNG ...

  9. 数据结构One_Vector(向量的简单实现)

    #include <iostream> using namespace std; template<typename Object> class Vector { privat ...

  10. Mysql之多源复制

    在复制时,可以有多个Master.这些Master不进行冲突检查拓扑到Slave.在使用多源复制时对Slave的表存储格式是有要求的,必须要基于table存储而非文件存储[require table ...