WindowsAPI编程核心技术-进线程监控
进程创建监控
因为x64下没法HOOK,微软搞了一套接口。
PsSetCreateProcessNotifyRoutineEx
设置进程回调监控创建与退出,控制是否允许进程创建。
1 2 3 4
| NTSTATUS PsSetCreateProcessNotifyRoutineEx( _In_ PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, _In_ BOOLEAN Remove )
|
PS_CREATE_NOTIFY_INFO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| typedef struct _PS_CREATE_NOTIFY_INFO { _In_ SIZE_T Size; union { _In_ ULONG Flags; struct { _In_ ULONG FileOpenNameAvailable : 1; _In_ ULONG IsSubsystemProcess : 1; _In_ ULONG Reserved : 30; }; }; _In_ HANDLE ParentProcessId; _In_ CLIENT_ID CreatingThreadId; _Inout_ struct _FILE_OBJECT *FileObject; _In_ PCUNICODE_STRING ImageFileName; _In_opt_ PCUNICODE_STRING CommandLine; _Inout_ NTSTATUS CreationStatus; } PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;
|
破解内核函数强制完整性签名限制
例如PsSetCreateProcessNotifyRoutineEx
、ObRegisterCallbacks
等函数要求程序强制完整性校验,否则调用失败。
首先要在编译时启用驱动签名强制签名属性。在/integritycheck链接器标志设置IMAGE_OPTIONAL_HEADER中DllCharacteristics字段为IMAGE_DLLCHARACTERISTICS_INTEGRITY属性。
内核通过MmVerifyCallbackFunction
验证限制函数调用是否合法,但它只是验证了DriverObejct->DriverSection->Flags
是否包含0x20,所以破解方法就异或0x20就行了。其中LDR_DATA_TABLE_ENTRY在x86和x64下有差异:
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
| BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject) { #ifdef _WIN64 typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY listEntry; ULONG64 __Undefined1; ULONG64 __Undefined2; ULONG64 __Undefined3; ULONG64 NonPagedDebugInfo; ULONG64 DllBase; ULONG64 EntryPoint; ULONG SizeOfImage; UNICODE_STRING path; UNICODE_STRING name; ULONG Flags; USHORT LoadCount; USHORT __Undefined5; ULONG64 __Undefined6; ULONG CheckSum; ULONG __padding1; ULONG TimeDateStamp; ULONG __padding2; } KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY; #else typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY listEntry; ULONG unknown1; ULONG unknown2; ULONG unknown3; ULONG unknown4; ULONG unknown5; ULONG unknown6; ULONG unknown7; UNICODE_STRING path; UNICODE_STRING name; ULONG Flags; } KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY; #endif PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection; pLdrData->Flags = pLdrData->Flags | 0x20; return TRUE; };
|
例子
设置进程监控回调函数:
1 2 3 4 5 6 7
| NTSTATUS SetProcessNotifyRoutine(VOID) { NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyExRoutine, FALSE); if (!NT_SUCCESS(status)) ShowError("PsSetCreateProcessNotifyRoutineEx", status); return status; };
|
回调函数原型:
1 2 3 4 5
| VOID ProcessNotifyExRoutine ( _In_ HANDLE ParentId, _In_ HANDLE ProcessId, _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo )
|
进程创建回调函数的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| VOID ProcessNotifyExRoutine(PEPROCESS pEProcess, HANDLE hProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo) { if (NULL == CreateInfo) return; PCHAR pszImageFileName = PsGetProcessImageFileName(pEProcess); DbgPrint("[%s][%d][%wZ]\n", pszImageFileName, hProcessId, CreateInfo->ImageFileName); if (0 == _stricmp(pszImageFileName, "520.exe")) { CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL; DbgPrint("[禁止创建]\n"); }; return; };
|
源代码
Driver.h:
1 2 3 4 5 6 7 8 9 10 11 12
| #ifndef _DRIVER_H_ #define _DRIVER_H_
#include <ntddk.h>
VOID DriverUnload(PDRIVER_OBJECT pDriverObject); NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp);
#endif
|
NotifyRoutine.h:
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
| #ifndef _NOTIFY_ROUTINE_H_ #define _NOTIFY_ROUTINE_H_
#include <ntddk.h>
PCHAR PsGetProcessImageFileName(PEPROCESS pEProcess);
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject);
NTSTATUS SetProcessNotifyRoutine();
NTSTATUS RemoveProcessNotifyRoutine();
VOID ProcessNotifyExRoutine(PEPROCESS pEProcess, HANDLE hProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo);
#endif
|
Driver.c:
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
| #include "Driver.h" #include "NotifyRoutine.h"
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath) { DbgPrint("Enter DriverEntry\n");
NTSTATUS status = STATUS_SUCCESS; pDriverObject->DriverUnload = DriverUnload; for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { pDriverObject->MajorFunction[i] = DriverDefaultHandle; }
BypassCheckSign(pDriverObject);
SetProcessNotifyRoutine();
DbgPrint("Leave DriverEntry\n"); return status; }
VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { RemoveProcessNotifyRoutine(); }
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; }
|
NotifyRoutine.c:
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
| #include "NotifyRoutine.h"
VOID ShowError(PCHAR pszText, NTSTATUS ntStatus) { DbgPrint("%s Error[0x%X]\n", pszText, ntStatus); }
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject) { #ifdef _WIN64 typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY listEntry; ULONG64 __Undefined1; ULONG64 __Undefined2; ULONG64 __Undefined3; ULONG64 NonPagedDebugInfo; ULONG64 DllBase; ULONG64 EntryPoint; ULONG SizeOfImage; UNICODE_STRING path; UNICODE_STRING name; ULONG Flags; USHORT LoadCount; USHORT __Undefined5; ULONG64 __Undefined6; ULONG CheckSum; ULONG __padding1; ULONG TimeDateStamp; ULONG __padding2; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY; #else typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY listEntry; ULONG unknown1; ULONG unknown2; ULONG unknown3; ULONG unknown4; ULONG unknown5; ULONG unknown6; ULONG unknown7; UNICODE_STRING path; UNICODE_STRING name; ULONG Flags; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY; #endif
PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection; pLdrData->Flags = pLdrData->Flags | 0x20;
return TRUE; }
NTSTATUS SetProcessNotifyRoutine() { NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyExRoutine, FALSE); if (!NT_SUCCESS(status)) { ShowError("PsSetCreateProcessNotifyRoutineEx", status); } return status; }
NTSTATUS RemoveProcessNotifyRoutine() { NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyExRoutine, TRUE); if (!NT_SUCCESS(status)) { ShowError("PsSetCreateProcessNotifyRoutineEx", status); } return status; }
VOID ProcessNotifyExRoutine(PEPROCESS pEProcess, HANDLE hProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo) { if (NULL == CreateInfo) { return; } PCHAR pszImageFileName = PsGetProcessImageFileName(pEProcess); DbgPrint("[%s][%d][%wZ]\n", pszImageFileName, hProcessId, CreateInfo->ImageFileName); if (0 == _stricmp(pszImageFileName, "520.exe")) { CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;
DbgPrint("[禁止创建]\n"); } }
|
反进程创建监控
基本原理
系统设置的进程创建回调函数会存储在PspCreateProcessNotifyRoutine
的PVOID类型数组里,这里面存储着系统里所有用PsSetCreateProcessNotifyRoutine
函数设置的进程创建回调函数的加密地址。这个数组没有导出不能直接获取,需要逆向PspSetCreateProcessNotifyRoutine
函数:
1 2 3
| nt!PspSetCreateProcessNotifyRoutine+0x4C: 33 FF xor edi,edi 4C 8D 3D FB 0B DF FF lea r15, [nt!PspCreateProcessNotifyRoutine]
|
定位PspCreateProcessNotifyRoutine
数组地址的方法是特征码,每个系统都不一样:
|
Windows 7 |
Windows 8.1 |
Windows 10 |
x86 |
C7450C |
B8 |
BB |
x64 |
4C8D35 |
4C8D3D |
4C8D3D |
但PspSetCreateProcessNotifyRoutine
也不是导出函数,需要逆向PsSetCreateProcessNotifyRoutine
函数:
1 2 3
| nt!PsSetCreateProcessNotifyRoutine: 45 33 C0 xor r8d, r8d E9 E8 FD FF FF jmp nt!PspSetCreateprocessNotifyRoutine
|
定位PspSetCreateProcessNotifyRoutine
函数地址的方法也是特征码,每个系统也不一样:
|
Windows 7 |
Windows 8.1 |
Windows 10 |
x86 |
E8 |
E8 |
E8 |
x64 |
E9 |
E9 |
E9 |
于是获取PspCreateProcessNotifyRoutine
数组实现代码:
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
| PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize) { UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetCteateProcessNotifyRoutine = NULL; PVOID pPspSetCreateProcessNotifyRoutineAddress = NULL; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateProcessNotifyRoutine"); pPsSetCteateProcessNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetCteateProcessNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspCreateProcessNotifyRoutineAddress; }; pAddress = SearchMemory(pPsSetCteateProcessNotifyRoutine, (PVOID)((PUCHAR)pPsSetCteateProcessNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory1", 0); return pPspCreateProcessNotifyRoutineAddress; }; lOffset = *(PLONG)pAddress; pPspSetCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); pAddress = SearchMemory(pPspSetCreateProcessNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateProcessNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory2", 0); return pPspCreateProcessNotifyRoutineAddress; }; #ifdef _WIN64 lOffset = *(PLONG)pAddress; pPspCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); #else pPspCreateProcessNotifyRoutineAddress = *(PVOID*)pAddress; #endif return pPspCreateProcessNotifyRoutineAddress; };
|
数组PspCreateProcessNotifyRoutine
是加密的,还需要解密。x86和x64加密方法不同。x86下来说,该数组是个4字节无符号类型数组,数组大小最大为8,每个值与运算0xFFFFFFF8再加4即为解密;x64下来说,该数组是个8字节无符号类型数组,数组大小最大为64,每个值与运算0xFFFFFFFFFFFFFFF8即为解密:
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
| BOOLEAN EnumNotifyRoutine(VOID) { ULONG i = 0; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL; pPspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutine(); if (NULL == pPspCreateProcessNotifyRoutineAddress) { DbgPrint("GetPspCreateProcessNotifyRoutine Error!\n"); return FALSE; }; DbgPrint("pPspCreateProcessNotifyRoutineAddress=0x%p\n", pPspCreateProcessNotifyRoutineAddress); #ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID*)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); }; }; #else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); }; }; #endif return TRUE; };
|
为了删除进程创建回调函数,有3种方法:
- 函数
PsSetCreateProcessNotifyRoutine
也能删除回调函数,传入回调函数地址和删除标志TRUE即可。
- 修改
PspCreateProcessNotifyRoutine
数组中数据,指向自定义空回调函数地址。
- 修改回调函数内存数据前几字节,直接写入RET。
这里就用第一种最方便了:
1 2 3 4 5 6 7
| NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress) { NTSTATUS status = PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)pNotifyRoutineAddress, TRUE); if (!NT_SUCCESS(status)) ShowError("PsSetCreateProcessNotifyRoutine", status); return status; };
|
源代码
Driver.h:
1 2 3 4 5 6 7 8 9 10 11 12
| #ifndef _DRIVER_H_ #define _DRIVER_H_
#include <ntddk.h>
VOID DriverUnload(PDRIVER_OBJECT pDriverObject); NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp);
#endif
|
EnumRemove.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #ifndef _ENUM_REMOVE_H_ #define _ENUM_REMOVE_H_
#include <ntddk.h>
BOOLEAN EnumNotifyRoutine();
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress);
PVOID GetPspCreateProcessNotifyRoutine();
PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize);
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize);
#endif
|
Driver.c:
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
| #include "EnumRemove.h" #include "Driver.h"
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath) { DbgPrint("Enter DriverEntry\n");
NTSTATUS status = STATUS_SUCCESS; pDriverObject->DriverUnload = DriverUnload; for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { pDriverObject->MajorFunction[i] = DriverDefaultHandle; }
EnumNotifyRoutine();
DbgPrint("Leave DriverEntry\n"); return status; }
VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { }
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; }
|
EnumRemove.c:
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 247 248 249 250 251 252 253 254
| #include "EnumRemove.h"
VOID ShowError(PCHAR lpszText, NTSTATUS ntStatus) { DbgPrint("%s Error[0x%X]\n", lpszText, ntStatus); }
BOOLEAN EnumNotifyRoutine() { ULONG i = 0; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL;
pPspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutine(); if (NULL == pPspCreateProcessNotifyRoutineAddress) { DbgPrint("GetPspCreateProcessNotifyRoutine Error!\n"); return FALSE; } DbgPrint("pPspCreateProcessNotifyRoutineAddress=0x%p\n", pPspCreateProcessNotifyRoutineAddress);
#ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } } #else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } } #endif
return TRUE; }
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress) { NTSTATUS status = PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)pNotifyRoutineAddress, TRUE); if (!NT_SUCCESS(status)) { ShowError("PsSetCreateProcessNotifyRoutine", status); } return status; }
PVOID GetPspCreateProcessNotifyRoutine() { PVOID pPspCreateProcessNotifyRoutineAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pFirstSpecialData[50] = { 0 }; ULONG ulFirstSpecialDataSize = 0; UCHAR pSecondSpecialData[50] = { 0 }; ULONG ulSecondSpecialDataSize = 0;
RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { #ifdef _WIN64 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x35; ulSecondSpecialDataSize = 3; #else pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; pSecondSpecialData[0] = 0xC7; pSecondSpecialData[1] = 0x45; pSecondSpecialData[2] = 0x0C; ulSecondSpecialDataSize = 3; #endif } else if (2 == osInfo.dwMinorVersion) { #ifdef _WIN64 #else #endif } else if (3 == osInfo.dwMinorVersion) { #ifdef _WIN64 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x3D; ulSecondSpecialDataSize = 3; #else pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; pSecondSpecialData[0] = 0xB8; ulSecondSpecialDataSize = 1; #endif } } else if (10 == osInfo.dwMajorVersion) { #ifdef _WIN64 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x3D; ulSecondSpecialDataSize = 3; #else pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; pSecondSpecialData[0] = 0xBB; ulSecondSpecialDataSize = 1; #endif }
pPspCreateProcessNotifyRoutineAddress = SearchPspCreateProcessNotifyRoutine(pFirstSpecialData, ulFirstSpecialDataSize, pSecondSpecialData, ulSecondSpecialDataSize); return pPspCreateProcessNotifyRoutineAddress; }
PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize) { UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetCteateProcessNotifyRoutine = NULL; PVOID pPspSetCreateProcessNotifyRoutineAddress = NULL; PVOID pPspCreateProcessNotifyRoutineAddress = NULL;
RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateProcessNotifyRoutine"); pPsSetCteateProcessNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetCteateProcessNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspCreateProcessNotifyRoutineAddress; }
pAddress = SearchMemory(pPsSetCteateProcessNotifyRoutine, (PVOID)((PUCHAR)pPsSetCteateProcessNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory1", 0); return pPspCreateProcessNotifyRoutineAddress; } lOffset = *(PLONG)pAddress; pPspSetCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
pAddress = SearchMemory(pPspSetCreateProcessNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateProcessNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory2", 0); return pPspCreateProcessNotifyRoutineAddress; } #ifdef _WIN64 lOffset = *(PLONG)pAddress; pPspCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); #else pPspCreateProcessNotifyRoutineAddress = *(PVOID *)pAddress; #endif
return pPspCreateProcessNotifyRoutineAddress; }
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; }
|
反线程创建监控
基本原理
系统线程创建回调函数设置用PsSetCreateThreadNotifyRoutine
函数,这个上面没讲但跟进程操作差不多。
线程创建回调函数地址数组叫PspCreateThreadNotifyRoutine
,该数组地址通过PsSetCreateThreadNotifyRoutine
获取:
1 2 3 4 5 6 7 8 9 10 11
| ;Windows 8.1 x86下: nt!PsSetCreateThreadNotifyRoutine+0x16: 53 push ebx 57 push edi BB C8 64 01 81 mov ebx, offset nt!PspCreateThreadNofityRoutine 33 FF xor edir, edi
;Windows 8.1 x64下: nt!PsSetCreateThreadNotifyRoutine+0x1F: 48 8D 0D E6 FE DB FF lea rcx, [nt!PspCreateThreadNotifyRoutine] 45 33 C0 xor r8d, r8d
|
在Windows 7、Windows 8、Windows 8.1等中,函数PsSetCreateThreadNotifyRoutine
是导出的,x86下可直接定位该数组地址,x64下可直接获取该数组偏移并计算出数组地址。
但Windows 10 x64下函数PsSetCreateThreadNotifyRoutine
先调用了函数PspSetCreateThreadNotifyRoutine
:
1 2 3 4 5 6 7
| nt!PsSetCreateThreadNotifyRoutine: 33 D2 xor edx, edx E9 05 01 00 00 jmp nt!PspSetCreateThreadNotifyRoutine
nt!PspSetCreateThreadNotifyRoutine+0x2D: 48 8D 0D 74 0B DF FF lea rcx, [nt!PspCreateThreadNotifyRoutine] 45 33 C0 xor r8d, r8d
|
但是PspSetCreateThreadNotifyRoutine
函数没导出,所以这个函数得特征码扫描,然后数组地址获取方法同Windows 10前操作系统方法。
数组特征码:
|
Windows 7 |
Windows 8.1 |
Windows 10 |
x86 |
BE |
BB |
E8/BF |
x64 |
488D0D |
488D0D |
E9/488D0D |
根据特征码获取数组具体实现代码:
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
| PVOID SearchPspCreateThreadNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize) { UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetCreateThreadNotifyRoutine = NULL; PVOID pPspSetCreateThreadNotifyRoutineAddress = NULL; PVOID pPspCreateThreadNotifyRoutineAddress = NULL; RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateThreadNotifyRoutine"); pPsSetCreateThreadNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetCreateThreadNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspCreateThreadNotifyRoutineAddress; }; pAddress = SearchMemory(pPsSetCreateThreadNotifyRoutine, (PVOID)((PUCHAR)pPsSetCreateThreadNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory1", 0); return pPspCreateThreadNotifyRoutineAddress; }; if (0 == ulSecondSpecialDataSize) { #ifdef _WIN64 lOffset = *(PLONG)pAddress; pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); #else pPspCreateThreadNotifyRoutineAddress = *(PVOID*)pAddress; #endif return pPspCreateThreadNotifyRoutineAddress; }; lOffset = *(PLONG)pAddress; pPspSetCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); pAddress = SearchMemory(pPspSetCreateThreadNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateThreadNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory2", 0); return pPspCreateThreadNotifyRoutineAddress; }; #ifdef _WIN64 lOffset = *(PLONG)pAddress; pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); #else pPspCreateThreadNotifyRoutineAddress = *(PVOID*)pAddress; #endif return pPspCreateThreadNotifyRoutineAddress; };
|
别忘了数组还需要解密,解密方法同进程:
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
| BOOLEAN EnumNotifyRoutine(VOID) { ULONG i = 0; PVOID pPspCreateThreadNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL; pPspCreateThreadNotifyRoutineAddress = GetPspCreateThreadNotifyRoutine(); if (NULL == pPspCreateThreadNotifyRoutineAddress) { DbgPrint("GetPspCreateThreadNotifyRoutine Error!\n"); return FALSE; }; DbgPrint("pPspCreateThreadNotifyRoutineAddress=0x%p\n", pPspCreateThreadNotifyRoutineAddress); #ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID*)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); }; }; #else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); }; }; #endif return TRUE; };
|
删除线程创建回调函数的方法依然同进程:
1 2 3 4 5 6 7
| NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress) { NTSTATUS status = PsRemoveCreateThreadNotifyRoutine((PCREATE_THREAD_NOTIFY_ROUTINE)pNotifyRoutineAddress); if (!NT_SUCCESS(status)) ShowError("PsRemoveCreateThreadNotifyRoutine", status); return status; };
|
源代码
Driver.h:
1 2 3 4 5 6 7 8 9 10 11 12
| #ifndef _DRIVER_H_ #define _DRIVER_H_
#include <ntddk.h>
VOID DriverUnload(PDRIVER_OBJECT pDriverObject); NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp);
#endif
|
EnumRemove.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #ifndef _ENUM_REMOVE_H_ #define _ENUM_REMOVE_H_
#include <ntddk.h>
BOOLEAN EnumNotifyRoutine();
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress);
PVOID GetPspCreateThreadNotifyRoutine();
PVOID SearchPspCreateThreadNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize);
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize);
#endif
|
Driver.c:
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
| #include "EnumRemove.h" #include "Driver.h"
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath) { DbgPrint("Enter DriverEntry\n");
NTSTATUS status = STATUS_SUCCESS; pDriverObject->DriverUnload = DriverUnload; for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { pDriverObject->MajorFunction[i] = DriverDefaultHandle; }
EnumNotifyRoutine();
DbgPrint("Leave DriverEntry\n"); return status; }
VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { }
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; }
|
EnumRemove.c:
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 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
| #include "EnumRemove.h"
VOID ShowError(PCHAR lpszText, NTSTATUS ntStatus) { DbgPrint("%s Error[0x%X]\n", lpszText, ntStatus); }
BOOLEAN EnumNotifyRoutine() { ULONG i = 0; PVOID pPspCreateThreadNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL;
pPspCreateThreadNotifyRoutineAddress = GetPspCreateThreadNotifyRoutine(); if (NULL == pPspCreateThreadNotifyRoutineAddress) { DbgPrint("GetPspCreateThreadNotifyRoutine Error!\n"); return FALSE; } DbgPrint("pPspCreateThreadNotifyRoutineAddress=0x%p\n", pPspCreateThreadNotifyRoutineAddress);
#ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } } #else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } } #endif
return TRUE; }
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress) { NTSTATUS status = PsRemoveCreateThreadNotifyRoutine((PCREATE_THREAD_NOTIFY_ROUTINE)pNotifyRoutineAddress); if (!NT_SUCCESS(status)) { ShowError("PsRemoveCreateThreadNotifyRoutine", status); } return status; }
PVOID GetPspCreateThreadNotifyRoutine() { PVOID pPspCreateThreadNotifyRoutineAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pFirstSpecialData[50] = { 0 }; ULONG ulFirstSpecialDataSize = 0; UCHAR pSecondSpecialData[50] = { 0 }; ULONG ulSecondSpecialDataSize = 0;
RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { #ifdef _WIN64 pFirstSpecialData[0] = 0x48; pFirstSpecialData[1] = 0x8D; pFirstSpecialData[2] = 0x0D; ulFirstSpecialDataSize = 3; #else pFirstSpecialData[0] = 0xBE; ulFirstSpecialDataSize = 1; #endif } else if (2 == osInfo.dwMinorVersion) { #ifdef _WIN64 #else #endif } else if (3 == osInfo.dwMinorVersion) { #ifdef _WIN64 pFirstSpecialData[0] = 0x48; pFirstSpecialData[1] = 0x8D; pFirstSpecialData[2] = 0x0D; ulFirstSpecialDataSize = 3; #else pFirstSpecialData[0] = 0xBB; ulFirstSpecialDataSize = 1; #endif } } else if (10 == osInfo.dwMajorVersion) { #ifdef _WIN64 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; pSecondSpecialData[0] = 0x48; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x0D; ulSecondSpecialDataSize = 3; #else pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; pSecondSpecialData[0] = 0xBF; ulSecondSpecialDataSize = 1; #endif }
pPspCreateThreadNotifyRoutineAddress = SearchPspCreateThreadNotifyRoutine(pFirstSpecialData, ulFirstSpecialDataSize, pSecondSpecialData, ulSecondSpecialDataSize); return pPspCreateThreadNotifyRoutineAddress; }
PVOID SearchPspCreateThreadNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize) { UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetCreateThreadNotifyRoutine = NULL; PVOID pPspSetCreateThreadNotifyRoutineAddress = NULL; PVOID pPspCreateThreadNotifyRoutineAddress = NULL;
RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateThreadNotifyRoutine"); pPsSetCreateThreadNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetCreateThreadNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspCreateThreadNotifyRoutineAddress; }
pAddress = SearchMemory(pPsSetCreateThreadNotifyRoutine, (PVOID)((PUCHAR)pPsSetCreateThreadNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory1", 0); return pPspCreateThreadNotifyRoutineAddress; }
if (0 == ulSecondSpecialDataSize) { #ifdef _WIN64 lOffset = *(PLONG)pAddress; pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); #else pPspCreateThreadNotifyRoutineAddress = *(PVOID *)pAddress; #endif return pPspCreateThreadNotifyRoutineAddress; }
lOffset = *(PLONG)pAddress; pPspSetCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); pAddress = SearchMemory(pPspSetCreateThreadNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateThreadNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory2", 0); return pPspCreateThreadNotifyRoutineAddress; } #ifdef _WIN64 lOffset = *(PLONG)pAddress; pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); #else pPspCreateThreadNotifyRoutineAddress = *(PVOID *)pAddress; #endif
return pPspCreateThreadNotifyRoutineAddress; }
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; }
|