diff --git a/LibUserPreferences/LibUserPreferences.cpp b/LibUserPreferences/LibUserPreferences.cpp new file mode 100644 index 0000000..9703797 --- /dev/null +++ b/LibUserPreferences/LibUserPreferences.cpp @@ -0,0 +1,98 @@ +#include "LibUserPreferences.h" + +#include "../UserPreferences.Shared/Common.h" +#include "../UserPreferences.Shared/Encryption.h" + +DllExport APIResult DecryptData( + _In_ const uint8_t *encrypted_buffer, + _In_ size_t encrypted_buffer_size, + _In_ const char *machine_guid, + _In_ const char *salt_string, + _Out_ uint8_t *out_decrypted_buffer, + _Inout_ size_t *out_decrypted_buffer_size) +{ + if (encrypted_buffer == nullptr || out_decrypted_buffer_size == nullptr) + { + return kResult_GeneralFailure; + } + + std::string guid; + if (machine_guid == nullptr) + { + guid = UserPreferences::Common::GetMachineGuid(); + } + else + { + guid = machine_guid; + } + + std::string salt; + if (salt_string == nullptr) + { + salt = UserPreferences::Common::GetDefaultSalt(); + } + else + { + salt = salt_string; + } + + const auto encrypted = std::vector(&encrypted_buffer[0], &encrypted_buffer[encrypted_buffer_size]); + const auto decrypted = UserPreferences::Encryption::DecryptData(encrypted, guid, salt); + + if (out_decrypted_buffer == nullptr || *out_decrypted_buffer_size < decrypted.size()) + { + *out_decrypted_buffer_size = decrypted.size(); + return kResult_BufferTooSmall; + } + + *out_decrypted_buffer_size = decrypted.size(); + memcpy(out_decrypted_buffer, &decrypted[0], *out_decrypted_buffer_size); + return kResult_GeneralSuccess; +} + +DllExport APIResult EncryptData( + _In_ const uint8_t *plaintext_data, + _In_ size_t plaintext_data_size, + _In_ const char *machine_guid, + _In_ const char *salt_string, + _Out_ uint8_t *out_encrypted_buffer, + _Inout_ size_t *out_encrypted_buffer_size) +{ + if (plaintext_data == nullptr || out_encrypted_buffer_size == nullptr) + { + return kResult_GeneralFailure; + } + + std::string guid; + if (machine_guid == nullptr) + { + guid = UserPreferences::Common::GetMachineGuid(); + } + else + { + guid = machine_guid; + } + + std::string salt; + if (salt_string == nullptr) + { + salt = UserPreferences::Common::GetDefaultSalt(); + } + else + { + salt = salt_string; + } + + const auto plaintext = std::vector(&plaintext_data[0], &plaintext_data[plaintext_data_size]); + const auto encrypted = UserPreferences::Encryption::EncryptData(plaintext, guid, salt); + + if (out_encrypted_buffer == nullptr || *out_encrypted_buffer_size < encrypted.size()) + { + *out_encrypted_buffer_size = encrypted.size(); + return kResult_BufferTooSmall; + } + + *out_encrypted_buffer_size = encrypted.size(); + memcpy(out_encrypted_buffer, &encrypted[0], *out_encrypted_buffer_size); + return kResult_GeneralSuccess; +} diff --git a/LibUserPreferences/LibUserPreferences.h b/LibUserPreferences/LibUserPreferences.h new file mode 100644 index 0000000..3378b85 --- /dev/null +++ b/LibUserPreferences/LibUserPreferences.h @@ -0,0 +1,60 @@ +#pragma once +#define DllExport __declspec( dllexport ) + +#include +#include + +extern "C" +{ + enum APIResult { + kResult_GeneralSuccess = 0, + kResult_GeneralFailure = -1, + kResult_BufferTooSmall = -2 + }; + + /// + /// Decrypts encrypted data + /// + /// Encrypted bytes buffer to decrypt + /// Encrypted bytes buffer size in bytes + /// Optional machine GUID. Defaults to current machine GUID if not supplied. + /// Optional salt. Defaults to last known salt if not supplied. + /// Optional decrypted buffer + /// Decrypted buffer size in bytes. Will be set to the required size if decrypted buffer is NULL. + /// + /// kResult_GeneralSuccess on success. + /// kResult_BufferTooSmall if decrypted buffer size is too small or decrypted buffer was not provided. + /// kResult_GeneralFailure on failure. + /// + DllExport APIResult DecryptData( + _In_ const uint8_t *encrypted_buffer, + _In_ size_t encrypted_buffer_size, + _In_opt_ const char *machine_guid, + _In_opt_ const char *salt_string, + _Out_opt_ uint8_t *out_decrypted_buffer, + _Inout_ size_t *out_decrypted_buffer_size + ); + + /// + /// Encrypts data + /// + /// Plaintext to enctypt + /// Plaintext size in bytes + /// Optional machine GUID. Defaults to current machine GUID if not supplied. + /// Optional salt. Defaults to last known salt if not supplied. + /// Optional enctypted buffer + /// Encrypted buffer size in bytes. Will be set to the required size if enctypted buffer is NULL. + /// + /// kResult_GeneralSuccess on success. + /// kResult_BufferTooSmall if encrypted buffer size is too small or encrypted buffer was not provided. + /// kResult_GeneralFailure on failure. + /// + DllExport APIResult EncryptData( + _In_ const uint8_t *plaintext_data, + _In_ size_t plaintext_data_size, + _In_opt_ const char *machine_guid, + _In_opt_ const char *salt_string, + _Out_opt_ uint8_t *out_encrypted_buffer, + _Inout_ size_t *out_encrypted_buffer_size + ); +} diff --git a/LibUserPreferences/LibUserPreferences.vcxproj b/LibUserPreferences/LibUserPreferences.vcxproj new file mode 100644 index 0000000..6249391 --- /dev/null +++ b/LibUserPreferences/LibUserPreferences.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096} + Win32Proj + LibUserPreferences + 10.0.16299.0 + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Level3 + Disabled + true + _DEBUG;LIBUSERPREFERENCES_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + true + Windows + $(OutputPath);%(AdditionalLibraryDirectories) + + + + + Level3 + Disabled + true + WIN32;_DEBUG;LIBUSERPREFERENCES_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + true + Windows + $(OutputPath);%(AdditionalLibraryDirectories) + + + + + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;LIBUSERPREFERENCES_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + true + true + true + Windows + $(OutputPath);%(AdditionalLibraryDirectories) + + + + + Level3 + MaxSpeed + true + true + true + NDEBUG;LIBUSERPREFERENCES_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + true + true + true + Windows + $(OutputPath);%(AdditionalLibraryDirectories) + + + + + + + + + + + + \ No newline at end of file diff --git a/LibUserPreferences/LibUserPreferences.vcxproj.filters b/LibUserPreferences/LibUserPreferences.vcxproj.filters new file mode 100644 index 0000000..12455e5 --- /dev/null +++ b/LibUserPreferences/LibUserPreferences.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/UserPreferencesExplorer.Tests/Tests.cpp b/UserPreferences.Shared.Tests/Tests.cpp similarity index 77% rename from UserPreferencesExplorer.Tests/Tests.cpp rename to UserPreferences.Shared.Tests/Tests.cpp index a87c18a..28fc4d3 100644 --- a/UserPreferencesExplorer.Tests/Tests.cpp +++ b/UserPreferences.Shared.Tests/Tests.cpp @@ -3,8 +3,11 @@ #include -#include "../UserPreferencesExplorer/Encryption.cpp" -#include "../UserPreferencesExplorer/Utils.cpp" +#include "../UserPreferences.Shared/Encryption.h" +#include "../UserPreferences.Shared/Common.h" + +#include "../UserPreferences.Shared/Encryption.cpp" +#include "../UserPreferences.Shared/Common.cpp" TEST_CASE("Encryption") { @@ -26,14 +29,14 @@ TEST_CASE("Encryption") auto data_to_encrypt = std::vector(plaintext.begin(), plaintext.end()); data_to_encrypt.push_back('\0'); - auto result = Encryption::EncryptData(data_to_encrypt, guid, salt); + auto result = UserPreferences::Encryption::EncryptData(data_to_encrypt, guid, salt); REQUIRE(result == ciphertext); } SECTION("Decrypt") { - auto result = Encryption::DecryptData(ciphertext, guid, salt); + auto result = UserPreferences::Encryption::DecryptData(ciphertext, guid, salt); auto result_string = std::string(result.begin(), result.end() - 1); REQUIRE(result_string == plaintext); diff --git a/UserPreferencesExplorer.Tests/UserPreferencesExplorer.Tests.vcxproj.filters b/UserPreferences.Shared.Tests/UserPreferences.Shared.Tests.filters similarity index 100% rename from UserPreferencesExplorer.Tests/UserPreferencesExplorer.Tests.vcxproj.filters rename to UserPreferences.Shared.Tests/UserPreferences.Shared.Tests.filters diff --git a/UserPreferencesExplorer.Tests/UserPreferencesExplorer.Tests.vcxproj b/UserPreferences.Shared.Tests/UserPreferences.Shared.Tests.vcxproj similarity index 93% rename from UserPreferencesExplorer.Tests/UserPreferencesExplorer.Tests.vcxproj rename to UserPreferences.Shared.Tests/UserPreferences.Shared.Tests.vcxproj index 2402fd1..6f475fa 100644 --- a/UserPreferencesExplorer.Tests/UserPreferencesExplorer.Tests.vcxproj +++ b/UserPreferences.Shared.Tests/UserPreferences.Shared.Tests.vcxproj @@ -22,7 +22,7 @@ 15.0 {74BE1B66-A57A-44D1-9D92-05C1596BE7DA} Win32Proj - UserPreferencesExplorerTests + UserPreferencesSharedTests 10.0.15063.0 @@ -92,6 +92,7 @@ Console + $(OutputPath);%(AdditionalLibraryDirectories) @@ -104,6 +105,7 @@ Console + $(OutputPath);%(AdditionalLibraryDirectories) @@ -120,6 +122,7 @@ Console true true + $(OutputPath);%(AdditionalLibraryDirectories) @@ -136,6 +139,7 @@ Console true true + $(OutputPath);%(AdditionalLibraryDirectories) diff --git a/UserPreferencesExplorer.Tests/catch.hpp b/UserPreferences.Shared.Tests/catch.hpp similarity index 100% rename from UserPreferencesExplorer.Tests/catch.hpp rename to UserPreferences.Shared.Tests/catch.hpp diff --git a/UserPreferences.Shared/Common.cpp b/UserPreferences.Shared/Common.cpp new file mode 100644 index 0000000..f6da968 --- /dev/null +++ b/UserPreferences.Shared/Common.cpp @@ -0,0 +1,90 @@ +#include +#include +#include + +#include "Common.h" + +namespace UserPreferences +{ + namespace Common + { + static const auto kDefaultSalt = std::string("6E3F032949637D2E"); + + std::vector ReadAllBytes(const std::string &path) + { + std::ifstream inStream(path, std::ios::binary | std::ios::ate); + auto fileSize = static_cast(inStream.tellg()); + if (fileSize == 0) + { + throw std::exception("File is empty"); + } + + auto fileBytes = std::vector(fileSize); + + inStream.seekg(0, std::ios::beg); + inStream.read(reinterpret_cast(&fileBytes[0]), fileSize); + inStream.close(); + + return fileBytes; + } + + std::string GetMachineGuid() + { + static const auto kMachineGuidPath = std::string("SOFTWARE\\Microsoft\\Cryptography"); + static const auto kMachineGuidName = std::string("MachineGuid"); + + HKEY key_handle; + + auto result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, kMachineGuidPath.c_str(), NULL, KEY_READ | KEY_WOW64_64KEY, &key_handle); + if (result != ERROR_SUCCESS) + { + RegCloseKey(key_handle); + throw std::exception("RegOpenKeyEx - FAILED"); + } + + DWORD buff_size = 0; + result = RegQueryValueExA(key_handle, kMachineGuidName.c_str(), nullptr, nullptr, nullptr, &buff_size); + if (result != ERROR_SUCCESS) + { + RegCloseKey(key_handle); + throw std::exception("RegQueryValueEx - FAILED"); + } + + auto machine_guid = std::vector(buff_size); + result = RegQueryValueExA(key_handle, kMachineGuidName.c_str(), nullptr, nullptr, &machine_guid[0], &buff_size); + if (result != ERROR_SUCCESS) + { + RegCloseKey(key_handle); + throw std::exception("RegQueryValueEx - FAILED"); + } + + RegCloseKey(key_handle); + + return std::string(machine_guid.begin(), machine_guid.end() - 1); + } + + std::string GetPathToUserPreferencesBag() + { + static const auto kDefaultUserPreferencesPath = std::string("\\LindenLab\\SansarClient\\UserPreferences.bag"); + static const auto kEnvironmentVariable = std::string("localappdata"); + + // Resize our container to hold the environment variable + std::vector path(1); + auto required_path_length = GetEnvironmentVariableA(kEnvironmentVariable.c_str(), reinterpret_cast(&path[0]), 0); + path.resize(required_path_length); + + // Get the actual environment variable + GetEnvironmentVariableA(kEnvironmentVariable.c_str(), reinterpret_cast(&path[0]), static_cast(path.size())); + + // GetEnvironmentVariableA will add a null character in at the end, exclude it + auto local_app_data_path = std::string(path.begin(), path.end() - 1); + + return local_app_data_path + kDefaultUserPreferencesPath; + } + + std::string GetDefaultSalt() + { + return kDefaultSalt; + } + } +} \ No newline at end of file diff --git a/UserPreferences.Shared/Common.h b/UserPreferences.Shared/Common.h new file mode 100644 index 0000000..e5bda51 --- /dev/null +++ b/UserPreferences.Shared/Common.h @@ -0,0 +1,36 @@ +#pragma once +#pragma comment(lib, "UserPreferences.Shared.lib") + +#include +#include + +namespace UserPreferences +{ + namespace Common + { + /// + /// Reads all bytes from the specified path. + /// + /// Path to the file to read. + /// Container of sequential bytes or empty container on error. + std::vector ReadAllBytes(const std::string &path); + + /// + /// Gets the machine GUID. + /// + /// The machine GUID on success or an empty string on failure. + std::string GetPathToUserPreferencesBag(); + + /// + /// Gets the path to the user's UserPreferences.bag. + /// + /// Path to the user's UserPreferences.bag. + std::string GetMachineGuid(); + + /// + /// Gets the default salt. + /// + /// Default salt. + std::string GetDefaultSalt(); + } +}; diff --git a/UserPreferences.Shared/Encryption.cpp b/UserPreferences.Shared/Encryption.cpp new file mode 100644 index 0000000..5d77278 --- /dev/null +++ b/UserPreferences.Shared/Encryption.cpp @@ -0,0 +1,220 @@ +#include +#include + +#include "Encryption.h" + +#include +#include + +#pragma comment(lib, "libeay32") + +namespace +{ + /// + /// Decrypts ciphertext using the given key and initialization vector. + /// + /// Encrypted bytes to decrypt. + /// Key used for decryption. + /// Initialization vector used for decryption. + /// Decrypted contents of ciphertext. + std::vector Decrypt( + const std::vector &ciphertext, + const std::vector &key, + const std::vector &initialization_vector) + { + auto out_plaintext = std::vector(ciphertext.size()); + auto plaintext_length = 0; + auto cipher = EVP_aes_256_cbc(); + auto ctx = EVP_CIPHER_CTX_new(); + + EVP_CIPHER_CTX_init(ctx); + + if (!EVP_DecryptInit_ex(ctx, cipher, nullptr, &key[0], &initialization_vector[0])) + { + EVP_CIPHER_CTX_free(ctx); + throw std::exception("EVP_DecryptInit_ex - FAILED"); + } + + if (!EVP_DecryptUpdate(ctx, &out_plaintext[0], &plaintext_length, &ciphertext[0], static_cast(ciphertext.size()))) + { + EVP_CIPHER_CTX_free(ctx); + throw std::exception("EVP_DecryptUpdate - FAILED"); + } + + auto additional_length = 0; + if (!EVP_DecryptFinal_ex(ctx, &out_plaintext[plaintext_length], &additional_length)) + { + EVP_CIPHER_CTX_free(ctx); + throw std::exception("EVP_DecryptFinal_ex - FAILED"); + } + + out_plaintext.resize(plaintext_length + additional_length); + EVP_CIPHER_CTX_free(ctx); + return out_plaintext; + } + + /// + /// Encrypts plaintext into using the given key and initialization vector. + /// + /// Plaintext to encrypt. + /// Key used for encryption. + /// Initialization vector used for encryption. + /// Encrypted plaintext. + std::vector Encrypt( + const std::vector &plaintext, + const std::vector &key, + const std::vector &initialization_vector) + { + auto plaintext_length = 0; + auto cipher = EVP_aes_256_cbc(); + auto ctx = EVP_CIPHER_CTX_new(); + + auto num_blocks = plaintext.size() / cipher->block_size; + if (plaintext.size() % cipher->block_size != 0) { + ++num_blocks; + } + auto out_plaintext = std::vector(cipher->block_size * num_blocks); + + EVP_CIPHER_CTX_init(ctx); + + if (!EVP_EncryptInit_ex(ctx, cipher, nullptr, &key[0], &initialization_vector[0])) + { + EVP_CIPHER_CTX_free(ctx); + throw std::exception("EVP_EncryptInit_ex - FAILED"); + } + + if (!EVP_EncryptUpdate(ctx, &out_plaintext[0], &plaintext_length, &plaintext[0], static_cast(plaintext.size()))) + { + EVP_CIPHER_CTX_free(ctx); + throw std::exception("EVP_EncryptUpdate - FAILED"); + } + + auto additional_length = 0; + if (!EVP_EncryptFinal_ex(ctx, &out_plaintext[plaintext_length], &additional_length)) + { + EVP_CIPHER_CTX_free(ctx); + throw std::exception("EVP_EncryptFinal_ex - FAILED"); + } + + out_plaintext.resize(plaintext_length + additional_length); + EVP_CIPHER_CTX_free(ctx); + return out_plaintext; + } + + /// + /// Generates a key and initialization_vector out of the specified salt and source bytes. + /// + /// Salt bytes used to generate the key and initialization vector. + /// Source bytes used to generate the key and initialization vector. + /// [out] Generated key. + /// [out] Generated initialization vector. + /// True on success. + void GetKeys( + const std::vector &salt, + const std::vector &source_bytes, + std::vector &out_key, + std::vector &out_initialization_vector) + { + static const int kIterationCount = 5; + + auto cipher = EVP_aes_256_cbc(); + auto md = EVP_sha1(); + + out_key.resize(cipher->key_len); + out_initialization_vector.resize(cipher->iv_len); + + auto derived_key_length = EVP_BytesToKey( + cipher, + md, + &salt[0], + &source_bytes[0], + static_cast(source_bytes.size()), + kIterationCount, + &out_key[0], + &out_initialization_vector[0] + ); + + if (derived_key_length == 0) + { + throw std::exception("EVP_BytesToKey - Failed to generate key"); + } + } + + /// + /// Mangles data for use as source bytes when generating a key and initialization vector. + /// + /// Bytes to mangle. + /// Collection of mangled bytes. + std::vector MangleData(const std::vector &data_to_mangle) + { + auto mangled_data = std::vector(data_to_mangle); + const auto kMangledDataSize = mangled_data.size(); + + for (size_t index = 0; index < kMangledDataSize; ++index) + { + auto mangled_character = ((index + 2) * mangled_data[index]) % 128; + if (mangled_character != 0) + { + mangled_data[index] = static_cast(mangled_character); + } + } + + return mangled_data; + } + + /// + /// Encrypts or decrypts data + /// + /// Data to encrypt or decrypt + /// Machine GUID used to encrypt data + /// Salt used to encrypt data + /// Determines if data should be encrypted or decrypted + /// Encrypted or decrypted data + std::vector EncryptOrDecryptData( + const std::vector& data, + const std::string& machine_guid, + const std::string& salt_string, + bool is_encrypting) + { + std::vector salt_bytes; + boost::algorithm::unhex(salt_string.begin(), salt_string.end(), std::back_inserter(salt_bytes)); + + auto machine_guid_bytes = std::vector(machine_guid.begin(), machine_guid.end()); + auto mangled_data = MangleData(machine_guid_bytes); + + std::vector key; + std::vector initialization_vector; + GetKeys(salt_bytes, mangled_data, key, initialization_vector); + + if (is_encrypting) + { + return Encrypt(data, key, initialization_vector); + } + else + { + return Decrypt(data, key, initialization_vector); + } + } +} + +namespace UserPreferences +{ + namespace Encryption + { + std::vector DecryptData( + const std::vector& encrypted_data, + const std::string& machine_guid, + const std::string& salt_string) + { + return EncryptOrDecryptData(encrypted_data, machine_guid, salt_string, false); + } + + std::vector EncryptData( + const std::vector& plaintext_data, + const std::string& machine_guid, + const std::string& salt_string) + { + return EncryptOrDecryptData(plaintext_data, machine_guid, salt_string, true); + } + } +} diff --git a/UserPreferences.Shared/Encryption.h b/UserPreferences.Shared/Encryption.h new file mode 100644 index 0000000..557ef6a --- /dev/null +++ b/UserPreferences.Shared/Encryption.h @@ -0,0 +1,34 @@ +#pragma once +#pragma comment(lib, "UserPreferences.Shared.lib") + +#include +#include + +namespace UserPreferences +{ + namespace Encryption + { + /// + /// Decrypts encrypted data + /// + /// Encrypted data + /// Machine GUID used to encrypt data + /// Salt used to encrypt data + /// Decrypted data + std::vector DecryptData( + const std::vector& encrypted_data, + const std::string& machine_guid, + const std::string& salt_string); + + /// + /// Encrypts data + /// + /// Data to encrypt + /// Machine GUID used to encrypt data + /// Salt used to encrypt data + /// Decrypted data + std::vector EncryptData(const std::vector& plaintext_data, + const std::string& machine_guid, + const std::string& salt_string); + } +} diff --git a/UserPreferences.Shared/UserPreferences.Shared.vcxproj b/UserPreferences.Shared/UserPreferences.Shared.vcxproj new file mode 100644 index 0000000..178206f --- /dev/null +++ b/UserPreferences.Shared/UserPreferences.Shared.vcxproj @@ -0,0 +1,165 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C} + Win32Proj + UserPreferencesShared + 10.0.16299.0 + UserPreferences.Shared + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + + + Level3 + Disabled + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + + + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + Level3 + MaxSpeed + true + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UserPreferences.Shared/UserPreferences.Shared.vcxproj.filters b/UserPreferences.Shared/UserPreferences.Shared.vcxproj.filters new file mode 100644 index 0000000..d7a1079 --- /dev/null +++ b/UserPreferences.Shared/UserPreferences.Shared.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/UserPreferencesExplorer.sln b/UserPreferencesExplorer.sln index 3faff21..e113031 100644 --- a/UserPreferencesExplorer.sln +++ b/UserPreferencesExplorer.sln @@ -1,11 +1,24 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.16 +VisualStudioVersion = 15.0.27130.2036 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserPreferencesExplorer", "UserPreferencesExplorer\UserPreferencesExplorer.vcxproj", "{69E20B05-83B9-444C-ADF6-6E28C9B056B0}" + ProjectSection(ProjectDependencies) = postProject + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C} = {21DB7E9B-DA4E-420E-A209-A4F6C339F10C} + EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserPreferencesExplorer.Tests", "UserPreferencesExplorer.Tests\UserPreferencesExplorer.Tests.vcxproj", "{74BE1B66-A57A-44D1-9D92-05C1596BE7DA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserPreferences.Shared.Tests", "UserPreferences.Shared.Tests\UserPreferences.Shared.Tests.vcxproj", "{74BE1B66-A57A-44D1-9D92-05C1596BE7DA}" + ProjectSection(ProjectDependencies) = postProject + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C} = {21DB7E9B-DA4E-420E-A209-A4F6C339F10C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibUserPreferences", "LibUserPreferences\LibUserPreferences.vcxproj", "{E8A8F157-EBAE-43E4-90AE-32A8DAC51096}" + ProjectSection(ProjectDependencies) = postProject + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C} = {21DB7E9B-DA4E-420E-A209-A4F6C339F10C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserPreferences.Shared", "UserPreferences.Shared\UserPreferences.Shared.vcxproj", "{21DB7E9B-DA4E-420E-A209-A4F6C339F10C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -31,8 +44,27 @@ Global {74BE1B66-A57A-44D1-9D92-05C1596BE7DA}.Release|x64.Build.0 = Release|x64 {74BE1B66-A57A-44D1-9D92-05C1596BE7DA}.Release|x86.ActiveCfg = Release|Win32 {74BE1B66-A57A-44D1-9D92-05C1596BE7DA}.Release|x86.Build.0 = Release|Win32 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096}.Debug|x64.ActiveCfg = Debug|x64 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096}.Debug|x64.Build.0 = Debug|x64 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096}.Debug|x86.ActiveCfg = Debug|Win32 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096}.Debug|x86.Build.0 = Debug|Win32 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096}.Release|x64.ActiveCfg = Release|x64 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096}.Release|x64.Build.0 = Release|x64 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096}.Release|x86.ActiveCfg = Release|Win32 + {E8A8F157-EBAE-43E4-90AE-32A8DAC51096}.Release|x86.Build.0 = Release|Win32 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C}.Debug|x64.ActiveCfg = Debug|x64 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C}.Debug|x64.Build.0 = Debug|x64 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C}.Debug|x86.ActiveCfg = Debug|Win32 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C}.Debug|x86.Build.0 = Debug|Win32 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C}.Release|x64.ActiveCfg = Release|x64 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C}.Release|x64.Build.0 = Release|x64 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C}.Release|x86.ActiveCfg = Release|Win32 + {21DB7E9B-DA4E-420E-A209-A4F6C339F10C}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2AC63A31-3F08-4DCD-961A-9E3BD06DFE9F} + EndGlobalSection EndGlobal diff --git a/UserPreferencesExplorer/Encryption.cpp b/UserPreferencesExplorer/Encryption.cpp index c8cacfe..e1de94e 100644 --- a/UserPreferencesExplorer/Encryption.cpp +++ b/UserPreferencesExplorer/Encryption.cpp @@ -1,202 +1,13 @@ #include #include -#include "UserPreferences.h" -#include "Utils.h" +#include "../LibUserPreferences/LibUserPreferences.h" +#include "Encryption.h" #pragma comment(lib, "libeay32") namespace Encryption { - namespace - { - /// - /// Decrypts ciphertext using the given key and initialization vector. - /// - /// Encrypted bytes to decrypt. - /// Key used for decryption. - /// Initialization vector used for decryption. - /// Decrypted contents of ciphertext. - std::vector Decrypt( - const std::vector &ciphertext, - const std::vector &key, - const std::vector &initialization_vector) - { - auto out_plaintext = std::vector(ciphertext.size()); - auto plaintext_length = 0; - auto cipher = EVP_aes_256_cbc(); - auto ctx = EVP_CIPHER_CTX_new(); - - EVP_CIPHER_CTX_init(ctx); - - if (!EVP_DecryptInit_ex(ctx, cipher, nullptr, &key[0], &initialization_vector[0])) - { - EVP_CIPHER_CTX_free(ctx); - throw std::exception("EVP_DecryptInit_ex - FAILED"); - } - - if (!EVP_DecryptUpdate(ctx, &out_plaintext[0], &plaintext_length, &ciphertext[0], static_cast(ciphertext.size()))) - { - EVP_CIPHER_CTX_free(ctx); - throw std::exception("EVP_DecryptUpdate - FAILED"); - } - - auto additional_length = 0; - if (!EVP_DecryptFinal_ex(ctx, &out_plaintext[plaintext_length], &additional_length)) - { - EVP_CIPHER_CTX_free(ctx); - throw std::exception("EVP_DecryptFinal_ex - FAILED"); - } - - out_plaintext.resize(plaintext_length + additional_length); - EVP_CIPHER_CTX_free(ctx); - return out_plaintext; - } - - /// - /// Encrypts plaintext into using the given key and initialization vector. - /// - /// Plaintext to encrypt. - /// Key used for encryption. - /// Initialization vector used for encryption. - /// Encrypted plaintext. - std::vector Encrypt( - const std::vector &plaintext, - const std::vector &key, - const std::vector &initialization_vector) - { - auto plaintext_length = 0; - auto cipher = EVP_aes_256_cbc(); - auto ctx = EVP_CIPHER_CTX_new(); - - auto num_blocks = plaintext.size() / cipher->block_size; - if (plaintext.size() % cipher->block_size != 0) { - ++num_blocks; - } - auto out_plaintext = std::vector(cipher->block_size * num_blocks); - - EVP_CIPHER_CTX_init(ctx); - - if (!EVP_EncryptInit_ex(ctx, cipher, nullptr, &key[0], &initialization_vector[0])) - { - EVP_CIPHER_CTX_free(ctx); - throw std::exception("EVP_EncryptInit_ex - FAILED"); - } - - if (!EVP_EncryptUpdate(ctx, &out_plaintext[0], &plaintext_length, &plaintext[0], static_cast(plaintext.size()))) - { - EVP_CIPHER_CTX_free(ctx); - throw std::exception("EVP_EncryptUpdate - FAILED"); - } - - auto additional_length = 0; - if (!EVP_EncryptFinal_ex(ctx, &out_plaintext[plaintext_length], &additional_length)) - { - EVP_CIPHER_CTX_free(ctx); - throw std::exception("EVP_EncryptFinal_ex - FAILED"); - } - - out_plaintext.resize(plaintext_length + additional_length); - EVP_CIPHER_CTX_free(ctx); - return out_plaintext; - } - - /// - /// Generates a key and initialization_vector out of the specified salt and source bytes. - /// - /// Salt bytes used to generate the key and initialization vector. - /// Source bytes used to generate the key and initialization vector. - /// [out] Generated key. - /// [out] Generated initialization vector. - /// True on success. - void GetKeys( - const std::vector &salt, - const std::vector &source_bytes, - std::vector &out_key, - std::vector &out_initialization_vector) - { - static const int kIterationCount = 5; - - auto cipher = EVP_aes_256_cbc(); - auto md = EVP_sha1(); - - out_key.resize(cipher->key_len); - out_initialization_vector.resize(cipher->iv_len); - - auto derived_key_length = EVP_BytesToKey( - cipher, - md, - &salt[0], - &source_bytes[0], - static_cast(source_bytes.size()), - kIterationCount, - &out_key[0], - &out_initialization_vector[0] - ); - - if (derived_key_length == 0) - { - throw std::exception("EVP_BytesToKey - Failed to generate key"); - } - } - - /// - /// Mangles data for use as source bytes when generating a key and initialization vector. - /// - /// Bytes to mangle. - /// Collection of mangled bytes. - std::vector MangleData(const std::vector &data_to_mangle) - { - auto mangled_data = std::vector(data_to_mangle); - const auto kMangledDataSize = mangled_data.size(); - - for (size_t index = 0; index < kMangledDataSize; ++index) - { - auto mangled_character = ((index + 2) * mangled_data[index]) % 128; - if (mangled_character != 0) - { - mangled_data[index] = static_cast(mangled_character); - } - } - - return mangled_data; - } - - /// - /// Encrypts or decrypts data - /// - /// Data to encrypt or decrypt - /// Machine GUID used to encrypt data - /// Salt used to encrypt data - /// Determines if data should be encrypted or decrypted - /// Encrypted or decrypted data - std::vector EncryptOrDecryptData( - const std::vector& data, - const std::string& machine_guid, - const std::string& salt_string, - bool is_encrypting) - { - std::vector salt_bytes; - boost::algorithm::unhex(salt_string.begin(), salt_string.end(), std::back_inserter(salt_bytes)); - - auto machine_guid_bytes = std::vector(machine_guid.begin(), machine_guid.end()); - auto mangled_data = MangleData(machine_guid_bytes); - - std::vector key; - std::vector initialization_vector; - GetKeys(salt_bytes, mangled_data, key, initialization_vector); - - if (is_encrypting) - { - return Encrypt(data, key, initialization_vector); - } - else - { - return Decrypt(data, key, initialization_vector); - } - } - } - std::vector DecryptData( const std::vector& encrypted_data, const std::string& machine_guid, diff --git a/UserPreferencesExplorer/UserPreferences.h b/UserPreferencesExplorer/Encryption.h similarity index 100% rename from UserPreferencesExplorer/UserPreferences.h rename to UserPreferencesExplorer/Encryption.h diff --git a/UserPreferencesExplorer/Main.cpp b/UserPreferencesExplorer/Main.cpp index 4508d83..34f9064 100644 --- a/UserPreferencesExplorer/Main.cpp +++ b/UserPreferencesExplorer/Main.cpp @@ -2,19 +2,18 @@ #include #include -#include "UserPreferences.h" -#include "Utils.h" +#include "../UserPreferences.Shared/Common.h" +#include "../UserPreferences.Shared/Encryption.h" int main(int argc, char* argv[]) { namespace po = boost::program_options; - static const auto kSalt = std::string("6E3F032949637D2E"); try { - auto arg_salt = po::value()->default_value(kSalt, ""); - auto arg_guid = po::value()->default_value(Utils::GetMachineGuid(), ""); - auto arg_path = po::value()->default_value(Utils::GetPathToUserPreferencesBag(), ""); + auto arg_salt = po::value()->default_value(UserPreferences::Common::GetDefaultSalt(), ""); + auto arg_guid = po::value()->default_value(UserPreferences::Common::GetMachineGuid(), ""); + auto arg_path = po::value()->default_value(UserPreferences::Common::GetPathToUserPreferencesBag(), ""); auto commandline_descriptions = po::options_description("Arguments"); commandline_descriptions.add_options() @@ -38,8 +37,8 @@ int main(int argc, char* argv[]) auto guid = vm["guid"].as(); auto salt = vm["salt"].as(); - auto ciphertext = Utils::ReadAllBytes(vm["path"].as()); - auto plaintext = Encryption::DecryptData(ciphertext, guid, salt); + auto ciphertext = UserPreferences::Common::ReadAllBytes(vm["path"].as()); + auto plaintext = UserPreferences::Encryption::DecryptData(ciphertext, guid, salt); auto plaintext_string = std::string(plaintext.begin(), plaintext.end()); std::cout << plaintext_string << std::endl; diff --git a/UserPreferencesExplorer/UserPreferencesExplorer.vcxproj b/UserPreferencesExplorer/UserPreferencesExplorer.vcxproj index 8855729..28ac882 100644 --- a/UserPreferencesExplorer/UserPreferencesExplorer.vcxproj +++ b/UserPreferencesExplorer/UserPreferencesExplorer.vcxproj @@ -92,6 +92,7 @@ Console + $(OutputPath);%(AdditionalLibraryDirectories) @@ -104,6 +105,7 @@ Console + $(OutputPath);%(AdditionalLibraryDirectories) @@ -120,6 +122,7 @@ Console true true + $(OutputPath);%(AdditionalLibraryDirectories) @@ -136,16 +139,11 @@ Console true true + $(OutputPath);%(AdditionalLibraryDirectories) - - - - - - diff --git a/UserPreferencesExplorer/UserPreferencesExplorer.vcxproj.filters b/UserPreferencesExplorer/UserPreferencesExplorer.vcxproj.filters index df0912f..948faed 100644 --- a/UserPreferencesExplorer/UserPreferencesExplorer.vcxproj.filters +++ b/UserPreferencesExplorer/UserPreferencesExplorer.vcxproj.filters @@ -18,19 +18,5 @@ Source Files - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - \ No newline at end of file diff --git a/UserPreferencesExplorer/Utils.cpp b/UserPreferencesExplorer/Utils.cpp deleted file mode 100644 index 172a7a1..0000000 --- a/UserPreferencesExplorer/Utils.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include - -#include "Utils.h" - -namespace Utils -{ - std::vector ReadAllBytes(const std::string &path) - { - std::ifstream inStream(path, std::ios::binary | std::ios::ate); - auto fileSize = static_cast(inStream.tellg()); - if (fileSize == 0) - { - throw std::exception("File is empty"); - } - - auto fileBytes = std::vector(fileSize); - - inStream.seekg(0, std::ios::beg); - inStream.read(reinterpret_cast(&fileBytes[0]), fileSize); - inStream.close(); - - return fileBytes; - } - - std::string GetMachineGuid() - { - static const auto kMachineGuidPath = std::string("SOFTWARE\\Microsoft\\Cryptography"); - static const auto kMachineGuidName = std::string("MachineGuid"); - - HKEY key_handle; - - auto result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, kMachineGuidPath.c_str(), NULL, KEY_READ | KEY_WOW64_64KEY, &key_handle); - if (result != ERROR_SUCCESS) - { - RegCloseKey(key_handle); - throw std::exception("RegOpenKeyEx - FAILED"); - } - - DWORD buff_size = 0; - result = RegQueryValueExA(key_handle, kMachineGuidName.c_str(), nullptr, nullptr, nullptr, &buff_size); - if (result != ERROR_SUCCESS) - { - RegCloseKey(key_handle); - throw std::exception("RegQueryValueEx - FAILED"); - } - - auto machine_guid = std::vector(buff_size); - result = RegQueryValueExA(key_handle, kMachineGuidName.c_str(), nullptr, nullptr, &machine_guid[0], &buff_size); - if (result != ERROR_SUCCESS) - { - RegCloseKey(key_handle); - throw std::exception("RegQueryValueEx - FAILED"); - } - - RegCloseKey(key_handle); - - return std::string(machine_guid.begin(), machine_guid.end() - 1); - } - - std::string GetPathToUserPreferencesBag() - { - static const auto kDefaultUserPreferencesPath = std::string("\\LindenLab\\SansarClient\\UserPreferences.bag"); - static const auto kEnvironmentVariable = std::string("localappdata"); - - // Resize our container to hold the environment variable - std::vector path(1); - auto required_path_length = GetEnvironmentVariableA(kEnvironmentVariable.c_str(), reinterpret_cast(&path[0]), 0); - path.resize(required_path_length); - - // Get the actual environment variable - GetEnvironmentVariableA(kEnvironmentVariable.c_str(), reinterpret_cast(&path[0]), static_cast(path.size())); - - // GetEnvironmentVariableA will add a null character in at the end, exclude it - auto local_app_data_path = std::string(path.begin(), path.end() - 1); - - return local_app_data_path + kDefaultUserPreferencesPath; - } -} diff --git a/UserPreferencesExplorer/Utils.h b/UserPreferencesExplorer/Utils.h deleted file mode 100644 index e76d8a8..0000000 --- a/UserPreferencesExplorer/Utils.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -namespace Utils -{ - /// - /// Reads all bytes from the specified path. - /// - /// Path to the file to read. - /// Container of sequential bytes or empty container on error. - std::vector ReadAllBytes(const std::string &path); - - /// - /// Gets the machine GUID. - /// - /// The machine GUID on success or an empty string on failure. - std::string GetPathToUserPreferencesBag(); - - /// - /// Gets the path to the user's UserPreferences.bag. - /// - /// Path to the user's UserPreferences.bag. - std::string GetMachineGuid(); -};