Use MinHook on cpp side too (#858)

Co-authored-by: goaaats <goatsdev@protonmail.com>
This commit is contained in:
kizer 2022-05-30 00:33:57 +09:00 committed by GitHub
parent a7540d0fef
commit dbb0cb3d56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 165 additions and 64 deletions

5
.gitmodules vendored
View file

@ -6,4 +6,7 @@
url = https://github.com/goatcorp/FFXIVClientStructs.git
[submodule "lib/Nomade040-nmd"]
path = lib/Nomade040-nmd
url = https://github.com/Nomade040/nmd
url = https://github.com/Nomade040/nmd.git
[submodule "lib/TsudaKageyu-minhook"]
path = lib/TsudaKageyu-minhook
url = https://github.com/TsudaKageyu/minhook.git

View file

@ -59,6 +59,7 @@
<IntrinsicFunctions>false</IntrinsicFunctions>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26812</DisableSpecificWarnings>
</ClCompile>
<Link>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
@ -71,6 +72,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26812</DisableSpecificWarnings>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
@ -127,6 +129,11 @@
<ClInclude Include="veh.h" />
<ClInclude Include="xivfixes.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\lib\TsudaKageyu-minhook\build\VC16\libMinHook.vcxproj">
<Project>{f142a341-5ee0-442d-a15f-98ae9b48dbae}</Project>
</ProjectReference>
</ItemGroup>
<Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent">
<Delete Files="$(OutDir)$(TargetName).lib" />
<Delete Files="$(OutDir)$(TargetName).exp" />

View file

@ -24,12 +24,7 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue) {
if (bootconfig::is_wait_messagebox())
MessageBoxW(nullptr, L"Press OK to continue", L"Dalamud Boot", MB_OK);
try {
xivfixes::apply_all(true);
} catch (const std::exception& e) {
logging::print<logging::W>("Failed to do general fixups. Some things might not work.");
logging::print<logging::W>("Error: {}", e.what());
}
xivfixes::apply_all(true);
if (bootconfig::is_wait_debugger()) {
logging::print<logging::I>("Waiting for debugger to attach...");
@ -89,9 +84,24 @@ BOOL APIENTRY DllMain(const HMODULE hModule, const DWORD dwReason, LPVOID lpRese
switch (dwReason) {
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
if (const auto mhStatus = MH_Initialize(); MH_OK != mhStatus) {
logging::print<logging::E>("Failed to initialize MinHook (status={})", static_cast<int>(mhStatus));
return FALSE;
}
logging::update_dll_load_status(true);
break;
case DLL_PROCESS_DETACH:
logging::update_dll_load_status(false);
xivfixes::apply_all(false);
if (const auto mhStatus = MH_Uninitialize(); MH_OK != mhStatus) {
logging::print<logging::E>("Failed to uninitialize MinHook (status={})", static_cast<int>(mhStatus));
__fastfail(logging::MinHookUnload);
}
veh::remove_handler();
//logging::log_file.close();
break;

View file

@ -71,45 +71,31 @@ namespace hooks {
}
};
template<typename TFn>
class export_hook : public base_hook<TFn> {
template<typename>
class direct_hook;
template<typename TReturn, typename ... TArgs>
class direct_hook<TReturn(TArgs...)> : public base_hook<TReturn(TArgs...)> {
using TFn = TReturn(TArgs...);
using Base = base_hook<TFn>;
static constexpr uint8_t DetouringThunkTemplate[12]{
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs rax, 0x0000000000000000
0xFF, 0xE0, // jmp rax
};
TFn* const m_pfnExportThunk;
uint8_t s_originalThunk[sizeof DetouringThunkTemplate]{};
TFn* m_pfnMinHookBridge;
public:
export_hook(TFn* pfnExportThunk)
: Base(reinterpret_cast<TFn*>(utils::resolve_unconditional_jump_target(pfnExportThunk)))
, m_pfnExportThunk(pfnExportThunk) {
auto pExportThunk = reinterpret_cast<uint8_t*>(pfnExportThunk);
direct_hook(TFn* pfnFunction)
: Base(pfnFunction) {
if (const auto mhStatus = MH_CreateHook(pfnFunction, Base::get_thunk(), reinterpret_cast<void**>(&m_pfnMinHookBridge)); mhStatus != MH_OK)
throw std::runtime_error(std::format("MH_CreateHook(0x{:X}, ...) failure: {}", reinterpret_cast<size_t>(pfnFunction), static_cast<int>(mhStatus)));
// Make it writeable.
const utils::memory_tenderizer tenderizer(pfnExportThunk, sizeof DetouringThunkTemplate, PAGE_EXECUTE_READWRITE);
// Back up original thunk bytes.
memcpy(s_originalThunk, pExportThunk, sizeof s_originalThunk);
// Write thunk template.
memcpy(pExportThunk, DetouringThunkTemplate, sizeof DetouringThunkTemplate);
// Write target address.
*reinterpret_cast<TFn**>(&pExportThunk[2]) = Base::get_thunk();
MH_EnableHook(Base::get_original());
}
~export_hook() override {
const utils::memory_tenderizer tenderizer(m_pfnExportThunk, sizeof DetouringThunkTemplate, PAGE_EXECUTE_READWRITE);
~direct_hook() override {
MH_DisableHook(Base::get_original());
}
// Restore original thunk bytes.
memcpy(m_pfnExportThunk, s_originalThunk, sizeof s_originalThunk);
// Clear state.
memset(s_originalThunk, 0, sizeof s_originalThunk);
TReturn call_original(TArgs... args) override {
return m_pfnMinHookBridge(std::forward<TArgs>(args)...);
}
};

View file

@ -1,6 +1,8 @@
#include "pch.h"
#include "logging.h"
static bool s_bLoaded = false;
void logging::print(Level level, const char* s) {
SYSTEMTIME st;
GetLocalTime(&st);
@ -30,12 +32,21 @@ void logging::print(Level level, const char* s) {
break;
}
DWORD wr;
WriteFile(GetStdHandle(STD_ERROR_HANDLE), &estr[0], static_cast<DWORD>(estr.size()), &wr, nullptr);
OutputDebugStringW(unicode::convert<std::wstring>(estr).c_str());
if (log_file.is_open())
{
log_file << estr;
log_file.flush();
// Handle accesses should not be done during DllMain process attach/detach calls
if (s_bLoaded) {
DWORD wr{};
WriteFile(GetStdHandle(STD_ERROR_HANDLE), &estr[0], static_cast<DWORD>(estr.size()), &wr, nullptr);
if (log_file.is_open())
{
log_file << estr;
log_file.flush();
}
}
}
void logging::update_dll_load_status(bool loaded) {
s_bLoaded = loaded;
}

View file

@ -25,6 +25,11 @@ namespace logging {
F = 5,
};
enum FastFailErrorCode : int {
Unspecified = 12345,
MinHookUnload,
};
void print(Level level, const char* s);
inline void print(Level level, const wchar_t* s) {
@ -59,4 +64,6 @@ namespace logging {
inline void print(const T* pcszFormat, Arg arg1, Args...args) {
print(level, std::format(pcszFormat, std::forward<Arg>(arg1), std::forward<Args>(args)...));
}
void update_dll_load_status(bool loaded);
};

View file

@ -40,6 +40,9 @@
// https://www.akenotsuki.com/misc/srell/en/
#include "../lib/srell3_009/single-header/srell.hpp"
// https://github.com/TsudaKageyu/minhook
#include "../lib/TsudaKageyu-minhook/include/MinHook.h"
// https://github.com/Nomade040/nmd
#include "../lib/Nomade040-nmd/nmd_assembly.h"

View file

@ -229,17 +229,6 @@ std::shared_ptr<void> utils::allocate_executable_heap(size_t len) {
};
}
void* utils::resolve_unconditional_jump_target(void* pfn) {
const auto bytes = reinterpret_cast<uint8_t*>(pfn);
// JMP QWORD PTR [RIP + int32]
// 48 FF 25 ?? ?? ?? ??
if (bytes[0] == 0x48 && bytes[1] == 0xFF && bytes[2] == 0x25)
return *reinterpret_cast<void**>(&bytes[7 + *reinterpret_cast<int*>(&bytes[3])]);
throw std::runtime_error("Unexpected thunk bytes.");
}
template<typename TEntryType>
static bool find_imported_function_pointer_helper(const char* pcBaseAddress, const IMAGE_IMPORT_DESCRIPTOR& desc, const IMAGE_DATA_DIRECTORY& dir, std::string_view reqFunc, uint32_t hintOrOrdinal, void*& ppFunctionAddress) {
const auto importLookupsOversizedSpan = std::span(reinterpret_cast<const TEntryType*>(&pcBaseAddress[desc.OriginalFirstThunk]), (dir.Size - desc.OriginalFirstThunk) / sizeof TEntryType);

View file

@ -57,8 +57,6 @@ namespace utils {
~memory_tenderizer();
};
void* resolve_unconditional_jump_target(void* pfn);
bool find_imported_function_pointer(HMODULE hModule, const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal, void*& ppFunctionAddress);
void* get_imported_function_pointer(HMODULE hModule, const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal);

View file

@ -108,11 +108,11 @@ void xivfixes::disable_game_openprocess_access_check(bool bApply) {
void xivfixes::redirect_openprocess(bool bApply) {
static const char* LogTag = "[xivfixes:redirect_openprocess]";
static std::optional<hooks::export_hook<decltype(OpenProcess)>> hook;
static std::optional<hooks::direct_hook<decltype(OpenProcess)>> hook;
if (bApply) {
logging::print<logging::I>("{} Enable", LogTag);
hook.emplace(::OpenProcess);
hook.emplace(OpenProcess);
hook->set_detour([](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE {
if (dwProcessId == GetCurrentProcessId()) {
logging::print<logging::I>("{} OpenProcess(0{:08X}, {}, {}) was invoked by thread {}. Redirecting to DuplicateHandle.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId());
@ -130,3 +130,30 @@ void xivfixes::redirect_openprocess(bool bApply) {
hook.reset();
}
}
void xivfixes::apply_all(bool bApply) {
for (const auto& [taskName, taskFunction] : std::initializer_list<std::pair<const char*, void(*)(bool)>>
{
{ "prevent_devicechange_crashes", &prevent_devicechange_crashes },
{ "disable_game_openprocess_access_check", &disable_game_openprocess_access_check },
{ "redirect_openprocess", &redirect_openprocess },
}
) {
try {
taskFunction(bApply);
} catch (const std::exception& e) {
if (bApply)
logging::print<logging::W>("Error trying to activate fixup [{}]: {}", taskName, e.what());
else
logging::print<logging::W>("Error trying to deactivate fixup [{}]: {}", taskName, e.what());
continue;
}
if (bApply)
logging::print<logging::I>("Fixup [{}] activated.", taskName);
else
logging::print<logging::I>("Fixup [{}] deactivated.", taskName);
}
}

View file

@ -5,9 +5,5 @@ namespace xivfixes {
void disable_game_openprocess_access_check(bool bApply);
void redirect_openprocess(bool bApply);
inline void apply_all(bool bApply) {
prevent_devicechange_crashes(bApply);
disable_game_openprocess_access_check(bApply);
redirect_openprocess(bApply);
}
void apply_all(bool bApply);
}

View file

@ -34,110 +34,172 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs", "lib\F
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs.Generators", "lib\FFXIVClientStructs\FFXIVClientStructs.Generators\FFXIVClientStructs.Generators.csproj", "{05AB2F46-268B-4915-806F-DDF813E2D59D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libMinHook", "lib\TsudaKageyu-minhook\build\VC16\libMinHook.vcxproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|x64.ActiveCfg = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|x64.Build.0 = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|x86.ActiveCfg = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|Any CPU.Build.0 = Release|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|x64.ActiveCfg = Release|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|x64.Build.0 = Release|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|x86.ActiveCfg = Release|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.ActiveCfg = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.Build.0 = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x64.ActiveCfg = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x64.Build.0 = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x86.ActiveCfg = Debug|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x86.Build.0 = Debug|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.ActiveCfg = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.Build.0 = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x64.ActiveCfg = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x64.Build.0 = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x86.ActiveCfg = Release|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x86.Build.0 = Release|Any CPU
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|Any CPU.ActiveCfg = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|Any CPU.Build.0 = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x64.ActiveCfg = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x64.Build.0 = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x86.ActiveCfg = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x86.Build.0 = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|Any CPU.ActiveCfg = Release|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|Any CPU.Build.0 = Release|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|x64.ActiveCfg = Release|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|x64.Build.0 = Release|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|x86.ActiveCfg = Release|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|x86.Build.0 = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.ActiveCfg = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.Build.0 = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x64.ActiveCfg = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x64.Build.0 = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x86.ActiveCfg = Debug|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x86.Build.0 = Debug|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.ActiveCfg = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.Build.0 = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x64.ActiveCfg = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x64.Build.0 = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x86.ActiveCfg = Release|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x86.Build.0 = Release|Any CPU
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|Any CPU.ActiveCfg = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|Any CPU.Build.0 = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x64.ActiveCfg = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x64.Build.0 = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x86.ActiveCfg = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x86.Build.0 = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|Any CPU.ActiveCfg = Release|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|Any CPU.Build.0 = Release|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x64.ActiveCfg = Release|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x64.Build.0 = Release|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x86.ActiveCfg = Release|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x86.Build.0 = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.ActiveCfg = Debug|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.ActiveCfg = Debug|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.Build.0 = Debug|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x86.ActiveCfg = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x86.Build.0 = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.Build.0 = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.ActiveCfg = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.Build.0 = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x86.ActiveCfg = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x86.Build.0 = Release|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.ActiveCfg = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x86.ActiveCfg = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x86.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.Build.0 = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x86.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x86.Build.0 = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x86.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x86.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.Build.0 = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x86.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x86.Build.0 = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.ActiveCfg = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.Build.0 = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x64.ActiveCfg = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x64.Build.0 = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x86.ActiveCfg = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x86.Build.0 = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.ActiveCfg = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.Build.0 = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x64.ActiveCfg = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x64.Build.0 = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x86.ActiveCfg = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x86.Build.0 = Release|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.ActiveCfg = Debug|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.Build.0 = Debug|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x64.ActiveCfg = Debug|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x64.Build.0 = Debug|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x86.ActiveCfg = Debug|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x86.Build.0 = Debug|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.ActiveCfg = Release|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.Build.0 = Release|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x64.ActiveCfg = Release|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x64.Build.0 = Release|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x86.ActiveCfg = Release|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x86.Build.0 = Release|x64
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x64.ActiveCfg = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x64.Build.0 = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x86.ActiveCfg = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x86.Build.0 = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|Any CPU.Build.0 = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x64.ActiveCfg = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x64.Build.0 = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x86.ActiveCfg = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x86.Build.0 = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x64.ActiveCfg = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x64.Build.0 = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x86.ActiveCfg = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x86.Build.0 = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|Any CPU.Build.0 = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x64.ActiveCfg = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x64.Build.0 = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x86.ActiveCfg = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x86.Build.0 = Release|Any CPU
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Any CPU.ActiveCfg = Debug|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Any CPU.Build.0 = Debug|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x86.ActiveCfg = Debug|Win32
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x86.Build.0 = Debug|Win32
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Any CPU.ActiveCfg = Release|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Any CPU.Build.0 = Release|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x86.ActiveCfg = Release|Win32
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -148,6 +210,7 @@ Global
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{05AB2F46-268B-4915-806F-DDF813E2D59D} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{F142A341-5EE0-442D-A15F-98AE9B48DBAE} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599}

@ -0,0 +1 @@
Subproject commit 4a455528f61b5a375b1f9d44e7d296d47f18bb18