+2
GfWLUtility.csproj
+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
-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
+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
+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
+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
+
}