From b1ca4f80bce17293b0e78f73cb01a9e058079544 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 11 Feb 2023 17:33:08 +0100 Subject: [PATCH] feat(crypto): add cryptography server, client, world, login... --- .../ClientLoginCryptography.cs | 41 +++ .../ClientWorldCryptography.cs | 311 ++++++++++++++++++ .../CryptographyManager.cs | 57 ++++ Core/NosSmooth.Cryptography/ICryptography.cs | 31 ++ .../NosSmooth.Cryptography.csproj | 9 + .../ServerLoginCryptography.cs | 62 ++++ .../ServerWorldCryptography.cs | 282 ++++++++++++++++ NosSmooth.sln | 32 ++ 8 files changed, 825 insertions(+) create mode 100644 Core/NosSmooth.Cryptography/ClientLoginCryptography.cs create mode 100644 Core/NosSmooth.Cryptography/ClientWorldCryptography.cs create mode 100644 Core/NosSmooth.Cryptography/CryptographyManager.cs create mode 100644 Core/NosSmooth.Cryptography/ICryptography.cs create mode 100644 Core/NosSmooth.Cryptography/NosSmooth.Cryptography.csproj create mode 100644 Core/NosSmooth.Cryptography/ServerLoginCryptography.cs create mode 100644 Core/NosSmooth.Cryptography/ServerWorldCryptography.cs diff --git a/Core/NosSmooth.Cryptography/ClientLoginCryptography.cs b/Core/NosSmooth.Cryptography/ClientLoginCryptography.cs new file mode 100644 index 0000000000000000000000000000000000000000..d1ddac3655018556bcbc82f957c3d1c6d6d4f099 --- /dev/null +++ b/Core/NosSmooth.Cryptography/ClientLoginCryptography.cs @@ -0,0 +1,41 @@ +// +// ClientLoginCryptography.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text; + +namespace NosSmooth.Cryptography; + +/// +/// A cryptography used for logging to NosTale server from the client. +/// +public class ClientLoginCryptography : ICryptography +{ + private static readonly Random Random = new Random(DateTime.Now.Millisecond); + + /// + public string Decrypt(in ReadOnlySpan bytes, Encoding encoding) + { + var output = new StringBuilder(); + foreach (var c in bytes) + { + output.Append(Convert.ToChar(c - 0xF)); + } + + return output.ToString(); + } + + /// + public byte[] Encrypt(string value, Encoding encoding) + { + var output = new byte[value.Length + 1]; + for (int i = 0; i < value.Length; i++) + { + output[i] = (byte)((value[i] ^ 0xC3) + 0xF); + } + output[output.Length - 1] = 0xD8; + return output; + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Cryptography/ClientWorldCryptography.cs b/Core/NosSmooth.Cryptography/ClientWorldCryptography.cs new file mode 100644 index 0000000000000000000000000000000000000000..f12eeb0215e67ac5cc1ea95a2fa453aef4ffd2cf --- /dev/null +++ b/Core/NosSmooth.Cryptography/ClientWorldCryptography.cs @@ -0,0 +1,311 @@ +// +// ClientWorldCryptography.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text; + +namespace NosSmooth.Cryptography; + +/// +/// A cryptography used on world server, has to have a session id (encryption key) set from the client. +/// +public class ClientWorldCryptography : ICryptography +{ + private static readonly char[] Keys = { ' ', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'n' }; + + /// + /// Gets or sets the encryption key. + /// + public int EncryptionKey { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// Encryption key received by LoginServer. + public ClientWorldCryptography(int encryptionKey = 0) + { + EncryptionKey = encryptionKey; + } + + /// + public string Decrypt(in ReadOnlySpan bytes, Encoding encoding) + { + int index = 0; + var currentPacket = new StringBuilder(); + + while (index < bytes.Length) + { + byte currentByte = bytes[index++]; + + if (currentByte == 0xFF) + { + currentPacket.Append('\n'); + continue; + } + + int length = currentByte & 0x7F; + + if ((currentByte & 0x80) != 0) + { + while (length != 0) + { + if (index < bytes.Length) + { + currentByte = bytes[index++]; + int firstIndex = (currentByte & 0xF0) >> 4; + char first = '?'; + if (firstIndex != 0) + { + firstIndex--; + first = firstIndex != 14 ? Keys[firstIndex] : '\u0000'; + } + + if (first != 0x6E) + { + currentPacket.Append(first); + } + + if (length <= 1) + { + break; + } + + int secondIndex = currentByte & 0xF; + char second = '?'; + if (secondIndex != 0) + { + secondIndex--; + second = secondIndex != 14 ? Keys[secondIndex] : '\u0000'; + } + + if (second != 0x6E) + { + currentPacket.Append(second); + } + + length -= 2; + } + else + { + length--; + } + } + } + else + { + while (length != 0) + { + if (index < bytes.Length) + { + currentPacket.Append((char)(bytes[index] ^ 0xFF)); + index++; + } + else if (index == bytes.Length) + { + currentPacket.Append((char)0xFF); + index++; + } + + length--; + } + } + } + + return currentPacket.ToString(); + } + + /// + public byte[] Encrypt(string value, Encoding encoding) + { + var output = new List(); + + string mask = new string + ( + value.Select + ( + c => + { + sbyte b = (sbyte)c; + if (c == '#' || c == '/' || c == '%') + { + return '0'; + } + + if ((b -= 0x20) == 0 || (b += unchecked((sbyte)0xF1)) < 0 || (b -= 0xB) < 0 || + b - unchecked((sbyte)0xC5) == 0) + { + return '1'; + } + + return '0'; + } + ).ToArray() + ); + + int packetLength = value.Length; + + int sequenceCounter = 0; + int currentPosition = 0; + + while (currentPosition <= packetLength) + { + int lastPosition = currentPosition; + while (currentPosition < packetLength && mask[currentPosition] == '0') + { + currentPosition++; + } + + int sequences; + int length; + + if (currentPosition != 0) + { + length = currentPosition - lastPosition; + sequences = length / 0x7E; + for (int i = 0; i < length; i++, lastPosition++) + { + if (i == sequenceCounter * 0x7E) + { + if (sequences == 0) + { + output.Add((byte)(length - i)); + } + else + { + output.Add(0x7E); + sequences--; + sequenceCounter++; + } + } + + output.Add((byte)((byte)value[lastPosition] ^ 0xFF)); + } + } + + if (currentPosition >= packetLength) + { + break; + } + + lastPosition = currentPosition; + while (currentPosition < packetLength && mask[currentPosition] == '1') + { + currentPosition++; + } + + if (currentPosition == 0) + { + continue; + } + + length = currentPosition - lastPosition; + sequences = length / 0x7E; + for (int i = 0; i < length; i++, lastPosition++) + { + if (i == sequenceCounter * 0x7E) + { + if (sequences == 0) + { + output.Add((byte)((length - i) | 0x80)); + } + else + { + output.Add(0x7E | 0x80); + sequences--; + sequenceCounter++; + } + } + + byte currentByte = (byte)value[lastPosition]; + switch (currentByte) + { + case 0x20: + currentByte = 1; + break; + case 0x2D: + currentByte = 2; + break; + case 0xFF: + currentByte = 0xE; + break; + default: + currentByte -= 0x2C; + break; + } + + if (currentByte == 0x00) + { + continue; + } + + if (i % 2 == 0) + { + output.Add((byte)(currentByte << 4)); + } + else + { + output[output.Count - 1] = (byte)(output.Last() | currentByte); + } + } + } + + output.Add(0xFF); + + sbyte sessionNumber = (sbyte)((EncryptionKey >> 6) & 0xFF & 0x80000003); + + if (sessionNumber < 0) + { + sessionNumber = (sbyte)(((sessionNumber - 1) | 0xFFFFFFFC) + 1); + } + + byte sessionKey = (byte)(EncryptionKey & 0xFF); + + if (EncryptionKey != 0) + { + sessionNumber = -1; + } + + switch (sessionNumber) + { + case 0: + for (int i = 0; i < output.Count; i++) + { + output[i] = (byte)(output[i] + sessionKey + 0x40); + } + + break; + case 1: + for (int i = 0; i < output.Count; i++) + { + output[i] = (byte)(output[i] - (sessionKey + 0x40)); + } + + break; + case 2: + for (int i = 0; i < output.Count; i++) + { + output[i] = (byte)((output[i] ^ 0xC3) + sessionKey + 0x40); + } + + break; + case 3: + for (int i = 0; i < output.Count; i++) + { + output[i] = (byte)((output[i] ^ 0xC3) - (sessionKey + 0x40)); + } + + break; + default: + for (int i = 0; i < output.Count; i++) + { + output[i] = (byte)(output[i] + 0x0F); + } + + break; + } + + return output.ToArray(); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Cryptography/CryptographyManager.cs b/Core/NosSmooth.Cryptography/CryptographyManager.cs new file mode 100644 index 0000000000000000000000000000000000000000..f09a181a936cdfa59941ecbf096eae358cf4f4b2 --- /dev/null +++ b/Core/NosSmooth.Cryptography/CryptographyManager.cs @@ -0,0 +1,57 @@ +// +// CryptographyManager.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace NosSmooth.Cryptography; + +/// +/// A storage of server and client cryptography. +/// +public class CryptographyManager +{ + /// + /// Initializes a new instance of the class. + /// + public CryptographyManager() + { + ServerWorld = new ServerWorldCryptography(0); + ServerLogin = new ServerLoginCryptography(); + ClientLogin = new ClientLoginCryptography(); + ClientWorld = new ClientWorldCryptography(0); + } + + /// + /// Gets the cryptography for server world. + /// + public ICryptography ServerWorld { get; } + + /// + /// Gets the cryptography for server login. + /// + public ICryptography ServerLogin { get; } + + /// + /// Gets the cryptography for client world. + /// + public ICryptography ClientWorld { get; } + + /// + /// Gets the cryptography for client login. + /// + public ICryptography ClientLogin { get; } + + /// + /// Gets or sets the encryption key of the connection. + /// + public int EncryptionKey + { + get => ((ServerWorldCryptography)ServerWorld).EncryptionKey; + set + { + ((ServerWorldCryptography)ServerWorld).EncryptionKey = value; + ((ClientWorldCryptography)ClientWorld).EncryptionKey = value; + } + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Cryptography/ICryptography.cs b/Core/NosSmooth.Cryptography/ICryptography.cs new file mode 100644 index 0000000000000000000000000000000000000000..d04560324f2d14c28f205402ea0020d47dbec7c3 --- /dev/null +++ b/Core/NosSmooth.Cryptography/ICryptography.cs @@ -0,0 +1,31 @@ +// +// ICryptography.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text; + +namespace NosSmooth.Cryptography; + +/// +/// An intefrace for NosTale cryptography, encryption, decryption of packets. +/// +public interface ICryptography +{ + /// + /// Decrypt the raw packet (byte array) to a readable list string. + /// + /// Bytes to decrypt. + /// The encoding. + /// Decrypted packet to string list. + string Decrypt(in ReadOnlySpan str, Encoding encoding); + + /// + /// Encrypt the string packet to byte array. + /// + /// String to encrypt. + /// The encoding. + /// Encrypted packet as byte array. + byte[] Encrypt(string packet, Encoding encoding); +} \ No newline at end of file diff --git a/Core/NosSmooth.Cryptography/NosSmooth.Cryptography.csproj b/Core/NosSmooth.Cryptography/NosSmooth.Cryptography.csproj new file mode 100644 index 0000000000000000000000000000000000000000..6836c6808fb21d00e7e92c8af606745302a3cb54 --- /dev/null +++ b/Core/NosSmooth.Cryptography/NosSmooth.Cryptography.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/Core/NosSmooth.Cryptography/ServerLoginCryptography.cs b/Core/NosSmooth.Cryptography/ServerLoginCryptography.cs new file mode 100644 index 0000000000000000000000000000000000000000..7c42a3b15370c0144c225a7c8f57ac61d976b3c5 --- /dev/null +++ b/Core/NosSmooth.Cryptography/ServerLoginCryptography.cs @@ -0,0 +1,62 @@ +// +// ServerLoginCryptography.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text; + +namespace NosSmooth.Cryptography; + +/// +/// A cryptography used for logging to NosTale server from the server. +/// +public class ServerLoginCryptography : ICryptography +{ + /// + public string Decrypt(in ReadOnlySpan str, Encoding encoding) + { + try + { + string decryptedPacket = string.Empty; + + foreach (byte character in str) + { + if (character > 14) + { + decryptedPacket += Convert.ToChar((character - 15) ^ 195); + } + else + { + decryptedPacket += Convert.ToChar((256 - (15 - character)) ^ 195); + } + } + + return decryptedPacket; + } + catch + { + return string.Empty; + } + } + + /// + public byte[] Encrypt(string packet, Encoding encoding) + { + try + { + packet += " "; + byte[] tmp = Encoding.Default.GetBytes(packet); + for (int i = 0; i < packet.Length; i++) + { + tmp[i] = Convert.ToByte(tmp[i] + 15); + } + tmp[tmp.Length - 1] = 25; + return tmp; + } + catch + { + return Array.Empty(); + } + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Cryptography/ServerWorldCryptography.cs b/Core/NosSmooth.Cryptography/ServerWorldCryptography.cs new file mode 100644 index 0000000000000000000000000000000000000000..b0409249a2c41b7b65a41263004efde6933966cc --- /dev/null +++ b/Core/NosSmooth.Cryptography/ServerWorldCryptography.cs @@ -0,0 +1,282 @@ +// +// ServerWorldCryptography.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text; + +namespace NosSmooth.Cryptography; + +/// +/// A cryptography used on world server, has to have a session id (encryption key) set from the world. +/// +public class ServerWorldCryptography : ICryptography +{ + /// + /// Initializes a new instance of the class. + /// + /// The encryption key. + public ServerWorldCryptography(int encryptionKey) + { + EncryptionKey = encryptionKey; + } + + /// + /// Gets or sets the encryption key. + /// + public int EncryptionKey { get; set; } + + /// + public string Decrypt(in ReadOnlySpan str, Encoding encoding) + { + if (EncryptionKey == 0) + { + return DecryptUnauthed(str); + } + + return DecryptAuthed(str, EncryptionKey, encoding); + } + + /// + public byte[] Encrypt(string packet, Encoding encoding) + { + byte[] strBytes = encoding.GetBytes(packet); + int bytesLength = strBytes.Length; + + byte[] encryptedData = new byte[bytesLength + (int)Math.Ceiling((decimal)bytesLength / 0x7E) + 1]; + + int ii = 0; + for (int i = 0; i < bytesLength; i++) + { + if (i % 0x7E == 0) + { + encryptedData[i + ii] = (byte)(bytesLength - i > 0x7E ? 0x7E : bytesLength - i); + ii++; + } + encryptedData[i + ii] = (byte)~strBytes[i]; + } + encryptedData[encryptedData.Length - 1] = 0xFF; + + return encryptedData; + } + + private static string DecryptAuthed(in ReadOnlySpan str, int encryptionKey, Encoding encoding) + { + var encryptedString = new StringBuilder(); + + int sessionKey = encryptionKey & 0xFF; + byte sessionNumber = unchecked((byte)(encryptionKey >> 6)); + sessionNumber &= 0xFF; + sessionNumber &= 3; + + switch (sessionNumber) + { + case 0: + foreach (byte character in str) + { + byte firstbyte = unchecked((byte)(sessionKey + 0x40)); + byte highbyte = unchecked((byte)(character - firstbyte)); + encryptedString.Append((char)highbyte); + } + + break; + + case 1: + foreach (byte character in str) + { + byte firstbyte = unchecked((byte)(sessionKey + 0x40)); + byte highbyte = unchecked((byte)(character + firstbyte)); + encryptedString.Append((char)highbyte); + } + + break; + + case 2: + foreach (byte character in str) + { + byte firstbyte = unchecked((byte)(sessionKey + 0x40)); + byte highbyte = unchecked((byte)(character - firstbyte ^ 0xC3)); + encryptedString.Append((char)highbyte); + } + + break; + + case 3: + foreach (byte character in str) + { + byte firstbyte = unchecked((byte)(sessionKey + 0x40)); + byte highbyte = unchecked((byte)(character + firstbyte ^ 0xC3)); + encryptedString.Append((char)highbyte); + } + + break; + + default: + encryptedString.Append((char)0xF); + break; + } + + string[] temp = encryptedString.ToString().Split((char)0xFF); + + var save = new StringBuilder(); + + for (int i = 0; i < temp.Length; i++) + { + save.Append(DecryptPrivate(temp[i].AsSpan(), encoding)); + if (i < temp.Length - 2) + { + save.Append((char)'\n'); + } + } + + return save.ToString(); + } + + private static string DecryptPrivate(in ReadOnlySpan str, Encoding encoding) + { + using var receiveData = new MemoryStream(); + char[] table = { ' ', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '\n' }; + for (int count = 0; count < str.Length; count++) + { + if (str[count] <= 0x7A) + { + int len = str[count]; + + for (int i = 0; i < len; i++) + { + count++; + + try + { + receiveData.WriteByte(unchecked((byte)(str[count] ^ 0xFF))); + } + catch + { + receiveData.WriteByte(255); + } + } + } + else + { + int len = str[count]; + len &= 0x7F; + + for (int i = 0; i < len; i++) + { + count++; + int highbyte; + try + { + highbyte = str[count]; + } + catch + { + highbyte = 0; + } + + highbyte &= 0xF0; + highbyte >>= 0x4; + + int lowbyte; + try + { + lowbyte = str[count]; + } + catch + { + lowbyte = 0; + } + + lowbyte &= 0x0F; + + if (highbyte != 0x0 && highbyte != 0xF) + { + receiveData.WriteByte(unchecked((byte)table[highbyte - 1])); + i++; + } + + if (lowbyte != 0x0 && lowbyte != 0xF) + { + receiveData.WriteByte(unchecked((byte)table[lowbyte - 1])); + } + } + } + } + + byte[] tmp = Encoding.Convert(encoding, Encoding.UTF8, receiveData.ToArray()); + return Encoding.UTF8.GetString(tmp); + } + + private static string DecryptUnauthed(in ReadOnlySpan str) + { + try + { + var encryptedStringBuilder = new StringBuilder(); + for (int i = 1; i < str.Length; i++) + { + if (Convert.ToChar(str[i]) == 0xE) + { + return encryptedStringBuilder.ToString(); + } + + int firstbyte = Convert.ToInt32(str[i] - 0xF); + int secondbyte = firstbyte; + secondbyte &= 240; + firstbyte = Convert.ToInt32(firstbyte - secondbyte); + secondbyte >>= 4; + + switch (secondbyte) + { + case 0: + case 1: + encryptedStringBuilder.Append(' '); + break; + + case 2: + encryptedStringBuilder.Append('-'); + break; + + case 3: + encryptedStringBuilder.Append('.'); + break; + + default: + secondbyte += 0x2C; + encryptedStringBuilder.Append(Convert.ToChar(secondbyte)); + break; + } + + switch (firstbyte) + { + case 0: + encryptedStringBuilder.Append(' '); + break; + + case 1: + encryptedStringBuilder.Append(' '); + break; + + case 2: + encryptedStringBuilder.Append('-'); + break; + + case 3: + encryptedStringBuilder.Append('.'); + break; + + default: + firstbyte += 0x2C; + encryptedStringBuilder.Append(Convert.ToChar(firstbyte)); + break; + } + } + + return encryptedStringBuilder.ToString(); + } + catch (OverflowException) + { + return string.Empty; + } + } +} \ No newline at end of file diff --git a/NosSmooth.sln b/NosSmooth.sln index 509aa3afc437ff292ec32665b54b457181acc31e..d566b90ebef8ccfea08515db39be931159d5c4ff 100644 --- a/NosSmooth.sln +++ b/NosSmooth.sln @@ -54,6 +54,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Extensions.Pathfi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Game.Tests", "Tests\NosSmooth.Game.Tests\NosSmooth.Game.Tests.csproj", "{21ECBA0F-38FA-45A5-8B42-9A76425204BC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Cryptography", "Core\NosSmooth.Cryptography\NosSmooth.Cryptography.csproj", "{9035E5AD-8B5F-46CA-B8E1-5273722B392D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pcap", "Pcap", "{B4224C41-FDB4-426A-83D8-C5AFB4F7D362}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Pcap", "Pcap\NosSmooth.Pcap\NosSmooth.Pcap.csproj", "{FD185689-EE0F-4403-A1EB-5511D2292CF4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -268,6 +274,30 @@ Global {21ECBA0F-38FA-45A5-8B42-9A76425204BC}.Release|x64.Build.0 = Release|Any CPU {21ECBA0F-38FA-45A5-8B42-9A76425204BC}.Release|x86.ActiveCfg = Release|Any CPU {21ECBA0F-38FA-45A5-8B42-9A76425204BC}.Release|x86.Build.0 = Release|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|x64.ActiveCfg = Debug|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|x64.Build.0 = Debug|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|x86.ActiveCfg = Debug|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|x86.Build.0 = Debug|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|Any CPU.Build.0 = Release|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|x64.ActiveCfg = Release|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|x64.Build.0 = Release|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|x86.ActiveCfg = Release|Any CPU + {9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|x86.Build.0 = Release|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|x64.ActiveCfg = Debug|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|x64.Build.0 = Debug|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|x86.ActiveCfg = Debug|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|x86.Build.0 = Debug|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|Any CPU.Build.0 = Release|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|x64.ActiveCfg = Release|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|x64.Build.0 = Release|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|x86.ActiveCfg = Release|Any CPU + {FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -290,6 +320,8 @@ Global {21F7EA0B-5E3C-4016-8ADD-28AF37C00782} = {3A6D13E3-4BBA-4B3D-AE99-BAC8B375F7DF} {564CAD6F-09B1-450B-83ED-9BCDE106B646} = {3A6D13E3-4BBA-4B3D-AE99-BAC8B375F7DF} {21ECBA0F-38FA-45A5-8B42-9A76425204BC} = {C6A8760D-92CB-4307-88A7-36CCAEBA4AD1} + {9035E5AD-8B5F-46CA-B8E1-5273722B392D} = {01B5E872-271F-4D30-A1AA-AD48D81840C5} + {FD185689-EE0F-4403-A1EB-5511D2292CF4} = {B4224C41-FDB4-426A-83D8-C5AFB4F7D362} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C5F46653-4DEC-429B-8580-4ED18ED9B4CA}