functionInvoke-WakeOnLan { param ( # one or more MAC addresses [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] # MAC address must be a following this regex pattern [ValidatePattern('^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$')] [string[]] $MacAddress )
begin { # instantiate a UDP client $UDPclient = [System.Net.Sockets.UdpClient]::new() } process { foreach ($_in$MacAddress) { try { $currentMacAddress = $_
# get byte array from MAC address $mac = $currentMacAddress-split'[:-]' | # convert the hex number into byte ForEach-Object { [System.Convert]::ToByte($_, 16) }
#region compose the "magic packet"
# create a byte array with 102 bytes initialized to 255 each $packet = [byte[]](,0xFF * 102)
# leave the first 6 bytes untouched, and # repeat the target MAC address bytes in bytes 7 through 102 6..101 | ForEach-Object { # $_ is indexing in the byte array, # $_ % 6 produces repeating indices between 0 and 5 # (modulo operator) $packet[$_] = $mac[($_ % 6)] }
#endregion
# connect to port 4000 on broadcast address $UDPclient.Connect(([System.Net.IPAddress]::Broadcast),4000)
# send the magic packet to the broadcast address $null = $UDPclient.Send($packet, $packet.Length) Write-Verbose"Sent magic packet to $currentMacAddress..." } catch { Write-Warning"Unable to send ${mac}: $_" } } } end { # release the UDP client and free its memory $UDPclient.Close() $UDPclient.Dispose() } }
# define constructor with parameter for ID AutoLearnAttribute([string]$Id) : base() { $this.Id = $Id }
# Transform() is called whenever there is a variable or parameter assignment, # and returns the value that is actually assigned [object] Transform([System.Management.Automation.EngineIntrinsics]$engineIntrinsics, [object] $inputData) { # make sure the folder with hints exists $exists = Test-Path-Path$this.Path if (!$exists) { $null = New-Item-Path$this.Path -ItemType Directory }
# create a filename for hint list $filename = '{0}.hint'-f$this.Id $hintPath = Join-Path-Path$this.Path -ChildPath$filename
# use a hash table to keep hint list $hints = @{}
# read hint list if it exists $exists = Test-Path-Path$hintPath if ($exists) { Get-Content-Path$hintPath-Encoding Default | # remove leading and trailing blanks ForEach-Object { $_.Trim() } | # remove empty lines Where-Object { ![string]::IsNullOrEmpty($_) } | # add to hash table ForEach-Object { # value is not used, set it to $true $hints[$_] = $true } }
# does the user input start with the clearing key? if ($inputData.StartsWith($this.ClearKey)) { # remove the prefix $inputData = $inputData.SubString(1)
# clear the hint list $hints.Clear() }
# add new value to hint list if(![string]::IsNullOrWhiteSpace($inputData)) { $hints[$inputData] = $true } # save hints list $hints.Keys | Sort-Object | Set-Content-Path$hintPath-Encoding Default
# return the user input (if there was a clearing key at its start, # it is now stripped) return$inputData } }
# define [AutoComplete()] classAutoCompleteAttribute : System.Management.Automation.ArgumentCompleterAttribute { # define path to store hint lists [string]$Path = "$env:temp\hints"
# define ID to manage multiple hint lists [string]$Id = 'default'
# define constructor with parameter for ID AutoCompleteAttribute([string]$Id) : base([AutoCompleteAttribute]::_createScriptBlock($this)) { $this.Id = $Id }
# create a static helper method that creates the script block that the base constructor needs # this is necessary to be able to access the argument(s) submitted to the constructor # the method needs a reference to the object instance to (later) access its optional parameters hiddenstatic[ScriptBlock] _createScriptBlock([AutoCompleteAttribute] $instance) { $scriptblock = { # receive information about current state param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
# create filename for hint list $filename = '{0}.hint'-f$instance.Id $hintPath = Join-Path-Path$instance.Path -ChildPath$filename
# use a hash table to keep hint list $hints = @{}
# read hint list if it exists $exists = Test-Path-Path$hintPath if ($exists) { Get-Content-Path$hintPath-Encoding Default | # remove leading and trailing blanks ForEach-Object { $_.Trim() } | # remove empty lines Where-Object { ![string]::IsNullOrEmpty($_) } | # filter completion items based on existing text Where-Object { $_.LogName -like"$wordToComplete*" } | # create argument completion results Foreach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } }.GetNewClosure() return$scriptblock } }
functionGet-WindowsProductKey{ \# test whether this is Windows 7 or older function Test-Win7 { $OSVersion = [System.Environment]::OSVersion.Version ($OSVersion.Major -eq6-and$OSVersion.Minor -lt2) -or$OSVersion.Major -le6 }
\# implement decoder $code = @'// original implementation: https://github.com/mrpeardotnet/WinProdKeyFinder using System; using System.Collections;
public staticclassDecoder { public static string DecodeProductKeyWin7(byte[] digitalProductId) { const int keyStartIndex = 52; const int keyEndIndex = keyStartIndex + 15; var digits = new[] { 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R', 'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9', }; const int decodeLength = 29; const int decodeStringLength = 15; var decodedChars = new char[decodeLength]; var hexPid = new ArrayList(); for (var i = keyStartIndex; i <= keyEndIndex; i++) { hexPid.Add(digitalProductId[i]); } for (var i = decodeLength - 1; i >= 0; i--) { // Every sixth char is a separator. if ((i + 1) % 6 == 0) { decodedChars[i] = '-'; } else { // Do the actual decoding. var digitMapIndex = 0; for (var j = decodeStringLength - 1; j >= 0; j--) { var byteValue = (digitMapIndex << 8) | (byte)hexPid[j]; hexPid[j] = (byte)(byteValue / 24); digitMapIndex = byteValue % 24; decodedChars[i] = digits[digitMapIndex]; } } } return new string(decodedChars); }
public static string DecodeProductKey(byte[] digitalProductId) { var key = String.Empty; const int keyOffset = 52; var isWin8 = (byte)((digitalProductId[66] / 6) & 1); digitalProductId[66] = (byte)((digitalProductId[66] & 0xf7) | (isWin8 & 2) * 4);
const string digits = "BCDFGHJKMPQRTVWXY2346789"; var last = 0; for (var i = 24; i >= 0; i--) { var current = 0; for (var j = 14; j >= 0; j--) { current = current*256; current = digitalProductId[j + keyOffset] + current; digitalProductId[j + keyOffset] = (byte)(current/24); current = current%24; last = current; } key = digits[current] + key; }
var keypart1 = key.Substring(1, last); var keypart2 = key.Substring(last + 1, key.Length - (last + 1)); key = keypart1 + "N" + keypart2;
for (var i = 5; i < key.Length; i += 6) { key = key.Insert(i, "-"); }
return key; } }'@ \# compile C# Add-Type -TypeDefinition $code \# get raw product key $digitalId = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name DigitalProductId).DigitalProductId $isWin7 = Test-Win7 if ($isWin7) { \# use static C# method [Decoder]::DecodeProductKeyWin7($digitalId) } else { \# use static C# method: [Decoder]::DecodeProductKey($digitalId) } }
PS> Get-CimInstance-Query'Select PartialProductKey From SoftwareLicensingProduct Where Name LIKE "Windows%" AND LicenseStatus>0' | Select-Object-ExpandProperty PartialProductKey