概述
搜集了很多资料和网页,为了枚举dpc花了很大功夫,为了方便大家学习相关知识与减少花费无谓的时间,将removedpc的小技巧和自己遇到的问题分享给大家
关于枚举DPC
这个是整个removedpc最大的问题。xp下可以导出 KiTimerTableListHead 然后遍历,但是win7及以上是没有导出的(无论32位还是64位),所以当前我们的问题是如何获得“KiTimerTableListHead“
每一个CPU中都有一个KPRCB结构地址我们发现kpcb下存在一个_KTIMER_TABLE结构的成员叫timetable
下面是结构图
nt!_KTIMER_TABLE
+0x000 TimerExpiry : [64] Ptr64 _KTIMER
+0x040 TimerEntries : [256] _KTIMER_TABLE_ENTRY
然后TimerEntries 就是我们想要的KiTimerTableListHead 他是一个数组
关于 _KTIMER_TABLE_ENTRY 下面是它的结构
typedef struct _KTIMER_TABLE_ENTRY
{
UINT32 Lock;
LIST_ENTRY Entry;
UINT32 Unknow;
ULARGE_INTEGER Time;
}KTIMER_TABLE_ENTRY, PKTIMER_TABLE_ENTRY;
关于KPRCB
ntdll!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
+0x010 LegacyNumber : UChar
+0x011 NestingLevel : UChar
+0x012 BuildType : Uint2B
+0x014 CpuType : Char
+0x015 CpuID : Char
+0x016 CpuStep : Uint2B
+0x016 CpuStepping : UChar
+0x017 CpuModel : UChar
+0x018 ProcessorState : _KPROCESSOR_STATE
....................................................................................................
+0x1960 TimerTable : _KTIMER_TABLE
..........................................................................................
这是个相当大的结构体,具体结构成员不多列举感兴趣的朋友可以通过调试和查阅msdn得到
值得注意的是64位和32位略有不同,在64位下TimerTable的位置在0x2200处,而_KTIMER_TABLE结构的成员timetable则在0x200处
然后kprcb可以通过未导出的变量KdVersionBlock得到,KiProcessorBlock在KdDebuggerData64这个结构中,KdDebuggerData64可以从KdVersionBlock中得到,而KdVersionBlock又在kpcr这个结构中
具体相关结构请浏览
http://blog.csdn.net/hu3167343/article/details/7612595
现在遇到的问题是 32位下KdVersionBlock只在1号处理器中才有值,但是64位下却无论如何也没有值
然后我们先继续32位
我们可以通过KeSetSystemAffinityThread(1)来让当前线程在1号处理器运行而kpcr可以通过读fs寄存器中的0x34位得到
64位则需要读msr的0xC0000101得到kpcr
所以我们可以说已经解决了问题
下来是代码实例
//RemoveDPC32.h
#include <ntifs.h>
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG32 SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
UINT32 Unknow[17];
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
typedef struct _KTIMER_TABLE_ENTRY
{
UINT32 Lock;
LIST_ENTRY Entry;
UINT32 Unknow;
ULARGE_INTEGER Time;
}KTIMER_TABLE_ENTRY, PKTIMER_TABLE_ENTRY;
NTSTATUS GetKernelModuleInfoByDriverObject(PDRIVER_OBJECT DriverObject, WCHAR* KernelModuleName, PVOID* KernelModuleBase, UINT32* KernelModuleSize);
BOOLEAN RemoveDPCInKernelModule(PVOID KernelModuleBase, ULONG32 KernelModuleSize);
BOOLEAN GetDPCTimerInfoByModuleInfo(PVOID KernelModuleBase, ULONG32 KernelModuleSize, ULONG32* Timer);
PUINT32 KeGetCurrentPrcb();
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
//RemoveDPC32.c
#include "RemoveDPC32.h"
PVOID __KernelModuleBase = NULL;
ULONG32 __KernelModuleSize = 0;
//bp RemoveDPC32!DriverEntry
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
NTSTATUS Status = STATUS_SUCCESS;
WCHAR KernelModuleName[] = L"DPCHookSSDT32.sys";
PDEVICE_OBJECT DeviceObject = NULL;
DbgPrint("DriverEntry()rn");
DriverObject->DriverUnload = DriverUnload;
//获得模块信息 可以扩展成枚举模块信息
Status = GetKernelModuleInfoByDriverObject(DriverObject, KernelModuleName, &__KernelModuleBase, &__KernelModuleSize);
if (Status != STATUS_SUCCESS)
{
return Status;
}
if (RemoveDPCInKernelModule(__KernelModuleBase, __KernelModuleSize) == FALSE)
{
return STATUS_UNSUCCESSFUL;
}
return Status;
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("DriverUnload()rn");
}
NTSTATUS GetKernelModuleInfoByDriverObject(PDRIVER_OBJECT DriverObject, WCHAR* KernelModuleName, PVOID* KernelModuleBase, UINT32* KernelModuleSize)
{
PLDR_DATA_TABLE_ENTRY NextEntry = NULL;
PLDR_DATA_TABLE_ENTRY CurrentEntry = NULL;
/*
nt!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : Ptr32 Void
+0x01c EntryPoint : Ptr32 Void
+0x020 SizeOfImage : Uint4B
+0x024 FullDllName : _UNICODE_STRING
+0x02c BaseDllName : _UNICODE_STRING
+0x034 Flags : Uint4B
+0x038 LoadCount : Uint2B
+0x03a TlsIndex : Uint2B
+0x03c HashLinks : _LIST_ENTRY
+0x03c SectionPointer : Ptr32 Void
+0x040 CheckSum : Uint4B
+0x044 TimeDateStamp : Uint4B
+0x044 LoadedImports : Ptr32 Void
+0x048 EntryPointActivationContext : Ptr32 _ACTIVATION_CONTEXT
+0x04c PatchInformation : Ptr32 Void
+0x050 ForwarderLinks : _LIST_ENTRY
+0x058 ServiceTagLinks : _LIST_ENTRY
+0x060 StaticLinks : _LIST_ENTRY
+0x068 ContextInformation : Ptr32 Void
+0x06c OriginalBase : Uint4B
+0x070 LoadTime : _LARGE_INTEGER
*/
//判断是否合法
if (DriverObject&&MmIsAddressValid(DriverObject))
{
//指向驱动对象的驱动节的首地址也就是第一个成员
CurrentEntry = NextEntry = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
NextEntry = (PLDR_DATA_TABLE_ENTRY)CurrentEntry->InLoadOrderLinks.Flink;
//循环停止条件 双向链表当前entry指向一个空节点
while ((PLDR_DATA_TABLE_ENTRY)NextEntry != CurrentEntry)
{
//判断nnextentry指向的数据是否合法 并其中的bassdllname与所要得到的内核模块名字相对比(双字
if (NextEntry->BaseDllName.Buffer&&
MmIsAddressValid((PVOID)NextEntry->BaseDllName.Buffer) && !_wcsnicmp(KernelModuleName,
(WCHAR*)NextEntry->BaseDllName.Buffer, wcslen(KernelModuleName)))
{
//为要out的两个参数赋值
//得到基地址和大小
*KernelModuleBase = NextEntry->DllBase;
*KernelModuleSize = NextEntry->SizeOfImage;
return STATUS_SUCCESS;
}
//循环继续
NextEntry = (PLDR_DATA_TABLE_ENTRY)NextEntry->InLoadOrderLinks.Flink;
}
}
else
{
return STATUS_UNSUCCESSFUL;
}
return STATUS_UNSUCCESSFUL;
}
BOOLEAN RemoveDPCInKernelModule(PVOID KernelModuleBase, ULONG32 KernelModuleSize)
{
ULONG32 Timer = NULL;
//通过刚才获得的模块信息得到dcptimer
if (GetDPCTimerInfoByModuleInfo(KernelModuleBase, KernelModuleSize, &Timer) == FALSE)
{
return FALSE;
}
//如果找到dpc并且得到的ktimer变量合法就取消
if (Timer&&MmIsAddressValid((PVOID)Timer))
{
if (KeCancelTimer((PKTIMER)Timer))
{
return TRUE;
}
}
return FALSE;
}
BOOLEAN GetDPCTimerInfoByModuleInfo(PVOID KernelModuleBase, ULONG32 KernelModuleSize, ULONG32* Timer)
{
ULONG32 v1 = KeNumberProcessors; //已经被模块导入 就是说这个值是默认存在的可以直接用 表示的是cpu核心数
int i = 0;
int j = 0;
PULONG KPRCB = 0;
PUCHAR TimerEntries = NULL;
PKTIMER v3 = NULL;
PULONG32 KiWaitNever = NULL;
PULONG32 KiWaitAlways = NULL;
PLIST_ENTRY CurrentEntry = NULL;
PLIST_ENTRY NextEntry = NULL;
//设立中断级 <= DISPATCH_LEVEL
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
//提高中断级到dispatch
//这个应该是获取kprcb结构//dt _KPRCB
KPRCB = KeGetCurrentPrcb();
/*
结构贼大反正不写了
0x2200这个地方是一个_KTIMER_TABLE结构的成员叫timetable
nt!_KTIMER_TABLE
+0x000 TimerExpiry : [64] Ptr64 _KTIMER
+0x200 TimerEntries : [256] _KTIMER_TABLE_ENTRY
然后下面那个偏移也就解释的通了
*/
TimerEntries = (PUCHAR)(*(ULONG32*)KPRCB + 0x1960 + 0x40);
for (i = 0; i < 0x100; i++)
{
CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY) * i + 4); //这里是个数组 + 过Lock
NextEntry = CurrentEntry->Blink;
if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry))
{
while (NextEntry != CurrentEntry)
{
PKDPC RealDPC;
//获得这个结构的首地址
v3 = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
RealDPC = v3->Dpc;
//如果timer合法 并且dpc函数不为空
if (MmIsAddressValid(v3) && MmIsAddressValid(RealDPC) && MmIsAddressValid(RealDPC->DeferredRoutine))
{
if ((ULONG32)v3 >= (ULONG32)KernelModuleBase && (ULONG32)v3 <= (ULONG32)KernelModuleBase + KernelModuleSize)
{
//如果找到了而且找到的dpc在之前的内核模块之间 就赋值传出
*Timer = (ULONG32)v3;
KeLowerIrql(OldIrql);
return TRUE;
}
}
NextEntry = NextEntry->Blink;
}
}
}
KeLowerIrql(OldIrql);
return FALSE;
}
PUINT32 KeGetCurrentPrcb()
{
PUINT32 KiProcessorBlock = 0;
KeSetSystemAffinityThread(1); //使当前线程运行在第一个处理器上
_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;
}
32位和64位的不同还体现在得到dpc的解密,因为32位下是未经过加密的而64则需要解密解密则需要解密码在经过一番计算得出
获取解密码可以通过导出KeSetTimer函数的函数地址然后向下查找得到
具体方法如下
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 = 0;
PUCHAR StartSearchAddress = 0;
PUCHAR EndSearchAddress = 0;
INT64 iOffset = 0;
PUCHAR i = NULL;
//通过函数名字获取函数地址
KeSetTimer = (ULONG64)GetExportVariableFormNtosExportTableByVariableName(L"KeSetTimer");
//查找的范围
StartSearchAddress = (PUCHAR)KeSetTimer;
EndSearchAddress = StartSearchAddress + 0x500;
for(i=StartSearchAddress; i<EndSearchAddress; i++)
{
if(*i==0x48 && *(i+1)==0x8B && *(i+2)==0x05)
{
memcpy(&iOffset,i+3,4);
//两个密码的值
*KiWaitNever=(PULONG64)(iOffset + (ULONG64)i + 7);
i=i+7;
memcpy(&iOffset,i+3,4);
*KiWaitAlways=(PULONG64)(iOffset + (ULONG64)i + 7);
return TRUE;
}
}
return FALSE;
}
PVOID
GetExportVariableFormNtosExportTableByVariableName(WCHAR *VariableName)
{
UNICODE_STRING v1;
PVOID ExportVariable = NULL;
if (VariableName && wcslen(VariableName) > 0)
{
RtlInitUnicodeString(&v1, VariableName);
//从Ntos模块的导出表中获得一个导出变量的地址
ExportVariable = MmGetSystemRoutineAddress(&v1);
}
return ExportVariable;
}
解密算法如下
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;
}
具体计算不做过多阐述,因为我也看不明白qwq
补充说明的一点是关于枚举模块基地址方面_LDR_DATA_TABLE_ENTRY结构中在32位下basedllname中的buffer为一个垃圾值(有值)而length则为0,所以当我用
if (NextEntry->BaseDllName.Buffer&&
MmIsAddressValid((PVOID)NextEntry->BaseDllName.Buffer) && !_wcsnicmp(KernelModuleName,
(WCHAR*)NextEntry->BaseDllName.Buffer, NextEntry->BaseDllName.Length)))
进行判断的时候他总是会判断为1
在64位下basedllname的buffer为null而length为0所以判断为0
这个算是容易出错的地方我调了好久才发现qwq
最后
以上就是欢喜樱桃为你收集整理的关于win7下RemoveDPC学习到的一点东西的全部内容,希望文章能够帮你解决关于win7下RemoveDPC学习到的一点东西所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复