Windows-安全内部原理-全-

Windows 安全内部原理(全)

原文:zh.annas-archive.org/md5/63f2b3f5bf2495f7f0491e17932aa483

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

数以亿计的设备使用微软 Windows 平台。许多全球最大的公司依赖其安全性来保护数据和通信,任何在 Azure 云中托管代码的人也如此。但由于 Windows 对现代互联网安全至关重要,它也成为了攻击的热门目标。

Windows NT 操作系统从 1993 年开始就将安全性纳入设计,首次引入了用户账户、资源控制和网络远程访问。自那时以来的 20 多年里,Windows 安全性发生了巨大变化。微软已经用现代技术替代了原始的认证过程,赋予访问控制机制更多功能,并显著增强了平台的攻击防护能力。

今天,Windows 平台的安全性异常复杂,许多攻击依赖于滥用这种复杂性。不幸的是,微软在这一领域的文档可能存在不足。由于 Windows 并非开源,有时理解其安全性的唯一途径是通过深入的研究和分析。

这正是我的专长所在。我在 Windows 平台上作为开发者和安全研究员已经有超过 20 年的经验,积累了对操作系统中未公开部分的理解。在本书中,我将以易于理解的方式分享我广泛的专业知识。通过掌握 Windows 安全的原理,你将能够启动自己的研究项目或改进你的软件产品。

本书适合谁阅读?

我写这本书是为那些从事 Windows 安全工作的人准备的。也许你是 Windows 软件的开发者,想确保你的产品是安全的。或者你是负责在企业中保护 Windows 安全的系统管理员,但对各种安全功能如何结合保护平台理解不深。又或者你可能是研究人员,想通过漏洞挖掘操作系统中的安全漏洞。

本书假定读者对 Windows 用户界面及其基本操作有一定的了解,比如文件操作。也就是说,你不需要成为低级 Windows 专家:对于那些需要更多基础知识的读者,第二章和第三章提供了操作系统概述以及其组成部分。

我在很大程度上依赖 PowerShell 脚本,因此你会发现如果你对该语言有所了解,以及对其基于的.NET 框架有所掌握,将会对你有所帮助。为了帮助你快速入门,第一章提供了 PowerShell 一些特性的简要概述。在其他地方,我将尽力避免使用该语言的深奥特性,以便让具备其他脚本语言或 Shell 环境(如 bash)知识的读者也能理解代码。

本书内容概览

在每一章中,我们都会介绍现代版本的 Windows 中实现的核心安全特性。我们还将通过一些用 PowerShell 编写的实例来进行讲解,这些实例将帮助你更好地理解本章介绍的命令。以下是每章内容的简要总结。

第一部分从编程角度介绍了 Windows 操作系统。它应该为你提供理解本书其余部分所需的基础。

第一章:设置 PowerShell 测试环境在本章中,你将设置 PowerShell 以运行后续章节中的示例。这包括安装我编写的 PowerShell 模块,用于与 Windows 及其安全功能进行交互。本章还概述了 PowerShell 脚本语言。

第二章:Windows 内核本章介绍了 Windows 内核及其系统调用接口的基础知识,这是开发深入理解 Windows 安全的关键内容。我还描述了对象管理器,用于管理资源。

第三章:用户模式应用程序大多数应用程序并不直接使用内核的系统调用接口,而是使用一套更高层的编程接口。本章介绍了 Windows 的功能,例如文件处理和注册表。

第二部分涵盖了 Windows 内核中最重要的安全组件——安全参考监视器。我们将探讨访问控制的各个方面,从构建用户身份到保护单个资源(如文件)。

第四章:安全访问令牌Windows 为每个运行中的进程分配一个访问令牌,该令牌代表用户在系统中的身份。本章描述了存储在令牌中的各种组件,这些组件用于检查访问权限。

第五章:安全描述符每个可安全访问的资源都需要描述谁可以访问它以及授予何种访问权限。这正是安全描述符的作用。在本章中,我们将讨论它们的内部结构,以及如何创建和操作这些描述符。

第六章:读取和分配安全描述符为了检查系统的安全性,你需要能够查询资源的安全描述符。本章解释了不同类型资源如何进行安全描述符查询。它还涵盖了 Windows 为资源分配安全描述符的多种复杂方式。

**第七章:访问检查过程    **Windows 使用访问检查来确定应授予用户对资源的访问权限。此操作会使用令牌和安全描述符,并根据算法确定授予的访问权限。本章通过 PowerShell 实现此算法,深入探讨其设计。

**第八章:其他访问检查应用场景    **虽然 Windows 主要使用访问检查来授予资源访问权限,但有时也用它来确定其他安全属性,例如资源的可见性以及进程是否以较低权限运行。本章介绍了访问检查的这些替代应用场景。

**第九章:安全审计    **访问检查过程还可以生成用户访问过的资源的日志,并记录访问级别。本章介绍了这些系统审计策略。

第三部分包含了 Windows 认证的详细信息,以及为实现访问控制而验证用户身份的机制。

**第十章:Windows 认证    **由于认证这一话题相当复杂,本章总结了认证结构和服务,其他认证机制依赖于此结构和服务。

**第十一章:Active Directory    **Windows 2000 为企业中的 Windows 系统提供了一个新的网络模型,所有认证信息都存储在一个网络目录中,用户和管理员可以查询和修改。本章讲解了 Active Directory 如何存储信息,并保护其免受恶意修改。

**第十二章:交互式认证    **Windows 中最常见的认证场景是用户输入用户名和密码以访问计算机桌面。本章讲解了操作系统如何实现这一认证过程。

**第十三章:网络认证    **当用户想要访问 Windows 企业网络中的网络服务时,他们通常需要进行认证。Windows 提供了特殊的网络协议来实现这一认证,而无需将用户的凭证暴露给可能存在的恶意网络。本章讲解了网络认证过程,重点介绍了新技术局域网管理器(NTLM)认证协议。

**第十四章:Kerberos 认证    **与 Active Directory 一起,Windows 2000 还引入了开源的 Kerberos 认证协议,用于企业网络认证。本章讲解了 Kerberos 在 Windows 中如何进行交互式认证和网络认证。

第十五章:协商身份验证和其他安全包    多年来,Windows 增加了其他类型的网络身份验证协议。本章涵盖了这些新类型,包括 Negotiate,以补充第十三章和第十四章中讨论的内容。

最后,两个附录提供了配置细节和更多资源。

附录 A:为测试构建 Windows 域网络    为了运行书中的一些示例,您需要一个 Windows 域网络。本附录提供了使用 PowerShell 配置测试网络的一些步骤。

附录 B:SDDL SID 别名映射    本附录提供了第五章中引用的常量表。

本书中使用的 PowerShell 约定

PowerShell 脚本语言是所有版本 Windows 中都包含的一项功能,是灵活地实验操作系统内部结构的最佳方式之一,而无需安装太多额外的软件。由于 PowerShell 基于 .NET 运行时,本书将使用我为与 Windows 交互而编写的 .NET 库,使得开发复杂脚本变得更加容易。本书中的所有示例脚本都可以从 <wbr>github<wbr>.com<wbr>/tyranid<wbr>/windows<wbr>-security<wbr>-internals 下载。

每章中的 PowerShell 示例遵循一套常见的风格约定,旨在帮助您理解如何使用它们。每个示例都以列表的形式提供,其中有两种类型:交互式和非交互式。交互式 PowerShell 列表是您应在命令行输入的,以观察结果。以下是一个交互式列表示例:

❶ PS> **ls C:\**
❷ Directory: C:\
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-r---               4/17  11:45 AM        Program Files
❸ `--snip--` 

交互式列表在每个命令前面都会有一个 PowerShell 风格的提示符 (PS>),并以粗体显示命令 ❶。您将在命令下方看到相应的输出 ❷。有时输出可能非常长,因此为了节省空间,我使用 --snip-- 来表示输出已被截断 ❸。还请注意,在某些示例中,输出是示意性的,可能会根据您的操作系统或网络配置略有不同。

大多数交互式列表设计为从普通用户账户执行。然而,某些列表必须以管理员账户运行才能访问某些受保护的功能。如果不以管理员身份运行命令,结果将不正确。每个列表前面的文本将说明是否需要以管理员身份运行命令。

非交互式列表包含 PowerShell 代码,您可以将其复制到脚本文件中以供重用,示例如下:

function Get-Hello {
    "Hello"
} 

非交互式列表不包含 PowerShell 提示符,并且不会使用粗体。

如果你曾经在 PowerShell 中编写过脚本,你会知道该语言以冗长的命令和参数名称著称。这使得某些命令在书中难以适应单行显示。以下是一个长 PowerShell 命令的示例,以及书中可能将其拆分以适应页面的一些方式:

PS> **Get-ChildItem -LiteralPath "C:\" -Filter "*.exe" -Recurse -Hidden**
❶ **-System** **-Depth 5 | Where-Object {**
  ❷ **$_.Name -eq "Hello"**
**}** 

第一行,使用 Get-ChildItem 命令,过长以至于无法显示在同一行,因此它换行到了下一行 ❶。你不能在命令的中间随意添加换行符,所以当你在终端或文件中输入时,应该把它当作一行来处理。表明该行继续的关键标志是第一列有一个粗体字符,而不是输出的一部分。

PowerShell 可以在某些字符上断行,例如管道符号 (|)、逗号 (,) 或大括号 ({})。在这个示例中,我在开括号 ({) 后添加了换行符,并将后续的命令放在大括号内,缩进了一个层级 ❷。在这种情况下,Shell 会处理新行的引入。注意,闭括号 (}) 位于第一列,因此你可能认为它应该放在前一行上。尽管将大括号移动到前一行在这个特定情况下仍然有效,但这是不必要的。

请注意,Windows 操作系统仍在积极开发中。虽然所有 PowerShell 示例都已经在撰写时可用的最新版本的 Windows 上进行了测试,但到你阅读这本书时,可能会引入新的安全功能,或者一些旧功能会被弃用。以下是测试示例的版本列表以及主要的操作系统版本号:

  • Windows 11(操作系统版本 22631)

  • Windows 10(操作系统版本 19045)

  • Windows Server 2022(操作系统版本 20384)

  • Windows Server 2019(操作系统版本 17763)

文本中提到的“最新版本”指的是以下这些版本。

联系方式

我一直很乐意收到关于我工作中的反馈,无论是积极的还是消极的,这本书也不例外。你可以通过电子邮件联系我,地址是 winsecinternals.book@gmail.com。你还可以订阅我的博客 <wbr>www<wbr>.tiraniddo<wbr>.dev,我会在那儿发布一些我最新的高级安全研究。

第一章:1 设置 PowerShell 测试环境

在本章中,你将配置 PowerShell,以便能够通过本书后续的代码示例。然后,我们将快速概述 PowerShell 语言,包括其类型、变量和表达式。我们还将介绍如何执行命令、如何获取帮助以及如何导出数据以供以后使用。

选择 PowerShell 版本

要有效使用本书,最重要的工具就是 PowerShell,它从 Windows 7 开始默认安装在 Windows 操作系统中。然而,这个工具有许多不同的版本。当前受支持版本的 Windows 默认安装的是 PowerShell 5.1,这对于我们的目的已经足够,尽管微软不再完全支持它。更新版本的 PowerShell 是跨平台和开源的,但需要在 Windows 上单独安装。

本书中展示的所有代码都可以在 PowerShell 5.1 和最新的开源版本中运行,因此你选择哪个版本都无妨。如果你想使用开源 PowerShell,请访问该项目的 GitHub 页面 <wbr>github<wbr>.com<wbr>/PowerShell<wbr>/PowerShell,在该页面上可以找到适用于你版本 Windows 的安装说明。

配置 PowerShell

在 PowerShell 中,我们首先需要设置脚本执行策略,它决定了 PowerShell 可以执行哪些类型的脚本。对于运行 PowerShell 5.1 的 Windows 客户端,默认策略是 Restricted,它会阻止所有脚本的执行,除非这些脚本经过受信任证书签名。由于本书中的脚本没有签名,我们将把执行策略更改为 RemoteSigned。此执行策略允许我们运行本地创建的未签名 PowerShell 脚本,但不允许执行通过 Web 浏览器下载或附加在电子邮件中的未签名脚本。运行以下命令以设置执行策略:

PS> **Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force** 

该命令仅更改当前用户的执行策略,而不会影响整个系统。如果你想更改所有用户的策略,需要以管理员身份启动 PowerShell,然后重新运行该命令,去掉 Scope 参数。

如果你正在使用开源版本的 PowerShell 或在 Windows Server 上使用版本 5.1,则默认的脚本执行策略是 RemoteSigned,你无需更改任何设置。

现在我们可以运行未签名的脚本了,接下来可以安装本书中将使用的 PowerShell 模块。PowerShell 模块 是一组脚本和 .NET 二进制文件,导出 PowerShell 命令。每个 PowerShell 安装都预装了多个模块,处理从配置应用程序到设置 Windows 更新等任务。你可以通过复制模块文件手动安装模块,但最简单的方法是使用 PowerShell Gallery(<wbr>www<wbr>.powershellgallery<wbr>.com),这是一个在线模块库。

要从 PowerShell Gallery 安装模块,我们使用 PowerShell 的 Install-Module 命令。对于本书,我们需要安装 NtObjectManager 模块,使用以下命令可以完成安装:

PS> **Install-Module NtObjectManager -Scope CurrentUser -Force** 

确保如果安装程序问你任何问题时都选择“是”(当然是在你已阅读并理解问题后)。如果你已经安装了模块,可以通过使用 Update-Module 命令来确保你拥有最新版本:

PS> **Update-Module NtObjectManager**

安装完成后,你可以使用 Import-Module 命令加载模块:

PS> **Import-Module NtObjectManager** 

如果在导入模块后看到任何错误,仔细检查你是否正确设置了执行策略;这通常是模块加载不正确的最常见原因。作为最后的测试,让我们运行一个随模块附带的命令,检查它是否正常工作。执行 列表 1-1 中的命令,并验证输出是否与 PowerShell 控制台中显示的内容匹配。我们将在后续章节中探讨此命令的目的。

PS> **New-NtSecurityDescriptor**
Owner DACL ACE Count SACL ACE Count Integrity Level
----- -------------- -------------- ---------------
NONE  NONE           NONE           NONE 

列表 1-1:测试 NtObjectManager 模块是否正常工作

如果一切正常,并且你对 PowerShell 感到舒适,可以继续阅读下一章。如果你需要快速回顾 PowerShell 语言,可以继续阅读本节。

PowerShell 语言概述

本书不打算对 PowerShell 进行全面介绍。然而,本节会涉及你需要熟悉的各种语言特性,以便更有效地使用本书。

理解类型、变量和表达式

PowerShell 支持许多不同的类型,从基本的整数和字符串到复杂的对象。表 1-1 显示了一些最常见的内建类型,以及它们对应的 .NET 运行时类型和一些简单的示例。

表 1-1: 常见的基本 PowerShell 类型与 .NET 类型及示例

类型 .NET 类型 示例
int System.Int32 142, 0x8E, 0216
long System.Int64 142L, 0x8EL, 0216L
string System.String "Hello", 'World!'
double System.Double 1.0, 1e10
bool System.Boolean $true, $false
array System.Object[] @(1, "ABC", $true)
hashtable System.Collections.Hashtable @

要对基本类型进行计算,可以使用常见的运算符,如 +、−、* 和 /。这些运算符可以被重载;例如,+ 除了用于加法外,还用于连接字符串和数组。表 1-2 提供了常见运算符的列表,包括简单的示例及其结果。你可以自己测试这些示例,查看每个运算符的输出。

表 1-2: 常见操作符

操作符 名称 示例 结果
+ 加法或连接 1 + 2, "Hello" + "World!" 3, "HelloWorld!"
减法 2 − 1 1
* 乘法 2 * 4 8
/ 除法 8 / 4 2
% 取余 6 % 4 2
[] 索引 @(3, 2, 1, 0)[1] 2
-f 字符串格式化器 "0x{0:X} {1}" -f 42, 123 "0x2A 123"
-band 按位与 0x1FF -band 0xFF 255
-bor 按位或 0x100 -bor 0x20 288
-bxor 按位异或 0xCC -bxor 0xDD 17
-bnot 按位取反 -bnot 0xEE -239
-and 布尔与 $true -and $false $false
-or 布尔或 $true -or $false $true
-not 布尔取反 -not $true | $false
-eq 等于 "Hello" -eq "Hello" $true
-ne 不等于 "Hello" -ne "Hello" $false
-lt 小于 4 -lt 10 $true
-gt 大于 4 -gt 10 $false

你可以使用赋值运算符=将值赋给变量。变量有一个字母数字的名称,前面带有$字符。例如,清单 1-2 展示了如何将数组捕获到变量中,并使用索引运算符查找值。

PS> **$var = 3, 2, 1, 0**
PS> **$var[1]**
2 

清单 1-2:将数组捕获到变量中,并通过变量名称进行索引

本书的其余部分还将使用一些预定义的变量。这些变量包括:

$null  表示 NULL 值,表示在比较中不存在值

$pwd  包含当前工作目录

$pid  包含 Shell 的进程 ID

\(env  访问进程环境(例如,\)env:WinDir 获取Windows 目录)

你可以使用 Get-Variable 命令枚举所有变量。

在表 1-1 中,你可能注意到有两个字符串示例,一个使用双引号,另一个使用单引号。它们之间的一个区别是,双引号字符串支持字符串插值,你可以在字符串中插入变量名作为占位符,PowerShell 会将其值包含在结果中。示例 1-3 展示了当你在双引号和单引号字符串中进行操作时的结果。

PS> **$var = 42**
PS> **"The magic number is $var"**
The magic number is 42

PS> **'It is not $var'**
It is not $var 

示例 1-3:字符串插值示例

首先,我们定义一个值为 42 的变量,以便插入到字符串中。然后,我们创建一个双引号字符串,其中包含变量名。结果是字符串,其中变量名被其格式化为字符串的值替换。(如果你希望更好地控制格式,可以使用表 1-2 中定义的字符串格式化操作符。)

接下来,为了演示单引号字符串的不同表现,我们定义了一个内联包含变量名的单引号字符串。我们可以观察到,在这种情况下,变量名被逐字复制,并没有被值替换。

另一个区别是双引号字符串可以包含在单引号字符串中被忽略的字符转义。这些转义使用类似于 C 语言的语法,但 PowerShell 使用反引号(`)而不是反斜杠字符(\)。这是因为 Windows 使用反斜杠作为路径分隔符,如果每次都需要转义反斜杠,编写文件路径会非常麻烦。表 1-3 列出了你可以在 PowerShell 中使用的字符转义。

表 1-3: 字符串字符转义

字符转义 名称
`0 NUL 字符,值为零
`a 铃声
`b 退格符
`n 换行符
`r 回车符
`t 水平制表符
`v 垂直制表符
`` 反引号字符
`" 双引号字符

如果你想在双引号字符串中插入一个双引号字符,你需要使用`"转义字符。要在单引号字符串中插入单引号字符,你需要将引号字符加倍:例如,'Hello''There'会转换成Hello'There。另外请注意表格中提到的 NUL 字符。由于 PowerShell 使用的是.NET 字符串类型,它可以包含嵌入的 NUL 字符。与 C 语言不同,添加 NUL 字符不会提前终止字符串。

因为所有值都是.NET 类型,所以我们可以在对象上调用方法并访问属性。例如,下面的代码调用了ToCharArray方法,将字符串转换为单个字符的数组:

PS> **"Hello".ToCharArray()**
H
e
l
l
o 

我们可以使用 PowerShell 构造几乎任何.NET 类型。最简单的方法是通过在方括号中指定.NET 类型来将值转换为该类型。在进行类型转换时,PowerShell 会尝试查找适合该类型的构造函数并调用它。例如,下面的命令将字符串转换为System.Guid对象;PowerShell 会找到接受字符串的构造函数并调用它:

PS> **[System.Guid]"6c0a3a17-4459-4339-a3b6-1cdb1b3e8973"** 

你也可以通过在类型上调用new方法显式地调用构造函数。之前的示例可以重写如下:

PS> **[System.Guid]::new("6c0a3a17-4459-4339-a3b6-1cdb1b3e8973")** 

这种语法也可以用来调用类型上的静态方法。例如,下面的代码调用了NewGuid静态方法来创建一个新的随机全局唯一标识符(GUID):

PS> **[System.Guid]::NewGuid()** 

你也可以使用New-Object命令来创建新对象:

PS> **New-Object -TypeName Guid -ArgumentList "6c0a3a17-4459-4339-a3b6-1cdb1b3e8973"** 

这个示例等同于调用静态的new函数。

执行命令

几乎所有 PowerShell 命令都遵循一种常见的命名模式:一个动词和一个名词,通过连字符分隔。例如,考虑命令 Get-Item。Get 动词表示获取现有资源,而 Item 则是要返回的资源类型。

每个命令都可以接受一系列参数,这些参数控制命令的行为。例如,Get-Item 命令接受一个 Path 参数,该参数指示要检索的现有资源,如下所示:

PS> **Get-Item -Path "C:\Windows"** 

Path 参数是一个 位置 参数。这意味着你可以省略参数的名称,PowerShell 会尽力选择最合适的匹配项。因此,之前的命令也可以写成如下形式:

PS> **Get-Item "C:\Windows"** 

如果一个参数接受字符串值,并且字符串不包含任何特殊字符或空格,那么你无需在字符串周围使用引号。例如,Get-Item 命令也可以像下面这样使用:

PS> **Get-Item C:\Windows** 

单个命令的输出是零个或多个值,这些值可以是基本类型或复杂对象类型。你可以使用 管道(由竖线字符 | 表示)将一个命令的输出作为输入传递给另一个命令。稍后在本章中,我们会通过过滤、分组和排序的例子来演示如何使用管道。

你可以将整个命令或管道的结果捕获到一个变量中,然后与结果进行交互。例如,下面的命令捕获了 Get-Item 命令的结果,并查询 FullName 属性:

PS> **$var = Get-Item -Path "C:\Windows"**
PS> **$var.FullName**
C:\Windows 

如果你不想将结果捕获到变量中,你可以将命令括在圆括号中,直接访问其属性和方法:

PS> **(Get-Item -Path "C:\Windows").FullName**
C:\Windows 

命令行的长度实际上是无限的。然而,你应该尝试将长行分开,以使命令更易读。Shell 会在管道字符上自动拆分一行。如果你需要拆分一行且没有管道符,你可以使用反引号字符,然后开始新的一行。反引号必须是行尾的最后一个字符;否则,脚本解析时会发生错误。

发现命令和获取帮助

默认安装的 PowerShell 有成百上千的命令可供选择。这意味着找到执行特定任务的命令可能很困难,即使你找到了命令,如何使用它也可能不清晰。为了帮助,你可以使用两个内置命令,Get-Command和Get-Help。

Get-Command命令可用于列举所有可用的命令。在最简单的形式下,你可以在不带任何参数的情况下执行它,它将打印出所有模块中的所有命令。然而,通常更有用的是筛选出你感兴趣的特定词。例如,Listing 1-4 将仅列出名称中包含SecurityDescriptor的命令。

PS> **Get-Command -Name *SecurityDescriptor***
CommandType     Name                              Source
-----------     ----                              ------
Function        Add-NtSecurityDescriptorControl   NtObjectManager
Function        Add-NtSecurityDescriptorDaclAce   NtObjectManager
Function        Clear-NtSecurityDescriptorDacl    NtObjectManager
Function        Clear-NtSecurityDescriptorSacl    NtObjectManager
`--snip--` 

Listing 1-4:使用 Get-Command 列举命令

这个命令使用通配符语法来列出仅包含指定单词的命令。通配符语法使用字符来表示任何字符或字符序列。在这里,我们在SecurityDescriptor的两侧都加上了,表示它前后可以有任意文本。

你还可以列出某个模块中可用的命令。例如,Listing 1-5 将仅列出由NtObjectManager模块导出的并以动词Start开头的命令。

PS> **Get-Command -Module NtObjectManager -Name Start-***
CommandType     Name                             Source
-----------     ----                             ------
Function        Start-AccessibleScheduledTask    NtObjectManager
Function        Start-NtFileOplock               NtObjectManager
Function        Start-Win32ChildProcess          NtObjectManager
Cmdlet          Start-NtDebugWait                NtObjectManager
Cmdlet          Start-NtWait                     NtObjectManager 

Listing 1-5:使用 Get-Command 列举 NtObjectManager 模块中的命令

一旦你找到了一个看起来有前景的命令,你可以使用Get-Help命令来检查它的参数并获取一些用法示例。在 Listing 1-6 中,我们从 Listing 1-5 中拿出Start-NtWait命令并传递给Get-Help。

PS> **Get-Help Start-NtWait**
NAME
  ❶ Start-NtWait
SYNOPSIS
  ❷ Wait on one or more NT objects to become signaled.
SYNTAX
  ❸ Start-NtWait [-Object] <NtObject[]> [-Alertable <SwitchParameter>]
    [-Hour <int>] [-MilliSecond <long>]
    [-Minute <int>] [-Second <int>] [-WaitAll <SwitchParameter>]
    [<CommonParameters>]

    Start-NtWait [-Object] <NtObject[]> [-Alertable <SwitchParameter>]
    [-Infinite <SwitchParameter>] [-WaitAll <SwitchParameter>]
    [<CommonParameters>]
DESCRIPTION
  ❹ This cmdlet allows you to issue a wait on one or more NT
    objects until they become signaled.
`--snip--` 

Listing 1-6:显示 Start-NtWait 命令的帮助

默认情况下,Get-Help输出命令的名称❶,简短的概述❷,命令的语法❸,以及更深入的描述❹。在命令语法部分,你可以看到它的多种可能操作模式:在这种情况下,可以指定小时、分钟、秒和/或毫秒的时间,或者指定Infinite来无限期等待。

当语法的任何部分显示在括号中[]时,表示它是可选的。例如,唯一必需的参数是Object,它接受一个NtObject值的数组。即使该参数的名称也是可选的,因为-Object是括起来的。

你可以通过使用Parameter命令获取更多关于参数的信息。列表 1-7 显示了

posted @ 2025-11-27 09:19  绝不原创的飞龙  阅读(9)  评论(0)    收藏  举报