PowerShell 技能连载 - 使用注册表用户配置单元

读写注册表的 HKEY_LOCAL_USER 十分容易,因为这个配置单元对于所有用户都一致。那么如何读写其他用户的 HKEY_CURRENT_USER 配置单元?

假设您是管理员并且希望为其他用户的 HKEY_CURRENT_USER 配置单元添加注册表值。

首先您需要挂载该用户的用户配置单元。该配置单元位于该用户的用户配置文件下的 NTUSER.DAT 文件中。作为一个管理员,您需要先运行以下 PowerShell 代码来挂载 UserTobias 用户的用户配置文件:

1
PS C:\> REG LOAD HKEY_Users\UserTobias "C:\Users\Tobias\NTUSER.DAT"

该用户配置单元将挂载在 HKEY_USERS 下名为 UserTobias 的注册表键中,而且 PowerShell 可以类似这样存取该路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS C:\> Get-ChildItem -Path Registry::HKEY_USERS\UserTobias


Hive: HKEY_USERS\UserTobias


Name Property
---- --------
AppEvents
Console ColorTable00 : 789516
ColorTable01 : 14300928
ColorTable02 : 958739
ColorTable03 : 14521914
ColorTable04 : 2035653
ColorTable05 : 9967496
ColorTable06 : 40129

现在要读取甚至写入该指定用户的配置单元十分容易。以下代码将创建一个新的注册表键:

1
PS C:\> $null = New-Item -Path Registry::HKEY_USERS\UserTobias\Software\Microsoft\Windows\CurrentVersion\Test

以下是如何读取/写入一个值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PS C:\> Get-ItemProperty -Path Registry::HKEY_USERS\UserTobias\Software\Microsoft\OneDrive


EnableDownlevelInstallOnBluePlus : 0
EnableTHDFFeatures : 1
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_USERS\UserTobias\Software\Microsoft\OneDrive
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_USERS\UserTobias\Software\Microsoft
PSChildName : OneDrive
PSProvider : Microsoft.PowerShell.Core\Registry




PS C:\> Set-ItemProperty -Path Registry::HKEY_USERS\UserTobias\Software\Microsoft\OneDrive -Name EnableDownlevelInstallOnBluePlus -Value 1 -Type DWord

PS C:\> Get-ItemProperty -Path Registry::HKEY_USERS\UserTobias\Software\Microsoft\OneDrive


EnableDownlevelInstallOnBluePlus : 1
EnableTHDFFeatures : 1
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_USERS\UserTobias\Software\Microsoft\OneDrive
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_USERS\UserTobias\Software\Microsoft
PSChildName : OneDrive
PSProvider : Microsoft.PowerShell.Core\Registry

当您操作完 HKEY_USERS 注册表配置单元之后,别忘了卸载它:

1
PS C:\> $null = REG UNLOAD HKEY_Users\UserTobias

请注意这条命令将会抛出一个 “Access Denied” 错误,如果您没有管理员特权,或者该注册表配置单元正在被其他人使用。例如,如果您启动了 regedit.exe,当该用户配置单元加载以后,regedit.exe 可以显示加载的用户配置单元,而当 regedit 处于打开状态时,该配置单元被锁定并且无法关闭。

PowerShell 技能连载 - 管理 Windows 功能(第 2 部分)

在 Windows 10 中,不像 Windows Server,您无法使用 Get-WindowsFeatureAdd-WindowsFeature cmdlet 来管理 Windows 功能。

然而,对于客户端来说,有一个很类似的 cmdlet 可用:Enable-WindowsOptionalFeature。以下代码将添加 PowerShell Hyper-V cmdlet 和 Hyper-V 功能:

1
2
Enable-WindowsOptionalFeature -Online -All -FeatureName Microsoft-Hyper-V-Management-PowerShell -NoRestart
Enable-WindowsOptionalFeature -Online -All -FeatureName Microsoft-Hyper-V -NoRestart

执行的结果是一个对象,告知您是否需要重启。

请注意 -All 参数:如果您忽略了该参数,那么您需要自行确保所有先决条件和依赖项都已安装好,才能添加另一个新项。如果您懒,或者不了解依赖项,那么 -All 参数将自动为您安装所有必须的依赖项。

PowerShell 技能连载 - 管理 Windows 功能(第 1 部分)

Windows 10 带来一系列功能,但默认只安装了一个子集。您可以手工打开控制面板查看 Windows 功能。有经验的管理员也会使用 dism.exe 命令行工具。

在 PowerShell 中,您可以通过 Get-WindowsOptionalFeature 查看 Windows 功能的状态。当您指定了 -Online 参数,该 cmdlet 将返回当前可用的功能和它们的状态。

使用 Where-Object 命令,您可以容易地过滤结果,并且例如只显示未安装的功能清单:

1
2
3
4
5
6
7
# list all Windows features and their state
Get-WindowsOptionalFeature -Online | Out-GridView

# list only available features that are not yet installed
Get-WindowsOptionalFeature -Online |
Where-Object State -eq Disabled |
Out-GridView

PowerShell 技能连载 - 在 PowerShell 中运行 CMD 命令

PowerShell 默认情况下不支持原生的 cmd.exe 命令,例如 “dir“。替代的是,它使用历史别名 “dir“ 指向最接近的 PowerShell cmdlet:

1
2
3
4
5
PS C:\> Get-Command -Name dir | ft -AutoSize

CommandType Name Version Source
----------- ---- ------- ------
Alias dir -> Get-ChildItem

这解释了为什么 PowerShell 中的 “dir“ 不支持 cmd.exe 以及批处理文件中的开关和参数,例如 “cmd.exe/w“。

如果您必须使用 cmd 形式的命令,请用 /c 参数(代表 “command”)启动一个原生的 cmd.exe,执行这条命令,并在 PowerShell 内处理执行结果。这个例子运行 cmd.exe /c,然后以参数 /w 运行旧的 “dir“ 命令:

1
PS C:\> cmd.exe /c dir /w

一个更安全的方法是使用 “--%“ 操作符:

1
PS C:\> cmd.exe --% /c dir /w

当在参数之前添加了它之后,PowerShell 解释器将不会对参数进行处理,这样您甚至可以像在 cmd.exe 中一样使用环境变量注解。副作用是:您在参数中不再能使用 PowerShell 技术,比如变量:

1
PS C:\> cmd.exe --% /c dir %WINDIR% /w

PowerShell 技能连载 - 自动获取借口

Invoke-WebRequest 可以从网页获取 HTML 信息,并且可以用正则表达式来提取这些页面中的信息。

以下是一些可以获取英文的借口的代码:

1
2
3
4
5
6
7
8
9
$ProgressPreference = 'SilentlyContinue'

$url = "http://pages.cs.wisc.edu/~ballard/bofh/bofhserver.pl?$(Get-Random)"
$page = Invoke-WebRequest -Uri $url -UseBasicParsing
$pattern = '(?s)<br><font\ size\ =\ "\+2">(.{1,})</font'
if ($page.Content -match $pattern)
{
$matches[1].Trim() -replace '\n', '' -replace '\r', ''
}

以下代码将获取英语和德语混合的借口:

1
2
3
4
5
6
$page= Invoke-WebRequest "http://www.netzmafia.de/cgi-bin/bofhserver.cgi"
$pattern='(?s)<B>(.*?)</B>'
if ($page.Content -match $pattern)
{
$matches[1].Trim() -replace '\n', '' -replace '\r', ''
}

PowerShell 技能连载 - 在可扩展字符串中分隔变量

当使用双引号字符串时,您可以扩展它们当中的变量,类似这样:

1
2
PS C:\> "Windir: $env:windir"
Windir: C:\Windows

然而,没有明显的方法来标记变量的起止位置,所以以下操作将会失败:

1
2
PS C:\> "$env:windir: this is my Windows folder"
this is my Windows folder

解决方案是使用大括号来标识字符串内变量的起止位置:

1
2
PS C:\> "${env:windir}: this is my Windows folder"
C:\Windows: this is my Windows folder

PowerShell 技能连载 - 查看 Windows 生成号

当运行 winver.exe 时,您可以方便地获取到完整的 Windows 生成号。通过 PowerShell 读取生成号并不是那么明显。并没有内置的 cmdlet。

不过,要创建这样功能的函数很简单:

1
2
3
4
5
6
function Get-OSInfo
{
$path = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
Get-ItemProperty -Path $path -Name CurrentBuild, UBR, ReleaseID, CompositionEditionID |
Select-Object -Property CurrentBuild, UBR, ReleaseID, CompositionEditionID
}

结果看起来类似这样:

1
2
3
4
5
PS C:\> Get-OSInfo

CurrentBuild UBR ReleaseId CompositionEditionID
------------ --- --------- --------------------
15063 1088 1703 Enterprise

PowerShell 技能连载 - 检查 USB 设备

如果想知道某个特定的设备是否连接到您的计算机上,您可以使用 WMI 来提取所有即插即用设备的名称:

1
2
Get-WmiObject -Class Win32_PnpEntity |
Select-Object -ExpandProperty Caption

PowerShell 技能连载 - 使用 PSGraph

PSGraph 是一个非常棒的免费 PowerShell 库,您可以用它来将关系可视化。在使用 PSGraph 之前,需要安装它的依赖项(graphviz 引擎)。两者都需要管理员特权:

1
2
3
4
5
6
7
8
#requires -RunAsAdministrator

# install prerequisite (graphviz)
Register-PackageSource -Name Chocolatey -ProviderName Chocolatey -Location http://chocolatey.org/api/v2/
Find-Package graphviz | Install-Package -ForceBootstrap

# install PowerShell module
Install-Module -Name PSGraph

安装完成后,这是如何将对象关系可视化的代码:

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
$webServers = 'Web1','Web2','web3'
$apiServers = 'api1','api2'
$databaseServers = 'db1'

graph site1 {
# External/DMZ nodes
subgraph 0 -Attributes @{label='DMZ'} {
node 'loadbalancer' @{shape='house'}
rank $webServers
node $webServers @{shape='rect'}
edge 'loadbalancer' $webServers
}

subgraph 1 -Attributes @{label='Internal'} {
# Internal API servers
rank $apiServers
node $apiServers
edge $webServers -to $apiServers

# Database Servers
rank $databaseServers
node $databaseServers @{shape='octagon'}
edge $apiServers -to $databaseServers
}
} | Export-PSGraph -ShowGraph

这个例子中创建的图形使用脚本文件中的 hypothetical 服务器并向其添加合适的关系,并显示图像。

PowerShell 技能连载 - 使用 AD 过滤器配合 cmdlet(第 4 部分)

在前一个技能中,我们开始学习 ActiveDirectory 模块(免费的 RSAT 工具)中的 cmdlet 如何过滤执行结果,并且学习了如何合并过滤器表达式。今天我们将学习如何处理日期和时间。

有些 AD 属性包含日期和时间信息,例如上次登录的日期。这类信息是以一串非常长的 64 位整数标识的。您可以在 LDAP 过滤器中以这种格式使用日期和时间。

例如,要查找近 4 个星期中没有修改密码的所有用户:

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
$weeks = 4
# first, find out the AD time format from
# 4 weeks ago that will be used in the LDAPFilter

$today = Get-Date
# 4 weeks ago
$cutDate = $today.AddDays(-($weeks * 7))
# translate in AD time format
$cutDateAD = $cutDate.ToFileTimeUtc()

# next, find a way to convert back the AD file format
$realDate = @{
Name = 'Date'
Expression = { if ($_.pwdLastset -eq 0)
{
'[never]'
}
else
{
[DateTime]::FromFileTimeUtc($_.pwdLastset)
}
}

}

Get-ADUser -LDAPFilter "(pwdLastSet<=$cutDateAD)" -Properties pwdLastSet |
Select-Object -Property samaccountname, $realDate

实际上,当调用一个 DateTime 对象的 ToFileTimeUtc() 方法之后,将返回 AD 格式的数据。类似地,当您运行 [DateTime]::FromFileTimeUtc() 时,将会把 AD 格式转换为一个真实的 DateTime 对象。