一
什么是句柄
二
私有句柄表
eprocess
指向一个ObjectTable
,ObjectTbale
中存在TableCode
,这个指向的是这个进程的私有句柄表。同时ObjectTable
中还有一个HandleTableList
,这个是一个链表,通过HandleTableList
成员遍历得到所有进程的ObjectTable
地址。_object_header
结构体,这个结构体才是句柄的真正内容。但是不同版本系统下的取法不太一样,win7是直接指向句柄,win10则需要做一些偏移,这些偏移google没有资料,大多都是通过IDA静态分析函数才能得到。ntoskrnl.exe
下的ObpEnumFindHandleProcedure
函数,可以看到如下:__int64 __fastcall ObpEnumFindHandleProcedure(
_HANDLE_TABLE *handle_table,
_HANDLE_TABLE_ENTRY *handle_table_entry,
HANDLE a3,
HANDLE *object_header)
{
unsigned __int8 v5; // bl
HANDLE v7; // rbx
_DWORD *v8; // rcx
__int64 v9; // r11
int v10[10]; // [rsp+0h] [rbp-28h] BYREF
if ( !*object_header || *object_header == (HANDLE)((handle_table_entry->LowValue >> 16) & 0xFFFFFFFFFFFFFFF0ui64) )
{
v7 = object_header[1];
if ( !v7
|| v7 == (HANDLE)ObTypeIndexTable[(unsigned __int8)ObHeaderCookie ^ *(unsigned __int8 *)(((handle_table_entry->LowValue >> 16) & 0xFFFFFFFFFFFFFFF0ui64)
+ 0x18) ^ (unsigned __int64)(unsigned __int8)((unsigned __int16)(WORD1(handle_table_entry->LowValue) & 0xFFF0) >> 8)] )
{
v8 = object_header[2];
if ( !v8 )
goto LABEL_11;
v9 = (handle_table_entry->LowValue >> 17) & 7;
if ( (*(_DWORD *)(&handle_table_entry->4 + 1) & 0x2000000) != 0 )
LOBYTE(v9) = v9 | 8;
if ( *v8 == (v9 & 7) && v8[1] == (*(_DWORD *)(&handle_table_entry->4 + 1) & 0x1FFFFFF) )
LABEL_11:
v5 = 1;
else
v5 = 0;
}
else
{
v5 = 0;
}
}
else
{
v5 = 0;
}
_InterlockedExchangeAdd64(&handle_table_entry->VolatileLowValue, 1ui64);
_InterlockedOr(v10, 0);
if ( handle_table->HandleContentionEvent.Value )
ExfUnblockPushLock(&handle_table->HandleContentionEvent, 0i64);
return v5;
}
(handle_table_entry->LowValue >> 16) & 0xFFFFFFFFFFFFFFF0ui64)
,这样才能获取到句柄内容,获取到_object_header。
0xffff
,这说明右移前面不是补充0,而是补充1。(handle_table_entry->LowValue >> 16) & 0xFFFFFFFFFFFFFFF0ui64) + 0xffff000000000000
_OBJECT_HEADER
,这表示一个句柄头,句柄体在body的位置,我系统版本的偏移是0x30,进程句柄的话就是_eprocess
结构体。//0x38 bytes (sizeof)
struct _OBJECT_HEADER
{
LONGLONG PointerCount; //0x0
union
{
LONGLONG HandleCount; //0x8
VOID* NextToFree; //0x8
};
struct _EX_PUSH_LOCK Lock; //0x10
UCHAR TypeIndex; //0x18
union
{
UCHAR TraceFlags; //0x19
struct
{
UCHAR DbgRefTrace:1; //0x19
UCHAR DbgTracePermanent:1; //0x19
};
};
UCHAR InfoMask; //0x1a
union
{
UCHAR Flags; //0x1b
struct
{
UCHAR NewObject:1; //0x1b
UCHAR KernelObject:1; //0x1b
UCHAR KernelOnlyAccess:1; //0x1b
UCHAR ExclusiveObject:1; //0x1b
UCHAR PermanentObject:1; //0x1b
UCHAR DefaultSecurityQuota:1; //0x1b
UCHAR SingleHandleEntry:1; //0x1b
UCHAR DeletedInline:1; //0x1b
};
};
ULONG Reserved; //0x1c
union
{
struct _OBJECT_CREATE_INFORMATION* ObjectCreateInfo; //0x20
VOID* QuotaBlockCharged; //0x20
};
VOID* SecurityDescriptor; //0x28
struct _QUAD Body; //0x30
};
_EPROCESS
的结构,可以从这个结构便获得进程名、进程ID等等信息。三
TableCode句柄表
_handle_table_entry
,这不是一个结构体,是一个union。四
Windbg 调试
!process 0 0
查看所有进程的基本信息。3: kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS ffffbf8eaa092040
SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 001ad000 ObjectTable: ffffd00b12a10840 HandleCount: 1715.
Image: System
PROCESS ffffbf8eab1ea080
SessionId: 0 Cid: 01d8 Peb: bbe935c000 ParentCid: 01cc
DirBase: 2364c9000 ObjectTable: ffffd00b145784c0 HandleCount: 462.
Image: csrss.exe
ffffbf8eab1ea080。
ObjectTable
的值。3: kd> dt ffffbf8eab1ea080 nt!_EPROCESS -y object
+0x570 ObjectTable : 0xffffd00b`145784c0 _HANDLE_TABLE
3: kd> dt 0xffffd00b`145784c0 _HANDLE_TABLE
ntdll!_HANDLE_TABLE
+0x000 NextHandleNeedingPool : 0x800
+0x004 ExtraInfoPages : 0n0
+0x008 TableCode : 0xffffd00b`16d58001
+0x010 QuotaProcess : 0xffffbf8e`ab1ea080 _EPROCESS
+0x018 HandleTableList : _LIST_ENTRY [ 0xffffd00b`167057d8 - 0xffffd00b`140fde18 ]
+0x028 UniqueProcessId : 0x1d8
+0x02c Flags : 0x12
+0x02c StrictFIFO : 0y0
+0x02c EnableHandleExceptions : 0y1
+0x02c Rundown : 0y0
+0x02c Duplicated : 0y0
+0x02c RaiseUMExceptionOnInvalidHandleClose : 0y1
+0x030 HandleContentionEvent : _EX_PUSH_LOCK
+0x038 HandleTableLock : _EX_PUSH_LOCK
+0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST
+0x040 ActualEntry : [32] ""
+0x060 DebugInfo : (null)
2: kd> dq 0xffffd00b`16d58000
ffffd00b`16d58000 ffffd00b`16667000 ffffd00b`16d59000
ffffd00b`16d58010 ffffd00b`19a4c000 00000000`00000000
ffffd00b`16d58020 00000000`00000000 00000000`00000000
ffffd00b`16d58030 00000000`00000000 00000000`00000000
ffffd00b`16d58040 00000000`00000000 00000000`00000000
ffffd00b`16d58050 00000000`00000000 00000000`00000000
ffffd00b`16d58060 00000000`00000000 00000000`00000000
ffffd00b`16d58070 00000000`00000000 00000000`00000000
2: kd> dq ffffd00b`16667000
ffffd00b`16667000 00000000`00000000 00000000`00000000
ffffd00b`16667010 bf8eaaee`9430ffff 00000000`001f0003
ffffd00b`16667020 bf8eaaee`ae30fffb 00000000`001f0003
ffffd00b`16667030 bf8eab18`25b0fffd 00000000`00000001
ffffd00b`16667040 bf8eaaec`6890ffc3 00000000`001f0003
ffffd00b`16667050 bf8eab1e`ba40ffc3 00000000`000f00ff
ffffd00b`16667060 bf8eab18`bb00ffff 00000000`00100002
ffffd00b`16667070 bf8eab18`1b20ffff 00000000`00000001
2: kd> dt _handle_table_entry ffffd00b`16667010
nt!_HANDLE_TABLE_ENTRY
+0x000 VolatileLowValue : 0n-4643586224107225089
+0x000 LowValue : 0n-4643586224107225089
+0x000 InfoTable : 0xbf8eaaee`9430ffff _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : 0n2031619
+0x008 NextFreeHandleEntry : 0x00000000`001f0003 _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : 0n-4643586224107225089
+0x000 Unlocked : 0y1
+0x000 RefCnt : 0y0111111111111111 (0x7fff)
+0x000 Attributes : 0y000
+0x000 ObjectPointerBits : 0y10111111100011101010101011101110100101000011 (0xbf8eaaee943)
+0x008 GrantedAccessBits : 0y0000111110000000000000011 (0x1f0003)
+0x008 NoRightsUpgrade : 0y0
+0x008 Spare1 : 0y000000 (0)
+0x00c Spare2 : 0
(handle_table_entry->LowValue >> 16) & 0xFFFFFFFFFFFFFFF0ui64)
获取_object_header结构体,记得前面填充的要是1,计算一下值。In [2]: hex(((0xbf8eaaee9430ffff >> 0x10) & 0xFFFFF
...: FFFFFFFFFF0) | ((0xffff) << 48))
Out[2]: '0xffffbf8eaaee9430'
0xbf8eaaee9430ffff
是ffffd00b16667010
的值,对应的就是handle_table_entry->LowValue
,得到地址0xffffbf8eaaee9430
也就是_object_header。3: kd> dt _object_header 0xffffbf8eaaee9430
nt!_OBJECT_HEADER
+0x000 PointerCount : 0n32768
+0x008 HandleCount : 0n1
+0x008 NextToFree : 0x00000000`00000001 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0xaa ''
+0x019 TraceFlags : 0 ''
+0x019 DbgRefTrace : 0y0
+0x019 DbgTracePermanent : 0y0
+0x01a InfoMask : 0x8 ''
+0x01b Flags : 0 ''
+0x01b NewObject : 0y0
+0x01b KernelObject : 0y0
+0x01b KernelOnlyAccess : 0y0
+0x01b ExclusiveObject : 0y0
+0x01b PermanentObject : 0y0
+0x01b DefaultSecurityQuota : 0y0
+0x01b SingleHandleEntry : 0y0
+0x01b DeletedInline : 0y0
+0x01c Reserved : 0
+0x020 ObjectCreateInfo : 0xfffff807`28e53780 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xfffff807`28e53780 Void
+0x028 SecurityDescriptor : (null)
+0x030 Body : _QUAD
3: kd> dt _EPROCESS 0xffffbf8eaaee9430 + 0x30
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x438 ProcessLock : _EX_PUSH_LOCK
+0x440 UniqueProcessId : 0xffffbf8e`aaee9ba0 Void
+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffffbf8e`aaec6370 - 0xffffbf8e`ac4330e0 ]
+0x458 RundownProtect : _EX_RUNDOWN_REF
+0x460 Flags2 : 0xcdb1a88a
...
五
判断句柄类型
win7/8/8.1
//0x38 bytes (sizeof)
struct _OBJECT_HEADER
{
LONGLONG PointerCount; //0x0
union
{
LONGLONG HandleCount; //0x8
VOID* NextToFree; //0x8
};
struct _EX_PUSH_LOCK Lock; //0x10
UCHAR TypeIndex; //0x18
UCHAR TraceFlags; //0x19
UCHAR InfoMask; //0x1a
UCHAR Flags; //0x1b
union
{
struct _OBJECT_CREATE_INFORMATION* ObjectCreateInfo; //0x20
VOID* QuotaBlockCharged; //0x20
};
VOID* SecurityDescriptor; //0x28
struct _QUAD Body; //0x30
};
nt!object_type
这个表中的具体信息。Typeindex = 7
对应的意义,是进程句柄。win10
Typeindex
就不一样了,测试会发现,哪怕都是进程句柄这个Typeindex
的值也会不同。Typeindex ^ nt!ObHeaderCookie ^ 地址的第二个字节。
六
句柄降权/提权
#define PROCESS_TERMINATE (0x0001)
#define PROCESS_CREATE_THREAD (0x0002)
#define PROCESS_SET_SESSIONID (0x0004)
#define PROCESS_VM_OPERATION (0x0008)
#define PROCESS_VM_READ (0x0010)
#define PROCESS_VM_WRITE (0x0020)
#define PROCESS_DUP_HANDLE (0x0040)
#define PROCESS_CREATE_PROCESS (0x0080)
#define PROCESS_SET_QUOTA (0x0100)
#define PROCESS_SET_INFORMATION (0x0200)
#define PROCESS_QUERY_INFORMATION (0x0400)
#define PROCESS_SUSPEND_RESUME (0x0800)
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
#define PROCESS_SET_LIMITED_INFORMATION (0x2000)
#if (NTDDI_VERSION >= NTDDI_VISTA)
#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | xFFFF)
OpenProcess
以多种权限申请打开进程时便将多种权限或运算就得到了我们想要的权限值,我们的目的是为了降低句柄拥有者对我们要保护的进程的操作权限,那最简单暴力的方法便是把handle_table_entry->GrantedAccessBits
的值修改成我们设定的值,直接让句柄拥有者对我们的进程操作权限被修改。//0x10 bytes (sizeof)
union _HANDLE_TABLE_ENTRY
{
volatile LONGLONG VolatileLowValue; //0x0
LONGLONG LowValue; //0x0
struct
{
struct _HANDLE_TABLE_ENTRY_INFO* volatile InfoTable; //0x0
LONGLONG HighValue; //0x8
union _HANDLE_TABLE_ENTRY* NextFreeHandleEntry; //0x8
struct _EXHANDLE LeafHandleValue; //0x8
};
LONGLONG RefCountField; //0x0
ULONGLONG Unlocked:1; //0x0
ULONGLONG RefCnt:16; //0x0
ULONGLONG Attributes:3; //0x0
struct
{
ULONGLONG ObjectPointerBits:44; //0x0
ULONG GrantedAccessBits:25; //0x8
ULONG NoRightsUpgrade:1; //0x8
ULONG Spare1:6; //0x8
};
ULONG Spare2; //0xc
};
七
代码实现防止CE读取进程内存
PsLookupProcessByProcessId
获取指定PID的eprocess
结构体。NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath)
{
PEPROCESS eprocess = NULL;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)0xab4, &eprocess);
if (!NT_SUCCESS(status)) {
kprintf("Open process unsuccessfully!rn");
return STATUS_UNSUCCESSFUL;
}
ObDereferenceObject(eprocess);
ProtectProcessHandleByEprocess(eprocess);
pDriver->DriverUnload = DriveUnload;
return STATUS_SUCCESS;
}
ProtectProcessHandleByEprocess
函数,这个函数才是主要的逻辑,传入指定eprocess
,然后遍历链表所有的句柄表,匹配是否相同,如果相同则修改权限。typedef struct HANDLE_TABLE_ENTRY
{
UINT64 LowValue;
UINT32 GrantedAccessBits;
UINT32 Spare2;
} *PHANDLE_TABLE_ENTRY, HANDLE_TABLE_ENTRY;
/// @brief 存放每个进程的信息
typedef struct PROCESS_HANDLE_OBJECT
{
PEPROCESS eprocess;
PHANDLE_TABLE_ENTRY table_code;
} *PPROCESS_HANDLE_OBJECT, PROCESS_HANDLE_OBJECT;
◆CheckHandleTableEntry
:检查HANDLE_TABLE_ENTRY的值是否合法。
◆NewProcessHandleObject
:新建一个PROCESS_HANDLE_OBJECT结构体。
◆FreeProcessHandleObject
:释放一个PROCESS_HANDLE_OBJECT结构体。
◆HandleEntryTable2ObjectHeader
: 计算单个handle_table_entry转化成object_header地址。
/// @brief
/// 检查一个PHANDLE_TABLE_ENTRY中的数值是否合法,LowValue是否为0,合法返回TRUE,否则返回FALSE
/// @param pHandleTableEntry PHANDLE_TABLE_ENTRY指针
/// @return 合法返回TRUE,否则返回FALSE
BOOLEAN CheckHandleTableEntry(PHANDLE_TABLE_ENTRY pHandleTableEntry)
{
if (!pHandleTableEntry->LowValue) {
return FALSE;
}
return TRUE;
}
/// @brief
/// 新建一个PROCESS_HANDLE_OBJECT结构体。传入eprocess地址或者handle_table地址,二者至少其一
/// 创建成功返回结构体指针,失败则返回NULL
/// @param pEprocess eprocess地址或者NULL
/// @param pHandleTable _handle_table地址或者NULL
/// @return 创建成功返回结构体指针,失败则返回NULL
PPROCESS_HANDLE_OBJECT NewProcessHandleObject(PEPROCESS pEprocess,
PVOID64 pHandleTable)
{
UINT64 uTableCode;
PPROCESS_HANDLE_OBJECT ptr;
if (pEprocess == NULL && pHandleTable == NULL) {
return NULL;
}
if (pEprocess == NULL) {
pEprocess = *(PUINT64)((PUCHAR)pHandleTable +
WIN10_21H1_X64_QUOTOPROCESS_OFFSET);
}
if (pHandleTable == NULL) {
pHandleTable =
*(PUINT64)((PUCHAR)pEprocess + WIN10_21H1_X64_OBJECTTABLE_OFFSET);
}
uTableCode =
*(PUINT64)((PUINT8)pHandleTable + WIN10_21H1_X64_TABLECODE_OFFSET);
ptr = ExAllocatePool(NonPagedPool, sizeof(PROCESS_HANDLE_OBJECT));
if (ptr == NULL) {
kprintf("[!] Alloc struct PROCESS_HANDLE_OBJECT faildrn");
return NULL;
}
ptr->eprocess = pEprocess;
ptr->table_code = uTableCode;
}
/// @brief 销毁PROCESS_HANDLE_OBJECT结构体,传入一个对应指针
/// @param pProcessHandlePbject PROCESS_HANDLE_OBJECT的指针
/// @return
VOID FreeProcessHandleObject(PPROCESS_HANDLE_OBJECT pProcessHandlePbject)
{
pProcessHandlePbject->eprocess = NULL;
pProcessHandlePbject->table_code = 0;
ExFreePool(pProcessHandlePbject);
}
/// @brief 传入一个HANDLE_TABLE_ENTRY结构体的地址,计算出ObjectHeader地址
/// @param addr HANDLE_TABLE_ENTRY结构体的地址
/// @return 返回ObjectHeader地址
ULONG64 HandleEntryTable2ObjectHeader(PHANDLE_TABLE_ENTRY addr)
{
return ((addr->LowValue >> 0x10) & 0xFFFFFFFFFFFFFFF0) + 0xFFFF000000000000;
}
pHandleTable =
*(PUINT64)((PCHAR)pEprocess + WIN10_21H1_X64_OBJECTTABLE_OFFSET);
pPriList = (PLIST_ENTRY64)((PUCHAR)pHandleTable +
WIN10_21H1_X64_HANDLETABLELIST_OFFSET);
/// @brief
/// 传入一个PLIST_ENTRY64,会遍历这个链表,每个链表节点会生成一个对应的PROCESS_HANDLE_OBJECT指针
/// 组成一个数组,存放指针,存放到ObjArr
/// @param pHandleList Handle_list链表
/// @param ObjArr PPROCESS_HANDLE_OBJECT* 指针
/// @return 返回一个指针数组,数组元素是PROCESS_HANDLE_OBJECT指针
NTSTATUS CreateProcessObjArrByHandleList(PLIST_ENTRY64 pHandleList,
PPROCESS_HANDLE_OBJECT** ObjArr)
{
PLIST_ENTRY64 pTmp;
UINT64 cout = 0;
PPROCESS_HANDLE_OBJECT* pProcessObjArr;
// 获取链表节点数量,用于申请内存块大小
pTmp = pHandleList;
do {
pTmp = pTmp->Flink;
cout += 1;
} while (pTmp != pHandleList);
pProcessObjArr = ExAllocatePoolZero(
NonPagedPool, (cout + 1) * sizeof(PPROCESS_HANDLE_OBJECT), POOL_TAG);
if (!pProcessObjArr) {
kprintf("[!] Alloc process handle obj array failedrn");
return STATUS_ALLOCATE_BUCKET;
}
// 遍历链表获取节点信息,并创建ProcessHandleObject结构体
for (size_t i = 0; i < cout; i++) {
pProcessObjArr[i] = NewProcessHandleObject(
NULL, ((PUCHAR)pTmp - WIN10_21H1_X64_HANDLETABLELIST_OFFSET));
pTmp = pTmp->Flink;
}
*ObjArr = pProcessObjArr;
return STATUS_SUCCESS;
}
FilterObjByEprocess
函数中。/// @brief 传入需要保护的进程eprocess,保护程序句柄
/// @param pEprocess PEPROCESS地址
/// @return
NTSTATUS ProtectProcessHandleByEprocess(PEPROCESS pEprocess)
{
PVOID64 pHandleTable;
PLIST_ENTRY64 pPriList, pTmp;
UINT64 cout;
PPROCESS_HANDLE_OBJECT* ObjArr;
NTSTATUS status;
pHandleTable =
*(PUINT64)((PCHAR)pEprocess + WIN10_21H1_X64_OBJECTTABLE_OFFSET);
pPriList = (PLIST_ENTRY64)((PUCHAR)pHandleTable +
WIN10_21H1_X64_HANDLETABLELIST_OFFSET);
kprintf("[+] EPROCESS: %prn[+] handle object: %prn[+] handle table "
"list: %prn",
pEprocess,
pHandleTable,
pPriList);
status = CreateProcessObjArrByHandleList(pPriList, &ObjArr);
if (!NT_SUCCESS(status)) {
kprintf("[!] CreateProcessObjArrByHandleList error");
return STATUS_UNSUCCESSFUL;
}
for (size_t i = 0; ObjArr[i] != 0; i++) {
// kprintf("[+] Obj[%d]: %llxrn", i, ObjArr[i]);
// DisplayProcessHandleObj(ObjArr[i]);
kprintf("[+] Use handle process imagename: %s; eprocess: %prn",
(PUCHAR)ObjArr[i]->eprocess + EPROCESS_IMAGE_OFFSET,
ObjArr[i]->eprocess);
FilterObjByEprocess(ObjArr[i], pEprocess);
}
FreeProcessObjArr(ObjArr);
return STATUS_SUCCESS;
}
FilterObjByEprocess
函数。FilterOneTableByEprocess
FilterTWOTabelByEprocess
/// @brief 遍历一层/两层句柄表,判断其中是否有目标句柄进程pEprocess
/// 如果有则返回TRUE, 否则返回FALSE
/// @param pProcessHandleObj 需要遍历的pProcessHandleObj的结构体
/// @param pEprocess 目标进程句柄
/// @return
BOOLEAN FilterObjByEprocess(PPROCESS_HANDLE_OBJECT pProcessHandleObj,
PEPROCESS pEprocess)
{
UINT64 tablecode;
PHANDLE_TABLE_ENTRY pHandleTableEntry;
PVOID64 pObjHeader;
tablecode = pProcessHandleObj->table_code;
switch (tablecode & TABLE_LEVEL_MASK)
{
case TABLE_LEVEL_ZERO:
return FilterOneTableByEprocess(pEprocess, tablecode);
break;
case TABLE_LEVEL_ONE:
return FilterTWOTabelByEprocess(pEprocess, tablecode);
break;
default:
break;
}
return FALSE;
}
FilterOneTableByEprocess
需要传入两个参数,一个是需要保护的eprocess地址,一个是一层的句柄表tablecode,大致流程如下:/// @brief 针对单张句柄表的情况,匹配目标eprocess,如果匹配到则修改句柄权限
/// @param pEprocess 目标eprocess结构体指针
/// @param tablecode 单张句柄表的tablecode
/// @return
BOOLEAN FilterOneTableByEprocess(PEPROCESS pEprocess, UINT64 tablecode) {
PHANDLE_TABLE_ENTRY pHandleTableEntry;
PVOID64 pObjHeader;
pHandleTableEntry = tablecode;
for (size_t i = 0; i < PAGE_HANDLE_MAX; i++) {
// 如果tablecode有异常则跳过这个
if (!CheckHandleTableEntry(&pHandleTableEntry[i])) {
continue;
}
// 通过_handle_table_entry计算_object_header地址
pObjHeader = HandleEntryTable2ObjectHeader(&pHandleTableEntry[i]);
// Option: Check this object is process?
if (!IsProcess(pObjHeader)) {
continue;
}
// Compare whether the two eprocess variables are the same
if ((PVOID64)((PUCHAR)pObjHeader + HANDLE_BODY_OFFSET) == pEprocess) {
kprintf("[+] Found tablecode: %llx; object_handle: %p; "
"handle_table_entry: %p;rn",
tablecode,
pObjHeader,
&pHandleTableEntry[i]);
// 取消句柄的读写权限
ModfiyGrantedAccessBits(&pHandleTableEntry[i]);
return TRUE;
}
}
return FALSE;
}
/// @brief 修改handle_entry_table的GrantedAccessBits权限,句柄的内存读写权限
/// @param pHandleTableEntry
/// @return
NTSTATUS ModfiyGrantedAccessBits(PHANDLE_TABLE_ENTRY pHandleTableEntry)
{
pHandleTableEntry->GrantedAccessBits &=
~(PROCESS_VM_READ | PROCESS_VM_WRITE);
return STATUS_SUCCESS;
}
/// @brief 遍历两层的句柄表,判断其中是否有目标句柄进程pEprocess
/// 如果有则返回TRUE, 否则返回FALSE
/// @param pProcessHandleObj 需要遍历的pProcessHandleObj的结构体
/// @param pEprocess 目标进程句柄
/// @return
BOOLEAN FilterTWOTabelByEprocess(PEPROCESS pEprocess, UINT64 tablecode) {
PUINT64 tables;
tables = tablecode & TABLE_CODE_MASK;
for (size_t i = 0; tables[i] != 0; i++) {
if (FilterOneTableByEprocess(pEprocess, tables[i])){
return TRUE;
}
}
return FALSE;
}
main.c
#include "header.h"
VOID DriveUnload(PDRIVER_OBJECT pDriver)
{
kprintf("Unload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath)
{
PEPROCESS eprocess = NULL;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)0xab4, &eprocess);
if (!NT_SUCCESS(status)) {
kprintf("Open process unsuccessfully!rn");
return STATUS_UNSUCCESSFUL;
}
ObDereferenceObject(eprocess);
ProtectProcessHandleByEprocess(eprocess);
pDriver->DriverUnload = DriveUnload;
return STATUS_SUCCESS;
}
header.c
#pragma once
#include <ntifs.h>
#define WIN10_21H1_X64_OBJECTTABLE_OFFSET 0x570
#define WIN10_21H1_X64_HANDLETABLELIST_OFFSET 0x18
#define WIN10_21H1_X64_TABLECODE_OFFSET 0x8
#define WIN10_21H1_X64_QUOTOPROCESS_OFFSET 0x10
#define TABLE_LEVEL_MASK 3
#define TABLE_LEVEL_ZERO 0
#define TABLE_LEVEL_ONE 1
#define TABLE_LEVEL_TWO 2
#define PAGE_HANDLE_MAX 256
#define EPROCESS_IMAGE_OFFSET 0x5A8
#define HANDLE_BODY_OFFSET 0x30
#define TYPE_INDEX_OFFSET 0x18
#define TABLE_CODE_MASK 0xFFFFFFFFFFFFFFF8
#define POOL_TAG 'axe'
// GrantedAccessBits
#define PROCESS_VM_READ (0x0010)
#define PROCESS_VM_WRITE (0x0020)
/**
* 下面两个值是通过调试系统得到的
* OB_HEADER_COOKIE可以使用`db nt!ObHeaderCookie l1`得到
* PROCESS_TYPE通过计算得到当前系统的PROCESS的type index值为7
* */
#define OB_HEADER_COOKIE 0x21
#define PROCESS_TYPE 7
#define kprintf(...)
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, __VA_ARGS__))
typedef struct HANDLE_TABLE_ENTRY
{
UINT64 LowValue;
UINT32 GrantedAccessBits;
UINT32 Spare2;
} *PHANDLE_TABLE_ENTRY, HANDLE_TABLE_ENTRY;
/// @brief 存放每个进程的信息
typedef struct PROCESS_HANDLE_OBJECT
{
PEPROCESS eprocess;
PHANDLE_TABLE_ENTRY table_code;
} *PPROCESS_HANDLE_OBJECT, PROCESS_HANDLE_OBJECT;
VOID DisplayProcessHandleObj(PPROCESS_HANDLE_OBJECT pHandleObj)
{
kprintf("[+] eprocess: %p; table_code: %p; image_name: %15srn",
pHandleObj->eprocess,
pHandleObj->table_code,
(PUCHAR)(pHandleObj->eprocess) + EPROCESS_IMAGE_OFFSET);
}
/// @brief
/// 检查一个PHANDLE_TABLE_ENTRY中的数值是否合法,LowValue是否为0,合法返回TRUE,否则返回FALSE
/// @param pHandleTableEntry PHANDLE_TABLE_ENTRY指针
/// @return 合法返回TRUE,否则返回FALSE
BOOLEAN CheckHandleTableEntry(PHANDLE_TABLE_ENTRY pHandleTableEntry)
{
if (!pHandleTableEntry->LowValue) {
return FALSE;
}
return TRUE;
}
/// @brief
/// 新建一个PROCESS_HANDLE_OBJECT结构体。传入eprocess地址或者handle_table地址,二者至少其一
/// 创建成功返回结构体指针,失败则返回NULL
/// @param pEprocess eprocess地址或者NULL
/// @param pHandleTable _handle_table地址或者NULL
/// @return 创建成功返回结构体指针,失败则返回NULL
PPROCESS_HANDLE_OBJECT NewProcessHandleObject(PEPROCESS pEprocess,
PVOID64 pHandleTable)
{
UINT64 uTableCode;
PPROCESS_HANDLE_OBJECT ptr;
if (pEprocess == NULL && pHandleTable == NULL) {
return NULL;
}
if (pEprocess == NULL) {
pEprocess = *(PUINT64)((PUCHAR)pHandleTable +
WIN10_21H1_X64_QUOTOPROCESS_OFFSET);
}
if (pHandleTable == NULL) {
pHandleTable =
*(PUINT64)((PUCHAR)pEprocess + WIN10_21H1_X64_OBJECTTABLE_OFFSET);
}
uTableCode =
*(PUINT64)((PUINT8)pHandleTable + WIN10_21H1_X64_TABLECODE_OFFSET);
ptr = ExAllocatePool(NonPagedPool, sizeof(PROCESS_HANDLE_OBJECT));
if (ptr == NULL) {
kprintf("[!] Alloc struct PROCESS_HANDLE_OBJECT faildrn");
return NULL;
}
ptr->eprocess = pEprocess;
ptr->table_code = uTableCode;
}
/// @brief 销毁PROCESS_HANDLE_OBJECT结构体,传入一个对应指针
/// @param pProcessHandlePbject PROCESS_HANDLE_OBJECT的指针
/// @return
VOID FreeProcessHandleObject(PPROCESS_HANDLE_OBJECT pProcessHandlePbject)
{
pProcessHandlePbject->eprocess = NULL;
pProcessHandlePbject->table_code = 0;
ExFreePool(pProcessHandlePbject);
}
/// @brief 传入一个HANDLE_TABLE_ENTRY结构体的地址,计算出ObjectHeader地址
/// @param addr HANDLE_TABLE_ENTRY结构体的地址
/// @return 返回ObjectHeader地址
ULONG64 HandleEntryTable2ObjectHeader(PHANDLE_TABLE_ENTRY addr)
{
return ((addr->LowValue >> 0x10) & 0xFFFFFFFFFFFFFFF0) + 0xFFFF000000000000;
}
/// @brief 传入一个ObjectHeader地址,判断是否是进程对象,如果是则返回TRUE,
/// 不是则返回FALSE
/// @param Address 句柄头的地址,也就是_object_header结构体地址
/// @return 如果是则返回TRUE, 不是则返回FALSE
BOOLEAN IsProcess(PVOID64 Address)
{
UINT8 uTypeIndex;
UINT8 uByte;
uByte = ((ULONG64)Address >> 8) & 0xff;
uTypeIndex = *(PCHAR)((PCHAR)Address + TYPE_INDEX_OFFSET);
uTypeIndex = uTypeIndex ^ OB_HEADER_COOKIE ^ uByte;
if (uTypeIndex == PROCESS_TYPE) {
return TRUE;
}
return FALSE;
}
/// @brief 匹配进程的imageName,如果和指定的ImageName相同则返回
/// @param Address _object_header的地址
/// @param Name 需要匹配的程序名称
/// @return 如果这个是进程句柄且是目标进程则返回TRUE,否则返回FALSE
BOOLEAN IsProcessName(PVOID64 Address, PUCHAR Name)
{
PVOID64 pEprocess;
PUCHAR ImageName;
if (!IsProcess(Address)) {
return FALSE;
}
pEprocess = ((PCHAR)Address + HANDLE_BODY_OFFSET);
ImageName = (PUCHAR)pEprocess + EPROCESS_IMAGE_OFFSET;
if (strstr(ImageName, Name) == NULL) {
return FALSE;
}
return TRUE;
}
/// @brief
/// 传入一个PLIST_ENTRY64,会遍历这个链表,每个链表节点会生成一个对应的PROCESS_HANDLE_OBJECT指针
/// 组成一个数组,存放指针,存放到ObjArr
/// @param pHandleList Handle_list链表
/// @param ObjArr PPROCESS_HANDLE_OBJECT* 指针
/// @return 返回一个指针数组,数组元素是PROCESS_HANDLE_OBJECT指针
NTSTATUS CreateProcessObjArrByHandleList(PLIST_ENTRY64 pHandleList,
PPROCESS_HANDLE_OBJECT** ObjArr)
{
PLIST_ENTRY64 pTmp;
UINT64 cout = 0;
PPROCESS_HANDLE_OBJECT* pProcessObjArr;
// 获取链表节点数量,用于申请内存块大小
pTmp = pHandleList;
do {
pTmp = pTmp->Flink;
cout += 1;
} while (pTmp != pHandleList);
pProcessObjArr = ExAllocatePoolZero(
NonPagedPool, (cout + 1) * sizeof(PPROCESS_HANDLE_OBJECT), POOL_TAG);
if (!pProcessObjArr) {
kprintf("[!] Alloc process handle obj array failedrn");
return STATUS_ALLOCATE_BUCKET;
}
// 遍历链表获取节点信息,并创建ProcessHandleObject结构体
for (size_t i = 0; i < cout; i++) {
pProcessObjArr[i] = NewProcessHandleObject(
NULL, ((PUCHAR)pTmp - WIN10_21H1_X64_HANDLETABLELIST_OFFSET));
pTmp = pTmp->Flink;
}
*ObjArr = pProcessObjArr;
return STATUS_SUCCESS;
}
/// @brief 释放ProcessObject指针数组的内容
/// @param ObjArr PPROCESS_HANDLE_OBJECT数组
/// @return
VOID FreeProcessObjArr(PPROCESS_HANDLE_OBJECT* ObjArr)
{
for (size_t i = 0; ObjArr[i] != 0; i++) {
FreeProcessHandleObject(ObjArr[i]);
ObjArr[i] = NULL;
}
// ExFreePoolWithTag(&ObjArr, POOL_TAG);
}
/// @brief 传入一个_object_header指针打印body是_eprocess的ImageName字符内容
/// @param ObjectHeader
/// @return
VOID ShowImageNameByObjectHeader(PVOID64 ObjectHeader)
{
PVOID64 pEprocess;
PUCHAR ImageName;
pEprocess = ((PUCHAR)ObjectHeader + HANDLE_BODY_OFFSET);
ImageName = (PUCHAR)pEprocess + EPROCESS_IMAGE_OFFSET;
kprintf("[+] ImageName: %15srn", ImageName);
}
/// @brief 修改handle_entry_table的GrantedAccessBits权限,句柄的内存读写权限
/// @param pHandleTableEntry
/// @return
NTSTATUS ModfiyGrantedAccessBits(PHANDLE_TABLE_ENTRY pHandleTableEntry)
{
pHandleTableEntry->GrantedAccessBits &=
~(PROCESS_VM_READ | PROCESS_VM_WRITE);
return STATUS_SUCCESS;
}
/// @brief 针对单张句柄表的情况,匹配目标eprocess,如果匹配到则修改句柄权限
/// @param pEprocess 目标eprocess结构体指针
/// @param tablecode 单张句柄表的tablecode
/// @return
BOOLEAN FilterOneTableByEprocess(PEPROCESS pEprocess, UINT64 tablecode) {
PHANDLE_TABLE_ENTRY pHandleTableEntry;
PVOID64 pObjHeader;
pHandleTableEntry = tablecode;
for (size_t i = 0; i < PAGE_HANDLE_MAX; i++) {
// 如果tablecode有异常则跳过这个
if (!CheckHandleTableEntry(&pHandleTableEntry[i])) {
continue;
}
// 通过_handle_table_entry计算_object_header地址
pObjHeader = HandleEntryTable2ObjectHeader(&pHandleTableEntry[i]);
// Option: Check this object is process?
if (!IsProcess(pObjHeader)) {
continue;
}
// Compare whether the two eprocess variables are the same
if ((PVOID64)((PUCHAR)pObjHeader + HANDLE_BODY_OFFSET) == pEprocess) {
kprintf("[+] Found tablecode: %llx; object_handle: %p; "
"handle_table_entry: %p;rn",
tablecode,
pObjHeader,
&pHandleTableEntry[i]);
// 取消句柄的读写权限
ModfiyGrantedAccessBits(&pHandleTableEntry[i]);
return TRUE;
}
}
return FALSE;
}
/// @brief 遍历两层的句柄表,判断其中是否有目标句柄进程pEprocess
/// 如果有则返回TRUE, 否则返回FALSE
/// @param pProcessHandleObj 需要遍历的pProcessHandleObj的结构体
/// @param pEprocess 目标进程句柄
/// @return
BOOLEAN FilterTWOTabelByEprocess(PEPROCESS pEprocess, UINT64 tablecode) {
PUINT64 tables;
tables = tablecode & TABLE_CODE_MASK;
for (size_t i = 0; tables[i] != 0; i++) {
if (FilterOneTableByEprocess(pEprocess, tables[i])){
return TRUE;
}
}
return FALSE;
}
/// @brief 遍历一层/两层句柄表,判断其中是否有目标句柄进程pEprocess
/// 如果有则返回TRUE, 否则返回FALSE
/// @param pProcessHandleObj 需要遍历的pProcessHandleObj的结构体
/// @param pEprocess 目标进程句柄
/// @return
BOOLEAN FilterObjByEprocess(PPROCESS_HANDLE_OBJECT pProcessHandleObj,
PEPROCESS pEprocess)
{
UINT64 tablecode;
PHANDLE_TABLE_ENTRY pHandleTableEntry;
PVOID64 pObjHeader;
tablecode = pProcessHandleObj->table_code;
switch (tablecode & TABLE_LEVEL_MASK)
{
case TABLE_LEVEL_ZERO:
return FilterOneTableByEprocess(pEprocess, tablecode);
break;
case TABLE_LEVEL_ONE:
return FilterTWOTabelByEprocess(pEprocess, tablecode);
break;
default:
break;
}
return FALSE;
}
/// @brief 传入需要保护的进程eprocess,保护程序句柄
/// @param pEprocess PEPROCESS地址
/// @return
NTSTATUS ProtectProcessHandleByEprocess(PEPROCESS pEprocess)
{
PVOID64 pHandleTable;
PLIST_ENTRY64 pPriList, pTmp;
UINT64 cout;
PPROCESS_HANDLE_OBJECT* ObjArr;
NTSTATUS status;
pHandleTable =
*(PUINT64)((PCHAR)pEprocess + WIN10_21H1_X64_OBJECTTABLE_OFFSET);
pPriList = (PLIST_ENTRY64)((PUCHAR)pHandleTable +
WIN10_21H1_X64_HANDLETABLELIST_OFFSET);
kprintf("[+] EPROCESS: %prn[+] handle object: %prn[+] handle table "
"list: %prn",
pEprocess,
pHandleTable,
pPriList);
status = CreateProcessObjArrByHandleList(pPriList, &ObjArr);
if (!NT_SUCCESS(status)) {
kprintf("[!] CreateProcessObjArrByHandleList error");
return STATUS_UNSUCCESSFUL;
}
for (size_t i = 0; ObjArr[i] != 0; i++) {
// kprintf("[+] Obj[%d]: %llxrn", i, ObjArr[i]);
// DisplayProcessHandleObj(ObjArr[i]);
kprintf("[+] Use handle process imagename: %s; eprocess: %prn",
(PUCHAR)ObjArr[i]->eprocess + EPROCESS_IMAGE_OFFSET,
ObjArr[i]->eprocess);
FilterObjByEprocess(ObjArr[i], pEprocess);
}
FreeProcessObjArr(ObjArr);
return STATUS_SUCCESS;
}
结果
...
[+] Use handle process imagename: sppsvc.exe; eprocess: FFFFC40D2B969080
[+] Use handle process imagename: SppExtComObj.E; eprocess: FFFFC40D26F19080
[+] Use handle process imagename: svchost.exe; eprocess: FFFFC40D2BA1C080
[+] Use handle process imagename: slui.exe; eprocess: FFFFC40D2CBF4080
[+] Use handle process imagename: svchost.exe; eprocess: FFFFC40D24B43080
[+] Use handle process imagename: backgroundTask; eprocess: FFFFC40D2CCE2080
[+] Use handle process imagename: HxTsr.exe; eprocess: FFFFC40D2AD7E080
[+] Use handle process imagename: backgroundTask; eprocess: FFFFC40D295E5080
[+] Use handle process imagename: CompatTelRunne; eprocess: FFFFC40D2AD42080
[+] Use handle process imagename: RuntimeBroker.; eprocess: FFFFC40D2CA6F080
[+] Use handle process imagename: RuntimeBroker.; eprocess: FFFFC40D2CC3D080
[+] Use handle process imagename: svchost.exe; eprocess: FFFFC40D2B966080
[+] Use handle process imagename: ; eprocess: FFFFF8066AB8E038
[+] Found tablecode: ffff9981206e3000; object_handle: FFFFC40D2CBE4050; handle_table_entry: FFFF9981206E3430;
[+] Use handle process imagename: Registry; eprocess: FFFFC40D23D26080
[+] Use handle process imagename: smss.exe; eprocess: FFFFC40D24711040
[+] Use handle process imagename: csrss.exe; eprocess: FFFFC40D24599080
[+] Use handle process imagename: wininit.exe; eprocess: FFFFC40D2542E080
[+] Use handle process imagename: csrss.exe; eprocess: FFFFC40D25432140
[+] Found tablecode: ffff9981262f6000; object_handle: FFFFC40D2CBE4050; handle_table_entry: FFFF9981262F6110;
[+] Use handle process imagename: services.exe; eprocess: FFFFC40D254CB080
[+] Use handle process imagename: lsass.exe; eprocess: FFFFC40D254D70C0
[+] Use handle process imagename: winlogon.exe; eprocess: FFFFC40D255820C0
[+] Use handle process imagename: svchost.exe; eprocess: FFFFC40D25428080
[+] Use handle process imagename: fontdrvhost.ex; eprocess: FFFFC40D25D6E140
[+] Use handle process imagename: fontdrvhost.ex; eprocess: FFFFC40D25D9D140
[+] Use handle process imagename: svchost.exe; eprocess: FFFFC40D25DBB2C0
[+] Found tablecode: ffff9981206e8000; object_handle: FFFFC40D2CBE4050; handle_table_entry: FFFF9981206E8390;
...
八
对抗句柄降权思路
反复修改权限
断链
九
ObRegisterCallbacks 保护
NTSTATUS ObRegisterCallbacks(
[in] POB_CALLBACK_REGISTRATION CallbackRegistration,
[out] PVOID *RegistrationHandle
);
[in] CallbackRegistration
[out] RegistrationHandle
typedef struct _OB_CALLBACK_REGISTRATION {
USHORT Version;
USHORT OperationRegistrationCount;
UNICODE_STRING Altitude;
PVOID RegistrationContext;
OB_OPERATION_REGISTRATION *OperationRegistration;
} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;
OperationRegistration
参数是指向OB_OPERATION_REGISTRATION
结构的数组的指针。 每个结构指定ObjectPreCallback和ObjectPostCallback回调例程以及调用例程的操作类型。ObRegisterCallbacks例程使用此结构。 此例程的CallBackRegistration参数是指向包含OB_CALLBACK_REGISTRATION结构的缓冲区的指针,该结构后跟一个或多个OB_OPERATION_REGISTRATION结构的数组。 在传递给ObRegisterCallback的每个OB_OPERATION_REGISTRATION结构中,调用方必须提供一个或两个回调例程。 如果此结构的PreOperation和PostOperation成员均为NULL,则回调注册操作将失败。
具体代码可以参考驱动系统注册回调函数的三篇内容,我不写的主要原因是我64位win10 21h2我不知道怎么过驱动验证
参考
驱动注册系统回调函数
看雪ID:mi1itray.axe
https://bbs.kanxue.com/user-home-900501.htm
# 往期推荐
2、BFS Ekoparty 2022 Linux Kernel Exploitation Challenge
3、银狐样本分析
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):句柄降权绕过CallBacks检查