创建用户桌面进程(突破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终端服务)开头的函数,来完成服务层与应用层的交互。

如图所示:




实现代码如下:

  1. DWORD _stdcall CATLDemoServiceModule::LaunchWin7SessionProcess( LPTSTR lpCommand )  
  2. {  
  3.     DWORD dwRet = 0;  
  4.     PROCESS_INFORMATION pi;  
  5.     STARTUPINFO si;  
  6.     DWORD dwSessionId;//当前会话的ID  
  7.     HANDLE hUserToken = NULL;//当前登录用户的令牌  
  8.     HANDLE hUserTokenDup = NULL;//复制的用户令牌  
  9.     HANDLE hPToken = NULL;//进程令牌  
  10.     DWORD dwCreationFlags;  
  11.   
  12.     //得到当前活动的会话ID,即登录用户的会话ID  
  13.     dwSessionId = WTSGetActiveConsoleSessionId();  
  14.     do   
  15.     {  
  16.         WTSQueryUserToken(dwSessionId,&hUserToken);//读取当前登录用户的令牌信息  
  17.         dwCreationFlags = NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE;//创建参数  
  18.   
  19.         ZeroMemory(&si,sizeof(STARTUPINFO));  
  20.         ZeroMemory(&pi,sizeof(pi));  
  21.   
  22.         si.cb = sizeof(STARTUPINFO);  
  23.         si.lpDesktop = L"winsta0\\default";//指定创建进程的窗口站,Windows下唯一可交互的窗口站就是WinSta0\Default  
  24.   
  25.         TOKEN_PRIVILEGES tp;  
  26.         LUID luid;  
  27.   
  28.         //打开进程令牌  
  29.         if (!OpenProcessToken(GetCurrentProcess(),  
  30.             TOKEN_ADJUST_PRIVILEGES|  
  31.             TOKEN_QUERY|  
  32.             TOKEN_DUPLICATE|  
  33.             TOKEN_ASSIGN_PRIMARY|  
  34.             TOKEN_ADJUST_SESSIONID|  
  35.             TOKEN_READ|  
  36.             TOKEN_WRITE,&hPToken))  
  37.         {  
  38.             dwRet = GetLastError();  
  39.             break;  
  40.         }  
  41.           
  42.         //查找DEBUG权限的UID  
  43.         if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))  
  44.         {  
  45.             dwRet = GetLastError();  
  46.             break;  
  47.         }  
  48.           
  49.         //设置令牌信息  
  50.         tp.PrivilegeCount = 1;  
  51.         tp.Privileges[0].Luid = luid;  
  52.         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  
  53.   
  54.         //复制当前用户的令牌  
  55.         if (!DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,  
  56.             TokenPrimary,&hUserTokenDup))  
  57.         {  
  58.             dwRet  = GetLastError();  
  59.             break;  
  60.         }  
  61.           
  62.         //设置当前进程的令牌信息  
  63.         if (!SetTokenInformation(hUserTokenDup,TokenSessionId,(void*)&dwSessionId,sizeof(DWORD)))  
  64.         {  
  65.             dwRet = GetLastError();  
  66.             break;  
  67.         }  
  68.           
  69.         //应用令牌权限  
  70.         if (!AdjustTokenPrivileges(hUserTokenDup,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),  
  71.             (PTOKEN_PRIVILEGES)NULL,NULL))  
  72.         {  
  73.             dwRet = GetLastError();  
  74.             break;  
  75.         }  
  76.           
  77.         //创建进程环境块,保证环境块是在用户桌面的环境下  
  78.         LPVOID pEnv = NULL;  
  79.         if (CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))  
  80.         {  
  81.             dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;  
  82.         }  
  83.         else  
  84.         {  
  85.             pEnv = NULL;  
  86.         }  
  87.           
  88.         //创建用户进程  
  89.         if (!CreateProcessAsUser(hUserTokenDup,NULL,lpCommand,NULL,NULL,FALSE,  
  90.             dwCreationFlags,pEnv,NULL,&si,&pi))  
  91.         {  
  92.             dwRet = GetLastError();  
  93.             break;  
  94.         }  
  95.     } while (0);  
  96.   
  97.     //关闭句柄  
  98.     if (NULL != hUserToken)  
  99.     {  
  100.         CloseHandle(hUserToken);  
  101.     }  
  102.   
  103.     if (NULL != hUserTokenDup)  
  104.     {  
  105.         CloseHandle(hUserTokenDup);  
  106.     }  
  107.   
  108.     if (NULL != hPToken)  
  109.     {  
  110.         CloseHandle(hPToken);  
  111.     }  
  112.   
  113.     return dwRet;  
  114. }  

调用方式:

  1. 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)命名空间。

至些,关于服务程序与用户桌面程序的相关技术点,总结完毕,欢迎大家补充和提出改进意见。
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值