抓屏技术之MirrorDriver镜像驱动应用

抓屏技术之MirrorDriver镜像驱动应用

在前面的文章(抓屏技术之MirrorDriver镜像驱动实现),我们分析过Mirror Driver的基本框架以及实现技术;并且在文章的最后我们通过UVNC引入了Mirror Driver的一个使用例子,本文我们就来探讨一下这个例子的具体使用和实现。

首先,我们可以从https://uvnc.com/products/mirror-driver.html链接查看这个驱动SDK的使用;对于这个SDK在官网宣传能够支持的版本包括如下:

  1. Windows 2000。
  2. Windows 2003。
  3. Windows XP。
  4. Windows 2008。
  5. Windows Vista。
  6. Windows 7。

其实我们在抓屏技术之MirrorDriver镜像驱动实现中就分析过,Mirror Driver其实是可以支持Win 7以上系统的,但是UVNC在Win8以及以上系统使用的是ddengine来进行高效抓屏。对于这个SDK的使用如下:

HDC hDisplayDC = CreateDC("DISPLAY",NULL,NULL,NULL);
int cxWidth= GetDeviceCaps(hDisplayDC,HORZRES) ;
int cyHeight = GetDeviceCaps(hDisplayDC,VERTRES);
mydriver->VIDEODRIVER_start(0,0,cxWidth,cyHeight,32); 

虽然使用非常简单,但是遗憾的是,对于驱动模块部分并没有开源。本文我们就来分析一下这个驱动的实现,并且完全实现一个可以兼容UVNC_MD_SDK的驱动,并且可以支持UVNC的驱动替换。

1. UVNC_MD_SDK的使用

我们可以看一下UVNC_MD_SDK的使用,他们提供了一个VIDEODRIVER类来实现和驱动的基本通信以及获取FrameBuffer,这个类声明如下:


#define MAXCHANGES_BUF 2000

#define SCREEN_SCREEN 11
#define BLIT 12
#define SOLIDFILL 13
#define BLEND 14
#define TRANS 15
#define PLG 17
#define TEXTOUT 18

typedef BOOL (WINAPI* pEnumDisplayDevices)(PVOID,DWORD,PVOID,DWORD);
typedef LONG (WINAPI* pChangeDisplaySettingsExA)(LPCSTR,LPDEVMODEA,HWND,DWORD,LPVOID);
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
typedef struct _CHANGES_RECORD
{
	ULONG type;  //screen_to_screen, blit, newcache,oldcache
	RECT rect;	
	POINT point;
}CHANGES_RECORD;
typedef CHANGES_RECORD *PCHANGES_RECORD;
typedef struct _CHANGES_BUF
{
	 ULONG counter;
	 CHANGES_RECORD pointrect[MAXCHANGES_BUF];
}CHANGES_BUF;
typedef CHANGES_BUF *PCHANGES_BUF;

class VIDEODRIVER
{
public:
	VIDEODRIVER();
	void VIDEODRIVER_start(int x,int y,int w,int h,int depth);
	void VIDEODRIVER_Stop();
	virtual ~VIDEODRIVER();
	BOOL HardwareCursor();
	BOOL NoHardwareCursor();

	ULONG oldaantal;
	PCHAR mypVideoMemory;
	PCHAR myframebuffer;
	PCHANGES_BUF mypchangebuf;
	BOOL blocked;
	int shared_buffer_size;
	

protected:
	int OSVersion();
	bool Mirror_driver_attach_XP(int x,int y,int w,int h,int depth);
	void Mirror_driver_detach_XP();
	bool Mirror_driver_Vista(DWORD dwAttach,int x,int y,int w,int h,int depth);
	PCHAR VideoMemory_GetSharedMemory(void);
	void VideoMemory_ReleaseSharedMemory(PCHAR pVideoMemory);
	HDC GetDcMirror();

	int OSVER;
	
};

通过这个类,我们可以获取两个东西:

  1. myframebuffer表示图像帧。
  2. mypchangebuf表示图像变化区域。

并且图像帧和图像变化区域是存放在一个共享内存中的数据,获取共享内存的方法如下:

PCHAR VIDEODRIVER::VideoMemory_GetSharedMemory(void)
{
   PCHAR pVideoMemory=NULL;
   HANDLE hMapFile, hFile, hFile0,hFile1;
   hFile=NULL;
   
   hFile0 = CreateFile("c:\\video0.dat", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
   hFile1 = CreateFile("c:\\video1.dat", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

   if ((hFile0 && hFile0 != INVALID_HANDLE_VALUE) && !(hFile1 && hFile1 != INVALID_HANDLE_VALUE)) hFile=hFile0;
   if ((hFile1 && hFile1 != INVALID_HANDLE_VALUE) && !(hFile0 && hFile0 != INVALID_HANDLE_VALUE)) hFile=hFile1;
   if ((hFile0 && hFile0 != INVALID_HANDLE_VALUE) && (hFile1 && hFile1 != INVALID_HANDLE_VALUE))
   {
	   DWORD size0=GetFileSize(hFile0,NULL);
	   DWORD size1=GetFileSize(hFile1,NULL);
	   if (size0==shared_buffer_size) hFile=hFile0;
	   if (size1==shared_buffer_size) hFile=hFile1;

   }


   if(hFile && hFile != INVALID_HANDLE_VALUE)
   {
       hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);

       if(hMapFile && hMapFile != INVALID_HANDLE_VALUE)
       {
           pVideoMemory = (char *) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
    
           CloseHandle(hMapFile);
       }

       CloseHandle(hFile);
   }
   
   return pVideoMemory;
}

下面我们具体来看一下MV2.DLL的实现。

2. MV2.DLL的实现

我们知道(参考抓屏技术之MirrorDriver镜像驱动实现),Mirror Driver的主要实现是通过DrvEnableDriver入口函数来设置回调函数来实现的,对于MV2.DLL提供的绘图函数如下:

.data:00015258 ; _DRVFN MvDrvFn
.data:00015258 MvDrvFn         _DRVFN <0, offset DrvEnablePDEV>
.data:00015258                                         ; DATA XREF: DrvEnableDriverInternal+E↑o
.data:00015258                 _DRVFN <1, offset DrvCompletePDEV>
.data:00015258                 _DRVFN <2, offset DrvDisablePDEV>
.data:00015258                 _DRVFN <3, offset DrvEnableSurface>
.data:00015258                 _DRVFN <4, offset DrvDisableSurface>
.data:00015258                 _DRVFN <5, offset DrvAssertMode>
.data:00015258                 _DRVFN <18h, offset DrvEscape>
.data:00015258                 _DRVFN <17h, offset DrvTextOut>
.data:00015258                 _DRVFN <12h, offset DrvBitBlt>
.data:00015258                 _DRVFN <13h, offset DrvCopyBits>
.data:00015258                 _DRVFN <0Eh, offset DrvStrokePath>
.data:00015258                 _DRVFN <44h, offset DrvGradientFill>
.data:00015258                 _DRVFN <1Fh, offset DrvLineTo>
.data:00015258                 _DRVFN <0Fh, offset DrvFillPath>
.data:00015258                 _DRVFN <14h, offset DrvStretchBlt>
.data:00015258                 _DRVFN <47h, offset DrvAlphaBlend>
.data:00015258                 _DRVFN <4Ah, offset DrvTransparentBlt>
.data:00015258                 _DRVFN <46h, offset DrvPlgBlt>
.data:00015258                 _DRVFN <16h, offset DrvSetPalette>
.data:00015258                 _DRVFN <58h, offset DrvSynchronizeSurface>
.data:00015258                 _DRVFN <0Dh, offset DrvDitherColor>
.data:00015258                 _DRVFN <1Eh, offset DrvMovePointer>
.data:00015258                 _DRVFN <1Dh, offset DrvSetPointerShape>
.data:00015258                 _DRVFN <10h, offset DrvStrokeAndFillPath>
.data:00015258                 _DRVFN <45h, offset DrvStretchBltROP>

对于具体每个函数的具体作用,这里不做详细分析,我们先来看一下共享内存的创建过程,这个过程在图形表面被创建的回调函数DrvEnableSurface,具体实现如下:

HSURF __stdcall DrvEnableSurface(MV_DEV *dev)
{
	//..
	if (MvVideo0)
	{
		if (MvVideo1)
		{
			if (MvVideo2)
				return 0;
			ChangesBuf = (CHANGES_BUF *)EngMapFile(L"\\??\\c:\\video2.dat", v13 * v1->lDelta + 56004, &v1->piFile);
		}
		else
		{
			ChangesBuf = (CHANGES_BUF *)EngMapFile(L"\\??\\c:\\video1.dat", v13 * v1->lDelta + 56004, &v1->piFile);
		}
	}
	else
	{
		ChangesBuf = (CHANGES_BUF *)EngMapFile(L"\\??\\c:\\video0.dat", v6, &v1->piFile);
	}
	//...
	return 0;
}

这也是用户层可以直接使用共享内存的原因。MV2.DLL还有一个重要的实现就是可以获取图像帧的变化数据,这个获取其实是根据裁剪区来获取的,裁剪区被定义如下:

typedef struct _CLIPOBJ {
  ULONG iUniq;
  RECTL rclBounds;
  BYTE  iDComplexity;
  BYTE  iFComplexity;
  BYTE  iMode;
  BYTE  fjOptions;
} CLIPOBJ;

对于裁剪区的类型如下:

ValueMeaning
DC_COMPLEXThe clip region must be enumerated.
DC_RECTClip to a single rectangle.
DC_TRIVIALClipping need not be considered; draw the whole figure.

因此对于图像帧的脏区域(变化数据)也是通过上述方法来获取,下面我们通过DrvTextOut函数来看一下示例代码。如下:

int __stdcall DrvTextOut(_SURFOBJ *pso, _STROBJ *pstro, _FONTOBJ *pfo, _CLIPOBJ *pco, RECTL *prclExtra, RECTL *prclOpaque, _BRUSHOBJ *pboFore, _BRUSHOBJ *pboOpaque, POINTL *pptlOrg, MIX mix)
{
  //...
  if ( prclOpaque )
  {
    MvAddChangeBuf(...);
  }
  else if ( pstro )
  {
    MvAddChangeBuf(...);
  }
  if ( pso_3 == 1 )
  {
    if ( pco )
    {
      MvAddChangeBuf(...);
      return (int)pboOpaquea;
    }
  }
  else if ( pso_3 == 3 && pco )
  {
    CLIPOBJ_cEnumStart(pco, 0, 0, 4, 0);
    do
    {
      pstroa = (_STROBJ *)CLIPOBJ_bEnum(pco, 804, &v15);
      pboForea = v15;
      for ( prclOpaquea = (RECTL *)&v16; pboForea; ++prclOpaquea )
      {
        pboForea = (_BRUSHOBJ *)((char *)pboForea - 1);
        MvAddChangeBuf(...);
      }
    }
    while ( pstroa );
    return (int)pboOpaquea;
  }
  if ( prclOpaque )
  {
    MvAddChangeBuf(...);
  }
  //...
}

如上实现了一个图像帧脏数据的获取。

3. winvnc中的使用

其中在UVNC中也使用了Mirror Driver来获取数据,在vncDesktop::InitVideoDriver中有初始化Mirror Driver获取数据的代码如下:

BOOL vncDesktop::InitVideoDriver()
{
    //...
    if (IsWindows8OrGreater() && !VNC_OSVersion::getInstance()->OS_WINPE)
	{
		vnclog.Print(LL_INTERR, VNCLOG("Try ddengine\n"));
		m_screenCapture = new DeskDupEngine;
	}
	else
	{
		vnclog.Print(LL_INTERR, VNCLOG("Try mirrordriver\n"));
		m_screenCapture = new VideoDriver;
	}
    //...
}

这里有两种屏幕录制的技术:

  1. ddengine。
  2. mirrordriver。

这个mirrordriver就是我们实现的MV2.DLL。

4. 实现效果

依据上述原理分析,很快我们可以实现MV2.DLL相同功能的Mirror Driver驱动程序,并对其进行替换,实现效果如下:
在这里插入图片描述

通过自己实现的MV2.DLL,我们成功实现了高效抓图的应用。其实很多软件都使用了该项技术,例如我们在SunLogin(向日葵)远程软件中也可以发现Mirror Driver的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值