Update Notes

Clean up DLL-Injector code
Fix Crash in DX8 EndScene Hook
Update Hook.c to change whole page protection instead of first few bytes
Add Hashtable parser and dumper
Add keyboard shortcut to toggle DX8 overlay
Add VMT Hooking (currently not working)
Add Cleanup function that gets called on DLL-Unload
This commit is contained in:
Daniel S. 2019-03-04 15:11:47 +01:00
parent 3976fdea37
commit 3a9ab54240
12 changed files with 275 additions and 100 deletions

View File

@ -65,7 +65,7 @@ Points to GameState struct
## Entity Hash Table
Hashfunction used: [strhash](http://www.cs.ecu.edu/karl/3300/spr16/Notes/DataStructure/hashtable.html)
Hashfunction used: [PJW](https://en.wikipedia.org/wiki/PJW_hash_function) (Same parameters as the example implementation)
Entry format:
@ -81,6 +81,7 @@ Data format:
| ------ | ----------- | -------------------- |
| 0x0 | void** | Virtual Method Table |
| 0x4 | const char* | name as string |
| 0x14 | void* | pointer to self |
| 0x28 | float[3] | Position |
# File Formats

View File

@ -156,7 +156,7 @@ bool Injected(DWORD PID)
return HasModule(PID, DLL_NAME);
}
void InjectDll(DWORD PID, bool do_resume = false)
void InjectDll(DWORD PID)
{
HANDLE hRemThread, hProc;
const char *dll_name = DLL_NAME;
@ -175,42 +175,34 @@ void InjectDll(DWORD PID, bool do_resume = false)
if (HasModule(PID, dll_name))
{
cout << "[*] DLL already Loaded" << endl;
}
else
CloseHandle(hProc);
return;
};
if (!fexists(dll_full_path))
{
if (!fexists(dll_full_path))
{
cout << "[!] DLL file not found!" << endl;
return;
}
HINSTANCE hK32 = LoadLibraryA("kernel32");
cout << "[*] Getting Address of LoadLibrary" << endl;
LPVOID LoadLibrary_Address = (LPVOID)GetProcAddress(hK32, "LoadLibraryA");
FreeLibrary(hK32);
cout << "[+] LoadLibrary is at " << LoadLibrary_Address << endl;
cout << "[*] Allocating " << strlen(dll_full_path) << " Bytes of Memory" << endl;
LPVOID mem = VirtualAllocEx(hProc, NULL, strlen(dll_full_path), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (mem == NULL)
{
cout << "[!] Could not Allocate Memory: " << GetLastErrorAsString() << endl;
return;
}
cout << "[*] Writing DLL Name to Process Memory at " << mem << endl;
WriteProcessMemory(hProc, mem, dll_full_path, strlen(dll_full_path), 0);
cout << "[*] Creating Thread to Load DLL" << endl;
if (do_resume)
{
hRemThread = CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibrary_Address, mem, CREATE_SUSPENDED, 0);
ResumeThread(hRemThread);
}
else
{
hRemThread = CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibrary_Address, mem, 0, 0);
}
cout << "[*] Waiting for DLL to load" << endl;
WaitForSingleObject(hRemThread, INFINITE);
CloseHandle(hRemThread);
cout << "[!] DLL file not found!" << endl;
CloseHandle(hProc);
return;
}
HINSTANCE hK32 = LoadLibraryA("kernel32");
cout << "[*] Getting Address of LoadLibrary" << endl;
LPVOID LoadLibrary_Address = (LPVOID)GetProcAddress(hK32, "LoadLibraryA");
FreeLibrary(hK32);
cout << "[+] LoadLibrary is at " << LoadLibrary_Address << endl;
cout << "[*] Allocating " << strlen(dll_full_path) << " Bytes of Memory" << endl;
LPVOID mem = VirtualAllocEx(hProc, NULL, strlen(dll_full_path), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (mem == NULL)
{
cout << "[!] Could not Allocate Memory: " << GetLastErrorAsString() << endl;
return;
}
cout << "[*] Writing DLL Name to Process Memory at " << mem << endl;
WriteProcessMemory(hProc, mem, dll_full_path, strlen(dll_full_path), 0);
cout << "[*] Creating Thread to Load DLL" << endl;
hRemThread = CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibrary_Address, mem, 0, 0);
cout << "[*] Waiting for DLL to load" << endl;
WaitForSingleObject(hRemThread, INFINITE);
CloseHandle(hRemThread);
cout << "[*] Closing Process Handle" << endl;
CloseHandle(hProc);
return;

View File

@ -5,72 +5,89 @@ uintmax_t frame = 0;
DWORD* GetVTable(void* addr) {
return (DWORD*)(*(DWORD*)addr);
}
bool overlay = false;
LPD3DXFONT m_pFont;
HFONT hFont;
HBRUSH hBrush;
D3DCOLOR color = D3DCOLOR_XRGB(255, 0, 0);
RECT Rect = { 0,0,0,0 };
D3DRECT panel;
void Render(LPDIRECT3DDEVICE8 dev) {
char text[MAX_PATH];
LPD3DXFONT m_pFont;
HFONT hFont;
RECT Rect={0,0,0,0};
D3DCOLOR color = D3DCOLOR_XRGB(255, 0, 0);
hFont = CreateFont(50, 0, 0, 0, FW_BOLD, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, "Verdana");
D3DXCreateFont(dev, hFont, &m_pFont);
snprintf(text, MAX_PATH, "Frame: %d", ++frame);
if (m_pFont) {
m_pFont->Begin();
m_pFont->DrawTextA(text, -1, &Rect, DT_CALCRECT, 0);
m_pFont->DrawTextA(text, -1, &Rect, DT_LEFT, color);
m_pFont->End();
m_pFont->Release();
m_pFont = nullptr;
if (!overlay) {
return;
}
char text[MAX_PATH];
snprintf(text, MAX_PATH, "Frame: [%lld]\nTest", ++frame);
if (m_pFont == nullptr) {
D3DXCreateFont(dev, hFont, &m_pFont);
CloseHandle(hFont);
}
m_pFont->Begin();
m_pFont->DrawTextA(text, -1, &Rect, DT_CALCRECT, 0);
dev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(25, 0, 0, 0),0.5,0);
m_pFont->DrawTextA(text, -1, &Rect, DT_LEFT, color);
m_pFont->End();
}
HRESULT WINAPI H_EndScene(LPDIRECT3DDEVICE8 dev) {
typedef HRESULT(WINAPI *t_func)(LPDIRECT3DDEVICE8);
shared_ptr<Hook> hook = Hook::get(H_EndScene);
t_func func = reinterpret_cast<t_func>(hook->func());
_asm push esi;
_asm pushad;
Render(dev);
hook->disable();
HRESULT ret = func(dev);
HRESULT ret = hook->func<t_func>()(dev);
hook->enable();
_asm popad;
_asm pop esi;
return ret;
}
HRESULT WINAPI H_CreateDevice(void* pDirect3D, unsigned int uiAdapter, D3DDEVTYPE pDeviceType, HWND hFocusWindow,
unsigned long ulBehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters,
LPDIRECT3DDEVICE8* ppReturnedDeviceInterface) {
_asm push esi;
_asm pushad;
typedef HRESULT(WINAPI *t_func)(void*, unsigned int, D3DDEVTYPE, HWND, unsigned long, D3DPRESENT_PARAMETERS*, LPDIRECT3DDEVICE8*);
shared_ptr<Hook> hook = Hook::get(H_CreateDevice);
t_func func = reinterpret_cast<t_func>(hook->func());
hook->disable();
HRESULT ret = func(pDirect3D, uiAdapter, pDeviceType, hFocusWindow, ulBehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
cout << "Ret:" << ret << endl;
DWORD *vtable = GetVTable(ppReturnedDeviceInterface[0]);
cout << "Dev VTable @ " << vtable << endl;
cout << "Dev VTable[35]: " << (void*)(vtable[35]) << endl; // EndScene
Hook::addr((void*)vtable[35], H_EndScene);
HRESULT ret = hook->func<t_func>()(pDirect3D, uiAdapter, pDeviceType, hFocusWindow, ulBehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
cout << "CreateDevice ->" << ret << endl;
void* EndScene = reinterpret_cast<void*>(GetVTable(ppReturnedDeviceInterface[0])[35]);
cout << "EndScene @ " << EndScene << endl; // EndScene
Hook::addr(EndScene, H_EndScene);
Hook::drop(H_CreateDevice);
_asm popad;
_asm pop esi;
return ret;
}
LPDIRECT3D8 WINAPI H_Direct3DCreate8(unsigned int SDKVersion) {
typedef LPDIRECT3D8(_stdcall *t_func)(unsigned int);
shared_ptr<Hook> hook = Hook::get(H_Direct3DCreate8);
t_func func = reinterpret_cast<t_func>(hook->func());
_asm push esi;
_asm pushad;
hook->disable();
D3DPRESENT_PARAMETERS D3D_Present_Param = { 0,0,D3DFMT_UNKNOWN,0,D3DMULTISAMPLE_NONE,D3DSWAPEFFECT_DISCARD,0,1,0,D3DFMT_UNKNOWN,0,0,0 };
LPDIRECT3D8 ret = func(SDKVersion);
LPDIRECT3D8 ret = hook->func<t_func>()(SDKVersion);
cout << "D3D8-Create: " << SDKVersion << " -> " << ret << endl;
DWORD *vtable = GetVTable(ret);
cout << "ID3D8 VTable @ " << vtable << endl;
cout << "ID3D8 VTable[15]: " << (void*)(vtable[15]) << endl; // CreateDevice
Hook::addr((void*)vtable[15], reinterpret_cast<void*>(H_CreateDevice));
void *CreateDevice = reinterpret_cast<void*>(GetVTable(ret)[15]);
cout << "CreateDevice @ " << CreateDevice << endl; // CreateDevice
Hook::addr(CreateDevice, H_CreateDevice);
Hook::drop(H_Direct3DCreate8);
_asm popad;
_asm pop esi;
return ret;
}
void hook_d3d8() {
hFont = CreateFont(20, 0, 0, 0, FW_BOLD, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, "Verdana");
hBrush = CreateSolidBrush(D3DCOLOR_ARGB(25, 0, 0, 0));
Hook::module("d3d8.dll","Direct3DCreate8", H_Direct3DCreate8);
}

View File

@ -1,19 +1,18 @@
#pragma once
#include <functional>
class Hook;
class Hook
{
private:
DWORD protect;
MEMORY_BASIC_INFORMATION mbi;
void* orig;
void* detour;
bool enabled;
uint8_t orig_bytes[6];
uint8_t jmp_bytes[6];
static map<uintptr_t, shared_ptr<Hook>> hooks;
public:
Hook(void* func, void* detour) {
uintptr_t dest = reinterpret_cast<uintptr_t>(detour);
uintptr_t src = reinterpret_cast<uintptr_t>(func);
@ -25,16 +24,18 @@ public:
this->jmp_bytes[3] = (dest >> 16) & 0xff;
this->jmp_bytes[4] = (dest >> 24) & 0xff;
this->jmp_bytes[5] = 0xC3; // ret
VirtualProtect(func, 16, PAGE_EXECUTE_READWRITE, &(this->protect));
VirtualQuery(func, &mbi, sizeof(mbi));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
memcpy(this->orig_bytes, this->orig, 1 + 4 + 1);
VirtualProtect(func, 16, this->protect, NULL);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
this->enabled = false;
}
~Hook() {
cout << "Unhooking: [" << this->orig << " <- " << this->detour << "]" << endl;
this->disable();
}
static void addr(void* addr, void* detour) {
cout << "Hooking: [" << addr << " -> " << detour <<"]" << endl;
uintptr_t key = reinterpret_cast<uintptr_t>(detour);
@ -72,31 +73,35 @@ public:
}
static void clear() {
cout << "Clearing Hooks" << endl;
for (pair<uintptr_t,shared_ptr<Hook>> h : hooks) {
h.second->disable();
}
return hooks.clear();
}
void disable() {
if (enabled) {
//cout << "Disabling: [" << this->orig << " <- " << this->detour << "]" << endl;
VirtualProtect(this->orig, 16, PAGE_EXECUTE_READWRITE, NULL);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, NULL);
memcpy(this->orig, this->orig_bytes, 1 + 4 + 1);
VirtualProtect(this->orig, 16, this->protect, NULL);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
enabled = false;
}
}
void enable() {
if (!enabled) {
//cout << "Enabling: [" << this->orig << " -> " << this->detour << "]" << endl;
VirtualProtect(this->orig, 16, PAGE_EXECUTE_READWRITE, NULL);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, NULL);
memcpy(this->orig, this->jmp_bytes, 1 + 4 + 1);
VirtualProtect(this->orig, 16, this->protect, NULL);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
enabled = true;
}
}
void* func() {
return this->orig;
template<typename T>
T func() {
return reinterpret_cast<T>(this->orig);
}
};

View File

@ -22,6 +22,7 @@ using namespace std;
#include "Structures.h"
#include "Py_Utils.h"
#include "Hook.h"
#include "VMT_Hook.h"
#include "D3D8_Hook.h"
HMODULE hD3D8Dll = 0;
@ -30,29 +31,58 @@ bool initialized = false;
bool running = true;
HMODULE mod = 0;
void dump_ht(HashTable* ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry* ent = ht->chains[i];
if (ent != NULL) {
cout << i << ": ";
while (ent != NULL) {
++cnt;
cout << ent->name;
if (ent->next) {
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entities" << endl;
return;
}
void MainLoop(HMODULE mod)
{
Sleep(100);
cout << "[*] Starting main Loop" << endl;
cout << endl;
cout << "[F3 ] Unload ScrapHacks" << endl;
cout << "[F5 ] Show Overlay" << endl;
cout << "[F6 ] Show Alarm status" << endl;
cout << "[F7 ] Set Money to 0x7fffffff" << endl;
cout << "[F8 ] Dump python modules" << endl;
cout << "[F9 ] Dump Entity hashtable" << endl;
cout << "[F10] Enable python tracing" << endl;
cout << "[ F ] \"Handbrake\" (*Will* crash the game after some time!)" << endl;
while (running)
{
Sleep(100);
if (key_down_norepeat(VK_F10))
{
scrap_exec("dbg.settrace()");
}
while (key_down('F'))
{
scrap_exec("dbg.brake()");
}
if (key_down_norepeat(VK_F3))
{
break;
}
if (key_down_norepeat(VK_F5))
{
overlay = !overlay;
}
if (key_down_norepeat(VK_F6))
{
@ -75,17 +105,16 @@ void MainLoop(HMODULE mod)
}
}
}
if (key_down_norepeat(VK_F3))
if (key_down_norepeat(VK_F9)) {
HashTable *ht = ptr<HashTable>(P_WORLD,O_HASHTABLE);
dump_ht(ht);
}
if (key_down_norepeat(VK_F10))
{
break;
scrap_exec("dbg.settrace()");
}
}
SetConsoleCtrlHandler(NULL, false);
Hook::clear();
scrap_log(0xff0000, "ScrapHacks unloaded!\n");
cout << "[+] ScrapHacks unloaded, you can now close the console!" << endl;
FreeConsole();
PostQuitMessage(0);
FreeLibraryAndExitThread(mod, 0);
}
@ -107,13 +136,12 @@ void handle_command(const char* cmd) {
int hooked_console(const char* cmd) {
typedef int(_cdecl *t_func)(const char*);
shared_ptr<Hook> hook = Hook::get(hooked_console);
t_func func= reinterpret_cast<t_func>(hook->func());
if (cmd[0] == '$') {
handle_command(++cmd);
return 0;
}
hook->disable();
int ret=func(cmd);
int ret= hook->func<t_func>()(cmd);
hook->enable();
return ret;
}
@ -122,7 +150,6 @@ void hook_console() {
Hook::addr(reinterpret_cast<void*>(P_CON_HANDLER) , hooked_console);
}
void DllPreInit(HMODULE _mod) {
char mfn[1024];
InitConsole();
@ -151,4 +178,14 @@ void DllInit(HMODULE _mod)
DispatchMessage(&msg);
}
return;
}
void DllUnload(HMODULE _mod) {
SetConsoleCtrlHandler(NULL, false);
Hook::clear();
scrap_log(0xff0000, "ScrapHacks unloaded!\n");
cout << "[+] ScrapHacks unloaded, you can now close the console!" << endl;
FreeConsole();
PostQuitMessage(0);
return;
}

View File

@ -154,6 +154,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="D3D8_Hook.h" />
<ClInclude Include="VMT_Hook.h" />
<ClInclude Include="Hook.h" />
<ClInclude Include="Py_Utils.h" />
<ClInclude Include="Structures.h" />

View File

@ -39,6 +39,9 @@
<ClInclude Include="D3D8_Hook.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="VMT_Hook.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">

View File

@ -1,8 +1,10 @@
#pragma once
//OFFSETS
#define O_MONEY 0x2090
#define O_ALARM 0x1C6C
#define O_ALARM_GROW 0x1C68
#define O_HASHTABLE 0x4
//POINTERS
#define P_WORLD 0x7FE944

View File

@ -1,5 +1,5 @@
#pragma once
struct HashTableEntry;
struct Vector3 {
float x;
float y;
@ -32,3 +32,19 @@ struct Module
PyMod *mod;
map<string, PyMethodDef*> methods;
};
struct Entity {
void* VMT;
};
struct HashTable {
uint32_t size;
HashTableEntry** chains;
};
struct HashTableEntry {
Entity* data;
const char* name;
HashTableEntry* next;
};

View File

@ -0,0 +1,91 @@
#pragma once
class VMT_Hook
{
private:
MEMORY_BASIC_INFORMATION mbi;
void* orig;
void* detour;
DWORD* vtable;
size_t ord;
bool enabled;
static map<uintptr_t, shared_ptr<VMT_Hook>> hooks;
static DWORD* GetVTable(void* addr) {
return (DWORD *)*(DWORD *)addr;
};
public:
VMT_Hook(void* obj, size_t ord, void* detour) {
this->vtable = GetVTable(obj);
this->detour = detour;
this->orig = reinterpret_cast<void*>(vtable[ord]);
this->ord = ord;
this->enabled = false;
VirtualQuery(&this->vtable[this->ord], &mbi, sizeof(mbi));
cout << "Hooking: " << this->vtable << "[" << this->ord << "]: (" << this->orig << " -> " << this->detour << ")" << endl;
}
~VMT_Hook() {
cout << "Unhooking: " << this->vtable << "[" << this->ord << "]: (" << this->orig << " -> " << this->detour << ")" << endl;
this->disable();
}
static void create(void* obj, size_t ord, void* detour) {
uintptr_t key = reinterpret_cast<uintptr_t>(detour);
hooks[key] = make_shared<VMT_Hook>(obj,ord, detour);
hooks[key]->enable();
}
static shared_ptr<VMT_Hook> get(void* func) {
uintptr_t addr = reinterpret_cast<uintptr_t>(func);
return VMT_Hook::get(addr);
}
static shared_ptr<VMT_Hook> get(uintptr_t addr) {
return hooks.at(addr);
}
static size_t drop(void* func) {
uintptr_t addr = reinterpret_cast<uintptr_t>(func);
return VMT_Hook::drop(addr);
}
static size_t drop(uintptr_t addr) {
return hooks.erase(addr);
}
static void clear() {
return hooks.clear();
}
void disable() {
if (enabled) {
cout << "Disabling: " << this->vtable << "[" << this->ord << "]: (" << this->orig << " -> " << this->detour<<")" << endl;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, NULL);
this->vtable[ord] = reinterpret_cast<DWORD>(this->orig);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
enabled = false;
}
}
void enable() {
if (!enabled) {
cout << "Enabling: " << this->vtable << "[" << this->ord << "]: (" << this->orig << " -> " << this->detour <<")" << endl;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, NULL);
this->vtable[ord] = reinterpret_cast<DWORD>(this->detour);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
enabled = true;
}
}
template<typename T>
T func() {
return reinterpret_cast<T>(this->orig);
}
};
map<uintptr_t, shared_ptr<VMT_Hook>> VMT_Hook::hooks;

View File

@ -2,6 +2,8 @@
#define DLL_EXPORT extern "C" __declspec(dllexport)
void DllInit(HMODULE);
void DllPreInit(HMODULE);
void DllUnload(HMODULE);
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
@ -16,6 +18,8 @@ BOOL APIENTRY DllMain(HMODULE hModule,
hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)DllInit, hModule, 0, 0);
break;
case DLL_PROCESS_DETACH:
DllUnload(hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;

View File

@ -9,24 +9,30 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Injector", "Injector\Inject
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
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Debug|Any CPU.ActiveCfg = Debug|Win32
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Debug|x64.ActiveCfg = Debug|x64
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Debug|x64.Build.0 = Debug|x64
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Debug|x86.ActiveCfg = Debug|Win32
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Debug|x86.Build.0 = Debug|Win32
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Release|Any CPU.ActiveCfg = Release|Win32
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Release|x64.ActiveCfg = Release|x64
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Release|x64.Build.0 = Release|x64
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Release|x86.ActiveCfg = Release|Win32
{72CB1B9E-50C7-4010-BEAD-82FACF87A87A}.Release|x86.Build.0 = Release|Win32
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Debug|Any CPU.ActiveCfg = Debug|Win32
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Debug|x64.ActiveCfg = Debug|x64
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Debug|x64.Build.0 = Debug|x64
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Debug|x86.ActiveCfg = Debug|Win32
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Debug|x86.Build.0 = Debug|Win32
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Release|Any CPU.ActiveCfg = Release|Win32
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Release|x64.ActiveCfg = Release|x64
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Release|x64.Build.0 = Release|x64
{7C91C225-D95C-4B7A-9251-0CE358BAF556}.Release|x86.ActiveCfg = Release|Win32