PowerShell 技能连载 - 远程读取配置表(第 1 部分)

如果您无法使用 PowerShell 远程处理,那么您需要通过 DCOM 从另一个系统中读取注册表值,以下是一些您可能希望试验的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ComputerName = 'pc01'
# NOTE: RemoteRegistry Service needs to run on a target system!
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $ComputerName)
$key = $reg.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall')

$key.GetSubKeyNames() | ForEach-Object {
$subkey = $key.OpenSubKey($_)
[PSCustomObject]@{
Name = $subkey.GetValue(‘DisplayName’)
Version = $subkey.GetValue(‘DisplayVersion’)
}
$subkey.Close()
}

$key.Close()
$reg.Close()

只需要做一些小改动,该代码就能返回一个 AD 使用的列表

这段示例代码需要:

  • 您有目标机器的本地管理员特权
  • RemoteRegistry 服务在对方机器上运行
  • 对方机器的本地防火墙启用了“远程管理例外”

PowerShell 技能连载 - 强化 PowerShell 脚本块的日志

当您启用了 ScriptBlockLogging 后,PowerShell 将会记录所有在您机器上执行的所有 PowerShell 代码。如果没有启用它,所有安全相关的代码仍然会记录。这样很不错。然而,该任何用户都可以读取该日志,所以任何人都可以类似这样浏览记录下的代码:

1
Get-WinEvent -FilterHashtable @{ ProviderName="Microsoft-Windows-PowerShell";  Id = 4104 }

To harden security and limit the access to the log file, you have two choices:
要强化安全并限制日志文件的读取,您有两个选择:

  • 您可以通过安装数字证书来设置加密的日志。通过这种方法,记录的数据可以被保护起来,甚至其他的管理员同事也无法读取。然而,设置和管理这些证书并不那么简单。
  • 您可以增强 PowerShell 操作日志的存取安全,并且使用和传统的安全日志相同的存取方式。通过这种方法,只有管理员可以读取该日志。这是我们今天要在本技能中讨论的方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#requires -RunAsAdministrator

# this is where the PowerShell operational log stores its settings
$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\winevt\Channels\Microsoft-Windows-PowerShell/Operational"

# get the default SDDL security definition for the classic security log
$sddlSecurity = ((wevtutil gl security) -like 'channelAccess*').Split(' ')[-1]

# get the current SDDL security for the PowerShell log
$sddlPowerShell = (Get-ItemProperty -Path $Path).ChannelAccess

# store the current SDDL security (just in case you want to restore it later)
$existsBackup = Test-Path -Path $Path
if (!$existsBackup)
{
Set-ItemProperty -Path $Path -Name ChannelAccessBackup -Value $sddlPowerShell
}

# set the hardened security to the PowerShell operational log
Set-ItemProperty -Path $Path -Name ChannelAccess -Value $sddlSecurity

# restart the service to take effect
Restart-Service -Name EventLog -Force

当您运行该脚本时,读取 PowerShell 操作日志的权限被限制为只有本地管理员。

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