Translate

Archives

Revisit - Retrieve Microsoft Windows Product Key From UEFI Shell

In a February 2015 post, I published the source code for a simple UEFI shell utility which could retrieve a Microsoft Windows product license key from a system running Windows 8 or later. The code was based on the GNU EFI development and runtime libraries.

There are a number of freely available EFI development environments. GNU EFI is probably the simplest of these and is what I recommend for beginners. It’s easy to install in a Linux environment such as Fedora 23 as both runtime and development RPMs are available from the Fedora repos.

The most sophisticated EFI development environment is the EFI Development Kit, Version 2, commonly known as EDK2. This also is the official UEFI reference source. Validated development snapshots are released from time to time with UDK2015 being the latest.

Here is the modified source code for the utility which successfully builds in UDK2015:

//
//  Copyright (c) 2015  Finnbarr P. Murphy.   All rights reserved.
//
//  Display Microsoft Windows License via MSDM ACPI table 
//
//  License: BSD License
//

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/PrintLib.h>

#include <Protocol/EfiShell.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/AcpiSystemDescriptionTable.h>


#include EFI_GUID_DEFINITION (Acpi)

#undef DEBUG

#define EFI_ACPI_TABLE_GUID \
    { 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d }}
#define EFI_ACPI_20_TABLE_GUID \
    { 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 }} 

 
// Microsoft Data Management table structure
typedef struct {
    EFI_ACPI_SDT_HEADER Header;
    UINT32  SlsVersion;
    UINT32  SlsReserved;
    UINT32  SlsDataType;
    UINT32  SlsDataReserved;
    UINT32  SlsDataLength;
    CHAR8   ProductKey[30];
} EFI_ACPI_MSDM;


static VOID AsciiToUnicodeSize(CHAR8 *, UINT8, CHAR16 *);


static VOID
AsciiToUnicodeSize(CHAR8 *String, UINT8 length, CHAR16 *UniString)
{
    int len = length;
 
    while (*String != '\0' && len > 0) {
        *(UniString++) = (CHAR16) *(String++);
        len--;
    }
    *UniString = '\0';
}


static VOID 
ParseMSDM(EFI_ACPI_MSDM *Msdm, int verbose)
{
    CHAR16 Buffer[100];

    Print(L"\n");
    if (verbose) {
        AsciiToUnicodeSize((CHAR8 *)&(Msdm->Header.Signature), 4, Buffer);
        Print(L"Signature         : %s\n", Buffer);
        Print(L"Length            : %d\n", Msdm->Header.Length);
        Print(L"Revision          : %d\n", Msdm->Header.Revision);
        Print(L"Checksum          : %d\n", Msdm->Header.Checksum);
        AsciiToUnicodeSize((CHAR8 *)(Msdm->Header.OemId), 6, Buffer);
        Print(L"Oem ID            : %s\n", Buffer);
        AsciiToUnicodeSize((CHAR8 *)(Msdm->Header.OemTableId), 8, Buffer);
        Print(L"Oem Table ID      : %s\n", Buffer);
        Print(L"Oem Revision      : %d\n", Msdm->Header.OemRevision);
        AsciiToUnicodeSize((CHAR8 *)&(Msdm->Header.CreatorId), 4, Buffer);
        Print(L"Creator ID        : %s\n", Buffer);
        Print(L"Creator Revision  : %d\n", Msdm->Header.CreatorRevision);
        Print(L"SLS Version       : %d\n", Msdm->SlsVersion);
        Print(L"SLS Reserved      : %d\n", Msdm->SlsReserved);
        Print(L"SLS Data Type     : %d\n", Msdm->SlsDataType);
        Print(L"SLS Data Reserved : %d\n", Msdm->SlsDataReserved);
        Print(L"SLS Data Length   : %d\n", Msdm->SlsDataLength);
    }
    AsciiToUnicodeSize((CHAR8 *)(Msdm->ProductKey), 29, Buffer);
    if (verbose) {
        Print(L"Product Key       : %s\n", Buffer);
    } else {
        Print(L"%s\n", Buffer);
    }
    Print(L"\n");
}


static int
ParseRSDP( EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp, CHAR16* GuidStr, int verbose)
{
    EFI_ACPI_SDT_HEADER *Xsdt, *Entry;
    CHAR16 OemStr[20];
    UINT32 EntryCount;
    UINT64 *EntryPtr;

#ifdef DEBUG 
    Print(L"\n\nACPI GUID: %s\n", GuidStr);
#endif
    AsciiToUnicodeSize((CHAR8 *)(Rsdp->OemId), 6, OemStr);

#ifdef DEBUG 
    Print(L"\nFound RSDP. Version: %d  OEM ID: %s\n", (int)(Rsdp->Revision), OemStr);
#endif
    if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) {
        Xsdt = (EFI_ACPI_SDT_HEADER *)(Rsdp->XsdtAddress);
    } else {
#ifdef DEBUG 
        Print(L"ERROR: No ACPI XSDT table found.\n");
#endif
        return 1;
    }

    if (Xsdt->Signature != SIGNATURE_32 ('X', 'S', 'D', 'T')) {
#ifdef DEBUG 
        Print(L"ERROR: Invalid ACPI XSDT table found.\n");
#endif
        return 1;
    }

    AsciiToUnicodeSize((CHAR8 *)(Xsdt->OemId), 6, OemStr);
    EntryCount = (Xsdt->Length - sizeof (EFI_ACPI_SDT_HEADER)) / sizeof(UINT64);
#ifdef DEBUG 
    Print(L"Found XSDT. OEM ID: %s  Entry Count: %d\n\n", OemStr, EntryCount);
#endif

    EntryPtr = (UINT64 *)(Xsdt + 1);
    for (int Index = 0; Index < EntryCount; Index++, EntryPtr++) {
        Entry = (EFI_ACPI_SDT_HEADER *)((UINTN)(*EntryPtr));
        if (Entry->Signature == SIGNATURE_32 ('M', 'S', 'D', 'M')) {
            ParseMSDM((EFI_ACPI_MSDM *)((UINTN)(*EntryPtr)), verbose);
        }
    }

    return 0;
}


static void
Usage(void)
{
    Print(L"Usage: listmsdm [-v|--verbose]\n");
}


INTN
EFIAPI
ShellAppMain(UINTN Argc, CHAR16 **Argv)
{
    EFI_CONFIGURATION_TABLE *ect = gST->ConfigurationTable;
    EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp = NULL;
    EFI_GUID AcpiTableGuid = EFI_ACPI_TABLE_GUID;
    EFI_GUID Acpi20TableGuid = EFI_ACPI_20_TABLE_GUID;
    EFI_STATUS Status = EFI_SUCCESS;
    CHAR16 GuidStr[100];
    int Verbose = 0;


    if (Argc == 2) {
        if (!StrCmp(Argv[1], L"--verbose") ||
            !StrCmp(Argv[1], L"-v")) {
            Verbose = 1;
        }
        if (!StrCmp(Argv[1], L"--help") ||
            !StrCmp(Argv[1], L"-h") ||
            !StrCmp(Argv[1], L"-?")) {
            Usage();
            return Status;
        }
    }

    // locate RSDP (Root System Description Pointer) 
    for (int i = 0; i < gST->NumberOfTableEntries; i++) {
        if ((CompareGuid (&(gST->ConfigurationTable[i].VendorGuid), &AcpiTableGuid)) ||
            (CompareGuid (&(gST->ConfigurationTable[i].VendorGuid), &Acpi20TableGuid))) {
            if (!AsciiStrnCmp("RSD PTR ", (CHAR8 *)(ect->VendorTable), 8)) {
                UnicodeSPrint(GuidStr, sizeof(GuidStr), L"%g", &(gST->ConfigurationTable[i].VendorGuid));
                Rsdp = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)ect->VendorTable;
                ParseRSDP(Rsdp, GuidStr, Verbose); 
            }        
        }
        ect++;
    }

    if (Rsdp == NULL) {
        if (Verbose) {
            Print(L"ERROR: Could not find an ACPI RSDP table.\n");
        }
        return EFI_NOT_FOUND;
    }

    return Status;
}


If you compare the above source code with that which I published in my February 2015 blog post, you will see that significant changes were necessary to port the source code to build in the UDK2015 development environment. Can you have a single source code base which compiles successfully in UDK2015 and GNU EFI development environments? Yes you can, but you will end up with source code containing a lot of compiler preprocessor directives.

Here is the .INF build file for the utility:

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = listmsdm 
  FILE_GUID                      = 4ea87c51-7395-4dcd-0055-747010f3ce51
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = ShellCEntryLib
  VALID_ARCHITECTURES            = X64

[Sources]
  listmsdm.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec


[LibraryClasses]
  ShellCEntryLib
  ShellLib
  BaseLib
  BaseMemoryLib
  UefiLib

[Protocols]

[BuildOptions]

[Pcd]


If you are unfamiliar with the EDK2 module build information (INF) file format, do an Internet search for Intel EDK II INF File Specification. It contains all the gory details. INF files are used by EDK2 utilities to parse module build meta-data files (INF, DEC, DSC and FDF files) in order to generate AutoGen.c and AutoGen.h and Makefile files for the EDK2 build infrastructure.

Here is sample output when the utility is invoked from a UEFI shell:

FS0> listmsdm -h
Usage: listmsdm [-v|--verbose]

FS0> listmddm

NJCF7-T6T5Q-WRYF7-GY44B-VQVQ2

FS0> listmsdm -v
Signature         : MSDM
Length            : 85
Revision          : 3
Checksum          : 65
Oem ID            : LENOVO
Oem Table ID      : TP-JB   
Oem Revision      : 4480
Creator ID        : PTEC
Creator Revision  : 2
SLS Version       : 1
SLS Reserved      : 0
SLS Data Type     : 1
SLS Data Reserved : 0
SLS Data Length   : 29
Product Key       : NJCF7-T6T5Q-WRYF7-GY44B-VQVQ2


By the way, the product license key string has been altered so do not bother trying to use it!

Comments are closed.