admin 管理员组

文章数量: 1086019

恶意代码研究

屏幕监控

  • 大致过程
    • 绘制桌面位图
    • 绘制鼠标光标
    • 保存位图图像
  • 相关API
    • GetDesktopWindow函数
    • GetDC函数
    • CreateCompatibleDC函数
    • GetSystemMetrics函数
    • CreateCompatibleBitmap函数
    • SelectObject函数
    • BitBlt函数
    • GetCursorInfo函数
    • GetIconInfo函数
  • 示例代码
  • 运行结果
  • IDA查看
  • 参考

通过对目标计算机进行截屏,并回传截屏数据,有利于黑客通过恶意代码直接了解目标计算机的工作状态,以监控计算机的屏幕。截屏频率够快甚至还能连成一段视频。

大致过程

大致可分为3步:

  • 绘制桌面位图
  • 绘制鼠标光标
  • 保存位图图像

绘制桌面位图

windows程序在屏幕上绘图时,它并不是将像素直接输出到设备上,而是将图绘制到由DC(设备上下文)表示的具有逻辑意义的“显示平面”上。

DC是windows中的一种数据结构,包括GDI需要的所有与显示界面相关的描述字段,包括
相连的物理设备的各种状态信息。

windows程序从GDI(图形设备接口)获取设备上下文句柄(HDC),并每次调用完GDI输出函数后将句柄返回给HDC。

该过程流程如下:

  • 获取桌面窗口句柄(GetDesktopWindow),获取桌面窗口设备上下文句柄(GetDC),创建与桌面窗口兼容的内存上下文(CreateCompatibleDC),用来绘制位图。
  • 获取计算机屏幕的宽和高的像素值(GetSystemMetrics),创建兼容位图句柄(CreateCompatibleBitmap),并将其作为绘制桌面画面的位图句柄。
  • 将上述创建的兼容位图的句柄设置到兼容内存设备的上下文中(SelectObject),把桌面窗口设备上下文的内容绘制到兼容内存设备的上下文中(BitBlt)。这样,兼容位图的内容便是桌面画面的内容。

绘制鼠标光标

使用GDI获取的桌面截图并不包括鼠标。所以需要程序单独在桌面位图上绘制鼠标光标。流程如下:

  • 获取鼠标光标信息CURSORINFOGetCursorInfo),包括鼠标此时的位置信息,鼠标的光标句柄等。
  • 根据光标句柄,获取光标对应的图标信息ICONINFOGetIconInfo
  • 2次分别经过前景图和屏蔽图的绘制(BitBlt)(前景图和屏蔽图具体可参考windows黑客编程技术详解)

保存位图图像

可以使用基于CImage类的方式保存图像,需要引入头文件atlimage.h(VC6.0不支持),流程如下:

  • 将位图句柄附加到CImage对象上(CImage::Attach)。
  • 保存图像文件(CImage::Save,该方法支持PNG,JPG,GIF,BMP等格式图片的生成)。

相关API

GetDesktopWindow函数

该函数返回桌面窗口的句柄。桌面窗口覆盖整个屏幕。桌面窗口是一个要在其上绘制所有的图标和其他窗口的区域。

HWND WINAPI GetDesktopWindow(VOID);
  • 返回值
    • 桌面窗口句柄

GetDC函数

Windows不允许程序员直接访问硬件,它对屏幕的操作是通过环境设备,也就是DC来完成的。屏幕上的每一个窗口都对应一个DC,可以把DC想象成一个视频缓冲区,对这这个缓冲区的操作,会表现在这个缓冲区对应的屏幕窗口上。

GetDC函数为一个指定窗口的客户端区域或者整个屏幕从一个设备上下文(DC)中提取一个句柄。你可以使用这个返回的句柄。

HDC WINAPI GetDC(__in_opt HWND hWnd);
  • 参数

    • hWnd:检索设备上下文环境的句柄。如果为NULL,则GetDC将检索整个屏幕的设备上下文环境。
  • 返回值

    • 执行成功,返回指定窗口客户端区域的DC的句柄。
    • 执行失败,返回NULL

CreateCompatibleDC函数

该函数创建一个与指定设备兼容的内存设备上下文环境(相当于创建一个自己可控制的DC)

HDC WINAPI CreateCompatibleDC( __in_opt HDC hdc);
  • 参数

    • hWnd:现有设备上下文环境的句柄,如果该句柄为NULL,该函数创建一个与应用程序的当前显示器兼容的内存设备上下文环境。
  • 返回值

    • 执行成功,返回内存设备上下文环境的句柄。
    • 执行失败,返回NULL

GetSystemMetrics函数

用于得到被定义的系统数据或者系统配置信息。

int WINAPI GetSystemMetrics(__in int nIndex);
  • 参数
    • nIndex:一个索引,这个索引有75个标识符,通过设置不同的标识符就可以获取系统分辨率、窗体显示区域的宽度和高度、滚动条的宽度和高度等信息。

下面列举一些系统信息,具体可参考C# API GetSystemMetrics(int nIndex)用法及值说明

含义
SM_CMOUSEBUTTONS返回值为系统支持的鼠标键数,返回0,则系统中没有安装鼠标
SM_CXSCREEN屏幕宽度
SM_CYSCREEN屏幕高度
SM_CXFULLSCREEN获取最大化窗体的显示区域宽度
SM_CYFULLSCREEN获取最大化窗体的显示区域高度
  • 返回值
    • 相关系统信息。

CreateCompatibleBitmap函数

该函数创建与指定的设备环境相关的设备兼容的位图

HBITMAP WINAPI CreateCompatibleBitmap( __in HDC hdc,__in int nWidth, __in int nHeight);
  • 参数

    • hdc:设备环境句柄。
    • nWidth:指定位图的宽度,单位为像素。
    • nHeight:指定位图的高度,单位为像素。
  • 返回值

    • 执行成功,返回位图的句柄。
    • 执行失败,返回NULL

SelectObject函数

把一个对象(位图、画笔、画刷等)选入指定的设备描述表。新的对象代替同一类型的老对象。

HGDIOBJ WINAPI SelectObject(__in HDC hdc, __in HGDIOBJ hgdiobj);
  • 参数

    • hdc:设备描述表句柄(要载入的设备描述表句柄)。
    • hgdiobj:选择要载入的对象的句柄。
  • 返回值

    • 选择对象不是区域并且函数执行成功,返回被取代的对象的句柄。
    • 选择对象是区域并且函数执行成功,返回如下一值。

BitBlt函数

将一幅位图从一个设备场景复制到另一个。

BOOL  WINAPI BitBlt( __in HDC hdcDest, __in int nXDest, __in int nYDest, __in int nWidth, __in int nHeight, __in_opt HDC hdcSrc, __in int nXSrc, __in int nYSrc, __in DWORD rop);
  • 参数
    • hdcDest:指向目标设备环境的句柄
    • nXDest:指定目标矩形区域左上角X轴逻辑坐标
    • nYDest:指定目标矩形区域左上角Y轴逻辑坐标
    • nWidth:指定源和目标矩形区域的逻辑宽度
    • nHeight:指定源和目标矩形区域的逻辑高度
    • hdcSrc:指向源设备环境的句柄
    • nXSrc:指定源矩形区域左上角X轴逻辑坐标
    • nYSrc:指定源矩形区域左上角Y轴逻辑坐标
    • rop:指定光栅操作代码。这些代码将定义源矩形区域的颜色数据,如何与目标矩形区域的颜色数据组合以完成最后的颜色。

下面列出了一些常见的光栅操作代码

含义
SRCCOPY将源矩形区域直接拷贝到目标矩形区域
SRCERASE通过使用AND(与)操作符将目标矩形区域颜色取反后与源矩形区域的颜色值合并
SRCINVERT通过使用布尔型的XOR(异或)操作符将源和目标矩形区域的颜色合并
SRCPAINT通过使用布尔型的OR(或)操作符将源和目标矩形区域的颜色合并
WHITENESS使用与物理调色板中索引1有关的颜色填充目标矩形区域。(对于缺省物理调色板来说,这个颜色就是白色)
  • 返回值
    • 操作成功,返回true
    • 操作失败,返回false

GetCursorInfo函数

获取光标信息

BOOL WINAPI GetCursorInfo(__inout PCURSORINFO pci);

参数:

  • pci:指向光标的结构体

光标结构体定义如下:

typedef struct {DWORD cbSize;DWORD flags;HCURSOR hCursor;POINT ptScreenPos;
} CURSORINFO, *PCURSORINFO, *LPCURSORINFO;

GetIconInfo函数

获取光标图标信息

BOOL WINAPI GetIconInfo(__in HICON hIcon,__out PICONINFO piconinfo);

示例代码

#include <Windows.h>
#include <atlimage.h>
#include <stdio.h>
#include <tchar.h>
#include <SDKDDKVer.h>
// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
// WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。BOOL SaveBmp(HBITMAP hBmp)
{CImage image;// 附加位图句柄image.Attach(hBmp);// 保存成jpg格式图片image.Save("mybmp1.jpg");return TRUE;
}BOOL PaintMouse(HDC hdc)
{HDC bufdc = NULL;CURSORINFO cursorInfo = { 0 };ICONINFO iconInfo = { 0 };HBITMAP bmpOldMask = NULL;bufdc = ::CreateCompatibleDC(hdc);::RtlZeroMemory(&iconInfo, sizeof(iconInfo));cursorInfo.cbSize = sizeof(cursorInfo);// 获取光标信息::GetCursorInfo(&cursorInfo);// 获取光标图标信息::GetIconInfo(cursorInfo.hCursor, &iconInfo);// 绘制 白底黑鼠标(AND)bmpOldMask = (HBITMAP)::SelectObject(bufdc, iconInfo.hbmMask);     ::BitBlt(hdc, cursorInfo.ptScreenPos.x, cursorInfo.ptScreenPos.y, 20, 20,bufdc, 0, 0, SRCAND);// 绘制 黑底彩色鼠标(OR)::SelectObject(bufdc, iconInfo.hbmColor);::BitBlt(hdc, cursorInfo.ptScreenPos.x, cursorInfo.ptScreenPos.y, 20, 20,bufdc, 0, 0, SRCPAINT);// 释放资源::SelectObject(bufdc, bmpOldMask);::DeleteObject(iconInfo.hbmColor);::DeleteObject(iconInfo.hbmMask);::DeleteDC(bufdc);return TRUE;
}BOOL ScreenCapture()
{// 获取桌面窗口句柄HWND hDesktopWnd = ::GetDesktopWindow();// 获取桌面窗口DCHDC hdc = ::GetDC(hDesktopWnd);// 创建兼容DCHDC mdc = ::CreateCompatibleDC(hdc);// 获取计算机屏幕的宽和高int dwScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);int dwScreenHeight = ::GetSystemMetrics(SM_CYSCREEN);// 创建兼容位图HBITMAP bmp = ::CreateCompatibleBitmap(hdc, dwScreenWidth, dwScreenHeight);// 选中位图HBITMAP holdbmp = (HBITMAP)::SelectObject(mdc, bmp);// 将窗口内容绘制到位图上::BitBlt(mdc, 0, 0, dwScreenWidth, dwScreenHeight, hdc, 0, 0, SRCCOPY);//绘制鼠标PaintMouse(mdc);//保存为图片SaveBmp(bmp);return true;
}int _tmain(int argc, _TCHAR* argv[])
{if (FALSE == ScreenCapture()){printf("Screen Cpature Error.\n");}printf("Screen Cpature OK.\n");system("pause");return 0;
}

运行结果

可以看到编译后的exe文件的目录下生成了mybmp1.jpg

截屏一部分内容如下:

IDA查看

main函数
ScreenCapture函数
PaintMouse函数,可以看到形参类型还是HDC
SaveBmp函数,这个就比较有意思了,形参成了int类型(本来HBITMAP类型也是句柄),CImage类的成员方法也没显示出来
点进sub_43B8F6,之后一路点进
到处都有头文件路径,超出知识范围,希望有大神赐教下。

参考

  • windows黑客编程技术详解

本文标签: 恶意代码研究