WindowsAPI编程核心技术-内核枚举

枚举用户进程

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
66
67
#include <ntifs.h>
#include <windef.h>
extern PVOID PsGetProcessPeb(_In_ PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS* Process);
extern NTKERNELAPI PVOID PsGetProcessWow64Process(_In_ PEPROCESS Process);
extern NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
extern NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);
typedef struct{
DWORD Pid;
UCHAR ProcessName[2048];
DWORD Handle;
LIST_ENTRY ListEntry;
}ProcessList;
// 根据进程ID返回进程EPROCESS结构体失败返回NULL
PEPROCESS LookupProcess(HANDLE Pid) {
PEPROCESS eprocess = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
Status = PsLookupProcessByProcessId(Pid, &eprocess);
if (NT_SUCCESS(Status))
return eprocess;
return NULL;
};
// 内核链表操作
BOOLEAN GetAllProcess(VOID) {
PEPROCESS eproc = NULL;
LIST_ENTRY linkListHead;
// 初始化链表头部
InitializeListHead(&linkListHead);
ProcessList* pData = NULL;
for (int temp = 0; temp < 100000; temp += 4) {
eproc = LookupProcess((HANDLE)temp);
if (eproc != NULL) {
STRING nowProcessnameString = { 0 };
RtlInitString(&nowProcessnameString, PsGetProcessImageFileName(eproc));
// DbgPrint("进程名: %s --> 进程PID = %d --> 父进程PPID = %d\r\n",PsGetProcessImageFileName(eproc), PsGetProcessId(eproc),PsGetProcessInheritedFromUniqueProcessId(eproc));
// 分配内核堆空间
pData = (ProcessList*)ExAllocatePool(PagedPool, sizeof(ProcessList));
RtlZeroMemory(pData, sizeof(ProcessList));
// 设置变量
pData->Pid = (DWORD)PsGetProcessId(eproc);
RtlCopyMemory(pData->ProcessName, PsGetProcessImageFileName(eproc), strlen(PsGetProcessImageFileName(eproc)) * 2);
pData->Handle = (DWORD)PsGetProcessInheritedFromUniqueProcessId(eproc);
// 插入元素到
InsertTailList(&linkListHead, &pData->ListEntry);
ObDereferenceObject(eproc);
};
};
// 输出链表内的数据
while (!IsListEmpty(&linkListHead)) {
LIST_ENTRY* pEntry = RemoveHeadList(&linkListHead);
pData = CONTAINING_RECORD(pEntry, ProcessList, ListEntry);
DbgPrint("%d \n", pData->Pid);
DbgPrint("%s \n", pData->ProcessName);
DbgPrint("%d \n", pData->Handle);
ExFreePool(pData);
};
return TRUE;
};
VOID UnDriver(PDRIVER_OBJECT driver) {
DbgPrint(("Uninstall Driver Is OK \n"));
return;
};
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
GetAllProcess();
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
};

枚举IoTimer定时器

IoInitializeTimer中可找到IopTimerQueueHead地址,存储定时器链表头部。枚举方法如下:

  1. 找到IoInitializeTimer函数,该函数可以通过MmGetSystemRoutineAddress得到。
  2. 找到地址以后,我们向下增加0xFF偏移量,并搜索特征定位到IopTimerQueueHead链表头。
  3. 将链表头转换为IO_TIMER结构体,并循环链表头输出。

IoInitializeTimer定义为:

1
2
3
4
5
NTSTATUS IoInitializeTimer(
IN PDEVICE_OBJECT  DeviceObject,   // 设备对象指针
IN PIO_TIMER_ROUTINE  TimerRoutine,  // 定时器例程
IN PVOID  Context           // 传给定时器例程的函数
);

DEVICE_OBJECT对象指针中存在一个Timer成员,后者是个IO_TIMER结构体

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
3: kd> dt nt!_DEVICE_OBJECT
+0x000 Type : Int2B
+0x002 Size : Uint2B
+0x004 ReferenceCount : Int4B
+0x008 DriverObject : Ptr64 _DRIVER_OBJECT
+0x010 NextDevice : Ptr64 _DEVICE_OBJECT
+0x018 AttachedDevice : Ptr64 _DEVICE_OBJECT
+0x020 CurrentIrp : Ptr64 _IRP
+0x028 Timer : Ptr64 _IO_TIMER
+0x030 Flags : Uint4B
+0x034 Characteristics : Uint4B
+0x038 Vpb : Ptr64 _VPB
+0x040 DeviceExtension : Ptr64 Void
+0x048 DeviceType : Uint4B
+0x04c StackSize : Char
+0x050 Queue : <anonymous-tag>
+0x098 AlignmentRequirement : Uint4B
+0x0a0 DeviceQueue : _KDEVICE_QUEUE
+0x0c8 Dpc : _KDPC
+0x108 ActiveThreadCount : Uint4B
+0x110 SecurityDescriptor : Ptr64 Void
+0x118 DeviceLock : _KEVENT
+0x130 SectorSize : Uint2B
+0x132 Spare1 : Uint2B
+0x138 DeviceObjectExtension : Ptr64 _DEVOBJ_EXTENSION
+0x140 Reserved : Ptr64 Void
3: kd> dt nt!_IO_TIMER
+0x000 Type : Int2B
+0x002 TimerFlag : Int2B
+0x008 TimerList : _LIST_ENTRY
+0x018 TimerRoutine : Ptr64 void
+0x020 Context : Ptr64 Void
+0x028 DeviceObject : Ptr64 _DEVICE_OBJECT
2: kd> uf nt!IoInitializeTimer
...
nt!IoInitializeTimer+0x5d:
fffff800`559b5c1d 488d5008 lea rdx,[rax+8]
fffff800`559b5c21 48897018 mov qword ptr [rax+18h],rsi
fffff800`559b5c25 4c8d0514d55600 lea r8,[nt!IopTimerLock (fffff800`55f23140)]
fffff800`559b5c2c 48897820 mov qword ptr [rax+20h],rdi
fffff800`559b5c30 488d0d89004900 lea rcx,[nt!IopTimerQueueHead (fffff800`55e45cc0)]
fffff800`559b5c37 e84495acff call nt!ExInterlockedInsertTailList (fffff800`5547f180)
fffff800`559b5c3c 33c0 xor eax,eax
...

实现为:

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
66
67
68
69
70
71
72
73
74
#include <ntddk.h>
#include <ntstrsafe.h>
typedef struct _IO_TIMER {
INT16     Type;
INT16     TimerFlag;
LONG32    Unknown;
LIST_ENTRY  TimerList;
PVOID     TimerRoutine;
PVOID     Context;
PVOID     DeviceObject;
}IO_TIMER, * PIO_TIMER;
// 得到IoInitializeTimer基址
PVOID GetIoInitializeTimerAddress() {
PVOID VariableAddress = 0;
UNICODE_STRING uioiTime = { 0 };
RtlInitUnicodeString(&uioiTime, L"IoInitializeTimer");
VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime);
if (VariableAddress != 0)
return VariableAddress;
return 0;
}
VOID UnDriver(PDRIVER_OBJECT driver) {
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
// 得到基址
PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress();
DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer);
// 搜索IoTimerQueueHead地址
 /*
  nt!IoInitializeTimer+0x5d:
  fffff806`349963cd 488d5008    lea   rdx,[rax+8]
  fffff806`349963d1 48897018    mov   qword ptr [rax+18h],rsi
  fffff806`349963d5 4c8d05648de0ff lea   r8,[nt!IopTimerLock(fffff806`3479f140)]
  fffff806`349963dc 48897820    mov   qword ptr [rax+20h],rdi
  fffff806`349963e0 488d0d99f6cdff lea   rcx,[nt!IopTimerQueueHead(fffff806`34675a80)]
  fffff806`349963e7 e8c43598ff   call  nt!ExInterlockedInsertTailList(fffff806`343199b0)
  fffff806`349963ec 33c0      xor   eax,eax
 */
INT32 iOffset = 0;
PLIST_ENTRY IoTimerQueueHead = NULL;
PUCHAR StartSearchAddress = IoInitializeTimer;
PUCHAR EndSearchAddress = IoInitializeTimer + 0xFF;
UCHAR v1 = 0, v2 = 0, v3 = 0;
for (PUCHAR i = StartSearchAddress; i < EndSearchAddress; i++)
if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2)) {
v1 = *i;
v2 = *(i + 1);
v3 = *(i + 2);
// fffff806`349963e0 48 8d 0d 99 f6 cd ff lea rcx,[nt!IopTimerQueueHead(fffff806`34675a80)]
if (v1 == 0x48 && v2 == 0x8d && v3 == 0x0d) {
memcpy(&iOffset, i + 3, 4);
IoTimerQueueHead = (PLIST_ENTRY)(iOffset + (ULONG64)i + 7);
DbgPrint("IoTimerQueueHead = %p \n", IoTimerQueueHead);
break;
}
}
// 枚举列表
KIRQL OldIrql;
// 获得特权级
OldIrql = KeRaiseIrqlToDpcLevel();
if (IoTimerQueueHead && MmIsAddressValid((PVOID)IoTimerQueueHead)) {
PLIST_ENTRY NextEntry = IoTimerQueueHead->Flink;
while (MmIsAddressValid(NextEntry) && NextEntry != (PLIST_ENTRY)IoTimerQueueHead) {
PIO_TIMER Timer = CONTAINING_RECORD(NextEntry, IO_TIMER, TimerList);
if (Timer && MmIsAddressValid(Timer))
DbgPrint("IO对象地址: %p \n", Timer);
NextEntry = NextEntry->Flink;
}
}
// 恢复特权级
KeLowerIrql(OldIrql);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

枚举DpcTimer定时器

DPC在系统中是被异或加密的,得到DPC定时器步骤为:

  1. 找到KiProcessorBlock地址并解析成_KPRCB结构。
  2. 在_KPRCB 结构中得到_KTIMER_TABLE偏移。
  3. 解析_KTIMER_TABLE_ENTRY得到加密后的双向链表。

KiProcessorBlock中记录每个CPU的_KPRCB结构,有几个CPU就有几个项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0: kd> dq nt!KiProcessorBlock
fffff800`55efdc80 fffff800`50ef3180 ffffd501`a3267180
fffff800`55efdc90 ffffd501`a332a180 ffffd501`a33ad180
fffff800`55efdca0 00000000`00000000 00000000`00000000
0: kd> dt nt!_KPRCB fffff800`50ef3180
...
+0x3940 TimerTable : _KTIMER_TABLE
...
+0x7b80 DpcGate : _KGATE
...
0: kd> dt nt!_KTIMER_TABLE fffff800`50ef3180+0x3940
+0x000 TimerExpiry : [64] (null)
+0x200 TimerEntries : [2] [256] _KTIMER_TABLE_ENTRY
+0x4200 TableState : _KTIMER_TABLE_STATE
0: kd> dt nt!_KTIMER_TABLE_ENTRY fffff800`50ef3180+0x3940+0x200
+0x000 Lock : 0
+0x008 Entry : _LIST_ENTRY [ 0xfffff800`50ef6cc8 - 0xfffff800`50ef6cc8 ]
+0x018 Time : _ULARGE_INTEGER 0xffffffff`0c0340d1

至于如何解密,需要逆一下KeSetTimer

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
0: kd> u nt!KiSetTimerEx l50
nt!KiSetTimerEx:
fffff800`554bbbf0 48895c2408 mov qword ptr [rsp+8],rbx
fffff800`554bbbf5 48896c2410 mov qword ptr [rsp+10h],rbp
fffff800`554bbbfa 4889742420 mov qword ptr [rsp+20h],rsi
fffff800`554bbbff 57 push rdi
fffff800`554bbc00 4154 push r12
fffff800`554bbc02 4155 push r13
fffff800`554bbc04 4156 push r14
fffff800`554bbc06 4157 push r15
fffff800`554bbc08 4883ec30 sub rsp,30h
fffff800`554bbc0c 488b05e50ba400 mov rax,qword ptr [nt!KiWaitNever (fffff800`55efc7f8)]
fffff800`554bbc13 488bf9 mov rdi,rcx
fffff800`554bbc16 488b35db0da400 mov rsi,qword ptr [nt!KiWaitAlways (fffff800`55efc9f8)]
fffff800`554bbc1d 450fb6e1 movzx r12d,r9b
fffff800`554bbc21 4c8bb42480000000 mov r14,qword ptr [rsp+80h]
fffff800`554bbc29 458be8 mov r13d,r8d
fffff800`554bbc2c 4933f6 xor rsi,r14
fffff800`554bbc2f c744247000000000 mov dword ptr [rsp+70h],0
fffff800`554bbc37 480fce bswap rsi
fffff800`554bbc3a 4833f1 xor rsi,rcx
fffff800`554bbc3d 488bda mov rbx,rdx
fffff800`554bbc40 8bc8 mov ecx,eax
fffff800`554bbc42 48d3ce ror rsi,cl
fffff800`554bbc45 4833f0 xor rsi,rax
fffff800`554bbc48 440f20c1 mov rcx,tmm
...

具体解密方法:

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
#include <ntddk.h>
#include <ntstrsafe.h>
// 解密DPC
void DPC_Print(PKTIMER ptrTimer) {
ULONG_PTR ptrDpc = (ULONG_PTR)ptrTimer->Dpc;
KDPC* DecDpc = NULL;
DWORD nShift = (p2dq(ptrKiWaitNever) & 0xFF);
// _RSI->Dpc = (_KDPC *)v19;
// _RSI = Timer;
ptrDpc ^= p2dq(ptrKiWaitNever);     // v19 = KiWaitNever ^ v18;
ptrDpc = _rotl64(ptrDpc, nShift);    // v18 = __ROR8__((unsigned __int64)Timer^ _RBX, KiWaitNever);
ptrDpc ^= (ULONG_PTR)ptrTimer;
ptrDpc = _byteswap_uint64(ptrDpc);   // __asm { bswap  rbx }
ptrDpc ^= p2dq(ptrKiWaitAlways);    // _RBX = (unsigned __int64)DPC ^ KiWaitAlways;
// real DPC
if (MmIsAddressValid((PVOID)ptrDpc)) {
DecDpc = (KDPC*)ptrDpc;
DbgPrint("DPC = %p | routine = %p \n", DecDpc, DecDpc->DeferredRoutine);
}
}
VOID UnDriver(PDRIVER_OBJECT driver) {
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
PKTIMER ptrTimer = NULL;
DPC_Print(ptrTimer);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

得到KiProcessorBlock方法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <ntddk.h>
#include <ntstrsafe.h>
// 得到KiProcessorBlock地址
ULONG64 GetKiProcessorBlock() {
ULONG64 PrcbAddress = 0;
PrcbAddress = (ULONG64)__readmsr(0xC0000101) + 0x20;
if (PrcbAddress != 0)
// PrcbAddress 是一个地址 这个地址存放了某个 CPU 的 _KPRCB 的地址
return *(ULONG_PTR*)PrcbAddress;
return 0;
}
VOID UnDriver(PDRIVER_OBJECT driver) {
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
ULONG64 address = GetKiProcessorBlock();
if (address != 0)
DbgPrint("KiProcessorBlock = %p \n", address);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

接着在KeSetTimer中找KeSetTimerEx地址:

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
#include <ntddk.h>
#include <ntstrsafe.h>
// 得到KiProcessorBlock地址
ULONG64 GetKeSetTimerEx() {
// 获取 KeSetTimer 地址
ULONG64 ul_KeSetTimer = 0;
UNICODE_STRING  uc_KeSetTimer = { 0 };
RtlInitUnicodeString(&uc_KeSetTimer, L"KeSetTimer");
ul_KeSetTimer = (ULONG64)MmGetSystemRoutineAddress(&uc_KeSetTimer);
if (ul_KeSetTimer == 0)
return 0;
// 前 30 字节找 call 指令
BOOLEAN b_e8 = FALSE;
ULONG64 ul_e8Addr = 0;
for (INT i = 0; i < 30; i++) {
// 验证地址是否可读写
if (!MmIsAddressValid((PVOID64)ul_KeSetTimer))
continue;
// e8 0c 00 00 00 call nt!KiSetTimerEx (fffff807`70520a50)
if (*(PUCHAR)(ul_KeSetTimer + i) == 0xe8) {
b_e8 = TRUE;
ul_e8Addr = ul_KeSetTimer + i;
break;
}
}
// 找到 call 则解析目的地址
if (b_e8 == TRUE) {
if (!MmIsAddressValid((PVOID64)ul_e8Addr))
return 0;
INT ul_callCode = *(INT*)(ul_e8Addr + 1);
ULONG64 ul_KiSetTimerEx = ul_e8Addr + ul_callCode + 5;
return ul_KiSetTimerEx;
}
return 0;
}
VOID UnDriver(PDRIVER_OBJECT driver) {
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
ULONG64 address = GetKeSetTimerEx();
if (address != 0)
DbgPrint("KeSetTimerEx = %p \n", address);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

接着要在KiSetTimerEx中搜索KiWaitNeverKiWaitAlways两个函数地址:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <ntddk.h>
#include <ntstrsafe.h>
// 得到KiProcessorBlock地址
ULONG64 GetKeSetTimerEx() {
// 获取 KeSetTimer 地址
ULONG64 ul_KeSetTimer = 0;
UNICODE_STRING  uc_KeSetTimer = { 0 };
RtlInitUnicodeString(&uc_KeSetTimer, L"KeSetTimer");
ul_KeSetTimer = (ULONG64)MmGetSystemRoutineAddress(&uc_KeSetTimer);
if (ul_KeSetTimer == 0)
return 0;
// 前 30 字节找 call 指令
BOOLEAN b_e8 = FALSE;
ULONG64 ul_e8Addr = 0;
for (INT i = 0; i < 30; i++) {
// 验证地址是否可读写
if (!MmIsAddressValid((PVOID64)ul_KeSetTimer))
continue;
// e8 0c 00 00 00 call nt!KiSetTimerEx (fffff807`70520a50)
if (*(PUCHAR)(ul_KeSetTimer + i) == 0xe8) {
b_e8 = TRUE;
ul_e8Addr = ul_KeSetTimer + i;
break;
}
}
// 找到 call 则解析目的地址
if (b_e8 == TRUE) {
if (!MmIsAddressValid((PVOID64)ul_e8Addr))
return 0;
INT ul_callCode = *(INT*)(ul_e8Addr + 1);
ULONG64 ul_KiSetTimerEx = ul_e8Addr + ul_callCode + 5;
return ul_KiSetTimerEx;
}
return 0;
}
// 得到KiWaitNever地址
ULONG64 GetKiWaitNever(ULONG64 address) {
// 验证地址是否可读写
if (!MmIsAddressValid((PVOID64)address))
return 0;
// 前 100 字节找 找 KiWaitNever
for (INT i = 0; i < 100; i++)
// 48 8b 05 85 0c 51 00 | mov rax, qword ptr[nt!KiWaitNever(fffff807`70a316f8)]
if (*(PUCHAR)(address + i) == 0x48 && *(PUCHAR)(address + i + 1) == 0x8b && *(PUCHAR)(address + i + 2) == 0x05) {
ULONG64 ul_movCode = *(UINT32*)(address + i + 3);
ULONG64 ul_movAddr = address + i + ul_movCode + 7;
// DbgPrint("找到KiWaitNever地址: %p \n", ul_movAddr);
return ul_movAddr;
}
return 0;
}
// 得到KiWaitAlways地址
ULONG64 GetKiWaitAlways(ULONG64 address) {
// 验证地址是否可读写
if (!MmIsAddressValid((PVOID64)address))
return 0;
// 前 100 字节找 找 KiWaitNever
for (INT i = 0; i < 100; i++)
// 48 8b 35 6b 0e 51 00 | mov rsi,qword ptr [nt!KiWaitAlways(fffff807`70a318e8)]
if (*(PUCHAR)(address + i) == 0x48 && *(PUCHAR)(address + i + 1) == 0x8b && *(PUCHAR)(address + i + 2) == 0x35) {
ULONG64 ul_movCode = *(UINT32*)(address + i + 3);
ULONG64 ul_movAddr = address + i + ul_movCode + 7;
return ul_movAddr;
}
return 0;
}
VOID UnDriver(PDRIVER_OBJECT driver) {
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
ULONG64 address = GetKeSetTimerEx();
if (address != 0) {
ULONG64 KiWaitNeverAddress = GetKiWaitNever(address);
DbgPrint("KiWaitNeverAddress = %p \n", KiWaitNeverAddress);
ULONG64 KiWaitAlwaysAddress = GetKiWaitAlways(address);
DbgPrint("KiWaitAlwaysAddress = %p \n", KiWaitAlwaysAddress);
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

总结实现方法为:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#include <Fltkernel.h>
#include <ntddk.h>
#include <intrin.h>
#define IRP_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED,
FILE_ANY_ACCESS)
UNICODE_STRING name_device; // 设备名
UNICODE_STRING name_symbol; // 符号链接
PDEVICE_OBJECT deviceObj; // 设备对象
typedef struct _KTIMER_TABLE_ENTRY {
ULONG_PTR  Lock;
LIST_ENTRY  Entry;
ULONG_PTR  Time;
}KTIMER_TABLE_ENTRY, * PKTIMER_TABLE_ENTRY;
typedef struct _KTIMER_TABLE {
ULONG_PTR      TimerExpiry[64];
KTIMER_TABLE_ENTRY  TimerEntries[256];
}KTIMER_TABLE, * PKTIMER_TABLE;
BOOLEAN get_KiWait(PULONG64 never, PULONG64 always) {
// 获取 KeSetTimer 地址
ULONG64 ul_KeSetTimer = 0;
UNICODE_STRING uc_KeSetTimer = { 0 };
RtlInitUnicodeString(&uc_KeSetTimer, L"KeSetTimer");
ul_KeSetTimer = (ULONG64)MmGetSystemRoutineAddress(&uc_KeSetTimer);
if (ul_KeSetTimer == NULL)
return FALSE;
// 前 30 字节找 call 指令
BOOLEAN b_e8 = FALSE;
ULONG64 ul_e8Addr = 0;
for (INT i = 0; i < 30; i++) {
if (!MmIsAddressValid((PVOID64)ul_KeSetTimer))
continue;
/*
0: kd> u nt!KeSetTimer
nt!KeSetTimer:
fffff803`0fc63a40 4883ec38    sub   rsp,38h
fffff803`0fc63a44 4c89442420   mov   qword ptr [rsp+20h],r8
fffff803`0fc63a49 4533c9     xor   r9d,r9d
fffff803`0fc63a4c 4533c0     xor   r8d,r8d
fffff803`0fc63a4f e80c000000   call  nt!KiSetTimerEx
(fffff803`0fc63a60)
fffff803`0fc63a54 4883c438    add   rsp,38h
fffff803`0fc63a58 c3       ret
fffff803`0fc63a59 cc       int   3
*/
// fffff803`0fc63a4f e8 0c 00 00 00   call  nt!KiSetTimerEx(fffff803`0fc63a60)
if (*(PUCHAR)(ul_KeSetTimer + i) == 0xe8) {
b_e8 = TRUE;
ul_e8Addr = ul_KeSetTimer + i;
break;
}
}
// 找到 call 则解析目的地址
/*
0: kd> u nt!KiSetTimerEx l20
nt!KiSetTimerEx:
fffff803`0fc63a60 48895c2408   mov   qword ptr [rsp+8],rbx
fffff803`0fc63a65 48896c2410   mov   qword ptr [rsp+10h],rbp
fffff803`0fc63a6a 4889742418   mov   qword ptr [rsp+18h],rsi
fffff803`0fc63a6f 57       push  rdi
fffff803`0fc63a70 4154      push  r12
fffff803`0fc63a72 4155      push  r13
fffff803`0fc63a74 4156      push  r14
fffff803`0fc63a76 4157      push  r15
fffff803`0fc63a78 4883ec50    sub   rsp,50h
fffff803`0fc63a7c 488b057d0c5100 mov   rax,qword ptr [nt!KiWaitNever
(fffff803`10174700)]
fffff803`0fc63a83 488bf9     mov   rdi,rcx
*/
ULONG64 ul_KiSetTimerEx = 0;
if (b_e8 == TRUE) {
if (!MmIsAddressValid((PVOID64)ul_e8Addr))
return FALSE;
INT ul_callCode = *(INT*)(ul_e8Addr + 1);
ULONG64 ul_callAddr = ul_e8Addr + ul_callCode + 5;
ul_KiSetTimerEx = ul_callAddr;
}
// 没有 call 则直接在当前函数找
else
ul_KiSetTimerEx = ul_KeSetTimer;
// 前 50 字节找 找 KiWaitNever 和 KiWaitAlways
/*
0: kd> u nt!KiSetTimerEx l20
nt!KiSetTimerEx:
fffff803`0fc63a60 48895c2408   mov   qword ptr [rsp+8],rbx
fffff803`0fc63a65 48896c2410   mov   qword ptr [rsp+10h],rbp
fffff803`0fc63a6a 4889742418   mov   qword ptr [rsp+18h],rsi
fffff803`0fc63a6f 57       push  rdi
fffff803`0fc63a70 4154      push  r12
fffff803`0fc63a72 4155      push  r13
fffff803`0fc63a74 4156      push  r14
fffff803`0fc63a76 4157      push  r15
fffff803`0fc63a78 4883ec50    sub   rsp,50h
fffff803`0fc63a7c 488b057d0c5100 mov   rax,qword ptr [nt!KiWaitNever
(fffff803`10174700)]
fffff803`0fc63a83 488bf9     mov   rdi,rcx
fffff803`0fc63a86 488b35630e5100 mov   rsi,qword ptr [nt!KiWaitAlways
(fffff803`101748f0)]
*/
if (!MmIsAddressValid((PVOID64)ul_KiSetTimerEx))
return FALSE;
ULONG64 ul_arr_ret[2]; // 存放 KiWaitNever 和 KiWaitAlways 的地址
INT i_sub = 0; // 对应 ul_arr_ret 的下标
for (INT i = 0; i < 50; i++)
// fffff803`0fc63a7c 488b057d0c5100 mov   rax,qword ptr[nt!KiWaitNever(fffff803`10174700)]
if (*(PUCHAR)(ul_KiSetTimerEx + i) == 0x48 && *(PUCHAR)(ul_KiSetTimerEx + i + 1) == 0x8b && *(PUCHAR)(ul_KiSetTimerEx + i + 6) == 0x00) {
ULONG64 ul_movCode = *(UINT32*)(ul_KiSetTimerEx + i + 3);
ULONG64 ul_movAddr = ul_KiSetTimerEx + i + ul_movCode + 7;
// 只拿符合条件的前两个值
if (i_sub < 2)
ul_arr_ret[i_sub++] = ul_movAddr;
}
*never = ul_arr_ret[0];
*always = ul_arr_ret[1];
return TRUE;
}
BOOLEAN EnumDpc() {
// 获取 CPU 核心数
INT i_cpuNum = KeNumberProcessors;
DbgPrint("CPU核心数: %d \n", i_cpuNum);
for (KAFFINITY i = 0; i < i_cpuNum; i++) {
// 线程绑定特定 CPU
KeSetSystemAffinityThread(i + 1);
// 获得 KPRCB 的地址
ULONG64 p_PRCB = (ULONG64)__readmsr(0xC0000101) + 0x20;
if (!MmIsAddressValid((PVOID64)p_PRCB))
return FALSE;
// 取消绑定 CPU
KeRevertToUserAffinityThread();
// 计算 TimerTable 在 _KPRCB 结构中的偏移
PKTIMER_TABLE p_TimeTable = NULL;
// Windows 10 得到_KPRCB + 0x3680
p_TimeTable = (PKTIMER_TABLE)(*(PULONG64)p_PRCB + 0x3680);
// 遍历 TimerEntries[] 数组(大小 256)
for (INT j = 0; j < 256; j++) {
// 获取 Entry 双向链表地址
if (!MmIsAddressValid((PVOID64)p_TimeTable))
continue;
PLIST_ENTRY p_ListEntryHead = &(p_TimeTable->TimerEntries[j].Entry);
// 遍历 Entry 双向链表
for (PLIST_ENTRY p_ListEntry = p_ListEntryHead->Flink; p_ListEntry != p_ListEntryHead; p_ListEntry = p_ListEntry->Flink) {
// 根据 Entry 取 _KTIMER 对象地址
if (!MmIsAddressValid((PVOID64)p_ListEntry))
continue;
PKTIMER p_Timer = CONTAINING_RECORD(p_ListEntry, KTIMER, TimerListEntry);
// 硬编码取 KiWaitNever 和 KiWaitAlways
ULONG64 never = 0, always = 0;
if (get_KiWait(&never, &always) == FALSE)
return FALSE;
// 获取解密前的 Dpc 对象
if (!MmIsAddressValid((PVOID64)p_Timer))
continue;
ULONG64 ul_Dpc = (ULONG64)p_Timer->Dpc;
INT i_Shift = (*((PULONG64)never) & 0xFF);
// 解密 Dpc 对象
ul_Dpc ^= *((ULONG_PTR*)never); // 异或
ul_Dpc = _rotl64(ul_Dpc, i_Shift); // 循环左移
ul_Dpc ^= (ULONG_PTR)p_Timer; // 异或
ul_Dpc = _byteswap_uint64(ul_Dpc); // 颠倒顺序
ul_Dpc ^= *((ULONG_PTR*)always); // 异或
// 对象类型转换
PKDPC p_Dpc = (PKDPC)ul_Dpc;
// 打印验证
if (!MmIsAddressValid((PVOID64)p_Dpc))
continue;
DbgPrint("定时器对象:0x%p | 函数入口:0x%p | 触发周期: %d \n ", p_Timer, p_Dpc->DeferredRoutine, p_Timer->Period);
}
}
}
return TRUE;
}
// 对应 IRP_MJ_DEVICE_CONTROL
NTSTATUS myIrpControl(IN PDEVICE_OBJECT pDevObj, IN PIRP pIRP) {
// 获取 IRP 对应的 I/O 堆栈指针
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIRP);
// 得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
// 得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
// 得到 IOCTL 码
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
// 捕获 I/O 操作类型(MajorFunction)
switch (code) {
case IRP_TEST:
break;
default:
break;
}
// 完成 IO 请求
IoCompleteRequest(pIRP, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// 对应 IRP_MJ_CREATE 、 IRP_MJ_CLOSE
NTSTATUS dpc_CAC(IN PDEVICE_OBJECT pDevObj, IN PIRP pIRP) {
// 将 IRP 返回给 I/O 管理器
IoCompleteRequest(pIRP, IO_NO_INCREMENT);
// 设置 I/O 请求状态
pIRP->IoStatus.Status = STATUS_SUCCESS;
// 设置 I/O 请求传输的字节数
pIRP->IoStatus.Information = 0;
return STATUS_SUCCESS;
}
NTSTATUS CreateDevice(IN PDRIVER_OBJECT DriverObject) {
// 定义返回值
NTSTATUS status;
// 初始化设备名
RtlInitUnicodeString(&name_device, L"\\Device\\MyDriver");
// 创建设备
status = IoCreateDevice(DriverObject, 0, &name_device, FILE_DEVICE_UNKNOWN, 0, TRUE, &deviceObj);
if (!NT_SUCCESS(status))
return status;
// 初始化符号链接名
RtlInitUnicodeString(&name_symbol, L"\\??\\MyDriver");
// 创建符号链接
status = IoCreateSymbolicLink(&name_symbol, &name_device);
if (!NT_SUCCESS(status))
return status;
return STATUS_SUCCESS;
}
NTSTATUS DriverUnload(IN PDRIVER_OBJECT DriverObject) {
// 定义返回值
NTSTATUS status;
// 删除符号链接
status = IoDeleteSymbolicLink(&name_symbol);
if (!NT_SUCCESS(status))
return status;
// 删除设备
IoDeleteDevice(deviceObj);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRINGRegistryPath) {
// 定义返回值
NTSTATUS status;
// 指定驱动卸载函数
DriverObject->DriverUnload = (PDRIVER_UNLOAD)DriverUnload;
// 指定派遣函数
DriverObject->MajorFunction[IRP_MJ_CREATE] = dpc_CAC;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = dpc_CAC;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = myIrpControl;
// 创建设备
status = CreateDevice(DriverObject);
if (!NT_SUCCESS(status))
return status;
// 执行枚举
EnumDpc();
return STATUS_SUCCESS;
}

枚举PspCidTable句柄表

PspCidTable为一种内核句柄表,内部存放系统所有EPROCESS和ETHREAD的内核对象,通过PID和TID索引,ID号以4自增,访问对象时要掩掉低3位。

PspCidTable与每个进程的私有句柄表结构不同,后者每个表项为OBJECT_HEADER对象头。且PspCidTable是个独立的句柄表,进程私有句柄表是以双链连接的。

枚举句柄表方法为:

  1. 找到PsLookupProcessByProcessId函数地址,该函数是被导出的可以动态拿到。
  2. PsLookupProcessByProcessId地址中搜索PspReferenceCidTableEntry
  3. PspReferenceCidTableEntry地址中找到PspCidTable函数。

首先获取PspCidTable地址,程序中用MmGetSystemRoutineAddress得到。PspCidTable是个HANDLE_TABLE结构,新建进程时会在其内存入一个该进线程对应的HANDLE_TABLE_ENTRY项。其采用动态扩展方法,句柄数少则采用下层表,多时才采用中层表或上层表。

内核句柄表三层如下:

  • 下层表是个HANDLE_TABLE_ENTRY项索引,整个表有256个元素,每个元素是个8字节长的HANDLE_TABLE_ENTRY项及索引,每个项中保存指向对象的指针。下层表可看作是进程和线程的稠密索引。内容是加密的。
  • 中层表共有256个元素,每个元素是个4字节长指向下层表的入口指针及索引。中层表可看作是进程和线程的稀疏索引。
  • 上层表共有256个元素,每个元素是个4字节长指向中层表的入口指针及索引。上层表可看作是中层表的稀疏索引。
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
1: kd> dq nt!PspCidTable
fffff800`55efc5c0 ffffc18d`63834d40 ffff9786`796fd370
fffff800`55efc5d0 ffff9786`797cf2a0 00000000`00000002
fffff800`55efc5e0 00000000`00000000 00001000`00010000
fffff800`55efc5f0 00000000`00000000 00000000`0000b701
fffff800`55efc600 00000000`00000000 00000000`00000000
fffff800`55efc610 00000000`00000000 00000000`00000000
fffff800`55efc620 00000000`00000000 00000000`00000000
fffff800`55efc630 ffff9786`797fc2a0 fffff800`56246000
1: kd> dt nt!_HANDLE_TABLE ffffc18d`63834d40
+0x000 NextHandleNeedingPool : 0x1400
+0x004 ExtraInfoPages : 0n0
+0x008 TableCode : 0xffffc18d`671fd001 //抹掉低2位
+0x010 QuotaProcess : (null)
+0x018 HandleTableList : _LIST_ENTRY [ 0xffffc18d`63834d58 - 0xffffc18d`63834d58 ]
+0x028 UniqueProcessId : 0
+0x02c Flags : 1
+0x02c StrictFIFO : 0y1
+0x02c EnableHandleExceptions : 0y0
+0x02c Rundown : 0y0
+0x02c Duplicated : 0y0
+0x02c RaiseUMExceptionOnInvalidHandleClose : 0y0
+0x030 HandleContentionEvent : _EX_PUSH_LOCK
+0x038 HandleTableLock : _EX_PUSH_LOCK
+0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST
+0x040 ActualEntry : [32] ""
+0x060 DebugInfo : (null)
1: kd> dq 0xffffc18d`671fd000
ffffc18d`671fd000 ffffc18d`638a0000 ffffc18d`671fe000
ffffc18d`671fd010 ffffc18d`678ff000 ffffc18d`68963000
ffffc18d`671fd020 ffffc18d`6917f000 00000000`00000000
ffffc18d`671fd030 00000000`00000000 00000000`00000000
ffffc18d`671fd040 00000000`00000000 00000000`00000000
ffffc18d`671fd050 00000000`00000000 00000000`00000000
ffffc18d`671fd060 00000000`00000000 00000000`00000000
ffffc18d`671fd070 00000000`00000000 00000000`00000000
1: kd> dq ffffc18d`638a0000
ffffc18d`638a0000 00000000`00000000 00000000`00000000
ffffc18d`638a0010 97867967`6040ff59 00000000`00000000 //System的EPROCESS结构地址 需解密
ffffc18d`638a0020 97867e58`e0800001 00000000`00000000
ffffc18d`638a0030 97867967`80800001 00000000`00000000
ffffc18d`638a0040 9786796d`a0800001 00000000`00000000
ffffc18d`638a0050 97867970`20800001 00000000`00000000
ffffc18d`638a0060 97867973`50800001 00000000`00000000
ffffc18d`638a0070 9786797b`a1400001 00000000`00000000

上面TableCode成员是指向句柄表的指针,二进制低2位表示句柄表等级。0b00为下层表,0b01为中层表,0b10为上层表,这里0xffffc18d`671fd001就是个中层表。

EPROCESS地址解密方法为:

1
2
3
4
5
6
7
8
9
#include <Windows.h>
#include <iostream>
int _tmain(int argc, _TCHAR* argv[]) {
ULONG64 ul_recode = 0xb281de283300ffa7;
ULONG64 ul_decode = (LONG64)ul_recode >> 0x10;
ul_decode &= 0xfffffffffffffff0;
std::cout << "解密后地址: " << std::hex << ul_decode << std::endl;
return 0;
}

接下来获取PspCidTable地址,并对各级表解析:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <ntifs.h>
#include <windef.h>
// 获取 PspCidTable
BOOLEAN get_PspCidTable(ULONG64* tableAddr) {
// 获取 PsLookupProcessByProcessId 地址
UNICODE_STRING uc_funcName;
RtlInitUnicodeString(&uc_funcName, L"PsLookupProcessByProcessId");
ULONG64 ul_funcAddr = MmGetSystemRoutineAddress(&uc_funcName);
if (ul_funcAddr == NULL)
return FALSE;
DbgPrint("PsLookupProcessByProcessId addr = %p \n", ul_funcAddr);
// 前 40 字节有 call(PspReferenceCidTableEntry)
/*
0: kd> uf PsLookupProcessByProcessId
nt!PsLookupProcessByProcessId:
fffff802`0841cfe0 48895c2418   mov   qword ptr [rsp+18h],rbx
fffff802`0841cfe5 56       push  rsi
fffff802`0841cfe6 4883ec20    sub   rsp,20h
fffff802`0841cfea 48897c2438   mov   qword ptr [rsp+38h],rdi
fffff802`0841cfef 488bf2     mov   rsi,rdx
fffff802`0841cff2 65488b3c2588010000 mov  rdi,qword ptr gs:[188h]
fffff802`0841cffb 66ff8fe6010000 dec   word ptr [rdi+1E6h]
fffff802`0841d002 b203      mov   dl,3
fffff802`0841d004 e887000000   call  nt!PspReferenceCidTableEntry(fffff802`0841d090)
fffff802`0841d009 488bd8     mov   rbx,rax
fffff802`0841d00c 4885c0     test  rax,rax
fffff802`0841d00f 7435      je  nt!PsLookupProcessByProcessId+0x66 (fffff802`0841d046) Branch
*/
ULONG64 ul_entry = 0;
for (INT i = 0; i < 100; i++)
// fffff802`0841d004 e8 87 00 00 00   call nt!PspReferenceCidTableEntry(fffff802`0841d090)
if (*(PUCHAR)(ul_funcAddr + i) == 0xe8) {
ul_entry = ul_funcAddr + i;
break;
}
if (ul_entry != 0) {
// 解析 call 地址
INT i_callCode = *(INT*)(ul_entry + 1);
DbgPrint("i_callCode = %p \n", i_callCode);
ULONG64 ul_callJmp = ul_entry + i_callCode + 5;
DbgPrint("ul_callJmp = %p \n", ul_callJmp);
// 来到 call(PspReferenceCidTableEntry) 内找 PspCidTable
/*
0: kd> uf PspReferenceCidTableEntry
nt!PspReferenceCidTableEntry+0x115:
fffff802`0841d1a5 488b0d8473f5ff mov   rcx,qword ptr [nt!PspCidTable (fffff802`08374530)]
fffff802`0841d1ac b801000000   mov   eax,1
fffff802`0841d1b1 f0480fc107   lock xadd qword ptr [rdi],rax
fffff802`0841d1b6 4883c130    add   rcx,30h
fffff802`0841d1ba f0830c2400   lock or dword ptr [rsp],0
fffff802`0841d1bf 48833900    cmp   qword ptr [rcx],0
fffff802`0841d1c3 0f843fffffff  je   nt!PspReferenceCidTableEntry+0x78 (fffff802`0841d108) Branch
*/
for (INT i = 0; i < 0x120; i++) {
// fffff802`0841d1a5 48 8b 0d 84 73 f5 ff mov   rcx,qword ptr[nt!PspCidTable(fffff802`08374530)]
if (*(PUCHAR)(ul_callJmp + i) == 0x48 && *(PUCHAR)(ul_callJmp + i + 1) == 0x8b && *(PUCHAR)(ul_callJmp + i + 2) == 0x0d) {
// 解析 mov 地址
INT i_movCode = *(INT*)(ul_callJmp + i + 3);
DbgPrint("i_movCode = %p \n", i_movCode);
ULONG64 ul_movJmp = ul_callJmp + i + i_movCode + 7;
DbgPrint("ul_movJmp = %p \n", ul_movJmp);
// 得到 PspCidTable
*tableAddr = ul_movJmp;
return TRUE;
}
}
}
return FALSE;
}
/* 解析一级表
BaseAddr:一级表的基地址
index1:第几个一级表
index2:第几个二级表
*/
VOID parse_table_1(ULONG64 BaseAddr, INT index1, INT index2) {
// 遍历一级表(每个表项大小 16 ),表大小 4k,所以遍历 4096/16 = 526 次
PEPROCESS p_eprocess = NULL;
PETHREAD p_ethread = NULL;
INT i_id = 0;
for (INT i = 0; i < 256; i++) {
if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 16))) {
DbgPrint("非法地址= %p \n", BaseAddr + i * 16);
continue;
}
ULONG64 ul_recode = *(PULONG64)(BaseAddr + i * 16);
// 解密
ULONG64 ul_decode = (LONG64)ul_recode >> 0x10;
ul_decode &= 0xfffffffffffffff0;
// 判断是进程还是线程
i_id = i * 4 + 1024 * index1 + 512 * index2 * 1024;
if (PsLookupProcessByProcessId(i_id, &p_eprocess) == STATUS_SUCCESS)
DbgPrint("进程PID: %d | ID: %d | 内存地址: %p | 对象: %p \n", i_id, i, BaseAddr + i * 0x10, ul_decode);
else if (PsLookupThreadByThreadId(i_id, &p_ethread) == STATUS_SUCCESS)
DbgPrint("线程TID: %d | ID: %d | 内存地址: %p | 对象: %p \n", i_id, i, BaseAddr + i * 0x10, ul_decode);
}
}
/* 解析二级表
BaseAddr:二级表基地址
index2:第几个二级表
*/
VOID parse_table_2(ULONG64 BaseAddr, INT index2) {
// 遍历二级表(每个表项大小 8),表大小 4k,所以遍历 4096/8 = 512 次
ULONG64 ul_baseAddr_1 = 0;
for (INT i = 0; i < 512; i++) {
if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 8))) {
DbgPrint("非法二级表指针(1):%p \n", BaseAddr + i * 8);
continue;
}
if (!MmIsAddressValid((PVOID64) * (PULONG64)(BaseAddr + i * 8))) {
DbgPrint("非法二级表指针(2):%p \n", BaseAddr + i * 8);
continue;
}
ul_baseAddr_1 = *(PULONG64)(BaseAddr + i * 8);
parse_table_1(ul_baseAddr_1, i, index2);
}
}
/* 解析三级表
BaseAddr:三级表基地址
*/
VOID parse_table_3(ULONG64 BaseAddr) {
// 遍历三级表(每个表项大小 8),表大小 4k,所以遍历 4096/8 = 512 次
ULONG64 ul_baseAddr_2 = 0;
for (INT i = 0; i < 512; i++) {
if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 8)))
continue;
if (!MmIsAddressValid((PVOID64) * (PULONG64)(BaseAddr + i * 8)))
continue;
ul_baseAddr_2 = *(PULONG64)(BaseAddr + i * 8);
parse_table_2(ul_baseAddr_2, i);
}
}
VOID UnDriver(PDRIVER_OBJECT driver) {
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
ULONG64 tableAddr = 0;
get_PspCidTable(&tableAddr);
DbgPrint("PspCidTable Address = %p \n", tableAddr);
// 获取 _HANDLE_TABLE 的 TableCode
ULONG64 ul_tableCode = *(PULONG64)(((ULONG64) * (PULONG64)tableAddr) + 8);
DbgPrint("ul_tableCode = %p \n", ul_tableCode);
// 取低 2位(二级制11 = 3)
INT i_low2 = ul_tableCode & 3;
DbgPrint("i_low2 = %X \n", i_low2);
// 一级表
if (i_low2 == 0)
// TableCode 低 2位抹零(二级制11 = 3)
parse_table_1(ul_tableCode & (~3), 0, 0);
// 二级表
else if (i_low2 == 1)
// TableCode 低 2位抹零(二级制11 = 3)
parse_table_2(ul_tableCode & (~3), 0);
// 三级表
else if (i_low2 == 2)
// TableCode 低 2位抹零(二级制11 = 3)
parse_table_3(ul_tableCode & (~3));
else {
DbgPrint("错误,非法! ");
return FALSE;
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

枚举Minifilter微过滤驱动

FltEnumerateFilters可获取所有过滤器地址。当参数FilterList为NULL则输出系统存在的过滤器数量,为内存地址则枚举系统存在的过滤器信息。

1
2
3
4
5
NTSTATUS FLTAPI FltEnumerateFilters(
[out] PFLT_FILTER* FilterList,
[in]  ULONG    FilterListSize,
[out] PULONG    NumberFiltersReturned
);

该API返回过滤器对象FLT_FILTER的地址,由此得到记录过滤器PreCall、PostCall、IRP等信息的Operations成员,后者是个PFLT_OPERATION_REGISTRATION结构体指针。

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
1: kd> dt fltmgr!_FLT_FILTER
+0x000 Base : _FLT_OBJECT
+0x030 Frame : Ptr64 _FLTP_FRAME
+0x038 Name : _UNICODE_STRING
+0x048 DefaultAltitude : _UNICODE_STRING
+0x058 Flags : _FLT_FILTER_FLAGS
+0x060 DriverObject : Ptr64 _DRIVER_OBJECT
+0x068 InstanceList : _FLT_RESOURCE_LIST_HEAD
+0x0e8 VerifierExtension : Ptr64 _FLT_VERIFIER_EXTENSION
+0x0f0 VerifiedFiltersLink : _LIST_ENTRY
+0x100 FilterUnload : Ptr64 long
+0x108 InstanceSetup : Ptr64 long
+0x110 InstanceQueryTeardown : Ptr64 long
+0x118 InstanceTeardownStart : Ptr64 void
+0x120 InstanceTeardownComplete : Ptr64 void
+0x128 SupportedContextsListHead : Ptr64 _ALLOCATE_CONTEXT_HEADER
+0x130 SupportedContexts : [7] Ptr64 _ALLOCATE_CONTEXT_HEADER
+0x168 PreVolumeMount : Ptr64 _FLT_PREOP_CALLBACK_STATUS
+0x170 PostVolumeMount : Ptr64 _FLT_POSTOP_CALLBACK_STATUS
+0x178 GenerateFileName : Ptr64 long
+0x180 NormalizeNameComponent : Ptr64 long
+0x188 NormalizeNameComponentEx : Ptr64 long
+0x190 NormalizeContextCleanup : Ptr64 void
+0x198 KtmNotification : Ptr64 long
+0x1a0 SectionNotification : Ptr64 long
+0x1a8 Operations : Ptr64 _FLT_OPERATION_REGISTRATION
+0x1b0 OldDriverUnload : Ptr64 void
+0x1b8 ActiveOpens : _FLT_MUTEX_LIST_HEAD
+0x208 ConnectionList : _FLT_MUTEX_LIST_HEAD
+0x258 PortList : _FLT_MUTEX_LIST_HEAD
+0x2a8 PortLock : _EX_PUSH_LOCK
1: kd> dt fltmgr!_FLT_OPERATION_REGISTRATION
+0x000 MajorFunction : UChar
+0x004 Flags : Uint4B
+0x008 PreOperation : Ptr64 _FLT_PREOP_CALLBACK_STATUS
+0x010 PostOperation : Ptr64 _FLT_POSTOP_CALLBACK_STATUS
+0x018 Reserved1 : Ptr64 Void

实现如下,需要手动链接fltMgr.lib。

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
#include <fltKernel.h>
#include <dontuse.h>
#include <suppress.h>
// 设置默认回调
NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp) {
NTSTATUS status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
VOID DriverUnload(PDRIVER_OBJECT pDriverObject) {
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath) {
NTSTATUS status = STATUS_SUCCESS;
pDriverObject->DriverUnload = DriverUnload;
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
pDriverObject->MajorFunction[i] = DriverDefaultHandle;
ULONG ulFilterListSize = 0;
PFLT_FILTER* ppFilterList = NULL;
ULONG i = 0;
LONG lOperationsOffset = 0;
PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL;
// 获取 Minifilter 过滤器Filter 的数量
FltEnumerateFilters(NULL, 0, &ulFilterListSize);
// 申请内存
ppFilterList = (PFLT_FILTER*)ExAllocatePool(NonPagedPool, ulFilterListSize * sizeof(PFLT_FILTER));
if (NULL == ppFilterList)
return FALSE;
// 获取 Minifilter 中所有过滤器Filter 的信息
status = FltEnumerateFilters(ppFilterList, ulFilterListSize, &ulFilterListSize);
if (!NT_SUCCESS(status))
return FALSE;
DbgPrint("过滤器数量: %d \n", ulFilterListSize);
// 获取 PFLT_FILTER 中 Operations 偏移
lOperationsOffset = 0x1A8;
// 开始遍历 Minifilter
__try {
for (i = 0; i < ulFilterListSize; i++) {
// 获取 PFLT_FILTER 中 Operations 成员地址
pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(PVOID*)((PUCHAR)ppFilterList[i] + lOperationsOffset));
__try {
// 同一过滤器下的回调信息
while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction) {
if (IRP_MJ_MAXIMUM_FUNCTION > pFltOperationRegistration->MajorFunction)
// 显示
DbgPrint("Filter: %p | IRP: %d | PreFunc: 0x%p |PostFunc = 0x % p \n", ppFilterList[i], pFltOperationRegistration->MajorFunction, pFltOperationRegistration->PreOperation, pFltOperationRegistration->PostOperation);
// 获取下一个消息回调信息
pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((PUCHAR)pFltOperationRegistration + sizeof(FLT_OPERATION_REGISTRATION));
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
// 释放内存
ExFreePool(ppFilterList);
ppFilterList = NULL;
return status;
}

枚举LoadImage映像回调

回调数组名为PspLoadImageNotifyRoutine,可在PsSetLoadImageNotifyRoutineEx中找到,该数组还被加密了:

1
2
3
4
5
6
7
8
9
10
11
1: kd> uf PsSetLoadImageNotifyRoutineEx
...
nt!PsSetLoadImageNotifyRoutineEx+0x41:
fffff800`559733c1 488d0d588c5700 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff800`55eec020)]
fffff800`559733c8 4533c0 xor r8d,r8d
fffff800`559733cb 488d0cd9 lea rcx,[rcx+rbx*8]
fffff800`559733cf 488bd7 mov rdx,rdi
fffff800`559733d2 e8c514c3ff call nt!ExCompareExchangeCallBack (fffff800`555a489c)
fffff800`559733d7 84c0 test al,al
fffff800`559733d9 0f84a0000000 je nt!PsSetLoadImageNotifyRoutineEx+0xff (fffff800`5597347f) Branch
...

实现方法:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <ntddk.h>
#include <windef.h>
// 指定内存区域的特征码扫描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize) {
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;
// 扫描内存
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) {
// 判断特征码
for (m = 0; m < ulMemoryDataSize; m++)
if (*(PUCHAR)(i + m) != pMemoryData[m])
break;
// 判断是否找到符合特征码的地址
if (m >= ulMemoryDataSize) {
// 找到特征码位置, 获取紧接着特征码的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}
return pAddress;
}
// 根据特征码获取 PspLoadImageNotifyRoutine 数组地址
PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize) {
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetLoadImageNotifyRoutine = NULL;
PVOID pPspLoadImageNotifyRoutine = NULL;
// 先获取 PsSetLoadImageNotifyRoutineEx 函数地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetLoadImageNotifyRoutineEx");
pPsSetLoadImageNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetLoadImageNotifyRoutine)
return pPspLoadImageNotifyRoutine;
// 查找 PspLoadImageNotifyRoutine 函数地址
pAddress = SearchMemory(pPsSetLoadImageNotifyRoutine, (PVOID)((PUCHAR)pPsSetLoadImageNotifyRoutine + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
return pPspLoadImageNotifyRoutine;
// 先获取偏移, 再计算地址
lOffset = *(PLONG)pAddress;
pPspLoadImageNotifyRoutine = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
return pPspLoadImageNotifyRoutine;
}
// 移除回调
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress) {
NTSTATUS status = PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)pNotifyRoutineAddress);
return status;
}
VOID UnDriver(PDRIVER_OBJECT Driver) {
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
PVOID pPspLoadImageNotifyRoutineAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
// 获取系统版本信息, 判断系统版本
RtlGetVersion(&osInfo);
if (10 == osInfo.dwMajorVersion) {
// 48 8d 0d 88 e8 db ff
// 查找指令 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff804`44313ce0)]
/*
nt!PsSetLoadImageNotifyRoutineEx+0x41:
fffff801`80748a81 488d0dd8d3dbff lea   rcx, [nt!PspLoadImageNotifyRoutine (fffff801`80505e60)]
fffff801`80748a88 4533c0     xor   r8d,r8d
fffff801`80748a8b 488d0cd9    lea   rcx,[rcx+rbx*8]
fffff801`80748a8f 488bd7     mov   rdx,rdi
fffff801`80748a92 e80584a3ff   call  nt!ExCompareExchangeCallBack(fffff801`80180e9c)
fffff801`80748a97 84c0      test  al,al
fffff801`80748a99 0f849f000000  je  nt!PsSetLoadImageNotifyRoutineEx+0xfe (fffff801`80748b3e) Branch
*/
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x0D;
ulSpecialDataSize = 3;
}
// 根据特征码获取地址 获取 PspLoadImageNotifyRoutine 数组地址
pPspLoadImageNotifyRoutineAddress = SearchPspLoadImageNotifyRoutine(pSpecialData, ulSpecialDataSize);
DbgPrint("PspLoadImageNotifyRoutine = 0x%p \n", pPspLoadImageNotifyRoutineAddress);
// 遍历回调
ULONG i = 0;
PVOID pNotifyRoutineAddress = NULL;
// 获取 PspLoadImageNotifyRoutine 数组地址
if (NULL == pPspLoadImageNotifyRoutineAddress)
return FALSE;
// 获取回调地址并解密
for (i = 0; i < 64; i++) {
pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspLoadImageNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress)) {
pNotifyRoutineAddress = *(PVOID*)pNotifyRoutineAddress;
DbgPrint("序号: %d | 回调地址: 0x%p \n", i, pNotifyRoutineAddress);
}
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

枚举Registry注册表回调

消息回调链表头为CallbackListHead,是个CM_NOTIFY_ENTRY结构,需要遍历则用ListEntryHead.Flink向下移动指针。

1
2
3
4
5
6
7
8
9
10
11
12
3: kd> uf nt!CmUnRegisterCallback
...
nt!CmUnRegisterCallback+0x67:
fffff800`55a65b47 4533c0 xor r8d,r8d
fffff800`55a65b4a 488d542438 lea rdx,[rsp+38h]
fffff800`55a65b4f 488d0d5a283e00 lea rcx,[nt!CallbackListHead (fffff800`55e483b0)]
fffff800`55a65b56 e82576dfff call nt!CmListGetNextElement (fffff800`5585d180)
fffff800`55a65b5b 488bf8 mov rdi,rax
fffff800`55a65b5e 4889442440 mov qword ptr [rsp+40h],rax
fffff800`55a65b63 4885c0 test rax,rax
fffff800`55a65b66 0f84cf000000 je nt!CmUnRegisterCallback+0x15b (fffff800`55a65c3b) Branch
...

实现为:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <ntifs.h>
#include <windef.h>
// 指定内存区域的特征码扫描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize) {
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;
// 扫描内存
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) {
// 判断特征码
for (m = 0; m < ulMemoryDataSize; m++)
if (*(PUCHAR)(i + m) != pMemoryData[m])
break;
// 判断是否找到符合特征码的地址
if (m >= ulMemoryDataSize) {
// 找到特征码位置, 获取紧接着特征码的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}
return pAddress;
}
// 根据特征码获取 CallbackListHead 链表地址
PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset) {
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pCmUnRegisterCallback = NULL;
PVOID pCallbackListHead = NULL;
// 先获取 CmUnRegisterCallback 函数地址
RtlInitUnicodeString(&ustrFuncName, L"CmUnRegisterCallback");
pCmUnRegisterCallback = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pCmUnRegisterCallback)
return pCallbackListHead;
// 查找 fffff806`3a4271b3 488d0d06eac3ff lea   rcx,[nt!CallbackListHead(fffff806`3a065bc0)]
/*
nt!CmUnRegisterCallback+0x6b:
fffff806`3a4271ab 4533c0     xor   r8d,r8d
fffff806`3a4271ae 488d542438   lea   rdx,[rsp+38h]
fffff806`3a4271b3 488d0d06eac3ff lea   rcx,[nt!CallbackListHead(fffff806`3a065bc0)]
fffff806`3a4271ba e855e2e2ff   call  nt!CmListGetNextElement(fffff806`3a255414)
fffff806`3a4271bf 488bf8     mov   rdi,rax
fffff806`3a4271c2 4889442440   mov   qword ptr [rsp+40h],rax
fffff806`3a4271c7 4885c0     test  rax,rax
fffff806`3a4271ca 0f84c7000000  je   nt!CmUnRegisterCallback+0x157(fffff806`3a427297) Branch
*/
pAddress = SearchMemory(pCmUnRegisterCallback, (PVOID)((PUCHAR)pCmUnRegisterCallback + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
return pCallbackListHead;
// 先获取偏移再计算地址
lOffset = *(PLONG)((PUCHAR)pAddress + lSpecialOffset);
pCallbackListHead = (PVOID)((PUCHAR)pAddress + lSpecialOffset + sizeof(LONG) + lOffset);
return pCallbackListHead;
}
// 注册表回调函数结构体定义
typedef struct _CM_NOTIFY_ENTRY {
LIST_ENTRY  ListEntryHead;
ULONG  UnKnown1;
ULONG  UnKnown2;
LARGE_INTEGER Cookie;
PVOID  Context;
PVOID  Function;
}CM_NOTIFY_ENTRY, * PCM_NOTIFY_ENTRY;
VOID UnDriver(PDRIVER_OBJECT Driver) {
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) {
PVOID pCallbackListHeadAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
LONG lSpecialOffset = 0;
// 查找 fffff806`3a4271b3 488d0d06eac3ff lea   rcx,[nt!CallbackListHead(fffff806`3a065bc0)]
/*
nt!CmUnRegisterCallback+0x6b:
fffff806`3a4271ab 4533c0     xor   r8d,r8d
fffff806`3a4271ae 488d542438   lea   rdx,[rsp+38h]
fffff806`3a4271b3 488d0d06eac3ff lea   rcx,[nt!CallbackListHead(fffff806`3a065bc0)]
fffff806`3a4271ba e855e2e2ff   call  nt!CmListGetNextElement(fffff806`3a255414)
fffff806`3a4271bf 488bf8     mov   rdi,rax
fffff806`3a4271c2 4889442440   mov   qword ptr [rsp+40h],rax
fffff806`3a4271c7 4885c0     test  rax,rax
fffff806`3a4271ca 0f84c7000000  je   nt!CmUnRegisterCallback+0x157(fffff806`3a427297) Branch
*/
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x0D;
ulSpecialDataSize = 3;
// 根据特征码获取地址
pCallbackListHeadAddress = SearchCallbackListHead(pSpecialData, ulSpecialDataSize, lSpecialOffset);
DbgPrint("CallbackListHead => %p \n", pCallbackListHeadAddress);
// 遍历链表结构
ULONG i = 0;
PCM_NOTIFY_ENTRY pNotifyEntry = NULL;
if (NULL == pCallbackListHeadAddress)
return FALSE;
// 开始遍历双向链表
pNotifyEntry = (PCM_NOTIFY_ENTRY)pCallbackListHeadAddress;
do {
// 判断pNotifyEntry地址是否有效
if (FALSE == MmIsAddressValid(pNotifyEntry))
break;
// 判断回调函数地址是否有效
if (MmIsAddressValid(pNotifyEntry->Function))
DbgPrint("回调函数地址: 0x%p | 回调函数Cookie: 0x%I64X\n", pNotifyEntry->Function, pNotifyEntry->Cookie.QuadPart);
// 获取下一链表
pNotifyEntry = (PCM_NOTIFY_ENTRY)pNotifyEntry->ListEntryHead.Flink;
} while (pCallbackListHeadAddress != (PVOID)pNotifyEntry);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

枚举进线程ObCall回调

这里枚举系统中ProcessObCall进程回调和ThreadObCall线程回调,用到的结构体如下:

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
0: kd> dt nt!_OBJECT_TYPE_INITIALIZER
+0x000 Length : Uint2B
+0x002 ObjectTypeFlags : Uint2B
+0x002 CaseInsensitive : Pos 0, 1 Bit
+0x002 UnnamedObjectsOnly : Pos 1, 1 Bit
+0x002 UseDefaultObject : Pos 2, 1 Bit
+0x002 SecurityRequired : Pos 3, 1 Bit
+0x002 MaintainHandleCount : Pos 4, 1 Bit
+0x002 MaintainTypeList : Pos 5, 1 Bit
+0x002 SupportsObjectCallbacks : Pos 6, 1 Bit
+0x002 CacheAligned : Pos 7, 1 Bit
+0x003 UseExtendedParameters : Pos 0, 1 Bit
+0x003 Reserved : Pos 1, 7 Bits
+0x004 ObjectTypeCode : Uint4B
+0x008 InvalidAttributes : Uint4B
+0x00c GenericMapping : _GENERIC_MAPPING
+0x01c ValidAccessMask : Uint4B
+0x020 RetainAccess : Uint4B
+0x024 PoolType : _POOL_TYPE
+0x028 DefaultPagedPoolCharge : Uint4B
+0x02c DefaultNonPagedPoolCharge : Uint4B
+0x030 DumpProcedure : Ptr64 void
+0x038 OpenProcedure : Ptr64 long
+0x040 CloseProcedure : Ptr64 void
+0x048 DeleteProcedure : Ptr64 void
+0x050 ParseProcedure : Ptr64 long
+0x050 ParseProcedureEx : Ptr64 long
+0x058 SecurityProcedure : Ptr64 long
+0x060 QueryNameProcedure : Ptr64 long
+0x068 OkayToCloseProcedure : Ptr64 unsigned char
+0x070 WaitObjectFlagMask : Uint4B
+0x074 WaitObjectFlagOffset : Uint2B
+0x076 WaitObjectPointerOffset : Uint2B
0: kd> dt nt!_OBJECT_TYPE
+0x000 TypeList : _LIST_ENTRY
+0x010 Name : _UNICODE_STRING
+0x020 DefaultObject : Ptr64 Void
+0x028 Index : UChar
+0x02c TotalNumberOfObjects : Uint4B
+0x030 TotalNumberOfHandles : Uint4B
+0x034 HighWaterNumberOfObjects : Uint4B
+0x038 HighWaterNumberOfHandles : Uint4B
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : Uint4B
+0x0c8 CallbackList : _LIST_ENTRY

还有:

1
2
3
4
5
6
7
8
9
10
#pragma pack(1)
typedef struct _OB_CALLBACK {
LIST_ENTRY ListEntry;
ULONGLONG Unknown;
HANDLE ObHandle;
PVOID ObTypeAddr;
PVOID PreCall;
PVOID PostCall;
}OB_CALLBACK, * POB_CALLBACK;
#pragma pack()

枚举进程句柄回调:

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
66
67
68
69
70
#include <ntifs.h>
#include <wdm.h>
#include <ntddk.h>
typedef struct _OBJECT_TYPE_INITIALIZER {
USHORT Length;         // Uint2B
UCHAR ObjectTypeFlags;       // UChar
ULONG ObjectTypeCode;       // Uint4B
ULONG InvalidAttributes;      // Uint4B
GENERIC_MAPPING GenericMapping;  // _GENERIC_MAPPING
ULONG ValidAccessMask;    // Uint4B
ULONG RetainAccess;     // Uint4B
POOL_TYPE PoolType;     // _POOL_TYPE
ULONG DefaultPagedPoolCharge;  // Uint4B
ULONG DefaultNonPagedPoolCharge; // Uint4B
PVOID DumpProcedure;    // Ptr64   void
PVOID OpenProcedure;    // Ptr64   long
PVOID CloseProcedure;   // Ptr64   void
PVOID DeleteProcedure;     // Ptr64   void
PVOID ParseProcedure;   // Ptr64   long
PVOID SecurityProcedure;    // Ptr64   long
PVOID QueryNameProcedure;   // Ptr64   long
PVOID OkayToCloseProcedure;   // Ptr64   unsigned char
ULONG WaitObjectFlagMask;   // Uint4B
USHORT WaitObjectFlagOffset;   // Uint2B
USHORT WaitObjectPointerOffset;  // Uint2B
}OBJECT_TYPE_INITIALIZER, * POBJECT_TYPE_INITIALIZER;
typedef struct _OBJECT_TYPE {
LIST_ENTRY TypeList;      // _LIST_ENTRY
UNICODE_STRING Name;     // _UNICODE_STRING
PVOID DefaultObject;     // Ptr64 Void
UCHAR Index;       // UChar
ULONG TotalNumberOfObjects;    // Uint4B
ULONG TotalNumberOfHandles;    // Uint4B
ULONG HighWaterNumberOfObjects;   // Uint4B
ULONG HighWaterNumberOfHandles;   // Uint4B
OBJECT_TYPE_INITIALIZER TypeInfo;  // _OBJECT_TYPE_INITIALIZER
EX_PUSH_LOCK TypeLock;     // _EX_PUSH_LOCK
ULONG Key;         // Uint4B
LIST_ENTRY CallbackList;    // _LIST_ENTRY
}OBJECT_TYPE, * POBJECT_TYPE;
#pragma pack(1)
typedef struct _OB_CALLBACK {
LIST_ENTRY ListEntry;
ULONGLONG Unknown;
HANDLE ObHandle;
PVOID ObTypeAddr;
PVOID PreCall;
PVOID PostCall;
}OB_CALLBACK, * POB_CALLBACK;
#pragma pack()
VOID DriverUnload(PDRIVER_OBJECT pDriverObject) {
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath) {
NTSTATUS status = STATUS_SUCCESS;
POB_CALLBACK pObCallback = NULL;
// 直接获取 CallbackList 链表
LIST_ENTRY CallbackList = ((POBJECT_TYPE)(*PsProcessType))->CallbackList; //PsProcessType换成PsThreadType即可枚举线程句柄回调
// 开始遍历
pObCallback = (POB_CALLBACK)CallbackList.Flink;
do {
if (FALSE == MmIsAddressValid(pObCallback))
break;
if (NULL != pObCallback->ObHandle)
// 显示
DbgPrint("ObHandle = %p | PreCall = %p | PostCall = %p\n", pObCallback->ObHandle, pObCallback->PreCall, pObCallback->PostCall);
// 获取下一链表信息
pObCallback = (POB_CALLBACK)pObCallback->ListEntry.Flink;
} while (CallbackList.Flink != (PLIST_ENTRY)pObCallback);
return status;
}