24

I accidentally disconnected my hard drive while it was still running and corrupted my Windows 7 installation; I am now completely unable to boot into Windows. I have tried everything to try and repair the installation: Windows Startup Repair, chkdsk /r, SFC /scannow, bootrec /rebuildbcd, etc. and no luck. I want to just perform a fresh install, but my problem is that I do not have my Windows product key written down anywhere, and I am unable to use any scripts or utilities to retrieve it from the registry because I cannot boot into Windows.

Windows 7 product keys are stored, encrypted, in the "DigitalProductId" value of the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion. I was able to mount the corrupted Windows partition read-only from an Ubuntu live CD and copy the Windows\System32\config\SOFTWARE registry hive, which contains the key & value in question, to a flash drive, but loading this hive into regedit on a working Windows installation and then trying to use scripts or utilities to decrypt the loaded "DigitalProductId" value only returns the product key of the host Windows installation, no matter how much fiddling I try. I've tried contacting Microsoft support and they've been rather unhelpful. Would anyone be able to guide me further? Perhaps if there's a different way to retrieve the product key from Linux?

If someone more familiar with scripting/cryptography would be willing to try and follow the decryption script to decrypt the product key by hand, I could e-mail you the exported "DigitalProductId" value, SOFTWARE registry hive, and decryption script.

4
  • This doesn't sound right. If you bought a license you should have the key. Now, on the other hand, if you got your hands on someone elses windows image and want to extract its key, that's not really the point of this site.
    – JasonXA
    Commented Apr 4, 2015 at 5:23
  • I did buy a license. I have the installation DVD but can't find the product key that came with it. But I think I may have found a solution here: dagondesign.com/articles/windows-xp-product-key-recovery/…
    – sundiata
    Commented Apr 4, 2015 at 5:24
  • Yes, that does seem to work. Using the method I did manage to reproduce my key.
    – JasonXA
    Commented Apr 4, 2015 at 5:46
  • If your firmware is UEFI-based, the license key is actually stored in the ACPI MSDM table so it persists across a reboot. If so, see blog.fpmurphy.com for details on how to recover it.
    – fpmurphy
    Commented Apr 4, 2015 at 11:15

6 Answers 6

40

There is a great tool available for Linux called chntpw. You can get it easily on Debian/Ubuntu via:

sudo apt install chntpw

To look into the relevant registry file mount the Windows disk and open it like so:

chntpw -e /path/to/windisk/Windows/System32/config/software

Now to get the decoded DigitalProductId enter this command:

dpi \Microsoft\Windows NT\CurrentVersion\DigitalProductId
4
  • 8
    The path to the relevant registry file is into /path/to/windisk/Windows/System32/config/RegBack/SOFTWARE Commented Aug 7, 2017 at 4:15
  • 2
    This is a great answer, I quoted it on Ask Ubuntu askubuntu.com/a/953130/75060
    – Mark Kirby
    Commented Sep 5, 2017 at 21:13
  • Thank you for this. Please make a note to check the case of the folder and file names. In my case, I had to use uppercase SOFTWARE for the file name. Commented Sep 6, 2017 at 8:27
  • If you have problems reading the system32 folder, try making a copy and supplying the path to the copy to the chntpw. Commented Oct 12, 2019 at 10:54
4

For those who are not shy to do a little bit of coding.

I found an algorithm about 10 years ago and implemented it in C# (See below)


If you just want to run it on Windows

I took the liberty to convert it to a powershell script:

$dpid = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name "DigitalProductId"

# Get the range we are interested in
$id = $dpid.DigitalProductId[52..(52+14)]

# Character table
$chars = "BCDFGHJKMPQRTVWXY2346789"

# Variable for the final product key
$pkey = ""

# Calculate the product key
for ($i=0; $i -le 24; $i++) {
    $c = 0

    for($j=14; $j -ge 0; $j--) {
        $c = ($c -shl 8) -bxor $id[$j]

        $id[$j] = [Math]::Floor($c / 24) -band 255

        $c = $c % 24
    }
    $pkey = $chars[$c] + $pkey
}
# Insert some dashes
for($i = 4; $i -gt 0; $i--) {
    $pkey = $pkey.Insert($i * 5, "-")
}
$pkey

Run this and you get your product key. (So no coding for you after all)


Original post

So this is the actual C# code I dug up and commented.

public static string ConvertDigitalProductID(string regPath, string searchKey = "DigitalProductID") {
    // Open the sub key i.E.: "Software\Microsoft\Windows NT\CurrentVersion"
    var regkey = Registry.LocalMachine.OpenSubKey(regPath, false);
    // Retreive the value of "DigitalProductId"
    var dpid = (byte[])regkey.GetValue(searchKey);
    // Prepare an array for the relevant parts
    var idpart = new byte[15];

    // Copy the relevant parts of the array
    Array.Copy(dpid, 52, idpart, 0, 15);

    // Prepare the chars that will make up the key
    var charStore = "BCDFGHJKMPQRTVWXY2346789";

    // Prepare a string for the result
    string productkey = "";

    // We need 24 iterations (one for each character)
    for(int i = 0; i < 25; i++) {

        int c = 0;
        // Go through each of the 15 bytes of our dpid
        for(int j = 14; j >= 0; j--) {
            // Shift the current byte to the left and xor in the next byte
            c = (c << 8) ^ idpart[j];

            // Leave the result of the division in the current position
            idpart[j] = (byte)(c / 24);

            // Take the rest of the division forward to the next round
            c %= 24;
        }
        // After each round, add a character from the charStore to our key
        productkey = charStore[c] + productkey;
    }

    // Insert the dashes
    for(int i = 4; i > 0; i--) {
        productkey = productkey.Insert(i * 5, "-");
    }

    return productkey;
}

You'll have to pass it Software\Microsoft\Windows NT\CurrentVersion as a Key, where it'll find the DigitalProductId

At that time MS Office Products used the same algorithm, so by providing the function with the relevant registry key it could calculate those product keys as well.

You can of course refactor the function so that it takes a byte array as input.

As for today. I just tested it on my Windows 10 Machine, and it still works.

3
  • This is a good answer but the question is very old and may not recieve many views. As you are a member, please consider adding your answer to our current Ask Ubuntu post askubuntu.com/questions/953126/…
    – Mark Kirby
    Commented Sep 6, 2017 at 12:00
  • Thank you. But i believe it would be off topic over there. I may slap together a pseudo code implementation. And refer to this post as an actual implementation.
    – MrPaulch
    Commented Sep 6, 2017 at 12:01
  • No problem, do what you think is best
    – Mark Kirby
    Commented Sep 6, 2017 at 12:02
3

Here is a Python port of the other answer (adapted for Windows 8.1). The advantage of this over chntpw is that it will work even with drives in read-only state.

Requirements:

pip install python-registry

Code:

#!/usr/bin/env python
import sys
from Registry import Registry
reg = Registry.Registry("/path/to/drive/Windows/System32/config/RegBack/SOFTWARE")
# Uncomment for registry location for Windows 7 and below:
#reg = Registry.Registry("/path/to/drive/Windows/system32/config/software")
key = reg.open("Microsoft\Windows NT\CurrentVersion")
did = bytearray([v.value() for v in key.values() if v.name() == "DigitalProductId"][0])
idpart = did[52:52+15]
charStore = "BCDFGHJKMPQRTVWXY2346789";
productkey = "";
for i in range(25):
  c = 0
  for j in range(14, -1, -1):
    c = (c << 8) ^ idpart[j]
    idpart[j] = c // 24
    c %= 24
  productkey = charStore[c] + productkey
print('-'.join([productkey[i * 5:i * 5 + 5] for i in range(5)]))
1
  • The inner loop was one iteration too short. It should work now.
    – Lenar Hoyt
    Commented Sep 26, 2018 at 16:02
0

Here is my bash implementation. I call it get_windows_key.sh works good from clonezilla. I originally posted it here https://sourceforge.net/p/clonezilla/discussion/Open_discussion/thread/979f335385/

#!/bin/bash
# written by Jeff Sadowski
# credit
###################################################
# Pavel Hruška, Scott Skahht, and Philip M for writting
# https://github.com/mrpeardotnet/WinProdKeyFinder/blob/master/WinProdKeyFind/KeyDecoder.cs
# that I got my conversion code from
#
# I used the comments on the sudo code from
# https://askubuntu.com/questions/953126/can-i-recover-my-windows-product-key- from-ubuntu
# by MrPaulch
#
# and the creator of chntpw
#
# Petter Nordahl-Hagen
# without which I would not be able to get the key in linux
#
# also the creators of ntfs-3g, linux and bash

parted -l 2>/dev/null |grep -e ntfs -e fat -e Disk|grep -v Flags
#get the first mac address that isn't a loopback address
# loopback will have all zeros
MAC=$(cat /sys/class/net/*/address|grep -v 00:00:00:00:00:00|head -n 1|sed "s/:/-/g")
if [ "$1" = "" ];then
 echo "mount the Windows share then give this script the path where you mounted it"
 exit
fi
cd $1
#
# This way will work no matter what the capitalization is
next=$(find ./ -maxdepth 1 -iname windows);cd ${next}
next=$(find ./ -maxdepth 1 -iname system32);cd ${next}
next=$(find ./ -maxdepth 1 -iname config);cd ${next}
file=$(find ./ -maxdepth 1 -iname software)
#echo $(pwd)${file:1}
#Get the necissary keys
#get the version key
VERSION=$((16#$(echo -e "cat \\Microsoft\\Windows NT\\CurrentVersion\\CurrentMajorVersionNumber\nq\n" | chntpw -e ${file}|grep "^0x"|cut -dx -f2)))
hexPid_csv_full=$(echo $(echo -e "hex \\Microsoft\\Windows NT\\CurrentVersion\\DigitalProductId\nq\n" | chntpw -e ${file}|grep "^:"|cut -b 9-55)|sed 's/ /,/g' | tr '[:u>
# get the subset 53 to 68 of the registry entry
hexPid_csv=$(echo $(echo -e "hex \\Microsoft\\Windows NT\\CurrentVersion\\DigitalProductId\nq\n" | chntpw -e ${file}|grep "^:"|cut -b 9-55)|sed 's/ /,/g' | tr '[:upper:>
echo "${hexPid_csv_full}" > /custom/DigitalProductId_${MAC}.txt
#formatted output
spread()
{
 key=$1
 echo ${key:0:5}-${key:5:5}-${key:10:5}-${key:15:5}-${key:20:5}
}
# almost a direct conversion of c# code from
# https://github.com/mrpeardotnet/WinProdKeyFinder/blob/master/WinProdKeyFind/KeyDecoder.cs
# however most of this looks similar to sudo code I found
# https://askubuntu.com/questions/953126/can-i-recover-my-windows-product-key-from-ubuntu
DecodeProductKey()
{
digits=(B C D F G H J K M P Q R T V W X Y 2 3 4 6 7 8 9)
for j in {0..15};do
#Populate the Pid array from the values found in the registry
 Pid[$j]=$((16#$(echo ${hexPid_csv}|cut -d, -f $(($j+1)))))
done
if [ "$1" = "8+" ];then
# modifications needed for getting the windows 8+ key
 isWin8=$(($((${Pid[14]}/6))&1))
 Pid[14]=$(( $(( ${Pid[14]}&247 )) | $(( $(( ${isWin8} & 2 )) * 4 )) ))
fi
key=""
last=0
for i in {24..0};do
 current=0
 for j in {14..0};do
  # Shift the current contents of c to the left by 1 byte 
  # and add it with the next byte of our id
  current=$((${current}*256))
  current=$((${Pid[$j]} + current))
  # Put the result of the divison back into the array
  Pid[$j]=$((${current}/24))
  # Calculate remainder of c
  current=$((${current}%24))
  last=${current}
 done
 # Take character at position c and prepend it to the ProductKey
 key="${digits[${current}]}${key}"
done
if [ "$1" = "8+" ];then
# another modification needed for a windows 8+ key
 key="${key:1:${last}}N${key:$((${last}+1)):24}"
 echo -n "Windows 8+ key: "
else
 echo -n "Windows 7- key: "
fi
spread "${key}"
}
if [ "$VERSION" -gt "7" ];then
 DecodeProductKey 8+
else
 DecodeProductKey
fi
0

Many thanks indeed to Lenar Hoyt for the Python solution above.

I have a PC running Windows 10 with a license that was upgraded from Windows 8, and (as per penguinjeff's bash version below) you need slight modifications to handle such licences.

Note: If you are running on Windows you can partly verify the decoded key by running slmgr -dli

On my PCs this reports "Partial Product Key: 3V66T" whereas Lenar's code (as of Sept 2018) reports a spurious key "TY4CG-JDJH7-VJ2WF-DY4X9-HCFC6"

Here's my modified version of the above which correctly (I believe) returns a key of the form "xxxxx-xxxxx-xxxxx-xxxxx-3V66T"

#!/usr/bin/env python
import sys
from Registry import Registry
reg = Registry.Registry("/path/to/drive/Windows/System32/config/RegBack/SOFTWARE")
# Uncomment for registry location for Windows 7 and below:
#reg = Registry.Registry("/path/to/drive/Windows/system32/config/software")
key = reg.open("Microsoft\Windows NT\CurrentVersion")
did = bytearray([v.value() for v in key.values() if v.name() == "DigitalProductId"][0])
isWin8 = (did[66] // 6) & 1
if isWin8:
  did[66] = (did[66] & 0xF7) | ((isWin8 & 2) * 4)

idpart = did[52:52+15]
charStore = "BCDFGHJKMPQRTVWXY2346789";
productkey = "";
lastC = 0    # isWin8 support
for i in range(25):
  c = 0
  for j in range(14, -1, -1):
    c = (c << 8) ^ idpart[j]
    idpart[j] = c // 24
    c %= 24
  productkey = charStore[c] + productkey
  lastC = c
if isWin8:
  insert = "N"
  if lastC == 0:
    productkey = insert + productkey
  else:
    keypart1 = productkey[1:1+lastC]
    productkey = productkey[1:].replace(keypart1, keypart1 + insert)

print('-'.join([productkey[i * 5:i * 5 + 5] for i in range(5)]))
0

If you are not on Debian but want to use chntpw, you can download the source from here: http://pogostick.net/~pnh/ntpasswd/. With a little makefile tweaking (removing the -m32 compiler argument), I was able to compile it on macOS.

(I wish I could comment this on @Thomas's answer, but I don't have enough rep.)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .