summaryrefslogtreecommitdiff
path: root/LibTSforge
diff options
context:
space:
mode:
authorWither OrNot2025-02-13 14:49:35 +0000
committerWither OrNot2025-02-13 14:49:37 +0000
commit0d59561bee4cf7db10d53a8aa58952ae65e856b5 (patch)
treeb2ddb66c883dd2ef75426de4c080f3121f0a8658 /LibTSforge
downloadTSforge-0d59561bee4cf7db10d53a8aa58952ae65e856b5.zip
Initial commit1.0.0
Co-authored-by: neko <[email protected]> Co-authored-by: Lyssa <[email protected]> Co-authored-by: abbodi1406 <[email protected]>
Diffstat (limited to 'LibTSforge')
-rw-r--r--LibTSforge/.editorconfig78
-rw-r--r--LibTSforge/Activators/AVMA4K.cs126
-rw-r--r--LibTSforge/Activators/KMS4K.cs165
-rw-r--r--LibTSforge/Activators/ZeroCID.cs145
-rw-r--r--LibTSforge/Common.cs466
-rw-r--r--LibTSforge/Crypto/CryptoUtils.cs121
-rw-r--r--LibTSforge/Crypto/Keys.cs87
-rw-r--r--LibTSforge/Crypto/PhysStoreCrypto.cs71
-rw-r--r--LibTSforge/LibTSforge.csproj27
-rw-r--r--LibTSforge/Modifiers/GenPKeyInstall.cs207
-rw-r--r--LibTSforge/Modifiers/GracePeriodReset.cs29
-rw-r--r--LibTSforge/Modifiers/KMSHostCharge.cs119
-rw-r--r--LibTSforge/Modifiers/KeyChangeLockDelete.cs33
-rw-r--r--LibTSforge/Modifiers/RearmReset.cs45
-rw-r--r--LibTSforge/Modifiers/TamperedFlagsDelete.cs44
-rw-r--r--LibTSforge/Modifiers/UniqueIdDelete.cs55
-rw-r--r--LibTSforge/PhysicalStore/Common.cs21
-rw-r--r--LibTSforge/PhysicalStore/IPhysicalStore.cs92
-rw-r--r--LibTSforge/PhysicalStore/PhysicalStoreModern.cs411
-rw-r--r--LibTSforge/PhysicalStore/PhysicalStoreWin7.cs374
-rw-r--r--LibTSforge/PhysicalStore/VariableBag.cs187
-rw-r--r--LibTSforge/SPP/PKeyConfig.cs215
-rw-r--r--LibTSforge/SPP/ProductKey.cs313
-rw-r--r--LibTSforge/SPP/SLAPI.cs417
-rw-r--r--LibTSforge/TokenStore/Common.cs67
-rw-r--r--LibTSforge/TokenStore/ITokenStore.cs17
-rw-r--r--LibTSforge/TokenStore/TokenStoreModern.cs289
27 files changed, 4221 insertions, 0 deletions
diff --git a/LibTSforge/.editorconfig b/LibTSforge/.editorconfig
new file mode 100644
index 0000000..dd467cb
--- /dev/null
+++ b/LibTSforge/.editorconfig
@@ -0,0 +1,78 @@
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+insert_final_newline = true
+
+[*.{cs,vb}]
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+indent_size = 4
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+
+[*.cs]
+csharp_indent_labels = one_less_than_current
+csharp_using_directive_placement = inside_namespace:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_prefer_braces = true:silent
+csharp_style_namespace_declarations = block_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_prefer_system_threading_lock = true:suggestion
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
diff --git a/LibTSforge/Activators/AVMA4K.cs b/LibTSforge/Activators/AVMA4K.cs
new file mode 100644
index 0000000..5ac2838
--- /dev/null
+++ b/LibTSforge/Activators/AVMA4K.cs
@@ -0,0 +1,126 @@
+namespace LibTSforge.Activators
+{
+ using System;
+ using LibTSforge.PhysicalStore;
+ using LibTSforge.SPP;
+
+ public static class AVMA4k
+ {
+ public static void Activate(PSVersion version, bool production, Guid actId)
+ {
+ if (version != PSVersion.WinModern)
+ {
+ throw new NotSupportedException("AVMA licenses are not available for this product.");
+ }
+
+ Guid appId;
+ if (actId == Guid.Empty)
+ {
+ appId = SLApi.WINDOWS_APP_ID;
+ actId = SLApi.GetDefaultActivationID(appId, false);
+
+ if (actId == Guid.Empty)
+ {
+ throw new NotSupportedException("No applicable activation IDs found.");
+ }
+ }
+ else
+ {
+ appId = SLApi.GetAppId(actId);
+ }
+
+ if (SLApi.GetPKeyChannel(SLApi.GetInstalledPkeyID(actId)) != "VT:IA")
+ {
+ throw new NotSupportedException("Non-VT:IA product key installed.");
+ }
+
+ Utils.KillSPP();
+
+ Logger.WriteLine("Writing TrustedStore data...");
+
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ string key = string.Format("SPPSVC\\{0}\\{1}", appId, actId);
+
+ ulong unknown = 0;
+ ulong time1;
+ ulong crcBindTime = (ulong)DateTime.UtcNow.ToFileTime();
+ ulong timerTime;
+
+ ulong expiry = Constants.TimerMax;
+
+ long creationTime = BitConverter.ToInt64(store.GetBlock("__##USERSEP##\\$$_RESERVED_$$\\NAMESPACE__", "__##USERSEP-RESERVED##__$$GLOBAL-CREATION-TIME$$").Data, 0);
+ long tickCount = BitConverter.ToInt64(store.GetBlock("__##USERSEP##\\$$_RESERVED_$$\\NAMESPACE__", "__##USERSEP-RESERVED##__$$GLOBAL-TICKCOUNT-UPTIME$$").Data, 0);
+ long deltaTime = BitConverter.ToInt64(store.GetBlock(key, "__##USERSEP-RESERVED##__$$UP-TIME-DELTA$$").Data, 0);
+
+ time1 = (ulong)(creationTime + tickCount + deltaTime);
+ timerTime = crcBindTime / 10000;
+ expiry /= 10000;
+
+ VariableBag avmaBinding = new VariableBag();
+
+ avmaBinding.Blocks.AddRange(new CRCBlock[]
+ {
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ Key = new byte[] { },
+ Value = BitConverter.GetBytes(crcBindTime),
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ Key = new byte[] { },
+ ValueAsStr = "AVMA4K",
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ Key = new byte[] { },
+ ValueAsStr = "00491-50000-00001-AA666",
+ }
+ });
+
+ byte[] avmaBindingData = avmaBinding.Serialize();
+
+ Timer avmaTimer = new Timer
+ {
+ Unknown = unknown,
+ Time1 = time1,
+ Time2 = timerTime,
+ Expiry = expiry
+ };
+
+ string storeVal = string.Format("msft:spp/ia/bind/1.0/store/{0}/{1}", appId, actId);
+ string timerVal = string.Format("msft:spp/ia/bind/1.0/timer/{0}/{1}", appId, actId);
+
+ store.DeleteBlock(key, storeVal);
+ store.DeleteBlock(key, timerVal);
+
+ store.AddBlocks(new PSBlock[]
+ {
+ new PSBlock
+ {
+ Type = BlockType.NAMED,
+ Flags = 0x400,
+ KeyAsStr = key,
+ ValueAsStr = storeVal,
+ Data = avmaBindingData,
+ },
+ new PSBlock
+ {
+ Type = BlockType.TIMER,
+ Flags = 0x4,
+ KeyAsStr = key,
+ ValueAsStr = timerVal,
+ Data = avmaTimer.CastToArray()
+ }
+ });
+ }
+
+ SLApi.RefreshLicenseStatus();
+ SLApi.FireStateChangedEvent(appId);
+ Logger.WriteLine("Activated using AVMA4k successfully.");
+ }
+ }
+}
diff --git a/LibTSforge/Activators/KMS4K.cs b/LibTSforge/Activators/KMS4K.cs
new file mode 100644
index 0000000..fd09bd3
--- /dev/null
+++ b/LibTSforge/Activators/KMS4K.cs
@@ -0,0 +1,165 @@
+namespace LibTSforge.Activators
+{
+ using System;
+ using LibTSforge.PhysicalStore;
+ using LibTSforge.SPP;
+
+ public class KMS4k
+ {
+ public static void Activate(PSVersion version, bool production, Guid actId)
+ {
+ Guid appId;
+ if (actId == Guid.Empty)
+ {
+ appId = SLApi.WINDOWS_APP_ID;
+ actId = SLApi.GetDefaultActivationID(appId, true);
+
+ if (actId == Guid.Empty)
+ {
+ throw new NotSupportedException("No applicable activation IDs found.");
+ }
+ }
+ else
+ {
+ appId = SLApi.GetAppId(actId);
+ }
+
+ if (SLApi.GetPKeyChannel(SLApi.GetInstalledPkeyID(actId)) != "Volume:GVLK")
+ {
+ throw new NotSupportedException("Non-Volume:GVLK product key installed.");
+ }
+
+ Utils.KillSPP();
+
+ Logger.WriteLine("Writing TrustedStore data...");
+
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ string key = string.Format("SPPSVC\\{0}\\{1}", appId, actId);
+
+ ulong unknown = 0;
+ ulong time1;
+ ulong time2 = (ulong)DateTime.UtcNow.ToFileTime();
+ ulong expiry = Constants.TimerMax;
+
+ if (version == PSVersion.Win7)
+ {
+ unknown = 0x800000000;
+ time1 = 0;
+ }
+ else
+ {
+ long creationTime = BitConverter.ToInt64(store.GetBlock("__##USERSEP##\\$$_RESERVED_$$\\NAMESPACE__", "__##USERSEP-RESERVED##__$$GLOBAL-CREATION-TIME$$").Data, 0);
+ long tickCount = BitConverter.ToInt64(store.GetBlock("__##USERSEP##\\$$_RESERVED_$$\\NAMESPACE__", "__##USERSEP-RESERVED##__$$GLOBAL-TICKCOUNT-UPTIME$$").Data, 0);
+ long deltaTime = BitConverter.ToInt64(store.GetBlock(key, "__##USERSEP-RESERVED##__$$UP-TIME-DELTA$$").Data, 0);
+
+ time1 = (ulong)(creationTime + tickCount + deltaTime);
+ time2 /= 10000;
+ expiry /= 10000;
+ }
+
+ byte[] hwidBlock = Constants.UniversalHWIDBlock;
+ byte[] kmsResp;
+
+ switch (version)
+ {
+ case PSVersion.Win7:
+ kmsResp = Constants.KMSv4Response;
+ break;
+ case PSVersion.Win8:
+ kmsResp = Constants.KMSv5Response;
+ break;
+ case PSVersion.WinBlue:
+ case PSVersion.WinModern:
+ kmsResp = Constants.KMSv6Response;
+ break;
+ default:
+ throw new NotSupportedException("Unsupported PSVersion.");
+ }
+
+ VariableBag kmsBinding = new VariableBag();
+
+ kmsBinding.Blocks.AddRange(new CRCBlock[]
+ {
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ Key = new byte[] { },
+ Value = kmsResp
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ Key = new byte[] { },
+ ValueAsStr = "msft:rm/algorithm/hwid/4.0"
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ KeyAsStr = "SppBindingLicenseData",
+ Value = hwidBlock
+ }
+ });
+
+ if (version == PSVersion.WinModern)
+ {
+ kmsBinding.Blocks.AddRange(new CRCBlock[]
+ {
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ Key = new byte[] { },
+ ValueAsStr = "massgrave.dev"
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ Key = new byte[] { },
+ ValueAsStr = "6969"
+ }
+ });
+ }
+
+ byte[] kmsBindingData = kmsBinding.Serialize();
+
+ Timer kmsTimer = new Timer
+ {
+ Unknown = unknown,
+ Time1 = time1,
+ Time2 = time2,
+ Expiry = expiry
+ };
+
+ string storeVal = string.Format("msft:spp/kms/bind/2.0/store/{0}/{1}", appId, actId);
+ string timerVal = string.Format("msft:spp/kms/bind/2.0/timer/{0}/{1}", appId, actId);
+
+ store.DeleteBlock(key, storeVal);
+ store.DeleteBlock(key, timerVal);
+
+ store.AddBlocks(new PSBlock[]
+ {
+ new PSBlock
+ {
+ Type = BlockType.NAMED,
+ Flags = (version == PSVersion.WinModern) ? (uint)0x400 : 0,
+ KeyAsStr = key,
+ ValueAsStr = storeVal,
+ Data = kmsBindingData
+ },
+ new PSBlock
+ {
+ Type = BlockType.TIMER,
+ Flags = (version == PSVersion.Win7) ? (uint)0 : 0x4,
+ KeyAsStr = key,
+ ValueAsStr = timerVal,
+ Data = kmsTimer.CastToArray()
+ }
+ });
+ }
+
+ SLApi.RefreshLicenseStatus();
+ SLApi.FireStateChangedEvent(appId);
+ Logger.WriteLine("Activated using KMS4k successfully.");
+ }
+ }
+}
diff --git a/LibTSforge/Activators/ZeroCID.cs b/LibTSforge/Activators/ZeroCID.cs
new file mode 100644
index 0000000..99cedee
--- /dev/null
+++ b/LibTSforge/Activators/ZeroCID.cs
@@ -0,0 +1,145 @@
+namespace LibTSforge.Activators
+{
+ using System;
+ using System.IO;
+ using LibTSforge.Crypto;
+ using LibTSforge.PhysicalStore;
+ using LibTSforge.SPP;
+
+ public static class ZeroCID
+ {
+ public static void Deposit(Guid actId, string instId)
+ {
+ uint status = SLApi.DepositConfirmationID(actId, instId, Constants.ZeroCID);
+ Logger.WriteLine(string.Format("Depositing fake CID status {0:X}", status));
+
+ if (status != 0)
+ {
+ throw new InvalidOperationException("Failed to deposit fake CID.");
+ }
+ }
+
+ public static void Activate(PSVersion version, bool production, Guid actId)
+ {
+ Guid appId;
+
+ if (actId == Guid.Empty)
+ {
+ appId = SLApi.WINDOWS_APP_ID;
+ actId = SLApi.GetDefaultActivationID(appId, false);
+
+ if (actId == Guid.Empty)
+ {
+ throw new NotSupportedException("No applicable activation IDs found.");
+ }
+ }
+ else
+ {
+ appId = SLApi.GetAppId(actId);
+ }
+
+ if (!SLApi.IsPhoneActivatable(actId))
+ {
+ throw new NotSupportedException("Phone license is unavailable for this product.");
+ }
+
+ string instId = SLApi.GetInstallationID(actId);
+ Guid pkeyId = SLApi.GetInstalledPkeyID(actId);
+
+ if (version == PSVersion.Win7)
+ {
+ Deposit(actId, instId);
+ }
+
+ Utils.KillSPP();
+
+ Logger.WriteLine("Writing TrustedStore data...");
+
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ byte[] hwidBlock = Constants.UniversalHWIDBlock;
+
+ Logger.WriteLine("Activation ID: " + actId);
+ Logger.WriteLine("Installation ID: " + instId);
+ Logger.WriteLine("Product Key ID: " + pkeyId);
+
+ byte[] iidHash;
+
+ if (version == PSVersion.Win7)
+ {
+ iidHash = CryptoUtils.SHA256Hash(Utils.EncodeString(instId));
+ }
+ else
+ {
+ iidHash = CryptoUtils.SHA256Hash(Utils.EncodeString(instId + '\0' + Constants.ZeroCID));
+ }
+
+ string key = string.Format("SPPSVC\\{0}\\{1}", appId, actId);
+ PSBlock keyBlock = store.GetBlock(key, pkeyId.ToString());
+
+ if (keyBlock == null)
+ {
+ throw new InvalidDataException("Failed to get product key data for activation ID " + actId + ".");
+ }
+
+ VariableBag pkb = new VariableBag(keyBlock.Data);
+
+ byte[] pkeyData;
+
+ if (version == PSVersion.Win7)
+ {
+ pkeyData = pkb.GetBlock("SppPkeyShortAuthenticator").Value;
+ }
+ else
+ {
+ pkeyData = pkb.GetBlock("SppPkeyPhoneActivationData").Value;
+ }
+
+ pkb.DeleteBlock("SppPkeyVirtual");
+ store.SetBlock(key, pkeyId.ToString(), pkb.Serialize());
+
+ BinaryWriter writer = new BinaryWriter(new MemoryStream());
+ writer.Write(0x20);
+ writer.Write(iidHash);
+ writer.Write(hwidBlock.Length);
+ writer.Write(hwidBlock);
+ byte[] tsHwidData = writer.GetBytes();
+
+ writer = new BinaryWriter(new MemoryStream());
+ writer.Write(0x20);
+ writer.Write(iidHash);
+ writer.Write(pkeyData.Length);
+ writer.Write(pkeyData);
+ byte[] tsPkeyInfoData = writer.GetBytes();
+
+ store.AddBlocks(new PSBlock[] {
+ new PSBlock
+ {
+ Type = BlockType.NAMED,
+ Flags = 0,
+ KeyAsStr = key,
+ ValueAsStr = "msft:Windows/7.0/Phone/Cached/HwidBlock/" + pkeyId,
+ Data = tsHwidData
+ },
+ new PSBlock
+ {
+ Type = BlockType.NAMED,
+ Flags = 0,
+ KeyAsStr = key,
+ ValueAsStr = "msft:Windows/7.0/Phone/Cached/PKeyInfo/" + pkeyId,
+ Data = tsPkeyInfoData
+ }
+ });
+ }
+
+ if (version != PSVersion.Win7)
+ {
+ Deposit(actId, instId);
+ }
+
+ SLApi.RefreshLicenseStatus();
+ SLApi.FireStateChangedEvent(appId);
+ Logger.WriteLine("Activated using ZeroCID successfully.");
+ }
+ }
+}
diff --git a/LibTSforge/Common.cs b/LibTSforge/Common.cs
new file mode 100644
index 0000000..d414ab5
--- /dev/null
+++ b/LibTSforge/Common.cs
@@ -0,0 +1,466 @@
+namespace LibTSforge
+{
+ using Microsoft.Win32;
+ using System;
+ using System.IO;
+ using System.Linq;
+ using System.Runtime.InteropServices;
+ using System.ServiceProcess;
+ using System.Text;
+ using LibTSforge.Crypto;
+ using LibTSforge.PhysicalStore;
+ using LibTSforge.SPP;
+ using LibTSforge.TokenStore;
+
+ public enum PSVersion
+ {
+ Vista,
+ Win7,
+ Win8Early,
+ Win8,
+ WinBlue,
+ WinModern
+ }
+
+ public static class Constants
+ {
+ public static readonly byte[] UniversalHWIDBlock =
+ {
+ 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x0c, 0x01, 0x00
+ };
+
+ public static readonly byte[] KMSv4Response =
+ {
+ 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x30, 0x00, 0x35, 0x00, 0x34, 0x00, 0x32, 0x00,
+ 0x36, 0x00, 0x2D, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x30, 0x00, 0x36, 0x00, 0x2D, 0x00,
+ 0x31, 0x00, 0x36, 0x00, 0x31, 0x00, 0x2D, 0x00, 0x36, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00,
+ 0x30, 0x00, 0x36, 0x00, 0x2D, 0x00, 0x30, 0x00, 0x33, 0x00, 0x2D, 0x00, 0x31, 0x00, 0x30, 0x00,
+ 0x33, 0x00, 0x33, 0x00, 0x2D, 0x00, 0x39, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2E, 0x00,
+ 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2D, 0x00, 0x30, 0x00, 0x36, 0x00, 0x35, 0x00,
+ 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x33, 0x00, 0x00, 0x00, 0xDE, 0x19, 0x02, 0xCF, 0x1F, 0x35,
+ 0x97, 0x4E, 0x8A, 0x8F, 0xB8, 0x07, 0xB1, 0x92, 0xB5, 0xB5, 0x97, 0x42, 0xEC, 0x3A, 0x76, 0x84,
+ 0xD5, 0x01, 0x32, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x60, 0x27, 0x00, 0x00, 0xC4, 0x1E,
+ 0xAA, 0x8B, 0xDD, 0x0C, 0xAB, 0x55, 0x6A, 0xCE, 0xAF, 0xAC, 0x7F, 0x5F, 0xBD, 0xE9
+ };
+
+ public static readonly byte[] KMSv5Response =
+ {
+ 0x00, 0x00, 0x05, 0x00, 0xBE, 0x96, 0xF9, 0x04, 0x54, 0x17, 0x3F, 0xAF, 0xE3, 0x08, 0x50, 0xEB,
+ 0x22, 0xBA, 0x53, 0xBF, 0xF2, 0x6A, 0x7B, 0xC9, 0x05, 0x1D, 0xB5, 0x19, 0xDF, 0x98, 0xE2, 0x71,
+ 0x4D, 0x00, 0x61, 0xE9, 0x9D, 0x03, 0xFB, 0x31, 0xF9, 0x1F, 0x2E, 0x60, 0x59, 0xC7, 0x73, 0xC8,
+ 0xE8, 0xB6, 0xE1, 0x2B, 0x39, 0xC6, 0x35, 0x0E, 0x68, 0x7A, 0xAA, 0x4F, 0x28, 0x23, 0x12, 0x18,
+ 0xE3, 0xAA, 0x84, 0x81, 0x6E, 0x82, 0xF0, 0x3F, 0xD9, 0x69, 0xA9, 0xDF, 0xBA, 0x5F, 0xCA, 0x32,
+ 0x54, 0xB2, 0x52, 0x3B, 0x3E, 0xD1, 0x5C, 0x65, 0xBC, 0x3E, 0x59, 0x0D, 0x15, 0x9F, 0x37, 0xEC,
+ 0x30, 0x9C, 0xCC, 0x1B, 0x39, 0x0D, 0x21, 0x32, 0x29, 0xA2, 0xDD, 0xC7, 0xC1, 0x69, 0xF2, 0x72,
+ 0x3F, 0x00, 0x98, 0x1E, 0xF8, 0x9A, 0x79, 0x44, 0x5D, 0x25, 0x80, 0x7B, 0xF5, 0xE1, 0x7C, 0x68,
+ 0x25, 0xAA, 0x0D, 0x67, 0x98, 0xE5, 0x59, 0x9B, 0x04, 0xC1, 0x23, 0x33, 0x48, 0xFB, 0x28, 0xD0,
+ 0x76, 0xDF, 0x01, 0x56, 0xE7, 0xEC, 0xBF, 0x1A, 0xA2, 0x22, 0x28, 0xCA, 0xB1, 0xB4, 0x4C, 0x30,
+ 0x14, 0x6F, 0xD2, 0x2E, 0x01, 0x2A, 0x04, 0xE3, 0xBD, 0xA7, 0x41, 0x2F, 0xC9, 0xEF, 0x53, 0xC0,
+ 0x70, 0x48, 0xF1, 0xB2, 0xB6, 0xEA, 0xE7, 0x0F, 0x7A, 0x15, 0xD1, 0xA6, 0xFE, 0x23, 0xC8, 0xF3,
+ 0xE1, 0x02, 0x9E, 0xA0, 0x4E, 0xBD, 0xF5, 0xEA, 0x53, 0x74, 0x8E, 0x74, 0xA1, 0xA1, 0xBD, 0xBE,
+ 0x66, 0xC4, 0x73, 0x8F, 0x24, 0xA7, 0x2A, 0x2F, 0xE3, 0xD9, 0xF4, 0x28, 0xD9, 0xF8, 0xA3, 0x93,
+ 0x03, 0x9E, 0x29, 0xAB
+ };
+
+ public static readonly byte[] KMSv6Response =
+ {
+ 0x00, 0x00, 0x06, 0x00, 0x54, 0xD3, 0x40, 0x08, 0xF3, 0xCD, 0x03, 0xEF, 0xC8, 0x15, 0x87, 0x9E,
+ 0xCA, 0x2E, 0x85, 0xFB, 0xE6, 0xF6, 0x73, 0x66, 0xFB, 0xDA, 0xBB, 0x7B, 0xB1, 0xBC, 0xD6, 0xF9,
+ 0x5C, 0x41, 0xA0, 0xFE, 0xE1, 0x74, 0xC4, 0xBB, 0x91, 0xE5, 0xDE, 0x6D, 0x3A, 0x11, 0xD5, 0xFC,
+ 0x68, 0xC0, 0x7B, 0x82, 0xB2, 0x24, 0xD1, 0x85, 0xBA, 0x45, 0xBF, 0xF1, 0x26, 0xFA, 0xA5, 0xC6,
+ 0x61, 0x70, 0x69, 0x69, 0x6E, 0x0F, 0x0B, 0x60, 0xB7, 0x3D, 0xE8, 0xF1, 0x47, 0x0B, 0x65, 0xFD,
+ 0xA7, 0x30, 0x1E, 0xF6, 0xA4, 0xD0, 0x79, 0xC4, 0x58, 0x8D, 0x81, 0xFD, 0xA7, 0xE7, 0x53, 0xF1,
+ 0x67, 0x78, 0xF0, 0x0F, 0x60, 0x8F, 0xC8, 0x16, 0x35, 0x22, 0x94, 0x48, 0xCB, 0x0F, 0x8E, 0xB2,
+ 0x1D, 0xF7, 0x3E, 0x28, 0x42, 0x55, 0x6B, 0x07, 0xE3, 0xE8, 0x51, 0xD5, 0xFA, 0x22, 0x0C, 0x86,
+ 0x65, 0x0D, 0x3F, 0xDD, 0x8D, 0x9B, 0x1B, 0xC9, 0xD3, 0xB8, 0x3A, 0xEC, 0xF1, 0x11, 0x19, 0x25,
+ 0xF7, 0x84, 0x4A, 0x4C, 0x0A, 0xB5, 0x31, 0x94, 0x37, 0x76, 0xCE, 0xE7, 0xAB, 0xA9, 0x69, 0xDF,
+ 0xA4, 0xC9, 0x22, 0x6C, 0x23, 0xFF, 0x6B, 0xFC, 0xDA, 0x78, 0xD8, 0xC4, 0x8F, 0x74, 0xBB, 0x26,
+ 0x05, 0x00, 0x98, 0x9B, 0xE5, 0xE2, 0xAD, 0x0D, 0x57, 0x95, 0x80, 0x66, 0x8E, 0x43, 0x74, 0x87,
+ 0x93, 0x1F, 0xF4, 0xB2, 0x2C, 0x20, 0x5F, 0xD8, 0x9C, 0x4C, 0x56, 0xB3, 0x57, 0x44, 0x62, 0x68,
+ 0x8D, 0xAA, 0x40, 0x11, 0x9D, 0x84, 0x62, 0x0E, 0x43, 0x8A, 0x1D, 0xF0, 0x1C, 0x49, 0xD8, 0x56,
+ 0xEF, 0x4C, 0xD3, 0x64, 0xBA, 0x0D, 0xEF, 0x87, 0xB5, 0x2C, 0x88, 0xF3, 0x18, 0xFF, 0x3A, 0x8C,
+ 0xF5, 0xA6, 0x78, 0x5C, 0x62, 0xE3, 0x9E, 0x4C, 0xB6, 0x31, 0x2D, 0x06, 0x80, 0x92, 0xBC, 0x2E,
+ 0x92, 0xA6, 0x56, 0x96
+ };
+
+ // 2^31 - 1 minutes
+ public static ulong TimerMax = (ulong)TimeSpan.FromMinutes(2147483647).Ticks;
+
+ public static readonly string ZeroCID = new string('0', 48);
+ }
+
+ public static class BinaryReaderExt
+ {
+ public static void Align(this BinaryReader reader, int to)
+ {
+ int pos = (int)reader.BaseStream.Position;
+ reader.BaseStream.Seek(-pos & (to - 1), SeekOrigin.Current);
+ }
+
+ public static string ReadNullTerminatedString(this BinaryReader reader, int maxLen)
+ {
+ return Encoding.Unicode.GetString(reader.ReadBytes(maxLen)).Split(new char[] { '\0' }, 2)[0];
+ }
+ }
+
+ public static class BinaryWriterExt
+ {
+ public static void Align(this BinaryWriter writer, int to)
+ {
+ int pos = (int)writer.BaseStream.Position;
+ writer.WritePadding(-pos & (to - 1));
+ }
+
+ public static void WritePadding(this BinaryWriter writer, int len)
+ {
+ writer.Write(Enumerable.Repeat((byte)0, len).ToArray());
+ }
+
+ public static void WriteFixedString(this BinaryWriter writer, string str, int bLen)
+ {
+ writer.Write(Encoding.ASCII.GetBytes(str));
+ writer.WritePadding(bLen - str.Length);
+ }
+
+ public static void WriteFixedString16(this BinaryWriter writer, string str, int bLen)
+ {
+ byte[] bstr = Utils.EncodeString(str);
+ writer.Write(bstr);
+ writer.WritePadding(bLen - bstr.Length);
+ }
+
+ public static byte[] GetBytes(this BinaryWriter writer)
+ {
+ return ((MemoryStream)writer.BaseStream).ToArray();
+ }
+ }
+
+ public static class ByteArrayExt
+ {
+ public static byte[] CastToArray<T>(this T data) where T : struct
+ {
+ int size = Marshal.SizeOf(typeof(T));
+ byte[] result = new byte[size];
+ GCHandle handle = GCHandle.Alloc(result, GCHandleType.Pinned);
+ try
+ {
+ Marshal.StructureToPtr(data, handle.AddrOfPinnedObject(), false);
+ }
+ finally
+ {
+ handle.Free();
+ }
+ return result;
+ }
+
+ public static T CastToStruct<T>(this byte[] data) where T : struct
+ {
+ GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
+ try
+ {
+ IntPtr ptr = handle.AddrOfPinnedObject();
+ return (T)Marshal.PtrToStructure(ptr, typeof(T));
+ }
+ finally
+ {
+ handle.Free();
+ }
+ }
+ }
+
+ public static class FileStreamExt
+ {
+ public static byte[] ReadAllBytes(this FileStream fs)
+ {
+ BinaryReader br = new BinaryReader(fs);
+ return br.ReadBytes((int)fs.Length);
+ }
+
+ public static void WriteAllBytes(this FileStream fs, byte[] data)
+ {
+ fs.Seek(0, SeekOrigin.Begin);
+ fs.SetLength(data.Length);
+ fs.Write(data, 0, data.Length);
+ }
+ }
+
+ public static class Utils
+ {
+ public static string DecodeString(byte[] data)
+ {
+ return Encoding.Unicode.GetString(data).Trim('\0');
+ }
+
+ public static byte[] EncodeString(string str)
+ {
+ return Encoding.Unicode.GetBytes(str + '\0');
+ }
+
+ [DllImport("kernel32.dll")]
+ public static extern uint GetSystemDefaultLCID();
+
+ public static uint CRC32(byte[] data)
+ {
+ const uint polynomial = 0x04C11DB7;
+ uint crc = 0xffffffff;
+
+ foreach (byte b in data)
+ {
+ crc ^= (uint)b << 24;
+ for (int bit = 0; bit < 8; bit++)
+ {
+ if ((crc & 0x80000000) != 0)
+ {
+ crc = (crc << 1) ^ polynomial;
+ }
+ else
+ {
+ crc <<= 1;
+ }
+ }
+ }
+ return ~crc;
+ }
+
+ public static void KillSPP()
+ {
+ ServiceController sc;
+
+ try
+ {
+ sc = new ServiceController("sppsvc");
+
+ if (sc.Status == ServiceControllerStatus.Stopped)
+ return;
+ }
+ catch (InvalidOperationException ex)
+ {
+ throw new InvalidOperationException("Unable to access sppsvc: " + ex.Message);
+ }
+
+ Logger.WriteLine("Stopping sppsvc...");
+
+ bool stopped = false;
+
+ for (int i = 0; stopped == false && i < 60; i++)
+ {
+ try
+ {
+ if (sc.Status != ServiceControllerStatus.StopPending)
+ sc.Stop();
+
+ sc.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromMilliseconds(500));
+ }
+ catch (System.ServiceProcess.TimeoutException)
+ {
+ continue;
+ }
+ catch (InvalidOperationException)
+ {
+ System.Threading.Thread.Sleep(500);
+ continue;
+ }
+
+ stopped = true;
+ }
+
+ if (!stopped)
+ throw new System.TimeoutException("Failed to stop sppsvc");
+
+ Logger.WriteLine("sppsvc stopped successfully.");
+ }
+
+ public static string GetPSPath(PSVersion version)
+ {
+ switch (version)
+ {
+ case PSVersion.Win7:
+ return Directory.GetFiles(
+ Environment.GetFolderPath(Environment.SpecialFolder.System),
+ "7B296FB0-376B-497e-B012-9C450E1B7327-*.C7483456-A289-439d-8115-601632D005A0")
+ .FirstOrDefault() ?? "";
+ case PSVersion.Win8Early:
+ case PSVersion.WinBlue:
+ case PSVersion.Win8:
+ case PSVersion.WinModern:
+ return Path.Combine(
+ Environment.ExpandEnvironmentVariables(
+ (string)Registry.GetValue(
+ @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform",
+ "TokenStore",
+ string.Empty
+ )
+ ),
+ "data.dat"
+ );
+ default:
+ return "";
+ }
+ }
+
+ public static string GetTokensPath(PSVersion version)
+ {
+ switch (version)
+ {
+ case PSVersion.Win7:
+ return Path.Combine(
+ Environment.ExpandEnvironmentVariables("%WINDIR%"),
+ @"ServiceProfiles\NetworkService\AppData\Roaming\Microsoft\SoftwareProtectionPlatform\tokens.dat"
+ );
+ case PSVersion.Win8Early:
+ case PSVersion.WinBlue:
+ case PSVersion.Win8:
+ case PSVersion.WinModern:
+ return Path.Combine(
+ Environment.ExpandEnvironmentVariables(
+ (string)Registry.GetValue(
+ @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform",
+ "TokenStore",
+ string.Empty
+ )
+ ),
+ "tokens.dat"
+ );
+ default:
+ return "";
+ }
+ }
+
+ public static IPhysicalStore GetStore(PSVersion version, bool production)
+ {
+ string psPath;
+
+ try
+ {
+ psPath = GetPSPath(version);
+ }
+ catch
+ {
+ throw new FileNotFoundException("Failed to get path of physical store.");
+ }
+
+ if (string.IsNullOrEmpty(psPath) || !File.Exists(psPath))
+ {
+ throw new FileNotFoundException(string.Format("Physical store not found at expected path {0}.", psPath));
+ }
+
+ if (version == PSVersion.Vista)
+ {
+ throw new NotSupportedException("Physical store editing is not supported for Windows Vista.");
+ }
+
+ return version == PSVersion.Win7 ? new PhysicalStoreWin7(psPath, production) : (IPhysicalStore)new PhysicalStoreModern(psPath, production, version);
+ }
+
+ public static ITokenStore GetTokenStore(PSVersion version)
+ {
+ string tokPath;
+
+ try
+ {
+ tokPath = GetTokensPath(version);
+ }
+ catch
+ {
+ throw new FileNotFoundException("Failed to get path of physical store.");
+ }
+
+ if (string.IsNullOrEmpty(tokPath) || !File.Exists(tokPath))
+ {
+ throw new FileNotFoundException(string.Format("Token store not found at expected path {0}.", tokPath));
+ }
+
+ return new TokenStoreModern(tokPath);
+ }
+
+ public static string GetArchitecture()
+ {
+ string arch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE", EnvironmentVariableTarget.Machine).ToUpperInvariant();
+ return arch == "AMD64" ? "X64" : arch;
+ }
+
+ public static PSVersion DetectVersion()
+ {
+ int build = Environment.OSVersion.Version.Build;
+
+ if (build >= 9600) return PSVersion.WinModern;
+ if (build >= 6000 && build <= 6003) return PSVersion.Vista;
+ if (build >= 7600 && build <= 7602) return PSVersion.Win7;
+ if (build == 9200) return PSVersion.Win8;
+
+ throw new NotSupportedException("Unable to auto-detect version info, please specify one manually using the /ver argument.");
+ }
+
+ public static bool DetectCurrentKey()
+ {
+ SLApi.RefreshLicenseStatus();
+
+ using (RegistryKey wpaKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\WPA"))
+ {
+ foreach (string subKey in wpaKey.GetSubKeyNames())
+ {
+ if (subKey.StartsWith("8DEC0AF1") && subKey.EndsWith("-1"))
+ {
+ return subKey.Contains("P");
+ }
+ }
+ }
+
+ throw new FileNotFoundException("Failed to autodetect key type, specify physical store key with /prod or /test arguments.");
+ }
+
+ public static void DumpStore(PSVersion version, bool production, string filePath, string encrFilePath)
+ {
+ if (encrFilePath == null)
+ {
+ encrFilePath = GetPSPath(version);
+ }
+
+ if (string.IsNullOrEmpty(encrFilePath) || !File.Exists(encrFilePath))
+ {
+ throw new FileNotFoundException("Store does not exist at expected path '" + encrFilePath + "'.");
+ }
+
+ KillSPP();
+
+ using (FileStream fs = File.Open(encrFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
+ {
+ byte[] encrData = fs.ReadAllBytes();
+ File.WriteAllBytes(filePath, PhysStoreCrypto.DecryptPhysicalStore(encrData, production));
+ }
+
+ Logger.WriteLine("Store dumped successfully to '" + filePath + "'.");
+ }
+
+ public static void LoadStore(PSVersion version, bool production, string filePath)
+ {
+ if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
+ {
+ throw new FileNotFoundException("Store file '" + filePath + "' does not exist.");
+ }
+
+ KillSPP();
+
+ using (IPhysicalStore store = GetStore(version, production))
+ {
+ store.WriteRaw(File.ReadAllBytes(filePath));
+ }
+
+ Logger.WriteLine("Loaded store file succesfully.");
+ }
+ }
+
+ public static class Logger
+ {
+ public static bool HideOutput = false;
+
+ public static void WriteLine(string line)
+ {
+ if (!HideOutput) Console.WriteLine(line);
+ }
+ }
+}
diff --git a/LibTSforge/Crypto/CryptoUtils.cs b/LibTSforge/Crypto/CryptoUtils.cs
new file mode 100644
index 0000000..4851570
--- /dev/null
+++ b/LibTSforge/Crypto/CryptoUtils.cs
@@ -0,0 +1,121 @@
+namespace LibTSforge.Crypto
+{
+ using System;
+ using System.Linq;
+ using System.Security.Cryptography;
+
+ public static class CryptoUtils
+ {
+ public static byte[] GenerateRandomKey(int len)
+ {
+ byte[] rand = new byte[len];
+ Random r = new Random();
+ r.NextBytes(rand);
+
+ return rand;
+ }
+
+ public static byte[] AESEncrypt(byte[] data, byte[] key)
+ {
+ using (Aes aes = Aes.Create())
+ {
+ aes.Key = key;
+ aes.Mode = CipherMode.CBC;
+ aes.Padding = PaddingMode.PKCS7;
+
+ ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, Enumerable.Repeat((byte)0, 16).ToArray());
+ byte[] encryptedData = encryptor.TransformFinalBlock(data, 0, data.Length);
+ return encryptedData;
+ }
+ }
+
+ public static byte[] AESDecrypt(byte[] data, byte[] key)
+ {
+ using (Aes aes = Aes.Create())
+ {
+ aes.Key = key;
+ aes.Mode = CipherMode.CBC;
+ aes.Padding = PaddingMode.PKCS7;
+
+ ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, Enumerable.Repeat((byte)0, 16).ToArray());
+ byte[] decryptedData = decryptor.TransformFinalBlock(data, 0, data.Length);
+ return decryptedData;
+ }
+ }
+
+ public static byte[] RSADecrypt(byte[] rsaKey, byte[] data)
+ {
+
+ using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
+ {
+ rsa.ImportCspBlob(rsaKey);
+ return rsa.Decrypt(data, false);
+ }
+ }
+
+ public static byte[] RSAEncrypt(byte[] rsaKey, byte[] data)
+ {
+ using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
+ {
+ rsa.ImportCspBlob(rsaKey);
+ return rsa.Encrypt(data, false);
+ }
+ }
+
+ public static byte[] RSASign(byte[] rsaKey, byte[] data)
+ {
+ using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
+ {
+ rsa.ImportCspBlob(rsaKey);
+ RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(rsa);
+ formatter.SetHashAlgorithm("SHA1");
+
+ byte[] hash;
+ using (SHA1 sha1 = SHA1.Create())
+ {
+ hash = sha1.ComputeHash(data);
+ }
+
+ return formatter.CreateSignature(hash);
+ }
+ }
+
+ public static bool RSAVerifySignature(byte[] rsaKey, byte[] data, byte[] signature)
+ {
+ using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
+ {
+ rsa.ImportCspBlob(rsaKey);
+ RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(rsa);
+ deformatter.SetHashAlgorithm("SHA1");
+
+ byte[] hash;
+ using (SHA1 sha1 = SHA1.Create())
+ {
+ hash = sha1.ComputeHash(data);
+ }
+
+ return deformatter.VerifySignature(hash, signature);
+ }
+ }
+
+ public static byte[] HMACSign(byte[] key, byte[] data)
+ {
+ HMACSHA1 hmac = new HMACSHA1(key);
+ return hmac.ComputeHash(data);
+ }
+
+ public static bool HMACVerify(byte[] key, byte[] data, byte[] signature)
+ {
+ HMACSHA1 hmac = new HMACSHA1(key);
+ return Enumerable.SequenceEqual(signature, HMACSign(key, data));
+ }
+
+ public static byte[] SHA256Hash(byte[] data)
+ {
+ using (SHA256 sha256 = SHA256.Create())
+ {
+ return sha256.ComputeHash(data);
+ }
+ }
+ }
+}
diff --git a/LibTSforge/Crypto/Keys.cs b/LibTSforge/Crypto/Keys.cs
new file mode 100644
index 0000000..2950185
--- /dev/null
+++ b/LibTSforge/Crypto/Keys.cs
@@ -0,0 +1,87 @@
+namespace LibTSforge.Crypto
+{
+ public static class Keys
+ {
+ public static readonly byte[] PRODUCTION = {
+ 0x07, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x52, 0x53, 0x41, 0x32, 0x00, 0x04, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x29, 0x87, 0xBA, 0x3F, 0x52, 0x90, 0x57, 0xD8, 0x12, 0x26, 0x6B, 0x38,
+ 0xB2, 0x3B, 0xF9, 0x67, 0x08, 0x4F, 0xDD, 0x8B, 0xF5, 0xE3, 0x11, 0xB8, 0x61, 0x3A, 0x33, 0x42,
+ 0x51, 0x65, 0x05, 0x86, 0x1E, 0x00, 0x41, 0xDE, 0xC5, 0xDD, 0x44, 0x60, 0x56, 0x3D, 0x14, 0x39,
+ 0xB7, 0x43, 0x65, 0xE9, 0xF7, 0x2B, 0xA5, 0xF0, 0xA3, 0x65, 0x68, 0xE9, 0xE4, 0x8B, 0x5C, 0x03,
+ 0x2D, 0x36, 0xFE, 0x28, 0x4C, 0xD1, 0x3C, 0x3D, 0xC1, 0x90, 0x75, 0xF9, 0x6E, 0x02, 0xE0, 0x58,
+ 0x97, 0x6A, 0xCA, 0x80, 0x02, 0x42, 0x3F, 0x6C, 0x15, 0x85, 0x4D, 0x83, 0x23, 0x6A, 0x95, 0x9E,
+ 0x38, 0x52, 0x59, 0x38, 0x6A, 0x99, 0xF0, 0xB5, 0xCD, 0x53, 0x7E, 0x08, 0x7C, 0xB5, 0x51, 0xD3,
+ 0x8F, 0xA3, 0x0D, 0xA0, 0xFA, 0x8D, 0x87, 0x3C, 0xFC, 0x59, 0x21, 0xD8, 0x2E, 0xD9, 0x97, 0x8B,
+ 0x40, 0x60, 0xB1, 0xD7, 0x2B, 0x0A, 0x6E, 0x60, 0xB5, 0x50, 0xCC, 0x3C, 0xB1, 0x57, 0xE4, 0xB7,
+ 0xDC, 0x5A, 0x4D, 0xE1, 0x5C, 0xE0, 0x94, 0x4C, 0x5E, 0x28, 0xFF, 0xFA, 0x80, 0x6A, 0x13, 0x53,
+ 0x52, 0xDB, 0xF3, 0x04, 0x92, 0x43, 0x38, 0xB9, 0x1B, 0xD9, 0x85, 0x54, 0x7B, 0x14, 0xC7, 0x89,
+ 0x16, 0x8A, 0x4B, 0x82, 0xA1, 0x08, 0x02, 0x99, 0x23, 0x48, 0xDD, 0x75, 0x9C, 0xC8, 0xC1, 0xCE,
+ 0xB0, 0xD7, 0x1B, 0xD8, 0xFB, 0x2D, 0xA7, 0x2E, 0x47, 0xA7, 0x18, 0x4B, 0xF6, 0x29, 0x69, 0x44,
+ 0x30, 0x33, 0xBA, 0xA7, 0x1F, 0xCE, 0x96, 0x9E, 0x40, 0xE1, 0x43, 0xF0, 0xE0, 0x0D, 0x0A, 0x32,
+ 0xB4, 0xEE, 0xA1, 0xC3, 0x5E, 0x9B, 0xC7, 0x7F, 0xF5, 0x9D, 0xD8, 0xF2, 0x0F, 0xD9, 0x8F, 0xAD,
+ 0x75, 0x0A, 0x00, 0xD5, 0x25, 0x43, 0xF7, 0xAE, 0x51, 0x7F, 0xB7, 0xDE, 0xB7, 0xAD, 0xFB, 0xCE,
+ 0x83, 0xE1, 0x81, 0xFF, 0xDD, 0xA2, 0x77, 0xFE, 0xEB, 0x27, 0x1F, 0x10, 0xFA, 0x82, 0x37, 0xF4,
+ 0x7E, 0xCC, 0xE2, 0xA1, 0x58, 0xC8, 0xAF, 0x1D, 0x1A, 0x81, 0x31, 0x6E, 0xF4, 0x8B, 0x63, 0x34,
+ 0xF3, 0x05, 0x0F, 0xE1, 0xCC, 0x15, 0xDC, 0xA4, 0x28, 0x7A, 0x9E, 0xEB, 0x62, 0xD8, 0xD8, 0x8C,
+ 0x85, 0xD7, 0x07, 0x87, 0x90, 0x2F, 0xF7, 0x1C, 0x56, 0x85, 0x2F, 0xEF, 0x32, 0x37, 0x07, 0xAB,
+ 0xB0, 0xE6, 0xB5, 0x02, 0x19, 0x35, 0xAF, 0xDB, 0xD4, 0xA2, 0x9C, 0x36, 0x80, 0xC6, 0xDC, 0x82,
+ 0x08, 0xE0, 0xC0, 0x5F, 0x3C, 0x59, 0xAA, 0x4E, 0x26, 0x03, 0x29, 0xB3, 0x62, 0x58, 0x41, 0x59,
+ 0x3A, 0x37, 0x43, 0x35, 0xE3, 0x9F, 0x34, 0xE2, 0xA1, 0x04, 0x97, 0x12, 0x9D, 0x8C, 0xAD, 0xF7,
+ 0xFB, 0x8C, 0xA1, 0xA2, 0xE9, 0xE4, 0xEF, 0xD9, 0xC5, 0xE5, 0xDF, 0x0E, 0xBF, 0x4A, 0xE0, 0x7A,
+ 0x1E, 0x10, 0x50, 0x58, 0x63, 0x51, 0xE1, 0xD4, 0xFE, 0x57, 0xB0, 0x9E, 0xD7, 0xDA, 0x8C, 0xED,
+ 0x7D, 0x82, 0xAC, 0x2F, 0x25, 0x58, 0x0A, 0x58, 0xE6, 0xA4, 0xF4, 0x57, 0x4B, 0xA4, 0x1B, 0x65,
+ 0xB9, 0x4A, 0x87, 0x46, 0xEB, 0x8C, 0x0F, 0x9A, 0x48, 0x90, 0xF9, 0x9F, 0x76, 0x69, 0x03, 0x72,
+ 0x77, 0xEC, 0xC1, 0x42, 0x4C, 0x87, 0xDB, 0x0B, 0x3C, 0xD4, 0x74, 0xEF, 0xE5, 0x34, 0xE0, 0x32,
+ 0x45, 0xB0, 0xF8, 0xAB, 0xD5, 0x26, 0x21, 0xD7, 0xD2, 0x98, 0x54, 0x8F, 0x64, 0x88, 0x20, 0x2B,
+ 0x14, 0xE3, 0x82, 0xD5, 0x2A, 0x4B, 0x8F, 0x4E, 0x35, 0x20, 0x82, 0x7E, 0x1B, 0xFE, 0xFA, 0x2C,
+ 0x79, 0x6C, 0x6E, 0x66, 0x94, 0xBB, 0x0A, 0xEB, 0xBA, 0xD9, 0x70, 0x61, 0xE9, 0x47, 0xB5, 0x82,
+ 0xFC, 0x18, 0x3C, 0x66, 0x3A, 0x09, 0x2E, 0x1F, 0x61, 0x74, 0xCA, 0xCB, 0xF6, 0x7A, 0x52, 0x37,
+ 0x1D, 0xAC, 0x8D, 0x63, 0x69, 0x84, 0x8E, 0xC7, 0x70, 0x59, 0xDD, 0x2D, 0x91, 0x1E, 0xF7, 0xB1,
+ 0x56, 0xED, 0x7A, 0x06, 0x9D, 0x5B, 0x33, 0x15, 0xDD, 0x31, 0xD0, 0xE6, 0x16, 0x07, 0x9B, 0xA5,
+ 0x94, 0x06, 0x7D, 0xC1, 0xE9, 0xD6, 0xC8, 0xAF, 0xB4, 0x1E, 0x2D, 0x88, 0x06, 0xA7, 0x63, 0xB8,
+ 0xCF, 0xC8, 0xA2, 0x6E, 0x84, 0xB3, 0x8D, 0xE5, 0x47, 0xE6, 0x13, 0x63, 0x8E, 0xD1, 0x7F, 0xD4,
+ 0x81, 0x44, 0x38, 0xBF
+ };
+
+ public static readonly byte[] TEST = {
+ 0x07, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x52, 0x53, 0x41, 0x32, 0x00, 0x04, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x0F, 0xBE, 0x77, 0xB8, 0xDD, 0x54, 0x36, 0xDD, 0x67, 0xD4, 0x17, 0x66,
+ 0xC4, 0x13, 0xD1, 0x3F, 0x1E, 0x16, 0x0C, 0x16, 0x35, 0xAB, 0x6D, 0x3D, 0x34, 0x51, 0xED, 0x3F,
+ 0x57, 0x14, 0xB6, 0xB7, 0x08, 0xE9, 0xD9, 0x7A, 0x80, 0xB3, 0x5F, 0x9B, 0x3A, 0xFD, 0x9E, 0x37,
+ 0x3A, 0x53, 0x72, 0x67, 0x92, 0x60, 0xC3, 0xEF, 0xB5, 0x8E, 0x1E, 0xCF, 0x9D, 0x9C, 0xD3, 0x90,
+ 0xE5, 0xDD, 0xF4, 0xDB, 0xF3, 0xD6, 0x65, 0xB3, 0xC1, 0xBD, 0x69, 0xE1, 0x76, 0x95, 0xD9, 0x37,
+ 0xB8, 0x5E, 0xCA, 0x3D, 0x98, 0xFC, 0x50, 0x5C, 0x98, 0xAE, 0xE3, 0x7C, 0x4C, 0x27, 0xC3, 0xD0,
+ 0xCE, 0x78, 0x06, 0x51, 0x68, 0x23, 0xE6, 0x70, 0xF8, 0x7C, 0xAE, 0x36, 0xBE, 0x41, 0x57, 0xE2,
+ 0xC3, 0x2D, 0xAF, 0x21, 0xB1, 0xB3, 0x15, 0x81, 0x19, 0x26, 0x6B, 0x10, 0xB3, 0xE9, 0xD1, 0x45,
+ 0x21, 0x77, 0x9C, 0xF6, 0xE1, 0xDD, 0xB6, 0x78, 0x9D, 0x1D, 0x32, 0x61, 0xBC, 0x2B, 0xDB, 0x86,
+ 0xFB, 0x07, 0x24, 0x10, 0x19, 0x4F, 0x09, 0x6D, 0x03, 0x90, 0xD4, 0x5E, 0x30, 0x85, 0xC5, 0x58,
+ 0x7E, 0x5D, 0xAE, 0x9F, 0x64, 0x93, 0x04, 0x82, 0x09, 0x0E, 0x1C, 0x66, 0xA8, 0x95, 0x91, 0x51,
+ 0xB2, 0xED, 0x9A, 0x75, 0x04, 0x87, 0x50, 0xAC, 0xCC, 0x20, 0x06, 0x45, 0xB9, 0x7B, 0x42, 0x53,
+ 0x9A, 0xD1, 0x29, 0xFC, 0xEF, 0xB9, 0x47, 0x16, 0x75, 0x69, 0x05, 0x87, 0x2B, 0xCB, 0x54, 0x9C,
+ 0x21, 0x2D, 0x50, 0x8E, 0x12, 0xDE, 0xD3, 0x6B, 0xEC, 0x92, 0xA1, 0xB1, 0xE9, 0x4B, 0xBF, 0x6B,
+ 0x9A, 0x38, 0xC7, 0x13, 0xFA, 0x78, 0xA1, 0x3C, 0x1E, 0xBB, 0x38, 0x31, 0xBB, 0x0C, 0x9F, 0x70,
+ 0x1A, 0x31, 0x00, 0xD7, 0x5A, 0xA5, 0x84, 0x24, 0x89, 0x80, 0xF5, 0x88, 0xC2, 0x31, 0x18, 0xDC,
+ 0x53, 0x05, 0x5D, 0xFA, 0x81, 0xDC, 0xE1, 0xCE, 0xA4, 0xAA, 0xBA, 0x07, 0xDA, 0x28, 0x4F, 0x64,
+ 0x0E, 0x84, 0x9B, 0x06, 0xDE, 0xC8, 0x78, 0x66, 0x2F, 0x17, 0x25, 0xA8, 0x9C, 0x99, 0xFC, 0xBC,
+ 0x7D, 0x01, 0x42, 0xD7, 0x35, 0xBF, 0x19, 0xF6, 0x3F, 0x20, 0xD9, 0x98, 0x9B, 0x5D, 0xDD, 0x39,
+ 0xBE, 0x81, 0x00, 0x0B, 0xDE, 0x6F, 0x14, 0xCA, 0x7E, 0xF8, 0xC0, 0x26, 0xA8, 0x1D, 0xD1, 0x16,
+ 0x88, 0x64, 0x87, 0x36, 0x45, 0x37, 0x50, 0xDA, 0x6C, 0xEB, 0x85, 0xB5, 0x43, 0x29, 0x88, 0x6F,
+ 0x2F, 0xFE, 0x8D, 0x12, 0x8B, 0x72, 0xB7, 0x5A, 0xCB, 0x66, 0xC2, 0x2E, 0x1D, 0x7D, 0x42, 0xA6,
+ 0xF4, 0xFE, 0x26, 0x5D, 0x54, 0x9E, 0x77, 0x1D, 0x97, 0xC2, 0xF3, 0xFD, 0x60, 0xB3, 0x22, 0x88,
+ 0xCA, 0x27, 0x99, 0xDF, 0xC8, 0xB1, 0xD7, 0xC6, 0x54, 0xA6, 0x50, 0xB9, 0x54, 0xF5, 0xDE, 0xFE,
+ 0xE1, 0x81, 0xA2, 0xBE, 0x81, 0x9F, 0x48, 0xFF, 0x2F, 0xB8, 0xA4, 0xB3, 0x17, 0xD8, 0xC1, 0xB9,
+ 0x5D, 0x21, 0x3D, 0xA2, 0xED, 0x1C, 0x96, 0x66, 0xEE, 0x1F, 0x47, 0xCF, 0x62, 0xFA, 0xD6, 0xC1,
+ 0x87, 0x5B, 0xC4, 0xE5, 0xD9, 0x08, 0x38, 0x22, 0xFA, 0x21, 0xBD, 0xF2, 0x88, 0xDA, 0xE2, 0x24,
+ 0x25, 0x1F, 0xF1, 0x0B, 0x2D, 0xAE, 0x04, 0xBE, 0xA6, 0x7F, 0x75, 0x8C, 0xD9, 0x97, 0xE1, 0xCA,
+ 0x35, 0xB9, 0xFC, 0x6F, 0x01, 0x68, 0x11, 0xD3, 0x68, 0x32, 0xD0, 0xC1, 0x69, 0xA3, 0xCF, 0x9B,
+ 0x10, 0xE4, 0x69, 0xA7, 0xCF, 0xE1, 0xFE, 0x2A, 0x07, 0x9E, 0xC1, 0x37, 0x84, 0x68, 0xE5, 0xC5,
+ 0xAB, 0x25, 0xEC, 0x7D, 0x7D, 0x74, 0x6A, 0xD1, 0xD5, 0x4D, 0xD7, 0xE1, 0x7D, 0xDE, 0x30, 0x4B,
+ 0xE6, 0x5D, 0xCD, 0x91, 0x59, 0xF6, 0x80, 0xFD, 0xC6, 0x3C, 0xDD, 0x94, 0x7F, 0x15, 0x9D, 0xEF,
+ 0x2F, 0x00, 0x62, 0xD7, 0xDA, 0xB9, 0xB3, 0xD9, 0x8D, 0xE8, 0xD7, 0x3C, 0x96, 0x45, 0x5D, 0x1E,
+ 0x50, 0xFB, 0xAA, 0x43, 0xD3, 0x47, 0x77, 0x81, 0xE9, 0x67, 0xE4, 0xFE, 0xDF, 0x42, 0x79, 0xCB,
+ 0xA7, 0xAD, 0x5D, 0x48, 0xF5, 0xB7, 0x74, 0x96, 0x12, 0x23, 0x06, 0x70, 0x42, 0x68, 0x7A, 0x44,
+ 0xFC, 0xA0, 0x31, 0x7F, 0x68, 0xCA, 0xA2, 0x14, 0x5D, 0xA3, 0xCF, 0x42, 0x23, 0xAB, 0x47, 0xF6,
+ 0xB2, 0xFC, 0x6D, 0xF1
+ };
+ }
+}
diff --git a/LibTSforge/Crypto/PhysStoreCrypto.cs b/LibTSforge/Crypto/PhysStoreCrypto.cs
new file mode 100644
index 0000000..6d77897
--- /dev/null
+++ b/LibTSforge/Crypto/PhysStoreCrypto.cs
@@ -0,0 +1,71 @@
+namespace LibTSforge.Crypto
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+
+ public static class PhysStoreCrypto
+ {
+ public static byte[] DecryptPhysicalStore(byte[] data, bool production)
+ {
+ byte[] rsaKey = production ? Keys.PRODUCTION : Keys.TEST;
+ BinaryReader br = new BinaryReader(new MemoryStream(data));
+ br.BaseStream.Seek(0x10, SeekOrigin.Begin);
+ byte[] aesKeySig = br.ReadBytes(0x80);
+ byte[] encAesKey = br.ReadBytes(0x80);
+
+ if (CryptoUtils.RSAVerifySignature(rsaKey, encAesKey, aesKeySig))
+ {
+ byte[] aesKey = CryptoUtils.RSADecrypt(rsaKey, encAesKey);
+ byte[] decData = CryptoUtils.AESDecrypt(br.ReadBytes((int)br.BaseStream.Length - 0x110), aesKey);
+ byte[] hmacKey = decData.Take(0x10).ToArray();
+ byte[] hmacSig = decData.Skip(0x10).Take(0x14).ToArray();
+ byte[] psData = decData.Skip(0x28).ToArray();
+
+ if (!CryptoUtils.HMACVerify(hmacKey, psData, hmacSig))
+ {
+ Logger.WriteLine("Warning: Failed to verify HMAC. Physical store is either corrupt or in Vista format.");
+ }
+
+ return psData;
+ }
+
+ throw new Exception("Failed to decrypt physical store.");
+ }
+
+ public static byte[] EncryptPhysicalStore(byte[] data, bool production, PSVersion version)
+ {
+ Dictionary<PSVersion, int> versionTable = new Dictionary<PSVersion, int>
+ {
+ {PSVersion.Win7, 5},
+ {PSVersion.Win8, 1},
+ {PSVersion.WinBlue, 2},
+ {PSVersion.WinModern, 3}
+ };
+
+ byte[] rsaKey = production ? Keys.PRODUCTION : Keys.TEST;
+
+ byte[] aesKey = Encoding.UTF8.GetBytes("massgrave.dev :3");
+ byte[] hmacKey = CryptoUtils.GenerateRandomKey(0x10);
+
+ byte[] encAesKey = CryptoUtils.RSAEncrypt(rsaKey, aesKey);
+ byte[] aesKeySig = CryptoUtils.RSASign(rsaKey, encAesKey);
+ byte[] hmacSig = CryptoUtils.HMACSign(hmacKey, data);
+
+ byte[] decData = new byte[] { };
+ decData = decData.Concat(hmacKey).Concat(hmacSig).Concat(BitConverter.GetBytes(0)).Concat(data).ToArray();
+ byte[] encData = CryptoUtils.AESEncrypt(decData, aesKey);
+
+ BinaryWriter bw = new BinaryWriter(new MemoryStream());
+ bw.Write(versionTable[version]);
+ bw.Write(Encoding.UTF8.GetBytes("UNTRUSTSTORE"));
+ bw.Write(aesKeySig);
+ bw.Write(encAesKey);
+ bw.Write(encData);
+
+ return bw.GetBytes();
+ }
+ }
+}
diff --git a/LibTSforge/LibTSforge.csproj b/LibTSforge/LibTSforge.csproj
new file mode 100644
index 0000000..8c3504a
--- /dev/null
+++ b/LibTSforge/LibTSforge.csproj
@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Library</OutputType>
+ <TargetFramework>net35</TargetFramework>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <LangVersion>3</LangVersion>
+ <PackageReadmeFile>README.md</PackageReadmeFile>
+ <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+ <DebugType>none</DebugType>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <None Include="..\README.md">
+ <Pack>True</Pack>
+ <PackagePath>\</PackagePath>
+ </None>
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="System.ServiceProcess" />
+ </ItemGroup>
+
+</Project>
diff --git a/LibTSforge/Modifiers/GenPKeyInstall.cs b/LibTSforge/Modifiers/GenPKeyInstall.cs
new file mode 100644
index 0000000..28d0027
--- /dev/null
+++ b/LibTSforge/Modifiers/GenPKeyInstall.cs
@@ -0,0 +1,207 @@
+namespace LibTSforge.Modifiers
+{
+ using System;
+ using System.IO;
+ using Microsoft.Win32;
+ using LibTSforge.PhysicalStore;
+ using LibTSforge.SPP;
+ using LibTSforge.TokenStore;
+
+ public static class GenPKeyInstall
+ {
+ private static void WritePkey2005RegistryValues(PSVersion version, ProductKey pkey)
+ {
+ Logger.WriteLine("Writing registry data for Windows product key...");
+ Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductId", pkey.GetPid2());
+ Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "DigitalProductId", pkey.GetPid3());
+ Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "DigitalProductId4", pkey.GetPid4());
+
+ if (Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Registration", "ProductId", null) != null)
+ {
+ Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Registration", "ProductId", pkey.GetPid2());
+ Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Registration", "DigitalProductId", pkey.GetPid3());
+ Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Registration", "DigitalProductId4", pkey.GetPid4());
+ }
+
+ if (pkey.Channel == "Volume:CSVLK" && version != PSVersion.Win7)
+ {
+ Registry.SetValue(@"HKEY_USERS\S-1-5-20\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform", "KmsHostConfig", 1);
+ }
+ }
+
+ public static void InstallGenPKey(PSVersion version, bool production, Guid actId)
+ {
+ if (actId == Guid.Empty) throw new ArgumentException("Activation ID must be specified for generated product key install.");
+
+ PKeyConfig pkc = new PKeyConfig();
+
+ try
+ {
+ pkc.LoadConfig(actId);
+ }
+ catch (ArgumentException)
+ {
+ pkc.LoadAllConfigs(SLApi.GetAppId(actId));
+ }
+
+ ProductConfig config;
+ pkc.Products.TryGetValue(actId, out config);
+
+ if (config == null) throw new ArgumentException("Activation ID " + actId + " not found in PKeyConfig.");
+
+ ProductKey pkey = config.GetRandomKey();
+
+ Guid instPkeyId = SLApi.GetInstalledPkeyID(actId);
+ if (instPkeyId != Guid.Empty) SLApi.UninstallProductKey(instPkeyId);
+
+ if (pkey.Algorithm == PKeyAlgorithm.PKEY2009)
+ {
+ uint status = SLApi.InstallProductKey(pkey);
+ Logger.WriteLine(string.Format("Installing generated product key {0} status {1:X}", pkey.ToString(), status));
+
+ if ((int)status < 0)
+ {
+ throw new ApplicationException("Failed to install generated product key.");
+ }
+
+ Logger.WriteLine("Successfully deposited generated product key.");
+ return;
+ }
+
+ Logger.WriteLine("Key range is PKEY2005, creating fake key data...");
+
+ if (pkey.Channel == "Volume:GVLK" && version == PSVersion.Win7) throw new NotSupportedException("Fake GVLK generation is not supported on Windows 7.");
+
+ VariableBag pkb = new VariableBag();
+ pkb.Blocks.AddRange(new CRCBlock[]
+ {
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ KeyAsStr = "SppPkeyBindingProductKey",
+ ValueAsStr = pkey.ToString()
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ KeyAsStr = "SppPkeyBindingMPC",
+ ValueAsStr = pkey.GetMPC()
+ },
+ new CRCBlock {
+ DataType = CRCBlockType.BINARY,
+ KeyAsStr = "SppPkeyBindingPid2",
+ ValueAsStr = pkey.GetPid2()
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ KeyAsStr = "SppPkeyBindingPid3",
+ Value = pkey.GetPid3()
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ KeyAsStr = "SppPkeyBindingPid4",
+ Value = pkey.GetPid4()
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ KeyAsStr = "SppPkeyChannelId",
+ ValueAsStr = pkey.Channel
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.STRING,
+ KeyAsStr = "SppPkeyBindingEditionId",
+ ValueAsStr = pkey.Edition
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ KeyAsStr = (version == PSVersion.Win7) ? "SppPkeyShortAuthenticator" : "SppPkeyPhoneActivationData",
+ Value = pkey.GetPhoneData(version)
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ KeyAsStr = "SppPkeyBindingMiscData",
+ Value = new byte[] { }
+ }
+ });
+
+ Guid appId = SLApi.GetAppId(actId);
+ string pkeyId = pkey.GetPkeyId().ToString();
+ bool isAddon = SLApi.IsAddon(actId);
+ string currEdition = SLApi.GetMetaStr(actId, "Family");
+
+ if (appId == SLApi.WINDOWS_APP_ID && !isAddon)
+ {
+ SLApi.UninstallAllProductKeys(appId);
+ }
+
+ Utils.KillSPP();
+
+ using (IPhysicalStore ps = Utils.GetStore(version, production))
+ {
+ using (ITokenStore tks = Utils.GetTokenStore(version))
+ {
+ Logger.WriteLine("Writing to physical store and token store...");
+
+ string suffix = (version == PSVersion.Win8 || version == PSVersion.WinBlue || version == PSVersion.WinModern) ? "_--" : "";
+ string metSuffix = suffix + "_met";
+
+ if (appId == SLApi.WINDOWS_APP_ID && !isAddon)
+ {
+ string edTokName = "msft:spp/token/windows/productkeyid/" + currEdition;
+
+ TokenMeta edToken = tks.GetMetaEntry(edTokName);
+ edToken.Data["windowsComponentEditionPkeyId"] = pkeyId;
+ edToken.Data["windowsComponentEditionSkuId"] = actId.ToString();
+ tks.SetEntry(edTokName, "xml", edToken.Serialize());
+
+ WritePkey2005RegistryValues(version, pkey);
+ }
+
+ string uriMapName = "msft:spp/token/PKeyIdUriMapper" + metSuffix;
+ TokenMeta uriMap = tks.GetMetaEntry(uriMapName);
+ uriMap.Data[pkeyId] = pkey.GetAlgoUri();
+ tks.SetEntry(uriMapName, "xml", uriMap.Serialize());
+
+ string skuMetaName = actId.ToString() + metSuffix;
+ TokenMeta skuMeta = tks.GetMetaEntry(skuMetaName);
+
+ foreach (string k in skuMeta.Data.Keys)
+ {
+ if (k.StartsWith("pkeyId_"))
+ {
+ skuMeta.Data.Remove(k);
+ break;
+ }
+ }
+
+ skuMeta.Data["pkeyId"] = pkeyId;
+ skuMeta.Data["pkeyIdList"] = pkeyId;
+ tks.SetEntry(skuMetaName, "xml", skuMeta.Serialize());
+
+ string psKey = string.Format("SPPSVC\\{0}\\{1}", appId, actId);
+ ps.DeleteBlock(psKey, pkeyId);
+ ps.AddBlock(new PSBlock
+ {
+ Type = BlockType.NAMED,
+ Flags = (version == PSVersion.WinModern) ? (uint)0x402 : 0x2,
+ KeyAsStr = psKey,
+ ValueAsStr = pkeyId,
+ Data = pkb.Serialize()
+ });
+
+ string cachePath = Utils.GetTokensPath(version).Replace("tokens.dat", @"cache\cache.dat");
+ if (File.Exists(cachePath)) File.Delete(cachePath);
+ }
+ }
+
+ SLApi.RefreshTrustedTime(actId);
+ Logger.WriteLine("Successfully deposited fake product key.");
+ }
+ }
+}
diff --git a/LibTSforge/Modifiers/GracePeriodReset.cs b/LibTSforge/Modifiers/GracePeriodReset.cs
new file mode 100644
index 0000000..4743ea6
--- /dev/null
+++ b/LibTSforge/Modifiers/GracePeriodReset.cs
@@ -0,0 +1,29 @@
+namespace LibTSforge.Modifiers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using LibTSforge.PhysicalStore;
+
+ public static class GracePeriodReset
+ {
+ public static void Reset(PSVersion version, bool production)
+ {
+ Utils.KillSPP();
+ Logger.WriteLine("Writing TrustedStore data...");
+
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ string value = "msft:sl/timer";
+ List<PSBlock> blocks = store.FindBlocks(value).ToList();
+
+ foreach (PSBlock block in blocks)
+ {
+ store.DeleteBlock(block.KeyAsStr, block.ValueAsStr);
+ }
+ }
+
+ Logger.WriteLine("Successfully reset all grace and evaluation period timers.");
+ }
+ }
+}
diff --git a/LibTSforge/Modifiers/KMSHostCharge.cs b/LibTSforge/Modifiers/KMSHostCharge.cs
new file mode 100644
index 0000000..5bfabdf
--- /dev/null
+++ b/LibTSforge/Modifiers/KMSHostCharge.cs
@@ -0,0 +1,119 @@
+namespace LibTSforge.Modifiers
+{
+ using System;
+ using System.IO;
+ using LibTSforge.PhysicalStore;
+ using LibTSforge.SPP;
+
+ public static class KMSHostCharge
+ {
+ public static void Charge(PSVersion version, Guid actId, bool production)
+ {
+ if (actId == Guid.Empty)
+ {
+ actId = SLApi.GetDefaultActivationID(SLApi.WINDOWS_APP_ID, true);
+
+ if (actId == Guid.Empty)
+ {
+ throw new NotSupportedException("No applicable activation IDs found.");
+ }
+ }
+
+ if (SLApi.GetPKeyChannel(SLApi.GetInstalledPkeyID(actId)) != "Volume:CSVLK")
+ {
+ throw new NotSupportedException("Non-Volume:CSVLK product key installed.");
+ }
+
+ Guid appId = SLApi.GetAppId(actId);
+ int totalClients = 50;
+ int currClients = 25;
+ byte[] hwidBlock = Constants.UniversalHWIDBlock;
+ string key = string.Format("SPPSVC\\{0}", appId);
+ long ldapTimestamp = DateTime.Now.ToFileTime();
+
+ BinaryWriter writer = new BinaryWriter(new MemoryStream());
+
+ for (int i = 0; i < currClients; i++)
+ {
+ writer.Write(ldapTimestamp - (10 * (i + 1)));
+ writer.Write(Guid.NewGuid().ToByteArray());
+ }
+
+ byte[] cmidGuids = writer.GetBytes();
+
+ writer = new BinaryWriter(new MemoryStream());
+
+ writer.Write(new byte[40]);
+
+ writer.Seek(4, SeekOrigin.Begin);
+ writer.Write((byte)currClients);
+
+ writer.Seek(24, SeekOrigin.Begin);
+ writer.Write((byte)currClients);
+ byte[] reqCounts = writer.GetBytes();
+
+ Utils.KillSPP();
+
+ Logger.WriteLine("Writing TrustedStore data...");
+
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ VariableBag kmsCountData = new VariableBag();
+ kmsCountData.Blocks.AddRange(new CRCBlock[]
+ {
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ KeyAsStr = "SppBindingLicenseData",
+ Value = hwidBlock
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.UINT,
+ Key = new byte[] { },
+ ValueAsInt = (uint)totalClients
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.UINT,
+ Key = new byte[] { },
+ ValueAsInt = 1051200000
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.UINT,
+ Key = new byte[] { },
+ ValueAsInt = (uint)currClients
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ Key = new byte[] { },
+ Value = cmidGuids
+ },
+ new CRCBlock
+ {
+ DataType = CRCBlockType.BINARY,
+ Key = new byte[] { },
+ Value = reqCounts
+ }
+ });
+
+ byte[] kmsChargeData = kmsCountData.Serialize();
+ string countVal = string.Format("msft:spp/kms/host/2.0/store/counters/{0}", appId);
+
+ store.DeleteBlock(key, countVal);
+ store.AddBlock(new PSBlock
+ {
+ Type = BlockType.NAMED,
+ Flags = (version == PSVersion.WinModern) ? (uint)0x400 : 0,
+ KeyAsStr = key,
+ ValueAsStr = countVal,
+ Data = kmsChargeData
+ });
+
+ Logger.WriteLine(string.Format("Set charge count to {0} successfully.", currClients));
+ }
+ }
+ }
+}
diff --git a/LibTSforge/Modifiers/KeyChangeLockDelete.cs b/LibTSforge/Modifiers/KeyChangeLockDelete.cs
new file mode 100644
index 0000000..e3f32c4
--- /dev/null
+++ b/LibTSforge/Modifiers/KeyChangeLockDelete.cs
@@ -0,0 +1,33 @@
+namespace LibTSforge.Modifiers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using LibTSforge.PhysicalStore;
+ using LibTSforge;
+ public static class KeyChangeLockDelete
+ {
+ public static void Delete(PSVersion version, bool production)
+ {
+ Utils.KillSPP();
+ Logger.WriteLine("Writing TrustedStore data...");
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ List<string> values = new List<string>
+ {
+ "msft:spp/timebased/AB",
+ "msft:spp/timebased/CD"
+ };
+ List<PSBlock> blocks = new List<PSBlock>();
+ foreach (string value in values)
+ {
+ blocks.AddRange(store.FindBlocks(value).ToList());
+ }
+ foreach (PSBlock block in blocks)
+ {
+ store.DeleteBlock(block.KeyAsStr, block.ValueAsStr);
+ }
+ }
+ Logger.WriteLine("Successfully removed the key change lock.");
+ }
+ }
+}
diff --git a/LibTSforge/Modifiers/RearmReset.cs b/LibTSforge/Modifiers/RearmReset.cs
new file mode 100644
index 0000000..be2b174
--- /dev/null
+++ b/LibTSforge/Modifiers/RearmReset.cs
@@ -0,0 +1,45 @@
+namespace LibTSforge.Modifiers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using LibTSforge.PhysicalStore;
+
+ public static class RearmReset
+ {
+ public static void Reset(PSVersion version, bool production)
+ {
+ Utils.KillSPP();
+
+ Logger.WriteLine("Writing TrustedStore data...");
+
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ List<PSBlock> blocks;
+
+ if (version == PSVersion.Win7)
+ {
+ blocks = store.FindBlocks(0xA0000).ToList();
+ }
+ else
+ {
+ blocks = store.FindBlocks("__##USERSEP-RESERVED##__$$REARM-COUNT$$").ToList();
+ }
+
+ foreach (PSBlock block in blocks)
+ {
+ if (version == PSVersion.Win7)
+ {
+ store.SetBlock(block.KeyAsStr, block.ValueAsInt, new byte[8]);
+ }
+ else
+ {
+ store.SetBlock(block.KeyAsStr, block.ValueAsStr, new byte[8]);
+ }
+ }
+
+ Logger.WriteLine("Successfully reset all rearm counters.");
+ }
+ }
+ }
+}
diff --git a/LibTSforge/Modifiers/TamperedFlagsDelete.cs b/LibTSforge/Modifiers/TamperedFlagsDelete.cs
new file mode 100644
index 0000000..8ffb370
--- /dev/null
+++ b/LibTSforge/Modifiers/TamperedFlagsDelete.cs
@@ -0,0 +1,44 @@
+namespace LibTSforge.Modifiers
+{
+ using System;
+ using System.Linq;
+ using LibTSforge.PhysicalStore;
+
+ public static class TamperedFlagsDelete
+ {
+ public static void DeleteTamperFlags(PSVersion version, bool production)
+ {
+ Utils.KillSPP();
+
+ Logger.WriteLine("Writing TrustedStore data...");
+
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ if (version != PSVersion.Win7)
+ {
+ string recreatedFlag = "__##USERSEP-RESERVED##__$$RECREATED-FLAG$$";
+ string recoveredFlag = "__##USERSEP-RESERVED##__$$RECOVERED-FLAG$$";
+
+ DeleteFlag(store, recreatedFlag);
+ DeleteFlag(store, recoveredFlag);
+ }
+ else
+ {
+ SetFlag(store, 0xA0001);
+ }
+
+ Logger.WriteLine("Successfully cleared the tamper state.");
+ }
+ }
+
+ private static void DeleteFlag(IPhysicalStore store, string flag)
+ {
+ store.FindBlocks(flag).ToList().ForEach(block => store.DeleteBlock(block.KeyAsStr, block.ValueAsStr));
+ }
+
+ private static void SetFlag(IPhysicalStore store, uint flag)
+ {
+ store.FindBlocks(flag).ToList().ForEach(block => store.SetBlock(block.KeyAsStr, block.ValueAsInt, new byte[8]));
+ }
+ }
+}
diff --git a/LibTSforge/Modifiers/UniqueIdDelete.cs b/LibTSforge/Modifiers/UniqueIdDelete.cs
new file mode 100644
index 0000000..b83d328
--- /dev/null
+++ b/LibTSforge/Modifiers/UniqueIdDelete.cs
@@ -0,0 +1,55 @@
+namespace LibTSforge.Modifiers
+{
+ using System;
+ using LibTSforge.PhysicalStore;
+ using LibTSforge.SPP;
+
+ public static class UniqueIdDelete
+ {
+ public static void DeleteUniqueId(PSVersion version, bool production, Guid actId)
+ {
+ Guid appId;
+
+ if (actId == Guid.Empty)
+ {
+ appId = SLApi.WINDOWS_APP_ID;
+ actId = SLApi.GetDefaultActivationID(appId, true);
+
+ if (actId == Guid.Empty)
+ {
+ throw new Exception("No applicable activation IDs found.");
+ }
+ }
+ else
+ {
+ appId = SLApi.GetAppId(actId);
+ }
+
+ string instId = SLApi.GetInstallationID(actId);
+ Guid pkeyId = SLApi.GetInstalledPkeyID(actId);
+
+ Utils.KillSPP();
+
+ Logger.WriteLine("Writing TrustedStore data...");
+
+ using (IPhysicalStore store = Utils.GetStore(version, production))
+ {
+ string key = string.Format("SPPSVC\\{0}\\{1}", appId, actId);
+ PSBlock keyBlock = store.GetBlock(key, pkeyId.ToString());
+
+ if (keyBlock == null)
+ {
+ throw new Exception("No product key found.");
+ }
+
+ VariableBag pkb = new VariableBag(keyBlock.Data);
+
+ pkb.DeleteBlock("SppPkeyUniqueIdToken");
+
+ store.SetBlock(key, pkeyId.ToString(), pkb.Serialize());
+ }
+
+ Logger.WriteLine("Successfully removed Unique ID for product key ID " + pkeyId);
+ }
+ }
+}
diff --git a/LibTSforge/PhysicalStore/Common.cs b/LibTSforge/PhysicalStore/Common.cs
new file mode 100644
index 0000000..f73f022
--- /dev/null
+++ b/LibTSforge/PhysicalStore/Common.cs
@@ -0,0 +1,21 @@
+namespace LibTSforge.PhysicalStore
+{
+ using System.Runtime.InteropServices;
+
+ public enum BlockType : uint
+ {
+ NONE,
+ NAMED,
+ ATTRIBUTE,
+ TIMER
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct Timer
+ {
+ public ulong Unknown;
+ public ulong Time1;
+ public ulong Time2;
+ public ulong Expiry;
+ }
+}
diff --git a/LibTSforge/PhysicalStore/IPhysicalStore.cs b/LibTSforge/PhysicalStore/IPhysicalStore.cs
new file mode 100644
index 0000000..e451281
--- /dev/null
+++ b/LibTSforge/PhysicalStore/IPhysicalStore.cs
@@ -0,0 +1,92 @@
+namespace LibTSforge.PhysicalStore
+{
+ using System;
+ using System.Collections.Generic;
+
+ public class PSBlock
+ {
+ public BlockType Type;
+ public uint Flags;
+ public uint Unknown = 0;
+ public byte[] Key;
+ public string KeyAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Key);
+ }
+ set
+ {
+ Key = Utils.EncodeString(value);
+ }
+ }
+ public byte[] Value;
+ public string ValueAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Value);
+ }
+ set
+ {
+ Value = Utils.EncodeString(value);
+ }
+ }
+ public uint ValueAsInt
+ {
+ get
+ {
+ return BitConverter.ToUInt32(Value, 0);
+ }
+ set
+ {
+ Value = BitConverter.GetBytes(value);
+ }
+ }
+ public byte[] Data;
+ public string DataAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Data);
+ }
+ set
+ {
+ Data = Utils.EncodeString(value);
+ }
+ }
+ public uint DataAsInt
+ {
+ get
+ {
+ return BitConverter.ToUInt32(Data, 0);
+ }
+ set
+ {
+ Data = BitConverter.GetBytes(value);
+ }
+ }
+ }
+
+ public interface IPhysicalStore : IDisposable
+ {
+ PSBlock GetBlock(string key, string value);
+ PSBlock GetBlock(string key, uint value);
+ void AddBlock(PSBlock block);
+ void AddBlocks(IEnumerable<PSBlock> blocks);
+ void SetBlock(string key, string value, byte[] data);
+ void SetBlock(string key, string value, string data);
+ void SetBlock(string key, string value, uint data);
+ void SetBlock(string key, uint value, byte[] data);
+ void SetBlock(string key, uint value, string data);
+ void SetBlock(string key, uint value, uint data);
+ void DeleteBlock(string key, string value);
+ void DeleteBlock(string key, uint value);
+ byte[] Serialize();
+ void Deserialize(byte[] data);
+ byte[] ReadRaw();
+ void WriteRaw(byte[] data);
+ IEnumerable<PSBlock> FindBlocks(string valueSearch);
+ IEnumerable<PSBlock> FindBlocks(uint valueSearch);
+ }
+}
diff --git a/LibTSforge/PhysicalStore/PhysicalStoreModern.cs b/LibTSforge/PhysicalStore/PhysicalStoreModern.cs
new file mode 100644
index 0000000..f697bea
--- /dev/null
+++ b/LibTSforge/PhysicalStore/PhysicalStoreModern.cs
@@ -0,0 +1,411 @@
+namespace LibTSforge.PhysicalStore
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using LibTSforge.Crypto;
+
+ public class ModernBlock
+ {
+ public BlockType Type;
+ public uint Flags;
+ public uint Unknown;
+ public byte[] Value;
+ public string ValueAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Value);
+ }
+ set
+ {
+ Value = Utils.EncodeString(value);
+ }
+ }
+ public uint ValueAsInt
+ {
+ get
+ {
+ return BitConverter.ToUInt32(Value, 0);
+ }
+ set
+ {
+ Value = BitConverter.GetBytes(value);
+ }
+ }
+ public byte[] Data;
+ public string DataAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Data);
+ }
+ set
+ {
+ Data = Utils.EncodeString(value);
+ }
+ }
+ public uint DataAsInt
+ {
+ get
+ {
+ return BitConverter.ToUInt32(Data, 0);
+ }
+ set
+ {
+ Data = BitConverter.GetBytes(value);
+ }
+ }
+
+ public void Encode(BinaryWriter writer)
+ {
+ writer.Write((uint)Type);
+ writer.Write(Flags);
+ writer.Write((uint)Value.Length);
+ writer.Write((uint)Data.Length);
+ writer.Write(Unknown);
+ writer.Write(Value);
+ writer.Write(Data);
+ }
+
+ public static ModernBlock Decode(BinaryReader reader)
+ {
+ uint type = reader.ReadUInt32();
+ uint flags = reader.ReadUInt32();
+
+ uint valueLen = reader.ReadUInt32();
+ uint dataLen = reader.ReadUInt32();
+ uint unk3 = reader.ReadUInt32();
+
+ byte[] value = reader.ReadBytes((int)valueLen);
+ byte[] data = reader.ReadBytes((int)dataLen);
+
+ return new ModernBlock
+ {
+ Type = (BlockType)type,
+ Flags = flags,
+ Unknown = unk3,
+ Value = value,
+ Data = data,
+ };
+ }
+ }
+
+ public sealed class PhysicalStoreModern : IPhysicalStore
+ {
+ private byte[] PreHeaderBytes = new byte[] { };
+ private readonly Dictionary<string, List<ModernBlock>> Data = new Dictionary<string, List<ModernBlock>>();
+ private readonly FileStream TSFile;
+ private readonly PSVersion Version;
+ private readonly bool Production;
+
+ public byte[] Serialize()
+ {
+ BinaryWriter writer = new BinaryWriter(new MemoryStream());
+ writer.Write(PreHeaderBytes);
+ writer.Write(Data.Keys.Count);
+
+ foreach (string key in Data.Keys)
+ {
+ List<ModernBlock> blocks = Data[key];
+ byte[] keyNameEnc = Utils.EncodeString(key);
+
+ writer.Write(keyNameEnc.Length);
+ writer.Write(keyNameEnc);
+ writer.Write(blocks.Count);
+ writer.Align(4);
+
+ foreach (ModernBlock block in blocks)
+ {
+ block.Encode(writer);
+ writer.Align(4);
+ }
+ }
+
+ return writer.GetBytes();
+ }
+
+ public void Deserialize(byte[] data)
+ {
+ BinaryReader reader = new BinaryReader(new MemoryStream(data));
+ PreHeaderBytes = reader.ReadBytes(8);
+
+ while (reader.BaseStream.Position < data.Length - 0x4)
+ {
+ uint numKeys = reader.ReadUInt32();
+
+ for (int i = 0; i < numKeys; i++)
+ {
+ uint lenKeyName = reader.ReadUInt32();
+ string keyName = Utils.DecodeString(reader.ReadBytes((int)lenKeyName)); uint numValues = reader.ReadUInt32();
+
+ reader.Align(4);
+
+ Data[keyName] = new List<ModernBlock>();
+
+ for (int j = 0; j < numValues; j++)
+ {
+ Data[keyName].Add(ModernBlock.Decode(reader));
+ reader.Align(4);
+ }
+ }
+ }
+ }
+
+ public void AddBlock(PSBlock block)
+ {
+ if (!Data.ContainsKey(block.KeyAsStr))
+ {
+ Data[block.KeyAsStr] = new List<ModernBlock>();
+ }
+
+ Data[block.KeyAsStr].Add(new ModernBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ Unknown = block.Unknown,
+ Value = block.Value,
+ Data = block.Data
+ });
+ }
+
+ public void AddBlocks(IEnumerable<PSBlock> blocks)
+ {
+ foreach (PSBlock block in blocks)
+ {
+ AddBlock(block);
+ }
+ }
+
+ public PSBlock GetBlock(string key, string value)
+ {
+ List<ModernBlock> blocks = Data[key];
+
+ foreach (ModernBlock block in blocks)
+ {
+ if (block.ValueAsStr == value)
+ {
+ return new PSBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ Key = Utils.EncodeString(key),
+ Value = block.Value,
+ Data = block.Data
+ };
+ }
+ }
+
+ return null;
+ }
+
+ public PSBlock GetBlock(string key, uint value)
+ {
+ List<ModernBlock> blocks = Data[key];
+
+ foreach (ModernBlock block in blocks)
+ {
+ if (block.ValueAsInt == value)
+ {
+ return new PSBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ Key = Utils.EncodeString(key),
+ Value = block.Value,
+ Data = block.Data
+ };
+ }
+ }
+
+ return null;
+ }
+
+ public void SetBlock(string key, string value, byte[] data)
+ {
+ List<ModernBlock> blocks = Data[key];
+
+ for (int i = 0; i < blocks.Count; i++)
+ {
+ ModernBlock block = blocks[i];
+
+ if (block.ValueAsStr == value)
+ {
+ block.Data = data;
+ blocks[i] = block;
+ break;
+ }
+ }
+
+ Data[key] = blocks;
+ }
+
+ public void SetBlock(string key, uint value, byte[] data)
+ {
+ List<ModernBlock> blocks = Data[key];
+
+ for (int i = 0; i < blocks.Count; i++)
+ {
+ ModernBlock block = blocks[i];
+
+ if (block.ValueAsInt == value)
+ {
+ block.Data = data;
+ blocks[i] = block;
+ break;
+ }
+ }
+
+ Data[key] = blocks;
+ }
+
+ public void SetBlock(string key, string value, string data)
+ {
+ SetBlock(key, value, Utils.EncodeString(data));
+ }
+
+ public void SetBlock(string key, string value, uint data)
+ {
+ SetBlock(key, value, BitConverter.GetBytes(data));
+ }
+
+ public void SetBlock(string key, uint value, string data)
+ {
+ SetBlock(key, value, Utils.EncodeString(data));
+ }
+
+ public void SetBlock(string key, uint value, uint data)
+ {
+ SetBlock(key, value, BitConverter.GetBytes(data));
+ }
+
+ public void DeleteBlock(string key, string value)
+ {
+ if (Data.ContainsKey(key))
+ {
+ List<ModernBlock> blocks = Data[key];
+
+ foreach (ModernBlock block in blocks)
+ {
+ if (block.ValueAsStr == value)
+ {
+ blocks.Remove(block);
+ break;
+ }
+ }
+
+ Data[key] = blocks;
+ }
+ }
+
+ public void DeleteBlock(string key, uint value)
+ {
+ if (Data.ContainsKey(key))
+ {
+ List<ModernBlock> blocks = Data[key];
+
+ foreach (ModernBlock block in blocks)
+ {
+ if (block.ValueAsInt == value)
+ {
+ blocks.Remove(block);
+ break;
+ }
+ }
+
+ Data[key] = blocks;
+ }
+ }
+
+ public PhysicalStoreModern(string tsPath, bool production, PSVersion version)
+ {
+ TSFile = File.Open(tsPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
+ Deserialize(PhysStoreCrypto.DecryptPhysicalStore(TSFile.ReadAllBytes(), production));
+ TSFile.Seek(0, SeekOrigin.Begin);
+ Version = version;
+ Production = production;
+ }
+
+ public void Dispose()
+ {
+ if (TSFile.CanWrite)
+ {
+ byte[] data = PhysStoreCrypto.EncryptPhysicalStore(Serialize(), Production, Version);
+ TSFile.SetLength(data.LongLength);
+ TSFile.Seek(0, SeekOrigin.Begin);
+ TSFile.WriteAllBytes(data);
+ TSFile.Close();
+ }
+ }
+
+ public byte[] ReadRaw()
+ {
+ byte[] data = PhysStoreCrypto.DecryptPhysicalStore(TSFile.ReadAllBytes(), Production);
+ TSFile.Seek(0, SeekOrigin.Begin);
+ return data;
+ }
+
+ public void WriteRaw(byte[] data)
+ {
+ byte[] encrData = PhysStoreCrypto.EncryptPhysicalStore(data, Production, Version);
+ TSFile.SetLength(encrData.LongLength);
+ TSFile.Seek(0, SeekOrigin.Begin);
+ TSFile.WriteAllBytes(encrData);
+ TSFile.Close();
+ }
+
+ public IEnumerable<PSBlock> FindBlocks(string valueSearch)
+ {
+ List<PSBlock> results = new List<PSBlock>();
+
+ foreach (string key in Data.Keys)
+ {
+ List<ModernBlock> values = Data[key];
+
+ foreach (ModernBlock block in values)
+ {
+ if (block.ValueAsStr.Contains(valueSearch))
+ {
+ results.Add(new PSBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ KeyAsStr = key,
+ Value = block.Value,
+ Data = block.Data
+ });
+ }
+ }
+ }
+
+ return results;
+ }
+
+ public IEnumerable<PSBlock> FindBlocks(uint valueSearch)
+ {
+ List<PSBlock> results = new List<PSBlock>();
+
+ foreach (string key in Data.Keys)
+ {
+ List<ModernBlock> values = Data[key];
+
+ foreach (ModernBlock block in values)
+ {
+ if (block.ValueAsInt == valueSearch)
+ {
+ results.Add(new PSBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ KeyAsStr = key,
+ Value = block.Value,
+ Data = block.Data
+ });
+ }
+ }
+ }
+
+ return results;
+ }
+ }
+}
diff --git a/LibTSforge/PhysicalStore/PhysicalStoreWin7.cs b/LibTSforge/PhysicalStore/PhysicalStoreWin7.cs
new file mode 100644
index 0000000..c1af391
--- /dev/null
+++ b/LibTSforge/PhysicalStore/PhysicalStoreWin7.cs
@@ -0,0 +1,374 @@
+namespace LibTSforge.PhysicalStore
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using LibTSforge.Crypto;
+
+ public class Win7Block
+ {
+ public BlockType Type;
+ public uint Flags;
+ public byte[] Key;
+ public string KeyAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Key);
+ }
+ set
+ {
+ Key = Utils.EncodeString(value);
+ }
+ }
+ public byte[] Value;
+ public string ValueAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Value);
+ }
+ set
+ {
+ Value = Utils.EncodeString(value);
+ }
+ }
+ public uint ValueAsInt
+ {
+ get
+ {
+ return BitConverter.ToUInt32(Value, 0);
+ }
+ set
+ {
+ Value = BitConverter.GetBytes(value);
+ }
+ }
+ public byte[] Data;
+ public string DataAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Data);
+ }
+ set
+ {
+ Data = Utils.EncodeString(value);
+ }
+ }
+ public uint DataAsInt
+ {
+ get
+ {
+ return BitConverter.ToUInt32(Data, 0);
+ }
+ set
+ {
+ Data = BitConverter.GetBytes(value);
+ }
+ }
+
+ internal void Encode(BinaryWriter writer)
+ {
+ writer.Write((uint)Type);
+ writer.Write(Flags);
+ writer.Write(Key.Length);
+ writer.Write(Value.Length);
+ writer.Write(Data.Length);
+ writer.Write(Key);
+ writer.Write(Value);
+ writer.Write(Data);
+ }
+
+ internal static Win7Block Decode(BinaryReader reader)
+ {
+ uint type = reader.ReadUInt32();
+ uint flags = reader.ReadUInt32();
+
+ int keyLen = reader.ReadInt32();
+ int valueLen = reader.ReadInt32();
+ int dataLen = reader.ReadInt32();
+
+ byte[] key = reader.ReadBytes(keyLen);
+ byte[] value = reader.ReadBytes(valueLen);
+ byte[] data = reader.ReadBytes(dataLen);
+ return new Win7Block
+ {
+ Type = (BlockType)type,
+ Flags = flags,
+ Key = key,
+ Value = value,
+ Data = data,
+ };
+ }
+ }
+
+ public sealed class PhysicalStoreWin7 : IPhysicalStore
+ {
+ private byte[] PreHeaderBytes = new byte[] { };
+ private readonly List<Win7Block> Blocks = new List<Win7Block>();
+ private readonly FileStream TSPrimary;
+ private readonly FileStream TSSecondary;
+ private readonly bool Production;
+
+ public byte[] Serialize()
+ {
+ BinaryWriter writer = new BinaryWriter(new MemoryStream());
+ writer.Write(PreHeaderBytes);
+
+ foreach (Win7Block block in Blocks)
+ {
+ block.Encode(writer);
+ writer.Align(4);
+ }
+
+ return writer.GetBytes();
+ }
+
+ public void Deserialize(byte[] data)
+ {
+ int len = data.Length;
+
+ BinaryReader reader = new BinaryReader(new MemoryStream(data));
+ PreHeaderBytes = reader.ReadBytes(8);
+
+ while (reader.BaseStream.Position < len - 0x14)
+ {
+ Blocks.Add(Win7Block.Decode(reader));
+ reader.Align(4);
+ }
+ }
+
+ public void AddBlock(PSBlock block)
+ {
+ Blocks.Add(new Win7Block
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ Key = block.Key,
+ Value = block.Value,
+ Data = block.Data
+ });
+ }
+
+ public void AddBlocks(IEnumerable<PSBlock> blocks)
+ {
+ foreach (PSBlock block in blocks)
+ {
+ AddBlock(block);
+ }
+ }
+
+ public PSBlock GetBlock(string key, string value)
+ {
+ foreach (Win7Block block in Blocks)
+ {
+ if (block.KeyAsStr == key && block.ValueAsStr == value)
+ {
+ return new PSBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ Key = block.Key,
+ Value = block.Value,
+ Data = block.Data
+ };
+ }
+ }
+
+ return null;
+ }
+
+ public PSBlock GetBlock(string key, uint value)
+ {
+ foreach (Win7Block block in Blocks)
+ {
+ if (block.KeyAsStr == key && block.ValueAsInt == value)
+ {
+ return new PSBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ Key = block.Key,
+ Value = block.Value,
+ Data = block.Data
+ };
+ }
+ }
+
+ return null;
+ }
+
+ public void SetBlock(string key, string value, byte[] data)
+ {
+ for (int i = 0; i < Blocks.Count; i++)
+ {
+ Win7Block block = Blocks[i];
+
+ if (block.KeyAsStr == key && block.ValueAsStr == value)
+ {
+ block.Data = data;
+ Blocks[i] = block;
+ break;
+ }
+ }
+ }
+
+ public void SetBlock(string key, uint value, byte[] data)
+ {
+ for (int i = 0; i < Blocks.Count; i++)
+ {
+ Win7Block block = Blocks[i];
+
+ if (block.KeyAsStr == key && block.ValueAsInt == value)
+ {
+ block.Data = data;
+ Blocks[i] = block;
+ break;
+ }
+ }
+ }
+
+ public void SetBlock(string key, string value, string data)
+ {
+ SetBlock(key, value, Utils.EncodeString(data));
+ }
+
+ public void SetBlock(string key, string value, uint data)
+ {
+ SetBlock(key, value, BitConverter.GetBytes(data));
+ }
+
+ public void SetBlock(string key, uint value, string data)
+ {
+ SetBlock(key, value, Utils.EncodeString(data));
+ }
+
+ public void SetBlock(string key, uint value, uint data)
+ {
+ SetBlock(key, value, BitConverter.GetBytes(data));
+ }
+
+ public void DeleteBlock(string key, string value)
+ {
+ foreach (Win7Block block in Blocks)
+ {
+ if (block.KeyAsStr == key && block.ValueAsStr == value)
+ {
+ Blocks.Remove(block);
+ return;
+ }
+ }
+ }
+
+ public void DeleteBlock(string key, uint value)
+ {
+ foreach (Win7Block block in Blocks)
+ {
+ if (block.KeyAsStr == key && block.ValueAsInt == value)
+ {
+ Blocks.Remove(block);
+ return;
+ }
+ }
+ }
+
+ public PhysicalStoreWin7(string primaryPath, bool production)
+ {
+ TSPrimary = File.Open(primaryPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
+ TSSecondary = File.Open(primaryPath.Replace("-0.", "-1."), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
+ Production = production;
+
+ Deserialize(PhysStoreCrypto.DecryptPhysicalStore(TSPrimary.ReadAllBytes(), production));
+ TSPrimary.Seek(0, SeekOrigin.Begin);
+ }
+
+ public void Dispose()
+ {
+ if (TSPrimary.CanWrite && TSSecondary.CanWrite)
+ {
+ byte[] data = PhysStoreCrypto.EncryptPhysicalStore(Serialize(), Production, PSVersion.Win7);
+
+ TSPrimary.SetLength(data.LongLength);
+ TSSecondary.SetLength(data.LongLength);
+
+ TSPrimary.Seek(0, SeekOrigin.Begin);
+ TSSecondary.Seek(0, SeekOrigin.Begin);
+
+ TSPrimary.WriteAllBytes(data);
+ TSSecondary.WriteAllBytes(data);
+
+ TSPrimary.Close();
+ TSSecondary.Close();
+ }
+ }
+
+ public byte[] ReadRaw()
+ {
+ byte[] data = PhysStoreCrypto.DecryptPhysicalStore(TSPrimary.ReadAllBytes(), Production);
+ TSPrimary.Seek(0, SeekOrigin.Begin);
+ return data;
+ }
+
+ public void WriteRaw(byte[] data)
+ {
+ byte[] encrData = PhysStoreCrypto.EncryptPhysicalStore(data, Production, PSVersion.Win7);
+
+ TSPrimary.SetLength(encrData.LongLength);
+ TSSecondary.SetLength(encrData.LongLength);
+
+ TSPrimary.Seek(0, SeekOrigin.Begin);
+ TSSecondary.Seek(0, SeekOrigin.Begin);
+
+ TSPrimary.WriteAllBytes(encrData);
+ TSSecondary.WriteAllBytes(encrData);
+
+ TSPrimary.Close();
+ TSSecondary.Close();
+ }
+
+ public IEnumerable<PSBlock> FindBlocks(string valueSearch)
+ {
+ List<PSBlock> results = new List<PSBlock>();
+
+ foreach (Win7Block block in Blocks)
+ {
+ if (block.ValueAsStr.Contains(valueSearch))
+ {
+ results.Add(new PSBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ Key = block.Key,
+ Value = block.Value,
+ Data = block.Data
+ });
+ }
+ }
+
+ return results;
+ }
+
+ public IEnumerable<PSBlock> FindBlocks(uint valueSearch)
+ {
+ List<PSBlock> results = new List<PSBlock>();
+
+ foreach (Win7Block block in Blocks)
+ {
+ if (block.ValueAsInt == valueSearch)
+ {
+ results.Add(new PSBlock
+ {
+ Type = block.Type,
+ Flags = block.Flags,
+ Key = block.Key,
+ Value = block.Value,
+ Data = block.Data
+ });
+ }
+ }
+
+ return results;
+ }
+ }
+}
diff --git a/LibTSforge/PhysicalStore/VariableBag.cs b/LibTSforge/PhysicalStore/VariableBag.cs
new file mode 100644
index 0000000..ddc2efe
--- /dev/null
+++ b/LibTSforge/PhysicalStore/VariableBag.cs
@@ -0,0 +1,187 @@
+namespace LibTSforge.PhysicalStore
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+
+ public enum CRCBlockType : uint
+ {
+ UINT = 1 << 0,
+ STRING = 1 << 1,
+ BINARY = 1 << 2
+ }
+
+ public class CRCBlock
+ {
+ public CRCBlockType DataType;
+ public byte[] Key;
+ public string KeyAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Key);
+ }
+ set
+ {
+ Key = Utils.EncodeString(value);
+ }
+ }
+ public byte[] Value;
+ public string ValueAsStr
+ {
+ get
+ {
+ return Utils.DecodeString(Value);
+ }
+ set
+ {
+ Value = Utils.EncodeString(value);
+ }
+ }
+ public uint ValueAsInt
+ {
+ get
+ {
+ return BitConverter.ToUInt32(Value, 0);
+ }
+ set
+ {
+ Value = BitConverter.GetBytes(value);
+ }
+ }
+
+ public void Encode(BinaryWriter writer)
+ {
+ uint crc = CRC();
+ writer.Write(crc);
+ writer.Write((uint)DataType);
+ writer.Write(Key.Length);
+ writer.Write(Value.Length);
+
+ writer.Write(Key);
+ writer.Align(8);
+
+ writer.Write(Value);
+ writer.Align(8);
+ }
+
+ public static CRCBlock Decode(BinaryReader reader)
+ {
+ uint crc = reader.ReadUInt32();
+ uint type = reader.ReadUInt32();
+ uint lenName = reader.ReadUInt32();
+ uint lenVal = reader.ReadUInt32();
+
+ byte[] key = reader.ReadBytes((int)lenName);
+ reader.Align(8);
+
+ byte[] value = reader.ReadBytes((int)lenVal);
+ reader.Align(8);
+
+ CRCBlock block = new CRCBlock
+ {
+ DataType = (CRCBlockType)type,
+ Key = key,
+ Value = value,
+ };
+
+ if (block.CRC() != crc)
+ {
+ throw new InvalidDataException("Invalid CRC in variable bag.");
+ }
+
+ return block;
+ }
+
+ public uint CRC()
+ {
+ BinaryWriter wtemp = new BinaryWriter(new MemoryStream());
+ wtemp.Write(0);
+ wtemp.Write((uint)DataType);
+ wtemp.Write(Key.Length);
+ wtemp.Write(Value.Length);
+ wtemp.Write(Key);
+ wtemp.Write(Value);
+ return Utils.CRC32(wtemp.GetBytes());
+ }
+ }
+
+ public class VariableBag
+ {
+ public List<CRCBlock> Blocks = new List<CRCBlock>();
+
+ public void Deserialize(byte[] data)
+ {
+ int len = data.Length;
+
+ BinaryReader reader = new BinaryReader(new MemoryStream(data));
+
+ while (reader.BaseStream.Position < len - 0x10)
+ {
+ Blocks.Add(CRCBlock.Decode(reader));
+ }
+ }
+
+ public byte[] Serialize()
+ {
+ BinaryWriter writer = new BinaryWriter(new MemoryStream());
+
+ foreach (CRCBlock block in Blocks)
+ {
+ block.Encode(writer);
+ }
+
+ return writer.GetBytes();
+ }
+
+ public CRCBlock GetBlock(string key)
+ {
+ foreach (CRCBlock block in Blocks)
+ {
+ if (block.KeyAsStr == key)
+ {
+ return block;
+ }
+ }
+
+ return null;
+ }
+
+ public void SetBlock(string key, byte[] value)
+ {
+ for (int i = 0; i < Blocks.Count; i++)
+ {
+ CRCBlock block = Blocks[i];
+
+ if (block.KeyAsStr == key)
+ {
+ block.Value = value;
+ Blocks[i] = block;
+ break;
+ }
+ }
+ }
+
+ public void DeleteBlock(string key)
+ {
+ foreach (CRCBlock block in Blocks)
+ {
+ if (block.KeyAsStr == key)
+ {
+ Blocks.Remove(block);
+ return;
+ }
+ }
+ }
+
+ public VariableBag(byte[] data)
+ {
+ Deserialize(data);
+ }
+
+ public VariableBag()
+ {
+
+ }
+ }
+}
diff --git a/LibTSforge/SPP/PKeyConfig.cs b/LibTSforge/SPP/PKeyConfig.cs
new file mode 100644
index 0000000..a608608
--- /dev/null
+++ b/LibTSforge/SPP/PKeyConfig.cs
@@ -0,0 +1,215 @@
+namespace LibTSforge.SPP
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using System.Xml;
+
+ public enum PKeyAlgorithm
+ {
+ PKEY2005,
+ PKEY2009
+ }
+
+ public class KeyRange
+ {
+ public int Start;
+ public int End;
+ public string EulaType;
+ public string PartNumber;
+ public bool Valid;
+
+ public bool Contains(int n)
+ {
+ return Start <= n && End <= n;
+ }
+ }
+
+ public class ProductConfig
+ {
+ public int GroupId;
+ public string Edition;
+ public string Description;
+ public string Channel;
+ public bool Randomized;
+ public PKeyAlgorithm Algorithm;
+ public List<KeyRange> Ranges;
+ public Guid ActivationId;
+
+ private List<KeyRange> GetPkeyRanges()
+ {
+ if (Ranges.Count == 0)
+ {
+ throw new ArgumentException("No key ranges.");
+ }
+
+ if (Algorithm == PKeyAlgorithm.PKEY2005)
+ {
+ return Ranges;
+ }
+
+ List<KeyRange> FilteredRanges = Ranges.Where(r => !r.EulaType.Contains("WAU")).ToList();
+
+ if (FilteredRanges.Count == 0)
+ {
+ throw new NotSupportedException("Specified Activation ID is usable only for Windows Anytime Upgrade. Please use a non-WAU Activation ID instead.");
+ }
+
+ return FilteredRanges;
+ }
+
+ public ProductKey GetRandomKey()
+ {
+ List<KeyRange> KeyRanges = GetPkeyRanges();
+ Random rnd = new Random();
+
+ KeyRange range = KeyRanges[rnd.Next(KeyRanges.Count)];
+ int serial = rnd.Next(range.Start, range.End);
+
+ return new ProductKey(serial, 0, false, Algorithm, this, range);
+ }
+ }
+
+ public class PKeyConfig
+ {
+ public Dictionary<Guid, ProductConfig> Products = new Dictionary<Guid, ProductConfig>();
+ private List<Guid> loadedPkeyConfigs = new List<Guid>();
+
+ public void LoadConfig(Guid actId)
+ {
+ string pkcData;
+ Guid pkcFileId = SLApi.GetPkeyConfigFileId(actId);
+
+ if (loadedPkeyConfigs.Contains(pkcFileId)) return;
+
+ string licConts = SLApi.GetLicenseContents(pkcFileId);
+
+ using (TextReader tr = new StringReader(licConts))
+ {
+ XmlDocument lic = new XmlDocument();
+ lic.Load(tr);
+
+ XmlNamespaceManager nsmgr = new XmlNamespaceManager(lic.NameTable);
+ nsmgr.AddNamespace("rg", "urn:mpeg:mpeg21:2003:01-REL-R-NS");
+ nsmgr.AddNamespace("r", "urn:mpeg:mpeg21:2003:01-REL-R-NS");
+ nsmgr.AddNamespace("tm", "http://www.microsoft.com/DRM/XrML2/TM/v2");
+
+ XmlNode root = lic.DocumentElement;
+ XmlNode pkcDataNode = root.SelectSingleNode("/rg:licenseGroup/r:license/r:otherInfo/tm:infoTables/tm:infoList/tm:infoBin[@name=\"pkeyConfigData\"]", nsmgr);
+ pkcData = Encoding.UTF8.GetString(Convert.FromBase64String(pkcDataNode.InnerText));
+ }
+
+ using (TextReader tr = new StringReader(pkcData))
+ {
+ XmlDocument lic = new XmlDocument();
+ lic.Load(tr);
+
+ XmlNamespaceManager nsmgr = new XmlNamespaceManager(lic.NameTable);
+ nsmgr.AddNamespace("p", "http://www.microsoft.com/DRM/PKEY/Configuration/2.0");
+ XmlNodeList configNodes = lic.SelectNodes("//p:ProductKeyConfiguration/p:Configurations/p:Configuration", nsmgr);
+ XmlNodeList rangeNodes = lic.SelectNodes("//p:ProductKeyConfiguration/p:KeyRanges/p:KeyRange", nsmgr);
+ XmlNodeList pubKeyNodes = lic.SelectNodes("//p:ProductKeyConfiguration/p:PublicKeys/p:PublicKey", nsmgr);
+
+ Dictionary<int, PKeyAlgorithm> algorithms = new Dictionary<int, PKeyAlgorithm>();
+ Dictionary<string, List<KeyRange>> ranges = new Dictionary<string, List<KeyRange>>();
+
+ Dictionary<string, PKeyAlgorithm> algoConv = new Dictionary<string, PKeyAlgorithm>
+ {
+ { "msft:rm/algorithm/pkey/2005", PKeyAlgorithm.PKEY2005 },
+ { "msft:rm/algorithm/pkey/2009", PKeyAlgorithm.PKEY2009 }
+ };
+
+ foreach (XmlNode pubKeyNode in pubKeyNodes)
+ {
+ int group = int.Parse(pubKeyNode.SelectSingleNode("./p:GroupId", nsmgr).InnerText);
+ algorithms[group] = algoConv[pubKeyNode.SelectSingleNode("./p:AlgorithmId", nsmgr).InnerText];
+ }
+
+ foreach (XmlNode rangeNode in rangeNodes)
+ {
+ string refActIdStr = rangeNode.SelectSingleNode("./p:RefActConfigId", nsmgr).InnerText;
+
+ if (!ranges.ContainsKey(refActIdStr))
+ {
+ ranges[refActIdStr] = new List<KeyRange>();
+ }
+
+ KeyRange keyRange = new KeyRange();
+ keyRange.Start = int.Parse(rangeNode.SelectSingleNode("./p:Start", nsmgr).InnerText);
+ keyRange.End = int.Parse(rangeNode.SelectSingleNode("./p:End", nsmgr).InnerText);
+ keyRange.EulaType = rangeNode.SelectSingleNode("./p:EulaType", nsmgr).InnerText;
+ keyRange.PartNumber = rangeNode.SelectSingleNode("./p:PartNumber", nsmgr).InnerText;
+ keyRange.Valid = rangeNode.SelectSingleNode("./p:IsValid", nsmgr).InnerText.ToLower() == "true";
+
+ ranges[refActIdStr].Add(keyRange);
+ }
+
+ foreach (XmlNode configNode in configNodes)
+ {
+ string refActIdStr = configNode.SelectSingleNode("./p:ActConfigId", nsmgr).InnerText;
+ Guid refActId = new Guid(refActIdStr);
+ int group = int.Parse(configNode.SelectSingleNode("./p:RefGroupId", nsmgr).InnerText);
+ List<KeyRange> keyRanges = ranges[refActIdStr];
+
+ if (keyRanges.Count > 0 && !Products.ContainsKey(refActId))
+ {
+ ProductConfig productConfig = new ProductConfig();
+ productConfig.GroupId = group;
+ productConfig.Edition = configNode.SelectSingleNode("./p:EditionId", nsmgr).InnerText;
+ productConfig.Description = configNode.SelectSingleNode("./p:ProductDescription", nsmgr).InnerText;
+ productConfig.Channel = configNode.SelectSingleNode("./p:ProductKeyType", nsmgr).InnerText;
+ productConfig.Randomized = configNode.SelectSingleNode("./p:ProductKeyType", nsmgr).InnerText.ToLower() == "true";
+ productConfig.Algorithm = algorithms[group];
+ productConfig.Ranges = keyRanges;
+ productConfig.ActivationId = refActId;
+
+ Products[refActId] = productConfig;
+ }
+ }
+ }
+
+ loadedPkeyConfigs.Add(pkcFileId);
+ }
+
+ public ProductConfig MatchParams(int group, int serial)
+ {
+ foreach (ProductConfig config in Products.Values)
+ {
+ if (config.GroupId == group)
+ {
+ foreach (KeyRange range in config.Ranges)
+ {
+ if (range.Contains(serial))
+ {
+ return config;
+ }
+ }
+ }
+ }
+
+ throw new FileNotFoundException("Failed to find product matching supplied product key parameters.");
+ }
+
+ public void LoadAllConfigs(Guid appId)
+ {
+ foreach (Guid actId in SLApi.GetActivationIds(appId))
+ {
+ try
+ {
+ LoadConfig(actId);
+ }
+ catch (ArgumentException)
+ {
+
+ }
+ }
+ }
+
+ public PKeyConfig()
+ {
+
+ }
+ }
+}
diff --git a/LibTSforge/SPP/ProductKey.cs b/LibTSforge/SPP/ProductKey.cs
new file mode 100644
index 0000000..7a933da
--- /dev/null
+++ b/LibTSforge/SPP/ProductKey.cs
@@ -0,0 +1,313 @@
+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;
+ }
+ }
+}
diff --git a/LibTSforge/SPP/SLAPI.cs b/LibTSforge/SPP/SLAPI.cs
new file mode 100644
index 0000000..cfd7dc5
--- /dev/null
+++ b/LibTSforge/SPP/SLAPI.cs
@@ -0,0 +1,417 @@
+namespace LibTSforge.SPP
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Runtime.InteropServices;
+ using System.Text;
+
+ public static class SLApi
+ {
+ private enum SLIDTYPE
+ {
+ SL_ID_APPLICATION,
+ SL_ID_PRODUCT_SKU,
+ SL_ID_LICENSE_FILE,
+ SL_ID_LICENSE,
+ SL_ID_PKEY,
+ SL_ID_ALL_LICENSES,
+ SL_ID_ALL_LICENSE_FILES,
+ SL_ID_STORE_TOKEN,
+ SL_ID_LAST
+ }
+
+ private enum SLDATATYPE
+ {
+ SL_DATA_NONE,
+ SL_DATA_SZ,
+ SL_DATA_DWORD,
+ SL_DATA_BINARY,
+ SL_DATA_MULTI_SZ,
+ SL_DATA_SUM
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SL_LICENSING_STATUS
+ {
+ public Guid SkuId;
+ public uint eStatus;
+ public uint dwGraceTime;
+ public uint dwTotalGraceDays;
+ public uint hrReason;
+ public ulong qwValidityExpiration;
+ }
+
+ public static readonly Guid WINDOWS_APP_ID = new Guid("55c92734-d682-4d71-983e-d6ec3f16059f");
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
+ private static extern void SLOpen(out IntPtr hSLC);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
+ private static extern void SLClose(IntPtr hSLC);
+
+ [DllImport("slc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLGetWindowsInformationDWORD(string ValueName, ref int Value);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLInstallProofOfPurchase(IntPtr hSLC, string pwszPKeyAlgorithm, string pwszPKeyString, uint cbPKeySpecificData, byte[] pbPKeySpecificData, ref Guid PKeyId);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLUninstallProofOfPurchase(IntPtr hSLC, ref Guid PKeyId);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLGetPKeyInformation(IntPtr hSLC, ref Guid pPKeyId, string pwszValueName, out SLDATATYPE peDataType, out uint pcbValue, out IntPtr ppbValue);
+
+ [DllImport("sppcext.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLActivateProduct(IntPtr hSLC, ref Guid pProductSkuId, byte[] cbAppSpecificData, byte[] pvAppSpecificData, byte[] pActivationInfo, string pwszProxyServer, ushort wProxyPort);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLGenerateOfflineInstallationId(IntPtr hSLC, ref Guid pProductSkuId, ref string ppwszInstallationId);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLDepositOfflineConfirmationId(IntPtr hSLC, ref Guid pProductSkuId, string pwszInstallationId, string pwszConfirmationId);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLGetSLIDList(IntPtr hSLC, SLIDTYPE eQueryIdType, ref Guid pQueryId, SLIDTYPE eReturnIdType, out uint pnReturnIds, out IntPtr ppReturnIds);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
+ private static extern void SLGetLicensingStatusInformation(IntPtr hSLC, ref Guid pAppID, IntPtr pProductSkuId, string pwszRightName, out uint pnStatusCount, out IntPtr ppLicensingStatus);
+
+ [DllImport("sppc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLGetInstalledProductKeyIds(IntPtr hSLC, ref Guid pProductSkuId, out uint pnProductKeyIds, out IntPtr ppProductKeyIds);
+
+ [DllImport("slc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLConsumeWindowsRight(uint unknown);
+
+ [DllImport("slc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLGetProductSkuInformation(IntPtr hSLC, ref Guid pProductSkuId, string pwszValueName, out SLDATATYPE peDataType, out uint pcbValue, out IntPtr ppbValue);
+
+ [DllImport("slc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLGetProductSkuInformation(IntPtr hSLC, ref Guid pProductSkuId, string pwszValueName, IntPtr peDataType, out uint pcbValue, out IntPtr ppbValue);
+
+ [DllImport("slc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLGetLicense(IntPtr hSLC, ref Guid pLicenseFileId, out uint pcbLicenseFile, out IntPtr ppbLicenseFile);
+
+ [DllImport("slc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLSetCurrentProductKey(IntPtr hSLC, ref Guid pProductSkuId, ref Guid pProductKeyId);
+
+ [DllImport("slc.dll", CharSet = CharSet.Unicode)]
+ private static extern uint SLFireEvent(IntPtr hSLC, string pwszEventId, ref Guid pApplicationId);
+
+ public class SLContext : IDisposable
+ {
+ public readonly IntPtr Handle;
+
+ public SLContext()
+ {
+ SLOpen(out Handle);
+ }
+
+ public void Dispose()
+ {
+ SLClose(Handle);
+ GC.SuppressFinalize(this);
+ }
+
+ ~SLContext()
+ {
+ Dispose();
+ }
+ }
+
+ public static Guid GetDefaultActivationID(Guid appId, bool includeActivated)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ uint count;
+ IntPtr pLicStat;
+
+ SLGetLicensingStatusInformation(sl.Handle, ref appId, IntPtr.Zero, null, out count, out pLicStat);
+
+ unsafe
+ {
+ SL_LICENSING_STATUS* licensingStatuses = (SL_LICENSING_STATUS*)pLicStat;
+ for (int i = 0; i < count; i++)
+ {
+ SL_LICENSING_STATUS slStatus = licensingStatuses[i];
+
+ Guid actId = slStatus.SkuId;
+ if (GetInstalledPkeyID(actId) == Guid.Empty) continue;
+ if (IsAddon(actId)) continue;
+ if (!includeActivated && (slStatus.eStatus == 1)) continue;
+
+ return actId;
+ }
+ }
+
+ return Guid.Empty;
+ }
+ }
+
+ public static string GetInstallationID(Guid actId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ string installationId = null;
+ return SLGenerateOfflineInstallationId(sl.Handle, ref actId, ref installationId) == 0 ? installationId : null;
+ }
+ }
+
+ public static Guid GetInstalledPkeyID(Guid actId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ uint status;
+ uint count;
+ IntPtr pProductKeyIds;
+
+ status = SLGetInstalledProductKeyIds(sl.Handle, ref actId, out count, out pProductKeyIds);
+
+ if (status != 0 || count == 0)
+ {
+ return Guid.Empty;
+ }
+
+ unsafe { return *(Guid*)pProductKeyIds; }
+ }
+ }
+
+ public static uint DepositConfirmationID(Guid actId, string installationId, string confirmationId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ return SLDepositOfflineConfirmationId(sl.Handle, ref actId, installationId, confirmationId);
+ }
+ }
+
+ public static void RefreshLicenseStatus()
+ {
+ SLConsumeWindowsRight(0);
+ }
+
+ public static bool RefreshTrustedTime(Guid actId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ SLDATATYPE type;
+ uint count;
+ IntPtr ppbValue;
+
+ uint status = SLGetProductSkuInformation(sl.Handle, ref actId, "TrustedTime", out type, out count, out ppbValue);
+ return (int)status >= 0 && status != 0xC004F012;
+ }
+ }
+
+ public static void FireStateChangedEvent(Guid appId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ SLFireEvent(sl.Handle, "msft:rm/event/licensingstatechanged", ref appId);
+ }
+ }
+
+ public static Guid GetAppId(Guid actId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ uint status;
+ uint count;
+ IntPtr pAppIds;
+
+ status = SLGetSLIDList(sl.Handle, SLIDTYPE.SL_ID_PRODUCT_SKU, ref actId, SLIDTYPE.SL_ID_APPLICATION, out count, out pAppIds);
+
+ if (status != 0 || count == 0)
+ {
+ return Guid.Empty;
+ }
+
+ unsafe { return *(Guid*)pAppIds; }
+ }
+ }
+
+ public static bool IsAddon(Guid actId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ uint count;
+ SLDATATYPE type;
+ IntPtr ppbValue;
+
+ uint status = SLGetProductSkuInformation(sl.Handle, ref actId, "DependsOn", out type, out count, out ppbValue);
+ return (int)status >= 0 && status != 0xC004F012;
+ }
+ }
+
+ public static Guid GetLicenseFileId(Guid licId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ uint status;
+ uint count;
+ IntPtr ppReturnLics;
+
+ status = SLGetSLIDList(sl.Handle, SLIDTYPE.SL_ID_LICENSE, ref licId, SLIDTYPE.SL_ID_LICENSE_FILE, out count, out ppReturnLics);
+
+ if (status != 0 || count == 0)
+ {
+ return Guid.Empty;
+ }
+
+ unsafe { return *(Guid*)ppReturnLics; }
+ }
+ }
+
+ public static Guid GetPkeyConfigFileId(Guid actId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ SLDATATYPE type;
+ uint len;
+ IntPtr ppReturnLics;
+
+ uint status = SLGetProductSkuInformation(sl.Handle, ref actId, "pkeyConfigLicenseId", out type, out len, out ppReturnLics);
+
+ if (status != 0 || len == 0)
+ {
+ return Guid.Empty;
+ }
+
+ Guid pkcId = new Guid(Marshal.PtrToStringAuto(ppReturnLics));
+ return GetLicenseFileId(pkcId);
+ }
+ }
+
+ public static string GetLicenseContents(Guid fileId)
+ {
+ if (fileId == Guid.Empty) throw new ArgumentException("License contents could not be retrieved.");
+
+ using (SLContext sl = new SLContext())
+ {
+ uint dataLen;
+ IntPtr dataPtr;
+
+ if (SLGetLicense(sl.Handle, ref fileId, out dataLen, out dataPtr) != 0)
+ {
+ return null;
+ }
+
+ byte[] data = new byte[dataLen];
+ Marshal.Copy(dataPtr, data, 0, (int)dataLen);
+
+ data = data.Skip(Array.IndexOf(data, (byte)'<')).ToArray();
+ return Encoding.UTF8.GetString(data);
+ }
+ }
+
+ public static bool IsPhoneActivatable(Guid actId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ uint count;
+ SLDATATYPE type;
+ IntPtr ppbValue;
+
+ uint status = SLGetProductSkuInformation(sl.Handle, ref actId, "msft:sl/EUL/PHONE/PUBLIC", out type, out count, out ppbValue);
+ return status >= 0 && status != 0xC004F012;
+ }
+ }
+
+ public static string GetPKeyChannel(Guid pkeyId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ SLDATATYPE type;
+ uint len;
+ IntPtr ppbValue;
+
+ uint status = SLGetPKeyInformation(sl.Handle, ref pkeyId, "Channel", out type, out len, out ppbValue);
+
+ if (status != 0 || len == 0)
+ {
+ return null;
+ }
+
+ return Marshal.PtrToStringAuto(ppbValue);
+ }
+ }
+
+ public static string GetMetaStr(Guid actId, string value)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ uint len;
+ SLDATATYPE type;
+ IntPtr ppbValue;
+
+ uint status = SLGetProductSkuInformation(sl.Handle, ref actId, value, out type, out len, out ppbValue);
+
+ if (status != 0 || len == 0 || type != SLDATATYPE.SL_DATA_SZ)
+ {
+ return null;
+ }
+
+ return Marshal.PtrToStringAuto(ppbValue);
+ }
+ }
+
+ public static List<Guid> GetActivationIds(Guid appId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ uint count;
+ IntPtr pLicStat;
+
+ SLGetLicensingStatusInformation(sl.Handle, ref appId, IntPtr.Zero, null, out count, out pLicStat);
+
+ List<Guid> result = new List<Guid>();
+
+ unsafe
+ {
+ SL_LICENSING_STATUS* licensingStatuses = (SL_LICENSING_STATUS*)pLicStat;
+ for (int i = 0; i < count; i++)
+ {
+ result.Add(licensingStatuses[i].SkuId);
+ }
+ }
+
+ return result;
+ }
+ }
+
+ public static uint SetCurrentProductKey(Guid actId, Guid pkeyId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ return SLSetCurrentProductKey(sl.Handle, ref actId, ref pkeyId);
+ }
+ }
+
+ public static uint InstallProductKey(ProductKey pkey)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ Guid pkeyId = Guid.Empty;
+ return SLInstallProofOfPurchase(sl.Handle, pkey.GetAlgoUri(), pkey.ToString(), 0, null, ref pkeyId);
+ }
+ }
+
+ public static uint UninstallProductKey(Guid pkeyId)
+ {
+ using (SLContext sl = new SLContext())
+ {
+ return SLUninstallProofOfPurchase(sl.Handle, ref pkeyId);
+ }
+ }
+
+ public static void UninstallAllProductKeys(Guid appId)
+ {
+ foreach (Guid actId in GetActivationIds(appId))
+ {
+ Guid pkeyId = GetInstalledPkeyID(actId);
+ if (pkeyId == Guid.Empty) continue;
+ if (IsAddon(actId)) continue;
+ UninstallProductKey(pkeyId);
+ }
+ }
+ }
+}
diff --git a/LibTSforge/TokenStore/Common.cs b/LibTSforge/TokenStore/Common.cs
new file mode 100644
index 0000000..1dda7e7
--- /dev/null
+++ b/LibTSforge/TokenStore/Common.cs
@@ -0,0 +1,67 @@
+namespace LibTSforge.TokenStore
+{
+ using System.Collections.Generic;
+ using System.IO;
+
+ public class TokenEntry
+ {
+ public string Name;
+ public string Extension;
+ public byte[] Data;
+ public bool Populated;
+ }
+
+ public class TokenMeta
+ {
+ public string Name;
+ public Dictionary<string, string> Data = new Dictionary<string, string>();
+
+ public byte[] Serialize()
+ {
+ BinaryWriter writer = new BinaryWriter(new MemoryStream());
+ writer.Write(1);
+ byte[] nameBytes = Utils.EncodeString(Name);
+ writer.Write(nameBytes.Length);
+ writer.Write(nameBytes);
+
+ foreach (KeyValuePair<string, string> kv in Data)
+ {
+ byte[] keyBytes = Utils.EncodeString(kv.Key);
+ byte[] valueBytes = Utils.EncodeString(kv.Value);
+ writer.Write(keyBytes.Length);
+ writer.Write(valueBytes.Length);
+ writer.Write(keyBytes);
+ writer.Write(valueBytes);
+ }
+
+ return writer.GetBytes();
+ }
+
+ public void Deserialize(byte[] data)
+ {
+ BinaryReader reader = new BinaryReader(new MemoryStream(data));
+ reader.ReadInt32();
+ int nameLen = reader.ReadInt32();
+ Name = reader.ReadNullTerminatedString(nameLen);
+
+ while (reader.BaseStream.Position < data.Length - 0x8)
+ {
+ int keyLen = reader.ReadInt32();
+ int valueLen = reader.ReadInt32();
+ string key = reader.ReadNullTerminatedString(keyLen);
+ string value = reader.ReadNullTerminatedString(valueLen);
+ Data[key] = value;
+ }
+ }
+
+ public TokenMeta(byte[] data)
+ {
+ Deserialize(data);
+ }
+
+ public TokenMeta()
+ {
+
+ }
+ }
+}
diff --git a/LibTSforge/TokenStore/ITokenStore.cs b/LibTSforge/TokenStore/ITokenStore.cs
new file mode 100644
index 0000000..e2cca36
--- /dev/null
+++ b/LibTSforge/TokenStore/ITokenStore.cs
@@ -0,0 +1,17 @@
+namespace LibTSforge.TokenStore
+{
+ using System;
+
+ public interface ITokenStore : IDisposable
+ {
+ void Deserialize();
+ void Serialize();
+ void AddEntry(TokenEntry entry);
+ void AddEntries(TokenEntry[] entries);
+ void DeleteEntry(string name, string ext);
+ void DeleteUnpopEntry(string name, string ext);
+ TokenEntry GetEntry(string name, string ext);
+ TokenMeta GetMetaEntry(string name);
+ void SetEntry(string name, string ext, byte[] data);
+ }
+}
diff --git a/LibTSforge/TokenStore/TokenStoreModern.cs b/LibTSforge/TokenStore/TokenStoreModern.cs
new file mode 100644
index 0000000..c3483ff
--- /dev/null
+++ b/LibTSforge/TokenStore/TokenStoreModern.cs
@@ -0,0 +1,289 @@
+namespace LibTSforge.TokenStore
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using LibTSforge.Crypto;
+
+ public class TokenStoreModern : ITokenStore
+ {
+ private static readonly uint VERSION = 3;
+ private static readonly int ENTRY_SIZE = 0x9E;
+ private static readonly int BLOCK_SIZE = 0x4020;
+ private static readonly int ENTRIES_PER_BLOCK = BLOCK_SIZE / ENTRY_SIZE;
+ private static readonly int BLOCK_PAD_SIZE = 0x66;
+
+ private static readonly byte[] CONTS_HEADER = Enumerable.Repeat((byte)0x55, 0x20).ToArray();
+ private static readonly byte[] CONTS_FOOTER = Enumerable.Repeat((byte)0xAA, 0x20).ToArray();
+
+ private List<TokenEntry> Entries = new List<TokenEntry>();
+ public FileStream TokensFile;
+
+ public void Deserialize()
+ {
+ if (TokensFile.Length < BLOCK_SIZE) return;
+
+ TokensFile.Seek(0x24, SeekOrigin.Begin);
+ uint nextBlock = 0;
+
+ BinaryReader reader = new BinaryReader(TokensFile);
+ do
+ {
+ uint curOffset = reader.ReadUInt32();
+ nextBlock = reader.ReadUInt32();
+
+ for (int i = 0; i < ENTRIES_PER_BLOCK; i++)
+ {
+ curOffset = reader.ReadUInt32();
+ bool populated = reader.ReadUInt32() == 1;
+ uint contentOffset = reader.ReadUInt32();
+ uint contentLength = reader.ReadUInt32();
+ uint allocLength = reader.ReadUInt32();
+ byte[] contentData = new byte[] { };
+
+ if (populated)
+ {
+ reader.BaseStream.Seek(contentOffset + 0x20, SeekOrigin.Begin);
+ uint dataLength = reader.ReadUInt32();
+
+ if (dataLength != contentLength)
+ {
+ throw new FormatException("Data length in tokens content is inconsistent with entry.");
+ }
+
+ reader.ReadBytes(0x20);
+ contentData = reader.ReadBytes((int)contentLength);
+ }
+
+ reader.BaseStream.Seek(curOffset + 0x14, SeekOrigin.Begin);
+
+ Entries.Add(new TokenEntry
+ {
+ Name = reader.ReadNullTerminatedString(0x82),
+ Extension = reader.ReadNullTerminatedString(0x8),
+ Data = contentData,
+ Populated = populated
+ });
+ }
+
+ reader.BaseStream.Seek(nextBlock, SeekOrigin.Begin);
+ } while (nextBlock != 0);
+ }
+
+ public void Serialize()
+ {
+ MemoryStream tokens = new MemoryStream();
+
+ using (BinaryWriter writer = new BinaryWriter(tokens))
+ {
+ writer.Write(VERSION);
+ writer.Write(CONTS_HEADER);
+
+ int curBlockOffset = (int)writer.BaseStream.Position;
+ int curEntryOffset = curBlockOffset + 0x8;
+ int curContsOffset = curBlockOffset + BLOCK_SIZE;
+
+ for (int eIndex = 0; eIndex < ((Entries.Count / ENTRIES_PER_BLOCK) + 1) * ENTRIES_PER_BLOCK; eIndex++)
+ {
+ TokenEntry entry;
+
+ if (eIndex < Entries.Count)
+ {
+ entry = Entries[eIndex];
+ }
+ else
+ {
+ entry = new TokenEntry
+ {
+ Name = "",
+ Extension = "",
+ Populated = false,
+ Data = new byte[] { }
+ };
+ }
+
+ writer.BaseStream.Seek(curBlockOffset, SeekOrigin.Begin);
+ writer.Write(curBlockOffset);
+ writer.Write(0);
+
+ writer.BaseStream.Seek(curEntryOffset, SeekOrigin.Begin);
+ writer.Write(curEntryOffset);
+ writer.Write(entry.Populated ? 1 : 0);
+ writer.Write(entry.Populated ? curContsOffset : 0);
+ writer.Write(entry.Populated ? entry.Data.Length : -1);
+ writer.Write(entry.Populated ? entry.Data.Length : -1);
+ writer.WriteFixedString16(entry.Name, 0x82);
+ writer.WriteFixedString16(entry.Extension, 0x8);
+ curEntryOffset = (int)writer.BaseStream.Position;
+
+ if (entry.Populated)
+ {
+ writer.BaseStream.Seek(curContsOffset, SeekOrigin.Begin);
+ writer.Write(CONTS_HEADER);
+ writer.Write(entry.Data.Length);
+ writer.Write(CryptoUtils.SHA256Hash(entry.Data));
+ writer.Write(entry.Data);
+ writer.Write(CONTS_FOOTER);
+ curContsOffset = (int)writer.BaseStream.Position;
+ }
+
+ if ((eIndex + 1) % ENTRIES_PER_BLOCK == 0 && eIndex != 0)
+ {
+ if (eIndex < Entries.Count)
+ {
+ writer.BaseStream.Seek(curBlockOffset + 0x4, SeekOrigin.Begin);
+ writer.Write(curContsOffset);
+ }
+
+ writer.BaseStream.Seek(curEntryOffset, SeekOrigin.Begin);
+ writer.WritePadding(BLOCK_PAD_SIZE);
+
+ writer.BaseStream.Seek(curBlockOffset, SeekOrigin.Begin);
+ byte[] blockHash;
+ byte[] blockData = new byte[BLOCK_SIZE - 0x20];
+
+ tokens.Read(blockData, 0, BLOCK_SIZE - 0x20);
+ blockHash = CryptoUtils.SHA256Hash(blockData);
+
+ writer.BaseStream.Seek(curBlockOffset + BLOCK_SIZE - 0x20, SeekOrigin.Begin);
+ writer.Write(blockHash);
+
+ curBlockOffset = curContsOffset;
+ curEntryOffset = curBlockOffset + 0x8;
+ curContsOffset = curBlockOffset + BLOCK_SIZE;
+ }
+ }
+
+ tokens.SetLength(curBlockOffset);
+ }
+
+ byte[] tokensData = tokens.ToArray();
+ byte[] tokensHash = CryptoUtils.SHA256Hash(tokensData.Take(0x4).Concat(tokensData.Skip(0x24)).ToArray());
+
+ tokens = new MemoryStream(tokensData);
+
+ BinaryWriter tokWriter = new BinaryWriter(TokensFile);
+ using (BinaryReader reader = new BinaryReader(tokens))
+ {
+ TokensFile.Seek(0, SeekOrigin.Begin);
+ TokensFile.SetLength(tokens.Length);
+ tokWriter.Write(reader.ReadBytes(0x4));
+ reader.ReadBytes(0x20);
+ tokWriter.Write(tokensHash);
+ tokWriter.Write(reader.ReadBytes((int)reader.BaseStream.Length - 0x4));
+ }
+ }
+
+ public void AddEntry(TokenEntry entry)
+ {
+ Entries.Add(entry);
+ }
+
+ public void AddEntries(TokenEntry[] entries)
+ {
+ Entries.AddRange(entries);
+ }
+
+ public void DeleteEntry(string name, string ext)
+ {
+ foreach (TokenEntry entry in Entries)
+ {
+ if (entry.Name == name && entry.Extension == ext)
+ {
+ Entries.Remove(entry);
+ return;
+ }
+ }
+ }
+
+ public void DeleteUnpopEntry(string name, string ext)
+ {
+ List<TokenEntry> delEntries = new List<TokenEntry>();
+ foreach (TokenEntry entry in Entries)
+ {
+ if (entry.Name == name && entry.Extension == ext && !entry.Populated)
+ {
+ delEntries.Add(entry);
+ }
+ }
+
+ Entries = Entries.Except(delEntries).ToList();
+ }
+
+ public TokenEntry GetEntry(string name, string ext)
+ {
+ foreach (TokenEntry entry in Entries)
+ {
+ if (entry.Name == name && entry.Extension == ext)
+ {
+ if (!entry.Populated) continue;
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ public TokenMeta GetMetaEntry(string name)
+ {
+ DeleteUnpopEntry(name, "xml");
+ TokenEntry entry = GetEntry(name, "xml");
+ TokenMeta meta;
+
+ if (entry == null)
+ {
+ meta = new TokenMeta
+ {
+ Name = name
+ };
+ }
+ else
+ {
+ meta = new TokenMeta(entry.Data);
+ }
+
+ return meta;
+ }
+
+ public void SetEntry(string name, string ext, byte[] data)
+ {
+ for (int i = 0; i < Entries.Count; i++)
+ {
+ TokenEntry entry = Entries[i];
+
+ if (entry.Name == name && entry.Extension == ext && entry.Populated)
+ {
+ entry.Data = data;
+ Entries[i] = entry;
+ return;
+ }
+ }
+
+ Entries.Add(new TokenEntry
+ {
+ Populated = true,
+ Name = name,
+ Extension = ext,
+ Data = data
+ });
+ }
+
+ public TokenStoreModern(string tokensPath)
+ {
+ TokensFile = File.Open(tokensPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
+ Deserialize();
+ }
+
+ public TokenStoreModern()
+ {
+
+ }
+
+ public void Dispose()
+ {
+ Serialize();
+ TokensFile.Close();
+ }
+ }
+}