Project restructure
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// Created by Andrew on 24/05/2023.
|
||||
//
|
||||
|
||||
#include "header.h"
|
||||
|
||||
#define BINK_RETAIL MAKEINTRESOURCEW(1)
|
||||
#define BINK_OEM MAKEINTRESOURCEW(2)
|
||||
|
||||
#define RT_BINK L"BINK"
|
||||
|
||||
/*
|
||||
Bink resource doesn't exist
|
||||
The file you selected isn't a library
|
||||
Bink resource is invalid
|
||||
*/
|
||||
typedef struct _EC_BYTE_POINT {
|
||||
CHAR x[256]; // x-coordinate of the point on the elliptic curve.
|
||||
CHAR y[256]; // y-coordinate of the point on the elliptic curve.
|
||||
} EC_BYTE_POINT;
|
||||
|
||||
typedef struct _BINKHDR {
|
||||
// BINK version - not stored in the resource.
|
||||
ULONG32 dwVersion;
|
||||
|
||||
// Original BINK header.
|
||||
ULONG32 dwID;
|
||||
ULONG32 dwSize;
|
||||
ULONG32 dwHeaderLength;
|
||||
ULONG32 dwChecksum;
|
||||
ULONG32 dwDate;
|
||||
ULONG32 dwKeySizeInDWORDs;
|
||||
ULONG32 dwHashLength;
|
||||
ULONG32 dwSignatureLength;
|
||||
|
||||
// Extended BINK header. (Windows Server 2003+)
|
||||
ULONG32 dwAuthCodeLength;
|
||||
ULONG32 dwProductIDLength;
|
||||
} BINKHDR;
|
||||
|
||||
typedef struct _BINKDATA {
|
||||
CHAR p[256]; // Finite Field order p.
|
||||
CHAR a[256]; // Elliptic Curve parameter a.
|
||||
CHAR b[256]; // Elliptic Curve parameter b.
|
||||
|
||||
EC_BYTE_POINT G; // Base point (Generator) G.
|
||||
EC_BYTE_POINT K; // Public key K.
|
||||
EC_BYTE_POINT I; // Inverse of the public key K.
|
||||
} BINKDATA;
|
||||
|
||||
typedef struct _BINKEY {
|
||||
BINKHDR header;
|
||||
BINKDATA data;
|
||||
} BINKEY;
|
||||
|
||||
DWORD extractBINKResource(HMODULE hLibrary, BYTE **pData) {
|
||||
HRSRC hRes = FindResourceW(hLibrary, BINK_OEM, RT_BINK);
|
||||
DWORD dwSize = 0;
|
||||
|
||||
if (hRes != NULL) {
|
||||
dwSize = SizeofResource(hLibrary, hRes);
|
||||
*pData = (BYTE *)LoadResource(hLibrary, hRes);
|
||||
}
|
||||
|
||||
return dwSize;
|
||||
}
|
||||
|
||||
BYTE hexToDecDigit(CHAR nDigit) {
|
||||
nDigit = toupper(nDigit);
|
||||
|
||||
if (nDigit >= '0' && nDigit <= '9')
|
||||
return nDigit - '0';
|
||||
|
||||
else
|
||||
return nDigit - 'A' + 10;
|
||||
}
|
||||
|
||||
ULONG32 byteToInteger(BYTE *pByte) {
|
||||
return hexToDecDigit(pByte[0]) << 4 + hexToDecDigit(pByte[1]);
|
||||
}
|
||||
|
||||
void reverseBytes(CONST BYTE *pBytes, ULONG32 nBytes, BYTE *pReversed) {
|
||||
for (int i = nBytes - 1; i >= 0; i--) {
|
||||
memcpy((BYTE *)&pBytes[i * 2], (BYTE *)&pReversed[(nBytes - i + 1) * 2], 2 * sizeof(BYTE));
|
||||
}
|
||||
}
|
||||
|
||||
ULONG32 ulToInteger(BYTE *pUL, BOOL bLittleEndian) {
|
||||
BYTE pULCopy[8] = { 0 };
|
||||
ULONG32 nUL = 0;
|
||||
|
||||
if (pUL == NULL)
|
||||
return 0;
|
||||
|
||||
if (bLittleEndian)
|
||||
reverseBytes(pUL, 4, pULCopy);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
nUL += byteToInteger(&pULCopy[i * 2]);
|
||||
}
|
||||
|
||||
return nUL;
|
||||
}
|
||||
|
||||
void decodeBINKResource(BYTE *pData, ULONG32 nLength, BINKEY *pBINK) {
|
||||
ULONG32 nBlockBytes = 4;
|
||||
|
||||
// If BINK is incomplete, return.
|
||||
if (nLength < 0x170) return;
|
||||
|
||||
ulToInteger(pData, TRUE);
|
||||
|
||||
/*/ Read BINK header.
|
||||
for (ULONG32 nOffset = 0; nOffset < sizeof(BINKHDR); nOffset += nBlockBytes) {
|
||||
pBINK[nOffset] =
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
void base(WCHAR *pPath) {
|
||||
HMODULE pIDgen = LoadLibraryExW(pPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
||||
|
||||
if (pIDgen == NULL)
|
||||
return;
|
||||
|
||||
BYTE *pBuffer = NULL;
|
||||
ULONG32 nLength = extractBINKResource(pIDgen, &pBuffer);
|
||||
|
||||
if (nLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
BINKEY pBINK = { 0 };
|
||||
|
||||
decodeBINKResource(pBuffer, nLength, &pBINK);
|
||||
|
||||
FreeLibrary(pIDgen);
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
//
|
||||
// Created by Andrew on 09/04/2023.
|
||||
//
|
||||
|
||||
#ifndef KEYGEN_HEADER_H
|
||||
#define KEYGEN_HEADER_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#pragma warning(disable: 6387)
|
||||
|
||||
#define PK_LENGTH 25
|
||||
#define NULL_TERMINATOR 1
|
||||
|
||||
#define FIELD_BITS 384
|
||||
#define FIELD_BYTES (FIELD_BITS / 8)
|
||||
|
||||
#define FIELD_BITS_2003 512
|
||||
#define FIELD_BYTES_2003 (FIELD_BITS_2003 / 8)
|
||||
|
||||
#define SHA_MSG_LENGTH_XP (4 + 2 * FIELD_BYTES)
|
||||
#define SHA_MSG_LENGTH_2003 (3 + 2 * FIELD_BYTES_2003)
|
||||
|
||||
#define NEXTSNBITS(field, n, offset) (((QWORD)(field) >> (offset)) & ((1ULL << (n)) - 1))
|
||||
#define FIRSTNBITS(field, n) NEXTSNBITS((field), (n), 0)
|
||||
|
||||
#define HIBYTES(field, bytes) NEXTSNBITS((QWORD)(field), ((bytes) * 8), ((bytes) * 8))
|
||||
#define LOBYTES(field, bytes) FIRSTNBITS((QWORD)(field), ((bytes) * 8))
|
||||
|
||||
#define BYDWORD(n) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
|
||||
#define BITMASK(n) ((1ULL << (n)) - 1)
|
||||
|
||||
#define IDC_BUTTON1 1000
|
||||
#define IDC_BUTTON2 1001
|
||||
#define IDC_BUTTON3 1002
|
||||
#define IDC_BUTTON4 1003
|
||||
|
||||
#define IDC_COMBO1 1020
|
||||
|
||||
#define IDC_RADIO1 1030
|
||||
#define IDC_RADIO2 1031
|
||||
|
||||
#define IDC_CHECK1 1036
|
||||
|
||||
#define IDC_EDIT1 1040
|
||||
|
||||
#define IDC_INPUT1 1060
|
||||
#define IDC_INPUT2 1061
|
||||
#define IDC_INPUT3 1062
|
||||
|
||||
#define IDC_IMAGE1 1080
|
||||
#define IDC_IMAGE2 1081
|
||||
|
||||
#define IDC_LABEL1 1105
|
||||
#define IDC_LABEL2 1106
|
||||
#define IDC_LABEL3 1107
|
||||
#define IDC_LABEL4 1108
|
||||
#define IDC_LABEL5 1109
|
||||
#define IDC_LABEL6 1110
|
||||
|
||||
typedef uint64_t QWORD;
|
||||
|
||||
extern char pCharset[];
|
||||
|
||||
extern const char pXP[];
|
||||
extern const long aXP;
|
||||
extern const long bXP;
|
||||
|
||||
// xp.cpp
|
||||
VOID unpackXP(
|
||||
QWORD(&pRaw)[2],
|
||||
BOOL &pUpgrade,
|
||||
DWORD &pChannelID,
|
||||
DWORD &pSequence,
|
||||
DWORD &pHash,
|
||||
QWORD &pSignature
|
||||
);
|
||||
|
||||
VOID packXP(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL pUpgrade,
|
||||
DWORD pChannelID,
|
||||
DWORD pSequence,
|
||||
DWORD pHash,
|
||||
QWORD pSignature
|
||||
);
|
||||
|
||||
VOID generateXPKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pChannelID,
|
||||
DWORD pSequence,
|
||||
BOOL pUpgrade,
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR]
|
||||
);
|
||||
|
||||
BOOL keyXP(
|
||||
CHAR(&pKey)[PK_LENGTH + NULL_TERMINATOR],
|
||||
DWORD nChannelID,
|
||||
DWORD nSequence,
|
||||
BOOL bUpgrade
|
||||
);
|
||||
|
||||
|
||||
// server.cpp
|
||||
VOID unpackServer(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL &pUpgrade,
|
||||
DWORD &pChannelID,
|
||||
DWORD &pHash,
|
||||
QWORD &pSignature,
|
||||
DWORD &pAuthInfo
|
||||
);
|
||||
|
||||
VOID packServer(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL pUpgrade,
|
||||
DWORD pChannelID,
|
||||
DWORD pHash,
|
||||
QWORD pSignature,
|
||||
DWORD pAuthInfo
|
||||
);
|
||||
|
||||
BOOL verifyServerKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
CHAR (&cdKey)[PK_LENGTH + NULL_TERMINATOR]
|
||||
);
|
||||
|
||||
VOID generateServerKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pChannelID,
|
||||
DWORD pAuthInfo,
|
||||
BOOL pUpgrade,
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR]
|
||||
);
|
||||
|
||||
BOOL keyServer(
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR],
|
||||
DWORD nChannelID,
|
||||
DWORD nAuthInfo,
|
||||
BOOL bUpgrade
|
||||
);
|
||||
|
||||
|
||||
// utilities.cpp
|
||||
void endian(byte *data, int length);
|
||||
DWORD randomRange(DWORD dwLow, DWORD dwHigh);
|
||||
|
||||
void stopAudio();
|
||||
bool playAudio(HINSTANCE hInstance, WCHAR *lpName, UINT bFlags);
|
||||
|
||||
EC_GROUP *initializeEllipticCurve(
|
||||
const char *pSel,
|
||||
long aSel,
|
||||
long bSel,
|
||||
const char *generatorXSel,
|
||||
const char *generatorYSel,
|
||||
const char *publicKeyXSel,
|
||||
const char *publicKeyYSel,
|
||||
BIGNUM *genOrderSel,
|
||||
BIGNUM *privateKeySel,
|
||||
EC_POINT **genPoint,
|
||||
EC_POINT **pubPoint
|
||||
);
|
||||
|
||||
int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
|
||||
|
||||
// key.cpp
|
||||
bool unbase24(BYTE *byteSeq, CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR]);
|
||||
void base24(BYTE *byteSeq, CHAR(&pKey)[PK_LENGTH + NULL_TERMINATOR]);
|
||||
|
||||
VOID formatXP(BOOL bUpgrade, WCHAR *pBSection, WCHAR *pCSection, WCHAR *pText);
|
||||
VOID formatServer(BOOL bUpgrade, WCHAR *pBSection, WCHAR *pAuthSection, WCHAR *pText);
|
||||
|
||||
|
||||
// windows.cpp
|
||||
bool InitializeWindow(HINSTANCE hInstance);
|
||||
|
||||
|
||||
// bink.cpp
|
||||
void base(WCHAR *pPath);
|
||||
|
||||
#endif //KEYGEN_HEADER_H
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// Created by Andrew on 09/04/2023.
|
||||
//
|
||||
|
||||
#include "header.h"
|
||||
|
||||
/* Converts from CD-key to a byte sequence. */
|
||||
bool unbase24(BYTE *byteSeq, CHAR(&pKey)[PK_LENGTH + NULL_TERMINATOR]) {
|
||||
BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{};
|
||||
BIGNUM *y = BN_new();
|
||||
|
||||
BN_zero(y);
|
||||
|
||||
// Remove dashes from the CD-key and put it into a Base24 byte array.
|
||||
for (int i = 0, k = 0; i < strlen(pKey) && k < PK_LENGTH; i++) {
|
||||
for (int j = 0; j < strlen(pCharset); j++) {
|
||||
if (pKey[i] == pCharset[j]) {
|
||||
pDecodedKey[k++] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the k-index hasn't been incremented, and it's due to the key being garbage, quit.
|
||||
if (pKey[i] != '-' && k == i) return false;
|
||||
}
|
||||
|
||||
// Empty byte sequence.
|
||||
memset(byteSeq, 0, 16);
|
||||
|
||||
// Calculate the weighed sum of byte array elements.
|
||||
for (int i = 0; i < PK_LENGTH; i++) {
|
||||
BN_mul_word(y, strlen(pCharset));
|
||||
BN_add_word(y, pDecodedKey[i]);
|
||||
}
|
||||
|
||||
// Acquire length.
|
||||
int n = BN_num_bytes(y);
|
||||
|
||||
// Place the generated code into the byte sequence.
|
||||
BN_bn2bin(y, byteSeq);
|
||||
BN_free(y);
|
||||
|
||||
// Reverse the byte sequence.
|
||||
endian(byteSeq, n);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Converts from byte sequence to the CD-key. */
|
||||
void base24(BYTE *byteSeq, CHAR(&pKey)[PK_LENGTH + NULL_TERMINATOR]) {
|
||||
BYTE rbyteSeq[16];
|
||||
BIGNUM *z;
|
||||
|
||||
// Copy byte sequence to the reversed byte sequence.
|
||||
memcpy(rbyteSeq, byteSeq, sizeof(rbyteSeq));
|
||||
|
||||
// Skip trailing zeroes and reverse y.
|
||||
int length;
|
||||
|
||||
for (length = 15; rbyteSeq[length] == 0; length--);
|
||||
endian(rbyteSeq, ++length);
|
||||
|
||||
// Convert reversed byte sequence to BigNum z.
|
||||
z = BN_bin2bn(rbyteSeq, length, nullptr);
|
||||
|
||||
// Divide z by 24 and convert the remainder to a CD-key char.
|
||||
pKey[PK_LENGTH] = '\0';
|
||||
|
||||
for (int i = PK_LENGTH - 1; i >= 0; i--)
|
||||
pKey[i] = pCharset[BN_div_word(z, 24)];
|
||||
|
||||
BN_free(z);
|
||||
}
|
||||
|
||||
/* Formats Windows XP key output. */
|
||||
void formatXP(BOOL bUpgrade, WCHAR *pBSection, WCHAR *pCSection, WCHAR *pText) {
|
||||
WCHAR pDashedKey[PK_LENGTH + 4 + NULL_TERMINATOR]{};
|
||||
INT pSSection = 0;
|
||||
|
||||
for (int i = 0; i < wcslen(pCSection); i++)
|
||||
pSSection -= pCSection[i] - '0';
|
||||
|
||||
while (pSSection < 0)
|
||||
pSSection += 7;
|
||||
|
||||
CHAR pKey[PK_LENGTH + NULL_TERMINATOR]{};
|
||||
DWORD nChannelID = wcstoul(pBSection, nullptr, 10),
|
||||
nSequence = wcstoul(pCSection, nullptr, 10);
|
||||
|
||||
BOOL bValid = keyXP(pKey, nChannelID, nSequence, bUpgrade);
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature;
|
||||
|
||||
DWORD pChannelID,
|
||||
pSequence,
|
||||
pSerial,
|
||||
pHash;
|
||||
|
||||
BOOL pUpgrade;
|
||||
|
||||
unbase24((BYTE *)pRaw, pKey);
|
||||
unpackXP(pRaw, pUpgrade, pChannelID, pSequence, pHash, pSignature);
|
||||
|
||||
pSerial = pChannelID * 1'000'000 + pSequence;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
wsprintfW(pDashedKey, L"%s%s%.5S", pDashedKey, i != 0 ? L"-" : L"", &pKey[5 * i]);
|
||||
|
||||
swprintf(
|
||||
pText,
|
||||
L"PRODUCT ID:\tPPPPP-%03d-%06d%d-23XXX\r\n\r\nBYTECODE:\t%016llX %016llX\r\nUPGRADE:\t%s\r\nSERIAL:\t\t0x%lX (%d)\r\nHASH:\t\t0x%lX\r\nSIGNATURE:\t0x%llX\r\nCURVE POINT:\t%s\r\n\r\n\r\n%s\r\n",
|
||||
pChannelID,
|
||||
pSequence,
|
||||
pSSection,
|
||||
pRaw[1], pRaw[0],
|
||||
pUpgrade ? L"TRUE" : L"FALSE",
|
||||
pSerial, pSerial,
|
||||
pHash,
|
||||
pSignature,
|
||||
bValid ? L"TRUE" : L"FALSE",
|
||||
pDashedKey
|
||||
);
|
||||
}
|
||||
|
||||
/* Formats Windows Server 2003 key output. */
|
||||
void formatServer(BOOL bUpgrade, WCHAR *pBSection, WCHAR *pAuthSection, WCHAR *pText) {
|
||||
WCHAR pDashedKey[32]{};
|
||||
|
||||
CHAR pKey[PK_LENGTH + NULL_TERMINATOR]{};
|
||||
DWORD nChannelID = wcstoul(pBSection, nullptr, 10);
|
||||
DWORD nAuthInfo = wcstoul(pAuthSection, nullptr, 0) % 0x400;
|
||||
|
||||
BOOL bValid = keyServer(pKey, nChannelID, nAuthInfo, bUpgrade);
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature;
|
||||
|
||||
DWORD pHash,
|
||||
pChannelID,
|
||||
pAuthInfo;
|
||||
|
||||
BOOL pUpgrade;
|
||||
|
||||
unbase24((BYTE *)pRaw, pKey);
|
||||
unpackServer(pRaw, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
swprintf(pDashedKey, L"%s%s%.5S", pDashedKey, i != 0 ? L"-" : L"", &pKey[5 * i]);
|
||||
|
||||
swprintf(
|
||||
pText,
|
||||
L"PRODUCT ID:\tPPPPP-%03d-CCCCCCS-45XXX\r\n\r\nBYTECODE:\t%016llX %016llX\r\nUPGRADE:\t%s\r\nCHANNEL ID:\t0x%lX (%d)\r\nHASH:\t\t0x%lX\r\nSIGNATURE:\t0x%llX\r\nAUTHINFO:\t0x%03lX\r\nCURVE POINT:\t%s\r\n\r\n%s\r\n",
|
||||
pChannelID,
|
||||
pRaw[1], pRaw[0],
|
||||
pUpgrade ? L"TRUE" : L"FALSE",
|
||||
pChannelID, pChannelID,
|
||||
pHash,
|
||||
pSignature,
|
||||
pAuthInfo,
|
||||
bValid ? L"TRUE" : L"FALSE",
|
||||
pDashedKey
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Windows XP CD Key Verification/Generator by z22
|
||||
Rewritten by Endermanch
|
||||
*/
|
||||
|
||||
/*
|
||||
* PK: VX8CG-8KC6V-PVPMD-GKPPH-GC7W8
|
||||
*
|
||||
* The Windows XP product key is composed of 25 characters. The dashes store no information.
|
||||
* The product key is encoded in Base24 with an alphabet of "BCDFGHJKMPQRTVWXY2346789" in order
|
||||
* to avoid ambiguous characters (e.g. "I" and "1", "0" and "O").
|
||||
*
|
||||
* To convert a 25-digit key to binary data, we need to:
|
||||
* 1. Think of the key as of an array of bytes. Then convert the concatenated key VX8CG8KC6VPVPMDGKPPHGC7W8
|
||||
* into its Base24 representation ('B' = 0, 'C' = 1, 'D' = 2, ...) -> [ 13, 15, 22, 1, 4, ... ].
|
||||
* 2. Compute the decoded array in little-endiannessConvert.
|
||||
* 3. The decoded result is divided into sections:
|
||||
* - 12 bits -> OS Family
|
||||
* - 31 bits -> Hash
|
||||
* - 62 bits -> Signature
|
||||
* - 9 bits -> Prefix
|
||||
*
|
||||
* Product ID: AAAAA-BBB-CCCCCCC-DDEEE
|
||||
*
|
||||
* digits | length | encoding
|
||||
* --------+---------+---------------------------------------
|
||||
* AAAAA | 17 bits | bit 0 to bit 16 of P1
|
||||
* BBB | 10 bits | bit 17 to bit 26 of P1
|
||||
* CCCCCCC | 28 bits | bit 27 to bit 31 of P1 (lower 5 bits)
|
||||
* | | bit 0 to bit 22 of P2 (upper 23 bits)
|
||||
* DDEEE | 17 bits | bit 23 to bit 31 of P2 (lower 9 bits)
|
||||
* | | bit 0 to bit 7 of P3 (upper 8 bits)
|
||||
*
|
||||
* digits | meaning
|
||||
* --------+-------------------------------------------------
|
||||
* AAAAA | apparently always 55034 (in Windows XP RC1)
|
||||
* BBB | most significant three digits of Raw Product Key
|
||||
* | (see below)
|
||||
* CCCCCCC | least significant six digits of Raw Product Key
|
||||
* | plus check digit (see below)
|
||||
* DD | index of the public key used to verify the
|
||||
* | Product Key. Example: 22 for Professional keys; 23 for VLK keys
|
||||
* EEE | random value (used for phone activation, different installation IDs are generated)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Decoding the Product Key results in an example byte sequence.
|
||||
*
|
||||
* 0x6F 0xFA 0x95 0x45 0xFC 0x75 0xB5 0x52 0xBB 0xEF 0xB1 0x17 0xDA 0xCD 0x00
|
||||
*
|
||||
* Of these 15 bytes the least significant four bytes contain the Raw
|
||||
* Product Key in little endian byte order. The least significant bit is
|
||||
* removed by shifting this 32-bit value (0x4595FA6F - remember the
|
||||
* little endiannessConvert byte order) to the left by one bit position, resulting
|
||||
* in a Raw Product Key of 0x22CAFD37, or
|
||||
*
|
||||
* 583728439
|
||||
*
|
||||
* in decimal notation.
|
||||
*/
|
||||
|
||||
#include "header.h"
|
||||
|
||||
char pCharset[] = "BCDFGHJKMPQRTVWXY2346789";
|
||||
|
||||
INT wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ WCHAR *pCmdLine, _In_ INT nCmdShow) {
|
||||
srand(GetTickCount64());
|
||||
|
||||
//base(L"D:\\Desktop\\ECC Research\\pIDgen\\pidgenxp.dll");
|
||||
|
||||
return InitializeWindow(hInstance);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by resource.rc
|
||||
//
|
||||
#define IDI_ICON1 101
|
||||
#define IDR_WAVE1 102
|
||||
#define IDB_BITMAP1 103
|
||||
#define IDB_BITMAP2 104
|
||||
#define IDB_BITMAP3 105
|
||||
#define IDB_BITMAP4 106
|
||||
#define IDB_BITMAP5 107
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 108
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
Binary file not shown.
+449
@@ -0,0 +1,449 @@
|
||||
//
|
||||
// Created by Andrew on 09/04/2023.
|
||||
//
|
||||
|
||||
#include "header.h"
|
||||
|
||||
/* Windows Server 2003 */
|
||||
const char pSv[] = "C9AE7AED19F6A7E100AADE98134111AD8118E59B8264734327940064BC675A0C682E19C89695FBFA3A4653E47D47FD7592258C7E3C3C61BBEA07FE5A7E842379";
|
||||
const long aSv = 1;
|
||||
const long bSv = 0;
|
||||
|
||||
// Base point G (Generator)
|
||||
const char genXSv[] = "85ACEC9F9F9B456A78E43C3637DC88D21F977A9EC15E5225BD5060CE5B892F24FEDEE574BF5801F06BC232EEF2161074496613698D88FAC4B397CE3B475406A7";
|
||||
const char genYSv[] = "66B7D1983F5D4FE43E8B4F1E28685DE0E22BBE6576A1A6B86C67533BF72FD3D082DBA281A556A16E593DB522942C8DD7120BA50C9413DF944E7258BDDF30B3C4";
|
||||
|
||||
// Inverse of the public key
|
||||
const char pubXSv[] = "90BF6BD980C536A8DB93B52AA9AEBA640BABF1D31BEC7AA345BB7510194A9B07379F552DA7B4A3EF81A9B87E0B85B5118E1E20A098641EE4CCF2045558C98C0E";
|
||||
const char pubYSv[] = "6B87D1E658D03868362945CDD582E2CF33EE4BA06369E0EFE9E4851F6DCBEC7F15081E250D171EA0CC4CB06435BCFCFEA8F438C9766743A06CBD06E7EFB4C3AE";
|
||||
|
||||
// Order of G <- from MSKey 4-in-1
|
||||
const char genOrderSv[] = "4CC5C56529F0237D";
|
||||
|
||||
// Computed private key
|
||||
const char privateKeySv[] = "2606120F59C05118";
|
||||
|
||||
/* Windows XP x64
|
||||
Public key (-K) = (1989960177638374390878377737764297057685259206834686428253479199374616869742150776410973898745805799780071536831208959469038333664656928533078897351495263; 2680493145252003995204016438404731303203625133293449171132691660710342616258476835192643732221910418645447349019141673820306444587247165566828458285756618)
|
||||
Order of base point G (n) = 4710798293276956193
|
||||
Private key (k) = 4699066967014190092 for INVERSE. 11731326262766101
|
||||
|
||||
|
||||
const char pSv[] = "D4B49D04A01EF209121C370DCF0D6292569EC65B8F147A8C62319B6B90DEA2D1CD45199B93582732BFEE27F40BF62D7EB2559BCD08041E301E0D14037A25D989";
|
||||
const long aSv = 1;
|
||||
const long bSv = 0;
|
||||
|
||||
const char genXSv[] = "828A23E65A03F2CE12342DC2B3AA4089C1447DD5C4DC36C0470885A4662F10187037F72B2216C3F671B434267A329BD3363BB27055F0EBBA8A0ABEF451D3F6A3";
|
||||
const char genYSv[] = "23B0823295C9CB669E1643B298624083F68C58F14FEEC55D0B247EF37B353A1066F502D7BC71050056C7D006156A26CC9222F5135FB8B255D7773AE0CDCA31E2";
|
||||
|
||||
const char pubXSv[] = "25FEB90513F63C0833F1096369149E65C9359F4BCC8DE9A8F647030F96485BC71929594FF369DB967910B8F0A59BC7C30CF0D38311486293BA0B2952EE648E5F";
|
||||
const char pubYSv[] = "A186A2C2913E5584F05E97D3CD49E354E6C41BE329877D7FCC7B2BF877A0B00C9298901D305D7FF012FF7902B4202D4ED64D6A90C6AD05960253BAB8F69D68BF";
|
||||
|
||||
// Order of G <- CALCULATED ON MY i7-12700K in 20 seconds
|
||||
const char genOrderSv[] = "41601E16BF4A1621";
|
||||
|
||||
// Computed private key <- CALCULATED ON MY i7-12700K in 5 minutes 40 seconds
|
||||
const char privateKeySv[] = "29AD943EA2EA15"; */
|
||||
|
||||
|
||||
/* Windows XP x64 OEM
|
||||
const char pSv[] = "A6FEDE9568C7863685F783F864A5943D34DED45EC460EEB2EC0455B01BC3C4D21FE081E479F2338BAAF7B10903AC89D23774938F41FDBFB6F16A615ECE5A04A1";
|
||||
const long aSv = 1;
|
||||
const long bSv = 0;
|
||||
|
||||
const char genXSv[] = "3CCFE20244697894A5CF8F8A57F335462C8C7C4935E171A373C2C1BA85C304D121A48931A99E4DD911945B410E10DEF21C00B2ED33FEF4E8F6FCBE16014E0AA8";
|
||||
const char genYSv[] = "7D3F4583D6A45EF6547532B2AE6AC83281317A212223A47ADA92FB48DF055A225DD3E8DF17850EBFAD744780C8166B14F0A39C96B3D216E2247A89518985F6F8";
|
||||
|
||||
const char pubXSv[] = "19D3C8A75DACEAB3CE42970BCF3097F712FD3F6D3B171BE55D7AEF6210C48194480E998AFAC181935DCB9E66BD23769AF5E7ABB8ED2A7E5FAABD4FD1F8D24F7C";
|
||||
const char pubYSv[] = "47A138CDB3C51BEB5443A00FD24734C6DE5DCE6DBA3B2EC337984C09B1CB108E45E8B50F78AEE5FBCA068C0B285576AC26099BD4D52AE2AF9F32A30A340705AF";
|
||||
|
||||
// Order of G <- CALCULATED ON MY i7-12700K in 2 hours (single threaded).
|
||||
const char genOrderSv[] = "4782F84242B0A5E1";
|
||||
|
||||
// Computed private key <- CALCULATED ON MY i7-12700K in 5 minutes 40 seconds
|
||||
const char privateKeySv[] = "15F9B7336005CB82";// or "3189410EE2AADA5F";
|
||||
*/
|
||||
|
||||
/* Unpacks the Windows Server 2003-like Product Key. */
|
||||
VOID unpackServer(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL &pUpgrade,
|
||||
DWORD &pChannelID,
|
||||
DWORD &pHash,
|
||||
QWORD &pSignature,
|
||||
DWORD &pAuthInfo
|
||||
) {
|
||||
// We're assuming that the quantity of information within the product key is at most 114 bits.
|
||||
// log2(24^25) = 114.
|
||||
|
||||
// Upgrade = Bit 0
|
||||
pUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
|
||||
// Channel ID = Bits [1..10] -> 10 bits
|
||||
pChannelID = NEXTSNBITS(pRaw[0], 10, 1);
|
||||
|
||||
// Hash = Bits [11..41] -> 31 bits
|
||||
pHash = NEXTSNBITS(pRaw[0], 31, 11);
|
||||
|
||||
// Signature = Bits [42..103] -> 62 bits
|
||||
// The quad-word signature overlaps AuthInfo in bits 104 and 105,
|
||||
// hence Microsoft employs a secret technique called: Signature = HIDWORD(Signature) >> 2 | LODWORD(Signature)
|
||||
pSignature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42);
|
||||
|
||||
// AuthInfo = Bits [104..113] -> 10 bits
|
||||
pAuthInfo = NEXTSNBITS(pRaw[1], 10, 40);
|
||||
}
|
||||
|
||||
/* Packs the Windows Server 2003-like Product Key. */
|
||||
VOID packServer(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL pUpgrade,
|
||||
DWORD pChannelID,
|
||||
DWORD pHash,
|
||||
QWORD pSignature,
|
||||
DWORD pAuthInfo
|
||||
) {
|
||||
// AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0]
|
||||
pRaw[0] = FIRSTNBITS(pSignature, 22) << 42 | (QWORD)pHash << 11 | (QWORD)pChannelID << 1 | pUpgrade;
|
||||
pRaw[1] = FIRSTNBITS(pAuthInfo, 10) << 40 | NEXTSNBITS(pSignature, 40, 22);
|
||||
}
|
||||
|
||||
|
||||
/* Verifies the Windows Server 2003-like Product Key. */
|
||||
BOOL verifyServerKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR]
|
||||
) {
|
||||
BN_CTX *context = BN_CTX_new();
|
||||
|
||||
QWORD bKey[2]{},
|
||||
pSignature = 0;
|
||||
|
||||
DWORD pData,
|
||||
pChannelID,
|
||||
pHash,
|
||||
pAuthInfo;
|
||||
|
||||
BOOL pUpgrade;
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
unbase24((BYTE *)bKey, pKey);
|
||||
|
||||
// Extract product key segments from bytecode.
|
||||
unpackServer(bKey, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo);
|
||||
|
||||
pData = pChannelID << 1 | pUpgrade;
|
||||
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_2003]{},
|
||||
xBin[FIELD_BYTES_2003]{},
|
||||
yBin[FIELD_BYTES_2003]{};
|
||||
|
||||
// Assemble the first SHA message.
|
||||
msgBuffer[0x00] = 0x5D;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
msgBuffer[0x03] = (pHash & 0x000000FF);
|
||||
msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (pHash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (pAuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (pAuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x09] = 0x00;
|
||||
msgBuffer[0x0A] = 0x00;
|
||||
|
||||
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
|
||||
SHA1(msgBuffer, 11, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 64-bit integer - this is our computed intermediate signature.
|
||||
// As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2 bits (per spec).
|
||||
QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* e = Hash
|
||||
* s = Schnorr Signature
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* Equation:
|
||||
* P = s(sG + eK)
|
||||
*
|
||||
*/
|
||||
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&pSignature, sizeof(pSignature), nullptr),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
EC_POINT *p = EC_POINT_new(eCurve);
|
||||
EC_POINT *t = EC_POINT_new(eCurve);
|
||||
|
||||
// t = sG
|
||||
EC_POINT_mul(eCurve, t, nullptr, basePoint, s, context);
|
||||
|
||||
// p = eK
|
||||
EC_POINT_mul(eCurve, p, nullptr, publicKey, e, context);
|
||||
|
||||
// p += t
|
||||
EC_POINT_add(eCurve, p, t, p, context);
|
||||
|
||||
// p *= s
|
||||
EC_POINT_mul(eCurve, p, nullptr, p, s, context);
|
||||
|
||||
// x = p.x; y = p.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, p, x, y, context);
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
|
||||
|
||||
// Assemble the second SHA message.
|
||||
msgBuffer[0x00] = 0x79;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
|
||||
memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003);
|
||||
memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003);
|
||||
|
||||
// compHash = SHA1(79 || Channel ID || p.x || p.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 31 bits.
|
||||
DWORD compHash = BYDWORD(msgDigest) & BITMASK(31);
|
||||
|
||||
BN_free(s);
|
||||
BN_free(e);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
|
||||
BN_CTX_free(context);
|
||||
|
||||
EC_POINT_free(p);
|
||||
EC_POINT_free(t);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == pHash;
|
||||
}
|
||||
|
||||
/* Generates the Windows Server 2003-like Product Key. */
|
||||
VOID generateServerKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pChannelID,
|
||||
DWORD pAuthInfo,
|
||||
BOOL pUpgrade,
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR]
|
||||
) {
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
BIGNUM *c = BN_new(),
|
||||
*e = BN_new(),
|
||||
*s = BN_new(),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature = 0;
|
||||
|
||||
// Data segment of the RPK.
|
||||
DWORD pData = pChannelID << 1 | pUpgrade;
|
||||
BOOL noSquare;
|
||||
|
||||
do {
|
||||
EC_POINT *r = EC_POINT_new(eCurve);
|
||||
|
||||
// Generate a random number c consisting of 512 bits without any constraints.
|
||||
BN_rand(c, FIELD_BITS_2003, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
|
||||
|
||||
// R = cG
|
||||
EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext);
|
||||
|
||||
// Acquire its coordinates.
|
||||
// x = R.x; y = R.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
|
||||
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_2003]{},
|
||||
xBin[FIELD_BYTES_2003]{},
|
||||
yBin[FIELD_BYTES_2003]{};
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
|
||||
|
||||
// Assemble the first SHA message.
|
||||
msgBuffer[0x00] = 0x79;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
|
||||
memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003);
|
||||
memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003);
|
||||
|
||||
// pHash = SHA1(79 || Channel ID || R.x || R.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 31 bits.
|
||||
DWORD pHash = BYDWORD(msgDigest) & BITMASK(31);
|
||||
|
||||
// Assemble the second SHA message.
|
||||
msgBuffer[0x00] = 0x5D;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
msgBuffer[0x03] = (pHash & 0x000000FF);
|
||||
msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (pHash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (pAuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (pAuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x09] = 0x00;
|
||||
msgBuffer[0x0A] = 0x00;
|
||||
|
||||
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
|
||||
SHA1(msgBuffer, 11, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 64-bit integer - this is our computed intermediate signature.
|
||||
// As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2 bits (per spec).
|
||||
QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
|
||||
|
||||
BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), e);
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* c = Random multiplier
|
||||
* e = Intermediate Signature
|
||||
* s = Signature
|
||||
* n = Order of G
|
||||
* k = Private Key
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* R(x, y) = Random derivative of the generator
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* Equation:
|
||||
* s(sG + eK) = R (mod p)
|
||||
* ↓ K = kG; R = cG ↓
|
||||
*
|
||||
* s(sG + ekG) = cG (mod p)
|
||||
* s(s + ek)G = cG (mod p)
|
||||
* ↓ G cancels out, the scalar arithmetic shrinks to order n ↓
|
||||
*
|
||||
* s(s + ek) = c (mod n)
|
||||
* s² + (ek)s - c = 0 (mod n)
|
||||
* ↓ This is a quadratic equation in respect to the signature ↓
|
||||
*
|
||||
* s = (-ek ± √((ek)² + 4c)) / 2 (mod n)
|
||||
*/
|
||||
|
||||
// e = ek (mod n)
|
||||
BN_mod_mul(e, e, privateKey, genOrder, numContext);
|
||||
|
||||
// s = e
|
||||
BN_copy(s, e);
|
||||
|
||||
// s = (ek (mod n))²
|
||||
BN_mod_sqr(s, s, genOrder, numContext);
|
||||
|
||||
// c *= 4 (c <<= 2)
|
||||
BN_lshift(c, c, 2);
|
||||
|
||||
// s += c
|
||||
BN_add(s, s, c);
|
||||
|
||||
// Around half of numbers modulo a prime are not squares -> BN_sqrt_mod fails about half of the times,
|
||||
// hence if BN_sqrt_mod returns NULL, we need to restart with a different seed.
|
||||
// s = √((ek)² + 4c (mod n))
|
||||
noSquare = BN_mod_sqrt(s, s, genOrder, numContext) == nullptr;
|
||||
|
||||
// s = -ek + √((ek)² + 4c) (mod n)
|
||||
BN_mod_sub(s, s, e, genOrder, numContext);
|
||||
|
||||
// If s is odd, add order to it.
|
||||
// The order is a prime, so it can't be even.
|
||||
if (BN_is_odd(s))
|
||||
|
||||
// s = -ek + √((ek)² + 4c) + n
|
||||
BN_add(s, s, genOrder);
|
||||
|
||||
// s /= 2 (s >>= 1)
|
||||
BN_rshift1(s, s);
|
||||
|
||||
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
|
||||
BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s));
|
||||
|
||||
// Pack product key.
|
||||
packServer(pRaw, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo);
|
||||
|
||||
EC_POINT_free(r);
|
||||
} while (pSignature > BITMASK(62) || noSquare);
|
||||
// ↑ ↑ ↑
|
||||
// The signature can't be longer than 62 bits, else it will
|
||||
// overlap with the AuthInfo segment next to it.
|
||||
|
||||
// Convert bytecode to Base24 CD-key.
|
||||
base24((BYTE *)pRaw, pKey);
|
||||
|
||||
BN_free(c);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
BN_free(e);
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
}
|
||||
|
||||
BOOL keyServer(
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR],
|
||||
DWORD nChannelID,
|
||||
DWORD nAuthInfo,
|
||||
BOOL bUpgrade
|
||||
) {
|
||||
// If the Channel ID isn't valid, quit.
|
||||
if (nChannelID >= 1'000)
|
||||
return false;
|
||||
|
||||
BIGNUM *privateKey = BN_new();
|
||||
BIGNUM *genOrder = BN_new();
|
||||
|
||||
BN_hex2bn(&privateKey, privateKeySv);
|
||||
BN_hex2bn(&genOrder, genOrderSv);
|
||||
|
||||
EC_POINT *genPoint, *pubPoint;
|
||||
EC_GROUP *eCurve = initializeEllipticCurve(
|
||||
pSv,
|
||||
aSv,
|
||||
bSv,
|
||||
genXSv,
|
||||
genYSv,
|
||||
pubXSv,
|
||||
pubYSv,
|
||||
genOrder,
|
||||
privateKey,
|
||||
&genPoint,
|
||||
&pubPoint
|
||||
);
|
||||
|
||||
// Generate a stub 10-bit AuthInfo segment if none is specified.
|
||||
if (nAuthInfo == 0) {
|
||||
RAND_bytes((byte *)&nAuthInfo, 4);
|
||||
nAuthInfo &= 0x3FF;
|
||||
}
|
||||
|
||||
do {
|
||||
generateServerKey(eCurve, genPoint, genOrder, privateKey, nChannelID, nAuthInfo, bUpgrade, pKey);
|
||||
} while (!verifyServerKey(eCurve, genPoint, pubPoint, pKey));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// Created by Andrew on 09/04/2023.
|
||||
//
|
||||
|
||||
#include "header.h"
|
||||
|
||||
/* Convert data between endianness types. */
|
||||
void endian(byte *data, int length) {
|
||||
for (int i = 0; i < length / 2; i++) {
|
||||
byte temp = data[i];
|
||||
data[i] = data[length - i - 1];
|
||||
data[length - i - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generates a random 32-bit integer in range. */
|
||||
DWORD randomRange(DWORD dwLow, DWORD dwHigh) {
|
||||
return rand() % (dwHigh - dwLow) + dwLow;
|
||||
}
|
||||
|
||||
/* Stops current asynchronously played audio. */
|
||||
void stopAudio() {
|
||||
PlaySoundW(nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
/* Plays audio stored as a resource. */
|
||||
bool playAudio(HINSTANCE hInstance, WCHAR *lpName, UINT bFlags) {
|
||||
HANDLE hResInfo = FindResourceW(hInstance, lpName, L"WAVE");
|
||||
|
||||
if (hResInfo == nullptr)
|
||||
return false;
|
||||
|
||||
HANDLE hRes = LoadResource(hInstance, (HRSRC)hResInfo);
|
||||
|
||||
if (hRes == nullptr)
|
||||
return false;
|
||||
|
||||
WCHAR *lpRes = (WCHAR *)LockResource(hRes);
|
||||
FreeResource(hRes);
|
||||
|
||||
return sndPlaySoundW(lpRes, SND_MEMORY | bFlags);
|
||||
}
|
||||
|
||||
/* Initializes the elliptic curve. */
|
||||
EC_GROUP *initializeEllipticCurve(
|
||||
const char *pSel,
|
||||
long aSel,
|
||||
long bSel,
|
||||
const char *generatorXSel,
|
||||
const char *generatorYSel,
|
||||
const char *publicKeyXSel,
|
||||
const char *publicKeyYSel,
|
||||
BIGNUM *genOrderSel,
|
||||
BIGNUM *privateKeySel,
|
||||
EC_POINT **genPoint,
|
||||
EC_POINT **pubPoint
|
||||
) {
|
||||
// Initialize BIGNUM and BIGNUMCTX structures.
|
||||
// BIGNUM - Large numbers
|
||||
// BIGNUMCTX - Context large numbers (temporary)
|
||||
BIGNUM *a, *b, *p, *generatorX, *generatorY, *publicKeyX, *publicKeyY;
|
||||
BN_CTX *context;
|
||||
|
||||
// Microsoft Product Key identification program uses a public key stored in pidgen.dll's BINK resource,
|
||||
// which is an Elliptic Curve Cryptography (ECC) public key. It can be decomposed into a following mathematical task:
|
||||
|
||||
// We're presented with an elliptic curve, a multivariable function y(x; p; a; b), where
|
||||
// y^2 % p = x^3 + ax + b % p.
|
||||
a = BN_new();
|
||||
b = BN_new();
|
||||
p = BN_new();
|
||||
|
||||
// Public key will consist of the resulting (x; y) values.
|
||||
publicKeyX = BN_new();
|
||||
publicKeyY = BN_new();
|
||||
|
||||
// G(x; y) is a generator function, its return value represents a point on the elliptic curve.
|
||||
generatorX = BN_new();
|
||||
generatorY = BN_new();
|
||||
|
||||
// Context variable
|
||||
context = BN_CTX_new();
|
||||
|
||||
/* Public data */
|
||||
BN_hex2bn(&p, pSel);
|
||||
BN_set_word(a, aSel);
|
||||
BN_set_word(b, bSel);
|
||||
BN_hex2bn(&generatorX, generatorXSel);
|
||||
BN_hex2bn(&generatorY, generatorYSel);
|
||||
|
||||
BN_hex2bn(&publicKeyX, publicKeyXSel);
|
||||
BN_hex2bn(&publicKeyY, publicKeyYSel);
|
||||
|
||||
/* Elliptical Curve calculations. */
|
||||
// The group is defined via Fp = all integers [0; p - 1], where p is prime.
|
||||
// The function EC_POINT_set_affine_coordinates() sets the x and y coordinates for the point p defined over the curve given in group.
|
||||
EC_GROUP *eCurve = EC_GROUP_new_curve_GFp(p, a, b, context);
|
||||
|
||||
// Create new point for the generator on the elliptic curve and set its coordinates to (genX; genY).
|
||||
*genPoint = EC_POINT_new(eCurve);
|
||||
EC_POINT_set_affine_coordinates(eCurve, *genPoint, generatorX, generatorY, context);
|
||||
|
||||
// Create new point for the public key on the elliptic curve and set its coordinates to (pubX; pubY).
|
||||
*pubPoint = EC_POINT_new(eCurve);
|
||||
EC_POINT_set_affine_coordinates(eCurve, *pubPoint, publicKeyX, publicKeyY, context);
|
||||
|
||||
// If generator and public key points are not on the elliptic curve, either the generator or the public key values are incorrect.
|
||||
assert(EC_POINT_is_on_curve(eCurve, *genPoint, context) == 1);
|
||||
assert(EC_POINT_is_on_curve(eCurve, *pubPoint, context) == 1);
|
||||
|
||||
// Cleanup
|
||||
BN_CTX_free(context);
|
||||
|
||||
return eCurve;
|
||||
}
|
||||
|
||||
int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen) {
|
||||
if (a == nullptr || to == nullptr)
|
||||
return 0;
|
||||
|
||||
int len = BN_bn2bin(a, to);
|
||||
|
||||
if (len > tolen)
|
||||
return -1;
|
||||
|
||||
// Choke point inside BN_bn2lebinpad: OpenSSL uses len instead of tolen.
|
||||
endian(to, tolen);
|
||||
|
||||
return len;
|
||||
}
|
||||
+1191
File diff suppressed because it is too large
Load Diff
+333
@@ -0,0 +1,333 @@
|
||||
//
|
||||
// Created by Andrew on 09/04/2023.
|
||||
//
|
||||
|
||||
#include "header.h"
|
||||
|
||||
/* Windows XP */
|
||||
const char pXP[] = "92ddcf14cb9e71f4489a2e9ba350ae29454d98cb93bdbcc07d62b502ea12238ee904a8b20d017197aae0c103b32713a9";
|
||||
const long aXP = 1;
|
||||
const long bXP = 0;
|
||||
|
||||
// Base point G (Generator)
|
||||
const char genXXP[] = "46E3775ECE21B0898D39BEA57050D422A0AF989E497962BAEE2CB17E0A28D5360D5476B8DC966443E37A14F1AEF37742";
|
||||
const char genYXP[] = "7C8E741D2C34F4478E325469CD491603D807222C9C4AC09DDB2B31B3CE3F7CC191B3580079932BC6BEF70BE27604F65E";
|
||||
|
||||
// The public key
|
||||
const char pubXXP[] = "5D8DBE75198015EC41C45AAB6143542EB098F6A5CC9CE4178A1B8A1E7ABBB5BC64DF64FAF6177DC1B0988AB00BA94BF8";
|
||||
const char pubYXP[] = "23A2909A0B4803C89F910C7191758B48746CEA4D5FF07667444ACDB9512080DBCA55E6EBF30433672B894F44ACE92BFA";
|
||||
|
||||
// The order of G was computed in 18 hours using a Pentium III 450
|
||||
const char genOrderXP[] = "DB6B4C58EFBAFD";
|
||||
|
||||
// The private key was computed in 10 hours using a Pentium III 450
|
||||
const char privateKeyXP[] = "565B0DFF8496C8";
|
||||
|
||||
|
||||
/* Windows 98
|
||||
const char pXP[] = "ec224ff2613a9fe1411b51e89634643f79a272402ee146b012a3f71098c7e75df4bf8b3713c4f0ce56691ce56b9b5029";
|
||||
const long aXP = 1;
|
||||
const long bXP = 0;
|
||||
|
||||
// Base point G (Generator)
|
||||
const char genXXP[] = "b5e1957b19951b5523204a62fd83ab22056f59a13bf8aaaf16ac10b7540f8ea92ba28dbfa68996fa12510c024f912340";
|
||||
const char genYXP[] = "a84fbc02f311b1fd4521773e01821bd047f067c496ad54ce1504315cb88667d69130caa25efb2cb1e479ed50efb40d6b";
|
||||
|
||||
// The public key
|
||||
const char pubXXP[] = "26ea9efe57ab6da485225a13ed66533c143f81b7b9528e38c8568bb726a8f0f5607da0e8d85aebf2e1425758b409e811";
|
||||
const char pubYXP[] = "1a7c4cebe5f3919e96876a447a813efcd920979e9610d2b2146a04fab1041b31ae65e24efa3e0b0d61622483655716c2";
|
||||
|
||||
// The order of G was computed in 18 hours using a Pentium III 450
|
||||
const char genOrderXP[] = "E778E33AEE6B3D";
|
||||
|
||||
// The private key was computed in 10 hours using a Pentium III 450
|
||||
const char privateKeyXP[] = "B9E99B9BB9812E"; // "677A485D4BE4A0";*/
|
||||
|
||||
|
||||
/* Unpacks a Windows XP-like Product Key. */
|
||||
VOID unpackXP(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL &pUpgrade,
|
||||
DWORD &pChannelID,
|
||||
DWORD &pSequence,
|
||||
DWORD &pHash,
|
||||
QWORD &pSignature
|
||||
) {
|
||||
// We're assuming that the quantity of information within the product key is at most 114 bits.
|
||||
// log2(24^25) = 114.
|
||||
|
||||
// Upgrade = Bit 0
|
||||
pUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
|
||||
// Serial = Bits [1..30] -> 30 bits
|
||||
pChannelID = NEXTSNBITS(pRaw[0], 30, 1) / 1'000'000;
|
||||
pSequence = NEXTSNBITS(pRaw[0], 30, 1) % 1'000'000;
|
||||
|
||||
// Hash = Bits [31..58] -> 28 bits
|
||||
pHash = NEXTSNBITS(pRaw[0], 28, 31);
|
||||
|
||||
// Signature = Bits [59..113] -> 56 bits
|
||||
pSignature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59);
|
||||
}
|
||||
|
||||
/* Packs a Windows XP-like Product Key. */
|
||||
VOID packXP(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL pUpgrade,
|
||||
DWORD pChannelID,
|
||||
DWORD pSequence,
|
||||
DWORD pHash,
|
||||
QWORD pSignature
|
||||
) {
|
||||
// The quantity of information the key provides is 114 bits.
|
||||
// We're storing it in 2 64-bit quad-words with 14 trailing bits.
|
||||
// 64 * 2 = 128
|
||||
|
||||
// Signature [114..59] <- Hash [58..31] <- Serial [30..1] <- Upgrade [0]
|
||||
pRaw[0] = FIRSTNBITS(pSignature, 5) << 59 | FIRSTNBITS(pHash, 28) << 31 | (QWORD)(pChannelID * 1'000'000 + pSequence) << 1 | pUpgrade;
|
||||
pRaw[1] = NEXTSNBITS(pSignature, 51, 5);
|
||||
}
|
||||
|
||||
/* Verifies a Windows XP-like Product Key. */
|
||||
BOOL verifyXPKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR]
|
||||
) {
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature;
|
||||
|
||||
DWORD pData,
|
||||
pChannelID,
|
||||
pSequence,
|
||||
pHash;
|
||||
|
||||
BOOL pUpgrade;
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
unbase24((BYTE *)pRaw, pKey);
|
||||
|
||||
// Extract RPK, hash and signature from bytecode.
|
||||
unpackXP(pRaw, pUpgrade, pChannelID, pSequence, pHash, pSignature);
|
||||
|
||||
pData = (pChannelID * 1'000'000 + pSequence) << 1 | pUpgrade;
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* e = Hash
|
||||
* s = Schnorr Signature
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* Equation:
|
||||
* P = sG + eK
|
||||
*
|
||||
*/
|
||||
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&pHash, sizeof(pHash), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&pSignature, sizeof(pSignature), nullptr),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
EC_POINT *t = EC_POINT_new(eCurve);
|
||||
EC_POINT *p = EC_POINT_new(eCurve);
|
||||
|
||||
// t = sG
|
||||
EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext);
|
||||
|
||||
// P = eK
|
||||
EC_POINT_mul(eCurve, p, nullptr, publicKey, e, numContext);
|
||||
|
||||
// P += t
|
||||
EC_POINT_add(eCurve, p, t, p, numContext);
|
||||
|
||||
// x = P.x; y = P.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext);
|
||||
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_XP]{},
|
||||
xBin[FIELD_BYTES]{},
|
||||
yBin[FIELD_BYTES]{};
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||
|
||||
// Assemble the SHA message.
|
||||
memcpy((void *)&msgBuffer[0], (void *)&pData, 4);
|
||||
memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES);
|
||||
memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES);
|
||||
|
||||
// compHash = SHA1(pSerial || P.x || P.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 28 bits.
|
||||
DWORD compHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
|
||||
|
||||
BN_free(e);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
|
||||
EC_POINT_free(t);
|
||||
EC_POINT_free(p);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == pHash;
|
||||
}
|
||||
|
||||
/* Generates a Windows XP-like Product Key. */
|
||||
VOID generateXPKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pChannelID,
|
||||
DWORD pSequence,
|
||||
BOOL pUpgrade,
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR]
|
||||
) {
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
BIGNUM *c = BN_new(),
|
||||
*s = BN_new(),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature = 0;
|
||||
|
||||
// Data segment of the RPK (first 31 bits).
|
||||
DWORD pData = (pChannelID * 1'000'000 + pSequence) << 1 | pUpgrade;
|
||||
|
||||
do {
|
||||
EC_POINT *r = EC_POINT_new(eCurve);
|
||||
|
||||
// Generate a random number c consisting of 384 bits without any constraints.
|
||||
BN_rand(c, FIELD_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
|
||||
|
||||
// Pick a random derivative of the base point on the elliptic curve.
|
||||
// R = cG;
|
||||
EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext);
|
||||
|
||||
// Acquire its coordinates.
|
||||
// x = R.x; y = R.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
|
||||
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_XP]{},
|
||||
xBin[FIELD_BYTES]{},
|
||||
yBin[FIELD_BYTES]{};
|
||||
|
||||
// Convert coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||
|
||||
// Assemble the SHA message.
|
||||
memcpy((void *)&msgBuffer[0], (void *)&pData, 4);
|
||||
memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES);
|
||||
memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES);
|
||||
|
||||
// pHash = SHA1(pSerial || R.x || R.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed pHash.
|
||||
// Truncate the pHash to 28 bits.
|
||||
DWORD pHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* c = Random multiplier
|
||||
* e = Hash
|
||||
* s = Signature
|
||||
* n = Order of G
|
||||
* k = Private Key
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* R(x, y) = Random derivative of the generator
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* We need to find the signature s that satisfies the equation with a given hash:
|
||||
* P = sG + eK
|
||||
* s = ek + c (mod n) <- computation optimization
|
||||
*/
|
||||
|
||||
// s = ek;
|
||||
BN_copy(s, privateKey);
|
||||
BN_mul_word(s, pHash);
|
||||
|
||||
// s += c (mod n)
|
||||
BN_mod_add(s, s, c, genOrder, numContext);
|
||||
|
||||
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
|
||||
BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s));
|
||||
|
||||
// Pack product key.
|
||||
packXP(pRaw, pUpgrade, pChannelID, pSequence, pHash, pSignature);
|
||||
|
||||
EC_POINT_free(r);
|
||||
} while (pSignature > BITMASK(55));
|
||||
// ↑ ↑ ↑
|
||||
// The signature can't be longer than 55 bits, else it will
|
||||
// make the CD-key longer than 25 characters.
|
||||
|
||||
// Convert bytecode to Base24 CD-key.
|
||||
base24((BYTE *)pRaw, pKey);
|
||||
|
||||
BN_free(c);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
}
|
||||
|
||||
BOOL keyXP(
|
||||
CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR],
|
||||
DWORD nChannelID,
|
||||
DWORD nSequence,
|
||||
BOOL bUpgrade
|
||||
) {
|
||||
// If the Channel ID or the random sequence aren't valid, quit.
|
||||
if (nChannelID >= 1'000 || nSequence >= 1'000'000)
|
||||
return false;
|
||||
|
||||
BIGNUM *privateKey = BN_new();
|
||||
BIGNUM *genOrder = BN_new();
|
||||
|
||||
BN_hex2bn(&privateKey, privateKeyXP);
|
||||
BN_hex2bn(&genOrder, genOrderXP);
|
||||
|
||||
EC_POINT *genPoint, *pubPoint;
|
||||
EC_GROUP *eCurve = initializeEllipticCurve(
|
||||
pXP,
|
||||
aXP,
|
||||
bXP,
|
||||
genXXP,
|
||||
genYXP,
|
||||
pubXXP,
|
||||
pubYXP,
|
||||
genOrder,
|
||||
privateKey,
|
||||
&genPoint,
|
||||
&pubPoint
|
||||
);
|
||||
|
||||
do {
|
||||
generateXPKey(eCurve, genPoint, genOrder, privateKey, nChannelID, nSequence, bUpgrade, pKey);
|
||||
} while (!verifyXPKey(eCurve, genPoint, pubPoint, pKey));
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user