···11+using System;
22+using System.Linq;
33+using System.Security.Cryptography;
44+55+namespace GfWLUtility
66+{
77+ internal class DecryptionFailedException : Exception
88+ {
99+ public DecryptionFailedException(string message) : base(message) { }
1010+ }
1111+ internal class InvalidKeysetException : Exception
1212+ {
1313+ public InvalidKeysetException(string message) : base(message) { }
1414+ }
1515+1616+ internal class PanoramaCrypto
1717+ {
1818+ private int _keySet;
1919+ private ICryptoTransform _encryptor;
2020+ private ICryptoTransform _decryptor;
2121+2222+ //public static PanoramaCrypto Stub = new PanoramaCrypto(0); // for testing CBC mode, not in xlive.dll
2323+2424+ public static PanoramaCrypto Obfuscation = new PanoramaCrypto(1); // obfuscation
2525+ public static PanoramaCrypto SysLink = new PanoramaCrypto(2); // system link
2626+2727+ private void SetUpAes(byte[] key)
2828+ {
2929+ Aes aes = Aes.Create();
3030+ aes.Key = key;
3131+ aes.Mode = CipherMode.ECB;
3232+ _encryptor = aes.CreateEncryptor();
3333+ _decryptor = aes.CreateDecryptor();
3434+ }
3535+3636+ public PanoramaCrypto(int keySet)
3737+ {
3838+ _keySet = keySet;
3939+ // we have the system link key available thanks to x360
4040+ // TODO(Emma): get the GfWL obfuscation keyset!
4141+ if (_keySet == 2)
4242+ SetUpAes(new byte[0x10] { 0x64, 0xFA, 0x1A, 0xC2, 0x0F, 0xD7, 0x58, 0x07, 0xCA, 0xE6, 0x74, 0xBA, 0xA3, 0xB4, 0x78, 0x7F });
4343+ }
4444+4545+ private void XorWithIv(byte[] block, byte[] iv)
4646+ {
4747+ for (int i = 0; i < 0x10; i++)
4848+ block[i] ^= iv[i];
4949+ }
5050+5151+ private static byte[] StubDecrypt(byte[] input_block)
5252+ {
5353+ byte[] output_block = new byte[0x10];
5454+ for (int i = 0; i < 0x10; i++)
5555+ output_block[i] = (byte)~input_block[i];
5656+ return output_block;
5757+ }
5858+5959+ private byte[] AesDecrypt(byte[] input_block)
6060+ {
6161+ byte[] output_block = new byte[0x10];
6262+ _decryptor.TransformBlock(input_block, 0, 0x10, output_block, 0);
6363+ return output_block;
6464+ }
6565+6666+ private byte[] AesEncrypt(byte[] input_block)
6767+ {
6868+ byte[] output_block = new byte[0x10];
6969+ _encryptor.TransformBlock(input_block, 0, 0x10, output_block, 0);
7070+ return output_block;
7171+ }
7272+7373+ private byte[] Decrypt(byte[] block)
7474+ {
7575+ if (_keySet == 0)
7676+ return StubDecrypt(block);
7777+ else if (_keySet == 2)
7878+ return AesDecrypt(block);
7979+ throw new InvalidKeysetException("PanoramaCrypto doesn't support this key set for decryption");
8080+ }
8181+8282+ private byte[] Encrypt(byte[] block)
8383+ {
8484+ if (_keySet == 0)
8585+ return StubDecrypt(block);
8686+ else if (_keySet == 2)
8787+ return AesEncrypt(block);
8888+ throw new InvalidKeysetException("PanoramaCrypto doesn't support this key set for encryption");
8989+ }
9090+9191+ public byte[] DecryptBuffer(byte[] buffer, bool padding = true)
9292+ {
9393+ byte[] iv = new byte[0x10];
9494+ byte[] currBlock = new byte[0x10];
9595+ byte[] output = new byte[buffer.Length];
9696+ // sanity check
9797+ if (buffer.Length % 0x10 != 0)
9898+ throw new DecryptionFailedException("Invalid length");
9999+ // decrypt each block of the input data
100100+ for (int i = 0; i < buffer.Length; i += 0x10)
101101+ {
102102+ Buffer.BlockCopy(buffer, i, currBlock, 0, 0x10);
103103+ byte[] newBlock = Decrypt(currBlock);
104104+ XorWithIv(newBlock, iv);
105105+ Buffer.BlockCopy(currBlock, 0, iv, 0, 0x10);
106106+ Buffer.BlockCopy(newBlock, 0, output, i, 0x10);
107107+ }
108108+ // hacky (and slow, inefficient) handling for PKCS#5 padding
109109+ if (padding)
110110+ {
111111+ byte padByte = output[output.Length - 1];
112112+ if (padByte <= 0x10)
113113+ {
114114+ int num_pad_bytes = (int)padByte;
115115+ byte[] padding_bytes = output.Skip(output.Length - num_pad_bytes).ToArray();
116116+ if (padding_bytes.Length == padByte)
117117+ {
118118+ for (int i = 0; i < padding_bytes.Length; i++)
119119+ if (padding_bytes[i] != padByte)
120120+ throw new DecryptionFailedException("Invalid padding");
121121+ } else
122122+ {
123123+ throw new DecryptionFailedException("Invalid padding");
124124+ }
125125+ return output.Take(output.Length - num_pad_bytes).ToArray();
126126+ }
127127+ else
128128+ {
129129+ throw new DecryptionFailedException("Invalid padding");
130130+ }
131131+ }
132132+ return output;
133133+ }
134134+ }
135135+}
+33-4
UserManager.cs
···11-using System;
22-using System.Collections.Generic;
11+using System.Collections.Generic;
32using System.Drawing;
44-using System.Linq;
33+using System.IO;
54using System.Runtime.InteropServices;
66-using System.Text;
7586namespace GfWLUtility
97{
···9694 {
9795 FoundUserExists(xuid);
9896 KnownUsers[xuid].Gamertag = name;
9797+ }
9898+9999+ public static void PopulateUserInformation(ulong xuid, string accountFile = null)
100100+ {
101101+ FoundUserExists(xuid);
102102+ byte[] accBytes = null;
103103+ // if we're given one, attempt to decrypt profile information directly
104104+ if (accountFile != null)
105105+ {
106106+ byte[] encBytes = File.ReadAllBytes(accountFile);
107107+ accBytes = XeKeys.UnObfuscate(encBytes);
108108+ }
109109+ // otherwise check if we have decrypted profile information in the account cache
110110+ if (accBytes == null)
111111+ {
112112+ string accCacheFilename = UtilityFuncs.GetLocalDirectory("ProfileCache") + xuid.ToString("x16") + ".bin";
113113+ if (File.Exists(accCacheFilename))
114114+ accBytes = File.ReadAllBytes(accCacheFilename);
115115+ }
116116+ // if we have a byte stream of the right length of a decrypted account file, hooray
117117+ if (accBytes != null && accBytes.Length == 0x17C)
118118+ {
119119+ XamAccount account = UtilityFuncs.BytesToStructure<XamAccount>(accBytes);
120120+ KnownUsers[xuid].Gamertag = account.Gamertag;
121121+ if ((account.Flags1 & 0x20000000) == 0x20000000)
122122+ KnownUsers[xuid].LiveEnabled = true;
123123+ KnownUsers[xuid].OnlineXUID = account.OnlineXUID;
124124+ KnownUsers[xuid].Pnet = account.OnlineServiceID == 0x54524150;
125125+ KnownUsers[xuid].MsaEmail = account.PassportEmail;
126126+ KnownUsers[xuid].HasFullInformation = true;
127127+ }
99128 }
100129 }
101130}
+30
XeKeys.cs
···11+using System;
22+using System.Linq;
33+44+namespace GfWLUtility
55+{
66+ /*
77+ * For Xbox 360 researchers among us who might've stumbled upon this code,
88+ * this is not what you're looking for, at all.
99+ *
1010+ * I recommend checking out emoose's ExCrypt library for a reference of
1111+ * Xbox 360 XeKeys, which will be much more useful for you:
1212+ * https://github.com/emoose/ExCrypt
1313+ */
1414+1515+ internal class XeKeys
1616+ {
1717+ public static byte[] UnObfuscate(byte[] input)
1818+ {
1919+ // skip over the header - this is populated on xenon but not xlive
2020+ byte[] buffer = input.Skip(0x18).ToArray();
2121+ try
2222+ {
2323+ return PanoramaCrypto.Obfuscation.DecryptBuffer(buffer);
2424+ } catch (Exception)
2525+ {
2626+ return null;
2727+ }
2828+ }
2929+ }
3030+}