PowerShell 技能连载 - 通过校验位测试原始数据——实际案例

随着 PowerShell 进入物联网世界,有些时候需要处理二进制的传感器数据和古老的校验模型来确保数据的完整性。

这是一个如何用 PowerShell 处理传感器数据并且校验其正确性的真实案例。这里介绍的案例仅适用于特定的用例,但是这里介绍的技术原理对类似的传感器依然有效。

在这个例子中,PowerShell 收到一系列十六进制数据 ($data)。校验码是最后一个字节 (3A)。它的定义是数据报文中所有字节的和。在这个总和中用的是最低有效字节来表示校验位,而且要求比特为反码。听起来很奇怪但是有道理。通过这种方式计算出来的校验和永远只有一个字节。

以下是 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
$data = '00030028401D2E8D4022C0EE4022C0E64022C0E6418B4ACD419FE7B641A05F0E41A060D041A061C23F0A7CDA3A'

# checksum is last byte
$checksum = [Convert]::ToByte($data.Substring($data.Length-2,2), 16)

# remove checksum from data
$data = $data.Substring(0, $data.Length -2)

# sum up all bytes
$sum = $data -split '(?<=\G.{2})(?=.)' |
Foreach-Object {$c = 0}{$c+=[Convert]::ToByte($_,16)}{ $c }

# get the least significant byte
$lsb = ([Net.IPAddress]$sum).GetAddressBytes()[0]

# invert bits
$checksumReal = $lsb -bxor 0xFF

# compare
if ($checksum -ne $checksumReal)
{
throw "Checksum does not match"
}
else
{
Write-Warning "Checksum ok"
}

今日知识点:

  • [Convert]::ToByte($number, 16) 将十六进制字符串转换为数字。
  • 通过正则表达式,可以很容易地将一个字符流(例如十六进制对)转换为一对一对字符的列表,这样可以计算总和。
  • 通过将一个数字转换为 IPAddress,可以方便地操作不同的字节顺序 (LSB, MSB)。
  • -bxor 求比特位反码。

PowerShell 技能连载 - 求比特位反码

有些时候需要求一个数字的比特位反码。最常见的情况是属于某种自定义的算法或计算校验。这引出了一个通用的问题:最简单的实现方法是什么?

“求比特位反码”操作可以通过 -bnot 操作符实现,类似这样:

1
2
3
4
5
$number = 76
[Convert]::ToString($number,2)

$newnumber = -bnot $number
[Convert]::ToString($newnumber,2)

不过,结果会显示一个警告:

1001100
11111111111111111111111110110011

这个操作符总是针对 64 位的有符号数。一个更好的方法是使用 -bxor 操作符,并且根据需要颠倒的数据类型提供对应的比特位掩码。对于一个字节,比特位掩码是 0xFF,对于 Int32,比特位掩码是 0xFFFFFFFF。以下是一个求某个字节的比特位反码的示例。我们将一个字符串结果填充至 8 个字符,来确保前导零可见:

1
2
3
4
5
$number = 76
[Convert]::ToString([byte]$number,2).PadLeft(8, '0')

$newnumber = $number -bxor 0xFF
[Convert]::ToString($newnumber,2).PadLeft(8, '0')

结果是正确的:

01001100
10110011

今日知识点:

  • PowerShell 包含了许多二进制操作符,它们都以 -b... 开头。
  • 要求比特位反码,您可以使用 -bnot。如果只要反转某几位,请使用 -bxor 和自定义比特掩码。

PowerShell 技能连载 - 计算最高和最低有效字节

数字在内部是以字节的形式存储的。例如一个 Int32 数值使用 4 个字节。有些时候需要将数字分割为字节,例如要以 TODO least significiant 字节顺序计算校验和。

我们创建了一个快速的全览,您也可以将它当作一个基本的数字处理教程。它演示了数字如何对应到字节,并且如何计算最低有效字节 (LSB) 和最高有效字节 (MSB),以及其它:

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
function Show-Header([Parameter(ValueFromRemainingArguments)][string]$Text)
{
$Width=80
$padLeft = [int]($width / 2) + ($text.Length / 2)
''
$text.PadLeft($padLeft, "=").PadRight($Width, "=")
''
}


Show-Header Starting with this number:
$number = 26443007
$number

Show-Header Display the bits for this number:
$bits = [Convert]::ToString($number,2)
$bits

Show-Header Add missing leading zeroes:
# pad the string to its full bit range (32 bits)
$bitsAligned = $bits.PadLeft(32, '0')
$bitsAligned

Show-Header Display the four byte groups
$bitsAligned -split '(?<=\G.{8})(?=.)' -join '-'

Show-Header Get the bytes by conversion to IPAddress object:
$bytes = ([Net.IPAddress]$number).GetAddressBytes()
$bytes

Show-Header Display the bits for the IPAddress bytes:
$bitbytes = $bytes | ForEach-Object { [Convert]::ToString($_, 2).PadLeft(8,'0')}
$bitbytes -join '-'

Show-Header Show the Least Significant Byte LSB:
$bytes[0]

Show-Header Show LSB by turning the 8 bits to the right into a number to verify:
$bits = [Convert]::ToString($number, 2)
# take least significant bits
[Convert]::toByte($bits.Substring($bits.Length-8),2)

Show-Header Show the Most Significant Byte MSB:
$bytes[3]

如您所见,有许多方法可以实现。一个特别聪明的办法是将数字转换为一个 IPAddressIPAddress 对象有一个好用的 GetAddressBytes() 方法,可以将数字轻松地分割为字节。

结果看起来类似这样:

===========================Starting with this number:===========================

26443007

=======================Display the bits for this number:========================

1100100110111110011111111

===========================Add missing leading Zeroes:==========================

00000001100100110111110011111111

==========================Display the four byte groups==========================

00000001-10010011-01111100-11111111

================Get the bytes by conversion to IPAddress object:================

255
124
147
1

===================Display the bits for the IPAddress bytes:====================

11111111-01111100-10010011-00000001

======================Show the Least Significant Byte LSB:======================

255

======Show LSB by turning the 8 bits to the right into a number to verify:======

255

=======================Show the Most Significant Byte MSB:======================

1

PowerShell 技能连载 - 接受不带引号的参数

在前一个技能中,我们介绍了一个能够输入一个字符串并生成完美居中的标题的函数。以下是该函数和它的执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Show-Header($Text)
{
$Width=80
$padLeft = [int]($width / 2) + ($text.Length / 2)
$text.PadLeft($padLeft, "=").PadRight($Width, "=")
}


PS> Show-Header Starting
====================================Starting====================================

PS> Show-Header "Processing Input Values"
=============================Processing Input Values============================

如您所见,这个函数工作起来很完美,但是如果该字符串包含空格或其它特殊字符,用户需要用双引号将它包起来。难道不能让这个函数接受没有双引号的字符串,并将所有的输入视为一个参数吗?

这是完全可能的,如下:

  • 这个参数定义了明确的数据类型,例如 [string],那么 PowerShell 就了解了应当如何处理您的参数。
  • 只能使用单个参数,这样 PowerShell 便知道了所有的输入项都需要转换为该参数。

以下是修改后的函数:

1
2
3
4
5
6
function Show-Header([Parameter(ValueFromRemainingArguments)][string]$Text)
{
$Width=80
$padLeft = [int]($width / 2) + ($text.Length / 2)
$text.PadLeft($padLeft, "=").PadRight($Width, "=")
}

魔力是通过 ValueFromRemainingArguments 属性产生的,用户可以简单地输入文字并切不需要使用双引号:

1
2
3
4
5
PS> Show-Header Starting
====================================Starting====================================

PS> Show-Header Processing Input Values
=============================Processing Input Values============================

然欧,有一点需要注意:任何特殊字符,例如圆括号和双引号仍会干扰解析。在这些情况下,您需要像之前那样将字符串用双引号包起来。

今日知识点:

  • ValueFromRemainingArguments 属性使 PowerShell 能够将所有未绑定(额外)的参数分配到该参数上。
  • 使用清晰的数据类型做为参数,这样 PowerShell 知道如何转换含糊的数据。例如,没有 [string] 数据类型的话,如果输入值包含空格,PowerShell 将会为创建一个字符串数组。

PowerShell 技能连载 - 创建对齐的标题

在 PowerShell 中常常需要创建报表和日志。以下是一个创建漂亮的居中标题的函数。只需要将 $width 改为期望的宽度:

1
2
3
4
5
6
function Show-Header($Text)
{
$Width=80
$padLeft = [int]($width / 2) + ($text.Length / 2)
$text.PadLeft($padLeft, "=").PadRight($Width, "=")
}

以下是执行结果:

1
2
3
4
5
6
7
8
9
10
11
PS> Show-Header Starting
====================================Starting====================================

PS> Show-Header "Processing Input Values"
=============================Processing Input Values============================

PS> Show-Header "Calculating..."
=================================Calculating...=================================

PS> Show-Header "OK"
=======================================OK=======================================

额外的知识点如下:

  • 使用 PadLeft()PadRight() 来填充(扩展)两端,并且是使用您希望的字符来填充空白。

基于这个知识点,现在可以做许多相关的事情,例如创建一个固定宽度的服务器名称列表:

1
2
3
4
5
6
1,4,12,888 | ForEach-Object { 'Server_' + "$_".PadLeft(8, "0") }

Server_00000001
Server_00000004
Server_00000012
Server_00000888

PowerShell 技能连载 - 计算机名、DNS 名称,和 IP 地址

以下是一个单行的代码,返回您计算机的当前 IP 地址和它的完整 DNS 名:

1
2
3
4
5
PS> [System.Net.Dns]::GetHostEntry('')

HostName Aliases AddressList
-------- ------- -----------
DESKTOP-7AAMJLF {} {fe80::532:c4e:c409:b987%18, 192.168.2.129}

要检测一台远程的及其,只需要传入远程计算机的名字来代替空白字符串即可:

1
2
3
4
5
PS> [System.Net.Dns]::GetHostEntry('microsoft.com')

HostName Aliases AddressList
-------- ------- -----------
microsoft.com {} {40.113.200.201, 40.76.4.15, 104.215.148.63, 13.77.161.179...}

如果你只希望获取绑定的 IP 地址列表,请试试以下代码:

1
2
3
4
5
6
PS> [System.Net.Dns]::GetHostEntry('microsoft.com').AddressList.IPAddressToString
40.113.200.201
40.76.4.15
104.215.148.63
13.77.161.179
40.112.72.205

今日知识点:

  • System.Net.Dns 类包含许多有用的 DNS 相关方法。GetHostEntry() 非常有用,因为它既可以接受计算机名称也可以接受 IP 地址,并返回对应的 DNS 名和 IP 地址。
  • 传入空白字符串可以查看本机的设置。

PowerShell 技能连载 - 管理 Windows 授权密钥(第 4 部分)

Slmgr.vbs 是一个用于自动化 Windows 许可证管理的古老的 VBScript。在前一个技能中我们开始直接读取 WMI,跳过 slmgr.vbs。除了已经介绍过的 SoftwareLicensingService 类,还有一个 SoftwareLicensingProduct 拥有许多实例并且获取需要一些时间:

1
Get-WmiObject -Class SoftwareLicensingProduct | Out-GridView

描述列展示了这个类描述了 Windows 中许多不同的可用的许可证类型,只有其中的一部分有获得授权。要获得有用的信息,您需要过滤出正在使用的许可证信息。

-Filter 参数来使用服务端过滤器可以大大加速查询速度。请确保 WMI 只返回 ProductKeyId 非空的实例:

1
Get-WmiObject -Class SoftwareLicensingProduct -Filter 'ProductKeyId != NULL' | Select-Object -Property Name, Description, LicenseStatus, EvaluationEndDate, PartialProductKey, ProductKeyChannel, RemainingAppRearmCount, trustedTime, UseLicenseUrl, ValidationUrl | Out-GridView

结果显示在一个网格视图窗口中,看起来类似这样:

Name                   : Office 16, Office16ProPlusR_Grace edition
Description            : Office 16, RETAIL(Grace) channel
LicenseStatus          : 5
EvaluationEndDate      : 16010101000000.000000-000
PartialProductKey      : XXXXX
ProductKeyChannel      : Retail
RemainingAppRearmCount : 1
trustedTime            : 20190203111404.180000-000
UseLicenseUrl          : https://activation.sls.microsoft.com/SLActivateProduct/SLActivateProduct.asmx?c
                         onfigextension=o14
ValidationUrl          : https://go.microsoft.com/fwlink/?LinkID=187557

Name                   : Office 16, Office16ProPlusMSDNR_Retail edition
Description            : Office 16, RETAIL channel
LicenseStatus          : 1
EvaluationEndDate      : 16010101000000.000000-000
PartialProductKey      : XXXXX
ProductKeyChannel      : Retail
RemainingAppRearmCount : 1
trustedTime            : 20190203111404.622000-000
UseLicenseUrl          : https://activation.sls.microsoft.com/SLActivateProduct/SLActivateProduct.asmx?c
                         onfigextension=o14
ValidationUrl          : https://go.microsoft.com/fwlink/?LinkID=187557

Name                   : Windows(R), Professional edition
Description            : Windows(R) Operating System, OEM_DM channel
LicenseStatus          : 1
EvaluationEndDate      : 16010101000000.000000-000
PartialProductKey      : XXXXX
ProductKeyChannel      : OEM:DM
RemainingAppRearmCount : 1001
trustedTime            : 20190203111405.221000-000
UseLicenseUrl          : https://activation-v2.sls.microsoft.com/SLActivateProduct/SLActivateProduct.asm
                         x?configextension=DM
ValidationUrl          : https://validation-v2.sls.microsoft.com/SLWGA/slwga.asmx

如您所见,日期是以 WMI 格式返回的。用 Get-CimInstance 代替 Get-WmiObject 来获取“真实的”日期对象:

1
PS> Get-CimInstance -Class SoftwareLicensingProduct -Filter 'ProductKeyId != NULL' | Select-Object -Property Name, Description, LicenseStatus, EvaluationEndDate, PartialProductKey, ProductKeyChannel, RemainingAppRearmCount, trustedTime, UseLicenseUrl, ValidationUrl

今日知识点:

  • 通过 SoftwareLicensingProduct WMI 类可以获取到独立的微软产品(包括 Office)许可证信息。
  • 如果您希望 PowerShell 返回可阅读的 DateTime 对象,而不是难阅读的 WMI 日期格式,请使用 Get-CimInstance 来代替 Get-WmiObject

PowerShell 技能连载 - 管理 Windows 授权密钥(第 3 部分)

多数 Windows 许可证激活任务可以通过一个古老的名为 slmgr.vbs 的 VBScript 来实现自动化。不过,用这个工具来获取信息的意义不大。因为 slmgr.vbs 使用的事本地化的字符串,并且将所有数据转换为一个大字符串格式。

一个更好的方法是跳过 slmgr.vbs,而直接查询信息源。要搞清 slmgr.vbs 如何实现这些功能,您可以查看该脚本的源码。请在 PowerShell ISE 中运行这段代码:

1
2
3
4
# get the path to the script file
$Path = Get-Command slmgr.vbs | Select-Object -ExpandProperty Source
# load the script file
$file = $psise.CurrentPowerShellTab.Files.Add($Path)

让我开始将这部分功能转换为 PowerShell。例如 /dlv 参数显示许可证信息:

1
PS> slmgr.vbs /dlv

当您在 VBScript 源码中搜索 “dlv”,能快速找到所有相关的核心代码,有一个名为 GetServiceObject() 的函数,是关于简单 WMI 查询的:

1
g_objWMIService.ExecQuery("SELECT " & strQuery & " FROM " & ServiceClass)

实际上,该脚本展现了关于许可证方面的 WMI 类。从 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
44
45
46
47
48
49
50
51
52
PS> Get-WmiObject -Class SoftwareLicensingService


__GENUS : 2
__CLASS : SoftwareLicensingService
__SUPERCLASS :
__DYNASTY : SoftwareLicensingService
__RELPATH : SoftwareLicensingService.Version="10.0.17134.471"
__PROPERTY_COUNT : 38
__DERIVATION : {}
__SERVER : DESKTOP-7AAMJLF
__NAMESPACE : root\cimv2
__PATH : \\DESKTOP-7AAMJLF\root\cimv2:SoftwareLicensingService.Version="10.0.17134.471"
ClientMachineID :
DiscoveredKeyManagementServiceMachineIpAddress :
DiscoveredKeyManagementServiceMachineName :
DiscoveredKeyManagementServiceMachinePort : 0
IsKeyManagementServiceMachine : 0
KeyManagementServiceCurrentCount : 4294967295
KeyManagementServiceDnsPublishing : True
KeyManagementServiceFailedRequests : 4294967295
KeyManagementServiceHostCaching : True
KeyManagementServiceLicensedRequests : 4294967295
KeyManagementServiceListeningPort : 1688
KeyManagementServiceLookupDomain :
KeyManagementServiceLowPriority : False
KeyManagementServiceMachine :
KeyManagementServiceNonGenuineGraceRequests : 4294967295
KeyManagementServiceNotificationRequests : 4294967295
KeyManagementServiceOOBGraceRequests : 4294967295
KeyManagementServiceOOTGraceRequests : 4294967295
KeyManagementServicePort : 1688
KeyManagementServiceProductKeyID :
KeyManagementServiceTotalRequests : 4294967295
KeyManagementServiceUnlicensedRequests : 4294967295
OA2xBiosMarkerMinorVersion : 1
OA2xBiosMarkerStatus : 1
OA3xOriginalProductKey : XXXXXXXXXXXXXXXXXXXXXXXXX
OA3xOriginalProductKeyDescription : [4.0] Professional OEM
OA3xOriginalProductKeyPkPn : [TH]XXXXXXX
PolicyCacheRefreshRequired : 0
RemainingWindowsReArmCount : 1001
RequiredClientCount : 4294967295
TokenActivationAdditionalInfo :
TokenActivationCertificateThumbprint :
TokenActivationGrantNumber : 4294967295
TokenActivationILID :
TokenActivationILVID : 4294967295
Version : 10.0.17134.471
VLActivationInterval : 4294967295
VLRenewalInterval : 4294967295
PSComputerName : DESKTOP-7AAMJLF

slmgr.vbs 不同,您可以以面向对象的格式获得该信息,所以访问具体的项十分容易:

1
2
3
4
5
6
7
8
PS> $all = Get-WmiObject -Class SoftwareLicensingService

PS> $all.OA3xOriginalProductKeyDescription
[4.0] Professional OEM:DM


funcValue 1
}

还有更多内容可以发现。

今日知识点:

  • slmgr.vbs 返回的所有 Windows 许可证信息都来自 WMI。相比于 slmgr.vbs 收到的是难以阅读的文本,您可以直接查询 WMI 并跳过 slmgr.vbs
  • 一个大数据源是 SoftwareLicensingService WMI 类。

PowerShell 技能连载 - 管理 Windows 授权密钥(第 2 部分)

大多数的授权密钥管理任务都可以通过 slmgr 命令完成。

This command is actually an ancient VBScript. To read all of your license settings, for example, try this:
这个命令实际上是一个古老的 VBScript。例如要读取所有许可证设置,请使用以下代码:

1
PS> slmgr.vbs /dlv

这将打开一个独立的窗口,并且显示许多许可证激活细节。在对话框窗口中显示信息对于 PowerShell 和自动化操作帮助不大,而且通过 cscript.exe 运行该 VBScript,可能会失败:

1
2
3
4
5
6
7
8
9
PS> slmgr.vbs /dlv

PS> cscript.exe slmgr.vbs /dlv
Microsoft (R) Windows Script Host, Version 5.812
Copyright (C) Microsoft Corporation. All Rights Reserved.

Script file "C:\Users\tobwe\slmgr.vbs" not found.

PS>

您可以将缺省的 VBS 宿主改为 cscript.exe,但是一个更好的不改变全局设置的做法是:找出这个 VBScript 的完整路径,然后用绝对路径执行它。以下是获得 VBScript 路径的办法:

1
2
PS> Get-Command slmgr.vbs | Select-Object -ExpandProperty Source
C:\WINDOWS\system32\slmgr.vbs

这段代码将读取该信息输出到 PowerShell 控制台:

1
2
3
$Path = Get-Command slmgr.vbs | Select-Object -ExpandProperty Source
$Data = cscript.exe //Nologo $Path /dlv
$Data

不过,$Data 包含了难以阅读的纯文本,而且是本地化的。用这种方法获取许可证和激活信息不太优雅。

由于 slmgr.exe 可以做许多神奇的许可证任务,就如打开它的帮助时看到的那样,让我们在随后的技能中检查它的源码,并且跳过以前的 VBScript 直接获取信息。

1
PS> slmgr /?

今日知识点:

  • Windows 许可证管理通常是通过一个 VBScript slmgr.vbs 来实现。当您通过 “/?“ 调用它,将会获得该命令参数的帮助信息。
  • PowerShell 可以调用 slmgr。要接收执行结果,请通过 cscript.exe 调用它,并且向 slmgr.vbs 提供完整的(绝对)路径。
  • Get-Command 是一个查找系统文件夹(在 $env:PATH 中列出的)中的文件的完整(绝对)路径的方法。

PowerShell 技能连载 - 管理 Windows 许可证密钥(第 1 部分)

我们来看看这个单行的解析 Windows 许可证密钥的迷你系列:

1
2
3
4
PS> $key = (Get-WmiObject -Class SoftwareLicensingService).OA3xOriginalProductKey

PS> $key
KJU8F-XXUZH-UU776-IUZGT-HHGR5

今日知识点:

  • WMI 可以获取许多关于 Windows 许可证和 Windows 许可证状态的信息。
  • Windows 用于许可证管理的一个 WMI 名为 “SoftwareLicensingService”。它可以提供 Windows 许可证号码。
  • 同一个类叶包含了许多额外的信息。请看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PS> Get-WmiObject -Class SoftwareLicensingService


...
KeyManagementServiceUnlicensedRequests : 4294967295
OA2xBiosMarkerMinorVersion : 1
OA2xBiosMarkerStatus : 1
OA3xOriginalProductKey : XXXXXXXXX
OA3xOriginalProductKeyDescription : [4.0] Professional OEM:DM
OA3xOriginalProductKeyPkPn : [TH]X19-99481
PolicyCacheRefreshRequired : 0
RemainingWindowsReArmCount : 1001
RequiredClientCount : 4294967295
TokenActivationAdditionalInfo :
TokenActivationCertificateThumbprint :
TokenActivationGrantNumber : 4294967295
TokenActivationILID :
TokenActivationILVID : 4294967295
Version : 10.0.17134.471
VLActivationInterval : 4294967295
VLRenewalInterval : 4294967295
PSComputerName : DESKTOP-7AAMJLF

title: “PowerShell 技能连载 - 管理 Windows 许可证密钥”
description: PowerTip of the Day - Managing Windows License Key
categories:

  • powershell
  • tip
    tags:
  • powershell
  • tip
  • powertip
  • series

我们来看看这个单行的解析 Windows 许可证密钥的迷你系列:

1
2
3
4
PS> $key = (Get-WmiObject -Class SoftwareLicensingService).OA3xOriginalProductKey

PS> $key
KJU8F-XXUZH-UU776-IUZGT-HHGR5

今日知识点:

  • WMI 可以获取许多关于 Windows 许可证和 Windows 许可证状态的信息。
  • Windows 用于许可证管理的一个 WMI 名为 “SoftwareLicensingService”。它可以提供 Windows 许可证号码。
  • 同一个类叶包含了许多额外的信息。请看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PS> Get-WmiObject -Class SoftwareLicensingService


...
KeyManagementServiceUnlicensedRequests : 4294967295
OA2xBiosMarkerMinorVersion : 1
OA2xBiosMarkerStatus : 1
OA3xOriginalProductKey : XXXXXXXXX
OA3xOriginalProductKeyDescription : [4.0] Professional OEM:DM
OA3xOriginalProductKeyPkPn : [TH]X19-99481
PolicyCacheRefreshRequired : 0
RemainingWindowsReArmCount : 1001
RequiredClientCount : 4294967295
TokenActivationAdditionalInfo :
TokenActivationCertificateThumbprint :
TokenActivationGrantNumber : 4294967295
TokenActivationILID :
TokenActivationILVID : 4294967295
Version : 10.0.17134.471
VLActivationInterval : 4294967295
VLRenewalInterval : 4294967295
PSComputerName : DESKTOP-7AAMJLF