如何在SYSTEM权限下实现屏幕监控

屏幕监控是远控软件的基本功能之一。
版权声明
作者:iprowq
现在很多远控程序的服务端通常为DLL形式,通过远程线程注入等方法插入到services、svchost等SYSTEM权限的进程中去,而此时常规的屏幕监控就会失效(这是因为与SYSTEM权限进程关联的窗口站、桌面与普通进程不同)。
如何才能在SYSTEM权限下实现屏幕监控呢?

一、 屏幕监控的基本原理

屏幕监控简单说就是对进程的当前桌面进行截屏存成位图,然后将此位图数据传输到远程。
对桌面进行截图需要通过一系列Windows GDI API来完成的。
首先通过CreateDC,CreateCompatibleDC,CreateCompatibleBitmap,SelectObject等API将“DISPLAY”驱动器的设备上下文与位图句柄关联起来。
然后通过GetStockObject,GetDC,SelectPalette等API处理调色板。
最后在一个循环中通过GetDIBits将所有水平线像素数据存入到缓冲区中去。

这个缓冲区就是我们想要的位图数据,只要将这些数据组织一下,就可以当成位图显示出来了。通过连续传输位图,就可以实时对远程屏幕进行监控了。这个过程比较简单,就不浪费文字了。

二、窗口站与桌面

首先必须了解几个重要的概念:
窗口站(WindowsStation)和桌面(Desktop)是Windows操作系统底层暴露给Windows API的执行体对象(Windows内部有两种类型的对象:执行体对象和内核对象。执行体对象指由执行体的各种组件如进程管理器、内存管理器等等所实现的对象。内核对象是由Windows内核实现的一组更基本的对象)。

其中,窗口站对象包含了一个剪贴板、一组全局原子和一组桌面对象。桌面对象是一个被包含在窗口站内部的对象,桌面对象有一个逻辑显示器表面,其中包含了窗口、菜单和钩子。

0号窗口站(WinSta0)和默认的桌面对象(default desktop)是有Winlogon进程创建的。窗口站是会话(Session)的下一层组织结构。一个会话可以有多个窗口站,但同一时刻只能有一个窗口站可以与用户进行交互。每个窗口站有自己的剪贴板,可以有多个桌面。Winlogon进程调用NtUserCreateWindowsStation函数创建窗口站,再调用NtUserCreateDesktop来创建桌面。它首先会创建一个名为Winlogon的桌面供自己使用(Windows登录界面就属于属于这个桌面),然后再创建一个名为Default的桌面给应用程序使用。创建完桌面后,Winlogon调用SetActiveDesktop函数将Winlogon桌面设置为当前的活动桌面。

之后,Winlogon会创建用于管理系统服务的服务管理器(Service.exe)和本地安全认证子系统(LSASS.exe)。用户登陆信息被验证后,Winlogon会将应用程序桌面激活,启动UserInit程序,UserInit会运行注册表中定义的登录脚本,然后启动操作系统外壳程序(Shell-默认是explorer.exe)。这是SYSTEM权限进程和普通用户进程逻辑显示器桌面分离的开始。在以后进程创建CreateProcess的过程中,如果没有指定桌面,那么进程就会与调用者的当前桌面关联在一起。

在实际测试中,发现services、svchost这些进程似乎没有关联任何桌面(截的屏都是黑屏)。普通的进程都是Default桌面,登录界面是Winlogon桌面。所以,当dll插入到service.exe等进程中的时候,要想实现截屏必须将进程与Default桌面关联,用户注销、离开或未登录时就要将进程与Winlogon桌面关联。

Windows给我们提供的一些API允许我们干这些事。

首先可以通过OpenWindowStation打开一个窗口站对象,然后通过SetProcessWindowStation将进程与窗口站关联,通过OpenDesktop打开一个桌面对象,再通过SetThreadDesktop将线程与这个桌面关联。这样service.exe就可以实现截屏了。但如何才能知道当前用户在哪个桌面呢?可以通过下列函数实现:
OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, MAXIMUM_ALLOWED);//打开输入桌面
GetUserObjectInformation(hActiveDesktop, UOI_NAME, pvInfo, sizeof(pvInfo), &dwLen); //获取指定桌面对象的信息,一般情况和屏保状态为default,登陆界面为winlogon

pvInfo缓冲区包含的就是当前桌面。这样就可以放心的调用OpenDesktop打开它了。

 進行桌面切換需要有System權限,如果是以服務啟動的話就是了,也可以用注入或其它的方式。

附资料:(转自:http://topic.csdn.net/t/20050825/14/4231020.html)

在winlogon注册了SAS后,就调用GINA生成3个桌面系统,在用户需要的时候使用,它们分别为: 
 ◇Winlogon   桌面用户在进入登录界面时,就进入了Winlogon桌面。而我们看到的登录对话框,只是GINA负责显示的。如果用户取消以“欢迎屏幕”方式登录,在进入   WindowsXP中任何时候按下“Ctrl-Alt-Delete”,都会激活Winlogon桌面,并显示图5的“Windows安全”对话框。(注意,Winlogon桌面并不等同对话框,对话框只是Winlogon调用其他程序来显示的) 
 ◇用户桌面   用户桌面就是我们日常操作的桌面,它是系统最主要的桌面系统。用户需要提供正确的帐号和密码,成功登录后才能显示“用户桌面”。而且,不同的用户,winlogon会根据注册表中的信息和用户配置文件来初始化用户桌面。 
 ◇   屏幕保护桌面屏幕保护桌面就是屏幕保护,包括“系统屏幕保护”和“用户屏幕保护”。在启用了“系统屏幕保护”的前提下,用户未进行登录并且长时间无操作,系统就会进入   “系统屏幕保护”;而对于“用户屏幕保护”来说,用户要登录后才能访问,不同的用户可以设置不同的“用户屏幕保护”。

 

我们使用用户帐户登录以后,看到的桌面,是WinSta0下的Default桌面
WinSta0下有3个桌面:
WinLogon :当用户登录以后,WinLogon.exe切换到Default桌面.
Default :这是Explorer.exe和所有用户程序窗口出现的地方,也就是我们通常使用windows看见的地方.应用程序就运行在这个桌面上
Screen saver :系统空闲的时候,运行屏保的桌面.
你们每个用户都有各自的Default桌面,但是WinLogon.exe是运行在winlogon桌面下的系统级进程。
win32k.sys里有对winlogon名字桌面的引用。
Winlogon.exe被所有用户共享一份实例,跟你的普通进程是不同的,你的普通进程只是自己的Default桌面下的各自的进程。

 

1.会话问题
ProcessIdToSessionId获得自己的会话Id
可以把内核对象强制指定为全局内核对象,就可以在不同会话直接共享了。
创建命名内核对象时加global前缀,CreateXXX(XXX, XXX, "Global\\内核对象名")
内核对象能共享了,进程就可以随意通信了。
2.另一个问题是权限问题
两个会话权限不同的话,低权限没办法打开高权建立的内核对象,即使是全局的
给一个函数,高权限降权内核对象的,调用后低权限打开高权限的对象,之后开始随意共享吧

LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = TEXT("S:(ML;;NW;;;LW)");
BOOL SetObjectToLowIntegrity (HANDLE hObject, SE_OBJECT_TYPE type = SE_KERNEL_OBJECT)
{
BOOL bRet = FALSE;
DWORD dwErr = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR pSD = NULL;
PACL pSacl = NULL;
BOOL fSaclPresent = FALSE;
BOOL fSaclDefaulted = FALSE;
if (ConvertStringSecurityDescriptorToSecurityDescriptor(LOW_INTEGRITY_SDDL_SACL_W,
SDDL_REVISION_1,
&pSD,
NULL))
{
if (GetSecurityDescriptorSacl(pSD,
&fSaclPresent,
&pSacl,  
&fSaclDefaulted))
{
dwErr = SetSecurityInfo (hObject,  
type,
LABEL_SECURITY_INFORMATION,
NULL,
NULL,
NULL,
pSacl);
bRet = (ERROR_SUCCESS == dwErr);
}
LocalFree(pSD);
}
return bRet;
}
3.这么做有个问题,就是每个会话里都要有一个你的进程,不太爽
楼上说的方法没试过,学习了,不过看起来也是要在每个会话里都拉进程起来

转自:http://topic.csdn.net/u/20101201/08/0DFD70DD-F5C6-4B5D-96BD-C36E175FC6FD.html


完整代码如下:
BOOL OpenDesktop(LPCWSTR szName)
{
    WCHAR pvInfo[128] = {0};
    WCHAR tmp[1024] = {0};

    if(szName != NULL)
        lstrcpy(pvInfo, szName);
    else
    {

        HDESK hActiveDesktop;
        DWORD dwLen;
        hActiveDesktop = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, MAXIMUM_ALLOWED);
        if(!hActiveDesktop)//打开失败
        {
             return FALSE;
        }
        //获取指定桌面对象的信息,一般情况和屏保状态为default,登陆界面为winlogon
        GetUserObjectInformation(hActiveDesktop, UOI_NAME, pvInfo, sizeof(pvInfo), &dwLen);
        if(dwLen==0)//获取失败
        {
            return FALSE;
        }
        CloseDesktop(hActiveDesktop);
//打开winsta0
    m_hwinsta = OpenWindowStation(_T("winsta0"), FALSE,                           
                                  WINSTA_ACCESSCLIPBOARD   |
                                  WINSTA_ACCESSGLOBALATOMS |
                                  WINSTA_CREATEDESKTOP     |
                                  WINSTA_ENUMDESKTOPS      |
                                  WINSTA_ENUMERATE         |
                                  WINSTA_EXITWINDOWS       |
                                  WINSTA_READATTRIBUTES    |
                                  WINSTA_READSCREEN        |
                                  WINSTA_WRITEATTRIBUTES);
    if (m_hwinsta == NULL){
        return FALSE;
       }

    if (!SetProcessWindowStation(m_hwinsta)){
          return FALSE;
       }

//打开desktop
m_hdesk = OpenDesktop(pvInfo, 0, FALSE,                 
                            DESKTOP_CREATEMENU |
                            DESKTOP_CREATEWINDOW |
                            DESKTOP_ENUMERATE    |
                            DESKTOP_HOOKCONTROL |
                            DESKTOP_JOURNALPLAYBACK |
                            DESKTOP_JOURNALRECORD |
                            DESKTOP_READOBJECTS |
                            DESKTOP_SWITCHDESKTOP |
                            DESKTOP_WRITEOBJECTS);
       if (m_hdesk == NULL){
           return FALSE;
       }

       SetThreadDesktop(m_hdesk);
       return TRUE;
}
代码有点乱,将就一下!

三、后记


上面的代码只是针对service.exe这样的进程,要想做的通用还要再加些代码。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值