Multipurpose utility for managing Games for Windows - LIVE installs and content. (Mirrored from https://github.com/InvoxiPlayGames/GfWLUtility)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

move profile info parsing into UserManager also adds XeKeysUnObfuscate and some PanoramaCrypto however no key yet :(

+201 -25
+2
GfWLUtility.csproj
··· 94 94 <Compile Include="MainWindow.Designer.cs"> 95 95 <DependentUpon>MainWindow.cs</DependentUpon> 96 96 </Compile> 97 + <Compile Include="PanoramaCrypto.cs" /> 97 98 <Compile Include="ProfileInfoExtractor.cs"> 98 99 <SubType>Form</SubType> 99 100 </Compile> ··· 105 106 <Compile Include="TitleManager.cs" /> 106 107 <Compile Include="UserManager.cs" /> 107 108 <Compile Include="UtilityFuncs.cs" /> 109 + <Compile Include="XeKeys.cs" /> 108 110 <EmbeddedResource Include="DownloadForm.resx"> 109 111 <DependentUpon>DownloadForm.cs</DependentUpon> 110 112 </EmbeddedResource>
+1 -21
MainWindow.cs
··· 1 1 using GfWLUtility.Properties; 2 2 using System; 3 - using System.Collections.Generic; 4 - using System.ComponentModel; 5 - using System.Data; 6 3 using System.Diagnostics; 7 4 using System.Drawing; 8 5 using System.Globalization; 9 6 using System.IO; 10 7 using System.Linq; 11 - using System.Reflection; 12 - using System.Runtime.InteropServices; 13 - using System.Text; 14 8 using System.Windows.Forms; 15 9 16 10 namespace GfWLUtility ··· 333 327 if ((xuidInt & 0xE000000000000000) != 0xE000000000000000) 334 328 continue; 335 329 UserManager.FoundUserExists(xuidInt); 336 - // check the account cache 337 - // TODO: move this into code outside of the main form 338 - string accCacheFilename = UtilityFuncs.GetLocalDirectory("ProfileCache") + xuidInt.ToString("x16") + ".bin"; 339 - if (File.Exists(accCacheFilename)) 340 - { 341 - byte[] accBytes = File.ReadAllBytes(accCacheFilename); 342 - XamAccount account = UtilityFuncs.BytesToStructure<XamAccount>(accBytes); 343 - UserManager.FoundUserGamertag(xuidInt, account.Gamertag); 344 - if ((account.Flags1 & 0x20000000) == 0x20000000) 345 - UserManager.KnownUsers[xuidInt].LiveEnabled = true; 346 - UserManager.KnownUsers[xuidInt].OnlineXUID = account.OnlineXUID; 347 - UserManager.KnownUsers[xuidInt].Pnet = account.OnlineServiceID == 0x54524150; 348 - UserManager.KnownUsers[xuidInt].MsaEmail = account.PassportEmail; 349 - UserManager.KnownUsers[xuidInt].HasFullInformation = true; 350 - } 330 + UserManager.PopulateUserInformation(xuidInt, $"{subdir}\\FFFE07D1\\00010000\\{xuidInt:X16}_MountPt\\Account"); 351 331 } 352 332 } 353 333
+135
PanoramaCrypto.cs
··· 1 + using System; 2 + using System.Linq; 3 + using System.Security.Cryptography; 4 + 5 + namespace GfWLUtility 6 + { 7 + internal class DecryptionFailedException : Exception 8 + { 9 + public DecryptionFailedException(string message) : base(message) { } 10 + } 11 + internal class InvalidKeysetException : Exception 12 + { 13 + public InvalidKeysetException(string message) : base(message) { } 14 + } 15 + 16 + internal class PanoramaCrypto 17 + { 18 + private int _keySet; 19 + private ICryptoTransform _encryptor; 20 + private ICryptoTransform _decryptor; 21 + 22 + //public static PanoramaCrypto Stub = new PanoramaCrypto(0); // for testing CBC mode, not in xlive.dll 23 + 24 + public static PanoramaCrypto Obfuscation = new PanoramaCrypto(1); // obfuscation 25 + public static PanoramaCrypto SysLink = new PanoramaCrypto(2); // system link 26 + 27 + private void SetUpAes(byte[] key) 28 + { 29 + Aes aes = Aes.Create(); 30 + aes.Key = key; 31 + aes.Mode = CipherMode.ECB; 32 + _encryptor = aes.CreateEncryptor(); 33 + _decryptor = aes.CreateDecryptor(); 34 + } 35 + 36 + public PanoramaCrypto(int keySet) 37 + { 38 + _keySet = keySet; 39 + // we have the system link key available thanks to x360 40 + // TODO(Emma): get the GfWL obfuscation keyset! 41 + if (_keySet == 2) 42 + SetUpAes(new byte[0x10] { 0x64, 0xFA, 0x1A, 0xC2, 0x0F, 0xD7, 0x58, 0x07, 0xCA, 0xE6, 0x74, 0xBA, 0xA3, 0xB4, 0x78, 0x7F }); 43 + } 44 + 45 + private void XorWithIv(byte[] block, byte[] iv) 46 + { 47 + for (int i = 0; i < 0x10; i++) 48 + block[i] ^= iv[i]; 49 + } 50 + 51 + private static byte[] StubDecrypt(byte[] input_block) 52 + { 53 + byte[] output_block = new byte[0x10]; 54 + for (int i = 0; i < 0x10; i++) 55 + output_block[i] = (byte)~input_block[i]; 56 + return output_block; 57 + } 58 + 59 + private byte[] AesDecrypt(byte[] input_block) 60 + { 61 + byte[] output_block = new byte[0x10]; 62 + _decryptor.TransformBlock(input_block, 0, 0x10, output_block, 0); 63 + return output_block; 64 + } 65 + 66 + private byte[] AesEncrypt(byte[] input_block) 67 + { 68 + byte[] output_block = new byte[0x10]; 69 + _encryptor.TransformBlock(input_block, 0, 0x10, output_block, 0); 70 + return output_block; 71 + } 72 + 73 + private byte[] Decrypt(byte[] block) 74 + { 75 + if (_keySet == 0) 76 + return StubDecrypt(block); 77 + else if (_keySet == 2) 78 + return AesDecrypt(block); 79 + throw new InvalidKeysetException("PanoramaCrypto doesn't support this key set for decryption"); 80 + } 81 + 82 + private byte[] Encrypt(byte[] block) 83 + { 84 + if (_keySet == 0) 85 + return StubDecrypt(block); 86 + else if (_keySet == 2) 87 + return AesEncrypt(block); 88 + throw new InvalidKeysetException("PanoramaCrypto doesn't support this key set for encryption"); 89 + } 90 + 91 + public byte[] DecryptBuffer(byte[] buffer, bool padding = true) 92 + { 93 + byte[] iv = new byte[0x10]; 94 + byte[] currBlock = new byte[0x10]; 95 + byte[] output = new byte[buffer.Length]; 96 + // sanity check 97 + if (buffer.Length % 0x10 != 0) 98 + throw new DecryptionFailedException("Invalid length"); 99 + // decrypt each block of the input data 100 + for (int i = 0; i < buffer.Length; i += 0x10) 101 + { 102 + Buffer.BlockCopy(buffer, i, currBlock, 0, 0x10); 103 + byte[] newBlock = Decrypt(currBlock); 104 + XorWithIv(newBlock, iv); 105 + Buffer.BlockCopy(currBlock, 0, iv, 0, 0x10); 106 + Buffer.BlockCopy(newBlock, 0, output, i, 0x10); 107 + } 108 + // hacky (and slow, inefficient) handling for PKCS#5 padding 109 + if (padding) 110 + { 111 + byte padByte = output[output.Length - 1]; 112 + if (padByte <= 0x10) 113 + { 114 + int num_pad_bytes = (int)padByte; 115 + byte[] padding_bytes = output.Skip(output.Length - num_pad_bytes).ToArray(); 116 + if (padding_bytes.Length == padByte) 117 + { 118 + for (int i = 0; i < padding_bytes.Length; i++) 119 + if (padding_bytes[i] != padByte) 120 + throw new DecryptionFailedException("Invalid padding"); 121 + } else 122 + { 123 + throw new DecryptionFailedException("Invalid padding"); 124 + } 125 + return output.Take(output.Length - num_pad_bytes).ToArray(); 126 + } 127 + else 128 + { 129 + throw new DecryptionFailedException("Invalid padding"); 130 + } 131 + } 132 + return output; 133 + } 134 + } 135 + }
+33 -4
UserManager.cs
··· 1 - using System; 2 - using System.Collections.Generic; 1 + using System.Collections.Generic; 3 2 using System.Drawing; 4 - using System.Linq; 3 + using System.IO; 5 4 using System.Runtime.InteropServices; 6 - using System.Text; 7 5 8 6 namespace GfWLUtility 9 7 { ··· 96 94 { 97 95 FoundUserExists(xuid); 98 96 KnownUsers[xuid].Gamertag = name; 97 + } 98 + 99 + public static void PopulateUserInformation(ulong xuid, string accountFile = null) 100 + { 101 + FoundUserExists(xuid); 102 + byte[] accBytes = null; 103 + // if we're given one, attempt to decrypt profile information directly 104 + if (accountFile != null) 105 + { 106 + byte[] encBytes = File.ReadAllBytes(accountFile); 107 + accBytes = XeKeys.UnObfuscate(encBytes); 108 + } 109 + // otherwise check if we have decrypted profile information in the account cache 110 + if (accBytes == null) 111 + { 112 + string accCacheFilename = UtilityFuncs.GetLocalDirectory("ProfileCache") + xuid.ToString("x16") + ".bin"; 113 + if (File.Exists(accCacheFilename)) 114 + accBytes = File.ReadAllBytes(accCacheFilename); 115 + } 116 + // if we have a byte stream of the right length of a decrypted account file, hooray 117 + if (accBytes != null && accBytes.Length == 0x17C) 118 + { 119 + XamAccount account = UtilityFuncs.BytesToStructure<XamAccount>(accBytes); 120 + KnownUsers[xuid].Gamertag = account.Gamertag; 121 + if ((account.Flags1 & 0x20000000) == 0x20000000) 122 + KnownUsers[xuid].LiveEnabled = true; 123 + KnownUsers[xuid].OnlineXUID = account.OnlineXUID; 124 + KnownUsers[xuid].Pnet = account.OnlineServiceID == 0x54524150; 125 + KnownUsers[xuid].MsaEmail = account.PassportEmail; 126 + KnownUsers[xuid].HasFullInformation = true; 127 + } 99 128 } 100 129 } 101 130 }
+30
XeKeys.cs
··· 1 + using System; 2 + using System.Linq; 3 + 4 + namespace GfWLUtility 5 + { 6 + /* 7 + * For Xbox 360 researchers among us who might've stumbled upon this code, 8 + * this is not what you're looking for, at all. 9 + * 10 + * I recommend checking out emoose's ExCrypt library for a reference of 11 + * Xbox 360 XeKeys, which will be much more useful for you: 12 + * https://github.com/emoose/ExCrypt 13 + */ 14 + 15 + internal class XeKeys 16 + { 17 + public static byte[] UnObfuscate(byte[] input) 18 + { 19 + // skip over the header - this is populated on xenon but not xlive 20 + byte[] buffer = input.Skip(0x18).ToArray(); 21 + try 22 + { 23 + return PanoramaCrypto.Obfuscation.DecryptBuffer(buffer); 24 + } catch (Exception) 25 + { 26 + return null; 27 + } 28 + } 29 + } 30 + }