Microsoft Windows and Microsoft Office products (excluding Office 365 Editions) encode the original installation key into a registry value with the name DigitalProductId.
Windows stores this key under HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
Microsoft Office uses a key such as HKLM\SOFTWARE\Microsoft\Office\11.0\Registration{90120409-6000-11D3-8CFE-0150048383C9}
This script will decode a byte array containing the contents of DigitalProductId and convert it back into original registration key. Note : This doesn’t work for Microsoft products of the 90s such as Visual Basic 6, or Windows 95.
Decode Key:
Function Decode-Key
{
param([byte[]] $key)
$KeyOutput=""
$KeyOffset = 52
$IsWin8 = ([System.Math]::Truncate($key[66] / 6)) -band 1
$key[66] = ($Key[66] -band 0xF7) -bor (($isWin8 -band 2) * 4)
$i = 24
$maps = "BCDFGHJKMPQRTVWXY2346789"
Do
{
$current= 0
$j = 14
Do {
$current = $current* 256
$current = $Key[$j + $KeyOffset] + $Current
$Key[$j + $KeyOffset] = [System.Math]::Truncate($Current / 24 )
$Current=$Current % 24
$j--
} while ($j -ge 0)
$i--
$KeyOutput = $Maps.Substring($Current, 1) + $KeyOutput
$last = $current
} while ($i -ge 0)
If ($isWin8 -eq 1)
{
$keypart1 = $KeyOutput.Substring(1,$last)
$insert = "N"
$KeyOutput = $KeyOutput.Replace($keypart1, $keypart1 + $insert)
if ($Last -eq 0) { $KeyOutput = $insert + $KeyOutput }
}
if ($keyOutput.Length -eq 26)
{
$result = [String]::Format("{0}-{1}-{2}-{3}-{4}",
$KeyOutput.Substring(1, 5),
$KeyOutput.Substring(6, 5),
$KeyOutput.Substring(11,5),
$KeyOutput.Substring(16,5),
$KeyOutput.Substring(21,5))
}
else
{
$KeyOutput
}
return $result
}
This can be combined with our previous registry searching script, to find and decode all DigitalProductId keys under HKLM:
$code = @'
using Microsoft.VisualBasic.CompilerServices;
using Microsoft.Win32;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace RegUtils
{
public class RegistryFinder
{
public ConcurrentBag<KeyValuePair<string,string>> Results;
public RegistryFinder()
{
Results = new ConcurrentBag<KeyValuePair<string, string>>();
}
public void FindRegValueData(RegistryKey SubKey, string pattern)
{
FindRegValueData(SubKey, pattern, true);
}
private void FindRegValueData(RegistryKey SubKey, string pattern, bool first)
{
Parallel.ForEach(SubKey.GetSubKeyNames(), sub =>
{
try
{
if (first)
{
foreach (var valueName in SubKey.GetValueNames())
{
if (SubKey.GetValue(valueName) != null)
{
if (LikeOperator.LikeString(SubKey.GetValue(valueName).ToString(), pattern, Microsoft.VisualBasic.CompareMethod.Text))
{
Results.Add(new KeyValuePair<string,string>(SubKey.Name, valueName));
}
}
}
}
RegistryKey local = SubKey.OpenSubKey(sub);
foreach (var valueName in local.GetValueNames())
{
if (local.GetValue(valueName) != null)
{
if (LikeOperator.LikeString(local.GetValue(valueName).ToString(), pattern, Microsoft.VisualBasic.CompareMethod.Text))
{
Results.Add(new KeyValuePair<string, string>(local.Name, valueName));
}
}
}
FindRegValueData(local, pattern, false);
}
catch (System.Security.SecurityException)
{
// expect we will get some of these and ignore
}
});
}
public void FindRegValueNames(RegistryKey SubKey, string pattern)
{
FindRegValueNames(SubKey, pattern, true);
}
private void FindRegValueNames(RegistryKey SubKey, string pattern, bool first)
{
Parallel.ForEach(SubKey.GetSubKeyNames(), sub =>
{
try
{
if (first)
{
foreach (var valueName in SubKey.GetValueNames())
{
if (LikeOperator.LikeString(valueName, pattern, Microsoft.VisualBasic.CompareMethod.Text))
{
Results.Add(new KeyValuePair<string, string>(SubKey.Name, valueName));
}
}
}
RegistryKey local = SubKey.OpenSubKey(sub);
foreach (var valueName in local.GetValueNames())
{
if (LikeOperator.LikeString(valueName, pattern, Microsoft.VisualBasic.CompareMethod.Text))
{
Results.Add(new KeyValuePair<string, string>(local.Name, valueName));
}
}
FindRegValueNames(local, pattern, false);
}
catch (System.Security.SecurityException)
{
// expect we will get some of these and ignore
}
});
}
public void FindRegKeys(RegistryKey SubKey, string pattern)
{
Parallel.ForEach(SubKey.GetSubKeyNames(), sub =>
{
if (LikeOperator.LikeString(sub, pattern, Microsoft.VisualBasic.CompareMethod.Text))
{
Results.Add(new KeyValuePair<string, string>(String.Format("{0}\\{1}", SubKey.Name, sub),null));
}
try
{
RegistryKey local = SubKey.OpenSubKey(sub);
FindRegKeys(local, pattern);
}
catch (System.Security.SecurityException)
{
// expect we will get some of these and ignore
}
});
}
}
}
'@
Function Decode-Key
{
param([byte[]] $key)
$KeyOutput=""
$KeyOffset = 52
$IsWin8 = ([System.Math]::Truncate($key[66] / 6)) -band 1
$key[66] = ($Key[66] -band 0xF7) -bor (($isWin8 -band 2) * 4)
$i = 24
$maps = "BCDFGHJKMPQRTVWXY2346789"
Do
{
$current= 0
$j = 14
Do {
$current = $current* 256
$current = $Key[$j + $KeyOffset] + $Current
$Key[$j + $KeyOffset] = [System.Math]::Truncate($Current / 24 )
$Current=$Current % 24
$j--
} while ($j -ge 0)
$i--
$KeyOutput = $Maps.Substring($Current, 1) + $KeyOutput
$last = $current
} while ($i -ge 0)
If ($isWin8 -eq 1)
{
$keypart1 = $KeyOutput.Substring(1,$last)
$insert = "N"
$KeyOutput = $KeyOutput.Replace($keypart1, $keypart1 + $insert)
if ($Last -eq 0) { $KeyOutput = $insert + $KeyOutput }
}
if ($keyOutput.Length -eq 26)
{
$result = [String]::Format("{0}-{1}-{2}-{3}-{4}",
$KeyOutput.Substring(1, 5),
$KeyOutput.Substring(6, 5),
$KeyOutput.Substring(11,5),
$KeyOutput.Substring(16,5),
$KeyOutput.Substring(21,5))
}
else
{
$KeyOutput
}
return $result
}
Add-Type -TypeDefinition $code -ReferencedAssemblies Microsoft.VisualBasic.dll
$regFinder = New-Object RegUtils.RegistryFinder
$searchTerm = "DigitalProductId"
$baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64)
$regFinder.FindRegValueNames($baseKey, $searchTerm)
ForEach ($result in $regFinder.Results)
{
$result.Key
$keyName = $result.Key.Replace("HKEY_LOCAL_MACHINE\","HKLM:\").Replace("HKEY_CURRENT_USER","HKCU:\")
$keyData = (Get-ItemProperty -Path $keyName).DigitalProductId
Decode-Key $keyData
}
Returns 25(B) :(
The first script you need to provide it a valid byte array. The 2nd script it will just search the reg and decode it. It works on keys from Office XP and later (except O365) and Windows 2000 and later for windows keys