mirror of
https://github.com/Neo-Desktop/WindowsXPKg
synced 2024-12-22 12:30:17 +02:00
Complete rewrite of XP Algorithm (w/ comments). Now fully optimized and readable
This commit is contained in:
parent
21e31fd1b6
commit
9812529903
16
src/header.h
16
src/header.h
@ -28,6 +28,7 @@
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
// Algorithm macros
|
||||
@ -39,9 +40,14 @@
|
||||
#define FIELD_BITS_2003 512
|
||||
#define FIELD_BYTES_2003 64
|
||||
|
||||
#define SHA_MSG_LENGTH_XP (4 + 2 * FIELD_BYTES)
|
||||
|
||||
#define NEXTSNBITS(field, n, offset) (((QWORD)field >> offset) & ((1ULL << (n)) - 1))
|
||||
#define FIRSTNBITS(field, n) NEXTSNBITS(field, n, 0)
|
||||
|
||||
#define BYDWORD(n) (n[0] | n[1] << 8 | n[2] << 16 | n[3] << 24)
|
||||
#define BITMASK(n) ((1ULL << n) - 1)
|
||||
|
||||
// Confirmation ID generator constants
|
||||
#define SUCCESS 0
|
||||
#define ERR_TOO_SHORT 1
|
||||
@ -62,7 +68,9 @@ typedef uint64_t QWORD;
|
||||
extern char pCharset[];
|
||||
|
||||
// util.cpp
|
||||
int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen); // Hello OpenSSL developers, please tell me, where is this function at?
|
||||
void endian(BYTE *data, int length);
|
||||
|
||||
EC_GROUP *initializeEllipticCurve(
|
||||
std::string pSel,
|
||||
std::string aSel,
|
||||
@ -98,17 +106,17 @@ void showHelp(char *argv[]);
|
||||
// xp.cpp
|
||||
bool verifyXPKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *generator,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
char (&cdKey)[25]
|
||||
);
|
||||
|
||||
void generateXPKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *generator,
|
||||
BIGNUM *order,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pRaw,
|
||||
DWORD pSerial,
|
||||
char (&cdKey)[25]
|
||||
);
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
//
|
||||
// Created by Andrew on 01/06/2023.
|
||||
//
|
||||
|
||||
#include "header.h"
|
||||
|
||||
char pCharset[] = "BCDFGHJKMPQRTVWXY2346789";
|
||||
|
15
src/util.cpp
15
src/util.cpp
@ -85,3 +85,18 @@ EC_GROUP *initializeEllipticCurve(
|
||||
|
||||
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;
|
||||
}
|
307
src/xp.cpp
307
src/xp.cpp
@ -1,23 +1,10 @@
|
||||
/*
|
||||
Windows XP CD Key Verification/Generator v0.03
|
||||
by z22
|
||||
|
||||
Compile with OpenSSL libs, modify to suit your needs.
|
||||
http://gnuwin32.sourceforge.net/packages/openssl.htm
|
||||
|
||||
History:
|
||||
0.03 Stack corruptionerror on exit fixed (now pkey is large enough)
|
||||
More Comments added
|
||||
0.02 Changed name the *.cpp;
|
||||
Fixed minor bugs & Make it compilable on VC++
|
||||
0.01 First version compilable MingW
|
||||
|
||||
|
||||
*/
|
||||
//
|
||||
// Created by Andrew on 01/06/2023.
|
||||
//
|
||||
|
||||
#include "header.h"
|
||||
|
||||
/* Unpacks the Windows XP Product Key. */
|
||||
/* Unpacks the Windows XP-like Product Key. */
|
||||
void unpackXP(
|
||||
QWORD (&pRaw)[2],
|
||||
DWORD &pSerial,
|
||||
@ -37,7 +24,7 @@ void unpackXP(
|
||||
pSignature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59);
|
||||
}
|
||||
|
||||
/* Packs the Windows XP Product Key. */
|
||||
/* Packs the Windows XP-like Product Key. */
|
||||
void packXP(
|
||||
QWORD (&pRaw)[2],
|
||||
DWORD pSerial,
|
||||
@ -53,213 +40,207 @@ void packXP(
|
||||
pRaw[1] = NEXTSNBITS(pSignature, 51, 5);
|
||||
}
|
||||
|
||||
/* Verify Product Key */
|
||||
bool verifyXPKey(EC_GROUP *eCurve, EC_POINT *generator, EC_POINT *publicKey, char (&cdKey)[25]) {
|
||||
BN_CTX *context = BN_CTX_new();
|
||||
/* Verifies the Windows XP-like Product Key. */
|
||||
bool verifyXPKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
char (&cdKey)[25]
|
||||
) {
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature = 0;
|
||||
|
||||
DWORD pSerial = 0,
|
||||
pHash = 0;
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
QWORD bKey[2]{};
|
||||
DWORD pID, checkHash;
|
||||
unbase24((BYTE *)pRaw, cdKey);
|
||||
|
||||
QWORD sig = 0;
|
||||
// Extract RPK, hash and signature from bytecode.
|
||||
unpackXP(pRaw, pSerial, pHash, pSignature);
|
||||
|
||||
unbase24((BYTE *)bKey, cdKey);
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* e = Hash
|
||||
* s = Schnorr Signature
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* Equation:
|
||||
* P = sG + eK
|
||||
*
|
||||
*/
|
||||
|
||||
// Extract data, hash and signature from the bytecode.
|
||||
unpackXP(bKey, pID, checkHash, sig);
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&pHash, sizeof(pHash), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&pSignature, sizeof(pSignature), nullptr),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
|
||||
// e = Hash
|
||||
// s = Signature
|
||||
BIGNUM *e, *s;
|
||||
// Create 2 points on the elliptic curve.
|
||||
EC_POINT *t = EC_POINT_new(eCurve);
|
||||
EC_POINT *p = EC_POINT_new(eCurve);
|
||||
|
||||
// Put hash word into BigNum e.
|
||||
e = BN_new();
|
||||
BN_set_word(e, checkHash);
|
||||
// t = sG
|
||||
EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext);
|
||||
|
||||
// Reverse signature and create a new BigNum s.
|
||||
endian((BYTE *)&sig, sizeof(sig));
|
||||
s = BN_bin2bn((BYTE *)&sig, sizeof(sig), nullptr);
|
||||
// p = eK
|
||||
EC_POINT_mul(eCurve, p, nullptr, publicKey, e, numContext);
|
||||
|
||||
// Create x and y.
|
||||
BIGNUM *x = BN_new();
|
||||
BIGNUM *y = BN_new();
|
||||
// p += t
|
||||
EC_POINT_add(eCurve, p, t, p, numContext);
|
||||
|
||||
// Create 2 new points on the existing elliptic curve.
|
||||
EC_POINT *u = EC_POINT_new(eCurve);
|
||||
EC_POINT *v = EC_POINT_new(eCurve);
|
||||
// x = p.x; y = p.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext);
|
||||
|
||||
// EC_POINT_mul calculates r = generator * n + q * m.
|
||||
// v = s * generator + e * (-publicKey)
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_XP]{},
|
||||
xBin[FIELD_BYTES]{},
|
||||
yBin[FIELD_BYTES]{};
|
||||
|
||||
// u = generator * s
|
||||
EC_POINT_mul(eCurve, u, nullptr, generator, s, context);
|
||||
DWORD compHash;
|
||||
|
||||
// v = publicKey * e
|
||||
EC_POINT_mul(eCurve, v, nullptr, publicKey, e, context);
|
||||
// Convert resulting point coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||
|
||||
// v += u
|
||||
EC_POINT_add(eCurve, v, u, v, context);
|
||||
// Assemble the SHA message.
|
||||
memcpy((void *)&msgBuffer[0], (void *)&pSerial, 4);
|
||||
memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES);
|
||||
memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES);
|
||||
|
||||
// EC_POINT_get_affine_coordinates() sets x and y, either of which may be nullptr, to the corresponding coordinates of p.
|
||||
// x = v.x; y = v.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, v, x, y, context);
|
||||
// Retrieve the message digest.
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
|
||||
BYTE buf[FIELD_BYTES], md[SHA_DIGEST_LENGTH], t[4];
|
||||
DWORD newHash;
|
||||
|
||||
SHA_CTX hContext;
|
||||
|
||||
// h = First32(SHA-1(pID || v.x || v.y)) >> 4
|
||||
SHA1_Init(&hContext);
|
||||
|
||||
// Chop Product ID into 4 bytes.
|
||||
t[0] = (pID & 0xff); // First 8 bits
|
||||
t[1] = (pID & 0xff00) >> 8; // Second 8 bits
|
||||
t[2] = (pID & 0xff0000) >> 16; // Third 8 bits
|
||||
t[3] = (pID & 0xff000000) >> 24; // Fourth 8 bits
|
||||
|
||||
// Hash chunk of data.
|
||||
SHA1_Update(&hContext, t, sizeof(t));
|
||||
|
||||
// Empty buffer, place v.x in little-endian.
|
||||
memset(buf, 0, FIELD_BYTES);
|
||||
BN_bn2bin(x, buf);
|
||||
endian(buf, FIELD_BYTES);
|
||||
|
||||
// Hash chunk of data.
|
||||
SHA1_Update(&hContext, buf, FIELD_BYTES);
|
||||
|
||||
// Empty buffer, place v.y in little-endian.
|
||||
memset(buf, 0, FIELD_BYTES);
|
||||
BN_bn2bin(y, buf);
|
||||
endian(buf, FIELD_BYTES);
|
||||
|
||||
// Hash chunk of data.
|
||||
SHA1_Update(&hContext, buf, FIELD_BYTES);
|
||||
|
||||
// Store the final message from hContext in md.
|
||||
SHA1_Final(md, &hContext);
|
||||
|
||||
// h = First32(SHA-1(pID || v.x || v.y)) >> 4
|
||||
newHash = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) >> 4;
|
||||
newHash &= 0xfffffff;
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 28 bits.
|
||||
compHash = BYDWORD(msgDigest) >> 4;
|
||||
compHash &= BITMASK(28);
|
||||
|
||||
BN_free(e);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
|
||||
BN_CTX_free(context);
|
||||
BN_CTX_free(numContext);
|
||||
|
||||
EC_POINT_free(u);
|
||||
EC_POINT_free(v);
|
||||
EC_POINT_free(t);
|
||||
EC_POINT_free(p);
|
||||
|
||||
// If we managed to generate a key with the same hash, the key is correct.
|
||||
return newHash == checkHash;
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == pHash;
|
||||
}
|
||||
|
||||
/* Generate a valid Product Key. */
|
||||
void generateXPKey(EC_GROUP *eCurve, EC_POINT *generator, BIGNUM *order, BIGNUM *privateKey, DWORD pRaw, char (&pKey)[25]) {
|
||||
EC_POINT *r = EC_POINT_new(eCurve);
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
void generateXPKey(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pSerial,
|
||||
char (&pKey)[25]
|
||||
) {
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
BIGNUM *c = BN_new();
|
||||
BIGNUM *s = BN_new();
|
||||
BIGNUM *x = BN_new();
|
||||
BIGNUM *y = BN_new();
|
||||
|
||||
QWORD bKey[2]{};
|
||||
QWORD pRaw[2]{};
|
||||
|
||||
do {
|
||||
DWORD hash = 0;
|
||||
QWORD sig = 0;
|
||||
EC_POINT *r = EC_POINT_new(eCurve);
|
||||
|
||||
memset(bKey, 0, 2 * sizeof(QWORD));
|
||||
QWORD pSignature = 0;
|
||||
DWORD pHash;
|
||||
|
||||
// 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);
|
||||
|
||||
// r = generator * c;
|
||||
EC_POINT_mul(eCurve, r, nullptr, generator, c, ctx);
|
||||
// Pick a random derivative of the base point on the elliptic curve.
|
||||
// R = cG;
|
||||
EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext);
|
||||
|
||||
// x = r.x; y = r.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, r, x, y, ctx);
|
||||
// Acquire its coordinates.
|
||||
// x = R.x; y = R.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
|
||||
|
||||
SHA_CTX hContext;
|
||||
BYTE md[SHA_DIGEST_LENGTH]{}, buf[FIELD_BYTES]{}, t[4]{};
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_XP]{},
|
||||
xBin[FIELD_BYTES]{},
|
||||
yBin[FIELD_BYTES]{};
|
||||
|
||||
// h = (First-32(SHA1(pRaw, r.x, r.y)) >> 4
|
||||
SHA1_Init(&hContext);
|
||||
// Convert coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||
|
||||
// Chop Raw Product Key into 4 bytes.
|
||||
t[0] = (pRaw & 0xff);
|
||||
t[1] = (pRaw & 0xff00) >> 8;
|
||||
t[2] = (pRaw & 0xff0000) >> 16;
|
||||
t[3] = (pRaw & 0xff000000) >> 24;
|
||||
// Assemble the SHA message.
|
||||
memcpy((void *)&msgBuffer[0], (void *)&pSerial, 4);
|
||||
memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES);
|
||||
memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES);
|
||||
|
||||
// Hash chunk of data.
|
||||
SHA1_Update(&hContext, t, sizeof(t));
|
||||
// Retrieve the message digest.
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
|
||||
// Empty buffer, place r.x in little-endian
|
||||
memset(buf, 0, FIELD_BYTES);
|
||||
BN_bn2bin(x, buf);
|
||||
endian(buf, FIELD_BYTES);
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed pHash.
|
||||
// Truncate the pHash to 28 bits.
|
||||
pHash = BYDWORD(msgDigest) >> 4;
|
||||
pHash &= BITMASK(28);
|
||||
|
||||
// Hash chunk of data.
|
||||
SHA1_Update(&hContext, buf, FIELD_BYTES);
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* c = Random multiplier
|
||||
* e = Hash
|
||||
* s = Signature
|
||||
* n = Order of G
|
||||
* k = Private Key
|
||||
* K = Public Key
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
// Empty buffer, place r.y in little-endian.
|
||||
memset(buf, 0, FIELD_BYTES);
|
||||
BN_bn2bin(y, buf);
|
||||
endian(buf, FIELD_BYTES);
|
||||
|
||||
// Hash chunk of data.
|
||||
SHA1_Update(&hContext, buf, FIELD_BYTES);
|
||||
|
||||
// Store the final message from hContext in md.
|
||||
SHA1_Final(md, &hContext);
|
||||
|
||||
// h = (First-32(SHA1(pRaw, r.x, r.y)) >> 4
|
||||
hash = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) >> 4;
|
||||
hash &= 0xfffffff;
|
||||
|
||||
/* s = privateKey * hash + c; */
|
||||
// s = privateKey;
|
||||
// s = ek;
|
||||
BN_copy(s, privateKey);
|
||||
BN_mul_word(s, pHash);
|
||||
|
||||
// s *= hash;
|
||||
BN_mul_word(s, hash);
|
||||
// s += c (mod n)
|
||||
BN_mod_add(s, s, c, genOrder, numContext);
|
||||
|
||||
// BN_mod_add() adds a to b % m and places the non-negative result in r.
|
||||
// s = |s + c % order|;
|
||||
BN_mod_add(s, s, c, order, ctx);
|
||||
|
||||
// Convert s from BigNum back to bytecode and reverse the endianness.
|
||||
BN_bn2bin(s, (BYTE *)&sig);
|
||||
endian((BYTE *)&sig, BN_num_bytes(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.
|
||||
packXP(bKey, pRaw, hash, sig);
|
||||
packXP(pRaw, pSerial, pHash, pSignature);
|
||||
|
||||
//printf("PID: %.8X\nHash: %.8X\nSig: %.8X %.8X\n", pRaw[0], hash, sig[1], sig[0]);
|
||||
std::cout << " PID: " << std::hex << std::setw(8) << std::setfill('0') << pRaw << std::endl
|
||||
<< "Hash: " << std::hex << std::setw(8) << std::setfill('0') << hash << std::endl
|
||||
<< " Sig: " << std::hex << std::setw(8) << std::setfill('0') << sig << std::endl
|
||||
std::cout << " Serial: 0x" << std::hex << std::setw(8) << std::setfill('0') << pSerial << std::endl
|
||||
<< " Hash: 0x" << std::hex << std::setw(8) << std::setfill('0') << pHash << std::endl
|
||||
<< " Signature: 0x" << std::hex << std::setw(8) << std::setfill('0') << pSignature << std::endl
|
||||
<< std::endl;
|
||||
|
||||
} while (bKey[1] >= (1ULL << 50));
|
||||
EC_POINT_free(r);
|
||||
} while (pRaw[1] > BITMASK(50));
|
||||
// ↑ ↑ ↑
|
||||
// bKey[1] can't be longer than 50 bits, else the signature part will make
|
||||
// the CD-key longer than 25 characters.
|
||||
// pRaw[1] can't be longer than 50 bits, else the signature part
|
||||
// will make the CD-key longer than 25 characters.
|
||||
|
||||
// Convert the key to Base24.
|
||||
base24(pKey, (BYTE *)bKey);
|
||||
// Convert bytecode to Base24 CD-key.
|
||||
base24(pKey, (BYTE *)pRaw);
|
||||
|
||||
BN_free(c);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
|
||||
BN_CTX_free(ctx);
|
||||
EC_POINT_free(r);
|
||||
BN_CTX_free(numContext);
|
||||
}
|
Loading…
Reference in New Issue
Block a user