首页
论坛
课程
招聘
雪    币: 461
活跃值: 活跃值 (65)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝

[原创]PspCidTable杂谈

2008-2-15 21:31 26918

[原创]PspCidTable杂谈

2008-2-15 21:31
26918
今天再次看了gz1X牛的文章,回忆了许多知识. 哈哈. 玩了很长时间游戏,写点儿文字算是对自责的一种安慰

要说R0枚举隐藏进程, 只是对没有抹掉PspCidTable而言的.gz1X牛提示了2种方法:

1. 利用未导出的ExEnumHandleTable函数
2. 获取PHANDLE_TABLE_ENTRY等,然后PsLookupProcessByProcessId


偶觉得都不是很保险(假设暂且PspCidTable没有被抹掉), ExEnumHandleTablePsLookupProcessByProcessId等都可以做手脚,简单的inline hook之类的.再说这些函数最终还是通过 PHANDLE_TABLE结构中的TableCode得到一些HANDLE_TABLE_ENTRY,到其中的Object里,取得相应的进程对象. 还不如自己来完成,嘿嘿. 安全感啊~

"第8个男人" 的code里面就做的很好,找到PspCidTable后自己搜索所有的进程对象就行了:

//----------------------------------------------------------------------
VOID
IsValidProcess ()
/*++

Author : 第8个男人
Leaner : sudami [xiao_rui_119@163.com]
Time   : 08/02/15
                                          
参数 : NULL
                                               
返回 : NULL

                                                  
功能 :
  给出PspCidTable的地址,结合_EXHANDLE、HANDLE_TABLE、HANDLE_TABLE_ENTRY
  搜索到每个进程对象,纪录之.
  前提条件: 假设暂且PspCidTable没有被抹掉
                                                          
--*/


{
        ULONG PspCidTable;
        ULONG TableCode;
        ULONG table1,table2;
        ULONG object,objectheader;
        ULONG NextFreeTableEntry;
        ULONG processtype,type;
        ULONG flags;
        ULONG i;

        PspCidTable = GetCidAddr(); // 搜索PsLookupProcessByProcessId函中的特征串即可
        processtype = GetProcessType();

        if (PspCidTable == 0) {
                return ;
        } else { //TableCode的低2位决定句柄表的级数
               
                TableCode = *(PULONG) ( *(PULONG) PspCidTable );

                if ( (TableCode & 3) == 0 ) { // 0级
                        table1 = TableCode;
                        table2 = 0;
                } else if ( (TableCode & 3 ) == 1 ) { // 1级
                        TableCode = TableCode & 0xfffffffc;
                        table1    = *(PULONG)TableCode;
                        table2    = *(PULONG)( TableCode + 4 );
                }

                // 对cid从0x0到0x4e1c进行遍历
                for (i = 0; i < 0x4e1c; i++) {
                        if (i <= 0x800) { // 第一张表中
                                if (MmIsAddressValid( (PULONG)(table1 + i*2) )) {  

                                        // HANDLE_TABLE_ENTRY地址 + PID * 2 ---> 对象的地址
                                        object = *(PULONG)( table1 + i*2 );  
                                        if (MmIsAddressValid( (PULONG)(table1 + i*2 + NEXTFREETABLEENTRY) )) {

                                                // 验证HANDLE_TABLE_ENTRY的合法性,这在ExEnumHandleTable函数的代码中也有
                                                // 正常的_HANDLE_TABLE_ENTRY中NextFreeTableEntry应该为0
                                                NextFreeTableEntry = *(PULONG)(table1 + i*2 + NEXTFREETABLEENTRY);
                                            if (NextFreeTableEntry == 0) {
                                                        // 去掉低3位掩码标志
                                                    object = ((object | 0x80000000) & 0xfffffff8); // 转换为对象(体)指针
                                                objectheader = (ULONG) \
                                                                OBJECT_TO_OBJECT_HEADER(object); // 获取对象(头)指针

                                                if (MmIsAddressValid( (PULONG)(objectheader + TYPE) )) {
                                                        type = *(PULONG)(objectheader + TYPE);
                                                                if (type == processtype) { // 是否为进程对象

                                                                    flags = *(PULONG)( (ULONG)object + \
                                                                                GetPlantformDependentInfo(OFFSET_EPROCESS_FLAGS) );
                                                                    if ((flags&0xc) != 0xc)
                                                                                RecordInfo( object ); //flags显示进程没有退出
                                                                }
                                                        }
                                                }
                                        }
                                }
                        } else { // 第2张表
               
                                if (table2 != 0) {
                                        // 步骤同上,只是object的获取为: HANDLE_TABLE_ENTRY地址 + (PID- 0x800)*2
                                        if (MmIsAddressValid( (PULONG)(table2 + (i - 0x800)*2) )) {
                                                object = *(PULONG)(table2 + (i - 0x800)*2);
                                                if (MmIsAddressValid((PULONG)((table2+(i-0x800)*2)+NEXTFREETABLEENTRY))) {
                                                        NextFreeTableEntry=*(PULONG)((table2+(i-0x800)*2)+NEXTFREETABLEENTRY);
                                                    if (NextFreeTableEntry==0x0) {
                                                            object = ((object | 0x80000000) & 0xfffffff8);
                                                        objectheader = (ULONG) OBJECT_TO_OBJECT_HEADER(object);
                                                        if (MmIsAddressValid( (PULONG)(objectheader + TYPE) )) {
                                                            type = *(PULONG)(objectheader + TYPE);

                                                        if(type == processtype) {       
                                                                            flags = *(PULONG) ((ULONG)object + \
                                                                                        GetPlantformDependentInfo(OFFSET_EPROCESS_FLAGS));
                                                                        if ((flags&0xc) != 0xc)
                                                                                        RecordInfo(object);
                                                                        }
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                }
        }
}

当然,用ExEnumHandleTable也是不错的. Windbg把它的实现看了一遍, 和WRK上的无任何出入[XP SP2], 实现起来也很方便

R3下就更简单了.一阵OpenProcess就把部分隐藏进程揪出来了:

#include <iostream.h>
#include <stdio.h>
#include <windows.h>

void main()
{           
        int a = 0;
        for (int i=0;i<=65535;i+=4)  {
                if(OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,i)!= 0) {
                        a++;
                        cout << "ProcessID: " << i << endl;                 
                        CloseHandle(&i);
                }
        }

        cout << "Total Counts = " << a << endl;
}

不过俺看了下,好像有些进程没有显示出来,比如SVCHOST.exe、SMSS.EXE...
嘿嘿,结合下面这些就更好搞了 [V大语录]:


killvxk的驱动查进程:
1.native api获得进程表a
2.通过activelist获得进程表b
3.通过pspCidTable获得进程表c
4.通过handletablelisthead获得进程表d
5.通过csrss的handletable用2种方法枚举获得进程表e和f
6.通过扫描当前进程的handletable获得进程表g
7.遍历表c的每一个进程的SessionProcessLinks获得进程表h
8.遍历表c的每一个进程Vm.WorkingSetExpansionLinks获得进程表i
9.通过Typelist分别取process和thread的表j和表k
10.通过表k得到进程表l
11.搜索内存中的threadobject和processobject得到进程表m
12.通过Wait/Dispatch得到进程表n
13.如果系统是Win2003以上遍历表c的每一个进程的MmProcessLinks得到表o
14.综合上面的进程表得到表p
15.对表p每一个进程做HandleTable,Vm.WorkXX,MmProcessXX,SessionProcessList扫描得到表q
16.枚举HWNDHandle得到进程表r
17.枚举JobObject得到表s
18.综合得表t,此时枚举结束~~

动态部分:
KiReadyThread
和KiSwapContext的钩子
还有KiService钩子
还有CreateProcessNotifyRoutine和CreateThreadNotifyRoutine
NtCreateThread钩子
动态维护一张表,静态枚举结束后综合两表~


其实,隐藏进程越来越难了,保护进程还让其稳定更加不易. 但俺们小菜还是需要掌握这些老掉的技术的,不断学习,不断进步

目前通过job杀进程MS很流行,内存清零也很厉害(就怕attach不上),插APC,杀线程之流的....杀杀IS等不在话下.
不过一般搜索到未导出的PspTerminateProcessByPointer,再深入点儿把这个函数,甚至PspExitThread 都自己实现一遍(用Windbg看了遍PspTerminateThreadByPointer,发现和WRK上的一样.哈哈,没有任何出入 [XP SP2]), Kill掉大部分进程是木有问题的,不过像KV2008这样猥亵的家伙在投递APC上下了手脚,就要先恢复了inline hook再杀了.
炉子牛的R0那个干掉KV2008的好像就是这样搞的.哈哈, 丢个驱动在这里,供有兴趣的IDA look之(应该无壳). R3下搞KV 炉子出了个录象,很神秘的样子,不过好像是成功了.哈哈

对了.炉子牛(又是炉子...)放了个simple task, R3下的简单管理器 [VB],---
检测隐藏进程的思路很科普:
一个EnumProcess or ToolHelp32来做对照, 一个ZwQuerySsytemInformation来走过场,一个从0 到65535的遍历OpenProcess来达到PspCidTable的效果. 3个途径来扫盲式的检测,方法是科普点儿.

结束进程部分很扫盲(原作者: EST的willy123牛):
ZwCreateJobObject-->ZwAssignProcessToJobObject-->ZwTerminateJobObject
对付IS这样inlie hook 了NtOpenProcess等的就用了一种很和谐的方法,FlowerCode提供滴.不过是VB写的, 俺无聊把其转化为了C描述,方便些:

//--------------------------------------------------------------
// btw: 头文件的那些申明自己写咯~
// 调用前要有SE_DEBUG权限
HANDLE
SDM_OpenProcess (
  DWORD dwDesiredAccess,
  BOOL  bInhert,
  DWORD ProcessId,
  BOOL  bOpenIt,
  LPDWORD aryPids
  )
/*++
                                 
原作者 : FlowerCode | 炉子 [VB]
转换者 : sudami [xiao_rui_119@163.com] [C]
Time   : 08/02/12
                                  
参数 :
  dwDesiredAccess - 希望以怎样的方式打开进程                                         
  bInhert - 是否有继承权限                                  
  ProcessId - PID
  bOpenIt - 是要打开进程,还是要保存所有进程ID
  paryPids - 保存PID的数组
                                                 
返回 :
  成功 - 指定的进程句柄
                                                  
  失败 - 0
                                                         
功能 :
                                                          
  1. 复制来复制去,总之是要获得指定进程的句柄
  2. 用普通方法得到所有进程ID

--*/
                                                                  
{
    ULONG              cbBuffer = 0x1000;  //先设定一个较小的缓冲空间
        ULONG              uRetSzie;
    ULONG              NumOfHandle = 0;
    PCHAR              pBuffer = NULL;
    PVOID              pOneEprocess = 0;
    CLIENT_ID          cid;
        NTSTATUS           st;
    OBJECT_ATTRIBUTES  oa;
    PROCESS_BASIC_INFORMATION        pbi;
    PSYSTEM_HANDLE_TABLE_ENTRY_INFO  h_info;
    HANDLE hProcessToDup, hProcessCur, hProcessToRet;
       
    oa.Length = sizeof (OBJECT_ATTRIBUTES);

        oa.RootDirectory = 0;
        oa.ObjectName = 0;
        oa.Attributes = 0;
        oa.SecurityDescriptor = 0;
        oa.SecurityQualityOfService = 0;
       
    if (bInhert) {
                oa.Attributes |= OBJ_INHERIT;
        }
       
        if (bOpenIt) {
                // 看能否直接得到句柄
                cid.UniqueProcess = (HANDLE) (ProcessId + 1);
                cid.UniqueThread  = 0;

                st = NtOpenProcess (&hProcessToRet, dwDesiredAccess, &oa, &cid);
       
                if (NT_SUCCESS (st)) {
                        return hProcessToRet;
                }
                ::MessageBox (NULL, "NtOpenProcess 失败", "=。=!", MB_OK);
        }
       
        // 传递16号获得所有句柄,可能被hook过;不管了,走过场~
    do {
                st = ZwQuerySystemInformation( /*SystemHandleInformation*/16, \
                    pBuffer, cbBuffer, &uRetSzie );

        if (st == STATUS_INFO_LENGTH_MISMATCH) {
            free( pBuffer);
                        cbBuffer *= 2;
            pBuffer = (PCHAR) malloc (cbBuffer);
        } else if ( !NT_SUCCESS (st) ) {

            free( pBuffer);
                        ::MessageBox (NULL, "ZwQuerySystemInformation失败", ".", MB_OK);
            return 0;
        }
        } while (st == STATUS_INFO_LENGTH_MISMATCH);
       
    NumOfHandle = (ULONG) pBuffer;
        h_info = (PSYSTEM_HANDLE_TABLE_ENTRY_INFO) ( (ULONG)pBuffer + 4 );
       
        /*
        // 获得进程对象类型
        pOneEprocess = h_info[0].Object;
        g_pObjectTypeProcess = *(PULONG) ( (ULONG)pOneEprocess - OBJECT_HEADER_SIZE \
        + OBJECT_TYPE_OFFSET );
        */

        for (ULONG i = 0; i < NumOfHandle; i++) {

                aryPids[i] = h_info[i].UniqueProcessId;

                if (bOpenIt) {

                        ::MessageBox (NULL, "333", "=。=!", MB_OK);
                        if ( 5 == h_info[i].ObjectTypeIndex) { // 是进程的句柄,打开它
                       
                                cid.UniqueProcess = (HANDLE)h_info[i].UniqueProcessId;
                                st = NtOpenProcess (&hProcessToDup, PROCESS_DUP_HANDLE, &oa, &cid);
                       
                                if (NT_SUCCESS (st)) { // 复制该句柄后赋予全部权限,以便调用ZwQuery*时顺利进行
                                        st = ZwDuplicateObject (hProcessToDup, (PHANDLE)h_info[i].HandleValue, (HANDLE)-1,\
                                                &hProcessCur, PROCESS_ALL_ACCESS, 0, DUPLICATE_SAME_ATTRIBUTES);
                               
                                        if (NT_SUCCESS (st)) { // 查看复制的句柄是否为我们想要的
                                                st = ZwQueryInformationProcess (hProcessCur, ProcessBasicInformation,\
                                                        &pbi, sizeof(pbi), 0);
                                       
                                                if (NT_SUCCESS (st)) {
                                                        if (ProcessId == pbi.UniqueProcessId ) {
                                                                // 若是想要的PID,就把该句柄按照希望的访问权限复制,然后返回
                                                                st = ZwDuplicateObject (hProcessToDup, (PHANDLE)h_info [i].HandleValue,\
                                                                        (HANDLE)-1, &hProcessToRet, dwDesiredAccess,\
                                                                        OBJ_INHERIT, DUPLICATE_SAME_ATTRIBUTES);
                                                       
                                                                ZwClose (hProcessCur);
                                                                ZwClose (hProcessToDup);
                                                                return hProcessToRet;
                                                        }
                                                }                
                                        }
                               
                                        ZwClose (hProcessCur);
                                }
                       
                                ZwClose (hProcessToDup);
                        }
                }
        }
       
        return 0;
}

顺便问下: 要抹掉PspCidTable中指定的进程容易,抹CSRSS.exe的也容易,什么挂SwapContext的,自己实现线程调度rootkit.com上的PHIDE2 engine也实现了(颇为复杂,引擎写的很漂亮),还有啥改ETHREAD,EPROCESS, flag等DKOM类的...好多好多.但是一个内存暴力搜索不就都出来了.这可怎么隐藏啊 ...

-------------------------------------------------------------------------------------------------------
参考资料:
  (1)  基于pspCidTable的进程检测技术
  (2)  句柄啊,3层表啊,ExpLookupHandleTableEntry啊
  (3)  PsLookupProcessByProcessId执行流程
  (4)  PspTerminateThreadByPointer
  (5)  WRK,ReactOS

[公告]看雪论坛2020激励机制上线了!多多参与讨论可以获得积分快速升级?

上传的附件:
最新回复 (25)
雪    币: 359
活跃值: 活跃值 (10)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
petnt 活跃值 12 2008-2-15 21:39
2
0
收藏,能看懂的时候再看
雪    币: 220
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhtjia 活跃值 2008-2-15 22:39
3
0
好文....
雪    币: 461
活跃值: 活跃值 (65)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2008-2-15 22:46
4
0
纠正下子:

ExEnumHandleTable函数已导出了
PspTerminateThreadByPointer在某些xp系统是2参数版不是3参数版。
雪    币: 217
活跃值: 活跃值 (11)
能力值: ( LV13,RANK:530 )
在线值:
发帖
回帖
粉丝
foxabu 活跃值 13 2008-2-16 00:21
5
0
顶 学习....

rootkit不是介绍过改页表 让CPU 只能够excute而不能够read/write 对于没有加壳得程序
把代码段这样子搞一下不知道可行不.  纯属乱弹。
雪    币: 461
活跃值: 活跃值 (65)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2008-2-16 06:29
6
0
强悍啊.学习
雪    币: 100
活跃值: 活跃值 (12)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
NWMonster 活跃值 1 2008-2-16 09:30
7
0
强,很强,学习中……………………
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
海风日影 活跃值 2008-2-16 10:43
8
0
向苏大米学习
雪    币: 218
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
htsf110 活跃值 2008-2-16 11:21
9
0
不错,帮你顶
雪    币: 13
活跃值: 活跃值 (16)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
NetRoc 活跃值 12 2008-2-16 12:51
10
0
ShadowWalker
雪    币: 108
活跃值: 活跃值 (34)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
combojiang 活跃值 26 2008-2-17 00:01
11
0
学习,icesword 列出隐藏进程也是用了这样的方法。
雪    币: 208
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wqrsksk 活跃值 2008-2-17 00:26
12
0
收藏,,,,刚学c++
雪    币: 277
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
SkyJack 活跃值 2008-2-17 22:40
13
0
谢谢LZ,学习ing!
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
robar 活跃值 2008-2-19 09:27
14
0
pfpf,对LZ的景仰犹如滔滔江水
雪    币: 172
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hchack 活跃值 2008-2-19 09:29
15
0
收下了,学习中。。。。。。
雪    币: 195
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
machfe 活跃值 1 2008-12-8 21:14
16
0
标记了  能看的懂了再说啊啊
雪    币: 99
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
twoseconds 活跃值 2008-12-31 00:26
17
0
好可怕,我啥时候能到你那地步啊。。。。
雪    币: 201
活跃值: 活跃值 (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
icewife 活跃值 2009-3-22 00:24
18
0
话说
为什么pid <= 0x800就是在第一张表中呢
一张表中511项
pid < 0x800才是在第一张表中啊.
=0x800的时候就已经在第二张表了
dbg很明显

加上windows internals 引用若干
...
...
...

PS: windows xp sp3
雪    币: 78
活跃值: 活跃值 (15)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
竹君 活跃值 5 2009-5-16 14:21
19
0
不知道是不是这样啊?
雪    币: 201
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
eviliori 活跃值 2009-6-12 16:29
20
0
同意楼上的~
希望大牛给出点意见~
雪    币: 0
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
王小攀 活跃值 2009-7-30 00:03
21
0
收藏,方便以后可以用
雪    币: 0
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
王小攀 活跃值 2009-7-30 00:05
22
0
收藏,方便以后可以用
雪    币: 9
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
深一海 活跃值 2011-1-7 14:31
23
0
学习了......
雪    币: 4
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tyson 活跃值 2011-11-8 16:32
24
0
谢谢了,正认真研究中....
雪    币: 564
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Hoiker 活跃值 2012-12-17 17:07
25
0
强大, hoho、
雪    币: 69
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Maiunjour 活跃值 2015-1-4 09:06
26
0
Mark!!!
游客
登录 | 注册 方可回帖
返回