移除时钟KTIMER和DPC

typedef struct _KTIMER {
    DISPATCHER_HEADER Header;
    ULARGE_INTEGER DueTime;
    LIST_ENTRY TimerListEntry;
    struct _KDPC *Dpc;

#if !defined(KENCODED_TIMER_PROCESSOR)

    ULONG Processor;

#endif

    ULONG Period;
} KTIMER, *PKTIMER, *PRKTIMER;

一、枚举所有KTIMER

通过这个结构体我们可以看出来DPC是KTIMER的一个成员,也就是说只要找到我们想要移除的KTIMER之后,移除KTIMER就可以移除DPC了,那么问题就来了,我们怎么找到KTIMER呢,而且系统里面有很多的KTIMER结构,就算找到了,怎么确定找到的KTIMER就是我们要移除的KTIMER呢。

所以我们可以理一下移除DPC的步骤:

1.先找到所有的KTIMER。

2.从所有的KTIMER中一个一个的找我们所要想要移除的KTIMER。(当然,如果一击即中,直接把我们所要移除的KTIMER从系统中拽出来,不用暴力枚举所有的KTIMER,那就最好了,不过好像没有办法这么做,网上也没有类似的方法,基本上都是暴力枚举所有KTIMER)

3.移除KTIMER。

 

说起来容易,那怎么寻找呢,原来,系统中有一个链表,这个链表将所有的KTIMER串起来,链表头存在于一个数组中,这个数组叫TimerEntries,256个数组成员,每一个成员都是一个链表头。这个数组存在于TimerTable数组中,TimerEntries是 TimerTable的第二数组成员,TimerTable这个数组存在结构体KPRCB0x2200(x64)或0x1960(x86)处,而KPRCB结构体指针又存在于结构体KPCR0x20处。KPCR结构体在64位中是在寄存器msr[c0000101]的位置,在32位中是在fs寄存器中。

关于KPRC等的解释可以看后面的脚注1。




好了,我们可以通过画图理一遍思路。


移除时钟KTIMER和DPC_第1张图片



我们可以再通过Windbg调试看一下具体的信息:

 

X64:

msr寄存器

kd> rdmsr 0xc0000101

msr[c0000101] = fffff800`04055d00

KPCR 结构体

kd> dt _KPCR fffff800`04055d00

ntdll!_KPCR

   +0x000 NtTib            : _NT_TIB

   +0x000 GdtBase          : 0xfffff800`00b95000 _KGDTENTRY64

   +0x008 TssBase          : 0xfffff800`00b96080 _KTSS64

   +0x010 UserRsp          : 0x30ffb48

   +0x018 Self             : 0xfffff800`04055d00 _KPCR

   +0x020 CurrentPrcb      : 0xfffff800`04055e80 _KPRCB

......  

KPRCB 结构体

kd> dt _KPRCB 0xfffff800`04055e80

ntdll!_KPRCB

   +0x000 MxCsr            : 0x1f80

   +0x004 LegacyNumber     : 0 ''

   +0x005 ReservedMustBeZero : 0 ''

   +0x006 InterruptRequest : 0 ''

.....

   +0x2200 TimerTable       : _KTIMER_TABLE

......

   

  

kd> dt _KTIMER_TABLE 0xfffff800`04055e80+2200

ntdll!_KTIMER_TABLE

   +0x000 TimerExpiry      : [64] (null)

   +0x200 TimerEntries     : [256] _KTIMER_TABLE_ENTRY

 

TimerEntries数组的第一个成员

kd> dt _KTIMER_TABLE_ENTRY 0xfffff800`04055e80+2200+200

ntdll!_KTIMER_TABLE_ENTRY

   +0x000 Lock             : 0

   +0x008 Entry            : _LIST_ENTRY [ 0xfffff800`04058288 - 0xfffff800`04058288 ]

   +0x018 Time             : _ULARGE_INTEGER 0xffffffff`415c8744

 

 

X86:

kd> dd KiProcessorBlock

83f84a00  83f45d20 00000000 00000000 00000000

KPRCB结构体就在83f45d20 这个位置

 

kd> dt _KPRCB 83f45d20

ntdll!_KPRCB

   +0x000 MinorVersion     : 1

   +0x002 MajorVersion     : 1

   +0x004 CurrentThread    : 0x83f4f380 _KTHREAD

   ......

   +0x1960 TimerTable       : _KTIMER_TABLE

 

KPRCB的地址减去0x120就是KPCR结构体的位置

kd> dt _KPCR 83f45d20-120

ntdll!_KPCR

   +0x000 NtTib            : _NT_TIB

   +0x000 Used_ExceptionList : 0x83f420ac _EXCEPTION_REGISTRATION_RECORD

   +0x004 Used_StackBase   : (null)

   +0x008 Spare2           : (null)

   +0x00c TssCopy          : 0x801e4000 Void

   +0x010 ContextSwitches  : 0x3a3f9

   +0x014 SetMemberCopy    : 1

   +0x018 Used_Self        : (null)

   +0x01c SelfPcr          : 0x83f45c00 _KPCR

   +0x020 Prcb             : 0x83f45d20 _KPRCB

 

可以看一下KPRCB的TimerTable

kd> dt _KTIMER_TABLE 83f45d20+1960

ntdll!_KTIMER_TABLE

   +0x000 TimerExpiry      : [16] (null)

   +0x040 TimerEntries     : [256] _KTIMER_TABLE_ENTRY

 

TimerEntries有256个成员,下面看一下第一个成员:

kd> dt _KTIMER_TABLE_ENTRY 83f45d20+1960+40

ntdll!_KTIMER_TABLE_ENTRY

   +0x000 Lock             : 0

   +0x004 Entry            : _LIST_ENTRY [ 0x885785e0 - 0x885785e0 ]

   +0x010 Time             : _ULARGE_INTEGER 0x31fef468


------------------------------------------------------------------




  +0x040 TimerEntries     : [256] _KTIMER_TABLE_ENTRY

这里就是我们要暴力枚举的地方,

 _KTIMER_TABLE_ENTRY的_LIST_ENTRY就是KTIMER 里面的TimerListEntry两个是同一个东西。不明白看图:

移除时钟KTIMER和DPC_第2张图片


二、从所有KTIMER中找到自己想要移除的

我们得到了所有的KTIMER,那么怎么找到我们所要的KTIMER呢,

比如我们要移除的KTIMER在驱动driver.sys中,可以通过找KTIMER所在的地址大于driver.sys的基地址,并且KTIMER所在的地址小于driver.sys的基地址+driver,sys的模块大小。也就是说KTIMER在driver.sys中,那么他就是我们要寻找的!获取内核模块的基地址和大小我们一会就说。

 

接下来就是移除了,只要通过KeCancelTimer(Timer);操作即可。

 

 

获取内核模块的基地址和大小有好几种方法,

1.ZwQuerySystemInformation通过此函数枚举所有模块的所有信息。这个网上一搜一大堆。

2.通过_LDR_DATA_TABLE_ENTRY现在我在这里说一下我对这个方法的理解。

 _LDR_DATA_TABLE_ENTRY这个结构体是DriverObject->DriverSection。

typedef struct _LDR_DATA_TABLE_ENTRY

{

     LIST_ENTRY InLoadOrderLinks;   // 加载顺序

     LIST_ENTRY InMemoryOrderLinks; // 在内存中的顺序

     LIST_ENTRY InInitializationOrderLinks; // 初始化的顺序

     PVOID DllBase;

     PVOID EntryPoint;

     ULONG SizeOfImage;

     UNICODE_STRING FullDllName;

     UNICODE_STRING BaseDllName;

     ULONG Flags;

     WORD LoadCount;

     WORD TlsIndex;

     union

     {

          LIST_ENTRY HashLinks;

          struct

          {

               PVOID SectionPointer;

               ULONG CheckSum;

          };

     };

     union

     {

          ULONG TimeDateStamp;

          PVOID LoadedImports;

     };

     _ACTIVATION_CONTEXT * EntryPointActivationContext;

     PVOID PatchInformation;

     LIST_ENTRY ForwarderLinks;

     LIST_ENTRY ServiceTagLinks;

     LIST_ENTRY StaticLinks;

} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

 

 

这个结构体里面的PVOID DllBase;

     ULONG SizeOfImage;

这两个成员就表示模块大小和模块基地址,

在这个结构体的前三个成员表示的是DriverObject链表,可以访问到每一个驱动对象的该成员,即可以访问我们想要的驱动的模块基地址和模块大小。





脚注1:

摘自博客:http://blog.csdn.net/hu3167343/article/details/7612595

由于Windows需要支持多个CPU, 因此Windows内核中为此定义了一套以处理器控制区(Processor Control Region)KPCR为枢纽的数据结构, 使每个CPU都有个KPCR. 其中KPCR这个结构中有一个域KPRCB(Kernel Processor Control Block)结构, 这个结构扩展了KPCR. 这两个结构用来保存与线程切换相关的全局信息

通常fs段寄存器在内核模式下指向KPCR, 用户模式下指向TEB.


所有工作都做完了,然后奉上代码。


#pragma once
#include 

typedef struct _LDR_DATA_TABLE_ENTRY
{
	LIST_ENTRY  InLoadOrderLinks;   // 加载顺序
	LIST_ENTRY  InMemoryOrderLinks; // 在内存中的顺序
	LIST_ENTRY  InInitializationOrderLinks; // 初始化顺序
	PVOID       DllBase;        // 模块的基地址
	PVOID       EntryPoint;     // DriverEntry
	ULONG32     SizeOfImage;    // 模块的大小
#ifdef _WIN64
	ULONG32   UnKnow0;         
#endif
	UNICODE_STRING FullDllName; // 包含路径的名称
	UNICODE_STRING BaseDllName; // 不包含路径的名称
	UINT32      Flags;
	UINT16      LoadCount;
	UINT16      TlsIndex;
	union 
	{
		LIST_ENTRY  HashLinks;
		struct
		{
			PVOID   SectionPointer;	
			UINT32  CheckSum;
		};
		
	};
	union
	{
		UINT32      TimeDateStamp;
		PVOID       LoadedImports;
	};
	PVOID        EntryPointActivationContext; // _ACTIVATION_CONTEXT*
	PVOID        PatchInformation;
	LIST_ENTRY   ForwarderLinks;
	LIST_ENTRY   ServiceTagLinks;
	LIST_ENTRY   StaticLinks;
	PVOID        ContextInformation;
	ULONG_PTR    OriginalBase;
	LARGE_INTEGER  LoadTime;
}LDR_DATA_TABLE_ENTRY,*PLDR_DATA_TABLE_ENTRY;


typedef struct _KTIMER_TABLE_ENTRY
{
	ULONG_PTR         Lock;
	LIST_ENTRY      Entry;
	ULARGE_INTEGER  Time;
}KTIMER_TABLE_ENTRY,*PKTIMER_TABLE_ENTRY;

VOID DriverUnload(PDRIVER_OBJECT DriverObject);
BOOLEAN GetKernelModuleInfoByDriverObjectInWin7(PDRIVER_OBJECT DriverObject, WCHAR* ModuleName, PVOID* ModuleBase, ULONG_PTR* ModuleSize);
BOOLEAN GetKiWaitVariableAddress(PULONG64* KiWaitNever, PULONG64* KiWaitAlways);
PVOID GetExportVariableAddressFromNtosExportTableByVariableName(WCHAR * VariableName);
KDPC* TransTimerDPCEx(PKTIMER Timer, ULONG64 KiWaitNever, ULONG64 KiWaitAlways);
BOOLEAN GetDpcTimerInfoByKernelModuleInWin7_x64(PKTIMER* Timer, PVOID KernelModuleBase, ULONG64 KernelModuleSize);
BOOLEAN GetDpcTimerInfoByKernelModuleInWin7_x86(PKTIMER* Timer, PVOID KernelModuleBase, ULONG32 KernelModuleSize);
BOOLEAN RemoveDpcInKernelModuleInWin7(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize);






*********************************************************************************
.c文件

#include "RemoveDPC.h"


PVOID      __KernelModuleBase = NULL;
ULONG_PTR  __KernelModuleSize = 0;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegisterPath)
{
	NTSTATUS   Status = STATUS_UNSUCCESSFUL;
	WCHAR      ModuleName[] = L"DPCHookSSDTx86.sys";


	DriverObject->DriverUnload = DriverUnload;
	if (GetKernelModuleInfoByDriverObjectInWin7(DriverObject,ModuleName,&__KernelModuleBase,&__KernelModuleSize) == FALSE)
	{	
		return Status;
	}
	DbgPrint("ModuleBase is %p \r\n", __KernelModuleBase);
	DbgPrint("ModuleSize is %p \r\n", __KernelModuleSize);

	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("ModuleBase is %p \r\n", *ModuleBase);
				DbgPrint("ModuleSize 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函数都合法
					if (MmIsAddressValid(RealDpc) && MmIsAddressValid(KTimer) && MmIsAddressValid(RealDpc->DeferredRoutine))
					{
						if ((ULONG64)KTimer >= (ULONG64)KernelModuleBase && (ULONG64)KTimer <= (ULONG64)KernelModuleBase + (ULONG64)KernelModuleSize)
						{
							*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;
}

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;
}






你可能感兴趣的:(驱动)