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这个数组存在结构体KPRCB的0x2200(x64)或0x1960(x86)处,而KPRCB结构体指针又存在于结构体KPCR的0x20处。KPCR结构体在64位中是在寄存器msr[c0000101]的位置,在32位中是在fs寄存器中。
关于KPRC等的解释可以看后面的脚注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中找到自己想要移除的
我们得到了所有的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;
}