From 492d245f86afd021a3a7fbf29d76cf3c5696705e Mon Sep 17 00:00:00 2001
From: pottzman <pottzman@hotmail.com>
Date: Sat, 2 Sep 2023 23:22:53 +1000
Subject: [PATCH 1/6] Update cli.cpp

added CLI options for confirmation ID generation
---
 src/cli.cpp | 50 +++++++++++++++++++++++++++++++++++---------------
 1 file changed, 35 insertions(+), 15 deletions(-)

diff --git a/src/cli.cpp b/src/cli.cpp
index 0592486..bf1b169 100644
--- a/src/cli.cpp
+++ b/src/cli.cpp
@@ -53,6 +53,8 @@ void CLI::showHelp(char *argv[]) {
     fmt::print("\t-n --number\tnumber of keys to generate (defaults to 1)\n");
     fmt::print("\t-f --file\tspecify which keys file to load\n");
     fmt::print("\t-i --instid\tinstallation ID used to generate confirmation ID\n");
+    fmt::print("\t-m --mode\tproduct family to activate.\n\t\t\tvalid options are \"WINDOWS\", \"OFFICEXP\", \"OFFICE2K3\", \"OFFICE2K7\" or \"PLUSDME\"\n\t\t\t(defaults to \"WINDOWS\")\n");
+    fmt::print("\t-p --productid\tthe product ID of the Program to activate. only required for Office 2K3 and Office 2K7 programs\n");
     fmt::print("\t-b --binkid\tspecify which BINK identifier to load (defaults to 2E)\n");
     fmt::print("\t-l --list\tshow which products/binks can be loaded\n");
     fmt::print("\t-c --channelid\tspecify which Channel Identifier to use (defaults to 640)\n");
@@ -69,16 +71,18 @@ int CLI::parseCommandLine(int argc, char* argv[], Options* options) {
             "",
             "",
             "",
+            "",
             640,
             0,
             1,
-	    false,
             false,
             false,
             false,
             false,
             false,
-            MODE_BINK1998_GENERATE
+            false,
+            MODE_BINK1998_GENERATE,
+            WINDOWS
     };
 
     for (int i = 1; i < argc; i++) {
@@ -158,6 +162,27 @@ int CLI::parseCommandLine(int argc, char* argv[], Options* options) {
             options->instid = argv[i+1];
             options->applicationMode = MODE_CONFIRMATION_ID;
             i++;
+        } else if (arg == "-m" || arg == "--mode") {
+            std::string mode = argv[i+1];
+            char *p = &mode[0];
+            for (int i = 0; *p; i++) {
+                *p+i = toupper((unsigned char)*p+i);
+	    }
+            if (strcmp(p, "WINDOWS") == 0) {
+                options->activationMode = WINDOWS;
+	    } else if (strcmp(p, "OFFICEXP") == 0) {
+                options->activationMode = OFFICE_XP;
+	    } else if (strcmp(p, "OFFICE2K3") == 0) {
+                options->activationMode = OFFICE_2K3;
+            } else if (strcmp(p, "OFFICE2K7") == 0) {
+                options->activationMode = OFFICE_2K7;
+	    } else if (strcmp(p, "PLUSDME") == 0) {
+                options->activationMode = PLUS_DME;
+	    }
+            i++;
+        } else if (arg == "-p" || arg == "--productid") {
+            options->productid = argv[i+1];
+            i++;
         } else if (arg == "-V" || arg == "--validate") {
             if (i == argc - 1) {
                 options->error = true;
@@ -172,6 +197,11 @@ int CLI::parseCommandLine(int argc, char* argv[], Options* options) {
         }
     }
 
+    // make sure that a product id is entered for OFFICE_2K3 or OFFICE_2K7 IIDs
+    if ((options->activationMode == OFFICE_2K3 || options->activationMode == OFFICE_2K7) && options->productid == "") {
+        return options->error = true;
+    }
+
     return !options->error;
 }
 
@@ -389,13 +419,8 @@ int CLI::BINK1998Generate() {
     // generate a key
     BN_sub(this->privateKey, this->genOrder, this->privateKey);
 
-    // Specify whether an upgrade version or not
-    bool bUpgrade = false;
-    if (options.upgrade == true)
-	    bUpgrade = true;
-
     for (int i = 0; i < this->total; i++) {
-        PIDGEN3::BINK1998::Generate(this->eCurve, this->genPoint, this->genOrder, this->privateKey, nRaw, bUpgrade, this->pKey);
+        PIDGEN3::BINK1998::Generate(this->eCurve, this->genPoint, this->genOrder, this->privateKey, nRaw, options.upgrade, this->pKey);
 
         bool isValid = PIDGEN3::BINK1998::Verify(this->eCurve, this->genPoint, this->pubPoint, this->pKey);
         if (isValid) {
@@ -444,12 +469,7 @@ int CLI::BINK2002Generate() {
             fmt::print("> AuthInfo: {}\n", pAuthInfo);
         }
 
-        // Specify whether an upgrade version or not
-        bool bUpgrade = false;
-        if (options.upgrade == true)
-            bUpgrade = true;
-
-        PIDGEN3::BINK2002::Generate(this->eCurve, this->genPoint, this->genOrder, this->privateKey, pChannelID, pAuthInfo, bUpgrade, this->pKey);
+        PIDGEN3::BINK2002::Generate(this->eCurve, this->genPoint, this->genOrder, this->privateKey, pChannelID, pAuthInfo, options.upgrade, this->pKey);
 
         bool isValid = PIDGEN3::BINK2002::Verify(this->eCurve, this->genPoint, this->pubPoint, this->pKey);
         if (isValid) {
@@ -521,7 +541,7 @@ int CLI::BINK2002Validate() {
 
 int CLI::ConfirmationID() {
     char confirmation_id[49];
-    int err = ConfirmationID::Generate(this->options.instid.c_str(), confirmation_id);
+    int err = ConfirmationID::Generate(this->options.instid.c_str(), confirmation_id, options.activationMode, options.productid);
 
     switch (err) {
         case ERR_TOO_SHORT:

From 90e31b667a91c4935875a4f6aab725d1f9fb5515 Mon Sep 17 00:00:00 2001
From: pottzman <pottzman@hotmail.com>
Date: Sat, 2 Sep 2023 23:23:59 +1000
Subject: [PATCH 2/6] Update cli.h

---
 src/cli.h | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/cli.h b/src/cli.h
index f2bc7a6..43b329d 100644
--- a/src/cli.h
+++ b/src/cli.h
@@ -36,10 +36,18 @@
 
 CMRC_DECLARE(umskt);
 
+enum ACTIVATION_ALGORITHM {
+    WINDOWS     = 0,
+    OFFICE_XP   = 1,
+    OFFICE_2K3  = 2,
+    OFFICE_2K7  = 3,
+    PLUS_DME    = 4,
+};
+
 enum MODE {
     MODE_BINK1998_GENERATE = 0,
     MODE_BINK2002_GENERATE = 1,
-    MODE_CONFIRMATION_ID = 2,
+    MODE_CONFIRMATION_ID   = 2,
     MODE_BINK1998_VALIDATE = 3,
     MODE_BINK2002_VALIDATE = 4,
 };
@@ -49,6 +57,7 @@ struct Options {
     std::string keysFilename;
     std::string instid;
     std::string keyToCheck;
+    std::string productid;
     int channelID;
     int serial;
     int numKeys;
@@ -60,6 +69,7 @@ struct Options {
     bool list;
 
     MODE applicationMode;
+    ACTIVATION_ALGORITHM activationMode;
 };
 
 class CLI {

From a2d9c46a4bd0c88744df598b24dea2396390ebcd Mon Sep 17 00:00:00 2001
From: pottzman <pottzman@hotmail.com>
Date: Sat, 2 Sep 2023 23:25:14 +1000
Subject: [PATCH 3/6] Update libumskt.cpp

---
 src/libumskt/libumskt.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libumskt/libumskt.cpp b/src/libumskt/libumskt.cpp
index 48d79b5..6293c14 100644
--- a/src/libumskt/libumskt.cpp
+++ b/src/libumskt/libumskt.cpp
@@ -27,8 +27,8 @@
 #include "pidgen3/BINK2002.h"
 #include "pidgen2/PIDGEN2.h"
 
-FNEXPORT int ConfirmationID_Generate(const char* installation_id_str, char confirmation_id[49]) {
-    return ConfirmationID::Generate(installation_id_str, confirmation_id);
+FNEXPORT int ConfirmationID_Generate(const char* installation_id_str, char confirmation_id[49], int mode, std::string productid) {
+    return ConfirmationID::Generate(installation_id_str, confirmation_id, mode, productid);
 }
 
 FNEXPORT EC_GROUP* PIDGEN3_initializeEllipticCurve(char* pSel, char* aSel, char* bSel, char* generatorXSel, char* generatorYSel, char* publicKeyXSel, char* publicKeyYSel, EC_POINT *&genPoint, EC_POINT *&pubPoint) {
@@ -57,4 +57,4 @@ FNEXPORT int PIDGEN2_GenerateRetail(char* channelID, char* &keyout) {
 
 FNEXPORT int PIDGEN2_GenerateOEM(char* year, char* day, char* oem, char* keyout) {
     return PIDGEN2::GenerateOEM(year, day, oem, keyout);
-}
\ No newline at end of file
+}

From f2f859faeb075093a157ca4ffee54bd9b42c8b94 Mon Sep 17 00:00:00 2001
From: pottzman <pottzman@hotmail.com>
Date: Sat, 2 Sep 2023 23:27:02 +1000
Subject: [PATCH 4/6] Update confid.h

---
 src/libumskt/confid/confid.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/libumskt/confid/confid.h b/src/libumskt/confid/confid.h
index f96b9cb..ae2489f 100644
--- a/src/libumskt/confid/confid.h
+++ b/src/libumskt/confid/confid.h
@@ -41,6 +41,7 @@ typedef struct {
 } TDivisor;
 
 EXPORT class ConfirmationID {
+    static int calculateCheckDigit(int pid);
     static QWORD residue_add(QWORD x, QWORD y);
     static QWORD residue_sub(QWORD x, QWORD y);
     static QWORD __umul128(QWORD a, QWORD b, QWORD* hi);
@@ -60,11 +61,12 @@ EXPORT class ConfirmationID {
     static void divisor_mul128(const TDivisor* src, QWORD mult_lo, QWORD mult_hi, TDivisor* dst);
     static unsigned rol(unsigned x, int shift);
     static void sha1_single_block(unsigned char input[64], unsigned char output[20]);
+    static void decode_iid_new_version(unsigned char* iid, unsigned char* hwid, int* version);
     static void Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize);
     static void Unmix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize);
 
 public:
-    static int Generate(const char* installation_id_str, char confirmation_id[49]);
+    static int Generate(const char* installation_id_str, char confirmation_id[49], int mode, std::string productid);
     //EXPORT static int CLIRun();
 };
 

From 104bdb19e391e05634368b043230572f53c0aa64 Mon Sep 17 00:00:00 2001
From: pottzman <pottzman@hotmail.com>
Date: Sat, 2 Sep 2023 23:33:47 +1000
Subject: [PATCH 5/6] Update confid.cpp

---
 src/libumskt/confid/confid.cpp | 287 +++++++++++++++++++++++++++++----
 1 file changed, 255 insertions(+), 32 deletions(-)

diff --git a/src/libumskt/confid/confid.cpp b/src/libumskt/confid/confid.cpp
index b2a822c..e4040ec 100644
--- a/src/libumskt/confid/confid.cpp
+++ b/src/libumskt/confid/confid.cpp
@@ -29,9 +29,25 @@
 
 #include "confid.h"
 
-#define MOD 0x16A6B036D7F2A79ULL
-#define NON_RESIDUE 43
-static const QWORD f[6] = {0, 0x21840136C85381ULL, 0x44197B83892AD0ULL, 0x1400606322B3B04ULL, 0x1400606322B3B04ULL, 1};
+QWORD MOD = 0;
+QWORD NON_RESIDUE = 0;
+QWORD f[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+int productID1;
+int productID2;
+int productID3;
+int productID4;
+int activationMode;
+
+int ConfirmationID::calculateCheckDigit(int pid)
+{
+	unsigned int i = 0, j = 0, k = 0;
+	for (j = pid; j; i += k)
+	{
+		k = j % 10;
+		j /= 10;
+	}
+	return ((10 * pid) - (i % 7)) + 7;
+}
 
 QWORD ConfirmationID::residue_add(QWORD x, QWORD y)
 {
@@ -95,18 +111,65 @@ QWORD ConfirmationID::ui128_quotient_mod(QWORD lo, QWORD hi)
 {
 	// hi:lo * ceil(2**170/MOD) >> (64 + 64 + 42)
 	QWORD prod1;
-	__umul128(lo, 0x604fa6a1c6346a87, &prod1);
+	switch (activationMode) {
+		case 0:
+			__umul128(lo, 0x604FA6A1C6346A87, &prod1);
+			break;
+		case 1:
+		case 2:
+		case 3:
+			__umul128(lo, 0x4FA8E4A40CDAE44A, &prod1);
+			break;
+		case 4:
+			__umul128(lo, 0x2C5C4D3654A594F0, &prod1);
+	}
 	QWORD part1hi;
-	QWORD part1lo = __umul128(lo, 0x2d351c6d04f8b, &part1hi);
+	QWORD part1lo;
+        switch (activationMode) {
+		case 0:
+			part1lo = __umul128(lo, 0x2D351C6D04F8B, &part1hi);
+			break;
+		case 1:
+		case 2:
+		case 3:
+			part1lo = __umul128(lo, 0x2CBAF12A59BBE, &part1hi);
+			break;
+		case 4:
+			part1lo = __umul128(lo, 0x2D36C691A4EA5, &part1hi);
+	}
 	QWORD part2hi;
-	QWORD part2lo = __umul128(hi, 0x604fa6a1c6346a87, &part2hi);
+	QWORD part2lo;
+	switch (activationMode) {
+		case 0:
+			part2lo = __umul128(hi, 0x604FA6A1C6346A87, &part2hi);
+			break;
+		case 1:
+		case 2:
+		case 3:
+			part2lo = __umul128(hi, 0x4FA8E4A40CDAE44A, &part2hi);
+			break;
+		case 4:
+			part2lo = __umul128(hi, 0x2C5C4D3654A594F0, &part2hi);
+	}
 	QWORD sum1 = part1lo + part2lo;
 	unsigned sum1carry = (sum1 < part1lo);
 	sum1 += prod1;
 	sum1carry += (sum1 < prod1);
 	QWORD prod2 = part1hi + part2hi + sum1carry;
 	QWORD prod3hi;
-	QWORD prod3lo = __umul128(hi, 0x2d351c6d04f8b, &prod3hi);
+	QWORD prod3lo;
+        switch (activationMode) {
+		case 0:
+			prod3lo = __umul128(hi, 0x2D351C6D04F8B, &prod3hi);
+			break;
+		case 1:
+		case 2:
+		case 3:
+			prod3lo = __umul128(hi, 0x2CBAF12A59BBE, &prod3hi);
+			break;
+		case 4:
+			prod3lo = __umul128(hi, 0x2D36C691A4EA5, &prod3hi);
+	}
 	prod3lo += prod2;
 	prod3hi += (prod3lo < prod2);
 	return (prod3lo >> 42) | (prod3hi << 22);
@@ -619,6 +682,20 @@ void ConfirmationID::sha1_single_block(unsigned char input[64], unsigned char ou
 	output[16] = e >> 24; output[17] = e >> 16; output[18] = e >> 8; output[19] = e;
 }
 
+void ConfirmationID::decode_iid_new_version(unsigned char* iid, unsigned char* hwid, int* version)
+{
+    QWORD buffer[5];
+    int i;
+    for (i = 0; i < 5; i++)
+        memcpy(&buffer[i], (iid + (4 * i)), 4);
+    DWORD v1 = (buffer[3] & 0xFFFFFFF8) | 2;
+    DWORD v2 = ((buffer[3] & 7) << 29) | (buffer[2] >> 3);
+    QWORD hardwareIDVal = ((QWORD)v1 << 32) | v2;
+    for (i = 0; i < 8; ++i)
+        hwid[i] = (hardwareIDVal >> (8 * i)) & 0xFF;
+    *version = buffer[0] & 7;
+}
+
 void ConfirmationID::Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize)
 {
 	unsigned char sha1_input[64];
@@ -628,12 +705,26 @@ void ConfirmationID::Mix(unsigned char* buffer, size_t bufSize, const unsigned c
 	int external_counter;
 	for (external_counter = 0; external_counter < 4; external_counter++) {
 		memset(sha1_input, 0, sizeof(sha1_input));
-		memcpy(sha1_input, buffer + half, half);
-		memcpy(sha1_input + half, key, keySize);
-		sha1_input[half + keySize] = 0x80;
-		sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8;
-		sha1_input[sizeof(sha1_input) - 2] = (half + keySize) * 8 / 0x100;
-        sha1_single_block(sha1_input, sha1_result);
+		switch (activationMode) {
+			case 0:
+			case 1:
+			case 4:
+				memcpy(sha1_input, buffer + half, half);
+				memcpy(sha1_input + half, key, keySize);
+				sha1_input[half + keySize] = 0x80;
+				sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8;
+				sha1_input[sizeof(sha1_input) - 2] = (half + keySize) * 8 / 0x100;
+				break;
+			case 2:
+			case 3:
+				sha1_input[0] = 0x79;
+				memcpy(sha1_input + 1, buffer + half, half);
+				memcpy(sha1_input + 1 + half, key, keySize);
+				sha1_input[1 + half + keySize] = 0x80;
+				sha1_input[sizeof(sha1_input) - 1] = (1 + half + keySize) * 8;
+				sha1_input[sizeof(sha1_input) - 2] = (1 + half + keySize) * 8 / 0x100;
+		}
+		sha1_single_block(sha1_input, sha1_result);
 		size_t i;
 		for (i = half & ~3; i < half; i++)
 			sha1_result[i] = sha1_result[i + 4 - (half & 3)];
@@ -654,12 +745,26 @@ void ConfirmationID::Unmix(unsigned char* buffer, size_t bufSize, const unsigned
 	int external_counter;
 	for (external_counter = 0; external_counter < 4; external_counter++) {
 		memset(sha1_input, 0, sizeof(sha1_input));
-		memcpy(sha1_input, buffer, half);
-		memcpy(sha1_input + half, key, keySize);
-		sha1_input[half + keySize] = 0x80;
-		sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8;
-		sha1_input[sizeof(sha1_input) - 2] = (half + keySize) * 8 / 0x100;
-        sha1_single_block(sha1_input, sha1_result);
+		switch (activationMode) {
+			case 0:
+			case 1:
+			case 4:
+				memcpy(sha1_input, buffer, half);
+				memcpy(sha1_input + half, key, keySize);
+				sha1_input[half + keySize] = 0x80;
+				sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8;
+				sha1_input[sizeof(sha1_input) - 2] = (half + keySize) * 8 / 0x100;
+				break;
+			case 2:
+			case 3:
+				sha1_input[0] = 0x79;
+				memcpy(sha1_input + 1, buffer, half);
+				memcpy(sha1_input + 1 + half, key, keySize);
+				sha1_input[1 + half + keySize] = 0x80;
+				sha1_input[sizeof(sha1_input) - 1] = (1 + half + keySize) * 8;
+				sha1_input[sizeof(sha1_input) - 2] = (1 + half + keySize) * 8 / 0x100;
+		}
+		sha1_single_block(sha1_input, sha1_result);
 		size_t i;
 		for (i = half & ~3; i < half; i++)
 			sha1_result[i] = sha1_result[i + 4 - (half & 3)];
@@ -671,8 +776,44 @@ void ConfirmationID::Unmix(unsigned char* buffer, size_t bufSize, const unsigned
 	}
 }
 
-int ConfirmationID::Generate(const char* installation_id_str, char confirmation_id[49])
+int ConfirmationID::Generate(const char* installation_id_str, char confirmation_id[49], int mode, std::string productid)
 {
+	int version;
+	unsigned char hardwareID[8];
+	activationMode = mode;
+	switch (activationMode) {
+		case 0:
+			MOD = 0x16A6B036D7F2A79;
+			NON_RESIDUE = 43;
+			f[0] = 0x0;
+			f[1] = 0x21840136C85381;
+			f[2] = 0x44197B83892AD0;
+			f[3] = 0x1400606322B3B04;
+			f[4] = 0x1400606322B3B04;
+			f[5] = 0x1;
+			break;
+		case 1:
+		case 2:
+		case 3:
+			MOD = 0x16E48DD18451FE9;
+			NON_RESIDUE = 3;
+			f[0] = 0x0;
+			f[1] = 0xE5F5ECD95C8FD2;
+			f[2] = 0xFF28276F11F61;
+			f[3] = 0xFB2BD9132627E6;
+			f[4] = 0xE5F5ECD95C8FD2;
+			f[5] = 0x1;
+			break;
+		case 4:
+			MOD = 0x16A5DABA0605983;
+			NON_RESIDUE = 2;
+			f[0] = 0x334F24F75CAA0E;
+			f[1] = 0x1392FF62889BD7B;
+			f[2] = 0x135131863BA2DB8;
+			f[3] = 0x153208E78006010;
+			f[4] = 0x163694F26056DB;
+			f[5] = 0x1;
+	}
 	unsigned char installation_id[19]; // 10**45 < 256**19
 	size_t installation_id_len = 0;
 	const char* p = installation_id_str;
@@ -714,7 +855,23 @@ int ConfirmationID::Generate(const char* installation_id_str, char confirmation_
 		return ERR_TOO_SHORT;
 	for (; installation_id_len < sizeof(installation_id); installation_id_len++)
 		installation_id[installation_id_len] = 0;
-	static const unsigned char iid_key[4] = {0x6A, 0xC8, 0x5E, 0xD4};
+	unsigned char iid_key[4] = { 0x0, 0x0, 0x0, 0x0 };
+	switch (activationMode) {
+		case 0:
+		case 4:
+			iid_key[0] = 0x6A;
+			iid_key[1] = 0xC8;
+			iid_key[2] = 0x5E;
+			iid_key[3] = 0xD4;
+			break;
+		case 1:
+		case 2:
+		case 3:
+			iid_key[0] = 0x5A;
+			iid_key[1] = 0x30;
+			iid_key[2] = 0xB9;
+			iid_key[3] = 0xF3;
+	}
 	Unmix(installation_id, totalCount == 41 ? 17 : 19, iid_key, 4);
 	if (installation_id[18] >= 0x10)
 		return ERR_UNKNOWN_VERSION;
@@ -727,19 +884,65 @@ int ConfirmationID::Generate(const char* installation_id_str, char confirmation_
 		unsigned short KeySHA1;
 	} parsed;
 #pragma pack(pop)
-	memcpy(&parsed, installation_id, sizeof(parsed));
-	unsigned productId1 = parsed.ProductIDLow & ((1 << 17) - 1);
-	unsigned productId2 = (parsed.ProductIDLow >> 17) & ((1 << 10) - 1);
-	unsigned productId3 = (parsed.ProductIDLow >> 27) & ((1 << 25) - 1);
-	unsigned version = (parsed.ProductIDLow >> 52) & 7;
-	unsigned productId4 = (parsed.ProductIDLow >> 55) | (parsed.ProductIDHigh << 9);
-	if (version != (totalCount == 41 ? 4 : 5))
-		return ERR_UNKNOWN_VERSION;
+	switch (activationMode) {
+		case 0:
+		case 1:
+		case 4:
+			memcpy(&parsed, installation_id, sizeof(parsed));
+			productID1 = parsed.ProductIDLow & ((1 << 17) - 1);
+			productID2 = (parsed.ProductIDLow >> 17) & ((1 << 10) - 1);
+			productID3 = (parsed.ProductIDLow >> 27) & ((1 << 24) - 1);
+			version    = (parsed.ProductIDLow >> 51) & 15;
+			productID4 = (parsed.ProductIDLow >> 55) | (parsed.ProductIDHigh << 9);
+			switch (activationMode) {
+				case 0:
+					if (version != (totalCount == 41 ? 9 : 10))
+						return ERR_UNKNOWN_VERSION;
+					break;
+				case 1:
+					if (version != 1)
+						return ERR_UNKNOWN_VERSION;
+					break;
+				case 3:
+					if (version != 4)
+						return ERR_UNKNOWN_VERSION;
+			}
+			break;
+		case 2:
+		case 3:
+			decode_iid_new_version(installation_id, hardwareID, &version);
+			productID1 = stoi(productid.substr(0,5));
+			std::string channelid = productid.substr(6,3);
+			char *p = &channelid[0];
+			for (; *p; p++) {
+				*p = toupper((unsigned char)*p);
+			}
+			if (strcmp(&channelid[0], "OEM") == 0) {
+				productID2 = stoi(productid.substr(12,3));
+				productID3 = calculateCheckDigit((stoi(productid.substr(15,1)) * 100000) + (stoi(productid.substr(18,5))));
+				productID4 = (stoi((productid.substr(10,2))) / 100000) * 1000;
+			} else {
+				productID2 = stoi(productid.substr(6,3));
+				productID3 = stoi(productid.substr(10,7));
+				productID4 = stoi(productid.substr(18,5));
+			}
+			switch (activationMode) {
+				case 2:
+					if (version != 3)
+						return ERR_UNKNOWN_VERSION;
+					break;
+				case 3:
+					if (version != 4)
+						return ERR_UNKNOWN_VERSION;
+			}
+			memcpy(&parsed, hardwareID, 8);
+			break;
+	}
 	//printf("Product ID: %05u-%03u-%07u-%05u\n", productId1, productId2, productId3, productId4);
 
 	unsigned char keybuf[16];
 	memcpy(keybuf, &parsed.HardwareID, 8);
-	QWORD productIdMixed = (QWORD)productId1 << 41 | (QWORD)productId2 << 58 | (QWORD)productId3 << 17 | productId4;
+	QWORD productIdMixed = (QWORD)productID1 << 41 | (QWORD)productID2 << 58 | (QWORD)productID3 << 17 | productID4;
 	memcpy(keybuf + 8, &productIdMixed, 8);
 
 	TDivisor d;
@@ -754,7 +957,16 @@ int ConfirmationID::Generate(const char* installation_id_str, char confirmation_
 		} u;
 		u.lo = 0;
 		u.hi = 0;
-		u.buffer[7] = attempt;
+		switch (activationMode) {
+			case 0:
+			case 1:
+			case 4:
+				u.buffer[7] = attempt;
+				break;
+			case 2:
+			case 3:
+				u.buffer[6] = attempt;
+		}
 		Mix(u.buffer, 14, keybuf, 16);
 		QWORD x2 = ui128_quotient_mod(u.lo, u.hi);
 		QWORD x1 = u.lo - x2 * MOD;
@@ -766,7 +978,18 @@ int ConfirmationID::Generate(const char* installation_id_str, char confirmation_
 	}
 	if (attempt > 0x80)
 		return ERR_UNLUCKY;
-	divisor_mul128(&d, 0x04e21b9d10f127c1, 0x40da7c36d44c, &d);
+	switch (activationMode) {
+		case 0:
+			divisor_mul128(&d, 0x04E21B9D10F127C1, 0x40DA7C36D44C, &d);
+			break;
+		case 1:
+		case 2:
+		case 3:
+			divisor_mul128(&d, 0xEFE0302A1F7A5341, 0x01FB8CF48A70DF, &d);
+			break;
+		case 4:
+			divisor_mul128(&d, 0x7C4254C43A5D1181, 0x01C61212ECE610, &d);
+	}
 	union {
 		struct {
 			QWORD encoded_lo, encoded_hi;

From 2703e17f69053787b7322da60d884642466430b1 Mon Sep 17 00:00:00 2001
From: pottzman <pottzman@hotmail.com>
Date: Sat, 2 Sep 2023 23:51:08 +1000
Subject: [PATCH 6/6] Update cli.cpp

---
 src/cli.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/cli.cpp b/src/cli.cpp
index bf1b169..80fe38f 100644
--- a/src/cli.cpp
+++ b/src/cli.cpp
@@ -165,9 +165,10 @@ int CLI::parseCommandLine(int argc, char* argv[], Options* options) {
         } else if (arg == "-m" || arg == "--mode") {
             std::string mode = argv[i+1];
             char *p = &mode[0];
-            for (int i = 0; *p; i++) {
-                *p+i = toupper((unsigned char)*p+i);
+            for (; *p; p++) {
+                *p = toupper((unsigned char)*p);
 	    }
+            p = &mode[0];
             if (strcmp(p, "WINDOWS") == 0) {
                 options->activationMode = WINDOWS;
 	    } else if (strcmp(p, "OFFICEXP") == 0) {