浅谈DPCHookSSDT和RemoveDPC
最近学了DPC这一对,把Win7 32位和64位都做了,查阅了大量的资料,并且进行了大量调试,理一下思路,为了后面更好的学习。
转载请注明出处:http://www.cnblogs.com/littlepear/p/6733145.html
1. 当我们需要定时的完成某项任务时,就需要注册一个DPC定时器了,在Ring3中, CTimer类中有一个SetTimer()函数,对应的驱动中的函数就是KeSetTimer()了。Timer和要执行回调函数结构的DPC都必须初始化, DPC需提供一个回调函数。然后会把KTimer插入到一个timer table的链表中,具体什么样的链表,我们移除的时候再看。
typedef struct MY_TIMER_ { KDPC Dpc; KTIMER Timer; PKDEFERRED_ROUTINE func; PVOID private_context; }MY_TIMER, *PMY_TIMER; VOID MyTimerInit(PMY_TIMER Timer, PKDEFERRED_ROUTINE Func) { KeInitializeTimer(&(Timer->Timer)); //定时器的Timer和要执行的回调函数结构Dpc都必须先初始化 KeInitializeDpc(&(Timer->Dpc), Func, Timer);//第三个参数把Timer作为 DeferredContext传给回调函数,更好的封装 Timer->func = Func; } BOOLEAN MyTimerSet(PMY_TIMER Timer, LONG Ms, PVOID context) { LARGE_INTEGER Interval; Interval = RtlConvertLongToLargeInteger(-10000 * Ms); //100ns每unit 1s 负数代表相对时间 Timer->private_context = context; //传一块上下背景文,便于利用 return KeSetTimer(&(Timer->Timer), Interval, &(Timer->Dpc)); // 定时器 延后执行的时间 要执行的回调函数结构 //return KeSetTimerEx(&(Timer->Timer), Interval,1000, &(Timer->Dpc)); }
为了更好的理解KeSetTimer函数,我查了下wrk,下面是wrk的源码。
BOOLEAN KeSetTimer ( __inout PKTIMER Timer, __in LARGE_INTEGER DueTime, __in_opt PKDPC Dpc ) /*++ Routine Description: This function sets a timer to expire at a specified time. If the timer is already set, then it is implicitly canceled before it is set to expire at the specified time. Setting a timer causes its due time to be computed, its state to be set to Not-Signaled, and the timer object itself to be inserted in the timer list. Arguments: Timer - Supplies a pointer to a dispatcher object of type timer. DueTime - Supplies an absolute or relative time at which the timer is to expire. Dpc - Supplies an optional pointer to a control object of type DPC. Return Value: A boolean value of TRUE is returned if the the specified timer was currently set. Else a value of FALSE is returned. --*/ { // // Set the timer with a period of zero. // return KeSetTimerEx(Timer, DueTime, 0, Dpc); } BOOLEAN KeSetTimerEx ( __inout PKTIMER Timer, __in LARGE_INTEGER DueTime, __in LONG Period, __in_opt PKDPC Dpc ) /*++ Routine Description: This function sets a timer to expire at a specified time. If the timer is already set, then it is implicitly canceled before it is set to expire at the specified time. Setting a timer causes its due time to be computed, its state to be set to Not-Signaled, and the timer object itself to be inserted in the timer list. Arguments: Timer - Supplies a pointer to a dispatcher object of type timer. DueTime - Supplies an absolute or relative time at which the timer is to expire. Period - Supplies an optional period for the timer in milliseconds. Dpc - Supplies an optional pointer to a control object of type DPC. Return Value: A boolean value of TRUE is returned if the the specified timer was currently set. Otherwise, a value of FALSE is returned. --*/ { ULONG Hand; BOOLEAN Inserted; KIRQL OldIrql; BOOLEAN RequestInterrupt; ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); // // Raise IRQL to dispatcher level and lock dispatcher database. // // Capture the timer inserted status and if the timer is currently // set, then remove it from the timer list. // KiLockDispatcherDatabase(&OldIrql); Inserted = Timer->Header.Inserted; if (Inserted != FALSE) { KiRemoveTreeTimer(Timer); } // // Set the DPC address, set the period, and compute the timer due time. // If the timer has already expired, then signal the timer. Otherwise, // set the signal state to false and attempt to insert the timer in the // timer table. // // N.B. The signal state must be cleared before it is inserted in the // timer table in case the period is not zero. // Timer->Dpc = Dpc; Timer->Period = Period; if (KiComputeDueTime(Timer, DueTime, &Hand) == FALSE) { RequestInterrupt = KiSignalTimer(Timer); KiUnlockDispatcherDatabaseFromSynchLevel(); if (RequestInterrupt == TRUE) { KiRequestSoftwareInterrupt(DISPATCH_LEVEL); } } else { Timer->Header.SignalState = FALSE; KiInsertOrSignalTimer(Timer, Hand); } KiExitDispatcher(OldIrql); // // Return boolean value that signifies whether the timer was set or // not. // return Inserted; }
下面是我完整的DPCHookSSDT代码。
1 #include <ntifs.h> 2 3 #define SEC_IMAGE 0x001000000 4 5 6 extern 7 PIMAGE_NT_HEADERS 8 NTAPI 9 RtlImageNtHeader(PVOID BaseAddress); 10 11 extern 12 char* PsGetProcessImageFileName(PEPROCESS EProcess); 13 14 typedef 15 NTSTATUS(*pfnNtOpenProcess)( 16 _Out_ PHANDLE ProcessHandle, 17 _In_ ACCESS_MASK DesiredAccess, 18 _In_ POBJECT_ATTRIBUTES ObjectAttributes, 19 _In_opt_ PCLIENT_ID ClientId 20 ); 21 22 typedef struct MY_TIMER_ 23 { 24 KDPC Dpc; 25 KTIMER Timer; 26 PKDEFERRED_ROUTINE func; 27 PVOID private_context; 28 }MY_TIMER, *PMY_TIMER; 29 30 typedef struct _SYSTEM_SERVICE_DESCRIPTOR_TABLE_ 31 { 32 PVOID ServiceTableBase; //SSDT(System Service Dispatch Table)服务表的基地址 33 PVOID ServiceCounterTableBase; //用于checked builds,包含SSDT中每个服务被调用的次数 34 PVOID NumberOfServices; //服务函数的个数,NumberOfService * 4就是整个地址表的大小 64位 0x191, 32位 0x11c 35 PVOID ParamTableBase; //SSPT(System Service Parameter Table)的基地址 36 }SYSTEM_SERVICE_DESCRIPTOR_TABLE, *PSYSTEM_SERVICE_DESCRIPTOR_TABLE; 37 38 BOOLEAN GetSSDTAddressInWin7_x64(ULONG64* SSDTAddress); 39 BOOLEAN GetSSDTAddressInWin7_x86(ULONG32* SSDTAddress); 40 VOID DriverUnload(PDRIVER_OBJECT DriverObject); 41 BOOLEAN GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(CHAR* szFuntionName, ULONG32* SSDTFunctionIndex); 42 PVOID 43 GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName); 44 BOOLEAN Ring0MappingPEFile(WCHAR* szFileFullPath, PVOID* MappingBaseAddress, ULONG_PTR* MappingViewSize); 45 ULONG32 CalcFunctionOffsetInSSDT(PVOID ServiceTableBase, PVOID FunctionAddress, ULONG32 ParamterCount); 46 VOID HookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, PVOID MyFunctionAddress, 47 PVOID OldFuntionAddress, ULONG32 OldFunctionParamterCount, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength); 48 VOID HookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 MyFunctionAddress); 49 VOID InlineHook(ULONG64 OldFuntionAddress, ULONG64 MyFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength); 50 NTSTATUS MyOpenProcess( 51 _Out_ PHANDLE ProcessHandle, 52 _In_ ACCESS_MASK DesiredAccess, 53 _In_ POBJECT_ATTRIBUTES ObjectAttributes, 54 _In_opt_ PCLIENT_ID ClientId 55 ); 56 VOID WPOFF(); 57 VOID WPON(); 58 59 VOID UnHookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionOffset, PVOID OldFunctionAddress, 60 UCHAR* OldFunctionCode, ULONG32 PatchCodeLength); 61 VOID UnHookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionAddress); 62 VOID UnlineHook(PVOID OldFunctionAddress, CHAR* OldFunctionCode, ULONG32 PatchCodeLength); 63 64 VOID MyTimerInit(PMY_TIMER Timer, PKDEFERRED_ROUTINE Func); 65 66 BOOLEAN MyTimerSet(PMY_TIMER Timer, LONG Ms, PVOID context); 67 68 VOID CustomDpc( 69 _In_ struct _KDPC *Dpc, 70 _In_opt_ PVOID DeferredContext, 71 _In_opt_ PVOID SystemArgument1, 72 _In_opt_ PVOID SystemArgument2 73 );
1 #include "NewDPCHookSSDT.h" 2 #include <ntimage.h> 3 #include <Ntstrsafe.h> 4 5 //bp NewDPCHookSSDT!DriverEntry 6 ULONG32 __NtOpenProcessIndex = 0; 7 PVOID __ServiceTableBase = NULL; 8 ULONG32 __OldNtOpenProcessOffset = 0; 9 pfnNtOpenProcess __OldNtOpenProcessAddress = NULL; //这里无所谓,PVOID也可以 10 BOOLEAN __IsHook = FALSE; 11 UCHAR __OldCode[15] = { 0 }; 12 13 MY_TIMER MyTimer; 14 15 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) 16 { 17 NTSTATUS Status = STATUS_UNSUCCESSFUL; 18 char szFunctionName[] = "ZwOpenProcess"; 19 ULONG_PTR SSDTAddress = 0; //保存整个SSDT基地址 KeServiceDescriptorTable 20 ULONG32 TempOffset = 0; 21 DriverObject->DriverUnload = DriverUnload; 22 23 #ifdef _WIN64 24 if (GetSSDTAddressInWin7_x64(&SSDTAddress) == FALSE) 25 { 26 return Status; 27 } 28 #else 29 if (GetSSDTAddressInWin7_x86(&SSDTAddress) == FALSE) 30 { 31 return Status; 32 } 33 #endif 34 35 DbgPrint("SSDT:%p\r\n", SSDTAddress); //0x23h 36 if (GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(szFunctionName, &__NtOpenProcessIndex) == FALSE) 37 { 38 return Status; 39 } 40 41 __ServiceTableBase = ((PSYSTEM_SERVICE_DESCRIPTOR_TABLE)SSDTAddress)->ServiceTableBase; 42 #ifdef _WIN64 43 __OldNtOpenProcessOffset = ((PULONG32)__ServiceTableBase)[__NtOpenProcessIndex]; 44 TempOffset = __OldNtOpenProcessOffset >> 4; //抹掉最右边的一位 45 __OldNtOpenProcessAddress = (PVOID)((ULONG64)__ServiceTableBase + TempOffset); 46 HookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, MyOpenProcess, KeBugCheckEx, 5, __OldCode, 15); 47 #else 48 __OldNtOpenProcessAddress = (pfnNtOpenProcess)(((PULONG32)__ServiceTableBase)[__NtOpenProcessIndex]); 49 /*kd > u 0x84016ba1 50 nt!NtOpenProcess: 51 84016ba1 8bff mov edi, edi 52 84016ba3 55 push ebp 53 84016ba4 8bec mov ebp, esp 54 84016ba6 51 push ecx 55 84016ba7 51 push ecx 56 84016ba8 64a124010000 mov eax, dword ptr fs : [00000124h] 57 84016bae 8a803a010000 mov al, byte ptr[eax + 13Ah] 58 84016bb4 8b4d14 mov ecx, dword ptr[ebp + 14h]*/ 59 60 //0x00145dc8 61 62 HookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, (ULONG32)MyOpenProcess);//强制转换后,MyOpenProcess才可以传,真奇怪 63 #endif 64 MyTimerInit(&MyTimer, (PKDEFERRED_ROUTINE)CustomDpc); 65 MyTimerSet(&MyTimer, 1000, NULL); 66 return STATUS_SUCCESS; 67 } 68 69 VOID MyTimerInit(PMY_TIMER Timer, PKDEFERRED_ROUTINE Func) 70 { 71 KeInitializeTimer(&(Timer->Timer)); //定时器的Timer和要执行的回调函数结构Dpc都必须先初始化 72 KeInitializeDpc(&(Timer->Dpc), Func, Timer);//第三个参数把Timer作为 DeferredContext传给回调函数,更好的封装 73 Timer->func = Func; 74 } 75 76 BOOLEAN MyTimerSet(PMY_TIMER Timer, LONG Ms, PVOID context) 77 { 78 LARGE_INTEGER Interval; 79 Interval = RtlConvertLongToLargeInteger(-10000 * Ms); //100ns每unit 1s 负数代表相对时间 80 Timer->private_context = context; //传一块上下背景文,便于利用 81 return KeSetTimer(&(Timer->Timer), Interval, &(Timer->Dpc)); 82 // 定时器 延后执行的时间 要执行的回调函数结构 83 //return KeSetTimerEx(&(Timer->Timer), Interval,1000, &(Timer->Dpc)); 84 } 85 86 VOID CustomDpc( 87 _In_ struct _KDPC *Dpc, 88 _In_opt_ PVOID DeferredContext, 89 _In_opt_ PVOID SystemArgument1, 90 _In_opt_ PVOID SystemArgument2 91 ) 92 { 93 PMY_TIMER Timer = (PMY_TIMER)DeferredContext; 94 PVOID MyContext = Timer->private_context; 95 DbgPrint("CustomDpc()\r\n"); 96 #ifdef _WIN64 97 HookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, MyOpenProcess, KeBugCheckEx, 5, __OldCode, 15); 98 #else 99 HookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, (ULONG32)MyOpenProcess);//强制转换后,MyOpenProcess才可以传,真奇怪 100 #endif // _WIN64 101 MyTimerSet(Timer, 1000, MyContext); 102 //如果前面是KeSetTimerEx函数,这里就不用写MyTimerSet啦 103 } 104 105 VOID DriverUnload(PDRIVER_OBJECT DriverObject) 106 { 107 DbgPrint("ByeBye,Driver!\r\n"); 108 109 KeCancelTimer(&(MyTimer.Timer)); 110 111 if (__IsHook) 112 { 113 #ifdef _WIN64 114 115 UnHookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, __OldNtOpenProcessOffset, KeBugCheckEx, 116 __OldCode, 15); 117 #else 118 UnHookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, __OldNtOpenProcessAddress); 119 #endif 120 __IsHook = FALSE; 121 } 122 } 123 124 BOOLEAN GetSSDTAddressInWin7_x64(ULONG64* SSDTAddress) 125 { 126 //rdmsr c0000082 127 CHAR* StartSearchAddress = (char*)__readmsr(0xc0000082); 128 CHAR* EndSearchAddress = StartSearchAddress + PAGE_SIZE; //4KB 129 CHAR* i = NULL; 130 UCHAR v1 = 0, v2 = 0, v3 = 0; 131 //必须用UCHAR类型,因为Char的范围是 -128~127 0x80-0x7F 132 INT64 Offset = 0; 133 *SSDTAddress = NULL; 134 for (i = StartSearchAddress; i < EndSearchAddress; i++) 135 { 136 if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2)) 137 { 138 v1 = *i; 139 v2 = *(i + 1); 140 v3 = *(i + 2); 141 if (v1 == 0x4c && v2 == 0x8d && v3 == 0x15) 142 { 143 // 4c8d15238f4700 144 memcpy(&Offset, i+3, 4); 145 *SSDTAddress = Offset + (ULONG64)i+7; 146 break; 147 } 148 } 149 } 150 if (*SSDTAddress == NULL) 151 { 152 return FALSE; 153 } 154 return TRUE; 155 } 156 157 BOOLEAN GetSSDTAddressInWin7_x86(ULONG32* SSDTAddress) 158 { 159 //32位下Ntoskrnl.exe有导出 KeServiceDescriptorTable,直接查找即可 160 *SSDTAddress = NULL; 161 *SSDTAddress = (ULONG32)GetExportVariableAddressFromNtosKrnlExportTableByVariableName(L"KeServiceDescriptorTable"); 162 if (*SSDTAddress != NULL) 163 { 164 return TRUE; 165 } 166 return FALSE; 167 } 168 PVOID 169 GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName) 170 { 171 //通过导出变量名字从NtosKrnl中获得导出变量地址 172 UNICODE_STRING uniVariableName; 173 PVOID VariableAddress = NULL; 174 175 if (wzVariableName&&wcslen(wzVariableName) > 0) 176 { 177 RtlUnicodeStringInit(&uniVariableName, wzVariableName); 178 //从Ntos模块的导出表中获得导出变量的地址 179 VariableAddress = MmGetSystemRoutineAddress(&uniVariableName); 180 } 181 return VariableAddress; 182 } 183 //从内存中读出 184 BOOLEAN GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(CHAR* szFuntionName, ULONG32* SSDTFunctionIndex) 185 { 186 WCHAR szFileFullPath[] = L"\\SystemRoot\\System32\\ntdll.dll"; //C:\Windows\ 187 ULONG_PTR MappingViewSize = 0; 188 PVOID MappingBaseAddress = 0; 189 BOOLEAN IsOk = FALSE; 190 PIMAGE_NT_HEADERS Image_Nt_Headers = NULL; 191 PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL; //导出表 192 //因为不识别DWORD,所以用UINT32 193 UINT32* AddressOfFunctions = NULL; 194 UINT32* AddressOfNames = NULL; 195 UINT16* AddressOfNameOrdinals = NULL; 196 int i = 0; 197 char* v1 = NULL; 198 ULONG32 OrdinalOfFunction = 0; 199 PVOID AddressOfFunction = 0; 200 #ifdef _WIN64 201 /* 202 0:004> u zwopenprocess 203 ntdll!NtOpenProcess: 204 00000000`774ddc10 4c8bd1 mov r10,rcx 205 00000000`774ddc13 b823000000 mov eax,23h 206 00000000`774ddc18 0f05 syscall 207 00000000`774ddc1a c3 ret 208 00000000`774ddc1b 0f1f440000 nop dword ptr [rax+rax] 209 */ 210 ULONG32 Offset_SSDTFunctionIndex = 4; 211 #else 212 /* 213 0:004> u zwopenprocess 214 ntdll!NtOpenProcess: 215 00000000`774ddc10 4c8bd1 mov r10,rcx 216 00000000`774ddc13 b823000000 mov eax,23h 217 00000000`774ddc18 0f05 syscall 218 00000000`774ddc1a c3 ret 219 00000000`774ddc1b 0f1f440000 nop dword ptr [rax+rax] 220 */ 221 ULONG32 Offset_SSDTFunctionIndex = 1; 222 #endif 223 224 225 *SSDTFunctionIndex = -1; 226 IsOk = Ring0MappingPEFile(szFileFullPath, &MappingBaseAddress, &MappingViewSize); 227 if (IsOk == FALSE) 228 { 229 return FALSE; 230 } 231 else 232 { 233 __try { 234 Image_Nt_Headers = RtlImageNtHeader(MappingBaseAddress); //extern进来 235 if (Image_Nt_Headers&&Image_Nt_Headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) 236 { 237 ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((char*)MappingBaseAddress 238 + Image_Nt_Headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); 239 240 AddressOfFunctions = (UINT32*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfFunctions); 241 //函数的地址(RVA) 242 AddressOfNames = (UINT32*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfNames);; 243 //函数名字的地址(RVA) 244 AddressOfNameOrdinals = (UINT16*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfNameOrdinals); 245 //有名字的序号的首地址(RVA) 246 for (i = 0; i < ImageExportDirectory->NumberOfNames; i++) 247 { 248 v1 = (char*)((char*)MappingBaseAddress + AddressOfNames[i]); //可以这样写 ImageExportDirectory->AddressOfNames + i * 4 249 if (_stricmp(szFuntionName, v1) == 0) 250 { 251 OrdinalOfFunction = AddressOfNameOrdinals[i]; 252 AddressOfFunction = (PVOID)((char*)MappingBaseAddress + AddressOfFunctions[OrdinalOfFunction]);//AddressOfFunctions[AddressOfNameOrdinals[i]] 253 *SSDTFunctionIndex = *(ULONG32*)((char*)AddressOfFunction + Offset_SSDTFunctionIndex); 254 break; 255 } 256 } 257 } 258 } 259 __except(EXCEPTION_EXECUTE_HANDLER) 260 {} 261 262 } 263 ZwUnmapViewOfSection(NtCurrentProcess(), MappingBaseAddress); 264 if (*SSDTFunctionIndex == -1) 265 { 266 return FALSE; 267 } 268 return TRUE; 269 } 270 //函数需要,所以传双字 二维指针 271 BOOLEAN Ring0MappingPEFile(WCHAR* szFileFullPath, PVOID* MappingBaseAddress, ULONG_PTR* MappingViewSize) 272 { 273 NTSTATUS Status; 274 UNICODE_STRING Temp; 275 OBJECT_ATTRIBUTES oa; 276 HANDLE hFile = NULL; 277 HANDLE hSection = NULL; 278 IO_STATUS_BLOCK IoStatusBlock; 279 280 if (!szFileFullPath&&MmIsAddressValid(szFileFullPath)) 281 { 282 return FALSE; 283 } 284 if (!MappingBaseAddress&&MmIsAddressValid(MappingBaseAddress)) 285 { 286 return FALSE; 287 } 288 RtlUnicodeStringInit(&Temp, szFileFullPath); 289 InitializeObjectAttributes(&oa, //out 290 &Temp, //in ObjectName 291 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,//被系统在和ObjectName比较匹配时不区分大小写||此句柄只能用于内核模式下 292 NULL, //与ObjectName参数匹配的根目录对象,如果ObjectName是对象的全路径则设置此参数为NULL 293 NULL //驱动程序可以使用NULL用于创建一个具有默认安全描述符的对象 294 ); 295 //打开文件,获得文件句柄 296 Status = IoCreateFile(&hFile, 297 GENERIC_READ | SYNCHRONIZE, //可读|同步 298 &oa, 299 &IoStatusBlock, //返回I/O请求的完成情况 300 NULL, 301 FILE_ATTRIBUTE_NORMAL, 302 FILE_SHARE_READ, 303 FILE_OPEN, 304 FILE_SYNCHRONOUS_IO_NONALERT, 305 NULL, 306 0, 307 CreateFileTypeNone, 308 NULL, 309 IO_NO_PARAMETER_CHECKING 310 ); 311 if (!NT_SUCCESS(Status)) 312 { 313 return FALSE; 314 } 315 oa.ObjectName = NULL; 316 Status = ZwCreateSection(&hSection, 317 SECTION_QUERY | SECTION_MAP_READ, 318 &oa, 319 NULL, 320 PAGE_WRITECOPY, 321 SEC_IMAGE,//内存按1M对齐 0x1000 000 322 hFile 323 ); 324 ZwClose(hFile); 325 if (!NT_SUCCESS(Status)) 326 { 327 return FALSE; 328 } 329 Status = ZwMapViewOfSection(hSection, 330 NtCurrentProcess(), 331 MappingBaseAddress, 332 0, 333 0, 334 0, 335 MappingViewSize, 336 ViewUnmap, //不能被映射到子进程中 337 0, 338 PAGE_WRITECOPY 339 ); 340 ZwClose(hSection); 341 if (!NT_SUCCESS(Status)) 342 { 343 return FALSE; 344 } 345 return TRUE; 346 } 347 348 ULONG32 CalcFunctionOffsetInSSDT(PVOID ServiceTableBase, PVOID FunctionAddress, ULONG32 ParamterCount) 349 { 350 //完全没必要像下面这样折腾,就是一个做个SSDT表中类似的地址,有时间自己写一个 351 ULONG32 Offset = 0; 352 CHAR Temp = 0; 353 CHAR Bits[4] = { 0 }; 354 int i = 0; 355 Offset = (ULONG32)((ULONG64)FunctionAddress - (ULONG64)ServiceTableBase); 356 Offset = Offset << 4; 357 if (ParamterCount > 4) 358 { 359 ParamterCount = ParamterCount-4; //NtReadFile 9个参数 和参数压栈有关 360 } 361 else 362 { 363 ParamterCount = 0; 364 } 365 memcpy(&Temp, &Offset, 1); // 1010 0010 <<4-----> 0010 0000 后面处理参数 366 #define SETBIT(x,y) x|=(1<<y) //将X的第Y位置1 367 #define CLRBIT(x,y) x&=~(1<<y) //将X的第Y位清0 368 #define GETBIT(x,y) (x & (1 << y)) //取X的第Y位,返回0或非0 369 370 for (i = 0; i < 4; i++) 371 { 372 Bits[i] = GETBIT(ParamterCount, i); 373 if (Bits[i]) 374 { 375 SETBIT(Temp, i); 376 } 377 else 378 { 379 CLRBIT(Temp, i); 380 } 381 } 382 memcpy(&Offset, &Temp, 1); 383 return Offset; 384 } 385 386 //write protect 第17位 387 VOID WPOFF() 388 { 389 _disable(); 390 __writecr0(__readcr0() & (~(0x10000))); 391 392 } 393 394 VOID WPON() 395 { 396 __writecr0(__readcr0() ^ 0x10000); 397 _enable(); 398 } 399 400 VOID HookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, PVOID MyFunctionAddress, 401 PVOID OldFuntionAddress, ULONG32 OldFunctionParamterCount, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength) 402 { 403 ULONG32 MyOffset = 0; 404 WPOFF(); 405 InlineHook(OldFuntionAddress, MyFunctionAddress, OldFunctionCode, PatchCodeLength); 406 WPON(); 407 408 MyOffset = CalcFunctionOffsetInSSDT(ServiceTableBase, OldFuntionAddress, OldFunctionParamterCount); 409 WPOFF(); 410 ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = MyOffset; 411 WPON(); 412 413 __IsHook = TRUE; 414 } 415 416 VOID HookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 MyFunctionAddress) 417 { 418 WPOFF(); 419 ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)MyFunctionAddress; 420 WPON(); 421 422 __IsHook = TRUE; 423 } 424 425 VOID InlineHook(ULONG64 OldFuntionAddress, ULONG64 MyFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength) 426 { 427 CHAR PatchCode[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; //神坑啊,脑残啊,写成PatchCode 到memcpy才崩溃 428 ULONG64 Temp = MyFunctionAddress; 429 memcpy(OldFunctionCode, (PVOID)OldFuntionAddress, PatchCodeLength); //保存原来的函数指令 430 memcpy(PatchCode + 6, &Temp, 8); 431 memset((PVOID)OldFuntionAddress, 0x90, PatchCodeLength); //先全部打上NOP(计组) 432 memcpy((PVOID)OldFuntionAddress, PatchCode, 14); 433 } 434 435 436 //SSDT HOOK的替换函数如何对访问进行过滤”比“如何实现SSDT HOOK”要复杂多了,详见ediary HookNtOpenProcess后的事情 437 NTSTATUS MyOpenProcess( 438 _Out_ PHANDLE ProcessHandle, 439 _In_ ACCESS_MASK DesiredAccess, 440 _In_ POBJECT_ATTRIBUTES ObjectAttributes, 441 _In_opt_ PCLIENT_ID ClientId 442 ) 443 { 444 //EnumProcessByForce.exe 445 //调用OpenProcess---->去NtDll导出表中寻找ntdll!NtOpenProcess或者ntdll!ZwOpenProcess 导出表有名称的索引---->切入内核-->跳板nt!ZwOpenProcess Ntoskernl.exe(NtOpenProcess mov eax,23h SSDT[23h]) 446 // ---->KeCheckBugEx ---->Jmp MyOpenProcess 447 PEPROCESS EProcess = PsGetCurrentProcess(); //EnumProcessByForce.exe 448 if (EProcess != NULL && MmIsAddressValid(EProcess)) 449 { 450 char *szProcessImageFileName = PsGetProcessImageFileName(EProcess); 451 if (strstr(szProcessImageFileName, "EnumProcess") != 0) 452 { 453 return STATUS_ACCESS_DENIED; //黑名单中返回 454 } 455 } 456 __OldNtOpenProcessAddress(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); //白名单 457 //OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, i) 458 //return STATUS_SUCCESS; 死循环 459 //return STATUS_UNSUCCESSFUL; 阻塞 很有趣 460 } 461 462 VOID UnHookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionOffset, PVOID OldFunctionAddress, 463 UCHAR* OldFunctionCode, ULONG32 PatchCodeLength) 464 { 465 WPOFF(); 466 UnlineHook(OldFunctionAddress, OldFunctionCode, PatchCodeLength); 467 WPON(); 468 469 WPOFF(); 470 ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)OldFunctionOffset; 471 WPON(); 472 } 473 474 VOID UnlineHook(PVOID OldFunctionAddress, CHAR* OldFunctionCode, ULONG32 PatchCodeLength) 475 { 476 memcpy((PVOID)OldFunctionAddress, OldFunctionCode, PatchCodeLength); 477 } 478 479 VOID UnHookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionAddress) 480 { 481 WPOFF(); 482 ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)OldFunctionAddress; 483 WPON(); 484 }
2. 接下来就是RemoveDPC了,首先我们要找到DPC,关于Win7以前的版本可以参考这份博客,讲解的很详细,http://bbs.pediy.com/thread-148135.htm
win7下定时器链表被放在KPRCB结构中,64位下,在KPRCB+0x2200的位置:
相关结构:
所以大体流程就是:取KPRCB地址,+0x2200偏移得到_KTIMER_TABLE,+0x200偏移得到_KTIMER_TABLE.TimerEntries
最后遍历_KTIMER_TABLE.TimerEntries.Entry 。32位下类似,只是偏移不同。
那么现在关键是获得KPRCB地址,那怎么获得呢?
每一个CPU中都有一个KPRCB结构地址,它保存在KiProcessorBlock全局变量中。
而KiProcessorBlock在KdDebuggerData64这个结构中,KdDebuggerData64可以从KdVersionBlock中得到。方法如下:
KdVersionBlock在KPCR中位置如图所示,这里需要注意,对于多处理器的环境下,如果想正确获取KdVersionBlock的值,必须保证当前的线程运行在1号处理器上。
具体原因可查看这份博客:http://www.youranshare.com/push/code/win-c-cpp/439.html
然后查看KdVersionBlock结构,最下面的便是KdDebuggerData64结构。
从wrk上查看KdDebuggerData64结构体的定义,发现KiProcessorBlock在0x218h的偏移处。
1 DO NOT ADD OR REMOVE FIELDS FROM THE MIDDLE OF THIS STRUCTURE!!! 2 // 3 // If you remove a field, replace it with an "unused" placeholder. 4 // Do not reuse fields until there has been enough time for old debuggers 5 // and extensions to age out. 6 // 7 typedef struct _KDDEBUGGER_DATA64 { 8 9 DBGKD_DEBUG_DATA_HEADER64 Header; 10 11 // 12 // Base address of kernel image 13 // 14 15 ULONG64 KernBase; 16 17 // 18 // DbgBreakPointWithStatus is a function which takes an argument 19 // and hits a breakpoint. This field contains the address of the 20 // breakpoint instruction. When the debugger sees a breakpoint 21 // at this address, it may retrieve the argument from the first 22 // argument register, or on x86 the eax register. 23 // 24 25 ULONG64 BreakpointWithStatus; // address of breakpoint 26 27 // 28 // Address of the saved context record during a bugcheck 29 // 30 // N.B. This is an automatic in KeBugcheckEx's frame, and 31 // is only valid after a bugcheck. 32 // 33 34 ULONG64 SavedContext; 35 36 // 37 // help for walking stacks with user callbacks: 38 // 39 40 // 41 // The address of the thread structure is provided in the 42 // WAIT_STATE_CHANGE packet. This is the offset from the base of 43 // the thread structure to the pointer to the kernel stack frame 44 // for the currently active usermode callback. 45 // 46 47 USHORT ThCallbackStack; // offset in thread data 48 49 // 50 // these values are offsets into that frame: 51 // 52 53 USHORT NextCallback; // saved pointer to next callback frame 54 USHORT FramePointer; // saved frame pointer 55 56 // 57 // pad to a quad boundary 58 // 59 USHORT PaeEnabled:1; 60 61 // 62 // Address of the kernel callout routine. 63 // 64 65 ULONG64 KiCallUserMode; // kernel routine 66 67 // 68 // Address of the usermode entry point for callbacks. 69 // 70 71 ULONG64 KeUserCallbackDispatcher; // address in ntdll 72 73 74 // 75 // Addresses of various kernel data structures and lists 76 // that are of interest to the kernel debugger. 77 // 78 79 ULONG64 PsLoadedModuleList; 80 ULONG64 PsActiveProcessHead; 81 ULONG64 PspCidTable; 82 83 ULONG64 ExpSystemResourcesList; 84 ULONG64 ExpPagedPoolDescriptor; 85 ULONG64 ExpNumberOfPagedPools; 86 87 ULONG64 KeTimeIncrement; 88 ULONG64 KeBugCheckCallbackListHead; 89 ULONG64 KiBugcheckData; 90 91 ULONG64 IopErrorLogListHead; 92 93 ULONG64 ObpRootDirectoryObject; 94 ULONG64 ObpTypeObjectType; 95 96 ULONG64 MmSystemCacheStart; 97 ULONG64 MmSystemCacheEnd; 98 ULONG64 MmSystemCacheWs; 99 100 ULONG64 MmPfnDatabase; 101 ULONG64 MmSystemPtesStart; 102 ULONG64 MmSystemPtesEnd; 103 ULONG64 MmSubsectionBase; 104 ULONG64 MmNumberOfPagingFiles; 105 106 ULONG64 MmLowestPhysicalPage; 107 ULONG64 MmHighestPhysicalPage; 108 ULONG64 MmNumberOfPhysicalPages; 109 110 ULONG64 MmMaximumNonPagedPoolInBytes; 111 ULONG64 MmNonPagedSystemStart; 112 ULONG64 MmNonPagedPoolStart; 113 ULONG64 MmNonPagedPoolEnd; 114 115 ULONG64 MmPagedPoolStart; 116 ULONG64 MmPagedPoolEnd; 117 ULONG64 MmPagedPoolInformation; 118 ULONG64 MmPageSize; 119 120 ULONG64 MmSizeOfPagedPoolInBytes; 121 122 ULONG64 MmTotalCommitLimit; 123 ULONG64 MmTotalCommittedPages; 124 ULONG64 MmSharedCommit; 125 ULONG64 MmDriverCommit; 126 ULONG64 MmProcessCommit; 127 ULONG64 MmPagedPoolCommit; 128 ULONG64 MmExtendedCommit; 129 130 ULONG64 MmZeroedPageListHead; 131 ULONG64 MmFreePageListHead; 132 ULONG64 MmStandbyPageListHead; 133 ULONG64 MmModifiedPageListHead; 134 ULONG64 MmModifiedNoWritePageListHead; 135 ULONG64 MmAvailablePages; 136 ULONG64 MmResidentAvailablePages; 137 138 ULONG64 PoolTrackTable; 139 ULONG64 NonPagedPoolDescriptor; 140 141 ULONG64 MmHighestUserAddress; 142 ULONG64 MmSystemRangeStart; 143 ULONG64 MmUserProbeAddress; 144 145 ULONG64 KdPrintCircularBuffer; 146 ULONG64 KdPrintCircularBufferEnd; 147 ULONG64 KdPrintWritePointer; 148 ULONG64 KdPrintRolloverCount; 149 150 ULONG64 MmLoadedUserImageList; 151 152 // NT 5.1 Addition 153 154 ULONG64 NtBuildLab; 155 ULONG64 KiNormalSystemCall; 156 157 // NT 5.0 QFE addition 158 159 ULONG64 KiProcessorBlock; 160 ULONG64 MmUnloadedDrivers; 161 ULONG64 MmLastUnloadedDriver; 162 ULONG64 MmTriageActionTaken; 163 ULONG64 MmSpecialPoolTag; 164 ULONG64 KernelVerifier; 165 ULONG64 MmVerifierData; 166 ULONG64 MmAllocatedNonPagedPool; 167 ULONG64 MmPeakCommitment; 168 ULONG64 MmTotalCommitLimitMaximum; 169 ULONG64 CmNtCSDVersion; 170 171 // NT 5.1 Addition 172 173 ULONG64 MmPhysicalMemoryBlock; 174 ULONG64 MmSessionBase; 175 ULONG64 MmSessionSize; 176 ULONG64 MmSystemParentTablePage; 177 178 // Server 2003 addition 179 180 ULONG64 MmVirtualTranslationBase; 181 182 USHORT OffsetKThreadNextProcessor; 183 USHORT OffsetKThreadTeb; 184 USHORT OffsetKThreadKernelStack; 185 USHORT OffsetKThreadInitialStack; 186 187 USHORT OffsetKThreadApcProcess; 188 USHORT OffsetKThreadState; 189 USHORT OffsetKThreadBStore; 190 USHORT OffsetKThreadBStoreLimit; 191 192 USHORT SizeEProcess; 193 USHORT OffsetEprocessPeb; 194 USHORT OffsetEprocessParentCID; 195 USHORT OffsetEprocessDirectoryTableBase; 196 197 USHORT SizePrcb; 198 USHORT OffsetPrcbDpcRoutine; 199 USHORT OffsetPrcbCurrentThread; 200 USHORT OffsetPrcbMhz; 201 202 USHORT OffsetPrcbCpuType; 203 USHORT OffsetPrcbVendorString; 204 USHORT OffsetPrcbProcStateContext; 205 USHORT OffsetPrcbNumber; 206 207 USHORT SizeEThread; 208 209 ULONG64 KdPrintCircularBufferPtr; 210 ULONG64 KdPrintBufferSize; 211 212 ULONG64 KeLoaderBlock; 213 214 USHORT SizePcr; 215 USHORT OffsetPcrSelfPcr; 216 USHORT OffsetPcrCurrentPrcb; 217 USHORT OffsetPcrContainedPrcb; 218 219 USHORT OffsetPcrInitialBStore; 220 USHORT OffsetPcrBStoreLimit; 221 USHORT OffsetPcrInitialStack; 222 USHORT OffsetPcrStackLimit; 223 224 USHORT OffsetPrcbPcrPage; 225 USHORT OffsetPrcbProcStateSpecialReg; 226 USHORT GdtR0Code; 227 USHORT GdtR0Data; 228 229 USHORT GdtR0Pcr; 230 USHORT GdtR3Code; 231 USHORT GdtR3Data; 232 USHORT GdtR3Teb; 233 234 USHORT GdtLdt; 235 USHORT GdtTss; 236 USHORT Gdt64R3CmCode; 237 USHORT Gdt64R3CmTeb; 238 239 ULONG64 IopNumTriageDumpDataBlocks; 240 ULONG64 IopTriageDumpDataBlocks; 241 242 } KDDEBUGGER_DATA64, *PKDDEBUGGER_DATA64;
相应的代码:(通常在内核模式下段寄存器FS指向的是KPCR结构,用户模式下指向的是TEB结构)
而我调试64位的时候发现,KdVersionBlock都为null,那我们就不能用上面的方法去得KPRCB了,
不过我们却发现,在msr寄存器的0xC0000101处,存的是KPCR,我们直接_KPCR+偏移0x20就得到KPRCB了。
结构体中的Self是自己的地址,CurrentPrcb是KPRCB的地址。
最后回到正题,我们通过遍历KTIMER_TABLE.TimerEntries.Entry链表,利用CONTAINING_RECORD得到KTIMER结构,判断它的地址是否在我们sys模块地址范围内即可。最后KeCancelTimer掉它。
(第一个地址是KTIMER,第二个地址是sys的基地址,判断是否落入范围)
代码如下:
1 #include <ntifs.h> 2 3 typedef struct _KTIMER_TABLE_ENTRY 4 { 5 ULONG_PTR Lock; 6 LIST_ENTRY Entry; 7 ULARGE_INTEGER Time; 8 } KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY; 9 10 typedef struct _LDR_DATA_TABLE_ENTRY64 11 { 12 LIST_ENTRY64 InLoadOrderLinks; 13 LIST_ENTRY64 InMemoryOrderLinks; 14 LIST_ENTRY64 InInitializationOrderLinks; 15 PVOID DllBase; 16 PVOID EntryPoint; 17 ULONG SizeOfImage; 18 UNICODE_STRING FullDllName; 19 UNICODE_STRING BaseDllName; 20 ULONG Flags; 21 USHORT LoadCount; 22 USHORT TlsIndex; 23 PVOID SectionPointer; 24 ULONG CheckSum; 25 PVOID LoadedImports; 26 PVOID EntryPointActivationContext; 27 PVOID PatchInformation; 28 LIST_ENTRY64 ForwarderLinks; 29 LIST_ENTRY64 ServiceTagLinks; 30 LIST_ENTRY64 StaticLinks; 31 PVOID ContextInformation; 32 ULONG64 OriginalBase; 33 LARGE_INTEGER LoadTime; 34 } LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64; 35 36 37 38 typedef struct _LDR_DATA_TABLE_ENTRY32 39 { 40 LIST_ENTRY32 InLoadOrderLinks; 41 LIST_ENTRY32 InMemoryOrderLinks; 42 LIST_ENTRY32 InInitializationOrderLinks; 43 ULONG DllBase; 44 ULONG EntryPoint; 45 ULONG SizeOfImage; 46 UNICODE_STRING32 FullDllName; 47 UNICODE_STRING32 BaseDllName; 48 ULONG Flags; 49 USHORT LoadCount; 50 USHORT TlsIndex; 51 union { 52 LIST_ENTRY32 HashLinks; 53 struct { 54 ULONG SectionPointer; 55 ULONG CheckSum; 56 }; 57 }; 58 union { 59 struct { 60 ULONG TimeDateStamp; 61 }; 62 struct { 63 ULONG LoadedImports; 64 }; 65 }; 66 } LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32; 67 68 69 #ifdef _WIN64 70 #define LDR_DATA_TABLE_ENTRY LDR_DATA_TABLE_ENTRY64 71 #define PLDR_DATA_TABLE_ENTRY PLDR_DATA_TABLE_ENTRY64 72 #else 73 #define LDR_DATA_TABLE_ENTRY LDR_DATA_TABLE_ENTRY32 74 #define PLDR_DATA_TABLE_ENTRY PLDR_DATA_TABLE_ENTRY32 75 #endif 76 77 VOID DriverUnload(PDRIVER_OBJECT DriverObject); 78 79 NTSTATUS GetKernelModuleInformationByKernelModuleName(PDRIVER_OBJECT DriverObject, WCHAR* wzKernelModuleName, PVOID* KernelModuleBase, ULONG_PTR* KernelModuleSize); 80 81 PVOID 82 GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName); 83 84 BOOLEAN GetKiWaitVariableAddress(PULONG64* KiWaitNever, PULONG64* KiWaitAlways); 85 86 KDPC* TransTimerDPCEx(PKTIMER Timer, ULONG64 KiWaitNever, ULONG64 KiWaitAlways); 87 88 PULONG GetKiProcessorBlock(); 89 90 BOOLEAN GetDPCTimerInfoByModuleInfo(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize, ULONG_PTR* Timer); 91 BOOLEAN RemoveDPCFromNtos(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize);
1 #include "RemoveDPC.h" 2 #include <ntstrsafe.h> 3 //bp RemoveDPC!DriverEntry 4 5 PVOID __KernelModuleBase = NULL; 6 ULONG_PTR __KernelModuleSize = 0; 7 8 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) 9 { 10 NTSTATUS Status = STATUS_SUCCESS; 11 PDEVICE_OBJECT DeviceObject = NULL; 12 DbgPrint("DriverSuccuss\r\n"); 13 DriverObject->DriverUnload = DriverUnload; 14 15 WCHAR wzKernelModuleName[] = L"NewDPCHookSSDT.sys"; 16 if (GetKernelModuleInformationByKernelModuleName(DriverObject, wzKernelModuleName, &__KernelModuleBase, &__KernelModuleSize) == STATUS_UNSUCCESSFUL) 17 { 18 return STATUS_UNSUCCESSFUL; 19 } 20 RemoveDPCFromNtos(__KernelModuleBase, __KernelModuleSize); 21 return Status; 22 } 23 24 25 NTSTATUS GetKernelModuleInformationByKernelModuleName(PDRIVER_OBJECT DriverObject,WCHAR* wzKernelModuleName,PVOID* KernelModuleBase,ULONG_PTR* KernelModuleSize) 26 { 27 PLDR_DATA_TABLE_ENTRY CurrentEntry = NULL; 28 PLDR_DATA_TABLE_ENTRY NextEntry = NULL; 29 if (DriverObject&&MmIsAddressValid(DriverObject)) 30 { 31 CurrentEntry = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection; 32 NextEntry = (PLDR_DATA_TABLE_ENTRY)CurrentEntry->InLoadOrderLinks.Flink; 33 while (NextEntry != CurrentEntry) 34 { 35 if (NextEntry->BaseDllName.Length >= wcslen(wzKernelModuleName)) 36 { 37 if (NextEntry->BaseDllName.Buffer 38 &&MmIsAddressValid((PVOID)NextEntry->BaseDllName.Buffer) 39 && !_wcsnicmp(wzKernelModuleName, (WCHAR*)NextEntry->BaseDllName.Buffer, wcslen(wzKernelModuleName))) //这里掉坑了,wcsnicmp是比较函数,相等时返回0, 40 //再次掉坑,第三个参数表示比较的的字母的个数,如果是0,肯定相等 41 { 42 DbgPrint("%S\r\n", wzKernelModuleName); 43 DbgPrint("%S\r\n", (WCHAR*)NextEntry->BaseDllName.Buffer); 44 *KernelModuleBase = NextEntry->DllBase; 45 *KernelModuleSize = NextEntry->SizeOfImage; 46 return STATUS_SUCCESS; 47 } 48 } 49 NextEntry = (PLDR_DATA_TABLE_ENTRY)NextEntry->InLoadOrderLinks.Flink; 50 } 51 } 52 return STATUS_UNSUCCESSFUL; 53 } 54 55 BOOLEAN RemoveDPCFromNtos(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize) 56 { 57 ULONG_PTR Timer = NULL; 58 if (GetDPCTimerInfoByModuleInfo(KernelModuleBase, KernelModuleSize, &Timer) == FALSE) 59 { 60 return FALSE; 61 } 62 if (Timer && MmIsAddressValid(Timer)) 63 { 64 if (KeCancelTimer(Timer)) 65 { 66 return TRUE; 67 } 68 } 69 return FALSE; 70 } 71 72 73 PULONG GetKiProcessorBlock() 74 { 75 ULONG* KiProcessorBlock = 0; 76 77 KeSetSystemAffinityThread(1); //使当前线程运行在第一个处理器上 78 79 _asm 80 { 81 push eax 82 mov eax, FS:[0x34]; 得到KdVersionBlock的地址 83 add eax, 20h; 得到指向DebuggerDataList的地址 84 mov eax, [eax]; 得到DebuggerDataList的地址 85 mov eax, [eax]; 取出里面的内容,即KdDebuggerData64结构 86 mov eax, [eax + 218h]; 取出KiProcessBlock的地址 87 mov KiProcessorBlock, eax;放到变量里 88 pop eax 89 } 90 91 KeRevertToUserAffinityThread(); 92 93 return KiProcessorBlock; 94 95 } 96 97 BOOLEAN GetDPCTimerInfoByModuleInfo(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize,ULONG_PTR* Timer) 98 { 99 ULONG32 NumberOfProcessors = KeNumberProcessors; //一般运行在第一个CPU上 100 ULONG_PTR KPRCB = 0; 101 PUCHAR TimerEntries = NULL; 102 PULONG_PTR KiWaitNever = NULL; 103 PULONG_PTR KiWaitAlways = NULL; 104 PLIST_ENTRY CurrentEntry = NULL; 105 PLIST_ENTRY NextEntry = NULL; 106 PKTIMER MyTimer = NULL; 107 int i = 0; 108 KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); 109 #ifdef _WIN64 110 111 KeSetSystemAffinityThread(1); //使当前线程运行在第一个CPU上 112 KPRCB = (ULONG64)__readmsr(0xC0000101) + 0x20; //msr(0xC0000101)处位置存的是KPCR 113 KeRevertToUserAffinityThread(); 114 TimerEntries = (PUCHAR)(*(ULONG64*)KPRCB + 0x2200 + 0x200); 115 if (GetKiWaitVariableAddress(&KiWaitNever, &KiWaitAlways) == FALSE) 116 { 117 return FALSE; 118 } 119 for (i = 0; i < 0x100; i++) 120 { 121 CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*i + 8); 122 NextEntry = CurrentEntry->Blink; 123 if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry)) 124 { 125 while (NextEntry != CurrentEntry) 126 { 127 PKDPC RealDPC; 128 MyTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); 129 130 RealDPC = TransTimerDPCEx(MyTimer, *KiWaitNever, *KiWaitAlways); 131 if (MmIsAddressValid(MyTimer) && MmIsAddressValid(RealDPC) && MmIsAddressValid(RealDPC->DeferredRoutine)) 132 { 133 if ((ULONG64)MyTimer >= (ULONG64)KernelModuleBase && (ULONG64)MyTimer <= (ULONG64)KernelModuleBase + KernelModuleSize) 134 { 135 *Timer = (ULONG64)MyTimer; 136 KeLowerIrql(OldIrql); 137 return TRUE; 138 } 139 } 140 NextEntry = NextEntry->Blink; 141 } 142 } 143 } 144 #else 145 PULONG KiProcessorBlock = NULL; 146 KiProcessorBlock = (PULONG)GetKiProcessorBlock(); 147 TimerEntries = (PUCHAR)(*KiProcessorBlock + 0x1960 + 0x40); 148 for (i = 0; i < 0x100; i++) 149 { 150 CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*i + 4); 151 NextEntry = CurrentEntry->Blink; 152 if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry)) 153 { 154 while (NextEntry != CurrentEntry) 155 { 156 MyTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); 157 158 if (MmIsAddressValid(MyTimer) && MmIsAddressValid(MyTimer->Dpc) && MmIsAddressValid(MyTimer->Dpc->DeferredRoutine)) 159 { 160 if ((ULONG32)MyTimer >= (ULONG32)KernelModuleBase && (ULONG32)MyTimer <= (ULONG32)KernelModuleBase + KernelModuleSize) 161 { 162 *Timer = (ULONG32)MyTimer; 163 KeLowerIrql(OldIrql); 164 return TRUE; 165 } 166 } 167 NextEntry = NextEntry->Blink; 168 } 169 } 170 } 171 172 #endif 173 174 175 KeLowerIrql(OldIrql); 176 return FALSE; 177 } 178 179 180 181 KDPC* TransTimerDPCEx(PKTIMER Timer, ULONG64 KiWaitNever, ULONG64 KiWaitAlways) 182 { 183 ULONG64 DPC = (ULONG64)Timer->Dpc; //Time 184 DPC ^= KiWaitNever; 185 DPC = _rotl64(DPC, (UCHAR)(KiWaitNever & 0xFF)); 186 DPC ^= (ULONG64)Timer; 187 DPC = _byteswap_uint64(DPC); 188 DPC ^= KiWaitAlways; 189 return (KDPC*)DPC; 190 } 191 192 BOOLEAN GetKiWaitVariableAddress(PULONG64* KiWaitNever, PULONG64* KiWaitAlways) 193 { 194 /* 195 kd> u kesettimer l 50 196 nt!KeSetTimer: 197 fffff800`03ef10a8 4883ec38 sub rsp,38h 198 fffff800`03ef10ac 4c89442420 mov qword ptr [rsp+20h],r8 199 fffff800`03ef10b1 4533c9 xor r9d,r9d 200 fffff800`03ef10b4 4533c0 xor r8d,r8d 201 fffff800`03ef10b7 e814000000 call nt!KiSetTimerEx (fffff800`03ef10d0) 202 fffff800`03ef10bc 4883c438 add rsp,38h 203 fffff800`03ef10c0 c3 ret 204 fffff800`03ef10c1 90 nop 205 fffff800`03ef10c2 90 nop 206 fffff800`03ef10c3 90 nop 207 fffff800`03ef10c4 90 nop 208 fffff800`03ef10c5 90 nop 209 fffff800`03ef10c6 90 nop 210 fffff800`03ef10c7 90 nop 211 nt!KxWaitForLockChainValid: 212 fffff800`03ef10c8 90 nop 213 fffff800`03ef10c9 90 nop 214 fffff800`03ef10ca 90 nop 215 fffff800`03ef10cb 90 nop 216 fffff800`03ef10cc 90 nop 217 fffff800`03ef10cd 90 nop 218 fffff800`03ef10ce 90 nop 219 fffff800`03ef10cf 90 nop 220 nt!KiSetTimerEx: 221 fffff800`03ef10d0 48895c2408 mov qword ptr [rsp+8],rbx 222 fffff800`03ef10d5 4889542410 mov qword ptr [rsp+10h],rdx 223 fffff800`03ef10da 55 push rbp 224 fffff800`03ef10db 56 push rsi 225 fffff800`03ef10dc 57 push rdi 226 fffff800`03ef10dd 4154 push r12 227 fffff800`03ef10df 4155 push r13 228 fffff800`03ef10e1 4156 push r14 229 fffff800`03ef10e3 4157 push r15 230 fffff800`03ef10e5 4883ec50 sub rsp,50h 231 fffff800`03ef10e9 488b0518502200 mov rax,qword ptr [nt!KiWaitNever (fffff800`04116108)] 232 fffff800`03ef10f0 488b1de9502200 mov rbx,qword ptr [nt!KiWaitAlways (fffff800`041161e0)] 233 */ 234 ULONG64 KeSetTimer = 0; 235 PUCHAR StartSearchAddress = 0; 236 PUCHAR EndSearchAddress = 0; 237 ULONG64 iOffset = 0; 238 PUCHAR i = NULL; 239 KeSetTimer = (ULONG64)GetExportVariableAddressFromNtosKrnlExportTableByVariableName(L"KeSetTimer"); 240 241 StartSearchAddress = (PUCHAR)KeSetTimer; 242 EndSearchAddress = StartSearchAddress + 0x500; 243 244 for (i = StartSearchAddress; i < EndSearchAddress; i++) 245 { 246 if (*i == 0x48 && *(i + 1) == 0x8B && *(i + 2) == 0x05) 247 { 248 memcpy(&iOffset, i + 3, 4); 249 *KiWaitNever = (PULONG64)(iOffset + (ULONG64)i + 7); 250 i = i + 7; 251 memcpy(&iOffset, i + 3, 4); 252 *KiWaitAlways = (PULONG64)(iOffset + (ULONG64)i + 7); 253 return TRUE; 254 } 255 } 256 257 return FALSE; 258 } 259 260 PVOID 261 GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName) 262 { 263 //通过导出变量名字从NtosKrnl中获得导出变量地址 264 UNICODE_STRING uniVariableName; 265 PVOID VariableAddress = NULL; 266 267 if (wzVariableName&&wcslen(wzVariableName) > 0) 268 { 269 RtlUnicodeStringInit(&uniVariableName, wzVariableName); 270 //从Ntos模块的导出表中获得导出变量的地址 271 VariableAddress = MmGetSystemRoutineAddress(&uniVariableName); 272 } 273 return VariableAddress; 274 } 275 276 VOID DriverUnload(PDRIVER_OBJECT DriverObject) 277 { 278 DbgPrint("Unload Success\r\n"); 279 }
That's all.