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;
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)); 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地址,存储定时器链表头部。枚举方法如下:
- 找到
IoInitializeTimer
函数,该函数可以通过MmGetSystemRoutineAddress
得到。
- 找到地址以后,我们向下增加0xFF偏移量,并搜索特征定位到IopTimerQueueHead链表头。
- 将链表头转换为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;
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);
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); 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定时器步骤为:
- 找到KiProcessorBlock地址并解析成_KPRCB结构。
- 在_KPRCB 结构中得到_KTIMER_TABLE偏移。
- 解析_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>
void DPC_Print(PKTIMER ptrTimer) { ULONG_PTR ptrDpc = (ULONG_PTR)ptrTimer->Dpc; KDPC* DecDpc = NULL; DWORD nShift = (p2dq(ptrKiWaitNever) & 0xFF); ptrDpc ^= p2dq(ptrKiWaitNever); ptrDpc = _rotl64(ptrDpc, nShift); ptrDpc ^= (ULONG_PTR)ptrTimer; ptrDpc = _byteswap_uint64(ptrDpc); ptrDpc ^= p2dq(ptrKiWaitAlways); 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>
ULONG64 GetKiProcessorBlock() { ULONG64 PrcbAddress = 0; PrcbAddress = (ULONG64)__readmsr(0xC0000101) + 0x20; if (PrcbAddress != 0) 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>
ULONG64 GetKeSetTimerEx() { 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; BOOLEAN b_e8 = FALSE; ULONG64 ul_e8Addr = 0; for (INT i = 0; i < 30; i++) { if (!MmIsAddressValid((PVOID64)ul_KeSetTimer)) continue; if (*(PUCHAR)(ul_KeSetTimer + i) == 0xe8) { b_e8 = TRUE; ul_e8Addr = ul_KeSetTimer + i; break; } } 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
中搜索KiWaitNever
和KiWaitAlways
两个函数地址:
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>
ULONG64 GetKeSetTimerEx() { 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; BOOLEAN b_e8 = FALSE; ULONG64 ul_e8Addr = 0; for (INT i = 0; i < 30; i++) { if (!MmIsAddressValid((PVOID64)ul_KeSetTimer)) continue; if (*(PUCHAR)(ul_KeSetTimer + i) == 0xe8) { b_e8 = TRUE; ul_e8Addr = ul_KeSetTimer + i; break; } } 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; }
ULONG64 GetKiWaitNever(ULONG64 address) { if (!MmIsAddressValid((PVOID64)address)) return 0; for (INT i = 0; i < 100; i++) 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; return ul_movAddr; } return 0; }
ULONG64 GetKiWaitAlways(ULONG64 address) { if (!MmIsAddressValid((PVOID64)address)) return 0; for (INT i = 0; i < 100; i++) 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) { 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; BOOLEAN b_e8 = FALSE; ULONG64 ul_e8Addr = 0; for (INT i = 0; i < 30; i++) { if (!MmIsAddressValid((PVOID64)ul_KeSetTimer)) continue;
if (*(PUCHAR)(ul_KeSetTimer + i) == 0xe8) { b_e8 = TRUE; ul_e8Addr = ul_KeSetTimer + i; break; } }
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; } else ul_KiSetTimerEx = ul_KeSetTimer;
if (!MmIsAddressValid((PVOID64)ul_KiSetTimerEx)) return FALSE; ULONG64 ul_arr_ret[2]; INT i_sub = 0; for (INT i = 0; i < 50; i++) 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() { INT i_cpuNum = KeNumberProcessors; DbgPrint("CPU核心数: %d \n", i_cpuNum); for (KAFFINITY i = 0; i < i_cpuNum; i++) { KeSetSystemAffinityThread(i + 1); ULONG64 p_PRCB = (ULONG64)__readmsr(0xC0000101) + 0x20; if (!MmIsAddressValid((PVOID64)p_PRCB)) return FALSE; KeRevertToUserAffinityThread(); PKTIMER_TABLE p_TimeTable = NULL; p_TimeTable = (PKTIMER_TABLE)(*(PULONG64)p_PRCB + 0x3680); for (INT j = 0; j < 256; j++) { if (!MmIsAddressValid((PVOID64)p_TimeTable)) continue; PLIST_ENTRY p_ListEntryHead = &(p_TimeTable->TimerEntries[j].Entry); for (PLIST_ENTRY p_ListEntry = p_ListEntryHead->Flink; p_ListEntry != p_ListEntryHead; p_ListEntry = p_ListEntry->Flink) { if (!MmIsAddressValid((PVOID64)p_ListEntry)) continue; PKTIMER p_Timer = CONTAINING_RECORD(p_ListEntry, KTIMER, TimerListEntry); ULONG64 never = 0, always = 0; if (get_KiWait(&never, &always) == FALSE) return FALSE; if (!MmIsAddressValid((PVOID64)p_Timer)) continue; ULONG64 ul_Dpc = (ULONG64)p_Timer->Dpc; INT i_Shift = (*((PULONG64)never) & 0xFF); 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; }
NTSTATUS myIrpControl(IN PDEVICE_OBJECT pDevObj, IN PIRP pIRP) { PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIRP); ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength; ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength; ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; switch (code) { case IRP_TEST: break; default: break; } IoCompleteRequest(pIRP, IO_NO_INCREMENT); return STATUS_SUCCESS; }
NTSTATUS dpc_CAC(IN PDEVICE_OBJECT pDevObj, IN PIRP pIRP) { IoCompleteRequest(pIRP, IO_NO_INCREMENT); pIRP->IoStatus.Status = STATUS_SUCCESS; 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是个独立的句柄表,进程私有句柄表是以双链连接的。
枚举句柄表方法为:
- 找到
PsLookupProcessByProcessId
函数地址,该函数是被导出的可以动态拿到。
- 在
PsLookupProcessByProcessId
地址中搜索PspReferenceCidTableEntry
。
- 在
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>
BOOLEAN get_PspCidTable(ULONG64* tableAddr) { 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);
ULONG64 ul_entry = 0; for (INT i = 0; i < 100; i++) if (*(PUCHAR)(ul_funcAddr + i) == 0xe8) { ul_entry = ul_funcAddr + i; break; } if (ul_entry != 0) { 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);
for (INT i = 0; i < 0x120; i++) { if (*(PUCHAR)(ul_callJmp + i) == 0x48 && *(PUCHAR)(ul_callJmp + i + 1) == 0x8b && *(PUCHAR)(ul_callJmp + i + 2) == 0x0d) { 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); *tableAddr = ul_movJmp; return TRUE; } } } return FALSE; }
VOID parse_table_1(ULONG64 BaseAddr, INT index1, INT index2) { 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); } }
VOID parse_table_2(ULONG64 BaseAddr, INT index2) { 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); } }
VOID parse_table_3(ULONG64 BaseAddr) { 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); ULONG64 ul_tableCode = *(PULONG64)(((ULONG64) * (PULONG64)tableAddr) + 8); DbgPrint("ul_tableCode = %p \n", ul_tableCode); INT i_low2 = ul_tableCode & 3; DbgPrint("i_low2 = %X \n", i_low2); if (i_low2 == 0) parse_table_1(ul_tableCode & (~3), 0, 0); else if (i_low2 == 1) parse_table_2(ul_tableCode & (~3), 0); else if (i_low2 == 2) 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; FltEnumerateFilters(NULL, 0, &ulFilterListSize); ppFilterList = (PFLT_FILTER*)ExAllocatePool(NonPagedPool, ulFilterListSize * sizeof(PFLT_FILTER)); if (NULL == ppFilterList) return FALSE; status = FltEnumerateFilters(ppFilterList, ulFilterListSize, &ulFilterListSize); if (!NT_SUCCESS(status)) return FALSE; DbgPrint("过滤器数量: %d \n", ulFilterListSize); lOperationsOffset = 0x1A8; __try { for (i = 0; i < ulFilterListSize; i++) { 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; }
PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize) { UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetLoadImageNotifyRoutine = NULL; PVOID pPspLoadImageNotifyRoutine = NULL; RtlInitUnicodeString(&ustrFuncName, L"PsSetLoadImageNotifyRoutineEx"); pPsSetLoadImageNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetLoadImageNotifyRoutine) return pPspLoadImageNotifyRoutine; 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) {
pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3; } pPspLoadImageNotifyRoutineAddress = SearchPspLoadImageNotifyRoutine(pSpecialData, ulSpecialDataSize); DbgPrint("PspLoadImageNotifyRoutine = 0x%p \n", pPspLoadImageNotifyRoutineAddress); ULONG i = 0; PVOID pNotifyRoutineAddress = NULL; 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; }
PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset) { UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pCmUnRegisterCallback = NULL; PVOID pCallbackListHead = NULL; RtlInitUnicodeString(&ustrFuncName, L"CmUnRegisterCallback"); pCmUnRegisterCallback = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pCmUnRegisterCallback) return pCallbackListHead;
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;
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 { 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; UCHAR ObjectTypeFlags; ULONG ObjectTypeCode; ULONG InvalidAttributes; GENERIC_MAPPING GenericMapping; ULONG ValidAccessMask; ULONG RetainAccess; POOL_TYPE PoolType; ULONG DefaultPagedPoolCharge; ULONG DefaultNonPagedPoolCharge; PVOID DumpProcedure; PVOID OpenProcedure; PVOID CloseProcedure; PVOID DeleteProcedure; PVOID ParseProcedure; PVOID SecurityProcedure; PVOID QueryNameProcedure; PVOID OkayToCloseProcedure; ULONG WaitObjectFlagMask; USHORT WaitObjectFlagOffset; USHORT WaitObjectPointerOffset; }OBJECT_TYPE_INITIALIZER, * POBJECT_TYPE_INITIALIZER; typedef struct _OBJECT_TYPE { LIST_ENTRY TypeList; UNICODE_STRING Name; PVOID DefaultObject; UCHAR Index; ULONG TotalNumberOfObjects; ULONG TotalNumberOfHandles; ULONG HighWaterNumberOfObjects; ULONG HighWaterNumberOfHandles; OBJECT_TYPE_INITIALIZER TypeInfo; EX_PUSH_LOCK TypeLock; ULONG Key; LIST_ENTRY CallbackList; }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; LIST_ENTRY CallbackList = ((POBJECT_TYPE)(*PsProcessType))->CallbackList; 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; }
|