pspcidtable杂谈

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

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

Quote:
: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大语录]:

Quote:
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 = h_info.UniqueProcessId;

if (bOpenIt) {

::MessageBox (NULL, "333", "=。=!", MB_OK);
if ( 5 == h_info.ObjectTypeIndex) { // 是进程的句柄,打开它

cid.UniqueProcess = (HANDLE)h_info.UniqueProcessId;
st = NtOpenProcess (&hProcessToDup, PROCESS_DUP_HANDLE, &oa, &cid);

if (NT_SUCCESS (st)) { // 复制该句柄后赋予全部权限,以便调用ZwQuery*时顺利进行
st = ZwDuplicateObject (hProcessToDup, (PHANDLE)h_info.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 .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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值