Hello啊 各位看雪的大佬们
在家天天玩游戏 无聊的一~ 在一个风和日丽的下午 我躺在床上玩手机 偶然间看到一个交流群里面 有人在收所谓的dwm反截图 于是就起来研究了一番
研究的第一件事 当然是打开从未注册过的看雪论坛 寻找一番 截图原理~ (由于看雪有太多关于构造Shellcode进dwm进行截图的帖子了 我也不清楚哪个是原创 所以没附链接)
还是叙述一下原理吧
先这样 再内样 最后那样 就搞定啦~
还是不开玩笑 直接上 GitHub - lainswork/dwm-screen-shot: 将shellcode注入dwm.exe以进行屏幕截取 在这个开源项目中 你将会看到他下载了当前系统版本dxgi模块的pdb文件 并从中寻找了4个地址
地址都有了 第一件事情 当然是丢到ce去看看他撒嘛个情况喽
好像 4个值是一样的 .... 那我们试着下个断点 看看是啥情况
直接说结果: 前三个地址断不下来 点击测试截图 按钮后 其中第1或2或3 的地址指向的空间会被清除 至于为什么会被清除...大家可以去那个开源项目研究研究
嘶~ 好吧 不过是 在不同版本的系统实测中 第四个地址 永远不会变 BUT...... 第四个 我发现处理第四个没啥用的 因为第四个一直被调用着 随时能断下来
大家对dwm下断 请做好卡死的准备 毕竟是处理所有窗口的程序 一旦停止了 你的画面也就无了~
那么 眼尖 又懒惰 的小伙伴 可能注意到了 前三个地址在截图时的变化 那我们是不是可以在截图的时候不绘制呢
if(IsBadHugeReadPtr(dxgi+0x14d0))
{
return;
}
欸 是不是可以利用这个思路进行反截图呢
欸还真可以(在我的电脑上...WIN11 22H2)
然后我就兴致勃勃的发给朋友试了 欸 您猜怎么着 他的绘制会消失一会儿 但是等恢复回来的时候 就刚好被截图到了....
好吧 还是不能走歪门邪道 我们还是老老实实看看那4个地址对应的啥函数吧 ~
OK 我们直接使用dumpbin工具获取到pdb内容
dumpbin工具 使用方法如下 : 1.打开vs 2.工具->命令行->开发者命令提示 然后出来了一个窗口 在里面输入
dumpbin /all "路径\dxgi.dll">D:\储存dump的文件.txt 注意dxgi的pdb要跟dll同目录
ok回归正题
在PDB文件中 找到了4个地址对应的虚函数名
第一个函数是PresentFullscreenFlip 第二个是 CDXGISwapChain::Present 第三个是PresentDWM@CDXGISwapChain 第四个是PresentMultiplaneOverlay
嘶~
在Bing后 在Baidu后 在Google后 再问过ChatGPT之后 我发现 这4个函数对我来说 用处不大~
于是我们退而求其次 去Bing一下 正常截图的方法
有一种比较简单的想法能比较快速的实现DirectX11 程序运行截图。
在当前场景帧进行Present()操作之前,渲染结果存储在交换链(Swap Chain)的后缓冲区中。我们定义一个Texture2D 对象,调用IDXGISwapChain类中的GetBuffer()方法可以将当前后缓冲区的绘制结果复制到该Texture2D对象中:
HRESULT GetBuffer(
[in] UINT Buffer,
[in] REFIID riid,
[in, out] void **ppSurface
);
例子:(来源于MSDN)
ID3D11Texture2D * p_RT;
g_pSwapChain->GetBuffer(0, __uuidof(p_RT), reinterpret_cast<void**>(&p_RT));
欸嘿 这不就赚大发了吗 直接HookGetBuffer不就完了
hook他的第一个问题 就是他的地址怎么取.... 由于我的知识有限 所以我直接在pdb里面找到了..... 简单又快捷
那我们直接导入Hook库 直接开干
MH_Initialize();
DWORD64 AddrGetBuffer =(DWORD64) DXGImoudlebase+ 0x1468; // GetBuffer
MH_CreateHook((DWORD_PTR *)AddrGetBuffer , &NewGetBuffer,(PVOID*)(&OrGetBuffer));
MH_EnableHook((DWORD_PTR *)AddrGetBuffer );
HRESULT NewGetBuffer(IDXGISwapChain *A, UINT Buffer, GUID riid, void **ppSurface)
{
*ppSurface = (void *)pTexture2D;
return S_OK;
}
那么细心的观众 或者 已经去CV的观众就要问了 你给返回的pTexture2D 是啥玩意
那我就不得不说说我这两天被折磨的路程了
我一开始的思路是:在我还没有 绘制任何东西的时候截用GetBuffer创建一个ID3D11Texture2D * 然后到时候返回我没绘制的时候的ID3D11Texture2D *就好了 这个思路是不是很好
当你这么想的时候你就掉进去了 我这样尝试的结果是: 即使返回一个我没绘制的时候使用GetBuffer获取到的数据(这里说数据都不严谨 微软官方说的是后台缓冲区接口的指针)也会截图到实时的画面 并且带着我的绘制.... ok 还是返回一张固定的图片方便....
ok啊 返回图片还不简单吗 ... 先这样在那样不就好了 哈哈
读入图片需要用到d3dx11的支持库
#include <d3d11.h>
#include"D3DX11.h"
#pragma comment(lib, "d3dx11.lib")
嗯对就是这玩意儿 什么? 你说你CV进去没有????
嘿嘿 直接上代码:
D3D11_TEXTURE2D_DESC dec;
UINT createDeviceFlags = 0;
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
D3D_FEATURE_LEVEL featureLevel;
ID3D11Device *md3dDevice;
ID3D11DeviceContext *md3dImmediateContext;
HRESULT hr =
D3D11CreateDevice(0, // 默认显示适配器
D3D_DRIVER_TYPE_HARDWARE, 0, // 不使用软件设备
createDeviceFlags, 0, 0, // 默认的特征等级数组
D3D11_SDK_VERSION, &md3dDevice, &featureLevel, &md3dImmediateContext);
HRESULT result;
D3DX11_IMAGE_LOAD_INFO loadInfo;
ZeroMemory(&loadInfo, sizeof(D3DX11_IMAGE_LOAD_INFO));
loadInfo.BindFlags = desc.BindFlags;// ;
loadInfo.Format = desc.Format; // DXGI_FORMAT_R8G8B8A8_UNORM;
loadInfo.MipLevels = desc.MipLevels; // D3DX11_DEFAULT; // 这时会产生最大的mipmaps层。
loadInfo.MipFilter =D3DX11_FILTER_LINEAR;
result = D3DX11CreateTextureFromFile(md3dDevice, "C:\\1.png", &loadInfo, NULL,
(ID3D11Resource **)(&pTexture2D), NULL);
pTexture2D->GetDesc(&dec);
代码都是从我的源码里面扣出来的 难免缺斤少两 希望大家自己看得懂 有一点解释一下 上图中 desc的定义
D3D11_TEXTURE2D_DESC desc;
memset(&desc, 0x00, sizeof(desc));
g_back_buffer->GetDesc(&desc);
如果你连GetDesc都不知道是啥 那我也无能为力了
最后让我们测试一下这个方法可行不可行吧
好了 其实到这里就完结撒花了
总结一下 GetBuffer 有更好的方法获取地址
本篇文章不涉及太多具体代码 因为反截图本身是在对抗反作弊 本人也是仅作技术交流 才发表本文章 我怕直接发源码 一堆人拿去玩游戏 也不太好...
好啦~提前祝大家5 1 快乐~ 感谢大家的观看
真是无语 看雪上了这么多次 一直没注册 现在想发表篇帖子 还没到24小时
== 现在有了 发表~
转载请注明出处
[培训]内核驱动高级班,冲击BAT一流互联网大厂工
作,每周日13:00-18:00直播授课