原创】PspCidTable杂谈

标 题: 【原创】PspCidTable杂谈
作 者: sudami
时 间: 2008-02-15,21:31
链 接: http://bbs.pediy.com/showthread.php?t=59680

今天再次看了 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
上传的附件
文件类型: rar炉子作品__killkv2008.rar (2008-02-15 21:33, 3.0 KB, 17 次下载)
文件类型: rarProcMgr.rar (2008-02-15 21:34, 29.3 KB, 20 次下载)
文件类型: txtExEnumHandleTable.txt (2008-02-15 21:37, 8.4 KB, 18 次下载)

 


 
### 智能车技术最新进展 智能车技术近年来取得了显著进步,尤其是在自动驾驶领域。长城汽车于2021年6月推出的咖啡智能2.0智慧线控底盘整合了多个核心底盘系统,包括线控转向、线控制动、线控换挡、线控油门和线控悬挂,成为国内首个支持L4+级自动驾驶的线控转向底盘系统[^4]。 此外,中航集团旗下的耐世特也在2018年发布了SBW技术,并推出了静默方向盘系统及随需转向系统,这些技术支持L3+级自动驾驶。这表明在高级别自动驾驶方面,国内外厂商都在积极布局并取得了一定的技术突破。 与此同时,新能源汽车市场的快速发展也为智能车技术提供了更多可能性。根据外媒CleanTechnica统计的数据,比亚迪、特斯拉和上汽通用五菱分别以506,868辆、406,869辆和183,054辆的销量位居前列,占据了全球新能源汽车市场约34%的份额[^3]。这种市场需求的增长推动了智能车技术的研发与应用。 值得注意的是,随着深度学习技术的进步及其广泛应用,人工智能伦理和社会影响逐渐引起重视。确保人工智能技术发展既能提升社会福祉又能规避潜在风险已成为重要议题[^2]。这一趋势同样适用于智能车领域,特别是在数据隐私保护、算法公平性和安全性等方面需要持续关注。 ```python class AutonomousDrivingSystem: def __init__(self, level): self.level = level def describe(self): if self.level >= 4: return f"L{self.level} autonomous driving system with full automation capabilities." elif self.level == 3: return f"L{self.level} autonomous driving system requiring driver attention but capable of handling most situations autonomously." else: return f"Lower-level ({self.level}) autonomous assistance features." # Example usage ad_system_l4 = AutonomousDrivingSystem(4) print(ad_system_l4.describe()) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值