AceInfinity
Emeritus, Contributor
Here's some code that I wrote to grab and decrypt the Windows 10 product key from the registry.
My initial problem was that this key doesn't exist under Wow64 if you're viewing the 32-bit version of the registry:
NTSTATUS 0xC0000034 = object name not found
So I needed to check for this condition and specify an additional access flag mask to specify that I want to read from the 64-bit registry.
Code can be cleaned up into a few more functions and perhaps better logging functionality for errors. This is to serve as a base.
:thumbsup2:
Code:
[plain]#include <Windows.h>
#include <stdio.h>
#include <string.h>
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define NT_SUCCESS(status) (((NTSTATUS)(status)) == STATUS_SUCCESS)
#define TD(func) T_##func
#define DynamicFunction(name, dll) TD(name) \
name = (TD(name))GetProcAddress(GetModuleHandle(dll), #name)
#define REG_KEY_BUFFER_MAX (4096 * 2)
#define OBJ_CASE_INSENSITIVE 0x00000040
typedef enum _KEY_VALUE_INFORMATION_CLASS
{
KeyValueBasicInformation = 0,
KeyValueFullInformation,
KeyValuePartialInformation,
KeyValueFullInformationAlign64,
KeyValuePartialInformationAlign64,
MaxKeyValueInfoClass
} KEY_VALUE_INFORMATION_CLASS;
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
PVOID RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
typedef struct _KEY_VALUE_FULL_INFORMATION
{
ULONG TitleIndex;
ULONG Type;
ULONG DataOffset;
ULONG DataLength;
ULONG NameLength;
WCHAR Buffer[REG_KEY_BUFFER_MAX];
} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION;
typedef NTSTATUS (NTAPI *TD(NtOpenKey))
(
PHANDLE KeyHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes
);
typedef NTSTATUS (NTAPI *TD(NtQueryValueKey))
(
HANDLE KeyHandle,
PUNICODE_STRING ValueName,
KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
PVOID KeyValueInformation,
ULONG Length,
PULONG ResultLength
);
BOOL __declspec(naked) WINAPI IsWow64(void)
{
__asm
{
MOV EAX, FS:[0xC0] ; wow64cpu!X86SwitchTo64BitMode
SHR EAX, 30
RET
}
}
#if UNICODE
# define tstrlen wcslen
#else
# define tstrlen strlen
#endif
BOOL OpenKeyForQuery(PHANDLE pKey, TCHAR *keyName)
{
NTSTATUS status;
HANDLE hKey;
OBJECT_ATTRIBUTES objAttribs;
DynamicFunction(NtOpenKey, TEXT("ntdll.dll"));
UNICODE_STRING wKeyName;
wKeyName.Buffer = keyName;
wKeyName.Length = (USHORT)tstrlen(keyName) * sizeof(*keyName);
objAttribs.Length = sizeof(OBJECT_ATTRIBUTES);
objAttribs.RootDirectory = NULL;
objAttribs.Attributes = OBJ_CASE_INSENSITIVE;
objAttribs.ObjectName = &wKeyName;
objAttribs.SecurityDescriptor = NULL;
objAttribs.SecurityQualityOfService = NULL;
ACCESS_MASK access = KEY_QUERY_VALUE;
if (IsWow64()) access |= KEY_WOW64_64KEY;
status = NtOpenKey(&hKey, access, &objAttribs);
if (NT_SUCCESS(status))
{
*pKey = hKey;
return TRUE;
}
printf("NtOpenKey() -> NTSTATUS: 0x%X\n", status); /* DEBUG */
return FALSE;
}
BOOL QueryValueKey(HANDLE hKey, TCHAR *valueName, PVOID pResultBuffer, PULONG pResultBufferLen)
{
NTSTATUS status;
DynamicFunction(NtQueryValueKey, TEXT("ntdll.dll"));
UNICODE_STRING wValueName;
wValueName.Buffer = valueName;
wValueName.Length = (USHORT)tstrlen(valueName) * sizeof(*valueName);
status = NtQueryValueKey(hKey, &wValueName, KeyValueFullInformation, pResultBuffer, *pResultBufferLen, pResultBufferLen);
if (NT_SUCCESS(status))
{
return TRUE;
}
printf("NtQueryValueKey() -> NTSTATUS: 0x%X\n", status); /* DEBUG */
return FALSE;
}
#define KEY_OFFSET 52
#define KEY_SECTION_LEN 5
#define DIGITAL_PID_ENCRYPTED_LEN 24
#define DIGITAL_PID_DECRYPTED_LEN 14
BOOL DecodeDigitalPID(BYTE *binData, ULONG binDataLen, PBYTE *pResultKey)
{
int i, j, n;
const char *Base24 = "BCDFGHJKMPQRTVWXY2346789";
const size_t Base24Len = strlen(Base24);
BYTE buf[DIGITAL_PID_ENCRYPTED_LEN + 1] = { 0 };
static BYTE key[DIGITAL_PID_ENCRYPTED_LEN + KEY_SECTION_LEN + 1] = { 0 };
BYTE *digitalPID = binData + KEY_OFFSET;
BOOL containsN = (digitalPID[DIGITAL_PID_DECRYPTED_LEN] >> 3) & 1;
printf("\nDIGITAL PID:\n");
for (i = 0; i <= DIGITAL_PID_DECRYPTED_LEN; ++i)
{
printf("0x%.2X ", digitalPID[i]);
}
puts("\n\n");
digitalPID[DIGITAL_PID_DECRYPTED_LEN] &= 0xF7;
for (i = DIGITAL_PID_ENCRYPTED_LEN; i >= 0; --i)
{
n = 0;
for (j = DIGITAL_PID_DECRYPTED_LEN; j >= 0; --j)
{
n = (UINT16)(n << 8 ^ digitalPID[j]);
digitalPID[j] = (BYTE)(n / Base24Len);
n = (UINT16)(n % Base24Len);
}
buf[i] = Base24[n];
}
if (containsN)
{
n = 0;
for (i = 0; i < DIGITAL_PID_ENCRYPTED_LEN; ++i)
{
if (*buf != Base24[i]) continue;
n = i;
break;
}
memcpy(key, buf + 1, n);
memcpy(key + n, buf + n, DIGITAL_PID_ENCRYPTED_LEN - n + 1);
key[n] = 'N';
}
else
{
memcpy(key, buf, DIGITAL_PID_ENCRYPTED_LEN);
}
if (pResultKey)
{
*pResultKey = key;
}
return TRUE;
}
int main(void)
{
ULONG i;
BOOL ret = FALSE;
HANDLE hKey = NULL;
KEY_VALUE_FULL_INFORMATION keyValueInfo;
ULONG keyValueInfolen = sizeof(keyValueInfo);
BYTE *binData = NULL;
BYTE *pKey = NULL;
ret = OpenKeyForQuery(&hKey, TEXT("\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\"));
if (ret)
{
ret = QueryValueKey(hKey, TEXT("DigitalProductId"), (PVOID)&keyValueInfo, &keyValueInfolen);
if (ret)
{
puts("DATA:");
binData = (BYTE *)&keyValueInfo + keyValueInfo.DataOffset;
for (i = 0; i < keyValueInfo.DataLength; ++i)
{
printf("0x%.2X ", binData[i]);
}
putchar('\n');
ret = DecodeDigitalPID(binData, keyValueInfo.DataLength, &pKey);
if (ret)
{
fputs("PRODUCT KEY: ", stdout);
for (i = 0; i <= DIGITAL_PID_ENCRYPTED_LEN; ++i)
{
putchar(pKey[i]);
if (i < DIGITAL_PID_ENCRYPTED_LEN && (i + 1) % 5 == 0) putchar('-');
}
putchar('\n');
}
}
else
{
fprintf(stdout, "[!] Failed to query registry key value\n");
}
}
else
{
fprintf(stdout, "[!] Failed to open registry key\n");
}
if (hKey) { CloseHandle(hKey); }
return ret;
}
[/plain]
My initial problem was that this key doesn't exist under Wow64 if you're viewing the 32-bit version of the registry:
NTSTATUS 0xC0000034 = object name not found
So I needed to check for this condition and specify an additional access flag mask to specify that I want to read from the 64-bit registry.
Code:
[plain]ACCESS_MASK access = KEY_QUERY_VALUE;
if (IsWow64()) access |= KEY_WOW64_64KEY;[/plain]
Code can be cleaned up into a few more functions and perhaps better logging functionality for errors. This is to serve as a base.
:thumbsup2:
Last edited: