observation / Going from good to brilliant at powershell

This topic contains 3 replies, has 4 voices, and was last updated by Profile photo of Graham Beer Graham Beer 1 year, 9 months ago.

  • Author
    Posts
  • #31008
    Profile photo of Graham Beer
    Graham Beer
    Participant

    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
        }
    } 
    
  • #31010
    Profile photo of Steven Ayers
    Steven Ayers
    Participant

    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 🙂

  • #31032
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    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

  • #31036
    Profile photo of Graham Beer
    Graham Beer
    Participant

    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 ! 🙂

You must be logged in to reply to this topic.