PowerShell: за гранью. Часть третья
Каждый по-своему распоряжается свободным временем, мне, например, нравится реализовывать аналоги различных утилит на скриптовых языках и, нужно отметить, занятие это довольно забавное, так как в процессе работы над некоторыми из них невольно закрадывается мысль, что последние, возможно, всего лишь экзерсисы их авторов, позиционируемые как крайне необходимый в быту тулкит. Какая от всего этого практическая ценность? Ничего кроме независимости от чужих ошибок, а также возможности дополнить\урезать уже реализованный в оригинальной утилите функционал, на ум не приходит. Хотя, если брать в расчет отсутсвие исходных кодов оригиналов, ценным может статься сам опыт.Среди моих коллег и знакомых, как ни странно, востребованными являются отнюдь не утилиты SysInternals, а собрание сочинений Нир Софера. Если честно, то из всего написанного последним ничем пользоваться не доводилось, более того, знакомство с его творчеством произошло совсем недавно и, следует заметить, оно не произвело эффекта, так как в некоторых случаях это не такие уж необходиммые в будничной практике вещи (о них-то и пойдет речь). Взять, к примеру, BatteryInfoView, какой смысл в этой утилите? Всего лишь GetSystemPowerStatus в упаковочном пенопласте и пометкой «не кантовать», а оно нам надо? Если голова и руки на том месте, где им и подобает быть согласно анатомии, можно нарисовать тот же BatteryInfoView всего двумя штрихами. PS C:\> Add-Type -AssemblyName System.Windows.Forms PS C:\> [Windows.Forms.PowerStatus].GetConstructor ([Reflection.BindingFlags]36, $null, [Type[]]@(), $null).Invoke ($null)
PowerLineStatus: Offline BatteryChargeStatus: 0 BatteryFullLifetime: -1 BatteryLifePercent: 0,61 BatteryLifeRemaining: 10808
PS C:\> Ладно, возьмем что-то менее очевидное, например, ProduKey, — подобные утилиты пользуются большим спросом не то в виду рассеяности пользователей, не то из-за убежденности последних в неких сакраментальных знаниях разработчика. А между тем никакого волшебства нет: активационные ключи M$ храниятся в REG_BINARY параметрах DigitalProductId, а их декодирование (на примере ключа Windows) происходит примерно так. PS C:\> $cur = gp 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' PS C:\> $raw = $cur.DigitalProductId[52…66] PS C:\> $map = 'BCDFGHJKMPQRTVWXY2346789' PS C:\> $key = '' PS C:\> for ($i = 24; $i -ge 0; $i--) { >> $k = 0 >> for ($j = 14; $j -ge 0; $j--) { >> $k = ($k * 256) -bxor $raw[$j] >> $raw[$j] = [Math]:: Floor ([Double]($k / 24)) >> $k %= 24 >> } >> $key = $map[$k] + $key >> if (($i % 5) -eq 0 -and $i -ne 0) {$key = '-' + $key} >> } >> PS C:\> New-Object PSObject -Property @{ >> Name = $cur.ProductName >> ProductId = [Text.Encoding]:: Default.GetString ($cur.DigitalProductId[8…30]) >> ProductKey = $key >> } >> … PS C:\> Следующей на очереди утилитой оказалась GACView. И снова PowerShell спешит на помощь. PS C:\> [Collections.ArrayList]$al = New-Object Collections.ArrayList PS C:\> ($o = [Object].Assembly).GetType ('Microsoft.Win32.Fusion').GetMethod ('ReadCache').Invoke ($null, @( >> $al, $null, $o.GetType ('Microsoft.Win32.ASM_CACHE').GetField ('GAC').GetValue ($null))) >> PS C:\> $al | Out-GridView -Title GACView Если нужен дополнительный функционал, можно использовать WinForms или WPF.Далее — MUICacheView. PS C:\> ($rk = gi 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache').GetValueNames () | >> % {New-Object PSObject -Property @{ApplicationPath=$_; ApplicationName=$rk.GetValue ($_)}} | >> Out-GridView -Title MUICacheView >> PS C:\> $rk.Close () И напоследок — UserAssistView. #requires -version 2.0 gp HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\*\Count | % { $rot13 = New-Object «Collections.Generic.Dictionary[Char, Char]» $table = { param ([Int32[]]$arr, [Int32]$lim) $arr | % { $$ = if ($_ -le $lim) {$_ + 13} else {$_ — 13} $rot13.Add ([Char]$_, [Char]$$) } } $table.Invoke (65…90, 77) #верхний регистр $table.Invoke (97…122, 109) #нижний регистр }{ $top = Split-Path -Leaf $_.PSParentPath $_.PSObject.Properties | ? {$_.Name -notlike 'PS*'} | % { if ($_.Value.Length -eq 16) { $idx = [BitConverter]:: ToUInt32($_.Value[0…3], 0) $cnt = [BitConverter]:: ToUInt32($_.Value[4…7], 0) $time = [BitConverter]:: ToInt64($_.Value[8…15], 0) $time = if ($time -ne 0) {[DateTime]:: FromFileTime ($time)} else {[String]:: Empty} } else { $idx = [BitConverter]:: ToUInt32($_.Value[4…7], 0) $cnt = [String]:: Empty } New-Object PSObject -Property @{ Name = -join ($_.Name.ToCharArray () | % {if ($rot13.ContainsKey ($_)) {$rot13[$_]} else {$_}}) Index = $idx Count = $cnt Time = $time ClassId = $top } } } | Out-GridView -Title UserAssistView Вот такой антихит-парад утилит Нир Софера.