PowerShell 技能连载 - 自动创建 HTTP 响应码清单

在前一个示例中我们学习了如何自动将数值型的 HTTP 响应码转换为描述性的文本,只需要将它们转换为 System.Net.HttpStatusCode 即可。

1
2
PS> [System.Net.HttpStatusCode]500
InternalServerError

这是因为 System.Net.HttpStatusCode 是一个所谓的“枚举”,其作用类似于“查找表”。您可以轻松地转储枚举的所有成员,例如创建一个 HTTP 响应代码表:

1
2
3
4
5
6
7
[Enum]::GetValues([System.Net.HttpStatusCode]) |
ForEach-Object {
[PSCustomObject]@{
Code = [int]$_
Description = $_.toString()
}
}

以上是创建一个最常见的 HTTP 响应码所需要的所有代码。

Code Description
---- -----------
 100 Continue
 101 SwitchingProtocols
 200 OK
 201 Created
 202 Accepted
 203 NonAuthoritativeInformation
 204 NoContent
 205 ResetContent
 206 PartialContent
 300 MultipleChoices
 300 MultipleChoices
 301 MovedPermanently
 301 MovedPermanently
 302 Redirect
 302 Redirect
 303 SeeOther
 303 SeeOther
 304 NotModified
 305 UseProxy
 306 Unused
 307 TemporaryRedirect
 307 TemporaryRedirect
 400 BadRequest
 401 Unauthorized
 402 PaymentRequired
 403 Forbidden
 404 NotFound
 405 MethodNotAllowed
 406 NotAcceptable
 407 ProxyAuthenticationRequired
 408 RequestTimeout
 409 Conflict
 410 Gone
 411 LengthRequired
 412 PreconditionFailed
 413 RequestEntityTooLarge
 414 RequestUriTooLong
 415 UnsupportedMediaType
 416 RequestedRangeNotSatisfiable
 417 ExpectationFailed
 426 UpgradeRequired
 500 InternalServerError
 501 NotImplemented
 502 BadGateway
 503 ServiceUnavailable
 504 GatewayTimeout
 505 HttpVersionNotSupported

这种方法适用于您可能遇到的任何枚举。只需更改枚举数据类型的名称即可。这个例子转储可用的控制台颜色代码:

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
28
[Enum]::GetValues([System.ConsoleColor]) |
ForEach-Object {
[PSCustomObject]@{
Code = [int]$_
Description = $_.toString()
}
}



Code Description
---- -----------
0 Black
1 DarkBlue
2 DarkGreen
3 DarkCyan
4 DarkRed
5 DarkMagenta
6 DarkYellow
7 Gray
8 DarkGray
9 Blue
10 Green
11 Cyan
12 Red
13 Magenta
14 Yellow
15 White

PowerShell 技能连载 - 转换 HTTP 响应码

在前一个示例中我们创建了一个小的 PowerShell 函数,它能够检查 Web 网络的可用性,并 HTTP 返回码会作为测试结果的一部分返回。让我们看看如何可以轻松地将这个数字代码转换为有意义的文本消息。

以下还是那个测试网站的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Test-Url
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$Url
)

Add-Type -AssemblyName System.Web

$check = "https://isitdown.site/api/v3/"
$encoded = [System.Web.HttpUtility]::UrlEncode($url)
$callUrl = "$check$encoded"

Invoke-RestMethod -Uri $callUrl |
Select-Object -Property Host, IsItDown, Response_Code
}

以下是典型的结果:

1
2
3
4
5
PS C:\> Test-Url -Url powershellmagazine.com

host isitdown response_code
---- -------- -------------
powershellmagazine.com False 200

在这个示例中,响应代码是 “200”,恰好代表 “OK”。如果您希望将 HTTP 响应码转换为文本,只需要将数据类型转换为 [System.Net.HttpStatusCode]。这样就可以了:

1
2
PS C:\> 200 -as [System.Net.HttpStatusCode]
OK

以下是包含该转换过程的版本:

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
function Test-Url
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$Url
)

Add-Type -AssemblyName System.Web

$check = "https://isitdown.site/api/v3/"
$encoded = [System.Web.HttpUtility]::UrlEncode($url)
$callUrl = "$check$encoded"
$response = @{
Name = 'Response'
Expression = {
'{0} ({1})' -f
($_.Response_Code -as [System.Net.HttpStatusCode]),
$_.Response_Code
}
}
Invoke-RestMethod -Uri $callUrl |
Select-Object -Property Host, IsItDown, $response
}

结果如下:

1
2
3
4
5
PS C:\> Test-Url -Url powershellmagazine.com

host isitdown Response
---- -------- --------
powershellmagazine.com False OK (200)

请注意计算字段 “Response” 现在体现的是原始的数值型响应码和对应的友好文本。

PowerShell 技能连载 - 测试网站的可用性

当一个网站不可用时,通常的问题是仅仅您不能访问该网站,还是其他所有人都不能访问。PowerShell 可以调用一个 Web Service 为您检查 web 站点的可用性。下面是一个简单的包装函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Test-Url
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$Url
)

Add-Type -AssemblyName System.Web

$check = "https://isitdown.site/api/v3/"
$encoded = [System.Web.HttpUtility]::UrlEncode($url)
$callUrl = "$check$encoded"

Invoke-RestMethod -Uri $callUrl |
Select-Object -Property Host, IsItDown, Response_Code
}

它将调用一个 RESTful API 并且通过 URL 参数进行检查。这是为什么待测试的 URL 需要进行 URL 编码,这段代码调用 Invoke-RestMethod 并且以一个对象的形式接收测试结果。

1
2
3
4
5
PS C:\> Test-Url -Url powershellmagazine.com

host isitdown response_code
---- -------- -------------
powershellmagazine.com False 200

请注意这个示例中使用的 Web Service 是免费的,并且不需要注册或 API 密钥。缺点是该 Web Service 是限流的它可能会返回一个异常,提示您提交了太多请求。当这种情况发生时,只需要等待一阵子再重试。

PowerShell 技能连载 - 通过 Web Service 做单位转换

通过 PowerShell 访问 RESTful Web Service 十分容易:只需要将您的发送数据发给公开的 Web Service,并且接收结果即可。

以下是三个 PowerShell 函数,每个函数处理一个数字转换:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function Convert-InchToCentimeter
{
param
(
[Parameter(Mandatory)]
[Double]
$Inch
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = 'https://ucum.nlm.nih.gov/ucum-service/v1/ucumtransform/{0}/from/%5Bin_i%5D/to/cm' -f $Inch
$result = Invoke-RestMethod -Uri $url -UseBasicParsing
$result.UCUMWebServiceResponse.Response
}


function Convert-FootToMicrometer
{
param
(
[Parameter(Mandatory)]
[Double]
$Foot
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = 'https://ucum.nlm.nih.gov/ucum-service/v1/ucumtransform/{0}/from/%5Bft_i%5D/to/um' -f $Foot
$result = Invoke-RestMethod -Uri $url -UseBasicParsing
$result.UCUMWebServiceResponse.Response
}


function Convert-GramToOunce
{
param
(
[Parameter(Mandatory)]
[Double]
$Gram
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = 'https://ucum.nlm.nih.gov/ucum-service/v1/ucumtransform/{0}/from/g/to/%5Boz_ap%5D' -f $Gram
$result = Invoke-RestMethod -Uri $url -UseBasicParsing
$result.UCUMWebServiceResponse.Response
}

假设您有 Internet 连接,然后做单位换算就是一个函数调用那么简单了:

1
2
3
4
5
PS C:\> Convert-GramToOunce -Gram 230

SourceQuantity SourceUnit TargetUnit ResultQuantity
-------------- ---------- ---------- --------------
230.0 g [oz_ap] 7.3946717

需要注意的点有:

  • 您需要允许 Tls12 来允许 HTTPS 连接(参考代码)
  • 您需要遵守 Web Service 规定的规则,即当它需要整个数数时,您不能提交小数。

更多的转换功能请参考 https://ucum.nlm.nih.gov/ucum-service.html#conversion,您可以使用以上提供的函数作为模板来创建更多的转换函数。

PowerShell 技能连载 - 验证 Active Directory 凭据

PowerShell 可以通过 Active Directory 验证 AD 用户名和密码:

1
2
3
4
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$account = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([DirectoryServices.AccountManagement.ContextType]::Domain, $env:userdomain)

$account.ValidateCredentials('user12', 'topSecret')

请注意这种方法只能作为诊断目的。它以明文的方式输入密码。

PowerShell 技能连载 - 为输出编号(第 1 部分)

如果你想增加一个递增的数字到你的输出,这里有一个简单的方法:

1
2
3
4
5
6
7
Get-Process |
Select-Object -Property '#', ProcessName, CPU -First 10 |
ForEach-Object -begin { $i = 0} -process {
$i++
$_.'#' = $i
$_
} -end {}

Select-Object 添加了一个名为 “#“ 的新属性,ForEach-Object 添加了一个自动递增的数字。结果如下:

 # ProcessName               CPU
 - -----------               ---
 1 AdobeCollabSync       65,5625
 2 AdobeCollabSync           0,5
 3 AGMService
...

PowerShell 技能连载 - 接受屏蔽的密码

如果曾经写过需要接受密码等敏感输入的 PowerShell 函数,请确保允许用户传入 SecureString。如果您通过明文接受密码,则存在很大的风险,即其他人可能在输入密码时看到密码,或者(更糟的是)密码已被记录,稍后可以在转储文件中找到。

下面是一个简单的框架,说明如何实现安全输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Enter-Secret
{
param
(
[Parameter(Mandatory)]
[SecureString]
$SafeInput
)

$PlainText = [Management.Automation.PSCredential]::
new('x',$SafeInput).GetNetworkCredential().Password

"User entered $PlainText"

}

当用户运行 Enter-Secret,可以以屏蔽的方式输入密码。在内部,函数将安全字符串转换为纯文本。这样,秘密密码就永远不可见,也永远不会被记录下来。

从 SecureString 到 String 的转换是通过创建一个临时凭证对象来执行的。凭据对象有一个内置方法 (GetNetworkCredential()),用于将SecureString 转换为字符串。

PowerShell 技能连载 - 查找隐藏的 PowerShell 应用

最广为人知的 PowerShell 宿主当然是 PowerShell.exe 和 powershell_ise.exe,因为它们是开箱即用的。但是,运行 PowerShell 的宿主可能更多(而且是隐藏的)。任何实例化 PowerShell 引擎的软件都是一个 PowerShell 宿主。这可以是 Visual Studio Code(安装了 PowerShell扩展)、Visual Studio 或任何其它类似的软件。

要找出所有当前运行 PowerShell 的宿主,请运行以下命令:

1
2
3
4
5
6
7
8
Get-ChildItem -Path "\\.\pipe\" -Filter '*pshost*' |
ForEach-Object {
$id = $_.Name.Split('.')[2]
if ($id -ne $pid)
{
Get-Process -ID $id
}
}

结果看起来类似这样:

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
   1131     101   628520      42440             11216   0 SupportAssistAgent
   1011      82   269920     299208      85,30  17420   1 powershell_ise
    520      29    68012      75880       1,23  33532   1 powershell
    590      31    69508      77712       2,02  36636   1 powershell
    545      27    67952      76668       1,14  37584   1 powershell
   4114     654   801136     965032     129,69  28968   1 devenv

“SupportAssistAgent” 是由 Visual Studio Code 打开的,而 “devenv” 代表由 Visual Studio 启动的内部 PowerShell 宿主。

PowerShell 技能连载 - 控制处理器关联性

大多数现代计算机都有不止一个处理器,无论是物理处理器还是逻辑处理器。如果你想知道处理器的数量,这里有一段 PowerShell 代码:

1
2
Get-WmiObject -Class Win32_Processor |
Select-Object -Property Caption, NumberOfLogicalProcessors

结果看起来类似这样:

Caption                              NumberOfLogicalProcessors
-------                              -------------------------
Intel64 Family 6 Model 78 Stepping 3                         4

当您在这样的机器上运行一个进程时,它通常没有特定的处理器关联性,因此 Windows 决定该进程将运行在哪个处理器上。

如果愿意,可以为每个进程声明一个特定的处理器关联。这是很有用的,例如,如果你想控制一个程序可以使用的处理器,也就是防止一个进程使用所有的处理器。

处理器关联性由位标志控制。要找出当前处理器与进程的关联关系,请使用以下代码:

1
2
$process = Get-Process -Id $PID
[Convert]::ToString([int]$process.ProcessorAffinity, 2)

在本例中,用的是当前的 PowerShell 进程,但是您可以指定任何进程。典型的结果是:

1111

在 4 处理器的机器上,进程没有特定的关联性,可以使用任何处理器。要设置新的关联,最简单的方法是使用自己的位掩码并更改属性。例如,要将当前 PowerShell 进程仅锁定到第一个处理器,可以这样做:

1
2
3
4
5
6
7
# calculate the bit mask
$mask = '1000'
$bits = [Convert]::ToInt32($mask, 2)

# assign new affinity to current PowerShell process
$process = Get-Process -Id $PID
$process.ProcessorAffinity = $bits

PowerShell 技能连载 - 转储 Chrome 的所有密码

在前面的技能中,我们演示了如何从个人 Windows 密码库转储所有密码。基本上任何密码管理器都是如此,因为这些程序的目的是返回它们为您存储的密码。

谷歌 Chrome 浏览器将您的个人密码(和网站历史记录)存储在 SQLLite 数据库中。PowerShell 可以轻松地访问这个数据库并为您转储信息。但是,要做到这一点,PowerShell 需要不属于 Windows 的特定 SQLLite 程序集。

下面的代码展示了如何 (a) 通过一行程序下载大量代码,(b) 在下载的代码中嵌入一个二进制 .net 程序集。

警告:只能在演示机器上运行此代码,或者确保先下载代码,然后在运行之前仔细检查。代码是从第三方下载的,您永远不知道这些代码是否包含恶意内容。

如果你希望转储 Chrome 的隐私信息,我们建议你把代码下载到一个安全的机器上,隔离并检查它,然后在需要的时候从本地保存的副本中使用它。永远不要从不可信的来源下载和执行代码,除非仔细检查代码的实际功能:

1
2
3
4
5
6
7
8
# download the code from GitHub
$url = 'https://raw.githubusercontent.com/adaptivethreat/Empire/master/data/module_source/collection/Get-ChromeDump.ps1'
$code = Invoke-RestMethod -Uri $url -UseBasicParsing
# run the code
Invoke-Expression $code

# now you have a new function called Get-ChromeDump
Get-ChromeDump

注意,当Chrome运行时,SQLLite数据库被锁定。你需要关闭Chrome才能转储它的隐私数据。

代码来自未知或不可信源的事实并不意味着代码不好。事实上,当您查看代码时,您会发现一些有趣的技术:

1
2
3
4
5
6
# download the code from GitHub
$url = 'https://raw.githubusercontent.com/adaptivethreat/Empire/master/data/module_source/collection/Get-ChromeDump.ps1'
# and copy it to the clipboard
Invoke-RestMethod -Uri $url -UseBasicParsing | Set-ClipBoard

# now paste the code into your editor of choice and inspect it!

您将看到,代码附带了访问 SQLLite 数据库所需的二进制 .net 程序集。二进制文件以 base64 编码为字符串。这是在你的电脑上恢复二进制的部分:

1
2
3
$content = [System.Convert]::FromBase64String($assembly)
$assemblyPath = "$($env:LOCALAPPDATA)\System.Data.SQLite.dll"
Add-Type -Path $assemblyPath

下面是更多与安全相关的PowerShell单行程序:https://chrishales.wordpress.com/2018/01/03/powershell-password-one-liners/