首页
社区
课程
招聘
[求助]X64枚举DCP定时器的问题
发表于: 2018-9-5 21:36 5985

[求助]X64枚举DCP定时器的问题

2018-9-5 21:36
5985
先上源码 有时输出就几个,有时输出几十个,大部分是几个的,是什么原因?
#include "Drive.h"


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


	DriverObject->DriverUnload = DriverUnload;
//if (GetKernelModuleInfoByDriverObjectInWin7(DriverObject, ModuleName, &__KernelModuleBase, &__KernelModuleSize) == FALSE)
//{
//return Status;
	//}
//DbgPrint("gabpfiModuleBase is %p \r\n", __KernelModuleBase);
//DbgPrint("gabpfiModuleSize is %p \r\n", __KernelModuleSize);
__KernelModuleBase = 0xfffff00886800000;
__KernelModuleSize = 0xffff0;
	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("gabpfiModuleBase is %p \r\n", *ModuleBase);
			DbgPrint("gabpfiModuleSize 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函数都合法
				//	DbgPrint("gabpfiRealDpc is: %p , gabpfiDeferredRoutine is: %p \r\n", RealDpc, RealDpc->DeferredRoutine);
					//DbgPrint("[gabpfidpc]dpcroutine:%p\n", RealDpc->DeferredRoutine);
					//__KernelModuleBase = 0xfffff00886800000;
					//__KernelModuleSize = 0xffff0;
					
					if (MmIsAddressValid(RealDpc) && MmIsAddressValid(KTimer) && MmIsAddressValid(RealDpc->DeferredRoutine))
					{
						DbgPrint("gabpfiOKis %p \r\n", RealDpc->DeferredRoutine);  //输出---------------------------
						
						if ((ULONG64)RealDpc->DeferredRoutine >= 0xfffff88006000000 && (ULONG64)RealDpc->DeferredRoutine <= 0xfffff88006ff0000)
						{

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

/*ULONG_PTR   ptrDpc = (ULONG_PTR)ptrTimer->Dpc;
KDPC*       DecDpc = NULL;
int         nShift = (p2dq(ptrKiWaitNever) & 0xFF);

//_RSI->Dpc = (_KDPC *)v19;
//_RSI = Timer;
ptrDpc ^= p2dq(ptrKiWaitNever);//v19 = KiWaitNever ^ v18;
ptrDpc = _rotl64(ptrDpc, nShift);//v18 = __ROR8__((unsigned __int64)Timer ^ _RBX, KiWaitNever);
ptrDpc ^= (ULONG_PTR)ptrTimer;
ptrDpc = _byteswap_uint64(ptrDpc);//__asm { bswap   rbx }
ptrDpc ^= p2dq(ptrKiWaitAlways);//_RBX = (unsigned __int64)DPC ^ KiWaitAlways;
   if (MmIsAddressValid((PVOID)ptrDpc))
	{
		nTotalCount++;
		DecDpc = (KDPC*)ptrDpc;
		DbgPrint("[dpc]dpc:%p,routine:%p\n", DecDpc, DecDpc->DeferredRoutine);
	}
*/

#define p2dq(x)  (*((ULONG_PTR*)x))

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


[课程]Android-CTF解题方法汇总!

最后于 2018-9-8 16:09 被gabpfiugdu编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 89
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
KTimer是多个的,解密二个值的获取也没有错,相同的值有时可以解密枚举出来,有时不可以?
最后于 2018-9-9 14:25 被gabpfiugdu编辑 ,原因:
2018-9-5 22:51
0
游客
登录 | 注册 方可回帖
返回
//