从主板获取Windows 8产品密钥

我的新笔记本电脑预装了Windows 8。天真地说,我只是格式化了硬盘并安装了老旧的Ubuntu系统。现在我想再次安装Windows 8以进行双系统启动,但我没有光盘,并且要下载ISO文件需要一个产品密钥。这个密钥不再在笔记本背面,而是在主板上的某个地方。
有没有办法在Ubuntu系统中从主板上恢复产品密钥?
5个回答

另一种不需要查看大量输出的方法是:
sudo acpidump -b -t MSDM | dd bs=1 skip=56 2>/dev/null;echo

acpidump会将表格转储出来(默认以十六进制格式),但是使用-b选项可以输出原始数据。由于我们只需要表格的最后一部分,所以将输出导入到dd中,并跳过不必要的垃圾数据。最后,在末尾添加一个echo命令,使其更适合终端使用 =D
另外,acpidump -t MSDM也可以实现相同的功能,但是密钥被分成多行,复制起来比较困难。

根据Lekensteyn的更新:

新版本的Ubuntu中发货的acpidump与上述描述有所不同。-b标志使acpidump在任何情况下都会写入文件,因此另一种方法是使用以下命令:

sudo tail -c+57 /sys/firmware/acpi/tables/MSDM

一个合法的Windows 8安装程序应该能够自动检测ACPI中的密钥,并继续使用内置密钥进行安装。
然而,需要注意的是,我曾尝试使用此方法在虚拟机中使用自己的产品密钥安装Win8,但它自动停用并显示该产品密钥正在使用中。所以,实际上这个方法几乎没有什么用处。由于Win8 OEM密钥设计为与特定计算机绑定,如果您要求Microsoft注销密钥以便在虚拟机中或其他计算机上使用,您将遇到困难。
唯一的办法是,如果您从未启动过Win8,或者在启动时未连接到网络。即使如此,如果您的虚拟机/新计算机允许连接到网络,它将自动注册密钥,使您的实际安装无法使用。

刚刚试了一下,上面的命令截断了一个字符。我使用了 sudo acpidump -b -t MSDM | dd bs=1 skip=56 2>/dev/null;echo,然后成功获取到完整的密钥。 - Andrew C
你是对的,抱歉。我会更新我的回答。 - Chuck R
1“-b”选项是特定于内核树中包含的“acpidump”工具的。较新版本的Ubuntu使用不同的“acpidump”工具(来自“iasl”),其具有不同的选项。我无法测试此命令,但它应该可以工作:“sudo acpidump -n HPET | tail -n+2 | xxd -r | head -c+57”。另一种方法:sudo tail -c+57 /sys/firmware/acpi/tables/MSDM。 - Lekensteyn
@Lekensteyn 我最近也注意到这个问题,当我和微软通话时。如果你使用-b选项,现在它会默认将数据写入文件中,原因不明。我想知道是否有一种方法可以在管道上没有更多数据时销毁它... 这是另一个话题,留待以后讨论。你的第一个命令对我来说不起作用,但第二个命令很好。我会更新我的答案以包含它 =) - Chuck R
1在Ubuntu 16.04 LTS上使用更新的版本: "sudo tail -c+57 /sys/firmware/acpi/tables/MSDM"可以确认,我成功地从一台三星笔记本电脑中获取了我的Windows密钥 =) - Valross.nu

通常,原始设备制造商在只读存储器上预装了一个电子密钥。Windows会识别并自动激活您的安装。所以,通常情况下,您不需要知道这个代码。然而,您可能会在使用过程中看到一些与此相关的痕迹。
sudo dmidecode

列为OEM特定类型的编码/加密文件,可能包含它。像惠普和戴尔这样的主要OEM厂商使用这种方式。请在Windows网站上咨询更多细节,这里不是正确的地方。我记得的唯一细节是需要使用OEM版本的Windows安装光盘(即非零售版)。

我记得以前在联想Thinkpad上使用过dmidecode,但是在这台联想Ideapad上似乎没有任何地方包含许可证密钥。最终我在/sys/firmware/acpi/tables/MSDM中找到了它,正如下面另一个答案中的Chuck R所提到的。 - Luc

 sudo tail -c+57 /sys/firmware/acpi/tables/MSDM

这让我得到了我在MSI笔记本上的OEM Windows 8产品密钥。

你也可以使用这个代码,如果上面的代码不适用于您,或者您只想查看带有密钥的十六进制输出,则此代码也可使用。它类似于bless hex二进制编辑器。Windows将以通常的格式HAN50-0L00M-4D31T-CR4ZY显示其密钥。5个字母或数字分为5组。
$ ls /sys/firmware/acpi/tables
$ sudo hd /sys/firmware/acpi/tables/MSDM

00000000  ha ns oo lo oe at es ap  pl le sa uc ef or li fe  |Key in area|
00000010  cu si ca nb ro ha ms am  cu si ca nb ro ha ms am  |In key area|
00000020  it sj us ho wz ir ol lz  it sj us ho wz ir ol lz  |Area in key|
00000000  ha ns oo lo oe at es ap  pl le sa uc ef or li fe  |It is 5 x 5|
00000010  cu si ca nb ro ha ms am  cu si ca nb ro ha ms am  |Key in area|
00000020  it sj us ho wz ir ol lz  it sj us ho wz ir ol lz  |In key area|
00000050  ha ns oo lo ow az he re                           |Area in key|
00000055                                                    |It is 5 x 5|

运行以下命令将以标准的微软格式导出产品密钥。
sudo hexdump -s 56 -e '"MSDM key: " /29 "%s\n"' /sys/firmware/acpi/tables/MSDM

所以我看到这里的其他答案,觉得需要插一句话。 找到了。
strings /sys/firmware/acpi/tables/MSDM

如果原始密钥仍在使用,这个方法非常有效。然而,我有一些系统是随房屋附带的,你需要从注册表中获取当前密钥。
winmount=/mnt
echo "hex \\Microsoft\\Windows NT\\CurrentVersion\\DigitalProductId\nq\nq" | chntpw -e ${winmount}/Windows/System32/config/SOFTWARE

然后我们需要通过算法运行它以获取密钥。
我在https://github.com/mrpeardotnet/WinProdKeyFinder/blob/master/WinProdKeyFind/KeyDecoder.cs中找到了一些代码。
    /// <summary>
    /// Decodes Windows Product Key from the DigitalProductId. 
    /// This method applies to DigitalProductId from Windows 7 or lower versions of Windows.
    /// </summary>
    /// <param name="digitalProductId">DigitalProductId to decode</param>
    /// <returns>Decoded Windows Product Key as a string</returns>
    private static string DecodeProductKey(byte[] digitalProductId)
    {
        const int keyStartIndex = 52;
        const int keyEndIndex = keyStartIndex + 15;
        var digits = new[]
        {
            'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R',
            'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9',
        };
        const int decodeLength = 29;
        const int decodeStringLength = 15;
        var decodedChars = new char[decodeLength];
        var hexPid = new ArrayList();
        for (var i = keyStartIndex; i <= keyEndIndex; i++)
        {
            hexPid.Add(digitalProductId[i]);
        }
        for (var i = decodeLength - 1; i >= 0; i--)
        {
            // Every sixth char is a separator.
            if ((i + 1) % 6 == 0)
            {
                decodedChars[i] = '-';
            }
            else
            {
                // Do the actual decoding.
                var digitMapIndex = 0;
                for (var j = decodeStringLength - 1; j >= 0; j--)
                {
                    var byteValue = (digitMapIndex << 8) | (byte)hexPid[j];
                    hexPid[j] = (byte)(byteValue / 24);
                    digitMapIndex = byteValue % 24;
                    decodedChars[i] = digits[digitMapIndex];
                }
            }
        }
        return new string(decodedChars);
    }

    /// <summary>
    /// Decodes Windows Product Key from the DigitalProductId. 
    /// This method applies to DigitalProductId from Windows 8 or newer versions of Windows.
    /// </summary>
    /// <param name="digitalProductId">DigitalProductId to decode</param>
    /// <returns>Decoded Windows Product Key as a string</returns>
    public static string DecodeProductKeyWin8AndUp(byte[] digitalProductId)
    {
        var key = String.Empty;
        const int keyOffset = 52;
        var isWin8 = (byte)((digitalProductId[66] / 6) & 1);
        digitalProductId[66] = (byte)((digitalProductId[66] & 0xf7) | (isWin8 & 2) * 4);

        const string digits = "BCDFGHJKMPQRTVWXY2346789";
        var last = 0;
        for (var i = 24; i >= 0; i--)
        {
            var current = 0;
            for (var j = 14; j >= 0; j--)
            {
                current = current*256;
                current = digitalProductId[j + keyOffset] + current;
                digitalProductId[j + keyOffset] = (byte)(current/24);
                current = current%24;
                last = current;
            }
            key = digits[current] + key;
        }

        var keypart1 = key.Substring(1, last);
        var keypart2 = key.Substring(last + 1, key.Length - (last + 1));
        key = keypart1 + "N" + keypart2;

        for (var i = 5; i < key.Length; i += 6)
        {
            key = key.Insert(i, "-");
        }

        return key;
    }

我打算尝试解码算法并用bash编写它。 DMI输出似乎是用于解码密钥的旧算法(小于win8)。我还没有找到使用新算法(大于win7)的选项。