最近学了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(- * 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, , 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;
}

KeSetTimer

下面是我完整的DPCHookSSDT代码。

 #include <ntifs.h>

 #define SEC_IMAGE  0x001000000

 extern
PIMAGE_NT_HEADERS
NTAPI
RtlImageNtHeader(PVOID BaseAddress); extern
char* PsGetProcessImageFileName(PEPROCESS EProcess); typedef
NTSTATUS(*pfnNtOpenProcess)(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
); typedef struct MY_TIMER_
{
KDPC Dpc;
KTIMER Timer;
PKDEFERRED_ROUTINE func;
PVOID private_context;
}MY_TIMER, *PMY_TIMER; typedef struct _SYSTEM_SERVICE_DESCRIPTOR_TABLE_
{
PVOID ServiceTableBase; //SSDT(System Service Dispatch Table)服务表的基地址
PVOID ServiceCounterTableBase; //用于checked builds,包含SSDT中每个服务被调用的次数
PVOID NumberOfServices; //服务函数的个数,NumberOfService * 4就是整个地址表的大小 64位 0x191, 32位 0x11c
PVOID ParamTableBase; //SSPT(System Service Parameter Table)的基地址
}SYSTEM_SERVICE_DESCRIPTOR_TABLE, *PSYSTEM_SERVICE_DESCRIPTOR_TABLE; BOOLEAN GetSSDTAddressInWin7_x64(ULONG64* SSDTAddress);
BOOLEAN GetSSDTAddressInWin7_x86(ULONG32* SSDTAddress);
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
BOOLEAN GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(CHAR* szFuntionName, ULONG32* SSDTFunctionIndex);
PVOID
GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName);
BOOLEAN Ring0MappingPEFile(WCHAR* szFileFullPath, PVOID* MappingBaseAddress, ULONG_PTR* MappingViewSize);
ULONG32 CalcFunctionOffsetInSSDT(PVOID ServiceTableBase, PVOID FunctionAddress, ULONG32 ParamterCount);
VOID HookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, PVOID MyFunctionAddress,
PVOID OldFuntionAddress, ULONG32 OldFunctionParamterCount, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength);
VOID HookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 MyFunctionAddress);
VOID InlineHook(ULONG64 OldFuntionAddress, ULONG64 MyFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength);
NTSTATUS MyOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
);
VOID WPOFF();
VOID WPON(); VOID UnHookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionOffset, PVOID OldFunctionAddress,
UCHAR* OldFunctionCode, ULONG32 PatchCodeLength);
VOID UnHookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionAddress);
VOID UnlineHook(PVOID OldFunctionAddress, CHAR* OldFunctionCode, ULONG32 PatchCodeLength); VOID MyTimerInit(PMY_TIMER Timer, PKDEFERRED_ROUTINE Func); BOOLEAN MyTimerSet(PMY_TIMER Timer, LONG Ms, PVOID context); VOID CustomDpc(
_In_ struct _KDPC *Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
);

DPCHookSSDT.h

 #include "NewDPCHookSSDT.h"
#include <ntimage.h>
#include <Ntstrsafe.h> //bp NewDPCHookSSDT!DriverEntry
ULONG32 __NtOpenProcessIndex = ;
PVOID __ServiceTableBase = NULL;
ULONG32 __OldNtOpenProcessOffset = ;
pfnNtOpenProcess __OldNtOpenProcessAddress = NULL; //这里无所谓,PVOID也可以
BOOLEAN __IsHook = FALSE;
UCHAR __OldCode[] = { }; MY_TIMER MyTimer; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
char szFunctionName[] = "ZwOpenProcess";
ULONG_PTR SSDTAddress = ; //保存整个SSDT基地址 KeServiceDescriptorTable
ULONG32 TempOffset = ;
DriverObject->DriverUnload = DriverUnload; #ifdef _WIN64
if (GetSSDTAddressInWin7_x64(&SSDTAddress) == FALSE)
{
return Status;
}
#else
if (GetSSDTAddressInWin7_x86(&SSDTAddress) == FALSE)
{
return Status;
}
#endif DbgPrint("SSDT:%p\r\n", SSDTAddress); //0x23h
if (GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(szFunctionName, &__NtOpenProcessIndex) == FALSE)
{
return Status;
} __ServiceTableBase = ((PSYSTEM_SERVICE_DESCRIPTOR_TABLE)SSDTAddress)->ServiceTableBase;
#ifdef _WIN64
__OldNtOpenProcessOffset = ((PULONG32)__ServiceTableBase)[__NtOpenProcessIndex];
TempOffset = __OldNtOpenProcessOffset >> ; //抹掉最右边的一位
__OldNtOpenProcessAddress = (PVOID)((ULONG64)__ServiceTableBase + TempOffset);
HookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, MyOpenProcess, KeBugCheckEx, , __OldCode, );
#else
__OldNtOpenProcessAddress = (pfnNtOpenProcess)(((PULONG32)__ServiceTableBase)[__NtOpenProcessIndex]);
/*kd > u 0x84016ba1
nt!NtOpenProcess:
84016ba1 8bff mov edi, edi
84016ba3 55 push ebp
84016ba4 8bec mov ebp, esp
84016ba6 51 push ecx
84016ba7 51 push ecx
84016ba8 64a124010000 mov eax, dword ptr fs : [00000124h]
84016bae 8a803a010000 mov al, byte ptr[eax + 13Ah]
84016bb4 8b4d14 mov ecx, dword ptr[ebp + 14h]*/ //0x00145dc8 HookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, (ULONG32)MyOpenProcess);//强制转换后,MyOpenProcess才可以传,真奇怪
#endif
MyTimerInit(&MyTimer, (PKDEFERRED_ROUTINE)CustomDpc);
MyTimerSet(&MyTimer, , NULL);
return STATUS_SUCCESS;
} 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(- * Ms); //100ns每unit 1s 负数代表相对时间
Timer->private_context = context; //传一块上下背景文,便于利用
return KeSetTimer(&(Timer->Timer), Interval, &(Timer->Dpc));
// 定时器 延后执行的时间 要执行的回调函数结构
//return KeSetTimerEx(&(Timer->Timer), Interval,1000, &(Timer->Dpc));
} VOID CustomDpc(
_In_ struct _KDPC *Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
)
{
PMY_TIMER Timer = (PMY_TIMER)DeferredContext;
PVOID MyContext = Timer->private_context;
DbgPrint("CustomDpc()\r\n");
#ifdef _WIN64
HookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, MyOpenProcess, KeBugCheckEx, , __OldCode, );
#else
HookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, (ULONG32)MyOpenProcess);//强制转换后,MyOpenProcess才可以传,真奇怪
#endif // _WIN64
MyTimerSet(Timer, , MyContext);
//如果前面是KeSetTimerEx函数,这里就不用写MyTimerSet啦
} VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("ByeBye,Driver!\r\n"); KeCancelTimer(&(MyTimer.Timer)); if (__IsHook)
{
#ifdef _WIN64 UnHookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, __OldNtOpenProcessOffset, KeBugCheckEx,
__OldCode, );
#else
UnHookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, __OldNtOpenProcessAddress);
#endif
__IsHook = FALSE;
}
} BOOLEAN GetSSDTAddressInWin7_x64(ULONG64* SSDTAddress)
{
//rdmsr c0000082
CHAR* StartSearchAddress = (char*)__readmsr(0xc0000082);
CHAR* EndSearchAddress = StartSearchAddress + PAGE_SIZE; //4KB
CHAR* i = NULL;
UCHAR v1 = , v2 = , v3 = ;
//必须用UCHAR类型,因为Char的范围是 -128~127 0x80-0x7F
INT64 Offset = ;
*SSDTAddress = NULL;
for (i = StartSearchAddress; i < EndSearchAddress; i++)
{
if (MmIsAddressValid(i) && MmIsAddressValid(i + ) && MmIsAddressValid(i + ))
{
v1 = *i;
v2 = *(i + );
v3 = *(i + );
if (v1 == 0x4c && v2 == 0x8d && v3 == 0x15)
{
// 4c8d15238f4700
memcpy(&Offset, i+, );
*SSDTAddress = Offset + (ULONG64)i+;
break;
}
}
}
if (*SSDTAddress == NULL)
{
return FALSE;
}
return TRUE;
} BOOLEAN GetSSDTAddressInWin7_x86(ULONG32* SSDTAddress)
{
//32位下Ntoskrnl.exe有导出 KeServiceDescriptorTable,直接查找即可
*SSDTAddress = NULL;
*SSDTAddress = (ULONG32)GetExportVariableAddressFromNtosKrnlExportTableByVariableName(L"KeServiceDescriptorTable");
if (*SSDTAddress != NULL)
{
return TRUE;
}
return FALSE;
}
PVOID
GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName)
{
//通过导出变量名字从NtosKrnl中获得导出变量地址
UNICODE_STRING uniVariableName;
PVOID VariableAddress = NULL; if (wzVariableName&&wcslen(wzVariableName) > )
{
RtlUnicodeStringInit(&uniVariableName, wzVariableName);
//从Ntos模块的导出表中获得导出变量的地址
VariableAddress = MmGetSystemRoutineAddress(&uniVariableName);
}
return VariableAddress;
}
//从内存中读出
BOOLEAN GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(CHAR* szFuntionName, ULONG32* SSDTFunctionIndex)
{
WCHAR szFileFullPath[] = L"\\SystemRoot\\System32\\ntdll.dll"; //C:\Windows\
ULONG_PTR MappingViewSize = ;
PVOID MappingBaseAddress = ;
BOOLEAN IsOk = FALSE;
PIMAGE_NT_HEADERS Image_Nt_Headers = NULL;
PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL; //导出表
//因为不识别DWORD,所以用UINT32
UINT32* AddressOfFunctions = NULL;
UINT32* AddressOfNames = NULL;
UINT16* AddressOfNameOrdinals = NULL;
int i = ;
char* v1 = NULL;
ULONG32 OrdinalOfFunction = ;
PVOID AddressOfFunction = ;
#ifdef _WIN64
/*
0:004> u zwopenprocess
ntdll!NtOpenProcess:
00000000`774ddc10 4c8bd1 mov r10,rcx
00000000`774ddc13 b823000000 mov eax,23h
00000000`774ddc18 0f05 syscall
00000000`774ddc1a c3 ret
00000000`774ddc1b 0f1f440000 nop dword ptr [rax+rax]
*/
ULONG32 Offset_SSDTFunctionIndex = ;
#else
/*
0:004> u zwopenprocess
ntdll!NtOpenProcess:
00000000`774ddc10 4c8bd1 mov r10,rcx
00000000`774ddc13 b823000000 mov eax,23h
00000000`774ddc18 0f05 syscall
00000000`774ddc1a c3 ret
00000000`774ddc1b 0f1f440000 nop dword ptr [rax+rax]
*/
ULONG32 Offset_SSDTFunctionIndex = ;
#endif *SSDTFunctionIndex = -;
IsOk = Ring0MappingPEFile(szFileFullPath, &MappingBaseAddress, &MappingViewSize);
if (IsOk == FALSE)
{
return FALSE;
}
else
{
__try {
Image_Nt_Headers = RtlImageNtHeader(MappingBaseAddress); //extern进来
if (Image_Nt_Headers&&Image_Nt_Headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
{
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((char*)MappingBaseAddress
+ Image_Nt_Headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); AddressOfFunctions = (UINT32*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfFunctions);
//函数的地址(RVA)
AddressOfNames = (UINT32*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfNames);;
//函数名字的地址(RVA)
AddressOfNameOrdinals = (UINT16*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfNameOrdinals);
//有名字的序号的首地址(RVA)
for (i = ; i < ImageExportDirectory->NumberOfNames; i++)
{
v1 = (char*)((char*)MappingBaseAddress + AddressOfNames[i]); //可以这样写 ImageExportDirectory->AddressOfNames + i * 4
if (_stricmp(szFuntionName, v1) == )
{
OrdinalOfFunction = AddressOfNameOrdinals[i];
AddressOfFunction = (PVOID)((char*)MappingBaseAddress + AddressOfFunctions[OrdinalOfFunction]);//AddressOfFunctions[AddressOfNameOrdinals[i]]
*SSDTFunctionIndex = *(ULONG32*)((char*)AddressOfFunction + Offset_SSDTFunctionIndex);
break;
}
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{} }
ZwUnmapViewOfSection(NtCurrentProcess(), MappingBaseAddress);
if (*SSDTFunctionIndex == -)
{
return FALSE;
}
return TRUE;
}
//函数需要,所以传双字 二维指针
BOOLEAN Ring0MappingPEFile(WCHAR* szFileFullPath, PVOID* MappingBaseAddress, ULONG_PTR* MappingViewSize)
{
NTSTATUS Status;
UNICODE_STRING Temp;
OBJECT_ATTRIBUTES oa;
HANDLE hFile = NULL;
HANDLE hSection = NULL;
IO_STATUS_BLOCK IoStatusBlock; if (!szFileFullPath&&MmIsAddressValid(szFileFullPath))
{
return FALSE;
}
if (!MappingBaseAddress&&MmIsAddressValid(MappingBaseAddress))
{
return FALSE;
}
RtlUnicodeStringInit(&Temp, szFileFullPath);
InitializeObjectAttributes(&oa, //out
&Temp, //in ObjectName
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,//被系统在和ObjectName比较匹配时不区分大小写||此句柄只能用于内核模式下
NULL, //与ObjectName参数匹配的根目录对象,如果ObjectName是对象的全路径则设置此参数为NULL
NULL //驱动程序可以使用NULL用于创建一个具有默认安全描述符的对象
);
//打开文件,获得文件句柄
Status = IoCreateFile(&hFile,
GENERIC_READ | SYNCHRONIZE, //可读|同步
&oa,
&IoStatusBlock, //返回I/O请求的完成情况
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
,
CreateFileTypeNone,
NULL,
IO_NO_PARAMETER_CHECKING
);
if (!NT_SUCCESS(Status))
{
return FALSE;
}
oa.ObjectName = NULL;
Status = ZwCreateSection(&hSection,
SECTION_QUERY | SECTION_MAP_READ,
&oa,
NULL,
PAGE_WRITECOPY,
SEC_IMAGE,//内存按1M对齐 0x1000 000
hFile
);
ZwClose(hFile);
if (!NT_SUCCESS(Status))
{
return FALSE;
}
Status = ZwMapViewOfSection(hSection,
NtCurrentProcess(),
MappingBaseAddress,
,
,
,
MappingViewSize,
ViewUnmap, //不能被映射到子进程中
,
PAGE_WRITECOPY
);
ZwClose(hSection);
if (!NT_SUCCESS(Status))
{
return FALSE;
}
return TRUE;
} ULONG32 CalcFunctionOffsetInSSDT(PVOID ServiceTableBase, PVOID FunctionAddress, ULONG32 ParamterCount)
{
//完全没必要像下面这样折腾,就是一个做个SSDT表中类似的地址,有时间自己写一个
ULONG32 Offset = ;
CHAR Temp = ;
CHAR Bits[] = { };
int i = ;
Offset = (ULONG32)((ULONG64)FunctionAddress - (ULONG64)ServiceTableBase);
Offset = Offset << ;
if (ParamterCount > )
{
ParamterCount = ParamterCount-; //NtReadFile 9个参数 和参数压栈有关
}
else
{
ParamterCount = ;
}
memcpy(&Temp, &Offset, ); // 1010 0010 <<4-----> 0010 0000 后面处理参数
#define SETBIT(x,y) x|=(1<<y) //将X的第Y位置1
#define CLRBIT(x,y) x&=~(1<<y) //将X的第Y位清0
#define GETBIT(x,y) (x & (1 << y)) //取X的第Y位,返回0或非0 for (i = ; i < ; i++)
{
Bits[i] = GETBIT(ParamterCount, i);
if (Bits[i])
{
SETBIT(Temp, i);
}
else
{
CLRBIT(Temp, i);
}
}
memcpy(&Offset, &Temp, );
return Offset;
} //write protect 第17位
VOID WPOFF()
{
_disable();
__writecr0(__readcr0() & (~(0x10000))); } VOID WPON()
{
__writecr0(__readcr0() ^ 0x10000);
_enable();
} VOID HookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, PVOID MyFunctionAddress,
PVOID OldFuntionAddress, ULONG32 OldFunctionParamterCount, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength)
{
ULONG32 MyOffset = ;
WPOFF();
InlineHook(OldFuntionAddress, MyFunctionAddress, OldFunctionCode, PatchCodeLength);
WPON(); MyOffset = CalcFunctionOffsetInSSDT(ServiceTableBase, OldFuntionAddress, OldFunctionParamterCount);
WPOFF();
((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = MyOffset;
WPON(); __IsHook = TRUE;
} VOID HookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 MyFunctionAddress)
{
WPOFF();
((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)MyFunctionAddress;
WPON(); __IsHook = TRUE;
} VOID InlineHook(ULONG64 OldFuntionAddress, ULONG64 MyFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength)
{
CHAR PatchCode[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; //神坑啊,脑残啊,写成PatchCode 到memcpy才崩溃
ULONG64 Temp = MyFunctionAddress;
memcpy(OldFunctionCode, (PVOID)OldFuntionAddress, PatchCodeLength); //保存原来的函数指令
memcpy(PatchCode + , &Temp, );
memset((PVOID)OldFuntionAddress, 0x90, PatchCodeLength); //先全部打上NOP(计组)
memcpy((PVOID)OldFuntionAddress, PatchCode, );
} //SSDT HOOK的替换函数如何对访问进行过滤”比“如何实现SSDT HOOK”要复杂多了,详见ediary HookNtOpenProcess后的事情
NTSTATUS MyOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
)
{
//EnumProcessByForce.exe
//调用OpenProcess---->去NtDll导出表中寻找ntdll!NtOpenProcess或者ntdll!ZwOpenProcess 导出表有名称的索引---->切入内核-->跳板nt!ZwOpenProcess Ntoskernl.exe(NtOpenProcess mov eax,23h SSDT[23h])
// ---->KeCheckBugEx ---->Jmp MyOpenProcess
PEPROCESS EProcess = PsGetCurrentProcess(); //EnumProcessByForce.exe
if (EProcess != NULL && MmIsAddressValid(EProcess))
{
char *szProcessImageFileName = PsGetProcessImageFileName(EProcess);
if (strstr(szProcessImageFileName, "EnumProcess") != )
{
return STATUS_ACCESS_DENIED; //黑名单中返回
}
}
__OldNtOpenProcessAddress(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); //白名单
//OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, i)
//return STATUS_SUCCESS; 死循环
//return STATUS_UNSUCCESSFUL; 阻塞 很有趣
} VOID UnHookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionOffset, PVOID OldFunctionAddress,
UCHAR* OldFunctionCode, ULONG32 PatchCodeLength)
{
WPOFF();
UnlineHook(OldFunctionAddress, OldFunctionCode, PatchCodeLength);
WPON(); WPOFF();
((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)OldFunctionOffset;
WPON();
} VOID UnlineHook(PVOID OldFunctionAddress, CHAR* OldFunctionCode, ULONG32 PatchCodeLength)
{
memcpy((PVOID)OldFunctionAddress, OldFunctionCode, PatchCodeLength);
} VOID UnHookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionAddress)
{
WPOFF();
((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)OldFunctionAddress;
WPON();
}

DPCHookSSDT.c

2. 接下来就是RemoveDPC了,首先我们要找到DPC,关于Win7以前的版本可以参考这份博客,讲解的很详细,http://bbs.pediy.com/thread-148135.htm

win7下定时器链表被放在KPRCB结构中,64位下,在KPRCB+0x2200的位置:

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

相关结构:

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

所以大体流程就是:取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

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

然后查看KdVersionBlock结构,最下面的便是KdDebuggerData64结构。

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

从wrk上查看KdDebuggerData64结构体的定义,发现KiProcessorBlock在0x218h的偏移处。

 DO NOT ADD OR REMOVE FIELDS FROM THE MIDDLE OF THIS STRUCTURE!!!
//
// If you remove a field, replace it with an "unused" placeholder.
// Do not reuse fields until there has been enough time for old debuggers
// and extensions to age out.
//
typedef struct _KDDEBUGGER_DATA64 { DBGKD_DEBUG_DATA_HEADER64 Header; //
// Base address of kernel image
// ULONG64 KernBase; //
// DbgBreakPointWithStatus is a function which takes an argument
// and hits a breakpoint. This field contains the address of the
// breakpoint instruction. When the debugger sees a breakpoint
// at this address, it may retrieve the argument from the first
// argument register, or on x86 the eax register.
// ULONG64 BreakpointWithStatus; // address of breakpoint //
// Address of the saved context record during a bugcheck
//
// N.B. This is an automatic in KeBugcheckEx's frame, and
// is only valid after a bugcheck.
// ULONG64 SavedContext; //
// help for walking stacks with user callbacks:
// //
// The address of the thread structure is provided in the
// WAIT_STATE_CHANGE packet. This is the offset from the base of
// the thread structure to the pointer to the kernel stack frame
// for the currently active usermode callback.
// USHORT ThCallbackStack; // offset in thread data //
// these values are offsets into that frame:
// USHORT NextCallback; // saved pointer to next callback frame
USHORT FramePointer; // saved frame pointer //
// pad to a quad boundary
//
USHORT PaeEnabled:; //
// Address of the kernel callout routine.
// ULONG64 KiCallUserMode; // kernel routine //
// Address of the usermode entry point for callbacks.
// ULONG64 KeUserCallbackDispatcher; // address in ntdll //
// Addresses of various kernel data structures and lists
// that are of interest to the kernel debugger.
// ULONG64 PsLoadedModuleList;
ULONG64 PsActiveProcessHead;
ULONG64 PspCidTable; ULONG64 ExpSystemResourcesList;
ULONG64 ExpPagedPoolDescriptor;
ULONG64 ExpNumberOfPagedPools; ULONG64 KeTimeIncrement;
ULONG64 KeBugCheckCallbackListHead;
ULONG64 KiBugcheckData; ULONG64 IopErrorLogListHead; ULONG64 ObpRootDirectoryObject;
ULONG64 ObpTypeObjectType; ULONG64 MmSystemCacheStart;
ULONG64 MmSystemCacheEnd;
ULONG64 MmSystemCacheWs; ULONG64 MmPfnDatabase;
ULONG64 MmSystemPtesStart;
ULONG64 MmSystemPtesEnd;
ULONG64 MmSubsectionBase;
ULONG64 MmNumberOfPagingFiles; ULONG64 MmLowestPhysicalPage;
ULONG64 MmHighestPhysicalPage;
ULONG64 MmNumberOfPhysicalPages; ULONG64 MmMaximumNonPagedPoolInBytes;
ULONG64 MmNonPagedSystemStart;
ULONG64 MmNonPagedPoolStart;
ULONG64 MmNonPagedPoolEnd; ULONG64 MmPagedPoolStart;
ULONG64 MmPagedPoolEnd;
ULONG64 MmPagedPoolInformation;
ULONG64 MmPageSize; ULONG64 MmSizeOfPagedPoolInBytes; ULONG64 MmTotalCommitLimit;
ULONG64 MmTotalCommittedPages;
ULONG64 MmSharedCommit;
ULONG64 MmDriverCommit;
ULONG64 MmProcessCommit;
ULONG64 MmPagedPoolCommit;
ULONG64 MmExtendedCommit; ULONG64 MmZeroedPageListHead;
ULONG64 MmFreePageListHead;
ULONG64 MmStandbyPageListHead;
ULONG64 MmModifiedPageListHead;
ULONG64 MmModifiedNoWritePageListHead;
ULONG64 MmAvailablePages;
ULONG64 MmResidentAvailablePages; ULONG64 PoolTrackTable;
ULONG64 NonPagedPoolDescriptor; ULONG64 MmHighestUserAddress;
ULONG64 MmSystemRangeStart;
ULONG64 MmUserProbeAddress; ULONG64 KdPrintCircularBuffer;
ULONG64 KdPrintCircularBufferEnd;
ULONG64 KdPrintWritePointer;
ULONG64 KdPrintRolloverCount; ULONG64 MmLoadedUserImageList; // NT 5.1 Addition ULONG64 NtBuildLab;
ULONG64 KiNormalSystemCall; // NT 5.0 QFE addition ULONG64 KiProcessorBlock;
ULONG64 MmUnloadedDrivers;
ULONG64 MmLastUnloadedDriver;
ULONG64 MmTriageActionTaken;
ULONG64 MmSpecialPoolTag;
ULONG64 KernelVerifier;
ULONG64 MmVerifierData;
ULONG64 MmAllocatedNonPagedPool;
ULONG64 MmPeakCommitment;
ULONG64 MmTotalCommitLimitMaximum;
ULONG64 CmNtCSDVersion; // NT 5.1 Addition ULONG64 MmPhysicalMemoryBlock;
ULONG64 MmSessionBase;
ULONG64 MmSessionSize;
ULONG64 MmSystemParentTablePage; // Server 2003 addition ULONG64 MmVirtualTranslationBase; USHORT OffsetKThreadNextProcessor;
USHORT OffsetKThreadTeb;
USHORT OffsetKThreadKernelStack;
USHORT OffsetKThreadInitialStack; USHORT OffsetKThreadApcProcess;
USHORT OffsetKThreadState;
USHORT OffsetKThreadBStore;
USHORT OffsetKThreadBStoreLimit; USHORT SizeEProcess;
USHORT OffsetEprocessPeb;
USHORT OffsetEprocessParentCID;
USHORT OffsetEprocessDirectoryTableBase; USHORT SizePrcb;
USHORT OffsetPrcbDpcRoutine;
USHORT OffsetPrcbCurrentThread;
USHORT OffsetPrcbMhz; USHORT OffsetPrcbCpuType;
USHORT OffsetPrcbVendorString;
USHORT OffsetPrcbProcStateContext;
USHORT OffsetPrcbNumber; USHORT SizeEThread; ULONG64 KdPrintCircularBufferPtr;
ULONG64 KdPrintBufferSize; ULONG64 KeLoaderBlock; USHORT SizePcr;
USHORT OffsetPcrSelfPcr;
USHORT OffsetPcrCurrentPrcb;
USHORT OffsetPcrContainedPrcb; USHORT OffsetPcrInitialBStore;
USHORT OffsetPcrBStoreLimit;
USHORT OffsetPcrInitialStack;
USHORT OffsetPcrStackLimit; USHORT OffsetPrcbPcrPage;
USHORT OffsetPrcbProcStateSpecialReg;
USHORT GdtR0Code;
USHORT GdtR0Data; USHORT GdtR0Pcr;
USHORT GdtR3Code;
USHORT GdtR3Data;
USHORT GdtR3Teb; USHORT GdtLdt;
USHORT GdtTss;
USHORT Gdt64R3CmCode;
USHORT Gdt64R3CmTeb; ULONG64 IopNumTriageDumpDataBlocks;
ULONG64 IopTriageDumpDataBlocks; } KDDEBUGGER_DATA64, *PKDDEBUGGER_DATA64;

KdDebuggerData64

相应的代码:(通常在内核模式下段寄存器FS指向的是KPCR结构,用户模式下指向的是TEB结构)

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

而我调试64位的时候发现,KdVersionBlock都为null,那我们就不能用上面的方法去得KPRCB了,

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

不过我们却发现,在msr寄存器的0xC0000101处,存的是KPCR,我们直接_KPCR+偏移0x20就得到KPRCB了。

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

结构体中的Self是自己的地址,CurrentPrcb是KPRCB的地址。

最后回到正题,我们通过遍历KTIMER_TABLE.TimerEntries.Entry链表,利用CONTAINING_RECORD得到KTIMER结构,判断它的地址是否在我们sys模块地址范围内即可。最后KeCancelTimer掉它。

浅谈DPCHookSSDT和RemoveDPC-LMLPHP

(第一个地址是KTIMER,第二个地址是sys的基地址,判断是否落入范围)

代码如下:

 #include <ntifs.h>

 typedef struct _KTIMER_TABLE_ENTRY
{
ULONG_PTR Lock;
LIST_ENTRY Entry;
ULARGE_INTEGER Time;
} KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY; typedef struct _LDR_DATA_TABLE_ENTRY64
{
LIST_ENTRY64 InLoadOrderLinks;
LIST_ENTRY64 InMemoryOrderLinks;
LIST_ENTRY64 InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
PVOID SectionPointer;
ULONG CheckSum;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY64 ForwarderLinks;
LIST_ENTRY64 ServiceTagLinks;
LIST_ENTRY64 StaticLinks;
PVOID ContextInformation;
ULONG64 OriginalBase;
LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64; typedef struct _LDR_DATA_TABLE_ENTRY32
{
LIST_ENTRY32 InLoadOrderLinks;
LIST_ENTRY32 InMemoryOrderLinks;
LIST_ENTRY32 InInitializationOrderLinks;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING32 FullDllName;
UNICODE_STRING32 BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union {
LIST_ENTRY32 HashLinks;
struct {
ULONG SectionPointer;
ULONG CheckSum;
};
};
union {
struct {
ULONG TimeDateStamp;
};
struct {
ULONG LoadedImports;
};
};
} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32; #ifdef _WIN64
#define LDR_DATA_TABLE_ENTRY LDR_DATA_TABLE_ENTRY64
#define PLDR_DATA_TABLE_ENTRY PLDR_DATA_TABLE_ENTRY64
#else
#define LDR_DATA_TABLE_ENTRY LDR_DATA_TABLE_ENTRY32
#define PLDR_DATA_TABLE_ENTRY PLDR_DATA_TABLE_ENTRY32
#endif VOID DriverUnload(PDRIVER_OBJECT DriverObject); NTSTATUS GetKernelModuleInformationByKernelModuleName(PDRIVER_OBJECT DriverObject, WCHAR* wzKernelModuleName, PVOID* KernelModuleBase, ULONG_PTR* KernelModuleSize); PVOID
GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName); BOOLEAN GetKiWaitVariableAddress(PULONG64* KiWaitNever, PULONG64* KiWaitAlways); KDPC* TransTimerDPCEx(PKTIMER Timer, ULONG64 KiWaitNever, ULONG64 KiWaitAlways); PULONG GetKiProcessorBlock(); BOOLEAN GetDPCTimerInfoByModuleInfo(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize, ULONG_PTR* Timer);
BOOLEAN RemoveDPCFromNtos(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize);

RemoveDPC.h

 #include "RemoveDPC.h"
#include <ntstrsafe.h>
//bp RemoveDPC!DriverEntry PVOID __KernelModuleBase = NULL;
ULONG_PTR __KernelModuleSize = ; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT DeviceObject = NULL;
DbgPrint("DriverSuccuss\r\n");
DriverObject->DriverUnload = DriverUnload; WCHAR wzKernelModuleName[] = L"NewDPCHookSSDT.sys";
if (GetKernelModuleInformationByKernelModuleName(DriverObject, wzKernelModuleName, &__KernelModuleBase, &__KernelModuleSize) == STATUS_UNSUCCESSFUL)
{
return STATUS_UNSUCCESSFUL;
}
RemoveDPCFromNtos(__KernelModuleBase, __KernelModuleSize);
return Status;
} NTSTATUS GetKernelModuleInformationByKernelModuleName(PDRIVER_OBJECT DriverObject,WCHAR* wzKernelModuleName,PVOID* KernelModuleBase,ULONG_PTR* KernelModuleSize)
{
PLDR_DATA_TABLE_ENTRY CurrentEntry = NULL;
PLDR_DATA_TABLE_ENTRY NextEntry = NULL;
if (DriverObject&&MmIsAddressValid(DriverObject))
{
CurrentEntry = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
NextEntry = (PLDR_DATA_TABLE_ENTRY)CurrentEntry->InLoadOrderLinks.Flink;
while (NextEntry != CurrentEntry)
{
if (NextEntry->BaseDllName.Length >= wcslen(wzKernelModuleName))
{
if (NextEntry->BaseDllName.Buffer
&&MmIsAddressValid((PVOID)NextEntry->BaseDllName.Buffer)
&& !_wcsnicmp(wzKernelModuleName, (WCHAR*)NextEntry->BaseDllName.Buffer, wcslen(wzKernelModuleName))) //这里掉坑了,wcsnicmp是比较函数,相等时返回0,
//再次掉坑,第三个参数表示比较的的字母的个数,如果是0,肯定相等
{
DbgPrint("%S\r\n", wzKernelModuleName);
DbgPrint("%S\r\n", (WCHAR*)NextEntry->BaseDllName.Buffer);
*KernelModuleBase = NextEntry->DllBase;
*KernelModuleSize = NextEntry->SizeOfImage;
return STATUS_SUCCESS;
}
}
NextEntry = (PLDR_DATA_TABLE_ENTRY)NextEntry->InLoadOrderLinks.Flink;
}
}
return STATUS_UNSUCCESSFUL;
} BOOLEAN RemoveDPCFromNtos(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize)
{
ULONG_PTR Timer = NULL;
if (GetDPCTimerInfoByModuleInfo(KernelModuleBase, KernelModuleSize, &Timer) == FALSE)
{
return FALSE;
}
if (Timer && MmIsAddressValid(Timer))
{
if (KeCancelTimer(Timer))
{
return TRUE;
}
}
return FALSE;
} PULONG GetKiProcessorBlock()
{
ULONG* KiProcessorBlock = ; KeSetSystemAffinityThread(); //使当前线程运行在第一个处理器上 _asm
{
push eax
mov eax, FS:[0x34]; 得到KdVersionBlock的地址
add eax, 20h; 得到指向DebuggerDataList的地址
mov eax, [eax]; 得到DebuggerDataList的地址
mov eax, [eax]; 取出里面的内容,即KdDebuggerData64结构
mov eax, [eax + 218h]; 取出KiProcessBlock的地址
mov KiProcessorBlock, eax;放到变量里
pop eax
} KeRevertToUserAffinityThread(); return KiProcessorBlock; } BOOLEAN GetDPCTimerInfoByModuleInfo(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize,ULONG_PTR* Timer)
{
ULONG32 NumberOfProcessors = KeNumberProcessors; //一般运行在第一个CPU上
ULONG_PTR KPRCB = ;
PUCHAR TimerEntries = NULL;
PULONG_PTR KiWaitNever = NULL;
PULONG_PTR KiWaitAlways = NULL;
PLIST_ENTRY CurrentEntry = NULL;
PLIST_ENTRY NextEntry = NULL;
PKTIMER MyTimer = NULL;
int i = ;
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
#ifdef _WIN64 KeSetSystemAffinityThread(); //使当前线程运行在第一个CPU上
KPRCB = (ULONG64)__readmsr(0xC0000101) + 0x20; //msr(0xC0000101)处位置存的是KPCR
KeRevertToUserAffinityThread();
TimerEntries = (PUCHAR)(*(ULONG64*)KPRCB + 0x2200 + 0x200);
if (GetKiWaitVariableAddress(&KiWaitNever, &KiWaitAlways) == FALSE)
{
return FALSE;
}
for (i = ; i < 0x100; i++)
{
CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*i + );
NextEntry = CurrentEntry->Blink;
if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry))
{
while (NextEntry != CurrentEntry)
{
PKDPC RealDPC;
MyTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); RealDPC = TransTimerDPCEx(MyTimer, *KiWaitNever, *KiWaitAlways);
if (MmIsAddressValid(MyTimer) && MmIsAddressValid(RealDPC) && MmIsAddressValid(RealDPC->DeferredRoutine))
{
if ((ULONG64)MyTimer >= (ULONG64)KernelModuleBase && (ULONG64)MyTimer <= (ULONG64)KernelModuleBase + KernelModuleSize)
{
*Timer = (ULONG64)MyTimer;
KeLowerIrql(OldIrql);
return TRUE;
}
}
NextEntry = NextEntry->Blink;
}
}
}
#else
PULONG KiProcessorBlock = NULL;
KiProcessorBlock = (PULONG)GetKiProcessorBlock();
TimerEntries = (PUCHAR)(*KiProcessorBlock + 0x1960 + 0x40);
for (i = ; i < 0x100; i++)
{
CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*i + );
NextEntry = CurrentEntry->Blink;
if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry))
{
while (NextEntry != CurrentEntry)
{
MyTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); if (MmIsAddressValid(MyTimer) && MmIsAddressValid(MyTimer->Dpc) && MmIsAddressValid(MyTimer->Dpc->DeferredRoutine))
{
if ((ULONG32)MyTimer >= (ULONG32)KernelModuleBase && (ULONG32)MyTimer <= (ULONG32)KernelModuleBase + KernelModuleSize)
{
*Timer = (ULONG32)MyTimer;
KeLowerIrql(OldIrql);
return TRUE;
}
}
NextEntry = NextEntry->Blink;
}
}
} #endif 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`03ef10a8 4883ec38 sub rsp,38h
fffff800`03ef10ac 4c89442420 mov qword ptr [rsp+20h],r8
fffff800`03ef10b1 4533c9 xor r9d,r9d
fffff800`03ef10b4 4533c0 xor r8d,r8d
fffff800`03ef10b7 e814000000 call nt!KiSetTimerEx (fffff800`03ef10d0)
fffff800`03ef10bc 4883c438 add rsp,38h
fffff800`03ef10c0 c3 ret
fffff800`03ef10c1 90 nop
fffff800`03ef10c2 90 nop
fffff800`03ef10c3 90 nop
fffff800`03ef10c4 90 nop
fffff800`03ef10c5 90 nop
fffff800`03ef10c6 90 nop
fffff800`03ef10c7 90 nop
nt!KxWaitForLockChainValid:
fffff800`03ef10c8 90 nop
fffff800`03ef10c9 90 nop
fffff800`03ef10ca 90 nop
fffff800`03ef10cb 90 nop
fffff800`03ef10cc 90 nop
fffff800`03ef10cd 90 nop
fffff800`03ef10ce 90 nop
fffff800`03ef10cf 90 nop
nt!KiSetTimerEx:
fffff800`03ef10d0 48895c2408 mov qword ptr [rsp+8],rbx
fffff800`03ef10d5 4889542410 mov qword ptr [rsp+10h],rdx
fffff800`03ef10da 55 push rbp
fffff800`03ef10db 56 push rsi
fffff800`03ef10dc 57 push rdi
fffff800`03ef10dd 4154 push r12
fffff800`03ef10df 4155 push r13
fffff800`03ef10e1 4156 push r14
fffff800`03ef10e3 4157 push r15
fffff800`03ef10e5 4883ec50 sub rsp,50h
fffff800`03ef10e9 488b0518502200 mov rax,qword ptr [nt!KiWaitNever (fffff800`04116108)]
fffff800`03ef10f0 488b1de9502200 mov rbx,qword ptr [nt!KiWaitAlways (fffff800`041161e0)]
*/
ULONG64 KeSetTimer = ;
PUCHAR StartSearchAddress = ;
PUCHAR EndSearchAddress = ;
ULONG64 iOffset = ;
PUCHAR i = NULL;
KeSetTimer = (ULONG64)GetExportVariableAddressFromNtosKrnlExportTableByVariableName(L"KeSetTimer"); StartSearchAddress = (PUCHAR)KeSetTimer;
EndSearchAddress = StartSearchAddress + 0x500; for (i = StartSearchAddress; i < EndSearchAddress; i++)
{
if (*i == 0x48 && *(i + ) == 0x8B && *(i + ) == 0x05)
{
memcpy(&iOffset, i + , );
*KiWaitNever = (PULONG64)(iOffset + (ULONG64)i + );
i = i + ;
memcpy(&iOffset, i + , );
*KiWaitAlways = (PULONG64)(iOffset + (ULONG64)i + );
return TRUE;
}
} return FALSE;
} PVOID
GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName)
{
//通过导出变量名字从NtosKrnl中获得导出变量地址
UNICODE_STRING uniVariableName;
PVOID VariableAddress = NULL; if (wzVariableName&&wcslen(wzVariableName) > )
{
RtlUnicodeStringInit(&uniVariableName, wzVariableName);
//从Ntos模块的导出表中获得导出变量的地址
VariableAddress = MmGetSystemRoutineAddress(&uniVariableName);
}
return VariableAddress;
} VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("Unload Success\r\n");
}

RemoveDPC.c

That's all.

05-12 10:35