现象:BitBlt 在 LSASS.EXE 进程中执行失败,GetLastError 返回 6(错误的句柄)
描述:对截屏类进行了两个测试,首先,在 exe 中使用该类,截屏成功。
      然后,在 DLL 中使用该类,并使用 rundll32 执行该 dll ,截屏操作成功。可是,
      使用线程注入方式将 dll 注入到 lsass.exe 中运行时,截屏失败,报告说 bitblt
      错误。请问到底是怎么回事?// TCapture.h#ifndef TCaptureH
#define TCaptureH
//----------------------------------------------------------------------------------class TScreenCapture
{
public:
TScreenCapture();
BOOL        Capture();
LPSTR Buffer();
DWORD       Length();
~TScreenCapture();
private:
LPSTR               FBuffer;
DWORD               FLength;
BOOL                FSucceed; INT                 FWidth;
INT                 FHeight; HDC                 HdcDesktop;
HWND                HwndDesktop; HDC                 HdcMemory;
HBITMAP             HBmpMemory;
PVOID               PDatBitmap; BITMAPINFO          BmpInfo;
BITMAPFILEHEADER    BmpHeader;
};//----------------------------------------------------------------------------------
#endif// TCapture.cpp
#include <stdio.h>
#include <windows.h>#include "TCapture.h"
//------------------------------------------------------------------------------------------------------------
TScreenCapture::TScreenCapture()
{
FBuffer = NULL;
FLength = 0;
}
//------------------------------------------------------------------------------------------------------------
BOOL TScreenCapture::Capture()
{
FWidth = GetSystemMetrics( SM_CXSCREEN );
FHeight = GetSystemMetrics( SM_CYSCREEN ); memset(&BmpInfo, 0, sizeof(BITMAPINFO));
BmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BmpInfo.bmiHeader.biHeight = FHeight;
BmpInfo.bmiHeader.biWidth = FWidth;
BmpInfo.bmiHeader.biPlanes = 1;
BmpInfo.bmiHeader.biBitCount = 16;
BmpInfo.bmiHeader.biCompression = BI_RGB;
BmpInfo.bmiHeader.biSizeImage = 2*FWidth*FHeight; memset(&BmpHeader, 0, sizeof(BITMAPFILEHEADER));
BmpHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
BmpHeader.bfSize = BmpInfo.bmiHeader.biSizeImage + BmpHeader.bfOffBits;
BmpHeader.bfType = 0x424D; FLength = BmpHeader.bfSize;
FSucceed = FALSE; HwndDesktop = GetDesktopWindow();
HdcDesktop = GetDC( HwndDesktop ); if ( HdcDesktop != NULL )
{
HdcMemory = CreateCompatibleDC( HdcDesktop ); if ( HdcMemory != NULL )
{
HBmpMemory = CreateDIBSection(HdcDesktop, &BmpInfo, DIB_RGB_COLORS, &PDatBitmap, NULL, 0); if ( HBmpMemory != NULL )
{
SelectObject(HdcMemory, HBmpMemory); if ( BitBlt(HdcMemory, 0, 0, FWidth, FHeight, HdcDesktop, 0, 0, SRCCOPY) )
{
if ( FBuffer != NULL )
{
free( FBuffer );
} FBuffer = (LPSTR)malloc( FLength ); if ( FBuffer != NULL )
{
memcpy(FBuffer, &BmpHeader, sizeof(BITMAPFILEHEADER));
memcpy(FBuffer+sizeof(BITMAPFILEHEADER), &BmpInfo, sizeof(BITMAPINFOHEADER));
memcpy(FBuffer+BmpHeader.bfOffBits, PDatBitmap, BmpInfo.bmiHeader.biSizeImage); FSucceed = TRUE;
}
} DeleteObject( HBmpMemory );
} DeleteDC( HdcMemory );
} ReleaseDC(HwndDesktop, HdcDesktop);
} return FSucceed;
}//------------------------------------------------------------------------------------------------------------
LPSTR TScreenCapture::Buffer()
{
return FBuffer;
}//------------------------------------------------------------------------------------------------------------
DWORD TScreenCapture::Length()
{
return FLength;
}
//------------------------------------------------------------------------------------------------------------
TScreenCapture::~TScreenCapture()
{
if ( FBuffer != NULL )
{
free( FBuffer );
}
}

解决方案 »

  1.   

    补充以下,上面这个类主要功能就是 保存当前屏幕的内容到内存中,内存中的数据格式是一个完整后效的 BMP 文件,可以直接将内存中的数据保存为 BMP文件。
      

  2.   

    LSASS.EXE 和用户不再同一个桌面。
      

  3.   

    问题解决。根据 wangk(倒之) 的回答,从网上找了 n 多关于 服务与桌面交互的东西,然后经过自己测试,确实是这个问题。这里有点奇怪的地方就是,通过 Windows 服务管理器,我在 lsass 服务的配置中,选中了“允许服务与桌面交互”选项,并且重新启动计算机后,仍然发生 Bitblt 错误,可是通过下面的代码就能解决这个问题,不知道为什么。公布一下解决方法:BOOL WINAPI ScreenCaptureDesktop()
    {
        BOOL    IsSucceed = FALSE;    // 保存当前窗口属性
        HWINSTA hwinstaLsass = GetProcessWindowStation();    if ( hwinstaLsass != NULL )
        {
            // 保存当前线程桌面
            HDESK hdeskLsass = GetThreadDesktop( GetCurrentThreadId() );        if ( hdeskLsass != NULL )
            {
                // 获取目标窗口属性
                HWINSTA hwinstaUser = OpenWindowStation(DefaultWindowStation, FALSE, MAXIMUM_ALLOWED);            if ( hwinstaUser != NULL )
                {
                    // 设置目标窗口属性
                    if ( SetProcessWindowStation(hwinstaUser) )
                    {
                        // 获取目标桌面
                        HDESK hdeskUser = OpenDesktop(DefaultDesktop, 0, FALSE, MAXIMUM_ALLOWED);                    if ( hdeskUser != NULL )
                        {
                            // 设置线程桌面
                            if ( SetThreadDesktop(hdeskUser) )
                            {
                                // to do something // 完成需要与桌面进行交互的功能
                                IsSucceed = ScreenCapture(); // 我在这里完成截屏操作                            // 恢复线程原始桌面
                                SetThreadDesktop( hdeskLsass );
                            }                        // 关闭目标桌面
                            CloseDesktop( hdeskUser );
                        }                    // 恢复进程原始窗口属性
                        SetProcessWindowStation( hwinstaLsass );
                    }                // 关闭目标窗口属性
                    CloseWindowStation( hwinstaUser );
                }
            }
        }    return IsSucceed;
    }
    此外,还需要更正一下上面的截屏类,其中 BOOL TScreenCapture::Capture() 中 BmpHeader.bfType = 0x424D; 应该为 BmpHeader.bfType = 0x4D42; 好了,再次感谢  wangk(倒之) ,没有你的帮助,我就不能这么快的解决问题。结帖了。