|
本文假定您熟悉Win32 |
|
四年前, 我被介绍给Windows NT® 服务。像许多其他程序员一样,我发现它们都很有趣,也很奇怪。不像的Windows ® 为基础的应用程序,服务通常没有任何直接的用户交互。开发人员经常告诉我,某些代码可以在基于Windows的应用程序中运行,但不能从他们的服务中运行。主要问题是对“环境”差异缺乏了解。服务的“环境”或Outlook与用户发起的基于Windows或控制台应用程序的“环境”有很大的不同。我所说的“环境”指的不仅是过程的环境变量,您还必须包括窗口站点和桌面,注册表配置单元, 许多的Win32 ® 技术,如MFC,ODBC和MAPI,可以表现不同的服务,由于在服务的差异“的环境。” 让我们来探讨一下为什么这些不同的技术在服务中显得不合时宜。 在我解释“环境”差异之前,让我们仔细看看服务是什么。服务是具有特殊要求的后台Win32进程。它需要一个独特的入口点和一个回调函数。回调函数被称为服务控制处理程序。新的入口点通常称为ServiceMain,但是您可以通过SERVICE_TABLE_ENTRY结构中的LPSERVICE_MAIN_FUNCTION成员命名它。 |
typedef struct _SERVICE_TABLE_ENTRY {// ste
LPTSTR lpServiceName;
LPSERVICE_MAIN_FUNCTION lpServiceProc;
} SERVICE_TABLE_ENTRY,
*LPSERVICE_TABLE_ENTRY; |
The service is manipulated (installed, started, stopped, and configured) through a subset of the Win32 API. You can pass a server name to manipulate services remotely over a network. A service can be configured to run in either a specific user account or the LocalSystem account (see Figure 1). Either account is referred to as the service account. A service can be configured to start running when the system is first booted or at the discretion of a user with the proper security. |
Figure 1 Configuring a Service |
AlerterFor further information, please refer to the Platform SDK documentation and "Design a Windows NT service to Exploit Special Operating System Facilities," by Jeffrey Richter (MSJ, October 1997). I'd like to emphasize that a process launched from a service using the CreateProcess API is not a service itself and should not be referred to as a service. It is definitely a background process running in the same "environment" as the service—but that's it. The documentation of the Windows NT Resource Kit misleads users into thinking that using the Srvany utility will magically turn their application into a service. This is not true. The utility just launches your application via a CreateProcess API call. This is very similar to the AT command available on Windows NT. But even though your app launched via CreateProcess is technically not a service, it is still running in the same "environment," so many of my comments will also apply to it. In this article, I will describe the environmental differences between a service and a process launched by the interactive user. I define the interactive user as somebody who logs onto a Windows NT machine via the Ctrl+Alt+ Delete secured key combination dialog box (see Figure 2). |
Figure 2 Logon Dialog |
Before I begin discussing the environmental security differences, let me briefly explain Windows NT security. One of the requirements of C2 level security is the ability to control access to a secured resource. This is done by granting or denying access to one or more users or groups to a particular securable object. Windows NT accomplishes this through two basic components, the access token and the security descriptor. When a user first logs on, a username and password is given to the system. If this information is correct, the system assigns an access token to all processes associated with the user. The access token contains information on the user's ID (known as the security identifier or SID), the user's group memberships, and the list of privileges granted to the user. The system uses the access token as an identification badge. The security descriptor, on the other hand, is associated with a securable object. It indicates who owns the object and defines what kind of access control a user or group may have to the object. This is known as the discretionary access control list (DACL). The DACL contains a list of access-control entries (ACE). Each ACE describes a user or group based on its SID (also known as trustees) and what kind of access can be granted or denied to the securable object. The system uses these two components to determine whether a handle to access the securable object should be returned to the user. It does so by walking the list of ACEs in the security descriptor of the object. The system will check every ACE to see if the user is granted the specified access to the object. If none of the ACEs grant the user the access requested, the system does not return a handle for the object. If the DACL contains an ACE denying access to the user, the search immediately ends and no handle is returned to the user. If the DACL does not contain any ACEs, all users are denied access to the object. If the security descriptor does not contain a DACL, all users are granted access to the object (see Figure 3). This is better known as a NULL DACL. |
Figure 3 Security Components |
|
typedef struct _SECURITY_ATTRIBUTES {// sa
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES; |
对于大多数开发人员来说,传递NULL参数就足够了。只要操作安全对象的所有进程都在相同的安全上下文中运行,就没有问题。 在Windows NT服务中,此技术可能会或可能不会工作。服务帐户允许服务以不同的用户身份运行。如果您曾经查看过安装在Windows NT计算机上的服务,则知道其中大部分都是为系统帐户(也称为LocalSystem帐户)配置的。系统帐户是仅在您的计算机上本地识别的特殊帐户。这意味着此帐户不能用于访问依赖NT LAN Manager(NTLM)身份验证的网络资源。这些资源包括文件共享,命名管道,注册表以及对远程计算机事件日志或服务控制管理器的访问。为什么这不可能? NTLM身份验证基于包含用户名和密码的加密凭据。如果操作系统遇到没有任何凭据的用户,则用户被认为具有NULL凭证。当系统尝试访问基于NULL凭证的安全网络资源时,这被称为NULL会话。只有当远程机器允许NULL会话访问时才允许访问。这可以通过注册表来配置。(有关详细信息,请参阅知识库文章Q122702。)唯一的其他解决方法是使用有效凭据模拟用户或使用可访问安全网络资源的服务帐户。Winsock和NetBIOS不是安全的NTLM资源,所以它们不会遇到上述安全限制。 这似乎是一个主要问题。那么为什么配置这么多的服务来使用本地系统帐户呢?本地系统帐户被授予在操作系统上可用的所有权限。权限允许用户操纵各种系统资源,例如更改系统时间,交互式登录和从远程系统关闭(参见图4)。 |
图4用户管理器授予权限 |
窗口工作站和桌面可能是Windows NT服务中最困难的一个方面。大多数程序员不会直接遇到这些对象,尽管用户总是碰到它们。与其他Windows NT对象(如事件,互斥和信号量)一样,窗口工作站和桌面对象也是可以保证的。窗口工作站对象包含一个剪贴板,一组全局原子和零个或多个桌面对象。窗口站可以是可见的或不可见的。一个可见的窗口站接收用户输入,可能来自鼠标或键盘。显示设备也与之相关联,以便信息可以显示给交互式用户(见图6)。 |
图6 Window Station和台式机 |
|
图7交互式流程与服务 |
一个有趣的事情要注意的是,即使您可能有两个服务配置为相同的服务帐户,每个将有一个独特的窗口站和桌面,因为他们的登录SID是不同的。用户SID是相同的,但每个都有一个唯一的登录会话,因为每个都在单独的服务帐户登录会话中运行。交互式和非交互式LocalSystem服务不是这种情况,因为它们与WinSta0或Service-0x0-3e7 $相关联。 现在,为什么知道哪个窗口站点和桌面与服务相关联是非常重要的?第一个问题是交互性。如果您的服务没有与WinSta0 \ Default相关联,则默认情况下,它将无法向用户显示任何USER对象。这意味着您不能显示窗口或获取用户输入。您可以显示一个非交互式服务的MessageBox。键标志类型是MB_SERVICE_NOTIFICATION和MB_DEFAULT_DESKTOP。(有关更多信息,请参阅Platform SDK文档。)另外,您不能安装窗口挂钩,甚至不能发送消息。这两个操作与您的进程所关联的桌面相关。如果不使用正确的窗口工作站和桌面重新分配进程,则无法完成这两项任务。这意味着窗口挂钩和消息不能跨桌面。 另外两个基于服务帐户的登录SID的窗口工作站和桌面组合是不可见的。正如我刚刚提到的,与不可见的窗口站相关联的进程永远不能显示或接收来自交互式用户的输入(使用两个特殊标志的MessageBox除外)。您仍然可以创建USER对象 - 但用户永远不会看到它们。很多服务开发者犯的一个错误就是显示一个对话框,提示用户输入一些信息。当服务被测试时,开发者会注意到服务正在挂起。挂起是由于服务与不可见的窗口站相关联的事实造成的。操作系统已成功创建对话框。问题是用户看不到它。 那么如何让你的服务显示和获取用户的信息呢?编写一个客户端应用程序供用户启动。客户端应用程序将显示并获取来自用户的信息,然后使用某种进程间通信将信息发送回服务。关于这一点的好处是您不必担心窗口站点和桌面。缺点是这需要用户做一些事情来启动你的客户端应用程序。 另一种方法要求您为LocalSystem帐户配置服务,将服务类型指定为SERVICE_INTERACTIVE_PROCESS。这将使您的服务与正确的窗口站和桌面相关联。唯一的问题是,该服务将不会有任何通过NTLM安全的网络访问。这可以通过两种方式来解决。NULL会话访问可以通过注册表在服务器端进行管理。如果服务开发人员对服务器具有正确的安全访问权限,则可以更改注册表项以允许访问LocalSystem进程。 另一种方法是模拟有权访问NTLM安全网络资源的用户。唯一的问题是服务必须知道用户的密码才能通过LogonUser API生成用户的令牌。为什么用户不能为一个服务帐户配置服务,然后重新分配服务与交互式窗口站和桌面?问题是安全。唯一有权访问交互式窗口站和桌面的用户是LocalSystem帐户和交互式用户(如果有的话)。我提到完全访问的原因是因为属于本地管理员组的用户可以部分访问交互式窗口工作站和桌面。某些USER API调用可能会或可能不会基于安全性工作。 最好的选择是使用本地管理员帐户完全访问,风险自负。交互式用户允许的访问权限是基于组登录SID而不是个人用户的SID。这意味着您可能为交互式用户配置了与该帐户相同的服务,但组登录SID将不同。允许为服务帐户配置的服务访问交互式窗口工作站和桌面的唯一方法是让交互式用户修改安全性以允许服务访问或让另一个进程在LocalSystem帐户中运行。(有关更多信息,请参阅知识库文章Q165194。) 现在您已经为交互式窗口工作站和桌面配置了一项服务,那么您可以运行哪些陷阱?首先考虑的是用户注销时发生的情况。如果一个服务是交互式的,这是否意味着默认的桌面被破坏?不。默认桌面仍然在那里。唯一的区别是Winlogon桌面现在是活动桌面。当下一个用户登录到本机时,系统将切换回默认桌面。用户显示的任何内容都将被交互式用户看到。 交互式服务与交互式用户启动的进程之间的一个区别当然是,当没有人登录时服务可以运行。这导致一些有趣的问题。例如,出于安全原因,当交互式用户注销时,操作系统清除全局原子表。如果交互式服务依赖于存储在全局原子表中的信息,则当交互式用户注销时,信息将消失。另一个例子是一个自动启动的交互式服务。默认桌面直到第一个交互式用户登录才会创建。这意味着在用户登录之前尝试显示信息的交互式服务会遇到问题。 我想讨论的最后一件事情是交互式服务暴露给交互式用户,如果服务具有顶层窗口,交互式用户可以通过任务管理器终止服务。如果在LocalSystem帐户中运行服务,则交互式用户没有必要的安全性来终止进程。说你进入任务管理器,并选择进程列表。如果按LocalSystem帐户中运行的进程的“结束进程”按钮,则会按预期得到“访问被拒绝”消息框。但是,如果此服务具有顶层窗口,则可以在任务管理器中选择应用程序列表。如果您点击“结束任务”按钮,则可以通过此暴露的窗口来终止服务。避免这种情况的一种方法是防止您的窗口出现在应用程序选项卡中。创建一个隐藏的窗口; 请记住,只有作为最后的手段才能使您的服务互动。最好的选择是创建一个交互式的客户端应用程序。 操作系统使用配置单元来存储用户特定的注册表信息。存储在注册表中的信息包括桌面,应用程序,打印和网络设置。系统在称为配置单元的文件中备份用户特定的注册表信息。Windows NT上的每个用户都被分配一个配置单元。这个配置单元可以位于本地,也可以位于远程服务器(漫游蜂房)上。当用户登录到Windows NT计算机时,系统将用户的配置单元加载到注册表中。该配置单元在HKEY_USERS项下加载。表示配置单元的注册表项名称基于用户的SID。如果您通过regedt32.exe或regedit.exe检查HKEY_USERS下的注册表项,您将会看到至少两个子项:.DEFAULT和一个以S-1开始的长字符串 - 等等等等等等等等。这是用户的SID。如果你' 有没有想过HKEY_CURRENT_USER代表什么,它基本上是HKEY_USERS \用户的SID的映射。您可以使用regedt32.exe或regedit.exe来验证这一点(请参阅图9)。 |
图9 Regedt32.exe |
我讨论了Windows NT服务与交互式用户启动的应用程序之间的一些“环境”差异。我讨论了服务开发人员关心的三个主要问题:Windows NT安全性,窗口站点和桌面以及注册表配置单元。这些信息应该使开发Windows NT服务的经验更容易,并且可以应用于服务中使用的其他技术来解释这些偶然的神秘行为。 |