PowerShell 技能连载 - 将 Ticks 转换为 DateTime

偶尔,日期和时间信息以所谓的“缺陷”的格式存储为 “Ticks”。 Ticks 是自 01/01/1601 以来,100 纳秒的单位数。Active Directory 在内部使用此格式,但您也可以在其他地方找到它。 以下是以 “Ticks” 为单位的 Windows 安装时间的示例:

1
2
3
4
$values = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$installDateTicks = $values.InstallTime

$installDateTicks

结果是(非常)大的 64 比特数字:

132457820129777032

要将 Ticks 转换为 DateTime,请使用 [DateTimeOffset]

1
2
3
4
5
$values = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$installDateTicks = $values.InstallTime

$installDate = [DateTimeOffset]::FromFileTime($installDateTicks)
$installDate.DateTime
评论

PowerShell 技能连载 - 将 UNIX 时间转为 DateTime

“UNIX时间”计算自 01/01/1970 以来经过的秒数。

例如,在 Windows 中,您可以从 Windows 注册表中读取安装日期,返回的值为 “Unix时间”:

1
2
3
4
$values = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$installDateUnix = $values.InstallDate

$installDateUnix

结果是类似这样的大数字:

1601308412

To convert “Unix time” to a real DateTime value, .NET Framework provides a type called [DateTimeOffset]:
要将“UNIX时间”转换为真实的 DateTime 值,请使用 .NET Framework 提供的 [DateTimeOffset] 类:

1
2
3
4
$values = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$installDateUnix = $values.InstallDate

[DateTimeOffset]::FromUnixTimeSeconds($installDateUnix)

现在您能得到不同的日期和时间表示:

DateTime      : 28.09.2020 15:53:32
UtcDateTime   : 28.09.2020 15:53:32
LocalDateTime : 28.09.2020 17:53:32
Date          : 28.09.2020 00:00:00
Day           : 28
DayOfWeek     : Monday
DayOfYear     : 272
Hour          : 15
Millisecond   : 0
Minute        : 53
Month         : 9
Offset        : 00:00:00
Second        : 32
Ticks         : 637369052120000000
UtcTicks      : 637369052120000000
TimeOfDay     : 15:53:32
Year          : 2020

要获取本地格式的安装时间,您可以在一行代码中写完它:

1
2
3
PS> [DateTimeOffset]::FromUnixTimeSeconds((Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').InstallDate).DateTime

Moday, September 28, 2020 15:53:32
评论

PowerShell 技能连载 - 创建动态参数

动态参数是一种特殊的参数,可以根据运行时条件显示或隐藏。 您的 PowerShell 函数可以例如具有一个参数,并基于用户选择的操作,将显示其他参数。或者,只有在用户具有管理员权限时才能显示参数。

不幸的是,组合动态参数并不是一件轻松的事。借助称为 “dynpar” 的模块,使用动态参数变得同样简单,就像使用“普通”静态参数一样简单,然后您可以简单地使用名为 [Dynamic()] 的新属性指定动态参数,该属性告诉 PowerShell 需要满足以哪些条件以显示参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
param
(
# regular static parameter
[string]
$Normal,

# show -Lunch only at 11 a.m. or later
[Dynamic({(Get-Date).Hour -ge 11})]
[switch]
$Lunch,

# show -Mount only when -Path refers to a local path (and not a UNC path)
[string]
$Path,

[Dynamic({$PSBoundParameters['Path'] -match '^[a-z]:'})]
[switch]
$Mount
)

您可以在此处找到一份详细的操作指南:https://github.com/tobiaspsp/modules.dynpar

评论

PowerShell 技能连载 - Turning Text into Individual Lines (Part 3)

In the previous tip we split a chunk of multi-line text into individual lines and removed any empty lines.

However, when a line isn’t really empty but contains whitespace (spaces or tabulators), it is still returned:

# $data is a single string and contains blank lines
$data = @'

Server1


Server2
Cluster4


'@

$data = $data.Trim()

# split in single lines and remove empty lines
$regex = '[\r\n]{1,}'
$array = $data -split $regex

$array.Count

$c = 0
Foreach ($_ in $array)
{
    '{0:d2} {1}' -f $c, $_
    $c++
}

Here, we have added a few spaces to the line right above “Server2” (which you obviously can’t see in the listing). This is the result:

00 Server1
01
02 Server2
03 Cluster4

Since we are splitting at any number of CR and LF characters, a space would break that pattern.

Rather than turning the regular expression in an even more complex beast, for such things you may want to append a simple Where-Object to do the fine polishing:

# $data is a single string and contains blank lines
$data = @'

Server1


Server2
Cluster4


'@

$data = $data.Trim()

# split in single lines and remove empty lines
$regex = '[\r\n]{1,}'
$array = $data -split $regex |
  Where-Object { [string]::IsNullOrWhiteSpace($_) -eq $false }

$array.Count

$c = 0
Foreach ($_ in $array)
{
    '{0:d2} {1}' -f $c, $_
    $c++
}

[string]::IsNullOrEmpty() identifies the situation we are after, so lines that qualify are removed by Where-Object. The result is what is needed:

00 Server1
01 Server2
02 Cluster4
评论

PowerShell 技能连载 - Turning Text into Individual Lines (Part 2)

Let’s assume your script gets text input data, and you need to split the text into individual lines. In the previous tip we suggested a number of regular expressions to do the job. But what if the input text contains blank lines?

# $data is a single string and contains blank lines
$data = @'

Server1


Server2
Cluster4


'@

# split in single lines and remove empty lines
$regex = '[\r\n]{1,}'

As you see, the regular expression we used automatically takes care of blank lines in the middle of the text, however blank lines at the beginning or end of the text stay put.

00
01 Server1
02 Server2
03 Cluster4
04

That’s because we are splitting at any number of new lines, so we are also splitting right at the beginning and end of the text. We are actually producing these two remaining blank lines ourselves.

To get rid of these, we must ensure that no line feed characters are present at the beginning and end of the text. That’s something Trim() can do:

# $data is a single string and contains blank lines
$data = @'

Server1


Server2
Cluster4


'@

$data = $data.Trim()

# split in single lines and remove empty lines
$regex = '[\r\n]{1,}'
$array = $data -split $regex

$array.Count

$c = 0
Foreach ($_ in $array)
{
    '{0:d2} {1}' -f $c, $_
    $c++
}



00 Server1
01 Server2
02 Cluster4
评论

PowerShell 技能连载 - Turning Text into Individual Lines (Part 1)

Occasionally, you need to process multi-line text line by line. Here is an example of a multi-line string to start with:

# working with 1-dimensional input

# $data is a single string
$data = @'
Server1
Server2
Cluster4
'@

$data.GetType().FullName
$data.Count

An efficient way to split the text into individual lines is using the -split operator with a regular expression that can deal with the variety of platform-dependent line terminators:

# split the string in individual lines
# $array is an array with individual lines now

$regex = '[\r\n]{1,}'
$array = $data -split $regex

$array.GetType().FullName
$array.Count

$array

Here are a few alternatives for the regular expression found in $regex that you encounter in scripts:

Regular expression for splittingRemarks
/rResembles “Carriage Return” (ASCII 13). If the OS does not use this for new lines, split fails. If OS uses this plus “Line Feed” (ASCII 10), remaining invisible line feed characters damage the strings.
/nSame as above, just the opposite
[\r\n]+Same as in the example code above. PowerShell splits at both characters provided there are one or more. This way, CR, LF, or CRLF, LFCR, are all removed while splitting. However, multiple consecutive new lines will all be removed, too: CRCRCR or CRLFCRLF
(\r\n|\r|\n)This will correctly split a single line break, regardless of which characters a particular OS uses. It leaves consecutive blank lines intact.
If you read text from a text file, Get-Content automatically splits the text into lines. To read the entire text content as a single string, you’d need to add the -Raw parameter.

评论

PowerShell 技能连载 - Taking Screenshot

With types found in System.Windows.Forms, PowerShell can easily capture your screen and save the screenshot to a file. The code below captures your entire virtual screen, saves the screenshot to file, then opens the bitmap file in the associated program (if any):

$Path = "$Env:temp\screenshot.bmp"
Add-Type -AssemblyName System.Windows.Forms

$screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$width = $screen.Width
$height = $screen.Height
$left = $screen.Left
$top = $screen.Top
$bitmap = [System.Drawing.Bitmap]::new($width, $height)
$MyDrawing = [System.Drawing.Graphics]::FromImage($bitmap)
$MyDrawing.CopyFromScreen($left, $top, 0, 0, $bitmap.Size)

$bitmap.Save($Path)
Start-Process -FilePath $Path
评论

PowerShell 技能连载 - Disabling Local “Guest” Account

Windows comes with the built-in account called “Guest”. Since this account is seldomly used, you may want to disable it. Else, its well-known name could serve as a vector for attackers.

Since the account name is localized and can slightly vary from culture to culture, to identify the account use its SID:

PS> Get-Localuser | Where-Object Sid -like 'S-1-5-*-501'

Name  Enabled Description
----  ------- -----------
Guest False   Built-in account for guest access to the computer/domain

If the account isn’t already disabled (see “Enabled” property), use an elevated PowerShell and the Disable-LocalUser cmdlet to disable the account.

ReTweet this Tip!

评论

PowerShell 技能连载 - 重命名本地管理员账户

出于安全原因,您可能需要考虑重命名内置的本地管理员帐户。这个账户权限很大,它的名字很容易猜到,所以它是攻击者的常用载体。在重命名此帐户之前,请确保您了解后果:

  • 该账户仍能继续工作,但您现在需要使用新分配的名称来登录该帐户。确保没有使用旧的默认名称的自动登录过程
  • 重命名帐户不会更改其 SID,因此老练的攻击者仍然可以使用其众所周知的 SID 锁定此帐户

要重命名内置 Administrator 帐户(或任何其他本地帐户),请以管理员权限启动 PowerShell,然后运行以下代码:

1
PS> Rename-LocalUser -Name "Administrator" -NewName "TobiasA"

要使用该帐户登录,请使用新分配的名称。通过使用账户的众所周知的 SID,即使您不知道其名称,您仍然可以识别重命名的帐户:

1
2
3
4
5
PS> Get-Localuser | Where-Object Sid -like 'S-1-5-*-500'

Name Enabled Description
---- ------- -----------
TobiasA False Built-in account for administering the computer/domain
评论

PowerShell 技能连载 - 识别本地管理员组

内置管理员组的任何成员都可以访问广泛的权限,因此检查该组的成员可以成为安全审核的一部分。虽然 “Administrators” 组默认存在,但其名称可能因文化而异,因为它是本地化的。例如,在德国系统中,该组称为 “Administratoren”。

要访问用户组,而无论文化和命名如何变化,请使用其 SID,它始终为 “S-1-5-32-544”:

1
2
3
4
5
PS> Get-LocalGroup -SID S-1-5-32-544

Name Description
---- -----------
Administrators Administrators have complete and unrestricted access to the...

同样,要转储具有管理员权限的用户和组列表,请使用 SID 而不是组名:

1
PS> Get-LocalGroupMember -SID S-1-5-32-544
评论