遍歷PspCidTable表檢測隱藏進程


一、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,所以我們來反匯編一下。

image所以我們可以使用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項的多級索引。

image

3.確定PspCidTable的層次

上面我們從windbg中看到PspCidTable的地址為8298aeb4。我們來看看這個地址的內容:

image

上面87a011d8是HANDLE_TBALE的地址,我們來看看HANDLE_TBALE的結構:

image

這里的TbaleCode記錄這句柄表的地址,如果后兩位是00則采用的是一層表,01是兩層表,10是三層表。通過上面的TableCode就可以判斷這個系統采用了兩層表的結構。

如果系統采用了兩層或者三層的時候,TableCode就不是句柄表的地址了,把這個值后兩位置為0之后,則是一個指向多層表的指針。

來看看這個指針指向的值:

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

根據上面說的,那么這個87a04000 指向的肯定是一個HANDLE_TBALE_ENTRY的數組。

image

_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保存所有句柄的系統對象。

image

image

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

 

關於PID遍歷PspCidTable表 參考這個 http://blog.csdn.net/whatday/article/details/17189093   表的大小應該是0x800

jpg gai rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM