From f20cf222a8e44bcf8c5f9d5c5b302e3cb91b5bf9 Mon Sep 17 00:00:00 2001 From: Lasse Lauwerys Date: Thu, 20 Feb 2025 16:33:04 +0100 Subject: [PATCH] Initial commit --- .gitattributes | 63 ++ .gitignore | 189 ++++++ MetroUnlocker.sln | 22 + MetroUnlocker/App.Designer.cs | 205 +++++++ MetroUnlocker/App.cs | 173 ++++++ MetroUnlocker/App.resx | 564 ++++++++++++++++++ MetroUnlocker/LOBManager.cs | 107 ++++ MetroUnlocker/LibTSForge/Common.cs | 281 +++++++++ .../LibTSForge/Crypto/CryptoUtils.cs | 121 ++++ MetroUnlocker/LibTSForge/Crypto/Keys.cs | 87 +++ .../LibTSForge/Crypto/PhysicalStoreCrypto.cs | 68 +++ .../LibTSForge/Modifiers/GenPKeyInstall.cs | 44 ++ .../LibTSForge/PhysicalStore/CRCBlock.cs | 111 ++++ .../LibTSForge/PhysicalStore/ModernBlock.cs | 106 ++++ .../LibTSForge/PhysicalStore/PhysicalStore.cs | 282 +++++++++ .../LibTSForge/PhysicalStore/VariableBag.cs | 78 +++ MetroUnlocker/LibTSForge/SPP/PKeyConfig.cs | 202 +++++++ MetroUnlocker/LibTSForge/SPP/ProductKey.cs | 310 ++++++++++ MetroUnlocker/LibTSForge/SPP/SLAPI.cs | 307 ++++++++++ .../LibTSForge/TokenStore/TokenEntry.cs | 15 + .../LibTSForge/TokenStore/TokenStore.cs | 292 +++++++++ MetroUnlocker/LibTSForge/ZeroCID.cs | 98 +++ MetroUnlocker/MetroUnlocker.csproj | 116 ++++ MetroUnlocker/ProductPolicy/ProductPolicy.cs | 153 +++++ .../ProductPolicy/ProductPolicyEditor.cs | 157 +++++ MetroUnlocker/ProductPolicy/Tools.cs | 82 +++ MetroUnlocker/Program.cs | 24 + MetroUnlocker/Properties/AssemblyInfo.cs | 36 ++ .../Properties/Resources.Designer.cs | 71 +++ MetroUnlocker/Properties/Resources.resx | 117 ++++ MetroUnlocker/Properties/Settings.Designer.cs | 30 + MetroUnlocker/Properties/Settings.settings | 7 + MetroUnlocker/Rebooter.cs | 33 + MetroUnlocker/SPPManager.cs | 100 ++++ MetroUnlocker/StartupArguments.cs | 49 ++ MetroUnlocker/app.manifest | 58 ++ 36 files changed, 4758 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 MetroUnlocker.sln create mode 100644 MetroUnlocker/App.Designer.cs create mode 100644 MetroUnlocker/App.cs create mode 100644 MetroUnlocker/App.resx create mode 100644 MetroUnlocker/LOBManager.cs create mode 100644 MetroUnlocker/LibTSForge/Common.cs create mode 100644 MetroUnlocker/LibTSForge/Crypto/CryptoUtils.cs create mode 100644 MetroUnlocker/LibTSForge/Crypto/Keys.cs create mode 100644 MetroUnlocker/LibTSForge/Crypto/PhysicalStoreCrypto.cs create mode 100644 MetroUnlocker/LibTSForge/Modifiers/GenPKeyInstall.cs create mode 100644 MetroUnlocker/LibTSForge/PhysicalStore/CRCBlock.cs create mode 100644 MetroUnlocker/LibTSForge/PhysicalStore/ModernBlock.cs create mode 100644 MetroUnlocker/LibTSForge/PhysicalStore/PhysicalStore.cs create mode 100644 MetroUnlocker/LibTSForge/PhysicalStore/VariableBag.cs create mode 100644 MetroUnlocker/LibTSForge/SPP/PKeyConfig.cs create mode 100644 MetroUnlocker/LibTSForge/SPP/ProductKey.cs create mode 100644 MetroUnlocker/LibTSForge/SPP/SLAPI.cs create mode 100644 MetroUnlocker/LibTSForge/TokenStore/TokenEntry.cs create mode 100644 MetroUnlocker/LibTSForge/TokenStore/TokenStore.cs create mode 100644 MetroUnlocker/LibTSForge/ZeroCID.cs create mode 100644 MetroUnlocker/MetroUnlocker.csproj create mode 100644 MetroUnlocker/ProductPolicy/ProductPolicy.cs create mode 100644 MetroUnlocker/ProductPolicy/ProductPolicyEditor.cs create mode 100644 MetroUnlocker/ProductPolicy/Tools.cs create mode 100644 MetroUnlocker/Program.cs create mode 100644 MetroUnlocker/Properties/AssemblyInfo.cs create mode 100644 MetroUnlocker/Properties/Resources.Designer.cs create mode 100644 MetroUnlocker/Properties/Resources.resx create mode 100644 MetroUnlocker/Properties/Settings.Designer.cs create mode 100644 MetroUnlocker/Properties/Settings.settings create mode 100644 MetroUnlocker/Rebooter.cs create mode 100644 MetroUnlocker/SPPManager.cs create mode 100644 MetroUnlocker/StartupArguments.cs create mode 100644 MetroUnlocker/app.manifest diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7964536 --- /dev/null +++ b/.gitignore @@ -0,0 +1,189 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +x64/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Roslyn cache directories +*.ide/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +## TODO: Comment the next line if you want to checkin your +## web deploy settings but do note that will include unencrypted +## passwords +#*.pubxml + +# NuGet Packages Directory +packages/* +## TODO: If the tool you use requires repositories.config +## uncomment the next line +#!packages/repositories.config + +# Enable "build/" folder in the NuGet Packages folder since +# NuGet packages use it for MSBuild targets. +# This line needs to be after the ignore of the build folder +# (and the packages folder if the line above has been uncommented) +!packages/build/ + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml \ No newline at end of file diff --git a/MetroUnlocker.sln b/MetroUnlocker.sln new file mode 100644 index 0000000..c0b7115 --- /dev/null +++ b/MetroUnlocker.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MetroUnlocker", "MetroUnlocker\MetroUnlocker.csproj", "{D27EB145-0B58-43AD-BB94-BE000D236D38}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D27EB145-0B58-43AD-BB94-BE000D236D38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D27EB145-0B58-43AD-BB94-BE000D236D38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D27EB145-0B58-43AD-BB94-BE000D236D38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D27EB145-0B58-43AD-BB94-BE000D236D38}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MetroUnlocker/App.Designer.cs b/MetroUnlocker/App.Designer.cs new file mode 100644 index 0000000..b0b60a5 --- /dev/null +++ b/MetroUnlocker/App.Designer.cs @@ -0,0 +1,205 @@ +namespace MetroUnlocker +{ + partial class App + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(App)); + this.temporaryButton = new System.Windows.Forms.Button(); + this.developmentCheckbox = new System.Windows.Forms.CheckBox(); + this.signedCheckbox = new System.Windows.Forms.CheckBox(); + this.allUsersCheckbox = new System.Windows.Forms.CheckBox(); + this.statusTextLabel = new System.Windows.Forms.Label(); + this.statusLabel = new System.Windows.Forms.Label(); + this.LOBCheckBox = new System.Windows.Forms.CheckBox(); + this.SPPCheckBox = new System.Windows.Forms.CheckBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.Manual = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.button1 = new System.Windows.Forms.Button(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.button2 = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.Manual.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.SuspendLayout(); + // + // temporaryButton + // + resources.ApplyResources(this.temporaryButton, "temporaryButton"); + this.temporaryButton.Name = "temporaryButton"; + this.temporaryButton.UseVisualStyleBackColor = true; + this.temporaryButton.Click += new System.EventHandler(this.button1_Click); + // + // developmentCheckbox + // + resources.ApplyResources(this.developmentCheckbox, "developmentCheckbox"); + this.developmentCheckbox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this, "DevelopmentEnabled", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.developmentCheckbox.Name = "developmentCheckbox"; + this.developmentCheckbox.UseVisualStyleBackColor = true; + // + // signedCheckbox + // + resources.ApplyResources(this.signedCheckbox, "signedCheckbox"); + this.signedCheckbox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this, "LOBEnabled", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.signedCheckbox.Name = "signedCheckbox"; + this.signedCheckbox.UseVisualStyleBackColor = true; + // + // allUsersCheckbox + // + resources.ApplyResources(this.allUsersCheckbox, "allUsersCheckbox"); + this.allUsersCheckbox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this, "SpecialProfilesEnabled", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.allUsersCheckbox.Name = "allUsersCheckbox"; + this.allUsersCheckbox.UseVisualStyleBackColor = true; + // + // statusTextLabel + // + resources.ApplyResources(this.statusTextLabel, "statusTextLabel"); + this.statusTextLabel.Name = "statusTextLabel"; + // + // statusLabel + // + resources.ApplyResources(this.statusLabel, "statusLabel"); + this.statusLabel.Name = "statusLabel"; + // + // LOBCheckBox + // + resources.ApplyResources(this.LOBCheckBox, "LOBCheckBox"); + this.LOBCheckBox.Name = "LOBCheckBox"; + this.LOBCheckBox.UseVisualStyleBackColor = true; + // + // SPPCheckBox + // + resources.ApplyResources(this.SPPCheckBox, "SPPCheckBox"); + this.SPPCheckBox.Name = "SPPCheckBox"; + this.SPPCheckBox.UseVisualStyleBackColor = true; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.signedCheckbox); + this.groupBox1.Controls.Add(this.allUsersCheckbox); + this.groupBox1.Controls.Add(this.developmentCheckbox); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // groupBox2 + // + resources.ApplyResources(this.groupBox2, "groupBox2"); + this.groupBox2.Controls.Add(this.LOBCheckBox); + this.groupBox2.Controls.Add(this.SPPCheckBox); + this.groupBox2.Controls.Add(this.temporaryButton); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.TabStop = false; + // + // Manual + // + resources.ApplyResources(this.Manual, "Manual"); + this.Manual.Controls.Add(this.tabPage1); + this.Manual.Controls.Add(this.tabPage2); + this.Manual.Name = "Manual"; + this.Manual.SelectedIndex = 0; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.button2); + this.tabPage1.Controls.Add(this.button1); + resources.ApplyResources(this.tabPage1, "tabPage1"); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.JailbreakButton_Click); + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.groupBox2); + this.tabPage2.Controls.Add(this.groupBox1); + resources.ApplyResources(this.tabPage2, "tabPage2"); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.Uninstall); + // + // App + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.Manual); + this.Controls.Add(this.statusLabel); + this.Controls.Add(this.statusTextLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "App"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.Manual.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button temporaryButton; + private System.Windows.Forms.CheckBox developmentCheckbox; + private System.Windows.Forms.CheckBox signedCheckbox; + private System.Windows.Forms.CheckBox allUsersCheckbox; + private System.Windows.Forms.Label statusTextLabel; + private System.Windows.Forms.Label statusLabel; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.CheckBox LOBCheckBox; + private System.Windows.Forms.CheckBox SPPCheckBox; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TabControl Manual; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.Button button2; + } +} + diff --git a/MetroUnlocker/App.cs b/MetroUnlocker/App.cs new file mode 100644 index 0000000..41a94d5 --- /dev/null +++ b/MetroUnlocker/App.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +using System.Diagnostics; +using Microsoft.Win32; + +using MetroUnlocker.ProductPolicy; + +namespace MetroUnlocker +{ + public partial class App : Form + { + public const string AppxKey = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Windows\\Appx"; + + string trustedAppsPolicyName = "AllowAllTrustedApps"; + string developmentPolicyName = "AllowDevelopmentWithoutDevLicense"; + string specialProfilesPolicyName = "AllowDeploymentInSpecialProfiles"; + + public bool LOBEnabled + { + get { return GetGroupPolicy(trustedAppsPolicyName); } + set { SetGroupPolicy(trustedAppsPolicyName, value); } + } + + public bool DevelopmentEnabled + { + get { return GetGroupPolicy(developmentPolicyName); } + set { SetGroupPolicy(developmentPolicyName, value); } + } + + public bool SpecialProfilesEnabled + { + get { return GetGroupPolicy(specialProfilesPolicyName); } + set { SetGroupPolicy(specialProfilesPolicyName, value); } + } + + public void SetGroupPolicy(string policyName, bool enabled) + { + Registry.SetValue(AppxKey, policyName, enabled ? 1 : 0, RegistryValueKind.DWord); + } + + public bool GetGroupPolicy(string policyName) + { + object value = Registry.GetValue(AppxKey, policyName, 0); + return value is int ? (int)value == 1 : false; + } + + public App() + { + InitializeComponent(); + + UpdatePolicyState(); + } + + public void UpdatePolicyState() + { + var productPolicyEditor = new ProductPolicyEditor(); + + var policyState = productPolicyEditor.GetPolicyStateByName("WSLicensingService-LOBSideloadingActivated"); + var isSideloadingKeyInstalled = LOBManager.IsSideloadingKeyInstalled(); + + switch (policyState) + { + case PolicyState.Disabled: + statusLabel.Text = "Disabled"; + statusLabel.ForeColor = Color.DarkRed; + break; + case PolicyState.Enabled: + if (isSideloadingKeyInstalled) + { + statusLabel.Text = "Sideloading enabled"; + statusLabel.ForeColor = Color.DarkGreen; + } + else + { + statusLabel.Text = "Sideloading will be disabled soon"; + statusLabel.ForeColor = Color.DarkOrange; + } + break; + case PolicyState.Unknown: + statusLabel.Text = "Unknown"; + statusLabel.ForeColor = Color.Black; + break; + } + } + + private string CombineArguments(params string[] arguments) + { + return string.Join(" ", arguments); + } + + private void SetSetupParameter(string key, object value, RegistryValueKind valueKind) + { + Registry.SetValue("HKEY_LOCAL_MACHINE\\SYSTEM\\Setup", key, value, valueKind); + } + + private void SetSetupType(int type) + { + SetSetupParameter("SetupType", type, RegistryValueKind.DWord); + } + + private void button1_Click(object sender, EventArgs e) + { + StartupArgument startupArgument; + + if (LOBCheckBox.Checked && SPPCheckBox.Checked) + startupArgument = StartupArgument.EnableLOBAndEnableSPP; + else if (LOBCheckBox.Checked) + startupArgument = StartupArgument.EnableLOBAndDisableSPP; + else if (SPPCheckBox.Checked) + startupArgument = StartupArgument.DisableLOBAndEnableSPP; + else + startupArgument = StartupArgument.DisableLOBAndDisableSPP; + + string commandLine = CombineArguments(new string[] { Application.ExecutablePath, StartupArguments.GetStartupArgumentString(startupArgument) }); + + SetSetupParameter("CmdLine", commandLine, RegistryValueKind.String); + SetSetupType(1); + DialogResult result = MessageBox.Show("Sideloading will be enabled after a reboot. Would you like to reboot now?", "Reboot?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); + + switch (result) + { + case DialogResult.Yes: + Rebooter.Reboot(); + break; + case DialogResult.Cancel: + SetSetupType(0); + break; + } + } + + private void JailbreakButton_Click(object sender, EventArgs e) + { + try + { + Guid productKey; + if (LOBManager.IsSideloadingKeyInstalled(out productKey)) + if (MessageBox.Show(this, "There is already a sideloading key installed. If you continue, the current key will be deleted and a new one will be generated.", "Already activated", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.Cancel) + return; + + LOBManager.ActivateZeroCID(); + MessageBox.Show(this, "Sideloading activated!", "Success!", MessageBoxButtons.OK, MessageBoxIcon.Information); + UpdatePolicyState(); + } + catch (Exception ex) + { + MessageBox.Show(this, ex.Message, "Error while activating sideloading!", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void Uninstall(object sender, EventArgs e) + { + Guid productKey; + if (LOBManager.IsSideloadingKeyInstalled(out productKey)) + { + if (MessageBox.Show(this, "Are you sure you want to disable sideloading?", "Really?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + { + if (LOBManager.UninstallSideloadingKey(productKey)) + MessageBox.Show(this, "The sideloading key was uninstalled successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + else MessageBox.Show(this, "Could not uninstall the sideloading key.", "Failed", MessageBoxButtons.OK, MessageBoxIcon.Error); + UpdatePolicyState(); + } + } + else MessageBox.Show(this, "There is no sideloading key installed.", "I got nothing to do...", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + } +} diff --git a/MetroUnlocker/App.resx b/MetroUnlocker/App.resx new file mode 100644 index 0000000..29e7d62 --- /dev/null +++ b/MetroUnlocker/App.resx @@ -0,0 +1,564 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Bottom, Left, Right + + + + 6, 42 + + + 210, 23 + + + + 0 + + + Reboot to Apply + + + temporaryButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 2 + + + Allows sideloading of unpackaged apps. + + + True + + + True + + + 6, 13 + + + True + + + GrowAndShrink + + + 266, 215 + + + Top, Bottom, Left, Right + + + 7, 123 + + + 221, 23 + + + 1 + + + Disable Sideloading + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage1 + + + 0 + + + Top, Bottom, Left, Right + + + 6, 6 + + + 222, 111 + + + 0 + + + Unlock! + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage1 + + + 1 + + + 4, 22 + + + 3, 3, 3, 3 + + + 234, 152 + + + 0 + + + Jailbreak + + + tabPage1 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Manual + + + 0 + + + Top, Bottom, Left, Right + + + True + + + 6, 19 + + + 81, 17 + + + 1 + + + Sideloading + + + LOBCheckBox + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 0 + + + True + + + 95, 19 + + + 119, 17 + + + 2 + + + Software Protection + + + SPPCheckBox + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 1 + + + 6, 75 + + + 222, 71 + + + 8 + + + Product Policy + + + groupBox2 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 0 + + + Top, Left, Right + + + Enables sideloading of signed apps. + + + True + + + 6, 19 + + + 117, 17 + + + 3 + + + Signed Sideloading + + + signedCheckbox + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Enables managing Store apps on guest and temporary users. + + + Top, Right + + + True + + + 149, 19 + + + 67, 17 + + + 4 + + + All Users + + + allUsersCheckbox + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 1 + + + 6, 6 + + + 222, 63 + + + 7 + + + Group Policy + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 1 + + + 4, 22 + + + 3, 3, 3, 3 + + + 234, 152 + + + 1 + + + Manual + + + tabPage2 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Manual + + + 1 + + + 12, 25 + + + 242, 178 + + + 9 + + + Manual + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Top, Right + + + 58, 9 + + + 196, 13 + + + 6 + + + Unknown + + + TopRight + + + statusLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + True + + + 12, 9 + + + 40, 13 + + + 5 + + + Status: + + + statusTextLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + CenterScreen + + + Sideloading Unlocker + + + App + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 6, 42 + + + 147, 17 + + + 2 + + + Development Sideloading + + + developmentCheckbox + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 2 + + \ No newline at end of file diff --git a/MetroUnlocker/LOBManager.cs b/MetroUnlocker/LOBManager.cs new file mode 100644 index 0000000..10995b0 --- /dev/null +++ b/MetroUnlocker/LOBManager.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +using MetroUnlocker.ProductPolicy; +using MetroUnlocker; +using MetroUnlocker.LibTSForge.PhysicalStore; +using MetroUnlocker.LibTSForge.Modifiers; +using MetroUnlocker.LibTSForge.TokenStore; +using MetroUnlocker.LibTSForge.SPP; + +namespace MetroUnlocker +{ + public class LOBManager + { + private static Guid ActivationId = new Guid("ec67814b-30e6-4a50-bf7b-d55daf729d1e"); + + public static bool Backup = true; + + public static void ActivateZeroCID() + { + PSVersion version = Utils.DetectVersion(); + bool production = Utils.DetectCurrentKey(); + + if (Backup) BackupPhysicalStore(); + + GenPKeyInstall.InstallGenPKey(version, production, ActivationId); + + ZeroCID.Activate(version, production, ActivationId); + } + + public static Guid GetInstalledSideloadingKeyId() + { + return SLApi.GetInstalledPkeyId(ActivationId); + } + + public static bool IsSideloadingKeyInstalled(out Guid sideloadingKeyId) + { + return (sideloadingKeyId = GetInstalledSideloadingKeyId()) != Guid.Empty; + } + + public static bool UninstallSideloadingKey(Guid sideloadingKeyId) + { + if (Backup) BackupPhysicalStore(); + bool result = SLApi.UninstallProductKey(sideloadingKeyId) == 0; + SLApi.RefreshLicenseStatus(); + SLApi.FireStateChangedEvent(SLApi.GetAppId(ActivationId)); + return result; + } + + public static string GetUniqueFileName(string fileName) + { + string uniqueFileName = fileName; + + for (int i = 1; File.Exists(uniqueFileName); i++) + { + string extension = Path.GetExtension(fileName); + string path = Path.GetFileNameWithoutExtension(fileName); + uniqueFileName = string.Format("{0}{1}.{2}", path, extension, i); + } + + return uniqueFileName; + } + + public static void BackupPhysicalStore() + { + string backupFileName = "data.dat.bak"; + + string physicalStore = PhysicalStore.GetPath(); + + File.Copy(physicalStore, GetUniqueFileName(backupFileName), false); + + + backupFileName = "tokens.dat.bak"; + + physicalStore = TokenStore.GetPath(); + + File.Copy(physicalStore, GetUniqueFileName(backupFileName), false); + } + + public static bool SetPolicyState(PolicyState state) + { + ProductPolicyEditor policyEditor = new ProductPolicyEditor(); + policyEditor.SetPolicyStateByName("WSLicensingService-LOBSideloadingActivated", state); + return policyEditor.Save(); + } + + public static bool Enable() + { + return SetPolicyState(PolicyState.Enabled); + } + + public static bool Disable() + { + return SetPolicyState(PolicyState.Disabled); + } + + public static bool IsSideloadingKeyInstalled() + { + Guid sideloadingKeyId; + return IsSideloadingKeyInstalled(out sideloadingKeyId); + } + } +} diff --git a/MetroUnlocker/LibTSForge/Common.cs b/MetroUnlocker/LibTSForge/Common.cs new file mode 100644 index 0000000..ad842de --- /dev/null +++ b/MetroUnlocker/LibTSForge/Common.cs @@ -0,0 +1,281 @@ +using Microsoft.Win32; +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.ServiceProcess; +using System.Text; +using System.Collections.Generic; +//using MetroUnlocker.Crypto; +//using MetroUnlocker.PhysicalStore; +using MetroUnlocker.LibTSForge.SPP; +//using MetroUnlocker.TokenStore; + +// Common.cs +namespace MetroUnlocker +{ + public enum PSVersion + { + Win8Early, + Win8, + WinBlue, + WinModern + } + + public enum BlockType : uint + { + NONE, + NAMED, + ATTRIBUTE, + TIMER + } + + public static class Constants + { + public static readonly string ZeroCID = new string('0', 48); + 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 class Utils + { + [DllImport("kernel32.dll")] + public static extern uint GetSystemDefaultLCID(); + + public static string GetArchitecture() + { + string arch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE", EnvironmentVariableTarget.Machine).ToUpperInvariant(); + return arch == "AMD64" ? "X64" : arch; + } + + 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 void Align(this BinaryWriter writer, int to) + { + int pos = (int)writer.BaseStream.Position; + writer.Write(Enumerable.Repeat((byte)0, -pos & (to - 1)).ToArray()); + } + + 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 byte[] GetBytes(this BinaryWriter writer) + { + return ((MemoryStream)writer.BaseStream).ToArray(); + } + + public static void WriteAllBytes(FileStream fs, byte[] data) + { + fs.Seek(0, SeekOrigin.Begin); + fs.SetLength(data.Length); + fs.Write(data, 0, data.Length); + } + + public static byte[] ReadAllBytes(FileStream fs) + { + BinaryReader br = new BinaryReader(fs); + return br.ReadBytes((int)fs.Length); + } + + 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 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("This version of Windows is not supported. (build " + build + ")"); + } + + 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 string DecodeString(byte[] data) + { + return Encoding.Unicode.GetString(data).Trim('\0'); + } + + public static byte[] EncodeString(string str) + { + return Encoding.Unicode.GetBytes(str + '\0'); + } + + internal static string GetTokenStorePath() + { + return (string)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform", "TokenStore", string.Empty); + } + } + + public class TokenMeta + { + public string Name; + public Dictionary Data = new Dictionary(); + + 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 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/MetroUnlocker/LibTSForge/Crypto/CryptoUtils.cs b/MetroUnlocker/LibTSForge/Crypto/CryptoUtils.cs new file mode 100644 index 0000000..95ab11b --- /dev/null +++ b/MetroUnlocker/LibTSForge/Crypto/CryptoUtils.cs @@ -0,0 +1,121 @@ +using System; +using System.Linq; +using System.Security.Cryptography; + +namespace MetroUnlocker.LibTSForge.Crypto +{ + 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/MetroUnlocker/LibTSForge/Crypto/Keys.cs b/MetroUnlocker/LibTSForge/Crypto/Keys.cs new file mode 100644 index 0000000..43b9d9c --- /dev/null +++ b/MetroUnlocker/LibTSForge/Crypto/Keys.cs @@ -0,0 +1,87 @@ +namespace MetroUnlocker.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/MetroUnlocker/LibTSForge/Crypto/PhysicalStoreCrypto.cs b/MetroUnlocker/LibTSForge/Crypto/PhysicalStoreCrypto.cs new file mode 100644 index 0000000..6fb3a2b --- /dev/null +++ b/MetroUnlocker/LibTSForge/Crypto/PhysicalStoreCrypto.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +using MetroUnlocker.LibTSForge; + +namespace MetroUnlocker.LibTSForge.Crypto +{ + public static class PhysicalStoreCrypto + { + 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)) + throw new Exception("Failed to decrypt physical store."); + + 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)) + throw new Exception("Failed to verify HMAC. Physical store is either corrupt or in Vista format."); + + return psData; + } + + public static byte[] EncryptPhysicalStore(byte[] data, bool production, PSVersion version) + { + Dictionary versionTable = new Dictionary + { + {PSVersion.Win8, 1}, + {PSVersion.WinBlue, 2}, + {PSVersion.WinModern, 3} + }; + + byte[] rsaKey = production ? Keys.PRODUCTION : Keys.TEST; + + byte[] aesKey = Encoding.UTF8.GetBytes("Boop Foxyz nose!"); + 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/MetroUnlocker/LibTSForge/Modifiers/GenPKeyInstall.cs b/MetroUnlocker/LibTSForge/Modifiers/GenPKeyInstall.cs new file mode 100644 index 0000000..2267da5 --- /dev/null +++ b/MetroUnlocker/LibTSForge/Modifiers/GenPKeyInstall.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MetroUnlocker.LibTSForge.Modifiers +{ + using System; + using System.IO; + using Microsoft.Win32; + using MetroUnlocker.LibTSForge.PhysicalStore; + using MetroUnlocker.LibTSForge.SPP; + using MetroUnlocker.LibTSForge.TokenStore; + + public static class GenPKeyInstall + { + public static void InstallGenPKey(PSVersion version, bool production, Guid actId) + { + PKeyConfig pkc = new PKeyConfig(); + + pkc.LoadConfig(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) + throw new Exception("The key algorithm isn't 2009"); + + uint status = SLApi.InstallProductKey(pkey); + + if (status != 0) + throw new ApplicationException("Failed to install generated product key."); + + return; + } + } +} diff --git a/MetroUnlocker/LibTSForge/PhysicalStore/CRCBlock.cs b/MetroUnlocker/LibTSForge/PhysicalStore/CRCBlock.cs new file mode 100644 index 0000000..a62daec --- /dev/null +++ b/MetroUnlocker/LibTSForge/PhysicalStore/CRCBlock.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace MetroUnlocker.LibTSForge.PhysicalStore +{ + 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(((MemoryStream)wtemp.BaseStream).ToArray()); + } + } +} diff --git a/MetroUnlocker/LibTSForge/PhysicalStore/ModernBlock.cs b/MetroUnlocker/LibTSForge/PhysicalStore/ModernBlock.cs new file mode 100644 index 0000000..ccf605f --- /dev/null +++ b/MetroUnlocker/LibTSForge/PhysicalStore/ModernBlock.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace MetroUnlocker.LibTSForge.PhysicalStore +{ + public class ModernBlock + { + public BlockType Type; + public uint Flags; + public uint Unknown; + 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 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, + }; + } + } +} diff --git a/MetroUnlocker/LibTSForge/PhysicalStore/PhysicalStore.cs b/MetroUnlocker/LibTSForge/PhysicalStore/PhysicalStore.cs new file mode 100644 index 0000000..a00bd56 --- /dev/null +++ b/MetroUnlocker/LibTSForge/PhysicalStore/PhysicalStore.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +using Microsoft.Win32; + +using MetroUnlocker.LibTSForge.Crypto; + +namespace MetroUnlocker.LibTSForge.PhysicalStore +{ + public sealed class PhysicalStore : IDisposable + { + private byte[] PreHeaderBytes = new byte[] { }; + private readonly Dictionary> Data = new Dictionary>(); + 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 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 Utils.GetBytes(writer); + } + + 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(); + + for (int j = 0; j < numValues; j++) + { + Data[keyName].Add(ModernBlock.Decode(reader)); + reader.Align(4); + } + } + } + } + + public void AddBlock(ModernBlock block) + { + if (!Data.ContainsKey(block.KeyAsStr)) + { + Data[block.KeyAsStr] = new List(); + } + + 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 blocks) + { + foreach (ModernBlock block in blocks) + { + AddBlock(block); + } + } + + public ModernBlock GetBlock(string key, string value) + { + List blocks = Data[key]; + + foreach (ModernBlock block in blocks) + { + if (block.ValueAsStr == value) + { + return new ModernBlock + { + Type = block.Type, + Flags = block.Flags, + Key = Utils.EncodeString(key), + Value = block.Value, + Data = block.Data + }; + } + } + + return null; + } + + public ModernBlock GetBlock(string key, uint value) + { + List blocks = Data[key]; + + foreach (ModernBlock block in blocks) + { + if (block.ValueAsInt == value) + { + return new ModernBlock + { + 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 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 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 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 blocks = Data[key]; + + foreach (ModernBlock block in blocks) + { + if (block.ValueAsInt == value) + { + blocks.Remove(block); + break; + } + } + + Data[key] = blocks; + } + } + + public static string GetPath() + { + string sppRoot = Utils.GetTokenStorePath(); + + return Path.Combine(Environment.ExpandEnvironmentVariables(sppRoot), "data.dat"); + } + + public PhysicalStore(PSVersion version, bool production) + { + Version = version; + Production = production; + + TSFile = File.Open(GetPath(), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); + + Deserialize(ReadRaw()); + } + + public void Dispose() + { + WriteRaw(Serialize()); + } + + public byte[] ReadRaw() + { + byte[] data = PhysicalStoreCrypto.DecryptPhysicalStore(Utils.ReadAllBytes(TSFile), Production); + TSFile.Seek(0, SeekOrigin.Begin); + return data; + } + + public void WriteRaw(byte[] data) + { + if (TSFile.CanWrite) + { + byte[] encrData = PhysicalStoreCrypto.EncryptPhysicalStore(data, Production, Version); + TSFile.SetLength(encrData.LongLength); + TSFile.Seek(0, SeekOrigin.Begin); + Utils.WriteAllBytes(TSFile, encrData); + TSFile.Close(); + } + } + } +} diff --git a/MetroUnlocker/LibTSForge/PhysicalStore/VariableBag.cs b/MetroUnlocker/LibTSForge/PhysicalStore/VariableBag.cs new file mode 100644 index 0000000..24aa100 --- /dev/null +++ b/MetroUnlocker/LibTSForge/PhysicalStore/VariableBag.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MetroUnlocker.LibTSForge.PhysicalStore +{ + public class VariableBag + { + public List Blocks = new List(); + + public VariableBag() { } + public VariableBag(byte[] data) + { + Deserialize(data); + } + + 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 ((MemoryStream)writer.BaseStream).ToArray(); + } + + 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; + } + } + } +} diff --git a/MetroUnlocker/LibTSForge/SPP/PKeyConfig.cs b/MetroUnlocker/LibTSForge/SPP/PKeyConfig.cs new file mode 100644 index 0000000..111d1e0 --- /dev/null +++ b/MetroUnlocker/LibTSForge/SPP/PKeyConfig.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.Xml; + +namespace MetroUnlocker.LibTSForge.SPP +{ + + 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 Ranges; + public Guid ActivationId; + + private List GetPkeyRanges() + { + if (Ranges.Count == 0) + { + throw new ArgumentException("No key ranges."); + } + + if (Algorithm == PKeyAlgorithm.PKEY2005) + { + return Ranges; + } + + List 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 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 Products = new Dictionary(); + private List loadedPkeyConfigs = new List(); + + 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 algorithms = new Dictionary(); + Dictionary> ranges = new Dictionary>(); + + Dictionary algoConv = new Dictionary + { + { "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 = 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 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 PKeyConfig() + { + + } + } +} diff --git a/MetroUnlocker/LibTSForge/SPP/ProductKey.cs b/MetroUnlocker/LibTSForge/SPP/ProductKey.cs new file mode 100644 index 0000000..9bce22d --- /dev/null +++ b/MetroUnlocker/LibTSForge/SPP/ProductKey.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +using MetroUnlocker.LibTSForge.Crypto; +using MetroUnlocker.LibTSForge.PhysicalStore; + +namespace MetroUnlocker.LibTSForge.SPP +{ + + 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) + { + 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/MetroUnlocker/LibTSForge/SPP/SLAPI.cs b/MetroUnlocker/LibTSForge/SPP/SLAPI.cs new file mode 100644 index 0000000..29dfb37 --- /dev/null +++ b/MetroUnlocker/LibTSForge/SPP/SLAPI.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace MetroUnlocker.LibTSForge.SPP +{ + 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 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 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); + + return (status == 0 && count != 0) ? (Guid)Marshal.PtrToStructure(ppReturnLics, typeof(Guid)) : Guid.Empty;//new Guid(Marshal.PtrToStringAuto(ppReturnLics)); + } + } + + 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 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 string GetInstallationId(Guid actId) + { + using (SLContext sl = new SLContext()) + { + string installationId = null; + var status = SLGenerateOfflineInstallationId(sl.Handle, ref actId, ref installationId); + + if (status != 0) + throw new Exception(string.Format("Failed to get installation ID: 0x{0}. Your data.dat is probably corrupt at the moment. Try again later.", status.ToString("X"))); + + return installationId; + } + } + + 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); + + //unsafe { return *(Guid*)pProductKeyIds; } + return (status == 0 && count != 0) ? (Guid)Marshal.PtrToStructure(pProductKeyIds, typeof(Guid)) : Guid.Empty; + } + } + + 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 count; + IntPtr pAppIds; + + uint 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; + + return (Guid)Marshal.PtrToStructure(pAppIds, typeof(Guid)); + } + } + + 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 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); + } + } +} diff --git a/MetroUnlocker/LibTSForge/TokenStore/TokenEntry.cs b/MetroUnlocker/LibTSForge/TokenStore/TokenEntry.cs new file mode 100644 index 0000000..2e1a26e --- /dev/null +++ b/MetroUnlocker/LibTSForge/TokenStore/TokenEntry.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MetroUnlocker.LibTSForge.TokenStore +{ + public class TokenEntry + { + public string Name; + public string Extension; + public byte[] Data; + public bool Populated; + } +} diff --git a/MetroUnlocker/LibTSForge/TokenStore/TokenStore.cs b/MetroUnlocker/LibTSForge/TokenStore/TokenStore.cs new file mode 100644 index 0000000..14a109f --- /dev/null +++ b/MetroUnlocker/LibTSForge/TokenStore/TokenStore.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using MetroUnlocker.LibTSForge.Crypto; + +namespace MetroUnlocker.LibTSForge.TokenStore +{ + + public class TokenStore : IDisposable + { + 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 Entries = new List(); + 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 delEntries = new List(); + 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 static string GetPath() + { + return Path.Combine(Environment.ExpandEnvironmentVariables(Utils.GetTokenStorePath()), "tokens.dat"); + } + + public TokenStore() + { + string tokensPath = GetPath(); + TokensFile = File.Open(tokensPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); + Deserialize(); + } + + public void Dispose() + { + Serialize(); + TokensFile.Close(); + } + } +} diff --git a/MetroUnlocker/LibTSForge/ZeroCID.cs b/MetroUnlocker/LibTSForge/ZeroCID.cs new file mode 100644 index 0000000..eb07131 --- /dev/null +++ b/MetroUnlocker/LibTSForge/ZeroCID.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using Microsoft.Win32; + + +using MetroUnlocker.LibTSForge.SPP; +using MetroUnlocker.LibTSForge.PhysicalStore; +using MetroUnlocker.LibTSForge.Crypto; + +namespace MetroUnlocker +{ + + class ZeroCID + { + public static void Deposit(Guid actId, string instId) + { + uint status = SLApi.DepositConfirmationId(actId, instId, Constants.ZeroCID); + + if (status != 0) + throw new InvalidOperationException(string.Format("Failed to deposit fake CID. Status code: 0x{0}", status.ToString("X"))); + } + + public static void Activate(PSVersion version, bool production, Guid actId) + { + Guid appId = SLApi.GetAppId(actId); + + string instId = SLApi.GetInstallationId(actId); + Guid pkeyId = SLApi.GetInstalledPkeyId(actId); + + Utils.KillSPP(); + + using (PhysicalStore store = new PhysicalStore(version, production)) + { + byte[] hwidBlock = Constants.UniversalHWIDBlock; + + byte[] iidHash = CryptoUtils.SHA256Hash(Utils.EncodeString(instId + '\0' + Constants.ZeroCID)); + + + string key = string.Format("SPPSVC\\{0}\\{1}", appId, actId); + ModernBlock keyBlock = store.GetBlock(key, pkeyId.ToString()); + + if (keyBlock == null) + { + throw new InvalidDataException("Failed to get product key data for activation ID: 0x" + actId + "."); + } + + VariableBag pkb = new VariableBag(keyBlock.Data); + + byte[] 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 = Utils.GetBytes(writer); + + writer = new BinaryWriter(new MemoryStream()); + writer.Write(0x20); + writer.Write(iidHash); + writer.Write(pkeyData.Length); + writer.Write(pkeyData); + byte[] tsPkeyInfoData = Utils.GetBytes(writer); + + store.AddBlocks(new ModernBlock[] { + new ModernBlock + { + Type = BlockType.NAMED, + Flags = 0, + KeyAsStr = key, + ValueAsStr = "msft:Windows/7.0/Phone/Cached/HwidBlock/" + pkeyId, + Data = tsHwidData + }, + new ModernBlock + { + Type = BlockType.NAMED, + Flags = 0, + KeyAsStr = key, + ValueAsStr = "msft:Windows/7.0/Phone/Cached/PKeyInfo/" + pkeyId, + Data = tsPkeyInfoData + } + }); + } + + Deposit(actId, instId); + + SLApi.RefreshLicenseStatus(); + SLApi.FireStateChangedEvent(appId); + } + } +} \ No newline at end of file diff --git a/MetroUnlocker/MetroUnlocker.csproj b/MetroUnlocker/MetroUnlocker.csproj new file mode 100644 index 0000000..aea953b --- /dev/null +++ b/MetroUnlocker/MetroUnlocker.csproj @@ -0,0 +1,116 @@ + + + + + Debug + AnyCPU + {D27EB145-0B58-43AD-BB94-BE000D236D38} + WinExe + Properties + MetroUnlocker + MetroUnlocker + v4.0 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + app.manifest + + + + + + + + + + + + + + + + + + Form + + + App.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + App.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + \ No newline at end of file diff --git a/MetroUnlocker/ProductPolicy/ProductPolicy.cs b/MetroUnlocker/ProductPolicy/ProductPolicy.cs new file mode 100644 index 0000000..14d20a0 --- /dev/null +++ b/MetroUnlocker/ProductPolicy/ProductPolicy.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +using Microsoft.Win32; + + +namespace MetroUnlocker.ProductPolicy +{ + //public struct ProductPolicyData + //{ + // public string String; + // public byte[] Bytes; + // public uint DWord; + //}; + class ProductPolicy + { + public bool Modified { get; set; } + public ProductPolicyValue Header { get; set; } + public string Name { get; set; } + //private ProductPolicyData _data; + + //public ProductPolicyData Data + //{ + // get { return _data; } + // set { _data = value; } + //} + + public byte[] Bytes { get; set; } + public string StringValue + { + get + { + return Encoding.Unicode.GetString(Bytes); + } + set { + Bytes = Encoding.Unicode.GetBytes(value); + } + } + public uint DWordValue + { + get + { + return BitConverter.ToUInt32(Bytes, 0); + } + set + { + Bytes = BitConverter.GetBytes(value); + } + } + + public RegistryValueKind Type + { + get + { + return (RegistryValueKind)Header.DataType; + } + } + + private void NameFromBin(ref byte[] PolicyBlob, int offset) + { + Name = Encoding.Unicode.GetString(PolicyBlob, offset + Marshal.SizeOf(Header), Header.Name); + } + + private void ValFromBin(ref byte[] PolicyBlob, int offset) + { + int posdata = offset + Marshal.SizeOf(Header) + Header.Name; + Bytes = new byte[Header.Data]; + Array.Copy(PolicyBlob, posdata, Bytes, 0, Header.Data); + } + + public void FromBin(ref byte[] PolicyBlob, int offset) + { + Header = Tools.BytesToStruct(PolicyBlob, typeof(ProductPolicyValue), offset); + if ((Header.Data + Header.Name + Marshal.SizeOf(Header)) > Header.Size || + (offset + Header.Size) > PolicyBlob.Length) + { + throw new Exception("Invalid _data Header format"); + } + NameFromBin(ref PolicyBlob, offset); + ValFromBin(ref PolicyBlob, offset); + } + + public override string ToString() + { + switch (Type) + { + case RegistryValueKind.String: + return StringValue; + case RegistryValueKind.DWord: + return DWordValue.ToString(); + default: + return Bytes != null ? BitConverter.ToString(Bytes) : ""; + } + } + + public int Size() + { + //switch (Type) + //{ + // case RegistryValueKind.String: + // return StringValue.Length * 2; + // case RegistryValueKind.DWord: + // return 4; + // default: + // return Bytes.Length; + //} + return Bytes.Length; + } + + //public byte[] ToBinary() + //{ + // switch (Type) + // { + // case RegistryValueKind.String: + // return Encoding.Unicode.GetBytes(_data.String); + // case RegistryValueKind.DWord: + // return Tools.StructToBytes(DWordVq); + // default: + // return Bytes; + // } + //} + + public byte[] ToBin() + { + ProductPolicyValue value = new ProductPolicyValue(); + value.Name = (UInt16)(2 * Name.Length); + value.Data = (UInt16)Size(); + int datablocksize = Marshal.SizeOf(Header) + value.Name + value.Data; + int suffixLength = 4 - (datablocksize % 4); + value.Size = (UInt16)(Marshal.SizeOf(Header) + value.Name + value.Data + suffixLength); + value.DataType = Header.DataType; + value.Unknown1 = Header.Unknown1; + value.Unknown2 = Header.Unknown2; + byte[] bytes = Tools.StructToBytes(value); + + Tools.AppendBytes(ref bytes, Encoding.Unicode.GetBytes(Name)); + Tools.AppendBytes(ref bytes, Bytes); + Tools.PaddingAppend(ref bytes, suffixLength); + + return bytes; + } + + // I would like to not split the data into dword and string on parsing but on getting. + //public void SetDWordValue(uint value) + //{ + // Data = new ProductPolicyData { DWord = value }; + //} + } +} diff --git a/MetroUnlocker/ProductPolicy/ProductPolicyEditor.cs b/MetroUnlocker/ProductPolicy/ProductPolicyEditor.cs new file mode 100644 index 0000000..28451fc --- /dev/null +++ b/MetroUnlocker/ProductPolicy/ProductPolicyEditor.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Win32; +using System.Runtime.InteropServices; + +namespace MetroUnlocker.ProductPolicy +{ + public struct ProductPolicyHeader + { + public UInt32 Size, DataSize, EndMarker, Unknown1, Unknown2; + } + + public struct ProductPolicyValue + { + public UInt16 Size, Name, DataType, Data; + public UInt32 Unknown1, Unknown2; + } + + public enum PolicyState : uint + { + Disabled = 0, + Enabled = 1, + Unknown + } + + class ProductPolicyEditor + { + public List PolicyValues { get; set; } + public ProductPolicyHeader Header; + private byte[] _policyBytes; + private byte[] _productPolicyBlobSuffix; + + const string ProductOptionsKey = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\ProductOptions"; + + public ProductPolicyEditor() + { + _policyBytes = GetPolicyBlob(); + PolicyValues = FromBinary(); + } + + public bool Save() + { + return SetPolicyBlob(ToBin()); + } + + private static byte[] GetPolicyBlob() + { + return (byte[])Registry.GetValue(ProductOptionsKey, "ProductPolicy", null); + } + + private static bool SetPolicyBlob(byte[] bytes) + { + Registry.SetValue(ProductOptionsKey, "ProductPolicy", bytes); + Thread.Sleep(1000); + return Tools.ArraysEqual(GetPolicyBlob(), bytes); + } + + public List FromBinary() + { + return FromBinary(_policyBytes); + } + + public List FromBinary(byte[] policyBlob) + { + var header = Tools.BytesToStruct(policyBlob, typeof(ProductPolicyHeader)); + + if (header.Size < policyBlob.Length || (header.DataSize + Marshal.SizeOf(header) + header.EndMarker) != header.Size) + throw new Exception("Invalid Header format"); + + int pos = Marshal.SizeOf(header); + int pos_end = pos + (int)header.DataSize; + + PolicyValues = new List(); + + while (pos < pos_end) + { + ProductPolicy pv = new ProductPolicy(); + pv.FromBin(ref policyBlob, pos); + PolicyValues.Add(pv); + + pos += pv.Header.Size; + } + + if (pos < header.Size) + { + _productPolicyBlobSuffix = new byte[policyBlob.Length - pos]; + Array.Copy(policyBlob, pos, _productPolicyBlobSuffix, 0, _productPolicyBlobSuffix.Length); + } + else + { + _productPolicyBlobSuffix = null; + } + + return PolicyValues; + } + + public byte[] ToBin() + { + int headerSize = Marshal.SizeOf(Header); + byte[] policyBlob = new byte[headerSize]; + + foreach (ProductPolicy v in PolicyValues) + Tools.AppendBytes(ref policyBlob, v.ToBin()); + + Tools.AppendBytes(ref policyBlob, _productPolicyBlobSuffix); + + Header.Size = (UInt32)policyBlob.Length; + Header.EndMarker = (UInt32)_productPolicyBlobSuffix.Length; + Header.DataSize = (UInt32)(Header.Size - Header.EndMarker - headerSize); + Array.Copy(Tools.StructToBytes(Header), 0, policyBlob, 0, headerSize); + + return policyBlob; + } + + public ProductPolicy GetPolicyWithName(string name) + { + return PolicyValues.Find(value => value.Name == name); + } + + public PolicyState GetPolicyStateByName(string name) + { + ProductPolicy policy = GetPolicyWithName(name); + if (policy.Type == RegistryValueKind.DWord) + { + if (policy.DWordValue == 0) + return PolicyState.Disabled; + else if (policy.DWordValue == 1) + return PolicyState.Enabled; + } + return PolicyState.Unknown; + } + + public bool SetPolicyStateByName(string name, PolicyState state) + { + ProductPolicy policy = GetPolicyWithName(name); + + if (policy.Type == RegistryValueKind.DWord) + { + switch (state) + { + case PolicyState.Disabled: + policy.DWordValue = 0; + break; + case PolicyState.Enabled: + policy.DWordValue = 1; + break; + } + } + return false; + } + } +} diff --git a/MetroUnlocker/ProductPolicy/Tools.cs b/MetroUnlocker/ProductPolicy/Tools.cs new file mode 100644 index 0000000..2e8a9a6 --- /dev/null +++ b/MetroUnlocker/ProductPolicy/Tools.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace MetroUnlocker.ProductPolicy +{ + class Tools + { + public static void AppendBytes(ref byte[] b1, byte[] b2) + { + int length = b1.Length; + Array.Resize(ref b1, length + b2.Length); + Array.Copy(b2, 0, b1, length, b2.Length); + } + + static public T BytesToStruct(byte[] bytes, Type type, int offset = 0) + { + int size = Marshal.SizeOf(type); + IntPtr buffer = Marshal.AllocHGlobal(size); + try + { + Marshal.Copy(bytes, offset, buffer, size); + return (T)Marshal.PtrToStructure(buffer, type); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + static public byte[] StructToBytes(object structObject) + { + int size = Marshal.SizeOf(structObject); + IntPtr buffer = Marshal.AllocHGlobal(size); + try + { + Marshal.StructureToPtr(structObject, buffer, false); + byte[] bytes = new byte[size]; + Marshal.Copy(buffer, bytes, 0, size); + return bytes; + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + public static void PaddingAppend(ref byte[] b1, int paddinglen) + { + if (paddinglen > 0) + { + int l = b1.Length; + Array.Resize(ref b1, b1.Length + paddinglen); + for (; l < b1.Length; l++) + b1[l] = 0; + } + } + + static public bool ArraysEqual(Array a1, Array a2) + { + if (a1 == a2) + return true; + + if (a1 == null || a2 == null) + return false; + + if (a1.Length != a2.Length) + return false; + + System.Collections.IList list1 = a1, list2 = a2; //error CS0305: Using the generic type 'System.Collections.Generic.IList' requires '1' type arguments + for (int i = 0; i < a1.Length; i++) + { + if (!Object.Equals(list1[i], list2[i])) //error CS0021: Cannot apply indexing with [] to an expression of type 'IList'(x2) + return false; + } + return true; + } + } +} diff --git a/MetroUnlocker/Program.cs b/MetroUnlocker/Program.cs new file mode 100644 index 0000000..0c9a2c5 --- /dev/null +++ b/MetroUnlocker/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +using System.IO; +using System.Security.Principal; + +namespace MetroUnlocker +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new App()); + } + } +} diff --git a/MetroUnlocker/Properties/AssemblyInfo.cs b/MetroUnlocker/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..061ab67 --- /dev/null +++ b/MetroUnlocker/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MetroUnlocker")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MetroUnlocker")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b976c029-d312-41a8-9d67-63cb061f7847")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MetroUnlocker/Properties/Resources.Designer.cs b/MetroUnlocker/Properties/Resources.Designer.cs new file mode 100644 index 0000000..dfa2a69 --- /dev/null +++ b/MetroUnlocker/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MetroUnlocker.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MetroUnlocker.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/MetroUnlocker/Properties/Resources.resx b/MetroUnlocker/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/MetroUnlocker/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/MetroUnlocker/Properties/Settings.Designer.cs b/MetroUnlocker/Properties/Settings.Designer.cs new file mode 100644 index 0000000..27abc47 --- /dev/null +++ b/MetroUnlocker/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MetroUnlocker.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/MetroUnlocker/Properties/Settings.settings b/MetroUnlocker/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/MetroUnlocker/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/MetroUnlocker/Rebooter.cs b/MetroUnlocker/Rebooter.cs new file mode 100644 index 0000000..c487216 --- /dev/null +++ b/MetroUnlocker/Rebooter.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +//using System.Runtime.InteropServices; +using System.Management; + +namespace MetroUnlocker +{ + public class Rebooter + { + public enum RebootMethod + { + LogOff = 0, + PowerOff = 8, + Reboot = 2, + ShutDown = 1, + Suspend = -1, + Hibernate = -2 + } + + public static void Reboot(RebootMethod method = RebootMethod.Reboot) + { + ManagementClass management = new ManagementClass("Win32_OperatingSystem"); + management.Scope.Options.EnablePrivileges = true; + ManagementBaseObject mboShutdownParams = management.GetMethodParameters("Win32Shutdown"); + mboShutdownParams["Flags"] = method; + mboShutdownParams["Reserved"] = 0; + management.GetInstances().OfType().First().InvokeMethod("Win32Shutdown", mboShutdownParams, null); + } + } +} diff --git a/MetroUnlocker/SPPManager.cs b/MetroUnlocker/SPPManager.cs new file mode 100644 index 0000000..dff8830 --- /dev/null +++ b/MetroUnlocker/SPPManager.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Diagnostics; +using System.Management; + +namespace MetroUnlocker +{ + enum SPPStartupMode + { + Disabled, + Enabled + } + + class SPPManager + { + private static string LogFullName = "C:\\Temp\\W8Sideloader.log"; + + private static string RunCommand(string command, string arguments) + { + string output; + RunCommand(command, arguments, out output); + return output; + } + + private static void RunCommand(string command, string arguments, out string output) + { + Process process = Process.Start(new ProcessStartInfo + { + FileName = command, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }); + + output = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + if (!string.IsNullOrWhiteSpace(error)) throw new Exception(error); + } + + public static string SetStartupMode(SPPStartupMode startupMode) + { + string output; + bool enabled = startupMode == SPPStartupMode.Enabled; + try + { + + RunCommand("sc", "sdset sppsvc D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPLOCRRC;;;IU)(A;;CCLCSWRPLOCRRC;;;SU)(A;;LCRP;;;AU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)", out output); + //RunCommand("sc", "config sppsvc start=" + (enabled ? "enabled" : "disabled"), out output); + using (var m = new ManagementObject("Win32_Service.Name=\"sppsvc\"")) + m.InvokeMethod("ChangeStartMode", new object[] { enabled ? "Automatic" : "Disabledx" }); + + RunCommand("net", "stop sppsvc", out output); + + if (!enabled) + { + DisableScheduledTask("SvcRestartTask"); + DisableScheduledTask("SvcRestartTaskLogon"); + DisableScheduledTask("SvcRestartTaskNetwork"); + } + + //MessageBox.Show(output + "\n----------------------\n", "sppsvc config change"); + } + catch { throw; } + return output; + } + + private static void DisableScheduledTask(string key) + { + try + { + RunCommand("schtasks", "/change /disable /tn \"\\Microsoft\\Windows\\SoftwareProtectionPlatform\\" + key + "\""); + } + catch { throw; } + } + + public static string Disable() + { + try + { + return SetStartupMode(SPPStartupMode.Disabled); + } + catch { throw; } + } + + public static string Enable() + { + try + { + return SetStartupMode(SPPStartupMode.Enabled); + } + catch { throw; } + } + } +} diff --git a/MetroUnlocker/StartupArguments.cs b/MetroUnlocker/StartupArguments.cs new file mode 100644 index 0000000..e8c9c71 --- /dev/null +++ b/MetroUnlocker/StartupArguments.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MetroUnlocker +{ + public enum StartupArgument + { + EnableLOBAndEnableSPP, + EnableLOBAndDisableSPP, + DisableLOBAndEnableSPP, + DisableLOBAndDisableSPP, + Other + } + + public class StartupArguments + { + + public static Dictionary KnownArguments = new Dictionary() + { + {StartupArgument.EnableLOBAndEnableSPP, "hi"}, + {StartupArgument.EnableLOBAndDisableSPP, "mister"}, + {StartupArgument.DisableLOBAndEnableSPP, "fox"}, + {StartupArgument.DisableLOBAndDisableSPP, ":3"}, + }; + + public static StartupArgument GetStartupArgument(string[] args) + { + return KnownArguments.ContainsValue(args[1]) ? KnownArguments.First(arg => arg.Value == args[1]).Key : StartupArgument.Other; + } + + public static string GetStartupArgumentString(StartupArgument startupArgument) + { + return KnownArguments[startupArgument]; + } + + public static bool HasStartupArgument(string[] args) + { + return args.Length > 1 && !HasOtherArgument(args); + } + + public static bool HasOtherArgument(string[] args) + { + return GetStartupArgument(args) == StartupArgument.Other; + } + } +} diff --git a/MetroUnlocker/app.manifest b/MetroUnlocker/app.manifest new file mode 100644 index 0000000..5036abd --- /dev/null +++ b/MetroUnlocker/app.manifest @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +