mirror of
https://github.com/Neo-Desktop/WindowsXPKg
synced 2024-12-22 12:30:17 +02:00
things are quite broken
This commit is contained in:
parent
bf6365916d
commit
5e68e1c8e1
142
src/cli.cpp
142
src/cli.cpp
@ -29,7 +29,7 @@ json CLI::keys;
|
||||
BYTE CLI::Init(int argcIn, char **argvIn)
|
||||
{
|
||||
// set default options
|
||||
options = {argcIn, argvIn, "2E", "", "", "", "", "", "WINXPPVLK", 0,
|
||||
options = {argcIn, argvIn, "", "", "", "", "", "", "WINXPPVLK", 0,
|
||||
0, 1, false, false, false, false, false, false, false, STATE_BINK1998_GENERATE};
|
||||
|
||||
SetHelpText();
|
||||
@ -140,6 +140,61 @@ BOOL CLI::processOptions()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!options.productCode.empty())
|
||||
{
|
||||
const char *productCode = &options.productCode[0];
|
||||
auto product = keys["Products"][productCode];
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selecting product: {}\n", productCode);
|
||||
}
|
||||
|
||||
if (options.oem)
|
||||
{
|
||||
options.binkid = product["BINK"][1].get<std::string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
options.binkid = product["BINK"][0].get<std::string>();
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selected BINK: {}\n", options.binkid);
|
||||
}
|
||||
|
||||
std::vector<json> filtered;
|
||||
|
||||
if (product.contains("DPC") && options.channelID == 0)
|
||||
{
|
||||
for (auto const &i : product["DPC"][options.binkid].items())
|
||||
{
|
||||
auto el = i.value();
|
||||
if (!el["IsEvaluation"].get<bool>())
|
||||
{
|
||||
filtered.push_back(el);
|
||||
}
|
||||
}
|
||||
|
||||
// roll a die to choose which DPC entry to pick
|
||||
auto rand = UMSKT::getRandom<BYTE>();
|
||||
auto dpc = filtered[rand % filtered.size()];
|
||||
auto min = dpc["Min"].get<WORD>(), max = dpc["Max"].get<WORD>();
|
||||
options.channelID = min + (rand % (max - min));
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selected channel ID: {} (DPC entry {})\n", options.channelID, rand % filtered.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (options.channelID == 0)
|
||||
{
|
||||
options.channelID = UMSKT::getRandom<WORD>() % 999;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD intBinkID;
|
||||
sscanf(&options.binkid[0], "%x", &intBinkID);
|
||||
|
||||
@ -195,7 +250,7 @@ void CLI::printID(DWORD *pid)
|
||||
*
|
||||
* @param pk
|
||||
*/
|
||||
void CLI::printKey(std::string pk)
|
||||
void CLI::printKey(std::string &pk)
|
||||
{
|
||||
assert(pk.length() >= PK_LENGTH);
|
||||
|
||||
@ -249,57 +304,6 @@ BOOL CLI::stripKey(const std::string &in_key, std::string &out_key)
|
||||
*/
|
||||
BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
|
||||
{
|
||||
if (!options.productCode.empty())
|
||||
{
|
||||
const char *productCode = &options.productCode[0];
|
||||
auto product = keys["Products"][productCode];
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selecting product: {}\n", productCode);
|
||||
}
|
||||
|
||||
if (options.oem)
|
||||
{
|
||||
options.binkid = product["BINK"][1].get<std::string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
options.binkid = product["BINK"][0].get<std::string>();
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selected BINK: {}\n", options.binkid);
|
||||
}
|
||||
|
||||
std::vector<json> filtered;
|
||||
|
||||
if (product.contains("DPC") && options.channelID == 0)
|
||||
{
|
||||
for (auto const &i : product["DPC"][options.binkid].items())
|
||||
{
|
||||
auto el = i.value();
|
||||
if (!el["IsEvaluation"].get<bool>())
|
||||
{
|
||||
filtered.push_back(el);
|
||||
}
|
||||
}
|
||||
|
||||
// roll a die to choose which DPC entry to pick
|
||||
auto rand = UMSKT::getRandom<BYTE>();
|
||||
auto dpc = filtered[rand % filtered.size()];
|
||||
auto min = dpc["Min"].get<WORD>(), max = dpc["Max"].get<WORD>();
|
||||
options.channelID = min + (rand % (max - min));
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selected channel ID: {} (DPC entry {})\n", options.channelID, rand % filtered.size());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *BINKID = &options.binkid[0];
|
||||
auto bink = keys["BINK"][BINKID];
|
||||
|
||||
@ -320,12 +324,10 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
pidgen3->LoadEllipticCurve(bink["p"].get<std::string>(), bink["a"].get<std::string>(), bink["b"].get<std::string>(),
|
||||
bink["g"]["x"].get<std::string>(), bink["g"]["y"].get<std::string>(),
|
||||
bink["pub"]["x"].get<std::string>(), bink["pub"]["y"].get<std::string>(),
|
||||
bink["n"].get<std::string>(), bink["priv"].get<std::string>());
|
||||
pidgen3->LoadEllipticCurve(bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], bink["pub"]["x"],
|
||||
bink["pub"]["y"], bink["n"], bink["priv"]);
|
||||
|
||||
pidgen3->setChannelID(options.channelID);
|
||||
options.info.setChannelID(options.channelID);
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> Channel ID: {:03d}\n", options.channelID);
|
||||
@ -333,7 +335,7 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
|
||||
|
||||
if (options.serialSet)
|
||||
{
|
||||
pidgen3->setSerial(options.serial);
|
||||
options.info.setSerial(options.serial);
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> Serial {:#09d}\n", options.serial);
|
||||
@ -368,21 +370,22 @@ BOOL CLI::BINK1998Generate()
|
||||
|
||||
// raw PID/serial value
|
||||
DWORD nRaw = options.channelID * 1'000'000;
|
||||
DWORD serialRnd;
|
||||
|
||||
// using user-provided serial
|
||||
if (options.serialSet)
|
||||
{
|
||||
// just in case, make sure it's less than 999999
|
||||
int serialRnd = (options.serial % 999999);
|
||||
nRaw += serialRnd;
|
||||
// using user-provided serial
|
||||
serialRnd = options.serial;
|
||||
}
|
||||
else
|
||||
{
|
||||
// generate a random number to use as a serial
|
||||
auto oRaw = UMSKT::getRandom<DWORD>();
|
||||
nRaw += (oRaw % 999999); // ensure our serial is less than 999999
|
||||
serialRnd = UMSKT::getRandom<DWORD>();
|
||||
}
|
||||
|
||||
// make sure it's less than 999999
|
||||
nRaw += (serialRnd % 999999);
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
// print the resulting Product ID
|
||||
@ -392,12 +395,13 @@ BOOL CLI::BINK1998Generate()
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
bink1998.Generate(pKey);
|
||||
options.info.setSerial(nRaw);
|
||||
bink1998.Generate(options.info, pKey);
|
||||
|
||||
bool isValid = bink1998.Verify(pKey);
|
||||
if (isValid)
|
||||
{
|
||||
CLI::printKey(pKey);
|
||||
printKey(pKey);
|
||||
if (i < total - 1 || options.verbose)
|
||||
{
|
||||
fmt::print("\n");
|
||||
@ -408,7 +412,7 @@ BOOL CLI::BINK1998Generate()
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
CLI::printKey(pKey);
|
||||
printKey(pKey);
|
||||
fmt::print(" [Invalid]");
|
||||
if (i < total - 1)
|
||||
{
|
||||
@ -448,7 +452,7 @@ BOOL CLI::BINK2002Generate()
|
||||
fmt::print("> AuthInfo: {}\n", pAuthInfo);
|
||||
}
|
||||
|
||||
bink2002.Generate(pKey);
|
||||
bink2002.Generate(options.info, pKey);
|
||||
|
||||
bool isValid = bink2002.Verify(pKey);
|
||||
if (isValid)
|
||||
|
@ -82,6 +82,8 @@ struct Options
|
||||
BOOL error;
|
||||
BOOL list;
|
||||
|
||||
PIDGEN3::KeyInfo info;
|
||||
|
||||
APPLICATION_STATE state;
|
||||
};
|
||||
|
||||
@ -105,7 +107,8 @@ class CLI
|
||||
static BYTE Init(int argv, char *argc[]);
|
||||
static void SetHelpText();
|
||||
|
||||
static CLIHandlerFunc loadJSON;
|
||||
static BOOL loadJSON(const fs::path &filename);
|
||||
|
||||
static CLIHandlerFunc DisplayHelp;
|
||||
static CLIHandlerFunc DisplayErrorMessage;
|
||||
static CLIHandlerFunc SetVerboseOption;
|
||||
@ -126,7 +129,7 @@ class CLI
|
||||
static BOOL parseCommandLine();
|
||||
static BOOL processOptions();
|
||||
static void printID(DWORD *pid);
|
||||
static void printKey(std::string pk);
|
||||
static void printKey(std::string &pk);
|
||||
static BOOL stripKey(const std::string &in_key, std::string &out_key);
|
||||
|
||||
BOOL InitPIDGEN3(PIDGEN3 *pidgen3);
|
||||
|
@ -34,8 +34,8 @@ std::FILE *UMSKT::debug = getFileStreamToNul();
|
||||
std::FILE *UMSKT::debug = std::fopen("/dev/null", "w");
|
||||
#endif
|
||||
|
||||
BOOL UMSKT::VERBOSE;
|
||||
BOOL UMSKT::DEBUG;
|
||||
BOOL UMSKT::VERBOSE = false;
|
||||
BOOL UMSKT::DEBUG = false;
|
||||
|
||||
void UMSKT::setDebugOutput(std::FILE *input)
|
||||
{
|
||||
|
@ -108,7 +108,7 @@ FNEXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn)
|
||||
auto *p3((PIDGEN3 *)ptrIn);
|
||||
|
||||
std::string str;
|
||||
BOOL retval = p3->Generate(str);
|
||||
// BOOL retval = p3->Generate(str);
|
||||
|
||||
if (pKeySizeIn > str.length() + 1)
|
||||
{
|
||||
@ -118,7 +118,7 @@ FNEXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn)
|
||||
memcpy(pKeyOut, &str[0], str.length());
|
||||
pKeyOut[str.length()] = 0;
|
||||
|
||||
return retval;
|
||||
return true;
|
||||
}
|
||||
|
||||
FNEXPORT BOOL PIDGEN3_Verify(void *&ptrIn, char *pKeyIn)
|
||||
|
@ -66,6 +66,12 @@ class UMSKT
|
||||
static BOOL VERBOSE;
|
||||
static BOOL DEBUG;
|
||||
|
||||
static void DESTRUCT()
|
||||
{
|
||||
std::fclose(debug);
|
||||
debug = nullptr;
|
||||
}
|
||||
|
||||
static void setDebugOutput(std::FILE *input);
|
||||
template <typename T> static T getRandom()
|
||||
{
|
||||
|
@ -34,22 +34,22 @@
|
||||
*
|
||||
* @param pRaw [out] *QWORD[2] raw product key output
|
||||
**/
|
||||
BOOL BINK1998::Unpack(QWORD (&pRaw)[2])
|
||||
BOOL BINK1998::Unpack(KeyInfo &info, QWORD *pRaw)
|
||||
{
|
||||
// We're assuming that the quantity of information within the product key is at most 114 bits.
|
||||
// log2(24^25) = 114.
|
||||
|
||||
// Upgrade = Bit 0
|
||||
isUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
info.isUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
|
||||
// Serial = Bits [1..30] -> 30 bits
|
||||
Serial = NEXTSNBITS(pRaw[0], 30, 1);
|
||||
info.Serial = NEXTSNBITS(pRaw[0], 30, 1);
|
||||
|
||||
// Hash = Bits [31..58] -> 28 bits
|
||||
Hash = NEXTSNBITS(pRaw[0], 28, 31);
|
||||
info.Hash = NEXTSNBITS(pRaw[0], 28, 31);
|
||||
|
||||
// Signature = Bits [59..113] -> 56 bits
|
||||
Signature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59);
|
||||
info.Signature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -59,15 +59,15 @@ BOOL BINK1998::Unpack(QWORD (&pRaw)[2])
|
||||
*
|
||||
* @param pRaw [in] *QWORD[2] raw product key input
|
||||
**/
|
||||
BOOL BINK1998::Pack(QWORD (&pRaw)[2])
|
||||
BOOL BINK1998::Pack(const KeyInfo &info, QWORD *pRaw)
|
||||
{
|
||||
// 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(Signature, 5) << 59 | FIRSTNBITS(Hash, 28) << 31 | Serial << 1 | isUpgrade;
|
||||
pRaw[1] = NEXTSNBITS(Signature, 51, 5);
|
||||
pRaw[0] = FIRSTNBITS(info.Signature, 5) << 59 | FIRSTNBITS(info.Hash, 28) << 31 | info.Serial << 1 | info.isUpgrade;
|
||||
pRaw[1] = NEXTSNBITS(info.Signature, 51, 5);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -87,23 +87,24 @@ BOOL BINK1998::Verify(std::string &pKey)
|
||||
}
|
||||
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
KeyInfo info;
|
||||
|
||||
QWORD pRaw[2]{};
|
||||
QWORD pRaw[2];
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
unbase24((BYTE *)pRaw, pKey);
|
||||
|
||||
// Extract RPK, hash and signature from bytecode.
|
||||
Unpack(pRaw);
|
||||
Unpack(info, pRaw);
|
||||
|
||||
fmt::print(UMSKT::debug, "Validation results:\n");
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade);
|
||||
fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", Serial);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature);
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade);
|
||||
fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", info.Serial);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
DWORD pData = Serial << 1 | isUpgrade;
|
||||
DWORD pData = info.Serial << 1 | info.isUpgrade;
|
||||
|
||||
/*
|
||||
*
|
||||
@ -120,12 +121,13 @@ BOOL BINK1998::Verify(std::string &pKey)
|
||||
*
|
||||
*/
|
||||
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&Hash, sizeof(Hash), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&Signature, sizeof(Signature), nullptr), *x = BN_new(), *y = BN_new();
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr);
|
||||
|
||||
BIGNUM *x = BN_CTX_get(numContext), *y = BN_CTX_get(numContext);
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
EC_POINT *t = EC_POINT_new(eCurve);
|
||||
EC_POINT *p = EC_POINT_new(eCurve);
|
||||
EC_POINT *t = EC_POINT_new(eCurve), *p = EC_POINT_new(eCurve);
|
||||
|
||||
// t = sG
|
||||
EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext);
|
||||
@ -139,16 +141,16 @@ BOOL BINK1998::Verify(std::string &pKey)
|
||||
// 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]{};
|
||||
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);
|
||||
memcpy(&msgBuffer[0], &pData, 4);
|
||||
memcpy(&msgBuffer[4], xBin, FIELD_BYTES);
|
||||
memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES);
|
||||
|
||||
// compHash = SHA1(pSerial || P.x || P.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
@ -159,8 +161,6 @@ BOOL BINK1998::Verify(std::string &pKey)
|
||||
|
||||
BN_free(e);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
|
||||
@ -168,7 +168,7 @@ BOOL BINK1998::Verify(std::string &pKey)
|
||||
EC_POINT_free(p);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == Hash;
|
||||
return compHash == info.Hash;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,16 +178,17 @@ BOOL BINK1998::Verify(std::string &pKey)
|
||||
*
|
||||
* @return true on success, false on fail
|
||||
*/
|
||||
BOOL BINK1998::Generate(std::string &pKey)
|
||||
BOOL BINK1998::Generate(KeyInfo &info, std::string &pKey)
|
||||
{
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
BIGNUM *c = BN_new(), *s = BN_new(), *x = BN_new(), *y = BN_new();
|
||||
BIGNUM *c = BN_CTX_get(numContext), *s = BN_CTX_get(numContext), *x = BN_CTX_get(numContext),
|
||||
*y = BN_CTX_get(numContext);
|
||||
|
||||
QWORD pRaw[2]{};
|
||||
|
||||
// Data segment of the RPK.
|
||||
DWORD pData = Serial << 1 | isUpgrade;
|
||||
DWORD pData = info.Serial << 1 | info.isUpgrade;
|
||||
|
||||
do
|
||||
{
|
||||
@ -204,23 +205,24 @@ BOOL BINK1998::Generate(std::string &pKey)
|
||||
// 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]{};
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{}, msgBuffer[SHA_MSG_LENGTH_XP]{};
|
||||
BYTE 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);
|
||||
memcpy(&msgBuffer[0], &pData, 4);
|
||||
memcpy(&msgBuffer[4], xBin, FIELD_BYTES);
|
||||
memcpy(&msgBuffer[4 + FIELD_BYTES], 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);
|
||||
info.Hash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
|
||||
|
||||
/*
|
||||
*
|
||||
@ -243,26 +245,26 @@ BOOL BINK1998::Generate(std::string &pKey)
|
||||
|
||||
// s = ek;
|
||||
BN_copy(s, privateKey);
|
||||
BN_mul_word(s, pHash);
|
||||
BN_mul_word(s, info.Hash);
|
||||
|
||||
// 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 *)&Signature, BN_num_bytes(s));
|
||||
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
|
||||
|
||||
// Pack product key.
|
||||
Pack(pRaw);
|
||||
Pack(info, pRaw);
|
||||
|
||||
fmt::print(UMSKT::debug, "Generation results:\n");
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade);
|
||||
fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", Serial);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature);
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade);
|
||||
fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", info.Serial);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
EC_POINT_free(r);
|
||||
} while (Signature > BITMASK(55));
|
||||
} while (info.Signature > BITMASK(55));
|
||||
// ↑ ↑ ↑
|
||||
// The signature can't be longer than 55 bits, else it will
|
||||
// make the CD-key longer than 25 characters.
|
||||
@ -270,11 +272,6 @@ BOOL BINK1998::Generate(std::string &pKey)
|
||||
// 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(numContext);
|
||||
|
||||
return true;
|
||||
|
@ -27,12 +27,11 @@
|
||||
|
||||
class BINK1998 : public PIDGEN3
|
||||
{
|
||||
|
||||
public:
|
||||
BOOL Unpack(QWORD (&pRaw)[2]) override;
|
||||
BOOL Pack(QWORD (&pRaw)[2]) override;
|
||||
BOOL Unpack(KeyInfo &info, QWORD *pRaw) override;
|
||||
BOOL Pack(const KeyInfo &info, QWORD *pRaw) override;
|
||||
BOOL Verify(std::string &pKey) override;
|
||||
BOOL Generate(std::string &pKey) override;
|
||||
BOOL Generate(KeyInfo &info, std::string &pKey) override;
|
||||
};
|
||||
|
||||
#endif // UMSKT_BINK1998_H
|
||||
|
@ -34,27 +34,27 @@
|
||||
*
|
||||
* @param pRaw *QWORD[2] raw product key input
|
||||
**/
|
||||
BOOL BINK2002::Unpack(QWORD (&pRaw)[2])
|
||||
BOOL BINK2002::Unpack(KeyInfo &info, QWORD *pRaw)
|
||||
{
|
||||
// We're assuming that the quantity of information within the product key is at most 114 bits.
|
||||
// log2(24^25) = 114.
|
||||
|
||||
// Upgrade = Bit 0
|
||||
isUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
info.isUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
|
||||
// Channel ID = Bits [1..10] -> 10 bits
|
||||
ChannelID = NEXTSNBITS(pRaw[0], 10, 1);
|
||||
info.ChannelID = NEXTSNBITS(pRaw[0], 10, 1);
|
||||
|
||||
// Hash = Bits [11..41] -> 31 bits
|
||||
Hash = NEXTSNBITS(pRaw[0], 31, 11);
|
||||
info.Hash = 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)
|
||||
Signature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42);
|
||||
info.Signature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42);
|
||||
|
||||
// AuthInfo = Bits [104..113] -> 10 bits
|
||||
AuthInfo = NEXTSNBITS(pRaw[1], 10, 40);
|
||||
info.AuthInfo = NEXTSNBITS(pRaw[1], 10, 40);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -64,11 +64,11 @@ BOOL BINK2002::Unpack(QWORD (&pRaw)[2])
|
||||
*
|
||||
* @param pRaw *QWORD[2] raw product key output
|
||||
**/
|
||||
BOOL BINK2002::Pack(QWORD (&pRaw)[2])
|
||||
BOOL BINK2002::Pack(const KeyInfo &info, QWORD *pRaw)
|
||||
{
|
||||
// AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0]
|
||||
pRaw[0] = FIRSTNBITS(Signature, 22) << 42 | (QWORD)Hash << 11 | ChannelID << 1 | isUpgrade;
|
||||
pRaw[1] = FIRSTNBITS(AuthInfo, 10) << 40 | NEXTSNBITS(Signature, 40, 22);
|
||||
pRaw[0] = FIRSTNBITS(info.Signature, 22) << 42 | (QWORD)info.Hash << 11 | info.ChannelID << 1 | info.isUpgrade;
|
||||
pRaw[1] = FIRSTNBITS(info.AuthInfo, 10) << 40 | NEXTSNBITS(info.Signature, 40, 22);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -81,23 +81,24 @@ BOOL BINK2002::Pack(QWORD (&pRaw)[2])
|
||||
BOOL BINK2002::Verify(std::string &pKey)
|
||||
{
|
||||
BN_CTX *context = BN_CTX_new();
|
||||
KeyInfo info;
|
||||
|
||||
QWORD bKey[2]{};
|
||||
QWORD bKey[2];
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
unbase24((BYTE *)bKey, pKey.c_str());
|
||||
|
||||
// Extract product key segments from bytecode.
|
||||
Unpack(bKey);
|
||||
Unpack(info, bKey);
|
||||
|
||||
DWORD pData = ChannelID << 1 | isUpgrade;
|
||||
DWORD pData = info.ChannelID << 1 | info.isUpgrade;
|
||||
|
||||
fmt::print(UMSKT::debug, "Validation results:\n");
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade);
|
||||
fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", ChannelID);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature);
|
||||
fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", AuthInfo);
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade);
|
||||
fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", info.ChannelID);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature);
|
||||
fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", info.AuthInfo);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{}, msgBuffer[SHA_MSG_LENGTH_2003]{}, xBin[FIELD_BYTES_2003]{},
|
||||
@ -107,12 +108,12 @@ BOOL BINK2002::Verify(std::string &pKey)
|
||||
msgBuffer[0x00] = 0x5D;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
msgBuffer[0x03] = (Hash & 0x000000FF);
|
||||
msgBuffer[0x04] = (Hash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (Hash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (Hash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (AuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (AuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x03] = (info.Hash & 0x000000FF);
|
||||
msgBuffer[0x04] = (info.Hash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (info.Hash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (info.Hash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (info.AuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x09] = 0x00;
|
||||
msgBuffer[0x0A] = 0x00;
|
||||
|
||||
@ -140,7 +141,7 @@ BOOL BINK2002::Verify(std::string &pKey)
|
||||
*/
|
||||
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&Signature, sizeof(Signature), nullptr), *x = BN_new(), *y = BN_new();
|
||||
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), nullptr), *x = BN_new(), *y = BN_new();
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
EC_POINT *p = EC_POINT_new(eCurve), *t = EC_POINT_new(eCurve);
|
||||
@ -190,11 +191,11 @@ BOOL BINK2002::Verify(std::string &pKey)
|
||||
EC_POINT_free(t);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == Hash;
|
||||
return compHash == info.Hash;
|
||||
}
|
||||
|
||||
/* Generates a Windows Server 2003-like Product Key. */
|
||||
BOOL BINK2002::Generate(std::string &pKey)
|
||||
BOOL BINK2002::Generate(KeyInfo &info, std::string &pKey)
|
||||
{
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
@ -203,7 +204,7 @@ BOOL BINK2002::Generate(std::string &pKey)
|
||||
QWORD pRaw[2]{};
|
||||
|
||||
// Data segment of the RPK.
|
||||
DWORD pData = ChannelID << 1 | isUpgrade;
|
||||
DWORD pData = info.ChannelID << 1 | info.isUpgrade;
|
||||
|
||||
BOOL noSquare;
|
||||
|
||||
@ -251,8 +252,8 @@ BOOL BINK2002::Generate(std::string &pKey)
|
||||
msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (pHash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (AuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (AuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x07] = (info.AuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x09] = 0x00;
|
||||
msgBuffer[0x0A] = 0x00;
|
||||
|
||||
@ -331,21 +332,21 @@ BOOL BINK2002::Generate(std::string &pKey)
|
||||
BN_rshift1(s, s);
|
||||
|
||||
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
|
||||
BN_bn2lebinpad(s, (BYTE *)&Signature, BN_num_bytes(s));
|
||||
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
|
||||
|
||||
// Pack product key.
|
||||
Pack(pRaw);
|
||||
Pack(info, pRaw);
|
||||
|
||||
fmt::print(UMSKT::debug, "Generation results:\n");
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade);
|
||||
fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", ChannelID);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature);
|
||||
fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", AuthInfo);
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade);
|
||||
fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", info.ChannelID);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature);
|
||||
fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", info.AuthInfo);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
EC_POINT_free(r);
|
||||
} while (Signature > BITMASK(62) || noSquare);
|
||||
} while (info.Signature > BITMASK(62) || noSquare);
|
||||
// ↑ ↑ ↑
|
||||
// The signature can't be longer than 62 bits, else it will
|
||||
// overlap with the AuthInfo segment next to it.
|
||||
|
@ -27,12 +27,11 @@
|
||||
|
||||
class BINK2002 : public PIDGEN3
|
||||
{
|
||||
|
||||
public:
|
||||
BOOL Unpack(QWORD (&pRaw)[2]) override;
|
||||
BOOL Pack(QWORD (&pRaw)[2]) override;
|
||||
BOOL Unpack(KeyInfo &info, QWORD *pRaw) override;
|
||||
BOOL Pack(const KeyInfo &info, QWORD *pRaw) override;
|
||||
BOOL Verify(std::string &pKey) override;
|
||||
BOOL Generate(std::string &pKey) override;
|
||||
BOOL Generate(KeyInfo &info, std::string &pKey) override;
|
||||
};
|
||||
|
||||
#endif // UMSKT_BINK2002_H
|
||||
|
@ -62,39 +62,36 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel,
|
||||
// Initialize BIGNUM and BIGNUMCTX structures.
|
||||
// BIGNUM - Large numbers
|
||||
// BIGNUMCTX - Context large numbers (temporary)
|
||||
BIGNUM *a, *b, *p, *generatorX, *generatorY, *publicKeyX, *publicKeyY;
|
||||
BN_CTX *context;
|
||||
|
||||
// Context variable
|
||||
BN_CTX *context = BN_CTX_new();
|
||||
|
||||
// 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();
|
||||
BIGNUM *a = BN_CTX_get(context), *b = BN_CTX_get(context), *p = BN_CTX_get(context);
|
||||
|
||||
// Public key will consist of the resulting (x; y) values.
|
||||
publicKeyX = BN_new();
|
||||
publicKeyY = BN_new();
|
||||
BIGNUM *publicKeyX = BN_CTX_get(context), *publicKeyY = BN_CTX_get(context);
|
||||
|
||||
// G(x; y) is a generator function, its return value represents a point on the elliptic curve.
|
||||
generatorX = BN_new();
|
||||
generatorY = BN_new();
|
||||
BIGNUM *generatorX = BN_CTX_get(context), *generatorY = BN_CTX_get(context);
|
||||
|
||||
// Context variable
|
||||
context = BN_CTX_new();
|
||||
genOrder = BN_new();
|
||||
privateKey = BN_new();
|
||||
|
||||
/* Public data */
|
||||
BN_dec2bn(&p, pSel.c_str());
|
||||
BN_dec2bn(&a, aSel.c_str());
|
||||
BN_dec2bn(&b, bSel.c_str());
|
||||
BN_dec2bn(&generatorX, generatorXSel.c_str());
|
||||
BN_dec2bn(&generatorY, generatorYSel.c_str());
|
||||
BN_dec2bn(&p, &pSel[0]);
|
||||
BN_dec2bn(&a, &aSel[0]);
|
||||
BN_dec2bn(&b, &bSel[0]);
|
||||
BN_dec2bn(&generatorX, &generatorXSel[0]);
|
||||
BN_dec2bn(&generatorY, &generatorYSel[0]);
|
||||
|
||||
BN_dec2bn(&publicKeyX, publicKeyXSel.c_str());
|
||||
BN_dec2bn(&publicKeyY, publicKeyYSel.c_str());
|
||||
BN_dec2bn(&publicKeyX, &publicKeyXSel[0]);
|
||||
BN_dec2bn(&publicKeyY, &publicKeyYSel[0]);
|
||||
|
||||
/* Computed Data */
|
||||
BN_dec2bn(&genOrder, genOrderSel.c_str());
|
||||
BN_dec2bn(&privateKey, privateKeySel.c_str());
|
||||
BN_dec2bn(&genOrder, &genOrderSel[0]);
|
||||
BN_dec2bn(&privateKey, &privateKeySel[0]);
|
||||
BN_sub(privateKey, genOrder, privateKey);
|
||||
|
||||
/* Elliptic Curve calculations. */
|
||||
@ -118,13 +115,6 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel,
|
||||
|
||||
// Cleanup
|
||||
BN_CTX_free(context);
|
||||
BN_free(p);
|
||||
BN_free(a);
|
||||
BN_free(b);
|
||||
BN_free(generatorX);
|
||||
BN_free(generatorY);
|
||||
BN_free(publicKeyX);
|
||||
BN_free(publicKeyY);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -182,7 +172,7 @@ int PIDGEN3::BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen)
|
||||
**/
|
||||
void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
|
||||
{
|
||||
BYTE rbyteSeq[16];
|
||||
BYTE rbyteSeq[16], output[26];
|
||||
BIGNUM *z;
|
||||
|
||||
// Copy byte sequence to the reversed byte sequence.
|
||||
@ -204,9 +194,13 @@ void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
|
||||
// Divide z by 24 and convert the remainder to a CD-key char.
|
||||
for (int i = 24; i >= 0; i--)
|
||||
{
|
||||
cdKey[i] = pKeyCharset[BN_div_word(z, 24)];
|
||||
output[i] = pKeyCharset[BN_div_word(z, 24)];
|
||||
}
|
||||
|
||||
output[25] = 0;
|
||||
|
||||
cdKey = (char *)output;
|
||||
|
||||
BN_free(z);
|
||||
}
|
||||
|
||||
|
@ -31,9 +31,6 @@ class PIDGEN3
|
||||
EC_GROUP *eCurve;
|
||||
EC_POINT *basePoint, *publicKey, *genPoint, *pubPoint;
|
||||
BIGNUM *genOrder, *privateKey;
|
||||
DWORD Serial, AuthInfo, ChannelID, Hash;
|
||||
QWORD Signature;
|
||||
BOOL isUpgrade;
|
||||
|
||||
public:
|
||||
PIDGEN3()
|
||||
@ -49,20 +46,40 @@ class PIDGEN3
|
||||
EC_POINT_free(publicKey);
|
||||
BN_free(genOrder);
|
||||
BN_free(privateKey);
|
||||
BN_free(privateKey);
|
||||
BN_free(genOrder);
|
||||
}
|
||||
|
||||
struct KeyInfo
|
||||
{
|
||||
DWORD Serial = 0, AuthInfo = 0, ChannelID = 0, Hash = 0;
|
||||
QWORD Signature = 0;
|
||||
BOOL isUpgrade = false;
|
||||
|
||||
void setSerial(DWORD serialIn)
|
||||
{
|
||||
Serial = serialIn;
|
||||
}
|
||||
|
||||
void setAuthInfo(DWORD AuthInfoIn)
|
||||
{
|
||||
AuthInfo = AuthInfoIn;
|
||||
}
|
||||
|
||||
void setChannelID(DWORD ChannelIDIn)
|
||||
{
|
||||
ChannelID = ChannelIDIn;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789";
|
||||
|
||||
BOOL LoadEllipticCurve(std::string pSel, std::string aSel, std::string bSel, std::string generatorXSel,
|
||||
std::string generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel,
|
||||
std::string genOrderSel, std::string privateKeySel);
|
||||
|
||||
virtual BOOL Unpack(QWORD (&pRaw)[2]) = 0;
|
||||
virtual BOOL Pack(QWORD (&pRaw)[2]) = 0;
|
||||
virtual BOOL Unpack(KeyInfo &info, QWORD *pRaw) = 0;
|
||||
virtual BOOL Pack(const KeyInfo &info, QWORD *pRaw) = 0;
|
||||
virtual BOOL Verify(std::string &pKey) = 0;
|
||||
virtual BOOL Generate(std::string &pKey) = 0;
|
||||
virtual BOOL Generate(KeyInfo &info, std::string &pKey) = 0;
|
||||
|
||||
// PIDGEN3.cpp
|
||||
// Hello OpenSSL developers, please tell me, where is this function at?
|
||||
@ -71,21 +88,6 @@ class PIDGEN3
|
||||
|
||||
void base24(std::string &cdKey, BYTE *byteSeq);
|
||||
void unbase24(BYTE *byteSeq, std::string cdKey);
|
||||
|
||||
void setSerial(DWORD serialIn)
|
||||
{
|
||||
Serial = serialIn;
|
||||
}
|
||||
|
||||
void setAuthInfo(DWORD AuthInfoIn)
|
||||
{
|
||||
AuthInfo = AuthInfoIn;
|
||||
}
|
||||
|
||||
void setChannelID(DWORD ChannelIDIn)
|
||||
{
|
||||
ChannelID = ChannelIDIn;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // UMSKT_PIDGEN3_H
|
||||
|
@ -31,5 +31,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
auto cli = CLI();
|
||||
return cli.Run();
|
||||
status = cli.Run();
|
||||
|
||||
UMSKT::DESTRUCT();
|
||||
return status;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ void CLI::SetHelpText()
|
||||
true, "1", &SetNumberOption};
|
||||
|
||||
helpOptions[OPTION_PRODUCT] = {"p", "product", "which product to generate keys for",
|
||||
true, options.productCode, nullptr};
|
||||
true, options.productCode, &SetProductCodeOption};
|
||||
|
||||
helpOptions[OPTION_ACTIVATIONID] = {
|
||||
"i", "instid", "(activation only) installation ID used to generate confirmation ID",
|
||||
@ -116,7 +116,8 @@ BOOL CLI::parseCommandLine()
|
||||
}
|
||||
else
|
||||
{
|
||||
nextarg = arg[i + 1];
|
||||
i++;
|
||||
nextarg = std::string(options.argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,8 +312,18 @@ BOOL CLI::SetProductIDOption(int count, char *product)
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetValidateOption(int count, char *product)
|
||||
BOOL CLI::SetValidateOption(int count, char *productID)
|
||||
{
|
||||
options.keyToCheck = product;
|
||||
options.keyToCheck = productID;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetProductCodeOption(int, char *product)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting product code to {}\n", product);
|
||||
}
|
||||
options.productCode = product;
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user