介绍:
前几天写过一篇SSDT DPC Hook的文章,最近才想起来写一下如何来对抗SSDT DPC Hook,所以有了这篇文章,其实对抗原理也很简单,就是检测出来目标驱动创建的DPC,移除掉,然后进行普通的SSDT Hook 就可以了。
(一)
⚪获取目标驱动信息(模块基址和大小)
⚪获取DPC信息 判断目标驱动创建出的DPC
⚪移除DPC、销毁时钟
⚪卸载SSDT Hook
(二)
#pragma once
#include<fltKernel.h>
#include"SSDTHook.h"
#include"Common.h"
#include"SystemHelper.h"
#include<ntimage.h>
typedef struct _KTIMER_TABLE_ENTRY32 {
ULONG Lock;
LIST_ENTRY32 Entry;
ULARGE_INTEGER Time;
} KTIMER_TABLE_ENTRY32, *PKTIMER_TABLE_ENTRY32;
typedef struct _KTIMER_TABLE_ENTRY64 {
ULONG64 Lock;
LIST_ENTRY64 Entry;
ULARGE_INTEGER Time;
} KTIMER_TABLE_ENTRY64, *PKTIMER_TABLE_ENTRY64;
#ifdef _WIN64
#define KTIMER_TABLE_ENTRY KTIMER_TABLE_ENTRY64
#define PKTIMER_TABLE_ENTRY PKTIMER_TABLE_ENTRY64
#else
#define KTIMER_TABLE_ENTRY KTIMER_TABLE_ENTRY32
#define PKTIMER_TABLE_ENTRY PKTIMER_TABLE_ENTRY32
#endif // _WIN64
NTSTATUS DPC_SSDTHook_ANTI();
NTSTATUS GetDelayProcedureCallInfo(PKTIMER* KTimer, PVOID TargetModuleBase, ULONG TargetModuleSize);
#include"DPC-SSDTHook-ANTI.h"
extern PDRIVER_OBJECT __DriverObject;
NTSTATUS DPC_SSDTHook_ANTI()
{
/*
0: kd> dt _DRIVER_OBJECT 0x88bd26d8
nt!_DRIVER_OBJECT
+0x000 Type : 0n4
+0x002 Size : 0n168
+0x004 DeviceObject : 0x889cc118 _DEVICE_OBJECT
+0x008 Flags : 0x12
+0x00c DriverStart : 0x9d3f2000 Void
+0x010 DriverSize : 0x8000
+0x014 DriverSection : 0x88014c70 Void
+0x018 DriverExtension : 0x88bd2780 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING "\Driver\HookRing0"
+0x024 HardwareDatabase : 0x84b8c250 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"
+0x028 FastIoDispatch : (null)
+0x02c DriverInit : 0x9d3f8000 long HookRing0!GsDriverEntry+0
+0x030 DriverStartIo : (null)
+0x034 DriverUnload : 0x9d3f3740 void HookRing0!DriverUnload+0
+0x038 MajorFunction : [28] 0x9d3f33e0 long HookRing0!CreateThroughDispatch+0
*/
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PKTIMER KTimer = NULL;
PVOID TargetModuleBase = NULL;
ULONG TargetModuleSize = 0;
WCHAR TargetModuleName[] = L"HookRing0.sys";
//获取目标驱动信息
Status = GetKernelModuleInfoByDriverName(__DriverObject, TargetModuleName,
&TargetModuleBase, &TargetModuleSize);
if (!NT_SUCCESS(Status))
{
return Status;
}
//获取DPC信息 判断目标驱动创建出的DPC
Status = GetDelayProcedureCallInfo(&KTimer, TargetModuleBase, TargetModuleSize);
if (!NT_SUCCESS(Status))
{
return Status;
}
//移除DPC
//销毁时钟
if (KTimer != 0)
{
KeCancelTimer(KTimer);
KTimer = NULL;
}
//卸载SSDT Hook
Status = SSDTHook(FALSE);
return Status;
}
NTSTATUS GetDelayProcedureCallInfo(PKTIMER * KTimer, PVOID TargetModuleBase, ULONG TargetModuleSize)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PVOID KPRCB = NULL;
ULONG32 CurrentProcessorNumber = 0;
PROCESSOR_NUMBER ProcessorNumber = { 0 };
GROUP_AFFINITY Affinity = { 0 };
GROUP_AFFINITY PreviousAffinity = { 0 };
ULONG32 i = 0;
ULONG32 j = 0;
PKTIMER v2 = NULL;
PKDPC KDpc = NULL;
PUCHAR TimerEntries = NULL;
PLIST_ENTRY KTimerTableEntry1 = NULL;
PLIST_ENTRY KTimerTableEntry2 = NULL;
KIRQL OldIrql = 0;
//提高IRQL
CurrentProcessorNumber = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
for (i = 0; i < CurrentProcessorNumber; i++)
{
Status = KeGetProcessorNumberFromIndex(i, &ProcessorNumber);
if (!NT_SUCCESS(Status))
{
return Status;
}
//切换当前处理器
Affinity.Group = ProcessorNumber.Group;
Affinity.Mask = 1ull << ProcessorNumber.Number;
//切换当前线程到指定的CPU内核中去
KeSetSystemGroupAffinityThread(&Affinity, &PreviousAffinity);
__asm
{
push eax
mov eax, fs:[20h]; //得到_KPRCB的地址
mov KPRCB, eax
pop eax
}
KeRevertToUserGroupAffinityThread(&PreviousAffinity);
OldIrql = KeRaiseIrqlToDpcLevel();
TimerEntries = ((PUCHAR)KPRCB + 0x1960 + 0x40);
/*
nt!_KPRCB
+0x1960 TimerTable : _KTIMER_TABLE
0: kd> dt!_KTIMER_TABLE
nt!_KTIMER_TABLE
+0x000 TimerExpiry : [16] Ptr32 _KTIMER
+0x040 TimerEntries : [256] _KTIMER_TABLE_ENTRY
*/
//因为_KTIMER_TABLE 是一个256个结构的数组
for (j = 0;j < 0x100;j++)
{
KTimerTableEntry1 = KTimerTableEntry2 =(PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*j + sizeof(ULONG_PTR));
//获取前一个
if (MmIsAddressValid(KTimerTableEntry1) && MmIsAddressValid(KTimerTableEntry2))
{
do
{
v2 = CONTAINING_RECORD(KTimerTableEntry2, KTIMER, TimerListEntry);
KDpc = v2->Dpc;
if (MmIsAddressValid(KDpc) && MmIsAddressValid(v2) && MmIsAddressValid(KDpc->DeferredRoutine))
{
//判断该DPC是否有效
if (((ULONG_PTR)v2 >= (ULONG_PTR)TargetModuleBase) &&
((ULONG_PTR)v2 <= ((ULONG_PTR)TargetModuleBase + (ULONG_PTR)TargetModuleSize)))
{
*KTimer = v2;
KeLowerIrql(OldIrql);
return STATUS_SUCCESS;
}
}
KTimerTableEntry2 = KTimerTableEntry2->Blink;
} while (KTimerTableEntry1 != KTimerTableEntry2);
}
}
KeLowerIrql(OldIrql);
}
return Status;
}
NTSTATUS GetKernelModuleInfoByDriverName(PDRIVER_OBJECT DriverObject, WCHAR* TargetModuleName,
PVOID* TargetModuleBase, ULONG* TargetModuleSize)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG32 ModuleNameLength = 0;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry1 = NULL;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry2 = NULL;
if (DriverObject)
{
ModuleNameLength = wcslen(TargetModuleName) * sizeof(WCHAR);
LdrDataTableEntry1 = LdrDataTableEntry2 = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
/*
1: kd> dt _LDR_DATA_TABLE_ENTRY32 0x87a764b8
HookRing0!_LDR_DATA_TABLE_ENTRY32
+0x000 InLoadOrderLinks : LIST_ENTRY32
+0x008 InMemoryOrderLinks : LIST_ENTRY32
+0x010 InInitializationOrderLinks : LIST_ENTRY32
+0x018 DllBase : 0xab074000 Void
+0x01c EntryPoint : 0xab07a000 Void
+0x020 SizeOfImage : 0x8000
+0x024 FullDllName : _STRING32
+0x02c BaseDllName : _STRING32
+0x034 Flags : 0x49104000
+0x038 LoadCount : 1
+0x03a TlsIndex : 0x69
+0x03c HashLinks : LIST_ENTRY32
+0x044 TimeDateStamp : 0x5c0073
1: kd> db 0x87a76514
87a76514 48 00 6f 00 6f 00 6b 00-52 00 69 00 6e 00 67 00 H.o.o.k.R.i.n.g.
87a76524 30 00 2e 00 73 00 79 00-73 00 00 00 10 00 03 00 0...s.y.s.......
*/
do
{
if (LdrDataTableEntry2->BaseDllName.Buffer &&
MmIsAddressValid((PVOID)LdrDataTableEntry2->BaseDllName.Buffer) &&
!_wcsnicmp(TargetModuleName, (WCHAR*)LdrDataTableEntry2->BaseDllName.Buffer, ModuleNameLength / sizeof(WCHAR)))
{
*TargetModuleBase = LdrDataTableEntry2->DllBase;
*TargetModuleSize = LdrDataTableEntry2->SizeOfImage;
break;
}
LdrDataTableEntry2 = LdrDataTableEntry2->InLoadOrderLinks.Flink;
} while (LdrDataTableEntry2 != LdrDataTableEntry1);
Status = STATUS_SUCCESS;
}
else
{
}
return Status;
}