Co-authored-by: neko <109633131+nekoppai@users.noreply.github.com> Co-authored-by: Lyssa <75037904+thecatontheceiling@users.noreply.github.com> Co-authored-by: abbodi1406 <33669284+abbodi1406@users.noreply.github.com>
314 lines
9.6 KiB
C#
314 lines
9.6 KiB
C#
namespace LibTSforge.SPP
|
|
{
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using LibTSforge.Crypto;
|
|
using LibTSforge.PhysicalStore;
|
|
|
|
public class ProductKey
|
|
{
|
|
private static readonly string ALPHABET = "BCDFGHJKMPQRTVWXY2346789";
|
|
|
|
private readonly ulong klow;
|
|
private readonly ulong khigh;
|
|
|
|
public int Group;
|
|
public int Serial;
|
|
public ulong Security;
|
|
public bool Upgrade;
|
|
public PKeyAlgorithm Algorithm;
|
|
public string EulaType;
|
|
public string PartNumber;
|
|
public string Edition;
|
|
public string Channel;
|
|
public Guid ActivationId;
|
|
|
|
private string mpc;
|
|
private string pid2;
|
|
|
|
public byte[] KeyBytes
|
|
{
|
|
get { return BitConverter.GetBytes(klow).Concat(BitConverter.GetBytes(khigh)).ToArray(); }
|
|
}
|
|
|
|
public ProductKey(int serial, ulong security, bool upgrade, PKeyAlgorithm algorithm, ProductConfig config, KeyRange range)
|
|
{
|
|
Group = config.GroupId;
|
|
Serial = serial;
|
|
Security = security;
|
|
Upgrade = upgrade;
|
|
Algorithm = algorithm;
|
|
EulaType = range.EulaType;
|
|
PartNumber = range.PartNumber.Split(':', ';')[0];
|
|
Edition = config.Edition;
|
|
Channel = config.Channel;
|
|
ActivationId = config.ActivationId;
|
|
|
|
klow = ((security & 0x3fff) << 50 | ((ulong)serial & 0x3fffffff) << 20 | ((ulong)Group & 0xfffff));
|
|
khigh = ((upgrade ? (ulong)1 : 0) << 49 | ((security >> 14) & 0x7fffffffff));
|
|
|
|
uint checksum = Utils.CRC32(KeyBytes) & 0x3ff;
|
|
|
|
khigh |= ((ulong)checksum << 39);
|
|
}
|
|
|
|
public string GetAlgoUri()
|
|
{
|
|
return "msft:rm/algorithm/pkey/" + (Algorithm == PKeyAlgorithm.PKEY2005 ? "2005" : (Algorithm == PKeyAlgorithm.PKEY2009 ? "2009" : "Unknown"));
|
|
}
|
|
|
|
public Guid GetPkeyId()
|
|
{
|
|
VariableBag pkb = new VariableBag();
|
|
pkb.Blocks.AddRange(new CRCBlock[]
|
|
{
|
|
new CRCBlock
|
|
{
|
|
DataType = CRCBlockType.STRING,
|
|
KeyAsStr = "SppPkeyBindingProductKey",
|
|
ValueAsStr = ToString()
|
|
},
|
|
new CRCBlock
|
|
{
|
|
DataType = CRCBlockType.BINARY,
|
|
KeyAsStr = "SppPkeyBindingMiscData",
|
|
Value = new byte[] { }
|
|
},
|
|
new CRCBlock
|
|
{
|
|
DataType = CRCBlockType.STRING,
|
|
KeyAsStr = "SppPkeyBindingAlgorithm",
|
|
ValueAsStr = GetAlgoUri()
|
|
}
|
|
});
|
|
|
|
return new Guid(CryptoUtils.SHA256Hash(pkb.Serialize()).Take(16).ToArray());
|
|
}
|
|
|
|
public string GetDefaultMPC()
|
|
{
|
|
int build = Environment.OSVersion.Version.Build;
|
|
string defaultMPC = build >= 10240 ? "03612" :
|
|
build >= 9600 ? "06401" :
|
|
build >= 9200 ? "05426" :
|
|
"55041";
|
|
return defaultMPC;
|
|
}
|
|
|
|
public string GetMPC()
|
|
{
|
|
if (mpc != null)
|
|
{
|
|
return mpc;
|
|
}
|
|
|
|
mpc = GetDefaultMPC();
|
|
|
|
// setup.cfg doesn't exist in Windows 8+
|
|
string setupcfg = string.Format("{0}\\oobe\\{1}", Environment.SystemDirectory, "setup.cfg");
|
|
|
|
if (!File.Exists(setupcfg) || Edition.Contains(";"))
|
|
{
|
|
return mpc;
|
|
}
|
|
|
|
string mpcKey = string.Format("{0}.{1}=", Utils.GetArchitecture(), Edition);
|
|
string localMPC = File.ReadAllLines(setupcfg).FirstOrDefault(line => line.Contains(mpcKey));
|
|
if (localMPC != null)
|
|
{
|
|
mpc = localMPC.Split('=')[1].Trim();
|
|
}
|
|
|
|
return mpc;
|
|
}
|
|
|
|
public string GetPid2()
|
|
{
|
|
if (pid2 != null)
|
|
{
|
|
return pid2;
|
|
}
|
|
|
|
pid2 = "";
|
|
|
|
if (Algorithm == PKeyAlgorithm.PKEY2005)
|
|
{
|
|
string mpc = GetMPC();
|
|
string serialHigh;
|
|
int serialLow;
|
|
int lastPart;
|
|
|
|
if (EulaType == "OEM")
|
|
{
|
|
serialHigh = "OEM";
|
|
serialLow = ((Group / 2) % 100) * 10000 + (Serial / 100000);
|
|
lastPart = Serial % 100000;
|
|
}
|
|
else
|
|
{
|
|
serialHigh = (Serial / 1000000).ToString("D3");
|
|
serialLow = Serial % 1000000;
|
|
lastPart = ((Group / 2) % 100) * 1000 + new Random().Next(1000);
|
|
}
|
|
|
|
int checksum = 0;
|
|
|
|
foreach (char c in serialLow.ToString())
|
|
{
|
|
checksum += int.Parse(c.ToString());
|
|
}
|
|
checksum = 7 - (checksum % 7);
|
|
|
|
pid2 = string.Format("{0}-{1}-{2:D6}{3}-{4:D5}", mpc, serialHigh, serialLow, checksum, lastPart);
|
|
}
|
|
|
|
return pid2;
|
|
}
|
|
|
|
public byte[] GetPid3()
|
|
{
|
|
BinaryWriter writer = new BinaryWriter(new MemoryStream());
|
|
writer.Write(0xA4);
|
|
writer.Write(0x3);
|
|
writer.WriteFixedString(GetPid2(), 24);
|
|
writer.Write(Group);
|
|
writer.WriteFixedString(PartNumber, 16);
|
|
writer.WritePadding(0x6C);
|
|
byte[] data = writer.GetBytes();
|
|
byte[] crc = BitConverter.GetBytes(~Utils.CRC32(data.Reverse().ToArray())).Reverse().ToArray();
|
|
writer.Write(crc);
|
|
|
|
return writer.GetBytes();
|
|
}
|
|
|
|
public byte[] GetPid4()
|
|
{
|
|
BinaryWriter writer = new BinaryWriter(new MemoryStream());
|
|
writer.Write(0x4F8);
|
|
writer.Write(0x4);
|
|
writer.WriteFixedString16(GetExtendedPid(), 0x80);
|
|
writer.WriteFixedString16(ActivationId.ToString(), 0x80);
|
|
writer.WritePadding(0x10);
|
|
writer.WriteFixedString16(Edition, 0x208);
|
|
writer.Write(Upgrade ? (ulong)1 : 0);
|
|
writer.WritePadding(0x50);
|
|
writer.WriteFixedString16(PartNumber, 0x80);
|
|
writer.WriteFixedString16(Channel, 0x80);
|
|
writer.WriteFixedString16(EulaType, 0x80);
|
|
|
|
return writer.GetBytes();
|
|
}
|
|
|
|
public string GetExtendedPid()
|
|
{
|
|
string mpc = GetMPC();
|
|
int serialHigh = Serial / 1000000;
|
|
int serialLow = Serial % 1000000;
|
|
int licenseType;
|
|
uint lcid = Utils.GetSystemDefaultLCID();
|
|
int build = Environment.OSVersion.Version.Build;
|
|
int dayOfYear = DateTime.Now.DayOfYear;
|
|
int year = DateTime.Now.Year;
|
|
|
|
switch (EulaType)
|
|
{
|
|
case "OEM":
|
|
licenseType = 2;
|
|
break;
|
|
|
|
case "Volume":
|
|
licenseType = 3;
|
|
break;
|
|
|
|
default:
|
|
licenseType = 0;
|
|
break;
|
|
}
|
|
|
|
return string.Format(
|
|
"{0}-{1:D5}-{2:D3}-{3:D6}-{4:D2}-{5:D4}-{6:D4}.0000-{7:D3}{8:D4}",
|
|
mpc,
|
|
Group,
|
|
serialHigh,
|
|
serialLow,
|
|
licenseType,
|
|
lcid,
|
|
build,
|
|
dayOfYear,
|
|
year
|
|
);
|
|
}
|
|
|
|
public byte[] GetPhoneData(PSVersion version)
|
|
{
|
|
if (version == PSVersion.Win7)
|
|
{
|
|
Random rnd = new Random(Group * 1000000000 + Serial);
|
|
byte[] data = new byte[8];
|
|
rnd.NextBytes(data);
|
|
return data;
|
|
}
|
|
|
|
int serialHigh = Serial / 1000000;
|
|
int serialLow = Serial % 1000000;
|
|
|
|
BinaryWriter writer = new BinaryWriter(new MemoryStream());
|
|
writer.Write(new Guid("B8731595-A2F6-430B-A799-FBFFB81A8D73").ToByteArray());
|
|
writer.Write(Group);
|
|
writer.Write(serialHigh);
|
|
writer.Write(serialLow);
|
|
writer.Write(Upgrade ? 1 : 0);
|
|
writer.Write(Security);
|
|
|
|
return writer.GetBytes();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
string keyStr = "";
|
|
Random rnd = new Random(Group * 1000000000 + Serial);
|
|
|
|
if (Algorithm == PKeyAlgorithm.PKEY2005)
|
|
{
|
|
keyStr = "H4X3DH4X3DH4X3DH4X3D";
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
keyStr += ALPHABET[rnd.Next(24)];
|
|
}
|
|
}
|
|
else if (Algorithm == PKeyAlgorithm.PKEY2009)
|
|
{
|
|
int last = 0;
|
|
byte[] bKey = KeyBytes;
|
|
|
|
for (int i = 24; i >= 0; i--)
|
|
{
|
|
int current = 0;
|
|
|
|
for (int j = 14; j >= 0; j--)
|
|
{
|
|
current *= 0x100;
|
|
current += bKey[j];
|
|
bKey[j] = (byte)(current / 24);
|
|
current %= 24;
|
|
last = current;
|
|
}
|
|
|
|
keyStr = ALPHABET[current] + keyStr;
|
|
}
|
|
|
|
keyStr = keyStr.Substring(1, last) + "N" + keyStr.Substring(last + 1, keyStr.Length - last - 1);
|
|
}
|
|
|
|
for (int i = 5; i < keyStr.Length; i += 6)
|
|
{
|
|
keyStr = keyStr.Insert(i, "-");
|
|
}
|
|
|
|
return keyStr;
|
|
}
|
|
}
|
|
}
|