本文会介绍一种无需在DWM中挂钩也可以实现DWM桌面绘制的方式 经过测试和验证 该方案可行
这个东西其实弄出来有一段时间了 最近很忙也一直没啥时间
之前问了下神话 大概了解了一下DWM绘制以及原理 顺便抄了一波代码
实际上目前大部分DWM绘制基本都依赖于寻找SwapChain并且HOOK某函数进行绘制
在一次偶然的调试中我发现 即便是在原绘制函数执行完毕之后再调用绘制也依然可以进行输出
于是就有了这份思路
不废话直接进入主题

DWM绘制路径如下 再往下的就没必要继续看了
其中VM3DUMP64为VMWARE的虚拟显卡3环驱动
在往上就是SSSDT函数了 NTGDIxxx
我觉得我说到这里已经有人明白要干什么了
首先直接讲DLL注入到DWM进程中 获取SwapChain并进行一些初始化操作 初始化完成之后可以释放钩子 对于是否可以不挂钩完成初始化我不太了解 不太懂3环的绘制原理
只要拿到了SwapChain 接下来问题就好说了
众所周知SSSDT并不受PG保护(实际上是里面的动态函数指针可以随意替换) 这也就给了一些可乘之机

只需要替换里面的动态指针即可完成函数HOOK(提示1:下面一层是dxgkrnl 这一层有相当多东西可以深挖)
(提示2:指针替换这种HOOK方式相比起一般HOOK来说难以发现许多 甚至可以用多层跳转来尽可能的伪装)

OK 在成功拦截来自DWM的绘制请求以后 就可以开始正式的绘制了
其实思路很简单
栈回潮 然后修改函数返回地址即可
也就是stack hook
FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
&ImageBase,
NULL);
if ((FunctionEntry != NULL))
{
RtlVirtualUnwind(0,
(ULONG64)ImageBase2.ModuleBase,
ContextRecord.Rip,
FunctionEntry,
&ContextRecord,
&HandlerData,
&EstablisherFrame,
NULL);
ULONG64 *p = (ULONG64*)((ULONG64)ContextRecord.Rsp - 8);
ULONG64 test12345 = *p;
if (test12345 == 0x00000)
{
*p = 0x123456789;
break;
}
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "%llx\n", test12345);
//std::cout << std::hex<< test12345 << std::endl;
}
先前已经成功将DLL注入进程中并且完成了初始化 那么现在要做的就是回潮 修改地址 然后放过3环请求
绘制正常进行 当运行到3环目标函数返回时 这个时候函数被截获 DLL的绘制函数也就拿到了执行权
正常绘制之后 循环再次运行 重新前往内核 这一层在提交的dma中 就已经带上了先前DLL的绘制内容
至此 整个绘制已经成功

由于stack hook的特性 在DWM中无法扫到任何HOOK 即便是文件内存对比也是无效的
至于DLL本身 隐藏内存简直不要太简单
虽然DWM进程中没有了HOOK 但R0依然有一个动态指针替换 不过既然主战场已经移到了内核 某种意义上来说就轻松太多了
至少对我来说 我宁愿把大家都拉倒内核去互掐 也不愿意在R3大乱斗
OK 最后的最后 放上几张图




我就愿意读栈回潮
伪代码地址:https://github.com/DragonQuestHero/DwmDraw
文学社github组织:https://github.com/ClassicalMusicClub
古典文学社Q群:780705352(较严格审核制入群)
我写代码经常闹的一堆人跟着我后面一起帮忙想 要感谢的人太多 就不一一列出来了...
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
最后于 2021-12-9 08:23
被章鱼C编辑
,原因: