65.9K
CodeProject 正在变化。 阅读更多。
Home

Target Eye 揭秘:第二部分 - Target Eye 的屏幕捕获

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (49投票s)

2012年9月17日

CPOL

10分钟阅读

viewsIcon

120740

downloadIcon

3789

Target Eye 的屏幕捕获机制如何工作

投票选为 2012 年 9 月最佳 C++ 文章

引言

屏幕捕获是任何监控产品中的主要组成部分之一。Target Eye 监控系统,自 2000 年至 2010 年开发,拥有其独特的 JPG 屏幕截图。本文是 Target Eye 揭秘 系列的第二篇文章。第三篇文章(紧随本文之后)是关于 购物清单机制。第四篇文章是关于 键盘捕获,第五篇文章是关于 封面故事机制第六篇文章 是关于 Target Eye 的文件隐藏方式。

背景

我十二年前开发的 Target Eye 监控系统,可能是第一个用于捕获远程计算机活动的监控工具。以下描述摘自该项目最初的商业计划书。

Target Eye 是一家初创公司,其使命是开发基于公司专利申请中技术(60/204,084 和 60/203,832)的实时远程 PC 监控集成软件解决方案。我们的主要产品 Target Eye 监控系统是一款软件产品,能够以用户无法察觉的方式,持续跟踪、记录、回放、分析和报告在一台或多台远程 PC 上执行的任何活动。该软件依赖于快速捕获、压缩的全屏图像流和连续的键盘输入捕获,以提供用户活动的全面准确记录,包括不产生任何网络流量的本地活动。通过这种方式,该软件可以跟踪和记录网络流量分析系统无法检测到的活动。在被监控 PC 上运行的智能代理模块使用规则库将警报发送到监控位置,或执行预定义的本地操作。监控可以从多个位置进行。主要市场是执法部门。

Target Eye 的组成部分

Target Eye 监控系统由以下几个术语和组成部分构成。

  • Target Eye Secret Agent - 该产品的隐蔽部分,运行在被监控的计算机上。
  • Target Eye 操作员 - 操作秘密代理的权威(例如:执法机构)。
  • 任务 - 执行某项操作的请求。有许多可能的操作(例如,本文中描述的屏幕捕获,或本文中描述的文件搜索和查找)。
  • Target Eye Compiler - Target Eye Operator 用于自定义 Secret Agent 以执行特定任务的工具。

屏幕捕获的目的

屏幕捕获的目的只有一个,那就是提供被监控计算机在特定时刻活动的准确图像。这是通过抓取屏幕上显示的图像,并为其添加水印来实现的,水印反映了任何有助于以后将截图用作证据的其他数据:此类附加数据可以是日期和时间戳、时区、唯一的顺序编号以及任何附加标签,例如活动应用程序的名称或作为“热词”机制一部分捕获的特定按键序列(例如:“本·拉登”)。

Target Eye 屏幕截图文件

屏幕捕获文件应大小小巧但仍可读。JPG 格式通过 1 到 100 之间的数值来设置图像质量。此值还为屏幕截图创建了独特的外观,使其几乎不可能伪造或篡改,从而确保每个截图文件都是真实的。Target Eye 使用其自己独特的不完美屏幕。

取自 2000 年 5 月的早期版本之一。如需原始图像,请单击此处

这是我笔记本电脑在 2003 年 12 月截取的缩小尺寸屏幕截图。您可以看到打开了 Visual C++ 6.0 和 Target Eye 源代码……要查看原始图像,请单击此处

屏幕捕获步骤

要捕获屏幕,应执行以下步骤:

获取 DC

DC 是 Device Contents(设备上下文)的缩写。GetDC 函数检索指定窗口客户区或整个屏幕的设备上下文 (DC) 的句柄。要获取给定窗口的 DC,请调用

HDC hDC = GetDC(hWnd);   

在本例中,我们将“0”传递给 GetDC,以捕获整个桌面。

HDC hDC = GetDC(0);   

返回的句柄在后续的 GDI 函数中用于在 DC 中绘图。设备上下文是一个不透明的数据结构,其值由 GDI 内部使用。

创建兼容内存 DC

现在,我们通过调用来创建一个与我们捕获的 DC 兼容的已分配内存。

HDC CreateCompatibleDC(
  HDC hdc
);  

我们还存储要捕获区域的 `width`(宽度)和 `height`(高度)。

int ScreenWidth = GetDeviceCaps(CI.m_hScreenDC, HORZRES);
int ScreenHeight = GetDeviceCaps(CI.m_hScreenDC, VERTRES);

创建兼容位图

通过调用 CreateCompatibleBitmap,我们创建一个与指定设备上下文关联的设备兼容的位图。

HBITMAP CreateCompatibleBitmap(
  __in  HDC hdc,
  __in  int nWidth,
  __in  int nHeight
);  

SelectObject 用于将数据从屏幕复制到内存。

HGDIOBJ SelectObject(
  HDC hdc,   
  HGDIOBJ hgdiobj);   

调用 BitBlt

BitBlt 函数用于捕获区域的位块传输,通过将每个颜色的全部数据传输到目标设备上下文。

BOOL BitBlt(
  __in  HDC hdcDest,
  __in  int nXDest,
  __in  int nYDest,
  __in  int nWidth,
  __in  int nHeight,
  __in  HDC hdcSrc,
  __in  int nXSrc,
  __in  int nYSrc,
  __in  DWORD dwRop
);

此时,我们可以将时间戳和任何其他数据添加到捕获的图像上,如以下各节所述,但首先,让我们解释一下我们为什么以及如何使用 ImgSource

ImgSource - Target Eye 屏幕捕获使用的 SDK

Target Eye 的屏幕截图是通过一个名为 ImgSource 的图形库创建的,该库由 Smaller Animals 提供,这是一家成立于 1993 年、于 1999 年注册的私营公司,总部位于 北卡罗来纳州研究三角园区。我拥有该库的完整源代码许可证,因为像 Target Eye 这样的产品(以及购买它的客户,如执法机构)要求拥有所有组件的完整源代码,没有任何“黑盒子”。

总裁 Chris Losinger 自 2000 年以来提供了大量的帮助。需要注意的是,ImgSource 不是开源库,也不是免费产品,但我强烈推荐它。未注册用户可以测试该库,但图像上会绘制一个大的红色 **X**(包括本文附带的源代码)。该库拥有众多功能,以下是部分列表:

图像文件函数
  • 从文件或内存读取图像。
  • 将图像写入文件或内存。
  • 支持用户定义的输入和输出管理器 - 终极的 I/O 灵活性。
  • 可以选择逐行读取和写入大多数支持的图像文件格式。
  • 一次函数调用读取到 DIB。
  • 从图像文件中获取图像尺寸和位深度。
  • 大多数文件 I/O 例程和图像处理选项都提供了进度/取消回调选项。
  • 支持 JPG、GIF、BMP、TIFF、PhotoCD、Photoshop、WBMP、PNG、PCX、PAX、TLA、WMF、EMF、APM、PPM、PGM、PBM 和 TGA。
  • JPG、PNG、PAX 和 TLA 注释支持。
  • 多页 TIFF I/O 功能。
  • 动画 GIF I/O 功能。
  • JPEG_APPx 标记支持。
  • 从 JPG 和 TIFF 读取 EXIF 数据。
  • 将 EXIF 数据写入 JPG。
  • 从 JPG、PNG、TIFF 读取和写入 ICC 配置文件数据。
  • 从 JPG、TIFF 读取和写入 IPTC 数据。
  • 无损 JPG 转换。
  • 读取/写入 1 位 G3/G4 (FAX) TIFF。
  • 图像文件读取的自定义内存分配回调。
  • 还有更多...
Windows 位图函数
  • 从 RGB 缓冲区生成 HBITMAP。
  • 从 HBITMAP 生成 RGB 缓冲区。
  • 从 DIB 生成 8 位和 RGB 缓冲区。
  • 从 1 位、8 位或 RGB 图像生成 DIB。
  • 一次调用将 HBITMAP、DIB、RGB、RGBA 或 8 位图像绘制到 HDC。
  • 一次调用将 DC 捕获到 RGB 缓冲区。
  • 绘制透明 HBITMAP 或 RGBA 或 RGB 图像。
  • 还有更多...
图像处理函数
  • 将图像调整为任意大小。
  • 将任意卷积矩阵应用于图像。
  • 锐化、模糊和锐化蒙版图像。
  • 以任意角度旋转图像。
  • 检测图像旋转(倾斜)角度。
  • 自适应阈值处理,用于将灰度转换为单色。
  • 在图像上进行多边形和椭圆填充、泛洪填充或绘制线条。
  • 将查找表 (LUT) 应用于图像。
  • 从 RGB 图像生成灰度图像。
  • 从 24 位 RGB 或 32 位 RGBA 图像生成 8 位调色板图像(颜色量化)。
  • 在 24 位和 8 位图像之间转换。
  • 使用任何调色板,从 RGB 图像生成 8 位图像。
  • 在每组件 16 位和每组件 8 位之间转换。
  • 修改图像的亮度/对比度/饱和度。
  • 自动亮度校正。
  • 自动图像边框检测。
  • 自动去除灰尘和划痕。
  • 浮雕图像。
  • 多边形扭曲和点对点扭曲函数。
  • FFT 和形态学运算。
  • 去噪灰度及 RGB 图像。
  • 直方图拉伸/均衡化。
  • 许多 alpha 混合和图像合成/叠加函数。
  • 裁剪图像。
  • 垂直和水平翻转缓冲区。
  • 计算 RGB 图像中的颜色数量。
  • 在图像上绘制平滑文本。
  • 在 RGB 图像中替换颜色。
  • 其中许多函数可用于每组件 16 位数据。
  • 还有更多...

CaptureScreen 函数

Target Eye 的屏幕截图是通过调用 CaptureScreen() 函数生成的,该函数用于将整个屏幕捕获到本地隐藏位置的 JPG 文件中,然后将其发送到 FTP 服务器。

Target Eye 的第一个版本于 2000 年 5 月发布,并具有一个简单的屏幕捕获例程,该例程将文件保存为 *.bmp 格式,文件体积庞大,如果我们希望每分钟捕获一次屏幕,就会造成问题……后来,它被替换为一种有损的 JPG 文件,从而解决了这个问题。以下函数经历了漫长的 QA 过程,因为 Windows 的 GDI 会消耗一些内存,如果此类例程运行数天,由于 GDI 中的 bug,应用程序会消耗计算机资源,但通过仔细分配和释放必要资源并正确管理内存分配,这个问题得到了解决。

static HDC ScreenDC=NULL;
static HDC hmemDC=NULL;
static HBITMAP hmemBM=NULL;
static HGLOBAL hRGB=NULL;
void SimpleCapture()
{    
    ScreenDC = GetWindowDC(0);
    if (ScreenDC != NULL)
    {
        hmemDC = CreateCompatibleDC(ScreenDC);   
        if (hmemDC != NULL)
        {
            int ScreenWidth = GetDeviceCaps(ScreenDC, HORZRES);
            int ScreenHeight = GetDeviceCaps(ScreenDC, VERTRES);
            hmemBM = CreateCompatibleBitmap(ScreenDC, ScreenWidth, ScreenHeight);
            if (hmemBM != NULL)
            {
                HGDIOBJ result=SelectObject(hmemDC, hmemBM);   // copy screen to memory DC
                if((ULONG)result!=0L && (ULONG)result!=GDI_ERROR)
                {
                    GdiFlush();
                    if((BitBlt(hmemDC, 0, 0, ScreenWidth, 
                               ScreenHeight, ScreenDC, 0, 0, SRCCOPY)))
                    { 
                        hRGB = ISHBITMAPToRGB(hmemBM, (unsigned int *)&ScreenWidth, 
                        (unsigned int *)&ScreenHeight, hmemDC, NULL);
                        if (hRGB)
                        {
                            // for grayscale
                            //.. RGB -> JPG
                            HISDEST hDest = ISOpenFileDest("c:\\test.jpg");
                            if(hDest!=NULL)
                            {
                                static ULONG ImageCount=0;
                                int Wrote=ISWriteJPG(hDest, (BYTE *)hRGB, 
                                ScreenWidth, ScreenHeight, 25, 1, 24, NULL);
                                ISCloseDest(hDest);
                                TRACE("Created Image %ld\n",ImageCount++);
                            }
                            
                            //.. clean up
                            GlobalFree(hRGB);
                            hRGB=NULL;                            
                        }
                        else
                        {
                            // handle error
                        } 
                    }
                }
                DeleteObject(hmemBM);   
                hmemBM=NULL;
            }
            DeleteDC(hmemDC);  
            hmemDC=NULL;
        }
        DeleteDC(ScreenDC);
        ScreenDC=NULL;
    }
}  

如您所见,此版本中设置的“Quality”(质量)属性为 25(满值为 100)。

创建头部

自 Target Eye 2003 年和 2004 年版本以来,创建了一个包含屏幕截图宝贵信息的独特头部。该头部在屏幕图像被捕获到内存之后,并且在创建 JPG 文件之前创建。

// Now we create the header which is stamped on top of the screen
sprintf(s,"%ld",cycle);
CString FormattedTime=(CString)CTime::GetCurrentTime().FormatGmt
                      (L" %A, %d.%m.%y Time Zone: %z %H:%M Label = ");
if(TextToWrite!="") FormattedTime+=TextToWrite;                                
FormattedTime += s;
HPEN hNewPen;
hNewPen=CreatePen(PS_SOLID, 1, RGB(233,238,45));
SelectObject(CI.m_hmemDC, hNewPen);
TextOut(CI.m_hmemDC,0,0,FormattedTime,FormattedTime.GetLength());

命名文件

监控应用程序应有一个命名其创建的各种文件的方案,包括捕获的屏幕。附加源代码中的函数提供了一个非常简单的文件命名方案,基于文本“Screen”,后跟一个顺序编号和“.jpg”扩展名。

CString ComposeFileName(int i)
{ 
    CString result;
    result.Format(L"%s%d%s",(CString)L"screen",i,(CString)L".jpg");
    return result;
}  

Target Eye 使用一个更复杂的系统,该系统基于计数“周期”(即应用程序到达主事件循环的时间)。周期每 X 秒发生一次,X 是计算出的任何信息被请求捕获的最短时间。例如,如果操作员请求每 5 分钟复制一次文件,每 10 分钟捕获一次屏幕,并且没有其他定期信息收集请求,那么周期将是每 5 分钟。这是通过使用 Timer 设置的。

使用源代码

本文附加的源代码基于 Target Eye 的一个新版本,该版本是 100% Win32API(不使用 MFC)并支持 UNICODE。 以下屏幕截图是使用附加代码创建的,但为了发布此源代码,我删除了我自己的个人许可证密钥,因此屏幕截图顶部会有一个红色的 **X**。

运行一款电脑游戏(《大蓝熊的家》冒险),这是我从女儿 Emma 那里借来的,连续运行了数天,这使得我们能够确保屏幕捕获功能足够稳定,没有内存泄漏。

历史

  • 2014 年 6 月 12 日:初始版本

Michael Haephrati 2013 年度 CodeProject MVP

©2000-2010 Target Eye LTD (UK)

本文中包含的所有材料均受国际版权法保护,未经 Target Eye LTD (UK) 事先书面许可,不得使用、复制、分发、传播、展示、发布或广播。您不得更改或删除任何商标、版权或其他通知。
© . All rights reserved.