昨天见群里有人提起,今天就随便写了一下。把PspCidTable中指定进程的Object改成一个自己构造的Object,从而使RING3的各种句 柄操作无效。一开始还没明白跟直接修改EPROCESS有什么区别,后来发现劫持EPROCESS更有针对性,而且比DKOM更稳定一些。很老很简单的技 术,没啥亮点。其实主要还是为了研究一下PspCidTable的稀疏数组结构,以前掌握很多理论但一直没写码遍历过这个表,今天总算是给弄明白了~之后 再看页表的结构应该也更容易理解了。代码贴上来做个记录。
#include "stdafx.h"
//===================结构体声明===================//
typedef struct _HANDLE_TABLE_ENTRY {
union {
VOID* Object;
ULONG32 ObAttributes;
struct _HANDLE_TABLE_ENTRY_INFO* InfoTable;
ULONG32 Value;
};
union {
ULONG32 GrantedAccess;
struct {
UINT16 GrantedAccessIndex;
UINT16 CreatorBackTraceIndex;
};
LONG32 NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
typedef struct _HANDLE_TABLE {
ULONG32 TableCode;
struct _EPROCESS* QuotaProcess;
VOID* UniqueProcessId;
EX_PUSH_LOCK HandleTableLock[4];
LIST_ENTRY HandleTableList;
EX_PUSH_LOCK HandleContentionEvent;
struct _HANDLE_TRACE_DEBUG_INFO* DebugInfo;
LONG32 ExtraInfoPages;
ULONG32 FirstFree;
ULONG32 LastFree;
ULONG32 NextHandleNeedingPool;
LONG32 HandleCount;
union {
ULONG32 Flags;
UINT8 StrictFIFO : 1;
};
} HANDLE_TABLE, *PHANDLE_TABLE;
//===================函数原型===================//
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
void DriverUnload(IN PDRIVER_OBJECT DriverObject);
BOOLEAN HijackProcess(ULONG pid);
PHANDLE_TABLE_ENTRY LookupHandleTableEntry(ULONG pid);
PHANDLE_TABLE GetPspCidTable();
void WPOFF();
void WPON();
BOOLEAN MmIsAddressRangeValid(PVOID Address, ULONG Length);
//===================全局变量===================//
ULONG EPROCESS_Size;
PVOID gpFakeProcess;
//===================驱动例程===================//
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = DriverUnload;
EPROCESS_Size = 0x260; // XP SP3: sizeof(struct _EPROCESS)
// 对进程进行EPROCESS劫持
HijackProcess(476);
return STATUS_SUCCESS;
}
void DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
return;
}
//===================Hijack关键函数===================//
BOOLEAN HijackProcess(ULONG pid)
{
// 取得进程在PspCidTable中的位置
PHANDLE_TABLE_ENTRY TableEntry = LookupHandleTableEntry(pid);
if (!MmIsAddressRangeValid(TableEntry, sizeof(HANDLE_TABLE_ENTRY)))
return FALSE;
// TODO: 验证EPROCESS的有效性
ULONG ProcessMask = (ULONG)TableEntry->Object & 7; // 取得Mask
PVOID Process = (PVOID)((ULONG)TableEntry->Object & (~3)); // 取得EPROCESS地址
if (!MmIsAddressRangeValid(Process, EPROCESS_Size))
return FALSE;
KdPrint(("[%d] EPROCESS: %X Mask: %d", pid, Process, ProcessMask));
// 构造自己的EPROCESS
gpFakeProcess = ExAllocatePool(NonPagedPool, EPROCESS_Size);
if (!MmIsAddressRangeValid(gpFakeProcess, EPROCESS_Size))
return FALSE;
RtlCopyMemory(gpFakeProcess, Process, EPROCESS_Size);
// 还原Mask
Process = (PVOID)((ULONG)gpFakeProcess + ProcessMask);
// 修改PspCidTable中进程的Object为伪造的EPROCESS
WPOFF();
InterlockedExchange((LONG*)&TableEntry->Object, (LONG)Process);
WPON();
return TRUE;
}
//===================查找指定PID的进程在PspCidTable中的位置===================//
PHANDLE_TABLE_ENTRY LookupHandleTableEntry(ULONG pid)
{
PHANDLE_TABLE PspCidTable;
PspCidTable = GetPspCidTable(); // 取得PspCidTable
if (!MmIsAddressRangeValid(PspCidTable, 4))
return NULL;
KdPrint(("PspCidTable: %X", PspCidTable));
if (pid >= PspCidTable->NextHandleNeedingPool)
return NULL;
ULONG TableLevel;
TableLevel = PspCidTable->TableCode & 3; // TableCode后两位表明句柄表级数
KdPrint(("TableLevel: %d", TableLevel));
PHANDLE_TABLE_ENTRY Table1;
PHANDLE_TABLE_ENTRY *Table2;
PHANDLE_TABLE_ENTRY **Table3;
int i, j, k; // 表索引
PHANDLE_TABLE_ENTRY pReturn;
switch (TableLevel) {
case 0x0: // 1级表
Table1 = (PHANDLE_TABLE_ENTRY)((PspCidTable->TableCode >> 2) << 2); // 表项数组
i = pid / 4;
pReturn = &Table1[i];
break;
case 0x1: // 2级表
Table2 = (PHANDLE_TABLE_ENTRY*)((PspCidTable->TableCode >> 2) << 2); // 一级表数组
j = pid / 0x800; // 根据pid确定表索引
Table1 = Table2[j];
i = (pid % 0x800) / 4;
pReturn = &Table1[i];
break;
case 0x2: // 3级表
Table3 = (PHANDLE_TABLE_ENTRY**)((PspCidTable->TableCode >> 2) << 2); // 二级表数组
k = pid / 0x1000;
Table2 = Table3[k];
j = (pid % 0x1000) / 0x800;
Table1 = Table2[j];
i = (pid % 0x1000) % 0x800;
pReturn = &Table1[i];
break;
default:
pReturn = NULL;
}
return pReturn;
}
//===================获取PspCidTable===================//
PHANDLE_TABLE GetPspCidTable()
{
PHANDLE_TABLE PspCidTable;
KeSetSystemAffinityThread(1); // 使当前线程运行在第一个处理器上,否则fs:[0x34]内容可能不正确
__asm {
push eax
mov eax,fs:[0x34] ;得到KdVersionBlock的地址
add eax,80h ;保存PspCidTable变量的偏移: KdVersionBlock + 80h
mov eax,[eax] ;得到PspCidTable的指针
mov eax,[eax] ;得到PspCidTable
mov PspCidTable,eax
pop eax
}
KeRevertToUserAffinityThread(); // 恢复线程运行的处理器
return PspCidTable;
}
//===================检查一个内存段是否有效===================//
BOOLEAN MmIsAddressRangeValid(PVOID Address, ULONG Length)
{
ULONG i;
for (i = 0; i < Length; i++) {
if (!MmIsAddressValid((PCHAR)Address + i)) return FALSE;
}
return TRUE;
}
//===================关/开 内存写保护===================//
void WPOFF()
{
__asm {
cli
mov eax, CR0
and eax, NOT 10000h //disable WP bit
mov CR0, eax
}
}
void WPON()
{
__asm {
mov eax, CR0
or eax, 10000h //disable WP bit
mov CR0, eax
sti
}
}
//==================================================================//