顺便聊一下滥用DComposition在外部窗口上渲染

2012年,微软推出了“ DirectComposition”技术,该技术可极大地帮助改善位图绘图和合成的性能,其工作方式是利用图形硬件来合成和渲染对象,这意味着它除了可以独立运行之外,还可以独立运行。主UI线程。

因此,可以推断出必须有一层交互作用,或者一种将组合物应用到所需窗口或目标上的方法,而滥用这一层交互作用是当今文章的主要目标。

DirectCompositions使用的交互层是称为“目标”和“视觉对象”的对象,每个IDCompositionTarget将由依赖于窗口句柄的相应API函数创建,并且每个目标都将依赖于IDCompositionVisual,后者包含表示的视觉内容屏幕上。

如果您认为可以轻松地创建一个窗口,然后通过非所有权过程在另一个窗口的顶部进行构图,那么您错了。这将导致错误,并且不会创建合成。

逆转

打开win32kfull,它是DWM,GDI和其他Windows功能的内核模式组件,然后搜索“ DComposition”将产生多个结果:

NtUserCreateDCompositionHwndTarget根据它的原型__int64 (HWND a1, int a2, _QWORD *a3),我们感兴趣的是:,我们可以得出这仅仅是just IDCompositionDevice::CreateTargetForHwnd,而参数是:(HWND hwnd, BOOL topmost, IDCompositionTarget** target)

在此功能的最开始,有一个测试检查您是否可以为此合成创建目标:

last_status = TestWindowForCompositionTarget(window_handle, top_most);

这是该功能的简化形式:

NTSTATUS TestWindowForCompositionTarget(HWND window_handle, BOOL top_most)
{	
	tagWND* window_instance = ValidateHwnd(window_handle);
	
	if (!window_instance 
		|| !window_instance->thread_info)
		return STATUS_INVALID_PARAMETER;
		
	// some checks here to verify that DCompositions are supported, and available
	
	PEPROCESS calling_process = IoGetCurrentProcess();
	PEPROCESS owning_process = PsGetThreadProcess(window_instance->thread_info->owning_thread); // tagWnd*->tagTHREADINFO*->KTHREAD*
	
	if (calling_process != owning_process)
		return STATUS_ACCESS_DENIED;
	
	CHwndTargetProp target_properties{};
	
	if (CWindowProp::GetProp<CHwndTargetProp>(window_instance, &target_properties))
	{
		bool unk_error = false;
		
		if (top_most)
			unk_error = !(target_properties.top_most_handle == nullptr);
		else
			unk_error = !(target_properties.active_bg_handle == nullptr);
		
		if (unk_error)
			return (NTSTATUS)0x803e0006; // unique error code, i don't know what it's supposed to resemble
	}
	
	return STATUS_SUCCESS;
}

导致失败的检查为if (calling_process != owning_process),它将调用者的进程与窗口的所有者进程进行比较,如果此检查失败,则它们返回STATUS_ACCESS_DENIED错误。

他们通过调用来检索窗口的所有者进程ValidateHwnd,这是win32k中到处使用的函数:

此函数将返回一个指向类型struct的指针tagWND,然后tagTHREADINFO在+ 0x10(window_instance-> thread_info)访问类型的成员,然后在+ 0x0(thread_info-> owning_thread)访问实际的线程指针。

规避这些检查的一种方法是将进程窗口的拥有线程临时交换到我们的窗口,在其上组成我们的目标,然后很快将其交换回去,这就是PoC的基础。

概念证明

我做了一个PoC,它将通过类名劫持一个窗口,然后在其中心渲染一个矩形。您可以在此处访问代码。

正文完