Last active
March 6, 2025 15:43
-
-
Save Sriram-PR/8329dc5aadd489de24ae7a2aba04bf04 to your computer and use it in GitHub Desktop.
Retrieve Windows Product Key
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ' VBScript utility to extract Windows product keys and activation status across Windows 7/8/10/11. | |
| ' Uses multiple fallback methods (WMI, PowerShell, Registry) for maximum compatibility with KMS, OEM, and retail licenses. | |
| ' Displays results in a dialog and optionally saves to disk. | |
| ' Requires admin rights. | |
| Option Explicit | |
| ' Variables for WMI, registry access, and data storage | |
| Dim objshell, path, DigitalID, Result | |
| Dim ProductName, ProductID, ProductKey, ProductData | |
| Dim isWin8OrNewer, WinVer | |
| Dim fso, logPath | |
| Dim KeySource ' Identifies which retrieval method succeeded | |
| ' Create shell object for registry access and environment variables | |
| ' Must be initialized first as other objects depend on it | |
| On Error Resume Next | |
| Set objshell = CreateObject("WScript.Shell") | |
| If Err.Number <> 0 Then | |
| MsgBox "Error creating WScript.Shell object: " & Err.Description, vbCritical, "Error" | |
| WScript.Quit | |
| End If | |
| On Error GoTo 0 | |
| ' Initialize FileSystemObject and set log directory to %SystemDrive%\Logs | |
| Set fso = CreateObject("Scripting.FileSystemObject") | |
| logPath = objshell.ExpandEnvironmentStrings("%SystemDrive%") & "\Logs\" | |
| On Error Resume Next | |
| If Not fso.FolderExists(logPath) Then | |
| fso.CreateFolder(logPath) | |
| If Err.Number <> 0 Then | |
| MsgBox "Unable to create " & logPath & " folder: " & Err.Description & vbNewLine & _ | |
| "Please run this script as administrator.", vbExclamation | |
| Set fso = Nothing | |
| Set objshell = Nothing | |
| WScript.Quit | |
| End If | |
| End If | |
| On Error GoTo 0 | |
| LogAction "Script started" | |
| ' Verify administrative privileges - required for registry and WMI access | |
| If Not IsAdmin() Then | |
| MsgBox "This script requires administrator rights to read license information and create folders." & vbNewLine & _ | |
| "Please right-click the script and select 'Run as administrator'.", vbExclamation, "Administrator Rights Required" | |
| LogAction "Script terminated - insufficient privileges" | |
| Set fso = Nothing | |
| Set objshell = Nothing | |
| WScript.Quit | |
| End If | |
| LogAction "Running with administrator privileges" | |
| ' Set registry path for Windows product information | |
| Path = "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\" | |
| ' Detect Windows version to apply appropriate key extraction algorithm | |
| ' Windows 8+ uses a different algorithm than earlier versions | |
| On Error Resume Next | |
| WinVer = objshell.RegRead(Path & "CurrentBuild") | |
| If Err.Number <> 0 Then | |
| LogAction "Warning: Could not determine Windows version - " & Err.Description | |
| WinVer = 0 | |
| End If | |
| isWin8OrNewer = (CDbl(WinVer) >= 9200) | |
| LogAction "Windows Build: " & WinVer & " (Windows 8 or newer: " & isWin8OrNewer & ")" | |
| On Error GoTo 0 | |
| ' Extract basic product information from registry | |
| On Error Resume Next | |
| ProductName = "Product Name: " & objshell.RegRead(Path & "ProductName") | |
| If Err.Number <> 0 Then ProductName = "Product Name: Unknown" | |
| ProductID = "Product ID: " & objshell.RegRead(Path & "ProductID") | |
| If Err.Number <> 0 Then ProductID = "Product ID: Unknown" | |
| On Error GoTo 0 | |
| ' Retrieve product key using the most reliable method available for this Windows version | |
| ProductKey = "Installed Key: " & GetCurrentProductKey() | |
| ' Add activation status if available through WMI | |
| On Error Resume Next | |
| Dim ActivationStatus | |
| ActivationStatus = GetActivationStatus() | |
| If ActivationStatus <> "" Then | |
| ProductData = ProductName & vbNewLine & ProductID & vbNewLine & ProductKey & vbNewLine & ActivationStatus | |
| Else | |
| ProductData = ProductName & vbNewLine & ProductID & vbNewLine & ProductKey | |
| End If | |
| On Error GoTo 0 | |
| ' Add source information to help with troubleshooting | |
| ProductData = ProductData & vbNewLine & vbNewLine & "Key retrieval method: " & KeySource | |
| ' Display results to user | |
| MsgBox ProductData, vbInformation, "Windows License Information" | |
| ' Offer to save the license information to a file | |
| Dim savePath | |
| savePath = logPath & "WindowsLicenseInfo.txt" | |
| If vbYes = MsgBox("Do you want to save this information to:" & vbNewLine & savePath & "?", vbYesNo + vbQuestion, "Save License Information") Then | |
| ' Try primary save method first | |
| Dim saveSuccess | |
| saveSuccess = False | |
| On Error Resume Next | |
| SaveToLogs ProductData | |
| If Err.Number = 0 Then | |
| saveSuccess = True | |
| Else | |
| LogAction "Regular save failed with error: " & Err.Description | |
| Err.Clear | |
| End If | |
| On Error GoTo 0 | |
| ' Fall back to alternative save method if primary method fails | |
| If Not saveSuccess Then | |
| LogAction "Trying direct command save method" | |
| SaveWithDirectCommand ProductData | |
| End If | |
| Else | |
| LogAction "User chose not to save the license information" | |
| End If | |
| LogAction "Script completed successfully" | |
| ' Release resources to prevent memory leaks | |
| Set fso = Nothing | |
| Set objshell = Nothing | |
| ' Function: SaveToLogs | |
| ' Purpose: Save license data to text file with timestamp if file already exists | |
| ' Returns: None, but raises error on failure | |
| Function SaveToLogs(Data) | |
| On Error Resume Next | |
| Dim fName, txt | |
| fName = logPath & "WindowsLicenseInfo.txt" | |
| LogAction "Attempting to save to: " & fName | |
| ' Use timestamped filename to preserve historical data | |
| If fso.FileExists(fName) Then | |
| Dim timestamp | |
| timestamp = Year(Now) & Right("0" & Month(Now), 2) & Right("0" & Day(Now), 2) & "_" & _ | |
| Right("0" & Hour(Now), 2) & Right("0" & Minute(Now), 2) | |
| fName = logPath & "WindowsLicenseInfo_" & timestamp & ".txt" | |
| LogAction "File already exists, creating timestamped version: " & fName | |
| End If | |
| ' Write data to file | |
| Set txt = fso.CreateTextFile(fName, True) | |
| If Err.Number <> 0 Then | |
| LogAction "Error creating file: " & Err.Description | |
| ' No cleanup needed for txt as it wasn't created | |
| Err.Raise Err.Number ' Re-raise error for caller to handle | |
| Exit Function | |
| End If | |
| txt.WriteLine Data | |
| If Err.Number <> 0 Then | |
| LogAction "Error writing to file: " & Err.Description | |
| txt.Close | |
| Set txt = Nothing | |
| Err.Raise Err.Number ' Re-raise error for caller to handle | |
| Exit Function | |
| End If | |
| txt.Close | |
| Set txt = Nothing | |
| ' Notify user and open file | |
| MsgBox "Information saved to:" & vbNewLine & fName, vbInformation, "Save Successful" | |
| LogAction "Information saved to: " & fName | |
| ' Open file in Notepad for immediate viewing | |
| objshell.Run "notepad.exe """ & fName & """", 1, False | |
| On Error GoTo 0 | |
| End Function | |
| ' Function: SaveWithDirectCommand | |
| ' Purpose: Alternative save method using CMD when direct file operations fail | |
| ' Returns: None | |
| Sub SaveWithDirectCommand(Data) | |
| On Error Resume Next | |
| Dim cmdText, i, line, lines | |
| Dim saveFile | |
| saveFile = logPath & "WindowsLicenseInfo.txt" | |
| LogAction "Attempting to save with direct command to: " & saveFile | |
| ' Split data into lines for batch processing | |
| lines = Split(Data, vbNewLine) | |
| ' Create temporary command file in user's temp directory | |
| Dim cmdFile | |
| cmdFile = objshell.ExpandEnvironmentStrings("%TEMP%") & "\SaveLicense.cmd" | |
| ' Build batch file to write data line by line | |
| Set cmdText = fso.CreateTextFile(cmdFile, True) | |
| If Err.Number <> 0 Then | |
| LogAction "Failed to create command file: " & Err.Description | |
| MsgBox "Cannot create temporary files needed for save operation.", vbCritical, "Save Failed" | |
| Exit Sub | |
| End If | |
| cmdText.WriteLine "@echo off" | |
| cmdText.WriteLine "echo. > """ & saveFile & """" ' Create/clear the file | |
| ' Add each line of data to batch file | |
| For i = 0 To UBound(lines) | |
| line = lines(i) | |
| If Trim(line) <> "" Then | |
| cmdText.WriteLine "echo " & line & " >> """ & saveFile & """" | |
| Else | |
| cmdText.WriteLine "echo. >> """ & saveFile & """" | |
| End If | |
| Next | |
| cmdText.Close | |
| Set cmdText = Nothing | |
| ' Execute batch file with hidden window | |
| objshell.Run "cmd /c """ & cmdFile & """", 0, True | |
| ' Verify success and notify user | |
| If fso.FileExists(saveFile) Then | |
| MsgBox "Information saved to:" & vbNewLine & saveFile, vbInformation, "Save Successful" | |
| LogAction "Successfully saved to: " & saveFile | |
| ' Open file in Notepad for immediate viewing | |
| objshell.Run "notepad.exe """ & saveFile & """", 1, False | |
| Else | |
| MsgBox "Could not save information to file using alternate method.", vbCritical, "Save Failed" | |
| LogAction "Failed to save with direct command approach" | |
| End If | |
| ' Clean up temporary batch file | |
| On Error Resume Next | |
| If fso.FileExists(cmdFile) Then | |
| fso.DeleteFile cmdFile, True | |
| End If | |
| On Error GoTo 0 | |
| End Sub | |
| ' Function: GetCurrentProductKey | |
| ' Purpose: Try multiple methods to retrieve Windows product key | |
| ' Returns: String containing the product key or error message | |
| Function GetCurrentProductKey() | |
| On Error Resume Next | |
| Dim objWMI, colItems, objItem, ProductKey, returnValue | |
| LogAction "Attempting to retrieve product key..." | |
| ' Method 1: SoftwareLicensingService WMI (Windows 8/10/11) | |
| ' Most reliable for modern Windows versions with digital licensing | |
| Set objWMI = GetObject("winmgts:\\.\root\cimv2") | |
| If Err.Number = 0 Then | |
| Set colItems = objWMI.ExecQuery("SELECT * FROM SoftwareLicensingService") | |
| If Err.Number = 0 Then | |
| For Each objItem in colItems | |
| ' Try KMS key first (Volume licensing) | |
| If objItem.KeyManagementServiceProductKeyID <> "" Then | |
| returnValue = objItem.KeyManagementServiceProductKeyID | |
| If Len(returnValue) > 5 Then | |
| LogAction "Retrieved KMS key from SoftwareLicensingService" | |
| KeySource = "KMS Product Key (SoftwareLicensingService WMI)" | |
| GetCurrentProductKey = FormatKey(returnValue) | |
| Set colItems = Nothing | |
| Set objWMI = Nothing | |
| Exit Function | |
| End If | |
| End If | |
| ' Try OEM key (Preinstalled Windows) | |
| If objItem.OA3xOriginalProductKey <> "" Then | |
| returnValue = objItem.OA3xOriginalProductKey | |
| If Len(returnValue) > 5 Then | |
| LogAction "Retrieved OEM key from SoftwareLicensingService" | |
| KeySource = "OEM Product Key (SoftwareLicensingService WMI)" | |
| GetCurrentProductKey = returnValue | |
| Set colItems = Nothing | |
| Set objWMI = Nothing | |
| Exit Function | |
| End If | |
| End If | |
| Next | |
| End If | |
| End If | |
| ' Clean up resources from Method 1 | |
| If Not colItems Is Nothing Then Set colItems = Nothing | |
| If Not objWMI Is Nothing Then Set objWMI = Nothing | |
| Err.Clear | |
| ' Method 2: MDM WMI namespace (Some OEM devices) | |
| ' Used primarily for MDM-managed devices (Intune, etc.) | |
| Set objWMI = GetObject("winmgmts:\\.\root\CIMV2\mdm\dmmap") | |
| If Err.Number = 0 Then | |
| Set colItems = objWMI.ExecQuery("Select * From Mdm_SoftwareLicensingService") | |
| If Err.Number = 0 Then | |
| For Each objItem in colItems | |
| If objItem.OA3xOriginalProductKey <> "" Then | |
| returnValue = objItem.OA3xOriginalProductKey | |
| If Len(returnValue) > 5 Then | |
| LogAction "Retrieved OEM key from Mdm_SoftwareLicensingService" | |
| KeySource = "OEM Product Key (MDM WMI)" | |
| GetCurrentProductKey = returnValue | |
| Set colItems = Nothing | |
| Set objWMI = Nothing | |
| Exit Function | |
| End If | |
| End If | |
| Next | |
| End If | |
| End If | |
| ' Clean up resources from Method 2 | |
| If Not colItems Is Nothing Then Set colItems = Nothing | |
| If Not objWMI Is Nothing Then Set objWMI = Nothing | |
| Err.Clear | |
| ' Method 3: PowerShell command for OEM key | |
| ' Some systems expose keys only via PowerShell | |
| Dim psCmd | |
| psCmd = "powershell -command ""& {(Get-WmiObject -query 'select * from SoftwareLicensingService').OA3xOriginalProductKey}""" | |
| returnValue = Trim(objShell.Exec(psCmd).StdOut.ReadAll()) | |
| If Err.Number = 0 And Len(returnValue) > 5 Then | |
| LogAction "Retrieved product key via PowerShell command" | |
| KeySource = "OEM Product Key (PowerShell)" | |
| GetCurrentProductKey = returnValue | |
| Exit Function | |
| End If | |
| Err.Clear | |
| ' Method 4: PowerShell for partial key | |
| ' When full key isn't available, try last 5 chars | |
| psCmd = "powershell -command ""& {(Get-WmiObject -query 'select * from SoftwareLicensingService').PartialProductKey}""" | |
| returnValue = Trim(objShell.Exec(psCmd).StdOut.ReadAll()) | |
| If Err.Number = 0 And Len(returnValue) > 0 Then | |
| LogAction "Retrieved partial product key via PowerShell command" | |
| KeySource = "Partial Product Key (last 5 characters)" | |
| GetCurrentProductKey = "[Last 5 characters: " & returnValue & "]" | |
| Exit Function | |
| End If | |
| Err.Clear | |
| ' Method 5: slmgr.vbs command line tool | |
| ' Built-in Windows script for license management | |
| LogAction "Trying to extract key from slmgr /dli output" | |
| psCmd = "cscript //nologo %windir%\system32\slmgr.vbs /dli" | |
| Dim slmgrOutput, keyPos, keyLine, parts | |
| slmgrOutput = objShell.Exec(psCmd).StdOut.ReadAll() | |
| If Err.Number = 0 And InStr(slmgrOutput, "Product Key:") > 0 Then | |
| keyPos = InStr(slmgrOutput, "Product Key:") | |
| keyLine = Mid(slmgrOutput, keyPos, InStr(keyPos, slmgrOutput, vbCrLf) - keyPos) | |
| parts = Split(keyLine, "-") | |
| If UBound(parts) >= 0 Then | |
| returnValue = "[Partial Key from slmgr: " & parts(UBound(parts)) & "]" | |
| LogAction "Retrieved partial key from slmgr /dli output" | |
| KeySource = "slmgr /dli command" | |
| GetCurrentProductKey = returnValue | |
| Exit Function | |
| End If | |
| End If | |
| Err.Clear | |
| ' Method 6: Legacy registry method (Last resort) | |
| ' Works on older Windows versions using binary conversion | |
| LogAction "All WMI methods failed, falling back to registry method" | |
| returnValue = ConvertToKey(objshell.RegRead("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DigitalProductId")) | |
| If returnValue <> "" And returnValue <> "Error converting product key" Then | |
| KeySource = "Registry DigitalProductId (legacy method)" | |
| GetCurrentProductKey = returnValue | |
| Else | |
| KeySource = "No valid product key found" | |
| GetCurrentProductKey = "No valid product key could be found" | |
| End If | |
| On Error GoTo 0 | |
| End Function | |
| ' Function: GetActivationStatus | |
| ' Purpose: Retrieve Windows activation status from WMI | |
| ' Returns: String with activation status or empty string if unavailable | |
| Function GetActivationStatus() | |
| On Error Resume Next | |
| Dim objWMI, colItems, objItem, status | |
| Set objWMI = GetObject("winmgts:\\.\root\cimv2") | |
| Set colItems = objWMI.ExecQuery("SELECT * FROM SoftwareLicensingProduct WHERE LicenseStatus > 0") | |
| If Err.Number = 0 Then | |
| For Each objItem in colItems | |
| ' Convert numeric status to human-readable form | |
| Select Case objItem.LicenseStatus | |
| Case 0: status = "Unlicensed" | |
| Case 1: status = "Licensed" | |
| Case 2: status = "Out-of-Box Grace Period" | |
| Case 3: status = "Out-of-Tolerance Grace Period" | |
| Case 4: status = "Non-Genuine Grace Period" | |
| Case 5: status = "Notification" | |
| Case 6: status = "Extended Grace" | |
| Case Else: status = "Unknown Status: " & objItem.LicenseStatus | |
| End Select | |
| GetActivationStatus = "Activation Status: " & status | |
| ' Clean up resources before exit | |
| Set colItems = Nothing | |
| Set objWMI = Nothing | |
| Exit Function | |
| Next | |
| End If | |
| ' Clean up resources | |
| If Not colItems Is Nothing Then Set colItems = Nothing | |
| If Not objWMI Is Nothing Then Set objWMI = Nothing | |
| GetActivationStatus = "" | |
| On Error GoTo 0 | |
| End Function | |
| ' Function: FormatKey | |
| ' Purpose: Format product key with dashes for readability | |
| ' Returns: Formatted product key string | |
| Function FormatKey(rawKey) | |
| If Len(rawKey) = 25 Then | |
| ' Standard 25-character key, add dashes | |
| FormatKey = Mid(rawKey, 1, 5) & "-" & Mid(rawKey, 6, 5) & "-" & Mid(rawKey, 11, 5) & "-" & Mid(rawKey, 16, 5) & "-" & Mid(rawKey, 21, 5) | |
| ElseIf Len(rawKey) = 29 Then | |
| ' Already formatted with dashes | |
| FormatKey = rawKey | |
| Else | |
| ' Non-standard length, return as-is | |
| FormatKey = rawKey | |
| End If | |
| End Function | |
| ' Function: ConvertToKey | |
| ' Purpose: Convert binary registry data to product key string | |
| ' Used for legacy Windows versions where WMI methods aren't available | |
| ' Algorithm sourced from Microsoft documentation | |
| Function ConvertToKey(Key) | |
| On Error Resume Next | |
| ' Validate key data | |
| If IsNull(Key) Then | |
| ConvertToKey = "Error reading registry key" | |
| Exit Function | |
| End If | |
| Const KeyOffset = 52 | |
| Dim Maps, i, j, Current, KeyOutput, Last, keypart1, insert | |
| ' Apply different algorithm based on Windows version | |
| If isWin8OrNewer Then | |
| ' Windows 8/10/11 algorithm | |
| Key(66) = (Key(66) And &HF7) Or 8 | |
| Else | |
| ' Pre-Windows 8 algorithm | |
| Key(66) = (Key(66) And &HF7) Or 0 | |
| End If | |
| i = 24 | |
| Maps = "BCDFGHJKMPQRTVWXY2346789" | |
| KeyOutput = "" | |
| ' Decode binary data to character string | |
| Do | |
| Current = 0 | |
| j = 14 | |
| Do | |
| Current = Current * 256 | |
| Current = Key(j + KeyOffset) + Current | |
| Key(j + KeyOffset) = (Current \ 24) | |
| Current = Current Mod 24 | |
| j = j - 1 | |
| Loop While j >= 0 | |
| i = i - 1 | |
| KeyOutput = Mid(Maps, Current + 1, 1) & KeyOutput | |
| Last = Current | |
| Loop While i >= 0 | |
| ' Insert 'N' character at specific position (part of algorithm) | |
| keypart1 = Mid(KeyOutput, 2, Last) | |
| insert = "N" | |
| KeyOutput = Replace(KeyOutput, keypart1, keypart1 & insert, 2, 1, 0) | |
| If Last = 0 Then KeyOutput = insert & KeyOutput | |
| ' Format key with standard hyphenation | |
| ConvertToKey = Mid(KeyOutput, 1, 5) & "-" & Mid(KeyOutput, 6, 5) & "-" & Mid(KeyOutput, 11, 5) & "-" & Mid(KeyOutput, 16, 5) & "-" & Mid(KeyOutput, 21, 5) | |
| If Err.Number <> 0 Then | |
| LogAction "Error in ConvertToKey function: " & Err.Description | |
| ConvertToKey = "Error converting product key" | |
| End If | |
| On Error GoTo 0 | |
| End Function | |
| ' Function: IsAdmin | |
| ' Purpose: Check if script is running with administrator privileges | |
| ' Returns: Boolean indicating admin status | |
| Function IsAdmin() | |
| On Error Resume Next | |
| Dim adminShell | |
| Set adminShell = CreateObject("WScript.Shell") | |
| ' Try to access a registry key that requires admin rights | |
| adminShell.RegRead "HKEY_USERS\S-1-5-19\Environment\TEMP" | |
| IsAdmin = (Err.Number = 0) | |
| Set adminShell = Nothing | |
| On Error GoTo 0 | |
| End Function | |
| ' Function: LogAction | |
| ' Purpose: Write timestamped entries to log file | |
| ' Returns: None | |
| Sub LogAction(Message) | |
| On Error Resume Next | |
| Dim logFile, logFileName | |
| logFileName = logPath & "WindowsLicenseRetrieval.log" | |
| ' Try to open existing log file or create new one | |
| Set logFile = fso.OpenTextFile(logFileName, 8, True) | |
| If Err.Number <> 0 Then | |
| ' 8 = ForAppending, True = Create if doesn't exist | |
| Err.Clear | |
| Set logFile = fso.CreateTextFile(logFileName, True) | |
| If Err.Number <> 0 Then | |
| Set logFile = Nothing | |
| Exit Sub ' Can't create log, silently fail | |
| End If | |
| End If | |
| ' Write timestamped message | |
| logFile.WriteLine Now & " - " & Message | |
| logFile.Close | |
| Set logFile = Nothing | |
| On Error GoTo 0 | |
| End Sub |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment