PowerShell 技能连载 - 探索 PowerShell 模块

大多数 PowerShell 的命令都存在于模块中,通过增加新的模块,就可以将新的命令添加到 PowerShell 环境中。要查找一个命令是否位于某个模块中,请使用 Get-Command 命令。下一行代码返回发布 Get-Service 命令的模块:

1
PS C:\> Get-Command -Name Get-Service | Select-Object -ExpandProperty Module

如果 Module 属性是空值,那么改命令不是通过一个模块发布的。对于所有非 PowerShell 命令,例如应用程序,都是这个情况:

1
PS C:\> Get-Command -Name notepad | Select-Object -ExpandProperty Module

下一步,让我们列出您系统中所有可用的模块:

powershell
PS> Get-Module -ListAvailable | Select-Object -Property Name, Path

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

如果更深入地查看这些结果,您会注意到 `Get-Module` 列出多于一个文件夹。PowerShell 指定的缺省模块文件夹是以分号分隔的列表形式存储在 `$env:PSModulePath` 环境变量中,类似应用程序的 `$env:Path`:

PS> $env:PSModulePath
C:\Users\tobia\OneDrive\Dokumente\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\Modules;C:
\Windows\system32\WindowsPowerShell\v1.0\Modules

PS> $env:PSModulePath -split ';'
C:\Users\tobia\OneDrive\Dokumente\WindowsPowerShell\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\Windows\system32\WindowsPowerShell\v1.0\Modules

如果您希望探索一个指定模块的内容,请查看 `Path` 属性:它通常指向一个 .psd1 文件,其中包含模块的元数据。它是指定模块版本和版权信息的地方,并且通常它的 `RootModule` 入口指定了模块的代码。如果这是一个使用 PowerShell 代码构建的模块,那么它是一个 .psm1 文件,否则是一个二进制的 DLL 文件。

要检查模块内容,请在 Windows 资源管理器中打开它的父文件夹。例如,下面一行代码在 Windows 资源管理器中打开 "PrintManagement" 模块(假设它存在于您的机器中):

```powershell
PS> Get-Module -Name PrintManagement -ListAvailable | Select-Object -ExpandProperty Path | Split-Path
C:\Windows\system32\WindowsPowerShell\v1.0\Modules\PrintManagement

PS> explorer (Get-Module -Name PrintManagement -ListAvailable | Select-Object -ExpandProperty Path | Split-Path)

这个快速的演练解释了为什么 PowerShell 没有固定的命令集,以及为什么给定命令在一个系统上可用并且在另一个系统上不可用。新模块可以由操作系统(例如 Windows 10 附带的模块多于 Windows 7)、已安装的软件(例如 SQLServer)、已激活的角色(例如域控制器)引入,并且模块也可以手动安装。

例如这行代码从公开的 PowerShell Gallery 中安装一个免费的模块,并且添加了用于创建各种二维码的新命令:

1
PS C:\> Install-Module -Name QRCodeGenerator -Scope CurrentUser -Force

一旦模块安装完毕,您会得到类似这样的命令,用来创建 Twitter 用户数据文件的二维码:

1
PS C:\> New-QRCodeTwitter -ProfileName tobiaspsp -Show

要查看某个模块中所有命令,请试试这行代码:

1
2
3
4
5
6
7
8
PS C:\> Get-Command -Module QRCodeGenerator

CommandType Name Version Source
----------- ---- ------- ------
Function New-QRCodeGeolocation 1.2 QRCodeGenerator
Function New-QRCodeTwitter 1.2 QRCodeGenerator
Function New-QRCodeVCard 1.2 QRCodeGenerator
Function New-QRCodeWifiAccess 1.2 QRCodeGenerator
评论

PowerShell 技能连载 - WMI Explorer

WMI (Windows Management Instrumentation) is a great information source: you can find almost any information about your computer somewhere. The hard part isn’t the WMI query itself. The hard part is finding out the appropriate WMI class names and properties:

To get information about your BIOS, for example, run this:

PS> Get-CimInstance -ClassName Win32_BIOS


SMBIOSBIOSVersion : 1.0.9
Manufacturer      : Dell Inc.
Name              : 1.0.9
SerialNumber      : 4ZKM0Z2
Version           : DELL   - 20170001

Getting other information is just a question of replacing the class name you are querying, so to get information about your operating system instead, for example, run this:

PS> Get-CimInstance -ClassName Win32_OperatingSystem


SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 18362
RegisteredUser  : tobias.weltner@email.de
SerialNumber    : 00330-50000-00000-AAOEM
Version         : 10.0.18362

To find just about any information and discover the appropriate WMI class names to use, we have created a smart “WMI Explorer” which really is just a simple function. It requires Windows PowerShell (PowerShell 3-5):

function Find-WmiClass
{
  # show all properties, not just 4
  $oldLimit = $FormatEnumerationLimit
  $global:FormatEnumerationLimit = -1
  # list all WMI classes...
  Get-WmiObject -Class * -List |
  # ...with at least 4 properties
  Where-Object { $_.Properties.Count -gt 4 } |
  # let the user select one
  Out-GridView -Title 'Select a class that seems interesting' -OutputMode Single |
  ForEach-Object {
    # query the selected class
    $Name = $_.name
    $props = Get-WmiObject -Class $Name |
    # take the first instance
    Select-Object -Property * -First 1 |
    ForEach-Object {
      # turn the object into a hash table, and exclude empty properties
      $_ | & {
              $_.PSObject.Properties |
        Sort-Object -Property Name |
        Where-Object { $_.Value -ne $null } |
        ForEach-Object {
            $hashtable = [Ordered]@{}} { $hashtable[$_.Name] = $_.Value } { $hashtable }
        } |
        # show the properties and let the user select
        Out-GridView -Title "$name : Select all properties you need (hold CTRL)" -PassThru |
        ForEach-Object {
          # return the selected property names
          $_.Name
        }
    }
    # take all selected properties
    $prop = $props -join ', '
    # create the command for it:
    $a = "Get-CimInstance -Class $Name | Select-Object -Property $prop"
    # place it into the clipboard
    $a | Set-Clipboard
    Write-Warning "Command is also available from the clipboard"
    $a
  }
  # reset format limit
  $global:FormatEnumerationLimit = $oldLimit
}

Here is how it works:

  • Run the code above which adds a new command called Find-WmiClass
  • Run Find-WmiClass to run the function
  • A grid view window opens and displays all WMI classes available in the standard namespace root\cimv2 (if you want to search a different namespace, adjust the code and add the -Namespace parameter to Get-WmiObject calls)
  • Now enter something that you are looking for into the top text field in the grid view window which acts like a filter. If you enter UserName for example, the grid view limits the list to all classes that have “UserName” anywhere in its name or in the name of any of their properties. You now have just a few classes to select from.
  • Select the class you want to investigate, for example “Win32_ComputerSystem”, and click OK in the bottom right corner of the grid.
  • Now the selected class is queried, and the first instance of it displays in another grid view. Each property is displayed on its own line, so you can again filter the display. Hold CTRL to select all properties you want to keep. Then click OK again.
  • The command is created for you and displays in the console. It is also placed into the clipboard, so to test drive it, press CTRL+V, then ENTER.

Thanks to Find-WmiClass, exploring the WMI and finding useful WMI classes and properties has just become very easy.


Twitter This Tip!ReTweet this Tip!

评论

PowerShell 技能连载 - 将对象转换为哈希表

我们经常需要检视一个对象,例如一个进程或是一个 Active Directory 用户。当您在一个网格视图窗口,例如 Out-GridView 中显示一个对象时,整个对象显示为一个长行。

一个好得多的方法是将一个对象转换为哈希表。通过这种方式,每个属性都在独立的一行中显示,这样您就可以用网格视图窗口顶部的文本过滤器搜索每个属性。另外,您可以完全控制转换的过程,这样您可以对对象的属性排序,并且确保它们按字母顺序排序,甚至排除空属性。

以下是一个将对象转换为哈希表并且可以根据需要排除空属性的函数:

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
function Convert-ObjectToHashtable
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
$object,

[Switch]
$ExcludeEmpty
)

process
{
$object.PSObject.Properties |
# sort property names
Sort-Object -Property Name |
# exclude empty properties if requested
Where-Object { $ExcludeEmpty.IsPresent -eq $false -or $_.Value -ne $null } |
ForEach-Object {
$hashtable = [Ordered]@{}} {
$hashtable[$_.Name] = $_.Value
} {
$hashtable
}
}
}

让我们来看看一个对象,例如当前进程,缺省情况下在 Out-GridView 是如何显示的:

1
$process = Get-Process -Id $pid | Out-GridView

和以下这个作对比:

1
$process = Get-Process -Id $pid | Convert-ObjectToHashtable -ExcludeEmpty | Out-GridView

现在分析起来好多了。

评论

PowerShell 技能连载 - 对象的魔法(第 4 部分)

将一个对象转换为一个哈希表如何?通过这种方式,当将对象输出到一个网格视图窗口时,可以每行显示一个对象属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
# get any object
$object = Get-Process -Id $pid

# try and access the PSObject
$hash = $object.PSObject.Properties.Where{$null -ne $_.Value}.Name |
Sort-Object |
ForEach-Object { $hash = [Ordered]@{} } { $hash[$_] = $object.$_ } { $hash }

# output regularly
$object | Out-GridView -Title Regular

# output as a hash table, only non-empty properties, sorted
$hash | Out-GridView -Title Hash
评论

PowerShell 技能连载 - 对象的魔法(第 3 部分)

假设您希望隐藏对象所有没有值(为空)的属性。以下是一个简单的实现:

1
2
3
4
5
6
# get any object
$object = Get-Process -Id $pid

# try and access the PSObject
$propNames = $object.PSObject.Properties.Where{$null -ne $_.Value}.Name
$object | Select-Object -Property $propNames

这段代码将只输出包含值的属性。您甚至可以对属性排序:

1
2
3
4
5
6
# get any object
$object = Get-Process -Id $pid

# try and access the PSObject
$propNames = $object.PSObject.Properties.Where{$null -ne $_.Value}.Name | Sort-Object
$object | Select-Object -Property $propNames
评论

PowerShell 技能连载 - 对象的魔法(第 2 部分)

Via the secret “PSObject” property, you can get detailed information about object members. For example, if you’d like to know which object properties can actually be changed, try this:
通过隐藏的 “PSObject“ 属性,您可以获取对象成员的详细信息。例如,如果您希望知道哪个属性可以被改变,请试试这段代码:

1
2
3
4
5
# get any object
$object = Get-Process -Id $pid

# try and access the PSObject
$object.PSObject.Properties.Where{$_.IsSettable}.Name

结果是进程对象的可以被赋值的属性列表:

MaxWorkingSet
MinWorkingSet
PriorityBoostEnabled
PriorityClass
ProcessorAffinity
StartInfo
SynchronizingObject
EnableRaisingEvents
Site

类似地,您可以轻松地找出所有当前没有值(为空)的属性:

1
2
3
4
5
# get any object
$object = Get-Process -Id $pid

# try and access the PSObject
$object.PSObject.Properties.Where{$null -eq $_.Value}.Name
评论

PowerShell 技能连载 - 对象的魔法(第 1 部分)

在 PowerShell 中,大多数据是以 PSObject 来表示的,它是一个由 PowerShell 添加的特殊的对象“包装器”。要获取这个特殊的包装器,可以通过对象名为 “PSObject“ 的隐藏属性。让我们来看看:

1
2
3
4
5
6
7
8
9
10
11
# get any object
$object = Get-Process -Id $pid

# try and access the PSObject
$object.PSObject

# get another object
$object = "Hello"

# try again
$object.PSObject

如您所见,该 “PSObject“ 基本上是一个对象的描述。并且它包含了许多有用的信息。以下是一部分:

1
2
3
4
5
6
7
8
9
10
# get any object
$object = Get-Process -Id $pid

# try and access the PSObject
$object.PSObject

# find useful information
$object.PSObject.TypeNames | Out-GridView -Title Type
$object.PSObject.Properties | Out-GridView -Title Properties
$object.PSObject.Methods | Out-GridView -Title Methods
评论

PowerShell 技能连载 - 加密文本(第 2 部分)

这是我们的加密解密系列的第二部分。在第一部分中您学到了如何在一台计算机中安全地加密文本。现在我们来关注解密部分。

To successfully decrypt text, you must specify the same encoding that was used during encryption. Based on your encryption parameters, you must specify the same password, and based on the -Scope settings, decryption will work only for you and/or only on the same machine where you encrypted the text.

要正确地解密文本,必须指定加密时使用的相同编码。基于您的加密参数,您必须指定相同的密码。并且基于 -Scope 设置,解密将只能针对您和/或仅在您加密文本的同一机器上工作。

以下是 Unprotect-Text 函数。我们也从上一个技能中复制了 Protect-Text 函数,这样您可以方便地使用这两种功能:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
function Protect-Text
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[String]
$SecretText,

[string]
$Password='',

[string]
[ValidateSet('CurrentUser','LocalMachine')]
$scope = 'CurrentUser',

[string]
[ValidateSet('UTF7','UTF8','UTF32','Unicode','ASCII','Default')]
$Encoding = 'Default',

[Switch]
$ReturnByteArray

)
begin
{
Add-Type -AssemblyName System.Security
if ([string]::IsNullOrEmpty($Password))
{
$optionalEntropy = $null
}
else
{
$optionalEntropy = [System.Text.Encoding]::$Encoding.GetBytes($Password)
}
}
process
{
try
{
$userData = [System.Text.Encoding]::$Encoding.GetBytes($SecretText)
$bytes = [System.Security.Cryptography.ProtectedData]::Protect($userData, $optionalEntropy, $scope)
if ($ReturnByteArray)
{
$bytes
}
else
{
[Convert]::ToBase64String($bytes)
}
}
catch
{
throw "Protect-Text: Unable to protect text. $_"
}
}
}



function Unprotect-Text
{
[CmdletBinding(DefaultParameterSetName='Byte')]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="Text", Position=0)]
[string]
$EncryptedString,

[Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="Byte", Position=0)]
[Byte[]]
$EncryptedBytes,

[string]
$Password='',

[string]
[ValidateSet('CurrentUser','LocalMachine')]
$scope = 'CurrentUser',

[string]
[ValidateSet('UTF7','UTF8','UTF32','Unicode','ASCII','Default')]
$Encoding = 'Default'

)
begin
{
Add-Type -AssemblyName System.Security

if ([string]::IsNullOrEmpty($Password))
{
$optionalEntropy = $null
}
else
{
$optionalEntropy = [System.Text.Encoding]::$Encoding.GetBytes($Password)
}
}
process
{
try
{
if ($PSCmdlet.ParameterSetName -eq 'Text')
{
$inBytes = [Convert]::FromBase64String($EncryptedString)
}
else
{
$inBytes = $EncryptedBytes
}
$bytes = [System.Security.Cryptography.ProtectedData]::Unprotect($inBytes, $optionalEntropy, $scope)
[System.Text.Encoding]::$Encoding.GetString($bytes)
}
catch
{
throw "Unprotect-Text: Unable to unprotect your text. Check optional password, and make sure you are using the same encoding that was used during protection."
}
}
}

以下是如何使用它的示例:

1
2
3
4
5
$text = "This is my secret"

$a = Protect-Text -SecretText $text -scope CurrentUser -Password zumsel

Unprotect-Text -EncryptedString $a -scope CurrentUser -Password zumsel

Protect-Text 创建了一个 Base64 编码的字符串,它可以用 Unprotect-Text 函数来解密。如果您不希望额外的密码,那么只能使用基于 -Scope 的缺省的保护。

要节省空间,您可以使用字节数组来代替 Base64 编码的字符串:

1
2
$b = Protect-Text -SecretText $text -scope CurrentUser -ReturnByteArray
Unprotect-Text -EncryptedBytes $b -scope CurrentUser
评论

PowerShell 技能连载 - 加密文本(第 1 部分)

让我们来看看一种在计算机上加密文本的安全方法。以下的 Protect-Text 函数接受任意文本,并自动加密,不需要密码。它不使用密码,而是使用您的用户帐户和机器,或者只使用您的机器作为密钥。

如果使用 -Scope LocalMachine,任何使用该机器的人都可以解密该文本,但如果该文本泄露给其他人,它无法在其它机器上解密。如果您使用 -Scope CurrentUser,只有加密者可以解密,并且只能在加密的机器上解密。这个加密方案很适合保存您的私人密码。

此外,为了提高安全性,可以在顶部添加(可选)密码。当指定了密码时,将应用上述相同的限制,但此外还需要知道密码才能解密文本。

请注意您也可以控制文本解码。请确保加密和解密使用相同的编码。

以下是加密函数:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
function Protect-Text
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[String]
$SecretText,

[string]
$Password='',

[string]
[ValidateSet('CurrentUser','LocalMachine')]
$scope = 'CurrentUser',

[string]
[ValidateSet('UTF7','UTF8','UTF32','Unicode','ASCII','Default')]
$Encoding = 'Default',

[Switch]
$ReturnByteArray

)
begin
{
Add-Type -AssemblyName System.Security
if ([string]::IsNullOrEmpty($Password))
{
$optionalEntropy = $null
}
else
{
$optionalEntropy = [System.Text.Encoding]::$Encoding.GetBytes($Password)
}
}
process
{
try
{
$userData = [System.Text.Encoding]::$Encoding.GetBytes($SecretText)
$bytes = [System.Security.Cryptography.ProtectedData]::Protect($userData, $optionalEntropy, $scope)
if ($ReturnByteArray)
{
$bytes
}
else
{
[Convert]::ToBase64String($bytes)
}
}
catch
{
throw "Protect-Text: Unable to protect text. $_"
}
}

结果类似这样:

1
2
3
4
PS> Protect-Text -SecretText 'I am encrypted' -scope LocalMachine
AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAF8Hpm9A5A0upZysoxLlvgwQAAAACAAAAAAAQZgAAAAEAACAAAACbOsUoDuZJXNkWIzfAABxktVg+Txn7A8Rz
SCvFP7I9YQAAAAAOgAAAAAIAACAAAABz7G7Tpuoje9meLOuugzx1WSoOUfaBtGPM/XZHytjC8hAAAAApt/TDhJ9EqeWEPLIDkd4bQAAAAAN0Q503Pa7X
MxIMOnaO7qd3LKXJa4qhht+jc+Z0HaaV5/md83ipP1vefYAAUXdj8qv4eREeCBSGMqvKjbaOsOg=

如果您想了解如何将 Base64 编码的文本转换回原始文本,请看我们的下一个技能!

评论

PowerShell 技能连载 - 创建 NT4 密码哈希

在内部,ActiveDirectory 将所有密码存储为所谓的 NTLM 哈希。有许多安全分析工具可以读取和转储这些哈希。

幸运的是,没有可行的方法来解密这些散列并检索原始密码,您可以使用(已知)密码并将其转换为 NTLM 哈希。这是字典攻击的基本过程:他们获取到很长的“已知密码”列表,将它们转换为 NTLM 散列,当它们与实际的 AD 帐户散列匹配时,就算破解出了密码。

This way, your security department could take password blacklist with insecure passwords such as “P@ssw0rd”, turn them into NTLM hashes, and compare them to the password hashes of your Active Directory to identify accounts that need a password change.

这样,您的安全部门就可以将密码黑名单中的不安全密码(如“P@ssw0rd”)转换为NTLM散列,并将它们与Active Directory的密码散列进行比较,以识别需要更改密码的帐户。

Here is PowerShell code to turn a plain text into an NTLM hash:

function ConvertTo-NTLMPasswordHash
{
  #Work based on code found here: https://www.myotherpcisacloud.com/post/getmd4hash
  #Original Author: Ryan Ries, 2014
  param(
    [Parameter(Mandatory=$true)][string]$password
  )

  Function Get-MD4Hash
  {
    Param ([Parameter(Mandatory=$True, ValueFromPipeline=$False)]
    [Byte[]]$DataToHash)

      Set-StrictMode -Version Latest
      Add-Type -TypeDefinition @'
        using System;
        using System.Text;
        using System.Runtime.InteropServices;
        public class BCrypt
        {
            [DllImport("bcrypt.dll", CharSet = CharSet.Auto)]
            public static extern NTStatus BCryptOpenAlgorithmProvider(
                [Out] out IntPtr phAlgorithm,
                [In] string pszAlgId,
                [In, Optional] string pszImplementation,
                [In] UInt32 dwFlags);

            [DllImport("bcrypt.dll")]
            public static extern NTStatus BCryptCloseAlgorithmProvider(
                [In, Out] IntPtr hAlgorithm,
                [In] UInt32 dwFlags);

            [DllImport("bcrypt.dll", CharSet = CharSet.Auto)]
            public static extern NTStatus BCryptCreateHash(
                [In, Out] IntPtr hAlgorithm,
                [Out] out IntPtr phHash,
                [Out] IntPtr pbHashObject,
                [In, Optional] UInt32 cbHashObject,
                [In, Optional] IntPtr pbSecret,
                [In] UInt32 cbSecret,
                [In] UInt32 dwFlags);

            [DllImport("bcrypt.dll")]
            public static extern NTStatus BCryptDestroyHash(
                [In, Out] IntPtr hHash);

            [DllImport("bcrypt.dll")]
            public static extern NTStatus BCryptHashData(
                [In, Out] IntPtr hHash,
                [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
                [In] int cbInput,
                [In] UInt32 dwFlags);

            [DllImport("bcrypt.dll")]
            public static extern NTStatus BCryptFinishHash(
                [In, Out] IntPtr hHash,
                [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
                [In] int cbInput,
                [In] UInt32 dwFlags);

            [Flags]
            public enum AlgOpsFlags : uint
            {
                BCRYPT_PROV_DISPATCH = 0x00000001,
                BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008,
                BCRYPT_HASH_REUSABLE_FLAG = 0x00000020
            }

            // This is a gigantic enum and I don't want to copy all of it into this Powershell script.
            // Basically anything other than zero means something went wrong.
            public enum NTStatus : uint
            {
                STATUS_SUCCESS = 0x00000000
            }
        }
'@

      [Byte[]]$HashBytes   = New-Object Byte[] 16
      [IntPtr]$PHAlgorithm = [IntPtr]::Zero
      [IntPtr]$PHHash      = [IntPtr]::Zero
      $NTStatus = [BCrypt]::BCryptOpenAlgorithmProvider([Ref] $PHAlgorithm, 'MD4', $Null, 0)
      If ($NTStatus -NE 0)
      {
        Write-Error "BCryptOpenAlgorithmProvider failed with NTSTATUS $NTStatus"
        If ($PHAlgorithm -NE [IntPtr]::Zero)
        {
          $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
        }
        Return
      }
      $NTStatus = [BCrypt]::BCryptCreateHash($PHAlgorithm, [Ref] $PHHash, [IntPtr]::Zero, 0, [IntPtr]::Zero, 0, 0)
      If ($NTStatus -ne 0)
      {
        Write-Error "BCryptCreateHash failed with NTSTATUS $NTStatus"
        If ($PHHash -ne [IntPtr]::Zero)
        {
          $NTStatus = [BCrypt]::BCryptDestroyHash($PHHash)
        }
        If ($PHAlgorithm -ne [IntPtr]::Zero)
        {
          $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
        }
        Return
      }

      $NTStatus = [BCrypt]::BCryptHashData($PHHash, $DataToHash, $DataToHash.Length, 0)
      $NTStatus = [BCrypt]::BCryptFinishHash($PHHash, $HashBytes, $HashBytes.Length, 0)

      If ($PHHash -NE [IntPtr]::Zero)
      {
        $NTStatus = [BCrypt]::BCryptDestroyHash($PHHash)
      }
      If ($PHAlgorithm -NE [IntPtr]::Zero)
      {
        $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
      }

      $HashString = New-Object System.Text.StringBuilder
      Foreach ($Byte In $HashBytes)
      {
        $null = $HashString.Append($Byte.ToString("x2"))
      }
      $HashString.ToString()

  }
  Get-MD4Hash -DataToHash ([System.Text.Encoding]::Unicode.getBytes($password))
}

Twitter This Tip!ReTweet this Tip!

评论