本帖最后由 万剑归宗 于 2023-6-3 19:13 编辑
开篇2017/10/31:
这是个什么东西?
为什么我可以随意画东西在屏幕上?
2023/06/03:
当年居然还写过这个,发出来玩玩。
1:废话不多说,直接上截图
绘制效果1
问:这有什么,不就是一张普通的画个方形和线条的图吗?
答:如果我说这是我直接操作内存画像素点实现的你信吗?
接上直接放代码
[C++] 纯文本查看 复制代码 VOID WriteVideoMem()
{
HRESULT hr;
LPDIRECTDRAW lpDD;
LPDIRECTDRAWSURFACE lpDDSPrimary;
hr = DirectDrawCreate(NULL, &lpDD, NULL);
hr = lpDD->SetCooperativeLevel(0, DDSCL_NORMAL);
hr = lpDD->SetDisplayMode(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), 32);
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY;
hr = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
ZeroMemory(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
hr = lpDDSPrimary->Lock(NULL, &ddsd, DDLOCK_NODIRTYUPDATE, NULL);
hr = lpDDSPrimary->Unlock(0);//unlock
POINT point;
while (1)
{
GetCursorPos(&point);
DrawBox(ddsd, point.x, point.y, 50, 50, 1, 0x00FF0000);
Drawline(ddsd, point.x, point.y, point.x + 100, point.y + 200, 0x00FF0000);
Sleep(1);
}
}
[C++] 纯文本查看 复制代码 VOID DrawBox(DDSURFACEDESC Surface, DWORD x, DWORD y, DWORD w, DWORD h, DWORD px, DWORD color)
{
if (x > Surface.dwWidth) x = 0;
//if (y > Surface.dwHeight) y = 0;
if ((x + w) > Surface.dwWidth) w = Surface.dwWidth - x ;
if ((h + y) > Surface.dwHeight) h = Surface.dwHeight - y ;
h += y;
DWORD pos;
for (DWORD g = y; g < h; g++)
{
pos = g*Surface.lPitch + x * 4;
for (DWORD k = 0; k < w; k++)
{
if ((g - y) < px || (h - g) <= px || k < px || (w - k) <= px)
{
memcpy((PVOID)((PUCHAR)Surface.lpSurface + pos), &color, 4);
}
pos += 4;
}
}
}
2:分析为什么这段代码可以操作显存
下面开始一本正经的胡说八道了
注意DrawBox函数,它居然在直接操作内存!
已知的情况是,操作的内存地址正是传进来的Surface.lpSurface
那我们看一下这个 Surface.lpSurface 地址里到底放了什么东西
搞错了
完全不知道是什么东西呢
也许是打开方式不对
搞错了
再来
哇哦
呕吼,好像有点吊。
试试把内存写空一个区域呢?
黑了
居然有效
那么现在问题来了,这内存到底哪儿来的
为什么可以取到屏幕的画面,甚至还能改写呢?
3:阅读源码以及继续挖坑
从上面老老实实捋一下,我们发现这份代码最开始是用DirectDraw创建了一个设备
重点是,设置显示模式(SetDisplayMode)时,选择了全屏的参数
以及下面创建页面(CreateSurface)时,选择创建主页面,并且有一个DDSCAPS_VIDEOMEMORY的标志
等等,好像下面那句是有华点的
hr = lpDDSPrimary->Lock(NULL, &ddsd, DDLOCK_NODIRTYUPDATE, NULL);
hr = lpDDSPrimary->Unlock(0);
这里选择了使用Lock方法锁定页面,但是又立马解锁了
以我们114514年的编程(吹水)经验来看
解锁之后,ddsd里面的内容应该不再可靠。
但是,事实如你所见,我们仍然可以在unlock后接着那它做事
问:能干吗?
答:截图录屏,无窗水印,某些灰色产业
好的,回到正题,现在知道这块内存是可以操作的了
我们就试着找找哪儿来的
先查查成分(内存属性)
成分
233333
完全没看懂,换个思路,查查物理地址
把CE的能力解放一下
解放
物理地址
等等,这个物理地址我似乎见过,让我找找
好家伙
好家伙,居然是显示适配器用的。
4:进阶验证
从上面一本正经的分析(胡说八道)中,找到了这块"显存"的物理地址
那么有没有可能直接操作物理地址呢?
答案是肯定的
为了确保测试差异,我选择重启后直入E8000000这个物理地址看一下
结果是
起飞
芜湖起飞~
2023/06/03:
到这里我就不再看了
因为这是来自2017/10/31的内容
楼主已经没有太多时间再花在这种事情上
填坑这种事情交给有缘的年轻人吧
测试环境:win7x64 Vmware 虚拟机
重点是物理地址,只有虚拟机的环境下几乎是固定的
真机的情况是,物理地址并不固定。
另外在windows10下有其他写法
这里就不多唠了。
|