observation / Going from good to brilliant at powershell

I came across this script that allows you to get the product key from your windows OS. I looked at this script as was amazed at the brilliance of it. I would say this is another level of PowerShell skill, how do you get to write scripts on the level ? This maybe a daft question and hard to answer but wanted to share my thoughts !

function Get-WindowsKey {
    ## function to retrieve the Windows Product Key from any PC
    param ($targets = ".")
    $hklm = 2147483650
    $regPath = "Software\Microsoft\Windows NT\CurrentVersion"
    $regValue = "DigitalProductId"
    Foreach ($target in $targets) {
        $productKey = $null
        $win32os = $null
        $wmi = [WMIClass]"\\$target\root\default:stdRegProv"
        $data = $wmi.GetBinaryValue($hklm,$regPath,$regValue)
        $binArray = ($data.uValue)[52..66]
        $charsArray = "B","C","D","F","G","H","J","K","M","P","Q","R","T","V","W","X","Y","2","3","4","6","7","8","9"
        ## decrypt base24 encoded binary data
        For ($i = 24; $i -ge 0; $i--) {
            $k = 0
            For ($j = 14; $j -ge 0; $j--) {
                $k = $k * 256 -bxor $binArray[$j]
                $binArray[$j] = [math]::truncate($k / 24)
                $k = $k % 24
            }
            $productKey = $charsArray[$k] + $productKey
            If (($i % 5 -eq 0) -and ($i -ne 0)) {
                $productKey = "-" + $productKey
            }
        }
        $win32os = Get-WmiObject Win32_OperatingSystem -computer $target
        $obj = New-Object Object
        $obj | Add-Member Noteproperty Computer -value $target
        $obj | Add-Member Noteproperty Caption -value $win32os.Caption
        $obj | Add-Member Noteproperty CSDVersion -value $win32os.CSDVersion
        $obj | Add-Member Noteproperty OSArch -value $win32os.OSArchitecture
        $obj | Add-Member Noteproperty BuildNumber -value $win32os.BuildNumber
        $obj | Add-Member Noteproperty RegisteredTo -value $win32os.RegisteredUser
        $obj | Add-Member Noteproperty ProductID -value $win32os.SerialNumber
        $obj | Add-Member Noteproperty ProductKey -value $productkey
        $obj
    }
} 

This would’ve taken time and research, however, If I’m honest, If you have watched Don Jones’ Ultimate PowerShell Training cover to cover, this gives you the skills you would need to apply your research.

I imagine it took quite some trial and error too! Kudos to whoever made this, I like how it utilises a range of different areas of PowerShell :slight_smile:

If you break down the script there are 4 main tasks:

  1. get the binary value of the product key from the registry
  2. decode the product key
  3. get the operating system data via WMI
  4. create an output object

From the look of the code it was written around the time of PowerShell 1.0 when using [WMICLASS] accelerator was the only way to access the WMI registry provider methods.

Using a lot of Add-Member statements is also a techniques that is effectively obsolete. New-Object provides a better approach.

I’ve brought the script up to date

function Get-WindowsKey {
    ## function to retrieve the Windows Product Key from any PC
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline=$true, 
        ValueFromPipelineByPropertyName=$true)]
        [String[]]$computername = "$env:COMPUTERNAME"
    )

    PROCESS {
        Foreach ($computer in $computername) {
            $productKey = $null
            $win32os = $null
            try {
                $data = Invoke-CimMethod -ClassName StDRegProv -ComputerName $computer `
                -MethodName GetBinaryValue -ErrorAction Stop -Arguments @{
                    hDefKey = [uint32]2147483650
                    sSubKeyName = 'Software\Microsoft\Windows NT\CurrentVersion'
                    sValueName = 'DigitalProductId'
                }
            }
            catch {
                Throw 'Error retreiving product Id'
            }

            $binArray = ($data.uValue)[52..66]
            $charsArray = 'B','C','D','F','G','H','J','K','M','P','Q','R','T','V','W','X','Y','2','3','4','6','7','8','9'
            ## decrypt base24 encoded binary data
            For ($i = 24; $i -ge 0; $i--) {
                $k = 0
                For ($j = 14; $j -ge 0; $j--) {
                    $k = $k * 256 -bxor $binArray[$j]
                    $binArray[$j] = [math]::truncate($k / 24)
                    $k = $k % 24
                }
                $productKey = $charsArray[$k] + $productKey
                If (($i % 5 -eq 0) -and ($i -ne 0)) {
                    $productKey = '-' + $productKey
                }
            }
            try {
                $win32os = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
            }
            catch {
                Throw 'Error OS data'
            }
            
            $props = @{
                Computer = $computer
                Caption = $win32os.Caption
                CSDVersion = $win32os.CSDVersion
                OSArchitecture = $win32os.OSArchitecture
                BuildNumber = $win32os.BuildNumber
                RegisteredTo = $win32os.RegisteredUser
                ProductID = $win32os.SerialNumber
                ProductKey = $productkey
            }
            New-Object -TypeName PSObject -Property $props
        }
    }
}

I’ve added the ability to use on the pipeline and try/catch error handling round the wmi calls. I’ve also renamed the parameter to computername which fits with the standard powershell parameter name.

AS to how you get to be able to write scripts like the original - its a fairly simple process:

  1. Understand basic PowerShell
  2. Define the task you need to perform
  3. Work out the PowerShell to do the task
  4. Put the script together
  5. Test and fix until it works the way you want.

By the way - I’m not convinced the productid decode is giving the correct answer. The result on my Windows 10 machine doesn’t match any product key I’ve used

Thank you Richard. On the front it looks impressive, but as you said, breaking it down really makes a difference. Your rework is impressive mind you ! :slight_smile: