服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

2021-11-18 11:57李敬然 C#

服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的组成部分。我们可以把服务想像成一种特殊的应用程序,它随系统的“开启~关闭”而“开始~停止”其工作内容,在这期间无需任何用户参与

windows 服务在后台执行着各种各样任务,支持着我们日常的桌面操作。有时候可能需要服务与用户进行信息或界面交互操作,这种方式在xp 时代是没有问题的,但自从vista 开始你会发现这种方式似乎已不起作用。

session 0 隔离实验

下面来做一个名叫alertservice 的服务,它的作用就是向用户发出一个提示对话框,我们看看这个服务在windows 7 中会发生什么情况。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using system.serviceprocess;
using system.windows.forms;
 
namespace alertservice
{
 public partial class service1 : servicebase
 {
  public service1()
  {
   initializecomponent();
  }
 
  protected override void onstart(string[] args)
  {
   messagebox.show("a message from alertservice.");
  }
 
  protected override void onstop()
  {
  }
 }
}

程序编译后通过installutil 将其加载到系统服务中:

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

在服务属性中勾选“allow service to interact with desktop” ,这样可以使alertservice 与桌面用户进行交互。

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

在服务管理器中将alertservice 服务“启动”,这时任务栏中会闪动一个图标:

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

点击该图标会显示下面窗口,提示有个程序(alertservice)正在试图显示信息,是否需要浏览该信息:

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

尝试点击“view the message”,便会显示下图界面(其实这个界面我已经不能从当前桌面操作截图了,是通过virtual pc 截屏的,其原因请继续阅读)。注意观察可以发现下图的桌面背景已经不是windows 7 默认的桌面背景了,说明alertservice 与桌面系统的session 并不相同,这就是session 0 隔离作用的结果。

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

session 0 隔离原理

在windows xp、windows server 2003 或早期windows 系统时代,当第一个用户登录系统后服务和应用程序是在同一个session 中运行的。这就是session 0 如下图所示:

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

 

 

但是这种运行方式提高了系统安全风险,因为服务是通过提升了用户权限运行的,而应用程序往往是那些不具备管理员身份的普通用户运行的,其中的危险显而易见。

从vista 开始session 0 中只包含系统服务,其他应用程序则通过分离的session 运行,将服务与应用程序隔离提高系统的安全性。如下图所示:

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

这样使得session 0 与其他session 之间无法进行交互,不能通过服务向桌面用户弹出信息窗口、ui 窗口等信息。这也就是为什么刚才我说那个图已经不能通过当前桌面进行截图了。

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

session 检查

在实际开发过程中,可以通过process explorer 检查服务或程序处于哪个session,会不会遇到session 0 隔离问题。我们在services 中找到之前加载的alertservice 服务,右键属性查看其session 状态。

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

可看到alertservice 处于session 0 中:

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

再来看看outlook 应用程序:

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

很明显在windows 7 中服务和应用程序是处于不同的session,它们之间加隔了一个保护墙,在下篇文章中将介绍如何穿过这堵保护墙使服务与桌面用户进行交互操作。

 

如果在开发过程中确实需要服务与桌面用户进行交互,可以通过远程桌面服务的api 绕过session 0 的隔离完成交互操作。

对于简单的交互,服务可以通过wtssendmessage 函数,在用户session 上显示消息窗口。对于一些复杂的ui 交互,必须调用createprocessasuser或其他方法(wcf、.net远程处理等)进行跨session 通信,在桌面用户上创建一个应用程序界面。

wtssendmessage 函数

如果服务只是简单的向桌面用户session 发送消息窗口,则可以使用wtssendmessage 函数实现。首先,在上一篇下载的代码中加入一个interop.cs 类,并在类中加入如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void showmessagebox(string message, string title)
{
 int resp = 0;
 wtssendmessage(
  wts_current_server_handle,
  wtsgetactiveconsolesessionid(),
  title, title.length,
  message, message.length,
  0, 0, out resp, false);
}
 
[dllimport("kernel32.dll", setlasterror = true)]
public static extern int wtsgetactiveconsolesessionid();
 
[dllimport("wtsapi32.dll", setlasterror = true)]
public static extern bool wtssendmessage(
 intptr hserver,
 int sessionid,
 string ptitle,
 int titlelength,
 string pmessage,
 int messagelength,
 int style,
 int timeout,
 out int presponse,
 bool bwait);

在showmessagebox 函数中调用了wtssendmessage 来发送信息窗口,这样我们就可以在service 的onstart 函数中使用,打开service1.cs 加入下面代码:

?
1
2
3
4
5
protected override void onstart(string[] args)
{
 interop.showmessagebox("this a message from alertservice.",
       "alertservice message");
}

编译程序后在服务管理器中重新启动alertservice 服务,从下图中可以看到消息窗口是在当前用户桌面显示的,而不是session 0 中。

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

createprocessasuser 函数

如果想通过服务向桌面用户session 创建一个复杂ui 程序界面,则需要使用createprocessasuser 函数为用户创建一个新进程用来运行相应的程序。打开interop 类继续添加下面代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
public static void createprocess(string app, string path)
{
 bool result;
 intptr htoken = windowsidentity.getcurrent().token;
 intptr hdupedtoken = intptr.zero;
 
 process_information pi = new process_information();
 security_attributes sa = new security_attributes();
 sa.length = marshal.sizeof(sa);
 
 startupinfo si = new startupinfo();
 si.cb = marshal.sizeof(si);
 
 int dwsessionid = wtsgetactiveconsolesessionid();
 result = wtsqueryusertoken(dwsessionid, out htoken);
 
 if (!result)
 {
  showmessagebox("wtsqueryusertoken failed", "alertservice message");
 }
 
 result = duplicatetokenex(
   htoken,
   generic_all_access,
   ref sa,
   (int)security_impersonation_level.securityidentification,
   (int)token_type.tokenprimary,
   ref hdupedtoken
  );
 
 if (!result)
 {
  showmessagebox("duplicatetokenex failed" ,"alertservice message");
 }
 
 intptr lpenvironment = intptr.zero;
 result = createenvironmentblock(out lpenvironment, hdupedtoken, false);
 
 if (!result)
 {
  showmessagebox("createenvironmentblock failed", "alertservice message");
 }
 
 result = createprocessasuser(
       hdupedtoken,
       app,
       string.empty,
       ref sa, ref sa,
       false, 0, intptr.zero,
       path, ref si, ref pi);
 
 if (!result)
 {
  int error = marshal.getlastwin32error();
  string message = string.format("createprocessasuser error: {0}", error);
  showmessagebox(message, "alertservice message");
 }
 
 if (pi.hprocess != intptr.zero)
  closehandle(pi.hprocess);
 if (pi.hthread != intptr.zero)
  closehandle(pi.hthread);
 if (hdupedtoken != intptr.zero)
  closehandle(hdupedtoken);
}
 
[structlayout(layoutkind.sequential)]
public struct startupinfo
{
 public int32 cb;
 public string lpreserved;
 public string lpdesktop;
 public string lptitle;
 public int32 dwx;
 public int32 dwy;
 public int32 dwxsize;
 public int32 dwxcountchars;
 public int32 dwycountchars;
 public int32 dwfillattribute;
 public int32 dwflags;
 public int16 wshowwindow;
 public int16 cbreserved2;
 public intptr lpreserved2;
 public intptr hstdinput;
 public intptr hstdoutput;
 public intptr hstderror;
}
 
[structlayout(layoutkind.sequential)]
public struct process_information
{
 public intptr hprocess;
 public intptr hthread;
 public int32 dwprocessid;
 public int32 dwthreadid;
}
 
[structlayout(layoutkind.sequential)]
public struct security_attributes
{
 public int32 length;
 public intptr lpsecuritydescriptor;
 public bool binherithandle;
}
 
public enum security_impersonation_level
{
 securityanonymous,
 securityidentification,
 securityimpersonation,
 securitydelegation
}
 
public enum token_type
{
 tokenprimary = 1,
 tokenimpersonation
}
 
public const int generic_all_access = 0x10000000;
 
[dllimport("kernel32.dll", setlasterror = true,
 charset = charset.auto, callingconvention = callingconvention.stdcall)]
public static extern bool closehandle(intptr handle);
 
[dllimport("advapi32.dll", setlasterror = true,
 charset = charset.ansi, callingconvention = callingconvention.stdcall)]
public static extern bool createprocessasuser(
 intptr htoken,
 string lpapplicationname,
 string lpcommandline,
 ref security_attributes lpprocessattributes,
 ref security_attributes lpthreadattributes,
 bool binherithandle,
 int32 dwcreationflags,
 intptr lpenvrionment,
 string lpcurrentdirectory,
 ref startupinfo lpstartupinfo,
 ref process_information lpprocessinformation);
 
[dllimport("advapi32.dll", setlasterror = true)]
public static extern bool duplicatetokenex(
 intptr hexistingtoken,
 int32 dwdesiredaccess,
 ref security_attributes lpthreadattributes,
 int32 impersonationlevel,
 int32 dwtokentype,
 ref intptr phnewtoken);
 
[dllimport("wtsapi32.dll", setlasterror=true)]
public static extern bool wtsqueryusertoken(
 int32 sessionid,
 out intptr token);
 
[dllimport("userenv.dll", setlasterror = true)]
static extern bool createenvironmentblock(
 out intptr lpenvironment,
 intptr htoken,
 bool binherit);

在createprocess 函数中同时也涉及到duplicatetokenex、wtsqueryusertoken、createenvironmentblock 函数的使用,有兴趣的朋友可通过msdn 进行学习。完成createprocess 函数创建后,就可以真正的通过它来调用应用程序了,回到service1.cs 修改一下onstart 我们来打开一个cmd 窗口。如下代码:

复制代码 代码如下:

protected override void onstart(string[] args)
{
    interop.createprocess("cmd.exe",@"c:windowssystem32");
}


     重新编译程序,启动alertservice 服务便可看到下图界面。至此,我们已经可以通过一些简单的方法对session 0 隔离问题进行解决。大家也可以通过wcf 等技术完成一些更复杂的跨session 通信方式,实现在windows 7 及vista 系统中服务与桌面用户的交互操作。

 

vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

延伸 · 阅读

精彩推荐
  • C#C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    这篇文章主要介绍了C#设计模式之Strategy策略模式解决007大破密码危机问题,简单描述了策略模式的定义并结合加密解密算法实例分析了C#策略模式的具体使用...

    GhostRider10972022-01-21
  • C#SQLite在C#中的安装与操作技巧

    SQLite在C#中的安装与操作技巧

    SQLite,是一款轻型的数据库,用于本地的数据储存。其优点有很多,下面通过本文给大家介绍SQLite在C#中的安装与操作技巧,感兴趣的的朋友参考下吧...

    蓝曈魅11162022-01-20
  • C#VS2012 程序打包部署图文详解

    VS2012 程序打包部署图文详解

    VS2012虽然没有集成打包工具,但它为我们提供了下载的端口,需要我们手动安装一个插件InstallShield。网上有很多第三方的打包工具,但为什么偏要使用微软...

    张信秀7712021-12-15
  • C#C#微信公众号与订阅号接口开发示例代码

    C#微信公众号与订阅号接口开发示例代码

    这篇文章主要介绍了C#微信公众号与订阅号接口开发示例代码,结合实例形式简单分析了C#针对微信接口的调用与处理技巧,需要的朋友可以参考下...

    smartsmile20127762021-11-25
  • C#如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    这篇文章主要给大家介绍了关于如何使用C#将Tensorflow训练的.pb文件用在生产环境的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴...

    bbird201811792022-03-05
  • C#利用C#实现网络爬虫

    利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...

    C#教程网11852021-11-16
  • C#三十分钟快速掌握C# 6.0知识点

    三十分钟快速掌握C# 6.0知识点

    这篇文章主要介绍了C# 6.0的相关知识点,文中介绍的非常详细,通过这篇文字可以让大家在三十分钟内快速的掌握C# 6.0,需要的朋友可以参考借鉴,下面来...

    雨夜潇湘8272021-12-28
  • C#深入理解C#的数组

    深入理解C#的数组

    本篇文章主要介绍了C#的数组,数组是一种数据结构,详细的介绍了数组的声明和访问等,有兴趣的可以了解一下。...

    佳园9492021-12-10