您的位置:首页 > 其它

【转】一些关于windows station的介绍

2012-05-30 10:11 176 查看
2608的专栏

Window station 是一个不为人所熟知但是却是非常重要的安全特性,它被设计成为一个限制操作系统中窗口环境的“沙箱”。为了避免将访问控制列表(ACL)放置到每个窗口中和对每个窗口消息进行权限检查而导致的性能下降,我们简单地让窗口互相通知而不执行任何安全检查。然而,我们是在一个私有环境中这么做的,这就是Window station。

Window station 是一个安全核心对象,它包括了剪贴板,一个私有的原子表格,一套桌面以及一套窗口。每一个进程都依赖于一个window station,而且这个关联通常平行于登录会话的分配。也就是说,对于每一个登录会话来说,都有一个相应的window station,就像下图所描述的那样。实际上,window station的名称得自登录会话标识符。例如,用于网络服务的window station 名称是Service-0x0-0x3e4$。偶尔一个internet后台服务程序(例如IIS)会自己管理window
station,但这只是一个例外。还有一个“交互式”的window station, 它会一直存在,甚至没有交互式用户存在时也如此。这个特殊的交互式window station 的名字硬编码为WinSta0, 需要我们对它更对关注。



WinSta0是唯一的一个实际绑定到硬件上的window station.也就是说,你可以在这里看到窗口,并且它们可以接收到鼠标和键盘的输入。WinSta0也是高度安全的,它通过一个能够限制登录会话运行权限的ACL来达到此目的,尽管在上图中你也可以看到一些运行在SYSTEM登录会话中的高度受信的服务程序可能运行在这里,比如说使winlogon.exe。WinSta0上的ACL在一个非常详细的等级上控制你的GUI可以做些什么,但是实际说来,这个一个要么全有,要么全无的授权,并且只有在限制哪个进程允许关联到window
station时才真正需要。(也可以调用函数SetProcessWindowStation来改变进程所属的window station,或通过在调用CreatProcess时设置STARTUPINFO参数将新启动的程序附着到特定的window station).使用这种方法,windos station上的ACL就可以阻止其他登录会话中的程序入侵到你的窗口中。

拥有window station的一个实际用处就是避免来自相对于交互式用户的internet后台服务程序的攻击。如果进程运行在一个低特权账号中同时台帐号可以发送窗口消息到一个高特权级别的程序,那么它就控制了这个程序(这称为lurring attact)。因为window station边界通常是与登录会话边界是平行的,这就有助于阻止这类的攻击。

当一个进程又创建了另外一个,那么新的进程就被放置到与旧进程相同的window station中,除非你指明不这么做(但这种情况很少)。所以,就像是令牌和登录会话一样,一个新的进程很自然的继承了它的父进程的窗口环境。注意启动一个服务不同于简单的创建一个新的进程。当一个服务启动时,它就会由操作系统分配一个合适的登录会话何window station.


关于WindowStations窗口

Window stations 和桌面可能是Windows NT服务中最与众不同的了。绝大多数编程者都不会直接接触这两种对象,尽管用户每时每刻都要碰到它们。window station 和桌面对象就象其它Windows NT对象,如事件、互斥量和信号量一样,是安全(securable)的。一个window station 对象包括一个剪贴板、一个全局原子集和更多的桌面对象。一个window station或者是可见的或者是不可见的。一个可见的window station接收用户来自于鼠标或键盘的输入。一个显示设备也与之相连这样信息可以显示给交互式用户。

  在 Windows NT 4.0中,只有一个window station 能被看得到,就是WinSta0。可见的window station也被定义成交互式的。一个不可见的window station是不可交互的,而且也不能接收任何用户的输入也没有显示设备与之相连。

  如前所述,桌面包含在window station对象中。一个桌面对象包含一个逻辑的显示表面,和窗口、菜单等。只有属于可见window station的桌面才能被看见并接收用户的输入。这个桌面叫做活动桌面。

  作为交互式用户你在不同的时候碰到三种不同的桌面:缺省(Default)、 Winlogon和Screen-saver。Winlogon 桌面是当你按下Ctrl+Alt+Delete组合键时显示在你面前的对话框。缺省(Default)桌面是浏览器(Explorer)或者是由交互式用户启动的所有进程。它更应当被理解成交互式的应用程序桌面。最终的桌面是Screen-saver,它显示你的屏幕saver。你可能已经注意到可以在不同的桌面之间切换。当一个用户按下Ctrl+Alt+Delete组合键时,操作系统可以从缺省状态切换到Winlogon桌面。当你在登录对话框中选择取消,系统将再切换回缺省桌面。有人问我当切换进行的时候,是否其它桌面上的东西都被破坏掉了,答案是
“不”。虽然你看不到其它桌面,但它们仍然在那里。

  系统中所有的进程都与window station 和桌面相联系。当一个用户第一次登录时,交互式window station, WinSta0,和缺省桌面都与这个用户的Shell进程相关联。这样用户就能看到shell了,如果不是这样,用户是什么也看不到的。而且在这之后,由shell启动的所有进程也会和WinSta0 及缺省桌面相关联。

  你还可以通过STARTUPINFO 数据结构的lpDesktop 成员指定你的进程同哪个window station 和桌面相关联。这个数据结构传递给CreateProcess 和 CreateProcessAsUser两个函数。你可以将lpDesktop 初始化为NULL,意思是让CreateProcess函数使用和调用进程相同的window station 和桌面。你可以将你自己的window station 和桌面组合定义成“WinSta0\Default” 或者就定义成空字符串。这个参数会让操作系统为启动进程创建一个新的不可见的window
station 及桌面。与这两个新对象关联的安全性授予每个组对它们的完全访问权限。

以下是代码片段:

typedef struct _STARTUPINFO { // si

  DWORD cb;

  LPTSTR lpReserved;

  LPTSTR lpDesktop;

  LPTSTR lpTitle;

  DWORD dwX;

  DWORD dwY;

  DWORD dwXSize;

  DWORD dwYSize;

  DWORD dwXCountChars;

  DWORD dwYCountChars;

  DWORD dwFillAttribute;

  DWORD dwFlags;

  WORD wShowWindow;

  WORD cbReserved2;

  LPBYTE lpReserved2;

  HANDLE hStdInput;

  HANDLE hStdOutput;

  HANDLE hStdError;

  } STARTUPINFO, *LPSTARTUPINFO;
  Window stations和桌面是具有安全性的对象。与window station 和桌面将关联的进程必须由对这些对象的合适的访问权限。如果进程没有访问权,你会看到这两个消息之一“User32.dll initialization failure(User32.dll初始化失败)” 或 “Kernel32.dll initialization failure.(Kernel32.dll初始化失败)”。由进程返回的退出码为128 或 ERROR_NO_WAIT_CHILDREN。那么我所指的合适的访问权是什么意思呢?假如你有一个文件这样的对象。你可以为这个文件创建一个DACL以使用户具有对这个文件的读访问权。Window
station 和 桌面是以相同的方式工作的。

对于一个桌面对象的一个访问权限叫做DESKTOP_CREATEWINDOW。如果用户没有被授予这个访问权限,任何由这个用户启动的进程都不能创建窗口。不幸的是, 象CreateWindow 这样的USER APIs 在发生同CreateFile 或 CreateMutex API类似的安全问题时,不会返回 “Access Denied(访问被拒绝)” 消息。User32.dll 中的Windows 应用程序将会被终止,导致DLL初始化错误的消息。Kernel32.dll 初始化过程是在创建一个控制板窗口时发生的。一个例程在没有对window
station和桌面的合适的访问权限的时候启动cmd.exe。然而不幸的是, CreateProcess 没有任何机制来检查这个错误。当用户不具有对window station和桌面的合适访问权限时,它并不返回一个错误信息。CreateProcess将会启动这个应用程序,然后这个应用程序本身在DLL失败后终止。

  编程人员还可以有一种方法越过“User32.dll initialization failure(User32.dll初始化失败”消息。系统有一个堆用来为window station分配内存。内存是有限的。缺省设置允许创建七个或八个window station对象。如果你用光了所有的内存,你就会看到这个消息。不过,值得庆幸的是,有一个注册表关键字可以用来增加这个设置。 (参见Knowledge Base article Q142676)

  如果你没在开发服务而只是普通的应用程序,Window stations 和桌面就不是真正的问题。你的应用程序只同交互式桌面WinSta0\Default 相关联。如果你是在开发一个服务,那么它可能就会同下面的window station 及桌面组合关联:

以下是代码片段:

WinSta0\Default

  Service-0x0-3e7$\Default

  Service Account‘s Logon SID\Default
  WinSta0\Default 同运行在LocalSystem帐户的,并且与桌面交互的服务关联 。 (在ServiceType必须指明SERVICE_INTERACTIVE_PROCESS标志)。如果服务不同桌面交互,那么它是与Service-0x0-3e7$\Default相关联的。这是个不可见的window station。你一定很疑惑这乱七八糟的0x0-3e7$ 是什么,它是服务的登录SID 。登录SID是独一无二的。它指的是用户所属的组。系统中的所有用户都会有一个登录SID。

  有一个有趣的现象是尽管你为同一个服务帐户配置了两个服务,但由于它们的登录SID是不同的,所以每个都将有一个独一无二的window station 及桌面。用户的SID是相同的但是由于它们运行在不同的服务帐户登录段中所以它们每一个都有独一无二的登录段。对于交互式服务和非交互式的LocalSystem 服务,情况就不同了,因为它们既同WinSta0关联又同Service-0x0-3e7$关联。

  为什么知道哪个window station和桌面与服务相关联是如此的重要呢?第一,交互性。如果你的服务没有和WinSta0\Default相关联,它就不能以缺省状态把任何USER对象展示给用户。这意味着你不能显示一个窗口或者是获得用户的输入。你可以为一个非交互式的服务显示一个MessageBox 。关键字标志的类型是MB_SERVICE_NOTIFICATION 和 MB_DEFAULT_DESKTOP。你不能用它来发送消息。这个动作与你的进程相关联的桌面有关系。如果你不为进程重新分配正确的window
station 及桌面,你就始终不能做这件事。

  另外两种基于服务帐号的登录SID的window station 和桌面组合是不可见的。正如我刚刚提到的,与一个不可见window station相关联的桌面永远不能显示或接收来自交互式用户的输入信息(除非MessageBox使用两个特殊的标志)。你仍然可以创建USER对象-但是用户永远不会看到它们。很多服务的开发者们经常要犯的一个错误是显示一个对话框提示用户输入信息。当服务被测试时,开发者会注意到服务被挂起了。这就是因为服务同一个不可见的window station相关联。操作系统是成功地创建了对话框。问题是用户看不到它。

  所以你应当怎样才能使你的服务能显示并获得从用户那来的信息呢?首先为用户写一个启动的客户应用程序。客户应用程序将实现显示及从用户处获得信息的功能,然后使用进程间通信机制把信息发回给服务。这样做的最大好处是你不必再为window stations及桌面操心。一个缺点就是需要用户做一些事情来启动你的客户应用程序。

  另外一个方法要求你为LocalSystem帐户配置你的服务,要将服务类型说明为SERVICE_INTERACTIVE_PROCESS。这将把你的服务与正确的window station及桌面相关联。唯一的问题就是服务将不能通过NTLM进行任何的网络访问。有两种办法可以解决。第一种办法是,NULL 段访问可以在服务器端通过注册表调整。如果服务开发者有对服务器的合适的安全访问权限,注册表的关键字将会改变,允许对LocalSystem 进程的访问。

  另一个解决的办法是扮演一个已经访问过NTLM 安全网络资源的用户。一个问题是服务必须知道用户的口令以通过LogonUser API生成用户的标志。为什么用户不能为服务帐户配置一个服务,那样就只须重新为服务分配交互式的window station 及桌面就可以了?答案只有一个,就是安全。对交互式window station 及桌面具有完全访问权限的唯一用户是LocalSystem帐户和交互式用户(如果有的话)。我特别指出完全访问权限的原因是属于本地管理员组的用户对交互式window station 及桌面拥有部分访问权限。有一部分USER
API 调用可能会由于安全的原因无法运行。

  最好的赌注是冒点风险使用本地管理员帐户。交互式用户的访问权限是基于组的登录SID 而不是单个用户的SID。这意味着你可能有一个与交互式用户相同帐户的服务但是组的登录SID是不同的。允许一个为服务帐户配置的服务具有对交互式window station及桌面访问的唯一条件是为交互式用户更改安全性以允许服务访问。

  现在你有了一个为交互式window station 及桌面配置的服务,你还会碰上什么样的问题呢?第一个要考虑的问题就是当用户退出时将会发生什么。如果服务是交互式的,那么是否缺省桌面被破坏了呢?不,缺省桌面仍然存在。唯一的区别是Winlogon 桌面现在是活动桌面。当第二个用户登录系统时,系统将会切换回缺省桌面。用户显示的任何事物都可以被交互式用户看到。

  交互式服务和由交互式用户启动的进程之间的区别是,当没有用户登录时,服务仍然可以运行。这就导致了一些有趣的问题。例如,基于安全的原因,当交互式用户退出系统后,操作系统竟全局原子表清为0。如果一个交互式服务依赖于存储在全局原子表中的信息,那么当这个交互式用户退出后,信息全部消失。另一个例子是自启动交互式服务。第一个用户登录之前缺省桌面还没有创建。这意味着在用户登录之前试图显示信息的交互式服务一定会出问题。

  我想讨论的最后一件事是交互式服务是暴露给交互式用户的,这个交互式用户可以通过任务管理器杀死服务。如果你有一个运行在LocalSystem 帐户上的服务,则交互式用户将不具备必须的安全性来杀死你的进程。假设你进入任务管理器列出所有的进程,如果你点击了End Process(终止进程)按纽来终止运行在LocalSystem帐户上的进程时,你将会得到意料之中的 “Access is denied(访问被拒绝)” 消息框。但是如果这个服务有一个显示在外层的窗口,你可以在任务管理器中列出所有应用程序。当你点击End
Task(终止任务)按纽,你就可以通过这个暴露的窗口杀死这个服务。避免这种情况发生的一种方法就是让你的窗口不要显示在应用程序标签中。创建一个隐藏的窗口,然后将可见的窗口作为这个隐藏窗口的儿子窗口。

记住,最后的手段才是将你的服务交互化。最好的选择是创建一个交互式的客户应用程序。

  注册表“蜂箱”

  “蜂箱”是操作系统用来存储用户注册信息的。在注册表中存储的注册信息包括桌面、应用程序、打印和网络设置。系统将用户的注册信息备份在一个象蜂箱一样的文件中。Windows NT的每个用户都分配一个蜂箱。这个蜂箱能放在本地的或远程的服务器上。当一个用户登录到Windows NT机器上时,系统把用户的蜂箱装载到注册表中,在HKEY_USERS 关键字下。 注册表关键字名称代表蜂箱时基于用户的SID的。如果你通过regedt32.exe 或regedit.exe检测到HKEY_USERS下的注册表关键字,你将看到至少两个子关键字,
.DEFAULT 和以S-1开头的长字符串。这是用户的SID。如果你还不清楚HKEY_CURRENT_USER代表的是什么意思,那么我告诉你,基本上说,它是一个HKEY_USERS\user’s SID的映射。通过regedt32.exe 或regedit.exe可以得到验证。

  HKEY_USER\user‘s SID和HKEY_CURRENT_USER的子关键字实际上是一模一样的。当一个应用程序是关于HKEY_CURRENT_USER的,系统将会基于用户的安全上下文把用户映射到合适的蜂箱中,包括用户的SID,它用来在HKEY_USERS下查找正确的蜂箱。如果由于某种原因用户的蜂箱没有被装载,系统将会把用户的蜂箱映射到。DEFAULT。

  现在,看起来一切都好了,但是还有很多问题。首先,服务要依赖用户的注册信息。绝大多数应用程序在HKEY_CURRENT_USER中存储用户的注册信息。例如,用户的打印信息存储在用户的蜂箱中。如果服务配置配置给不同的帐户,那这个服务将没有任何打印信息。不是所有的服务都使用。DEFAULT 蜂箱。如果一个服务配置给一个具有蜂箱的服务帐户,那么 Service Control Manager将会装载哟功能户的蜂箱。Windows NT 4.0中已经增添了这一功能 。

  Service Control Manager不能做的一件事是为服务正确地准备用户的环境。环境变量存储早用户的蜂箱中。服务继承的唯一一个环境变量来自于Service Control Manager。它的环境基于系统环境变量。如果你改变系统环境变量,那么只有在重新启动机器后,所做的改变才能反映到服务中。当系统环境变量改变时,Service Control Manager不象其它系统进程一样处理WM_SETTINGCHANGE 消息。

  让我们返回到LocalSystem 帐户的情况中。怎样得到一个已合理配置的“蜂箱”呢?有这样几种选择。如果你有创建注册关键字的应用程序的控制权,就可以创建两次关键字。一次是为HKEY_CURRENT_USER,另一次是为HKEY_USERS\Default。另一种方法是判断应用程序创建了那些关键字,进行复制,这种方法可以通过手工或编程实现。

  如果你不想和注册表搅在一起,你可以用编程的方式装载你自己的“蜂箱”。如果你知道一个已经正确初始化的“蜂箱”的用户名,服务将基于用户名决定蜂箱的位置。通过RegLoadKey API装载“蜂箱”,然后扮演这个用户。

  问题是你需要一个进程标志来扮演这个用户。一个进程标志可以通过LogonUser API调用生成。当然,你需要一个用户口令。如果由于某种原因你所感兴趣的用户有一些进程运行在系统中,服务将会列举这些进程知道运行在目标用户的安全上下文中的进程。一旦一个进程被定位,这个服务将通过调用OpenProcessToken获得用户标志。在这时,服务将用获得标志扮演这个用户。

  结尾

  讨论了在Windows NT服务和由交互式用户启动的应用程序之间的区别,也讨论了服务开发人员最关心的三个领域: Windows NT 安全、 window stations 和桌面以及注册表“蜂箱”。这些信息将使Windows NT服务的开发变得容易一些并能应用于服务中使用的其它技术中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: