-
-
[求助]X64枚举DCP定时器的问题
-
发表于: 2018-9-5 21:36 5985
-
先上源码 有时输出就几个,有时输出几十个,大部分是几个的,是什么原因?
#include "Drive.h" PVOID __KernelModuleBase = NULL; ULONG_PTR __KernelModuleSize = 0; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) { NTSTATUS Status = STATUS_UNSUCCESSFUL; WCHAR ModuleName[] = L"win32k.sys"; DriverObject->DriverUnload = DriverUnload; //if (GetKernelModuleInfoByDriverObjectInWin7(DriverObject, ModuleName, &__KernelModuleBase, &__KernelModuleSize) == FALSE) //{ //return Status; //} //DbgPrint("gabpfiModuleBase is %p \r\n", __KernelModuleBase); //DbgPrint("gabpfiModuleSize is %p \r\n", __KernelModuleSize); __KernelModuleBase = 0xfffff00886800000; __KernelModuleSize = 0xffff0; RemoveDpcInKernelModuleInWin7(__KernelModuleBase, __KernelModuleSize); return STATUS_SUCCESS; } VOID DriverUnload(PDRIVER_OBJECT DriverObject) { DbgPrint("ByeByeDriver\r\n"); } BOOLEAN GetKernelModuleInfoByDriverObjectInWin7(PDRIVER_OBJECT DriverObject, WCHAR* ModuleName, PVOID* ModuleBase, ULONG_PTR* ModuleSize) { PLDR_DATA_TABLE_ENTRY CurrentEntry = NULL; PLDR_DATA_TABLE_ENTRY NextEntry = NULL; if (DriverObject == NULL && !MmIsAddressValid((PVOID)DriverObject)) { return FALSE; } // DriverSection就是PLDR_DATA_TABLE_ENTRY结构 CurrentEntry = NextEntry = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection; NextEntry = (PLDR_DATA_TABLE_ENTRY)CurrentEntry->InLoadOrderLinks.Flink; while (CurrentEntry != NextEntry) { if (NextEntry->BaseDllName.Buffer&& NextEntry->BaseDllName.Length > 0 && MmIsAddressValid((PVOID)NextEntry->BaseDllName.Buffer) && !wcsncmp(ModuleName, (WCHAR*)NextEntry->BaseDllName.Buffer, NextEntry->BaseDllName.Length)) { *ModuleBase = NextEntry->DllBase; *ModuleSize = NextEntry->SizeOfImage; DbgPrint("gabpfiModuleBase is %p \r\n", *ModuleBase); DbgPrint("gabpfiModuleSize is %p \r\n", *ModuleSize); return TRUE; } NextEntry = NextEntry->InLoadOrderLinks.Flink; } return FALSE; } BOOLEAN RemoveDpcInKernelModuleInWin7(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize) { PKTIMER Timer = NULL; #ifdef _WIN64 if (GetDpcTimerInfoByKernelModuleInWin7_x64(&Timer, KernelModuleBase, KernelModuleSize) == FALSE) { return FALSE; } #else if (GetDpcTimerInfoByKernelModuleInWin7_x86(&Timer, KernelModuleBase, KernelModuleSize) == FALSE) { return FALSE; } #endif if (Timer&&MmIsAddressValid((PVOID)Timer)) { if (KeCancelTimer(Timer)) { return TRUE; } } return FALSE; } BOOLEAN GetDpcTimerInfoByKernelModuleInWin7_x64(PKTIMER* Timer, PVOID KernelModuleBase, ULONG64 KernelModuleSize) { ULONG32 v1 = KeNumberProcessors;// 此变量已经被导入,可以直接使用,表示的是CPU的核心数 ULONG32 i = 0; ULONG32 j = 0; ULONG64 KPRCB = 0; PKTIMER KTimer = NULL; PKDPC RealDpc = NULL; PUCHAR TimerEntries = NULL; PULONG64 KiWaitNever = NULL; PULONG64 KiWaitAlways = NULL; PUCHAR CurrentEntry = NULL; PUCHAR NextEntry = NULL; KIRQL OldIrql = KeRaiseIrqlToDpcLevel();// 提高中断级到DISPATCH_LEVEL,因为要暴力搜索内存,防止在搜索期间内存受到其他程序的影响! for (i = 0; i < v1; i++) { // 设置当前线程到第i+1个处理器上,我虚拟机上只有1个处理器,所以说这个没什么效果 // 这样做的原因是 在每个CPU核上都要一个KPCR(Kernel Processor Control Block),在KPCR上有一个KdVersionBlock指针, // KdVersionBlock指针的值只有在第一个核上有,其他核上是NULL.KdVersionBlock->PsLoadedModuleList是加载的内核模块的链表的表头 // 可是这里我们没有必要那么做,因为我们根本没有用这个KdVersionBlock指针。 KeSetSystemAffinityThread(i + 1); KPRCB = __readmsr(0xC0000101) + 0x20; // 获得KPRCB的地址 /* // dt _KPCR // dt _KPRCB kd> rdmsr 0xc0000101 msr[c0000101] = fffff800`04047d00 kd> dt _KPCR fffff800`04047d00 ntdll!_KPCR +0x000 NtTib : _NT_TIB +0x000 GdtBase : 0xfffff800`00b95000 _KGDTENTRY64 +0x008 TssBase : 0xfffff800`00b96080 _KTSS64 +0x010 UserRsp : 0x1adfa88 +0x018 Self : 0xfffff800`04047d00 _KPCR +0x020 CurrentPrcb : 0xfffff800`04047e80 _KPRCB ... kd> dt _KPRCB 0xfffff800`04047e80 ntdll!_KPRCB +0x000 MxCsr : 0x1f80 +0x004 LegacyNumber : 0 '' +0x21f0 PrcbPad50 : [2] 0 +0x2200 TimerTable : _KTIMER_TABLE ... kd> dt _KTIMER_TABLE 0xfffff800`04047e80+0x2200 ntdll!_KTIMER_TABLE +0x000 TimerExpiry : [64] (null) +0x200 TimerEntries : [256] _KTIMER_TABLE_ENTRY */ // 恢复当前线程到之前的处理器上 KeRevertToUserAffinityThread(); TimerEntries = (*(PULONG64)KPRCB + 0x2200 + 0x200); // 获得TimerEntries的地址 // x64下Timer->dpc是加密的,需要定位KiWaitAlways, KiWaitNever来解决 // x86下不用 if (GetKiWaitVariableAddress(&KiWaitNever, &KiWaitAlways) == FALSE) { return FALSE; } // j<0x100 是因为KPRCB结构体的0x2200处的结构体_KTIMER_TABLE->TimerEntries是一个256个结构体的数组!而我们要遍历这个数组 for (j = 0; j < 0x100; j++) { CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*j + 8); // 加8是为了过偏移,Lock8个字节,这里的*j如果不写的话,会造成虚拟机CPU直接崩溃 NextEntry = ((PLIST_ENTRY)CurrentEntry)->Blink; if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry)) { while (CurrentEntry != NextEntry) { KTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); // 获得结构的首地址 // 解码运算,通过Timer获得DPC RealDpc = TransTimerDPCEx(KTimer, *KiWaitNever, *KiWaitAlways); // RealDpc和KTimer以及DPC函数都合法 // DbgPrint("gabpfiRealDpc is: %p , gabpfiDeferredRoutine is: %p \r\n", RealDpc, RealDpc->DeferredRoutine); //DbgPrint("[gabpfidpc]dpcroutine:%p\n", RealDpc->DeferredRoutine); //__KernelModuleBase = 0xfffff00886800000; //__KernelModuleSize = 0xffff0; if (MmIsAddressValid(RealDpc) && MmIsAddressValid(KTimer) && MmIsAddressValid(RealDpc->DeferredRoutine)) { DbgPrint("gabpfiOKis %p \r\n", RealDpc->DeferredRoutine); //输出--------------------------- if ((ULONG64)RealDpc->DeferredRoutine >= 0xfffff88006000000 && (ULONG64)RealDpc->DeferredRoutine <= 0xfffff88006ff0000) { *Timer = KTimer; KeLowerIrql(OldIrql); return TRUE; } } NextEntry = ((PLIST_ENTRY)NextEntry)->Blink; } } } } KeLowerIrql(OldIrql); // 这里如果不恢复的话,会造成崩溃 return FALSE; } BOOLEAN GetDpcTimerInfoByKernelModuleInWin7_x86(PKTIMER* Timer, PVOID KernelModuleBase, ULONG32 KernelModuleSize) { ULONG32 v1 = KeNumberProcessors;// 此变量已经被导入,可以直接使用,表示的是CPU的核心数 ULONG32 i = 0; ULONG32 j = 0; ULONG32 KPRCB = 0; PKTIMER KTimer = NULL; PKDPC RealDpc = NULL; PUCHAR TimerEntries = NULL; PUCHAR CurrentEntry = NULL; PUCHAR NextEntry = NULL; KIRQL OldIrql = KeRaiseIrqlToDpcLevel();// 提高中断级到DISPATCH_LEVEL,因为要暴力搜索内存,防止在搜索期间内存受到其他程序的影响! for (i = 0; i < v1; i++) { // 设置当前线程到第i+1个处理器上,我虚拟机上只有1个处理器,所以说这个没什么效果 // 这样做的原因是 在每个CPU核上都要一个KPCR(Kernel Processor Control Block),在KPCR上有一个KdVersionBlock指针, // KdVersionBlock指针的值只有在第一个核上有,其他核上是NULL.KdVersionBlock->PsLoadedModuleList是加载的内核模块的链表的表头 // 在这里这样做是为了遍历每一个核心上的KPCR->_KTIMER_TABLE KeSetSystemAffinityThread(i + 1); //__asm { // push eax // mov eax, fs:[0x20]; 得到_KPRCB的地址 // // add eax, 20h // mov KPRCB, eax // pop eax //} /* // dt _KPCR // dt _KPRCB kd> dt _KPCR ntdll!_KPCR +0x000 NtTib : _NT_TIB +0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD +0x004 Used_StackBase : Ptr32 Void +0x008 Spare2 : Ptr32 Void +0x00c TssCopy : Ptr32 Void +0x010 ContextSwitches : Uint4B +0x014 SetMemberCopy : Uint4B +0x018 Used_Self : Ptr32 Void +0x01c SelfPcr : Ptr32 _KPCR +0x020 Prcb : Ptr32 _KPRCB ... kd> dt _KPRCB 00000050 ntdll!_KPRCB +0x000 MinorVersion : ?? +0x002 MajorVersion : ?? +0x1958 TickOffset : ?? +0x1960 TimerTable : _KTIMER_TABLE +0x31a0 CallDpc : _KDPC ... */ // 恢复当前线程到之前的处理器上 KeRevertToUserAffinityThread(); // TimerEntries = (*(PULONG32)KPRCB + 0x1960 + 0x40); // 获得TimerEntries的地址 TimerEntries = (KPRCB + 0x1960 + 0x40); // j<0x100 是因为_KTIMER_TABLE->TimerEntries是一个256个结构体的数组!而我们要遍历这个数组 for (j = 0; j < 0x100; j++) { CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*j + 4); // 加4是为了过偏移,Lock4个字节,这里的*j如果不写的话,会造成虚拟机CPU直接崩溃 NextEntry = ((PLIST_ENTRY)CurrentEntry)->Blink; if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry)) { while (CurrentEntry != NextEntry) { KTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); // 获得结构的首地址 RealDpc = KTimer->Dpc; // RealDpc和KTimer以及DPC函数都合法 if (MmIsAddressValid(RealDpc) && MmIsAddressValid(KTimer) && MmIsAddressValid(RealDpc->DeferredRoutine)) { if ((ULONG32)KTimer >= (ULONG32)KernelModuleBase && (ULONG32)KTimer <= (ULONG32)KernelModuleBase + (ULONG32)KernelModuleSize) { *Timer = KTimer; KeLowerIrql(OldIrql); return TRUE; } } NextEntry = ((PLIST_ENTRY)NextEntry)->Blink; } } } } KeLowerIrql(OldIrql); // 这里如果不恢复的话,会造成崩溃 return FALSE; } /*ULONG_PTR ptrDpc = (ULONG_PTR)ptrTimer->Dpc; KDPC* DecDpc = NULL; int nShift = (p2dq(ptrKiWaitNever) & 0xFF); //_RSI->Dpc = (_KDPC *)v19; //_RSI = Timer; ptrDpc ^= p2dq(ptrKiWaitNever);//v19 = KiWaitNever ^ v18; ptrDpc = _rotl64(ptrDpc, nShift);//v18 = __ROR8__((unsigned __int64)Timer ^ _RBX, KiWaitNever); ptrDpc ^= (ULONG_PTR)ptrTimer; ptrDpc = _byteswap_uint64(ptrDpc);//__asm { bswap rbx } ptrDpc ^= p2dq(ptrKiWaitAlways);//_RBX = (unsigned __int64)DPC ^ KiWaitAlways; if (MmIsAddressValid((PVOID)ptrDpc)) { nTotalCount++; DecDpc = (KDPC*)ptrDpc; DbgPrint("[dpc]dpc:%p,routine:%p\n", DecDpc, DecDpc->DeferredRoutine); } */ #define p2dq(x) (*((ULONG_PTR*)x)) KDPC* TransTimerDPCEx(PKTIMER Timer, ULONG64 KiWaitNever, ULONG64 KiWaitAlways) { ULONG64 DPC = (ULONG64)Timer->Dpc; //Time DPC ^= KiWaitNever; DPC = _rotl64(DPC, (UCHAR)(KiWaitNever & 0xFF)); DPC ^= (ULONG64)Timer; DPC = _byteswap_uint64(DPC); DPC ^= KiWaitAlways; return (KDPC*)DPC; } BOOLEAN GetKiWaitVariableAddress(PULONG64* KiWaitNever, PULONG64* KiWaitAlways) { /* kd> u keSetTimer l 50 nt!KeSetTimer: fffff800`03e908b0 4883ec38 sub rsp,38h fffff800`03e908b4 4c89442420 mov qword ptr [rsp+20h],r8 fffff800`03e908b9 4533c9 xor r9d,r9d fffff800`03e908bc 4533c0 xor r8d,r8d fffff800`03e908bf e80c000000 call nt!KiSetTimerEx (fffff800`03e908d0) fffff800`03e908c4 4883c438 add rsp,38h fffff800`03e908c8 c3 ret fffff800`03e908c9 90 nop fffff800`03e908ca 90 nop fffff800`03e908cb 90 nop fffff800`03e908cc 90 nop fffff800`03e908cd 90 nop fffff800`03e908ce 90 nop fffff800`03e908cf 90 nop nt!KiSetTimerEx: fffff800`03e908d0 48895c2408 mov qword ptr [rsp+8],rbx fffff800`03e908d5 4889542410 mov qword ptr [rsp+10h],rdx fffff800`03e908da 55 push rbp fffff800`03e908db 56 push rsi fffff800`03e908dc 57 push rdi fffff800`03e908dd 4154 push r12 fffff800`03e908df 4155 push r13 fffff800`03e908e1 4156 push r14 fffff800`03e908e3 4157 push r15 fffff800`03e908e5 4883ec50 sub rsp,50h fffff800`03e908e9 488b0530c82200 mov rax,qword ptr [nt!KiWaitNever (fffff800`040bd120)] fffff800`03e908f0 488b1d19c92200 mov rbx,qword ptr [nt!KiWaitAlways (fffff800`040bd210)] */ ULONG64 KeSetTimer = 0; PUCHAR StartSearchAddress = NULL; PUCHAR EndSearchAddress = NULL; PUCHAR i = NULL; INT64 Offset = 0; KeSetTimer = (ULONG64)GetExportVariableAddressFromNtosExportTableByVariableName(L"KeSetTimer"); StartSearchAddress = (PUCHAR)KeSetTimer; EndSearchAddress = StartSearchAddress + 0x500; for (i = StartSearchAddress; i <= EndSearchAddress; i++) { if (*i == 0x48 && *(i + 1) == 0x8B && *(i + 2) == 0x05) { memcpy(&Offset, i + 3, 4); *KiWaitNever = (PULONG64)((ULONG64)i + Offset + 7); i += 7; memcpy(&Offset, i + 3, 4); *KiWaitAlways = (PULONG64)((ULONG64)i + Offset + 7); return TRUE; } } return TRUE; } PVOID GetExportVariableAddressFromNtosExportTableByVariableName(WCHAR * VariableName) { PVOID VariableAddress = NULL; UNICODE_STRING v1 = { 0 }; if (VariableName&& wcslen(VariableName) > 0) { RtlInitUnicodeString(&v1, VariableName); VariableAddress = MmGetSystemRoutineAddress(&v1); } return VariableAddress; }
最后于 2018-9-8 16:09
被gabpfiugdu编辑
,原因:
赞赏
他的文章
看原图
赞赏
雪币:
留言: