Archived
1
1

First release build.

This commit is contained in:
Mona Lassa
2025-05-22 01:31:03 +02:00
parent ec55169d57
commit f90695dfda
30 changed files with 499 additions and 1389 deletions
-281
View File
@@ -1,281 +0,0 @@
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<string, string> Data = new Dictionary<string, string>();
public byte[] Serialize()
{
BinaryWriter writer = new BinaryWriter(new MemoryStream());
writer.Write(1);
byte[] nameBytes = Utils.EncodeString(Name);
writer.Write(nameBytes.Length);
writer.Write(nameBytes);
foreach (KeyValuePair<string, string> kv in Data)
{
byte[] keyBytes = Utils.EncodeString(kv.Key);
byte[] valueBytes = Utils.EncodeString(kv.Value);
writer.Write(keyBytes.Length);
writer.Write(valueBytes.Length);
writer.Write(keyBytes);
writer.Write(valueBytes);
}
return writer.GetBytes();
}
public void Deserialize(byte[] data)
{
BinaryReader reader = new BinaryReader(new MemoryStream(data));
reader.ReadInt32();
int nameLen = reader.ReadInt32();
Name = reader.ReadNullTerminatedString(nameLen);
while (reader.BaseStream.Position < data.Length - 0x8)
{
int keyLen = reader.ReadInt32();
int valueLen = reader.ReadInt32();
string key = reader.ReadNullTerminatedString(keyLen);
string value = reader.ReadNullTerminatedString(valueLen);
Data[key] = value;
}
}
public TokenMeta(byte[] data)
{
Deserialize(data);
}
public TokenMeta()
{
}
}
}
@@ -72,9 +72,7 @@ namespace MetroUnlocker.LibTSForge.Crypto
byte[] hash;
using (SHA1 sha1 = SHA1.Create())
{
hash = sha1.ComputeHash(data);
}
return formatter.CreateSignature(hash);
}
@@ -90,9 +88,7 @@ namespace MetroUnlocker.LibTSForge.Crypto
byte[] hash;
using (SHA1 sha1 = SHA1.Create())
{
hash = sha1.ComputeHash(data);
}
return deformatter.VerifySignature(hash, signature);
}
@@ -113,9 +109,7 @@ namespace MetroUnlocker.LibTSForge.Crypto
public static byte[] SHA256Hash(byte[] data)
{
using (SHA256 sha256 = SHA256.Create())
{
return sha256.ComputeHash(data);
}
}
}
}
+2 -2
View File
@@ -2,7 +2,7 @@ namespace MetroUnlocker.LibTSForge.Crypto
{
public static class Keys
{
public static readonly byte[] PRODUCTION = {
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,
@@ -43,7 +43,7 @@ namespace MetroUnlocker.LibTSForge.Crypto
0x81, 0x44, 0x38, 0xBF
};
public static readonly byte[] TEST = {
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,
@@ -12,7 +12,7 @@ namespace MetroUnlocker.LibTSForge.Crypto
{
public static byte[] DecryptPhysicalStore(byte[] data, bool production)
{
byte[] rsaKey = production ? Keys.PRODUCTION : Keys.TEST;
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);
@@ -33,16 +33,16 @@ namespace MetroUnlocker.LibTSForge.Crypto
return psData;
}
public static byte[] EncryptPhysicalStore(byte[] data, bool production, PSVersion version)
public static byte[] EncryptPhysicalStore(byte[] data, bool production, PhysicalStoreVersion version)
{
Dictionary<PSVersion, int> versionTable = new Dictionary<PSVersion, int>
Dictionary<PhysicalStoreVersion, int> versionTable = new Dictionary<PhysicalStoreVersion, int>
{
{PSVersion.Win8, 1},
{PSVersion.WinBlue, 2},
{PSVersion.WinModern, 3}
{PhysicalStoreVersion.Win8, 1},
{PhysicalStoreVersion.WinBlue, 2},
{PhysicalStoreVersion.WinModern, 3}
};
byte[] rsaKey = production ? Keys.PRODUCTION : Keys.TEST;
byte[] rsaKey = production ? Keys.Production : Keys.Test;
byte[] aesKey = Encoding.UTF8.GetBytes("Boop Foxyz nose!");
byte[] hmacKey = CryptoUtils.GenerateRandomKey(0x10);
@@ -1,33 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MetroUnlocker.LibTSForge.SPP;
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 class ProductKeyInstaller
{
public static void InstallGenPKey(PSVersion version, bool production, Guid actId)
public static void InstallGeneratedProductKey(PhysicalStoreVersion version, bool production, Guid actId)
{
PKeyConfig pkc = new PKeyConfig();
ProductKeyConfig keyConfig = new ProductKeyConfig();
pkc.LoadConfig(actId);
keyConfig.LoadConfig(actId);
ProductConfig config;
pkc.Products.TryGetValue(actId, out config);
keyConfig.Products.TryGetValue(actId, out config);
if (config == null) throw new ArgumentException("Activation ID " + actId + " not found in PKeyConfig.");
if (config == null) throw new ArgumentException("Activation ID " + actId + " not found in ProductKeyConfig.");
ProductKey pkey = config.GetRandomKey();
Guid instPkeyId = SLApi.GetInstalledPkeyId(actId);
Guid instPkeyId = SLApi.GetInstalledProductKeyId(actId);
if (instPkeyId != Guid.Empty) SLApi.UninstallProductKey(instPkeyId);
if (pkey.Algorithm != PKeyAlgorithm.PKEY2009)
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MetroUnlocker.LibTSForge.PhysicalStore
{
public class BasicBlock
{
public byte[] Key;
public string KeyAsString
{
get { return Utils.DecodeString(Key); }
set { Key = Utils.EncodeString(value); }
}
public byte[] Value;
public string ValueAsString
{
get { return Utils.DecodeString(Value); }
set { Value = Utils.EncodeString(value); }
}
public uint ValueAsInteger
{
get { return BitConverter.ToUInt32(Value, 0); }
set { Value = BitConverter.GetBytes(value); }
}
}
}
@@ -1,55 +1,42 @@
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
UInt = 1 << 0,
String = 1 << 1,
Binary = 1 << 2
}
public class CRCBlock
public class CRCBlock : BasicBlock
{
public CRCBlockType DataType;
public byte[] Key;
public string KeyAsStr
public CRCBlock() { }
public CRCBlock(BinaryReader reader)
{
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);
}
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);
DataType = (CRCBlockType)type;
Key = key;
Value = value;
if (CRC() != crc)
throw new InvalidDataException("Invalid CRC in variable bag.");
}
public void Encode(BinaryWriter writer)
@@ -67,45 +54,16 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
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());
BinaryWriter writer = new BinaryWriter(new MemoryStream());
writer.Write(0);
writer.Write((uint)DataType);
writer.Write(Key.Length);
writer.Write(Value.Length);
writer.Write(Key);
writer.Write(Value);
return Utils.CRC32(((MemoryStream)writer.BaseStream).ToArray());
}
}
}
@@ -6,68 +6,41 @@ using System.IO;
namespace MetroUnlocker.LibTSForge.PhysicalStore
{
public class ModernBlock
public class ModernBlock : BasicBlock
{
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
public string DataAsString
{
get
{
return Utils.DecodeString(Data);
}
set
{
Data = Utils.EncodeString(value);
}
get { return Utils.DecodeString(Data); }
set { Data = Utils.EncodeString(value); }
}
public uint DataAsInt
{
get
{
return BitConverter.ToUInt32(Data, 0);
}
set
{
Data = BitConverter.GetBytes(value);
}
get { return BitConverter.ToUInt32(Data, 0); }
set { Data = BitConverter.GetBytes(value); }
}
public ModernBlock() { }
public ModernBlock(string key, string value, byte[] data, BlockType type = BlockType.NAMED, uint flags = 0)
{
Type = type;
Flags = flags;
KeyAsString = key;
ValueAsString = value;
Data = data;
}
public ModernBlock(ModernBlock block)
{
Type = block.Type;
Flags = block.Flags;
Unknown = block.Unknown;
Value = block.Value;
Data = block.Data;
}
public void Encode(BinaryWriter writer)
@@ -81,7 +54,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
writer.Write(Data);
}
public static ModernBlock Decode(BinaryReader reader)
public ModernBlock(BinaryReader reader)
{
uint type = reader.ReadUInt32();
uint flags = reader.ReadUInt32();
@@ -93,14 +66,11 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
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,
};
Type = (BlockType)type;
Flags = flags;
Unknown = unk3;
Value = value;
Data = data;
}
}
}
@@ -16,7 +16,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
private byte[] PreHeaderBytes = new byte[] { };
private readonly Dictionary<string, List<ModernBlock>> Data = new Dictionary<string, List<ModernBlock>>();
private readonly FileStream TSFile;
private readonly PSVersion Version;
private readonly PhysicalStoreVersion Version;
private readonly bool Production;
public byte[] Serialize()
@@ -66,7 +66,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
for (int j = 0; j < numValues; j++)
{
Data[keyName].Add(ModernBlock.Decode(reader));
Data[keyName].Add(new ModernBlock(reader));
reader.Align(4);
}
}
@@ -75,12 +75,10 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
public void AddBlock(ModernBlock block)
{
if (!Data.ContainsKey(block.KeyAsStr))
{
Data[block.KeyAsStr] = new List<ModernBlock>();
}
if (!Data.ContainsKey(block.KeyAsString))
Data[block.KeyAsString] = new List<ModernBlock>();
Data[block.KeyAsStr].Add(new ModernBlock
Data[block.KeyAsString].Add(new ModernBlock
{
Type = block.Type,
Flags = block.Flags,
@@ -104,7 +102,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
foreach (ModernBlock block in blocks)
{
if (block.ValueAsStr == value)
if (block.ValueAsString == value)
{
return new ModernBlock
{
@@ -126,7 +124,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
foreach (ModernBlock block in blocks)
{
if (block.ValueAsInt == value)
if (block.ValueAsInteger == value)
{
return new ModernBlock
{
@@ -150,7 +148,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
{
ModernBlock block = blocks[i];
if (block.ValueAsStr == value)
if (block.ValueAsString == value)
{
block.Data = data;
blocks[i] = block;
@@ -169,7 +167,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
{
ModernBlock block = blocks[i];
if (block.ValueAsInt == value)
if (block.ValueAsInteger == value)
{
block.Data = data;
blocks[i] = block;
@@ -208,7 +206,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
foreach (ModernBlock block in blocks)
{
if (block.ValueAsStr == value)
if (block.ValueAsString == value)
{
blocks.Remove(block);
break;
@@ -227,7 +225,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
foreach (ModernBlock block in blocks)
{
if (block.ValueAsInt == value)
if (block.ValueAsInteger == value)
{
blocks.Remove(block);
break;
@@ -245,7 +243,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
return Path.Combine(Environment.ExpandEnvironmentVariables(sppRoot), "data.dat");
}
public PhysicalStore(PSVersion version, bool production)
public PhysicalStore(PhysicalStoreVersion version, bool production)
{
Version = version;
Production = production;
@@ -10,10 +10,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
public List<CRCBlock> Blocks = new List<CRCBlock>();
public VariableBag() { }
public VariableBag(byte[] data)
{
Deserialize(data);
}
public VariableBag(byte[] data) { Deserialize(data); }
public void Deserialize(byte[] data)
{
@@ -22,9 +19,7 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
BinaryReader reader = new BinaryReader(new MemoryStream(data));
while (reader.BaseStream.Position < len - 0x10)
{
Blocks.Add(CRCBlock.Decode(reader));
}
Blocks.Add(new CRCBlock(reader));
}
public byte[] Serialize()
@@ -39,40 +34,19 @@ namespace MetroUnlocker.LibTSForge.PhysicalStore
public CRCBlock GetBlock(string key)
{
foreach (CRCBlock block in Blocks)
{
if (block.KeyAsStr == key)
{
return block;
}
}
return null;
return Blocks.Find(block => block.KeyAsString == key);
}
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;
}
}
int index = Blocks.FindIndex(block => block.KeyAsString == key);
if (index != -1) Blocks[index].Value = value;
}
public void DeleteBlock(string key)
{
foreach (CRCBlock block in Blocks)
if (block.KeyAsStr == key)
{
Blocks.Remove(block);
return;
}
CRCBlock block = GetBlock(key);
if (block != null) Blocks.Remove(block);
}
}
}
+2 -2
View File
@@ -74,7 +74,7 @@ namespace MetroUnlocker.LibTSForge.SPP
}
}
public class PKeyConfig
public class ProductKeyConfig
{
public Dictionary<Guid, ProductConfig> Products = new Dictionary<Guid, ProductConfig>();
private List<Guid> loadedPkeyConfigs = new List<Guid>();
@@ -194,7 +194,7 @@ namespace MetroUnlocker.LibTSForge.SPP
throw new FileNotFoundException("Failed to find product matching supplied product key parameters.");
}
public PKeyConfig()
public ProductKeyConfig()
{
}
+9 -9
View File
@@ -70,21 +70,21 @@ namespace MetroUnlocker.LibTSForge.SPP
{
new CRCBlock
{
DataType = CRCBlockType.STRING,
KeyAsStr = "SppPkeyBindingProductKey",
ValueAsStr = ToString()
DataType = CRCBlockType.String,
KeyAsString = "SppPkeyBindingProductKey",
ValueAsString = ToString()
},
new CRCBlock
{
DataType = CRCBlockType.BINARY,
KeyAsStr = "SppPkeyBindingMiscData",
DataType = CRCBlockType.Binary,
KeyAsString = "SppPkeyBindingMiscData",
Value = new byte[] { }
},
new CRCBlock
{
DataType = CRCBlockType.STRING,
KeyAsStr = "SppPkeyBindingAlgorithm",
ValueAsStr = GetAlgoUri()
DataType = CRCBlockType.String,
KeyAsString = "SppPkeyBindingAlgorithm",
ValueAsString = GetAlgoUri()
}
});
@@ -245,7 +245,7 @@ namespace MetroUnlocker.LibTSForge.SPP
);
}
public byte[] GetPhoneData(PSVersion version)
public byte[] GetPhoneData(PhysicalStoreVersion version)
{
int serialHigh = Serial / 1000000;
int serialLow = Serial % 1000000;
+29 -38
View File
@@ -13,12 +13,12 @@ namespace MetroUnlocker.LibTSForge.SPP
{
public static class SLApi
{
private enum SLIDTYPE
private enum SLIDType
{
SL_ID_APPLICATION,
SL_ID_PRODUCT_SKU,
SL_ID_LICENSE_FILE,
SL_ID_LICENSE,
SLIDApplication,
SLIDProductSku,
SLIDLicenseFile,
SLIDLicense,
SL_ID_PKEY,
SL_ID_ALL_LICENSES,
SL_ID_ALL_LICENSE_FILES,
@@ -26,7 +26,7 @@ namespace MetroUnlocker.LibTSForge.SPP
SL_ID_LAST
}
private enum SLDATATYPE
private enum SLDataType
{
SL_DATA_NONE,
SL_DATA_SZ,
@@ -37,7 +37,7 @@ namespace MetroUnlocker.LibTSForge.SPP
}
[StructLayout(LayoutKind.Sequential)]
private struct SL_LICENSING_STATUS
private struct SLLicensingStatus
{
public Guid SkuId;
public uint eStatus;
@@ -47,8 +47,6 @@ namespace MetroUnlocker.LibTSForge.SPP
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);
@@ -65,7 +63,7 @@ namespace MetroUnlocker.LibTSForge.SPP
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);
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);
@@ -77,7 +75,7 @@ namespace MetroUnlocker.LibTSForge.SPP
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);
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);
@@ -89,7 +87,7 @@ namespace MetroUnlocker.LibTSForge.SPP
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);
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);
@@ -128,7 +126,7 @@ namespace MetroUnlocker.LibTSForge.SPP
{
using (SLContext sl = new SLContext())
{
SLDATATYPE type;
SLDataType type;
uint len;
IntPtr ppReturnLics;
@@ -144,17 +142,17 @@ namespace MetroUnlocker.LibTSForge.SPP
}
}
public static Guid GetLicenseFileId(Guid licId)
public static Guid GetLicenseFileId(Guid licenseId)
{
using (SLContext sl = new SLContext())
{
uint status;
uint count;
IntPtr ppReturnLics;
IntPtr returnLicenses;
status = SLGetSLIDList(sl.Handle, SLIDTYPE.SL_ID_LICENSE, ref licId, SLIDTYPE.SL_ID_LICENSE_FILE, out count, out ppReturnLics);
status = SLGetSLIDList(sl.Handle, SLIDType.SLIDLicense, ref licenseId, SLIDType.SLIDLicenseFile, out count, out returnLicenses);
return (status == 0 && count != 0) ? (Guid)Marshal.PtrToStructure(ppReturnLics, typeof(Guid)) : Guid.Empty;//new Guid(Marshal.PtrToStringAuto(ppReturnLics));
return (status == 0 && count != 0) ? (Guid)Marshal.PtrToStructure(returnLicenses, typeof(Guid)) : Guid.Empty;//new Guid(Marshal.PtrToStringAuto(returnLicenses));
}
}
@@ -168,9 +166,7 @@ namespace MetroUnlocker.LibTSForge.SPP
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);
@@ -180,22 +176,20 @@ namespace MetroUnlocker.LibTSForge.SPP
}
}
public static string GetMetaStr(Guid actId, string value)
public static string GetMetaStr(Guid productSkuId, string value)
{
using (SLContext sl = new SLContext())
{
uint len;
SLDATATYPE type;
IntPtr ppbValue;
uint length;
SLDataType type;
IntPtr binaryValue;
uint status = SLGetProductSkuInformation(sl.Handle, ref actId, value, out type, out len, out ppbValue);
uint status = SLGetProductSkuInformation(sl.Handle, ref productSkuId, value, out type, out length, out binaryValue);
if (status != 0 || len == 0 || type != SLDATATYPE.SL_DATA_SZ)
{
if (status != 0 || length == 0 || type != SLDataType.SL_DATA_SZ)
return null;
}
return Marshal.PtrToStringAuto(ppbValue);
return Marshal.PtrToStringAuto(binaryValue);
}
}
@@ -213,18 +207,17 @@ namespace MetroUnlocker.LibTSForge.SPP
}
}
public static Guid GetInstalledPkeyId(Guid actId)
public static Guid GetInstalledProductKeyId(Guid actId)
{
using (SLContext sl = new SLContext())
{
uint status;
uint count;
IntPtr pProductKeyIds;
IntPtr productKeyIds;
status = SLGetInstalledProductKeyIds(sl.Handle, ref actId, out count, out pProductKeyIds);
status = SLGetInstalledProductKeyIds(sl.Handle, ref actId, out count, out productKeyIds);
//unsafe { return *(Guid*)pProductKeyIds; }
return (status == 0 && count != 0) ? (Guid)Marshal.PtrToStructure(pProductKeyIds, typeof(Guid)) : Guid.Empty;
return (status == 0 && count != 0) ? (Guid)Marshal.PtrToStructure(productKeyIds, typeof(Guid)) : Guid.Empty;
}
}
@@ -243,7 +236,7 @@ namespace MetroUnlocker.LibTSForge.SPP
{
using (SLContext sl = new SLContext())
{
SLDATATYPE type;
SLDataType type;
uint count;
IntPtr ppbValue;
@@ -255,9 +248,7 @@ namespace MetroUnlocker.LibTSForge.SPP
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)
@@ -267,7 +258,7 @@ namespace MetroUnlocker.LibTSForge.SPP
uint count;
IntPtr pAppIds;
uint status = SLGetSLIDList(sl.Handle, SLIDTYPE.SL_ID_PRODUCT_SKU, ref actId, SLIDTYPE.SL_ID_APPLICATION, out count, out pAppIds);
uint status = SLGetSLIDList(sl.Handle, SLIDType.SLIDProductSku, ref actId, SLIDType.SLIDApplication, out count, out pAppIds);
if (status != 0 || count == 0)
return Guid.Empty;
@@ -281,7 +272,7 @@ namespace MetroUnlocker.LibTSForge.SPP
using (SLContext sl = new SLContext())
{
uint count;
SLDATATYPE type;
SLDataType type;
IntPtr ppbValue;
uint status = SLGetProductSkuInformation(sl.Handle, ref actId, "DependsOn", out type, out count, out ppbValue);
@@ -7,9 +7,18 @@ namespace MetroUnlocker.LibTSForge.TokenStore
{
public class TokenEntry
{
public string Name;
public string Extension;
public byte[] Data;
public bool Populated;
public string Name { get; set; }
public string Extension { get; set; }
public byte[] Data { get; set; }
public bool Populated { get; set; }
public TokenEntry() : this("", "", new byte[] { }, false) {}
public TokenEntry(string name, string extension, byte[] data, bool populated = true)
{
Name = name;
Extension = extension;
Data = data;
Populated = populated;
}
}
}
@@ -10,21 +10,21 @@ 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 uint Version = 3;
private static readonly int EntrySize = 0x9E;
private static readonly int BlockSize = 0x4020;
private static readonly int EntriesPerBlock = BlockSize / EntrySize;
private static readonly int BlockPadSize = 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 static readonly byte[] Header = Enumerable.Repeat((byte)0x55, 0x20).ToArray();
private static readonly byte[] Footer = Enumerable.Repeat((byte)0xAA, 0x20).ToArray();
private List<TokenEntry> Entries = new List<TokenEntry>();
public FileStream TokensFile;
public FileStream TokensFile { get; set; }
public void Deserialize()
{
if (TokensFile.Length < BLOCK_SIZE) return;
if (TokensFile.Length < BlockSize) return;
TokensFile.Seek(0x24, SeekOrigin.Begin);
uint nextBlock = 0;
@@ -35,7 +35,7 @@ namespace MetroUnlocker.LibTSForge.TokenStore
uint curOffset = reader.ReadUInt32();
nextBlock = reader.ReadUInt32();
for (int i = 0; i < ENTRIES_PER_BLOCK; i++)
for (int i = 0; i < EntriesPerBlock; i++)
{
curOffset = reader.ReadUInt32();
bool populated = reader.ReadUInt32() == 1;
@@ -50,9 +50,7 @@ namespace MetroUnlocker.LibTSForge.TokenStore
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);
@@ -60,13 +58,7 @@ namespace MetroUnlocker.LibTSForge.TokenStore
reader.BaseStream.Seek(curOffset + 0x14, SeekOrigin.Begin);
Entries.Add(new TokenEntry
{
Name = reader.ReadNullTerminatedString(0x82),
Extension = reader.ReadNullTerminatedString(0x8),
Data = contentData,
Populated = populated
});
Entries.Add(new TokenEntry(reader.ReadNullTerminatedString(0x82), reader.ReadNullTerminatedString(0x8), contentData, populated));
}
reader.BaseStream.Seek(nextBlock, SeekOrigin.Begin);
@@ -79,31 +71,16 @@ namespace MetroUnlocker.LibTSForge.TokenStore
using (BinaryWriter writer = new BinaryWriter(tokens))
{
writer.Write(VERSION);
writer.Write(CONTS_HEADER);
writer.Write(Version);
writer.Write(Header);
int curBlockOffset = (int)writer.BaseStream.Position;
int curEntryOffset = curBlockOffset + 0x8;
int curContsOffset = curBlockOffset + BLOCK_SIZE;
int curContsOffset = curBlockOffset + BlockSize;
for (int eIndex = 0; eIndex < ((Entries.Count / ENTRIES_PER_BLOCK) + 1) * ENTRIES_PER_BLOCK; eIndex++)
for (int eIndex = 0; eIndex < ((Entries.Count / EntriesPerBlock) + 1) * EntriesPerBlock; eIndex++)
{
TokenEntry entry;
if (eIndex < Entries.Count)
{
entry = Entries[eIndex];
}
else
{
entry = new TokenEntry
{
Name = "",
Extension = "",
Populated = false,
Data = new byte[] { }
};
}
TokenEntry entry = eIndex < Entries.Count ? entry = Entries[eIndex] : new TokenEntry();
writer.BaseStream.Seek(curBlockOffset, SeekOrigin.Begin);
writer.Write(curBlockOffset);
@@ -122,15 +99,15 @@ namespace MetroUnlocker.LibTSForge.TokenStore
if (entry.Populated)
{
writer.BaseStream.Seek(curContsOffset, SeekOrigin.Begin);
writer.Write(CONTS_HEADER);
writer.Write(Header);
writer.Write(entry.Data.Length);
writer.Write(CryptoUtils.SHA256Hash(entry.Data));
writer.Write(entry.Data);
writer.Write(CONTS_FOOTER);
writer.Write(Footer);
curContsOffset = (int)writer.BaseStream.Position;
}
if ((eIndex + 1) % ENTRIES_PER_BLOCK == 0 && eIndex != 0)
if ((eIndex + 1) % EntriesPerBlock == 0 && eIndex != 0)
{
if (eIndex < Entries.Count)
{
@@ -139,21 +116,21 @@ namespace MetroUnlocker.LibTSForge.TokenStore
}
writer.BaseStream.Seek(curEntryOffset, SeekOrigin.Begin);
writer.WritePadding(BLOCK_PAD_SIZE);
writer.WritePadding(BlockPadSize);
writer.BaseStream.Seek(curBlockOffset, SeekOrigin.Begin);
byte[] blockHash;
byte[] blockData = new byte[BLOCK_SIZE - 0x20];
byte[] blockData = new byte[BlockSize - 0x20];
tokens.Read(blockData, 0, BLOCK_SIZE - 0x20);
tokens.Read(blockData, 0, BlockSize - 0x20);
blockHash = CryptoUtils.SHA256Hash(blockData);
writer.BaseStream.Seek(curBlockOffset + BLOCK_SIZE - 0x20, SeekOrigin.Begin);
writer.BaseStream.Seek(curBlockOffset + BlockSize - 0x20, SeekOrigin.Begin);
writer.Write(blockHash);
curBlockOffset = curContsOffset;
curEntryOffset = curBlockOffset + 0x8;
curContsOffset = curBlockOffset + BLOCK_SIZE;
curContsOffset = curBlockOffset + BlockSize;
}
}
@@ -190,71 +167,41 @@ namespace MetroUnlocker.LibTSForge.TokenStore
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)
public void DeleteUnpopulatedEntry(string name, string extension)
{
List<TokenEntry> delEntries = new List<TokenEntry>();
foreach (TokenEntry entry in Entries)
{
if (entry.Name == name && entry.Extension == ext && !entry.Populated)
{
delEntries.Add(entry);
}
}
Entries = Entries.Except(delEntries).ToList();
Entries = Entries.FindAll(entry => entry.Name != name && entry.Extension != extension && entry.Populated);
}
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;
}
}
if (entry.Populated) return entry;
return null;
}
public TokenMeta GetMetaEntry(string name)
{
DeleteUnpopEntry(name, "xml");
DeleteUnpopulatedEntry(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;
return entry == null ? new TokenMeta(name) : new TokenMeta(entry.Data);
}
public void SetEntry(string name, string ext, byte[] data)
public void SetEntry(string name, string extension, byte[] data)
{
for (int i = 0; i < Entries.Count; i++)
{
TokenEntry entry = Entries[i];
if (entry.Name == name && entry.Extension == ext && entry.Populated)
if (entry.Name == name && entry.Extension == extension && entry.Populated)
{
entry.Data = data;
Entries[i] = entry;
@@ -262,13 +209,7 @@ namespace MetroUnlocker.LibTSForge.TokenStore
}
}
Entries.Add(new TokenEntry
{
Populated = true,
Name = name,
Extension = ext,
Data = data
});
Entries.Add(new TokenEntry(name, extension, data));
}
public static string GetPath()
+19 -32
View File
@@ -4,8 +4,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using MetroUnlocker.LibTSForge.SPP;
using MetroUnlocker.LibTSForge.PhysicalStore;
@@ -21,39 +22,37 @@ namespace MetroUnlocker
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")));
throw new COMException(string.Format("Failed to deposit fake CID. Status code: 0x{0}", status.ToString("X")), (int)status);
}
public static void Activate(PSVersion version, bool production, Guid actId)
public static void Activate(PhysicalStoreVersion version, bool production, Guid activationId)
{
Guid appId = SLApi.GetAppId(actId);
Guid appId = SLApi.GetAppId(activationId);
string instId = SLApi.GetInstallationId(actId);
Guid pkeyId = SLApi.GetInstalledPkeyId(actId);
string instId = SLApi.GetInstallationId(activationId);
Guid pkeyId = SLApi.GetInstalledProductKeyId(activationId);
Utils.KillSPP();
using (PhysicalStore store = new PhysicalStore(version, production))
{
byte[] hwidBlock = Constants.UniversalHWIDBlock;
byte[] hwidBlock = Constants.UniversalHardwareIdBlock;
byte[] iidHash = CryptoUtils.SHA256Hash(Utils.EncodeString(instId + '\0' + Constants.ZeroCID));
string key = string.Format("SPPSVC\\{0}\\{1}", appId, actId);
string key = string.Format("SPPSVC\\{0}\\{1}", appId, activationId);
ModernBlock keyBlock = store.GetBlock(key, pkeyId.ToString());
if (keyBlock == null)
{
throw new InvalidDataException("Failed to get product key data for activation ID: 0x" + actId + ".");
}
throw new InvalidDataException("Failed to get product key data for activation ID: 0x" + activationId + ".");
VariableBag pkb = new VariableBag(keyBlock.Data);
VariableBag keyBag = new VariableBag(keyBlock.Data);
byte[] pkeyData = pkb.GetBlock("SppPkeyPhoneActivationData").Value;
byte[] pkeyData = keyBag.GetBlock("SppPkeyPhoneActivationData").Value;
pkb.DeleteBlock("SppPkeyVirtual");
store.SetBlock(key, pkeyId.ToString(), pkb.Serialize());
keyBag.DeleteBlock("SppPkeyVirtual");
store.SetBlock(key, pkeyId.ToString(), keyBag.Serialize());
BinaryWriter writer = new BinaryWriter(new MemoryStream());
writer.Write(0x20);
@@ -69,27 +68,15 @@ namespace MetroUnlocker
writer.Write(pkeyData);
byte[] tsPkeyInfoData = Utils.GetBytes(writer);
string path = "msft:Windows/7.0/Phone/Cached/";
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
}
new ModernBlock(key, path + "HwidBlock/" + pkeyId, tsHwidData),
new ModernBlock(key, path + "PKeyInfo/" + pkeyId, tsPkeyInfoData)
});
}
Deposit(actId, instId);
Deposit(activationId, instId);
SLApi.RefreshLicenseStatus();
SLApi.FireStateChangedEvent(appId);