创建用户桌面进程(突破Session 0隔离)

创建用户桌面进程(突破Session 0隔离)

这里就引入了Windows Vista之后的Session 0隔离机制。

有些服务可能需要在用户界面上显示对话框,或需要与用户的应用程序通讯,这种类型的功能“通常”属于Windows XP服务,因为在Windows XP中,这样做很容易。如果服务恰好需要显示某些用户界面对象,例如对话框,或者需要与应用程序通讯,则在Windows 7下运行可能会遇到问题。

如果在Windows 7上运行需要显示对话框的服务,此时并不能看到所需的对话框,在任务栏会上看到一个闪烁的图标。而且,如果单击这个闪烁图标,还会看到一个对话框。更具体来说,在Windows 7中运行时,可能会遇到下列一个或多个现象,这个服务可能会:

正常运行,但无法完成既定工作,同时耗费大量CPU时钟和内存。
正常运行,但其他进程无法与该服务通讯,该服务业无法与用户或其他应用程序/服务通讯。
尝试通过Windows消息机制与用户应用程序通讯,但Windows消息机制无法实现目标。
在任务栏上显示一个闪烁的图标,代表该服务希望与桌面交互。
在Windows XP、Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Session)运行,而这个会话是由第一个登录到控制台的用户启动的。该会话就叫做Session 0,如下图所示,在Windows Vista之前,Session 0不仅包含服务,也包含标准用户应用程序。

如图所示:
在这里插入图片描述

将服务和用户应用程序一起在Session 0中运行会导致安全风险,因为服务会使用提升后的权限运行,而用户应用程序使用用户特权(大部分都是非管理员用户)运行,这会使得恶意软件以某个服务为攻击目标,通过“劫持”该服务,达到提升自己权限级别的目的。

从Windows Vista开始,只有服务可以托管到Session 0中,用户应用程序和服务之间会被隔离,并需要运行在用户登录到系统时创建的后续会话中。例如第一个登录的用户创建 Session 1,第二个登录的用户创建Session 2,以此类推,如下图所示。
在这里插入图片描述

使用不同会话运行的实体(应用程序或服务)如果不将自己明确标注为全局命名空间,并提供相应的访问控制设置,将无法互相发送消息,共享UI元素,或共享内核对象。这一过程如下图所示:、

上一篇中,因为我们使用全局的命名空间,因此实现的共享内核对象。

关于Session 0,就简单介绍这些,具体的详细内容,请查阅微软的官方文档。我们今天的重点并不在此。

回到正題。虽然Windows 7的Session 0给服务层和应用层的通信造成了很大的难度,但并不代表没有办法实现服务层与应用层的通信与交互。

微软提供了一系列WTS( Windows Terminal Service Windows终端服务)开头的函数,来完成服务层与应用层的交互。

如图所示:

在这里插入图片描述

实现代码如下:

[cpp] view plain copy
DWORD _stdcall CATLDemoServiceModule::LaunchWin7SessionProcess( LPTSTR lpCommand )
{
DWORD dwRet = 0;
PROCESS_INFORMATION pi;
STARTUPINFO si;
DWORD dwSessionId;//当前会话的ID
HANDLE hUserToken = NULL;//当前登录用户的令牌
HANDLE hUserTokenDup = NULL;//复制的用户令牌
HANDLE hPToken = NULL;//进程令牌
DWORD dwCreationFlags;

//得到当前活动的会话ID,即登录用户的会话ID  
dwSessionId = WTSGetActiveConsoleSessionId();  
do   
{  
    WTSQueryUserToken(dwSessionId,&hUserToken);//读取当前登录用户的令牌信息  
    dwCreationFlags = NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE;//创建参数  

    ZeroMemory(&si,sizeof(STARTUPINFO));  
    ZeroMemory(&pi,sizeof(pi));  

    si.cb = sizeof(STARTUPINFO);  
    si.lpDesktop = L"winsta0\\default";//指定创建进程的窗口站,Windows下唯一可交互的窗口站就是WinSta0\Default  

    TOKEN_PRIVILEGES tp;  
    LUID luid;  

    //打开进程令牌  
    if (!OpenProcessToken(GetCurrentProcess(),  
        TOKEN_ADJUST_PRIVILEGES|  
        TOKEN_QUERY|  
        TOKEN_DUPLICATE|  
        TOKEN_ASSIGN_PRIMARY|  
        TOKEN_ADJUST_SESSIONID|  
        TOKEN_READ|  
        TOKEN_WRITE,&hPToken))  
    {  
        dwRet = GetLastError();  
        break;  
    }  
      
    //查找DEBUG权限的UID  
    if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))  
    {  
        dwRet = GetLastError();  
        break;  
    }  
      
    //设置令牌信息  
    tp.PrivilegeCount = 1;  
    tp.Privileges[0].Luid = luid;  
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  

    //复制当前用户的令牌  
    if (!DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,  
        TokenPrimary,&hUserTokenDup))  
    {  
        dwRet  = GetLastError();  
        break;  
    }  
      
    //设置当前进程的令牌信息  
    if (!SetTokenInformation(hUserTokenDup,TokenSessionId,(void*)&dwSessionId,sizeof(DWORD)))  
    {  
        dwRet = GetLastError();  
        break;  
    }  
      
    //应用令牌权限  
    if (!AdjustTokenPrivileges(hUserTokenDup,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),  
        (PTOKEN_PRIVILEGES)NULL,NULL))  
    {  
        dwRet = GetLastError();  
        break;  
    }  
      
    //创建进程环境块,保证环境块是在用户桌面的环境下  
    LPVOID pEnv = NULL;  
    if (CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))  
    {  
        dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;  
    }  
    else  
    {  
        pEnv = NULL;  
    }  
      
    //创建用户进程  
    if (!CreateProcessAsUser(hUserTokenDup,NULL,lpCommand,NULL,NULL,FALSE,  
        dwCreationFlags,pEnv,NULL,&si,&pi))  
    {  
        dwRet = GetLastError();  
        break;  
    }  
} while (0);  

//关闭句柄  
if (NULL != hUserToken)  
{  
    CloseHandle(hUserToken);  
}  

if (NULL != hUserTokenDup)  
{  
    CloseHandle(hUserTokenDup);  
}  

if (NULL != hPToken)  
{  
    CloseHandle(hPToken);  
}  

return dwRet;  

}

调用方式:

[cpp] view plain copy
LaunchWin7SessionProcess(“C:\Windows\notepad.exe”);

这样,服务程序就可以在用户的桌面上创建一个应用程序了,而且具有System权限。

总结一下服务程序与用户桌面程序通信的解决办法:

如果服务需要通过发送消息的方式与用户交互,请使用WTSSendMessage函数。在功能上,该函数几乎与MessageBox相同,对于不需要过于完整的UI的服务,这是一种简单易行的方法,而且足够安全,因为所显示的信息框无法用于夺取对底层服务的控制。
如果您的服务需要更完整的UI,请使用CreateProcessAsUser函数在发起请求的用户的桌面上创建进程。但是这里需要注意,您依然需要在新创建的进程和原始服务之间进行通讯,因此这里就需要用到下一点内容。
如果需要双向交互,请使用Windows Communication Foundation(WCF),这是一种用于.NET Remoting命名管线或任何其他进程间通讯(IPC)的机制(Windows消息除外),可用于实现跨会话的通讯。如果需要提升,WCF和Remoting是一种比直接提升用户(假设UAC没有过关闭)更安全的做法。
请确保需要跨会话共享的内核对象使用了带有Global\字符串的名称前缀,该字符串意味着这个对象属于会话全局(Session-Global)命名空间。

至些,关于服务程序与用户桌面程序的相关技术点,总结完毕,欢迎大家补充和提出改进意见。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值