code:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>16.0</VCProjectVersion>
    <ProjectGuid>{CA479467-D518-46A2-AC86-3098ADA99FE5}</ProjectGuid>
    <Keyword>Win32Proj</Keyword>
    <RootNamespace>PDLoader</RootNamespace>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
    <TargetFrameworkVersion>
    </TargetFrameworkVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
    <CLRSupport>false</CLRSupport>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <LinkIncremental>true</LinkIncremental>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LinkIncremental>true</LinkIncremental>
    <TargetName>dnsapi</TargetName>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <LinkIncremental>false</LinkIncremental>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LinkIncremental>false</LinkIncremental>
    <TargetName>dnsapi</TargetName>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <PrecompiledHeader>NotUsing</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>X64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <AdditionalIncludeDirectories>..\MemoryModule;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <ModuleDefinitionFile>x64.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <PrecompiledHeader>NotUsing</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <AdditionalIncludeDirectories>..\dependencies\MemoryModule;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <LanguageStandard>stdcpp17</LanguageStandard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <ModuleDefinitionFile>x64.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;NDEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <ModuleDefinitionFile>x64.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <PrecompiledHeader>NotUsing</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <AdditionalIncludeDirectories>..\dependencies\MemoryModule;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <LanguageStandard>stdcpp17</LanguageStandard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <ModuleDefinitionFile>x64.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClInclude Include="exception.hpp" />
    <ClInclude Include="framework.h" />
    <ClInclude Include="ModuleList.h" />
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="dllmain.cpp" />
  </ItemGroup>
  <ItemGroup>
    <None Include="x64.def" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>


code:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <Filter Include="Source Files">
      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
    </Filter>
    <Filter Include="Header Files">
      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
    </Filter>
    <Filter Include="Resource Files">
      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
    </Filter>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="framework.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="ModuleList.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="exception.hpp">
      <Filter>Source Files</Filter>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="dllmain.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <None Include="x64.def">
      <Filter>Source Files</Filter>
    </None>
  </ItemGroup>
</Project>

repo_name: twistero/PD

license: mit


code:
#include "framework.h"
#include "exception.hpp"

HMODULE hm;
std::vector<std::wstring> iniPaths;

bool iequals(std::wstring_view s1, std::wstring_view s2)
{
	std::wstring str1(std::move(s1));
	std::wstring str2(std::move(s2));
	std::transform(str1.begin(), str1.end(), str1.begin(), [](wchar_t c) { return ::towlower(c); });
	std::transform(str2.begin(), str2.end(), str2.begin(), [](wchar_t c) { return ::towlower(c); });
	return (str1 == str2);
}

std::wstring to_wstring(std::string_view cstr)
{
	std::string str(std::move(cstr));
	auto charsReturned = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
	std::wstring wstrTo(charsReturned, 0);
	MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], charsReturned);
	return wstrTo;
}

std::wstring SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken)
{
	std::wstring r;
	WCHAR* szSystemPath = nullptr;
	if (SUCCEEDED(SHGetKnownFolderPath(rfid, dwFlags, hToken, &szSystemPath)))
	{
		r = szSystemPath;
	}
	CoTaskMemFree(szSystemPath);
	return r;
};

HMODULE LoadLibraryW(const std::wstring& lpLibFileName)
{
	return LoadLibraryW(lpLibFileName.c_str());
}

std::wstring GetCurrentDirectoryW()
{
	static constexpr auto INITIAL_BUFFER_SIZE = MAX_PATH;
	static constexpr auto MAX_ITERATIONS = 7;
	std::wstring ret;
	auto bufferSize = INITIAL_BUFFER_SIZE;
	for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations)
	{
		ret.resize(bufferSize);
		auto charsReturned = GetCurrentDirectoryW(bufferSize, &ret[0]);
		if (charsReturned < ret.length())
		{
			ret.resize(charsReturned);
			return ret;
		}
		else
		{
			bufferSize *= 2;
		}
	}
	return L"";
}

UINT GetPrivateProfileIntW(LPCWSTR lpAppName, LPCWSTR lpKeyName, INT nDefault, const std::vector<std::wstring>& fileNames)
{
	for (const auto& file : fileNames)
	{
		nDefault = GetPrivateProfileIntW(lpAppName, lpKeyName, nDefault, file.c_str());
	}
	return nDefault;
}

std::wstring GetSelfName()
{
	const std::wstring moduleFileName = GetModuleFileNameW(hm);
	return moduleFileName.substr(moduleFileName.find_last_of(L"/\\") + 1);
}

template<typename T, typename... Args>
void GetSections(T&& h, Args... args)
{
	const std::set< std::string_view, std::less<> > s = { args... };
	size_t dwLoadOffset = (size_t)GetModuleHandle(NULL);
	BYTE* pImageBase = reinterpret_cast<BYTE*>(dwLoadOffset);
	PIMAGE_DOS_HEADER   pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(dwLoadOffset);
	PIMAGE_NT_HEADERS   pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(pImageBase + pDosHeader->e_lfanew);
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHeader);
	for (int iSection = 0; iSection < pNtHeader->FileHeader.NumberOfSections; ++iSection, ++pSection)
	{
		auto pszSectionName = reinterpret_cast<const char*>(pSection->Name);
		if (s.find(pszSectionName) != s.end())
		{
			DWORD dwPhysSize = (pSection->Misc.VirtualSize + 4095) & ~4095;
			std::forward<T>(h)(pSection, dwLoadOffset, dwPhysSize);
		}
	}
}

enum Kernel32ExportsNames
{
	eGetStartupInfoA,
	eGetStartupInfoW,
	eGetModuleHandleA,
	eGetModuleHandleW,
	eGetProcAddress,
	eGetShortPathNameA,
	eFindNextFileA,
	eFindNextFileW,
	eLoadLibraryA,
	eLoadLibraryW,
	eFreeLibrary,
	eCreateEventA,
	eCreateEventW,
	eGetSystemInfo,
	eInterlockedCompareExchange,
	eSleep,

	Kernel32ExportsNamesCount
};

enum Kernel32ExportsData
{
	IATPtr,
	ProcAddress,

	Kernel32ExportsDataCount
};

size_t Kernel32Data[Kernel32ExportsNamesCount][Kernel32ExportsDataCount];

static LONG OriginalLibraryLoaded = 0;
void LoadOriginalLibrary()
{
	if (_InterlockedCompareExchange(&OriginalLibraryLoaded, 1, 0) != 0) return;

	auto szSelfName = GetSelfName();
	auto szSystemPath = SHGetKnownFolderPath(FOLDERID_System, 0, nullptr) + L'\\' + szSelfName;
	auto szLocalPath = GetModuleFileNameW(hm); szLocalPath = szLocalPath.substr(0, szLocalPath.find_last_of(L"/\\") + 1);

	if (iequals(szSelfName, L"dnsapi.dll")) {
		dnsapi.LoadOriginalLibrary(LoadLibraryW(szSystemPath));
	}
}

void FindFiles(WIN32_FIND_DATAW* fd)
{
	auto dir = GetCurrentDirectoryW();

	HANDLE dvaFile = FindFirstFileW(L"*.dva", fd);
	if (dvaFile != INVALID_HANDLE_VALUE)
	{
		do {
			if (!(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			{
				auto pos = wcslen(fd->cFileName);

				if (fd->cFileName[pos - 4] == '.' &&
					(fd->cFileName[pos - 3] == 'd' || fd->cFileName[pos - 3] == 'D') &&
					(fd->cFileName[pos - 2] == 'v' || fd->cFileName[pos - 2] == 'V') &&
					(fd->cFileName[pos - 1] == 'a' || fd->cFileName[pos - 1] == 'A') &&
					GetPrivateProfileIntW(L"plugins", fd->cFileName, TRUE, iniPaths))
				{
					auto path = dir + L'\\' + fd->cFileName;

					if (GetModuleHandle(path.c_str()) == NULL)
					{
						auto h = LoadLibraryW(path);
						SetCurrentDirectoryW(dir.c_str()); //in case dva switched it

						if (h == NULL)
						{
							auto e = GetLastError();
							if (e != ERROR_DLL_INIT_FAILED) // in case dllmain returns false
							{
								std::wstring msg = L"Unable to load " + std::wstring(fd->cFileName) + L". Error: " + std::to_wstring(e);
								MessageBoxW(0, msg.c_str(), L"PD Loader", MB_ICONERROR);
							}
						}
						else
						{
							auto procedure = (void(*)())GetProcAddress(h, "InitializeDVA");

							if (procedure != NULL)
							{
								procedure();
							}
						}
					}
				}
			}
		} while (FindNextFileW(dvaFile, fd));
		FindClose(dvaFile);
	}
}

void LoadPlugins()
{
	auto oldDir = GetCurrentDirectoryW(); // store the current directory

	auto szSelfPath = GetModuleFileNameW(hm).substr(0, GetModuleFileNameW(hm).find_last_of(L"/\\") + 1);
	SetCurrentDirectoryW(szSelfPath.c_str());

	auto nWantsToLoadPlugins = GetPrivateProfileIntW(L"global", L"enable", TRUE, iniPaths);

	if (nWantsToLoadPlugins)
	{
		WIN32_FIND_DATAW fd;

		SetCurrentDirectoryW(szSelfPath.c_str());

		if (SetCurrentDirectoryW(L"plugins\\"))
			FindFiles(&fd);
	}

	SetCurrentDirectoryW(oldDir.c_str()); // Reset the current directory
}

static LONG LoadedPluginsYet = 0;
void LoadEverything()
{
	if (_InterlockedCompareExchange(&LoadedPluginsYet, 1, 0) != 0) return;

	LoadOriginalLibrary();
	LoadPlugins();
}

static LONG RestoredOnce = 0;
void LoadPluginsAndRestoreIAT(uintptr_t retaddr)
{
	bool calledFromBind = false;

	//steam drm check
	GetSections([&](PIMAGE_SECTION_HEADER pSection, size_t dwLoadOffset, DWORD dwPhysSize) {
		auto dwStart = static_cast<uintptr_t>(dwLoadOffset + pSection->VirtualAddress);
		auto dwEnd = dwStart + dwPhysSize;
		if (retaddr >= dwStart && retaddr <= dwEnd)
			calledFromBind = true;
	}, ".bind");

	if (calledFromBind) return;

	if (_InterlockedCompareExchange(&RestoredOnce, 1, 0) != 0) return;

	LoadEverything();

	for (size_t i = 0; i < Kernel32ExportsNamesCount; i++)
	{
		if (Kernel32Data[i][IATPtr] && Kernel32Data[i][ProcAddress])
		{
			auto ptr = (size_t*)Kernel32Data[i][IATPtr];
			DWORD dwProtect[2];
			VirtualProtect(ptr, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]);
			*ptr = Kernel32Data[i][ProcAddress];
			VirtualProtect(ptr, sizeof(size_t), dwProtect[0], &dwProtect[1]);
		}
	}
}

void WINAPI CustomGetStartupInfoA(LPSTARTUPINFOA lpStartupInfo)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetStartupInfoA(lpStartupInfo);
}

void WINAPI CustomGetStartupInfoW(LPSTARTUPINFOW lpStartupInfo)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetStartupInfoW(lpStartupInfo);
}

HMODULE WINAPI CustomGetModuleHandleA(LPCSTR lpModuleName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetModuleHandleA(lpModuleName);
}

HMODULE WINAPI CustomGetModuleHandleW(LPCWSTR lpModuleName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetModuleHandleW(lpModuleName);
}

FARPROC WINAPI CustomGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetProcAddress(hModule, lpProcName);
}

DWORD WINAPI CustomGetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetShortPathNameA(lpszLongPath, lpszShortPath, cchBuffer);
}

BOOL WINAPI CustomFindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return FindNextFileA(hFindFile, lpFindFileData);
}

BOOL WINAPI CustomFindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return FindNextFileW(hFindFile, lpFindFileData);
}

HMODULE WINAPI CustomLoadLibraryA(LPCSTR lpLibFileName)
{
	LoadOriginalLibrary();

	return LoadLibraryA(lpLibFileName);
}

HMODULE WINAPI CustomLoadLibraryW(LPCWSTR lpLibFileName)
{
	LoadOriginalLibrary();

	return LoadLibraryW(lpLibFileName);
}

BOOL WINAPI CustomFreeLibrary(HMODULE hLibModule)
{
	if (hLibModule != hm)
		return FreeLibrary(hLibModule);
	else
		return !NULL;
}

HANDLE WINAPI CustomCreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return CreateEventA(lpEventAttributes, bManualReset, bInitialState, lpName);
}

HANDLE WINAPI CustomCreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName);
}

void WINAPI CustomGetSystemInfo(LPSYSTEM_INFO lpSystemInfo)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetSystemInfo(lpSystemInfo);
}

LONG WINAPI CustomInterlockedCompareExchange(LONG volatile* Destination, LONG ExChange, LONG Comperand)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return _InterlockedCompareExchange(Destination, ExChange, Comperand);
}

void WINAPI CustomSleep(DWORD dwMilliseconds)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return Sleep(dwMilliseconds);
}

bool HookKernel32IAT(HMODULE mod, bool exe)
{
	auto hExecutableInstance = (size_t)mod;
	IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(hExecutableInstance + ((IMAGE_DOS_HEADER*)hExecutableInstance)->e_lfanew);
	IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(hExecutableInstance + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
	size_t                      nNumImports = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;

	if (exe)
	{
		Kernel32Data[eGetStartupInfoA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetStartupInfoA");
		Kernel32Data[eGetStartupInfoW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetStartupInfoW");
		Kernel32Data[eGetModuleHandleA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetModuleHandleA");
		Kernel32Data[eGetModuleHandleW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetModuleHandleW");
		Kernel32Data[eGetProcAddress][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetProcAddress");
		Kernel32Data[eGetShortPathNameA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetShortPathNameA");
		Kernel32Data[eFindNextFileA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FindNextFileA");
		Kernel32Data[eFindNextFileW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FindNextFileW");
		Kernel32Data[eLoadLibraryA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "LoadLibraryA");
		Kernel32Data[eLoadLibraryW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "LoadLibraryW");
		Kernel32Data[eFreeLibrary][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FreeLibrary");
		Kernel32Data[eCreateEventA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "CreateEventA");
		Kernel32Data[eCreateEventW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "CreateEventW");
		Kernel32Data[eGetSystemInfo][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetSystemInfo");
		Kernel32Data[eInterlockedCompareExchange][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "InterlockedCompareExchange");
		Kernel32Data[eSleep][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "Sleep");
	}

	uint32_t matchedImports = 0;

	auto PatchIAT = [&](size_t start, size_t end, size_t exe_end)
	{
		for (size_t i = 0; i < nNumImports; i++)
		{
			if (hExecutableInstance + (pImports + i)->FirstThunk > start && !(end && hExecutableInstance + (pImports + i)->FirstThunk > end))
				end = hExecutableInstance + (pImports + i)->FirstThunk;
		}

		if (!end) { end = start + 0x100; }
		if (end > exe_end) //for very broken exes
		{
			start = hExecutableInstance;
			end = exe_end;
		}

		for (auto i = start; i < end; i += sizeof(size_t))
		{
			DWORD dwProtect[2];
			VirtualProtect((size_t*)i, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]);

			auto ptr = *(size_t*)i;
			if (!ptr)
				continue;

			if (ptr == Kernel32Data[eGetStartupInfoA][ProcAddress])
			{
				if (exe) Kernel32Data[eGetStartupInfoA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetStartupInfoA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetStartupInfoW][ProcAddress])
			{
				if (exe) Kernel32Data[eGetStartupInfoW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetStartupInfoW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetModuleHandleA][ProcAddress])
			{
				if (exe) Kernel32Data[eGetModuleHandleA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetModuleHandleA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetModuleHandleW][ProcAddress])
			{
				if (exe) Kernel32Data[eGetModuleHandleW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetModuleHandleW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetProcAddress][ProcAddress])
			{
				if (exe) Kernel32Data[eGetProcAddress][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetProcAddress;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetShortPathNameA][ProcAddress])
			{
				if (exe) Kernel32Data[eGetShortPathNameA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetShortPathNameA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eFindNextFileA][ProcAddress])
			{
				if (exe) Kernel32Data[eFindNextFileA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomFindNextFileA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eFindNextFileW][ProcAddress])
			{
				if (exe) Kernel32Data[eFindNextFileW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomFindNextFileW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eLoadLibraryA][ProcAddress])
			{
				if (exe) Kernel32Data[eLoadLibraryA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomLoadLibraryA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eLoadLibraryW][ProcAddress])
			{
				if (exe) Kernel32Data[eLoadLibraryW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomLoadLibraryW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eFreeLibrary][ProcAddress])
			{
				if (exe) Kernel32Data[eFreeLibrary][IATPtr] = i;
				*(size_t*)i = (size_t)CustomFreeLibrary;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eCreateEventA][ProcAddress])
			{
				if (exe) Kernel32Data[eCreateEventA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomCreateEventA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eCreateEventW][ProcAddress])
			{
				if (exe) Kernel32Data[eCreateEventW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomCreateEventW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetSystemInfo][ProcAddress])
			{
				if (exe) Kernel32Data[eGetSystemInfo][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetSystemInfo;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eInterlockedCompareExchange][ProcAddress])
			{
				if (exe) Kernel32Data[eInterlockedCompareExchange][IATPtr] = i;
				*(size_t*)i = (size_t)CustomInterlockedCompareExchange;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eSleep][ProcAddress])
			{
				if (exe) Kernel32Data[eSleep][IATPtr] = i;
				*(size_t*)i = (size_t)CustomSleep;
				matchedImports++;
			}

			VirtualProtect((size_t*)i, sizeof(size_t), dwProtect[0], &dwProtect[1]);
		}
	};

	static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER
	{
		return reinterpret_cast<PIMAGE_SECTION_HEADER>(
			(UCHAR*)nt_headers->OptionalHeader.DataDirectory +
			nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) +
			section * sizeof(IMAGE_SECTION_HEADER));
	};

	static auto getSectionEnd = [](IMAGE_NT_HEADERS* ntHeader, size_t inst) -> auto
	{
		auto sec = getSection(ntHeader, ntHeader->FileHeader.NumberOfSections - 1);
		auto secSize = max(sec->SizeOfRawData, sec->Misc.VirtualSize);
		auto end = inst + max(sec->PointerToRawData, sec->VirtualAddress) + secSize;
		return end;
	};

	auto hExecutableInstance_end = getSectionEnd(ntHeader, hExecutableInstance);

	// Find kernel32.dll
	for (size_t i = 0; i < nNumImports; i++)
	{
		if ((size_t)(hExecutableInstance + (pImports + i)->Name) < hExecutableInstance_end)
		{
			if (!_stricmp((const char*)(hExecutableInstance + (pImports + i)->Name), "KERNEL32.DLL"))
				PatchIAT(hExecutableInstance + (pImports + i)->FirstThunk, 0, hExecutableInstance_end);
		}
	}

	// Fixing ordinals
	auto szSelfName = GetSelfName();

	static auto PatchOrdinals = [&szSelfName](size_t hInstance)
	{
		IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(hInstance + ((IMAGE_DOS_HEADER*)hInstance)->e_lfanew);
		IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(hInstance + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
		size_t                      nNumImports = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;

		for (size_t i = 0; i < nNumImports; i++)
		{
			if ((size_t)(hInstance + (pImports + i)->Name) < getSectionEnd(ntHeader, (size_t)hInstance))
			{
				if (iequals(szSelfName, (to_wstring((const char*)(hInstance + (pImports + i)->Name)))))
				{
					PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(hInstance + (pImports + i)->OriginalFirstThunk);
					size_t j = 0;
					while (thunk->u1.Function)
					{
						if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
						{
							PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)(hInstance + thunk->u1.AddressOfData);
							void** p = (void**)(hInstance + (pImports + i)->FirstThunk);
						}
						++thunk;
					}
				}
			}
		}
	};

	ModuleList dlls;
	dlls.Enumerate(ModuleList::SearchLocation::LocalOnly);
	for (auto& e : dlls.m_moduleList)
	{
		PatchOrdinals((size_t)std::get<HMODULE>(e));
	}
	return matchedImports > 0;
}

LONG WINAPI CustomUnhandledExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo)
{
	// step 1: write minidump
	wchar_t		modulename[MAX_PATH];
	wchar_t		filename[MAX_PATH];
	wchar_t		timestamp[128];
	__time64_t	time;
	struct tm	ltime;
	HANDLE		hFile;
	HWND		hWnd;

	wchar_t* modulenameptr = NULL;
	if (GetModuleFileNameW(GetModuleHandle(NULL), modulename, _countof(modulename)) != 0)
	{
		modulenameptr = wcsrchr(modulename, '\\');
		*modulenameptr = L'\0';
		modulenameptr += 1;
	}
	else
	{
		*modulenameptr = L'err.err';
	}

	_time64(&time);
	_localtime64_s(&ltime, &time);
	wcsftime(timestamp, _countof(timestamp), L"%Y%m%d%H%M%S", &ltime);
	swprintf_s(filename, L"%s\\%s\\%s.%s.dmp", modulename, L"logs", modulenameptr, timestamp);

	hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile != INVALID_HANDLE_VALUE)
	{
		MINIDUMP_EXCEPTION_INFORMATION ex;
		memset(&ex, 0, sizeof(ex));
		ex.ThreadId = GetCurrentThreadId();
		ex.ExceptionPointers = ExceptionInfo;
		ex.ClientPointers = TRUE;

		if (FAILED(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithDataSegs, &ex, NULL, NULL)))
		{
		}

		CloseHandle(hFile);
	}

	// step 2: write log
	// Logs exception into buffer and writes to file
	swprintf_s(filename, L"%s\\%s\\%s.%s.log", modulename, L"logs", modulenameptr, timestamp);
	hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile != INVALID_HANDLE_VALUE)
	{
		auto Log = [ExceptionInfo, hFile](char* buffer, size_t size, bool reg, bool stack, bool trace)
		{
			if (LogException(buffer, size, (LPEXCEPTION_POINTERS)ExceptionInfo, reg, stack, trace))
			{
				DWORD NumberOfBytesWritten = 0;
				WriteFile(hFile, buffer, strlen(buffer), &NumberOfBytesWritten, NULL);
			}
		};

		// Try to make a very descriptive exception, for that we need to malloc a huge buffer...
		if (auto buffer = (char*)malloc(max_logsize_ever))
		{
			Log(buffer, max_logsize_ever, true, true, true);
			free(buffer);
		}
		else
		{
			// Use a static buffer, no need for any allocation
			static const auto size = max_logsize_basic + max_logsize_regs + max_logsize_stackdump;
			static char static_buf[size];
			static_assert(size <= max_static_buffer, "Static buffer is too big");

			Log(buffer = static_buf, sizeof(static_buf), true, true, false);
		}

		CloseHandle(hFile);
	}

	// step 3: exit the application
	ShowCursor(TRUE);
	hWnd = FindWindowW(0, L"");
	SetForegroundWindow(hWnd);

	return EXCEPTION_CONTINUE_SEARCH;
}

void Init()
{
	std::wstring modulePath = GetModuleFileNameW(hm);
	std::wstring moduleName = modulePath.substr(modulePath.find_last_of(L"/\\") + 1);
	moduleName.resize(moduleName.find_last_of(L'.'));
	modulePath.resize(modulePath.find_last_of(L"/\\") + 1);
	iniPaths.emplace_back(modulePath + moduleName + L".ini");
	iniPaths.emplace_back(modulePath + L"plugins\\config.ini");

	std::wstring m = GetModuleFileNameW(NULL);
	m = m.substr(0, m.find_last_of(L"/\\") + 1) + L"logs";

	auto FolderExists = [](LPCWSTR szPath) -> BOOL
	{
		DWORD dwAttrib = GetFileAttributes(szPath);
		return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
	};

	if (FolderExists(m.c_str()))
	{
		SetUnhandledExceptionFilter(CustomUnhandledExceptionFilter);
		// Now stub out CustomUnhandledExceptionFilter so NO ONE ELSE can set it!
		uint32_t ret = 0x909090C3; //ret
		DWORD protect[2];
		VirtualProtect(&SetUnhandledExceptionFilter, sizeof(ret), PAGE_EXECUTE_READWRITE, &protect[0]);
		memcpy(&SetUnhandledExceptionFilter, &ret, sizeof(ret));
		VirtualProtect(&SetUnhandledExceptionFilter, sizeof(ret), protect[0], &protect[1]);
	}

	LoadEverything();

}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID /*lpReserved*/)
{
	if (reason == DLL_PROCESS_ATTACH)
	{
		hm = hModule;
		Init();
	}
	return TRUE;
}


code:
#pragma once
/*
 * Unhandled Exception Tracer
 * by LINK/2012 <dma_2012@hotmail.com>
 *
 *  This source code is offered for use in the public domain. You may
 *  use, modify or distribute it freely.
 *
 *  This code is distributed in the hope that it will be useful but
 *  WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
 *  DISCLAIMED. This includes but is not limited to warranties of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <cassert>
#include <DbgHelp.h>
#pragma comment(lib, "Dbghelp.lib")

 /*
  *  Special Note:
  *      Try not to allocate any memory in this file!
  *      Allocation after a exception may not be a good idea...
  */

#define LODWORD(_qw)    ((DWORD)(_qw))
#define HIDWORD(_qw)    ((DWORD)(((_qw) >> 32) & 0xffffffff))

  // General constants
static const int sizeof_word = sizeof(void*);           // Size of a CPU word (4 bytes on x86)
static const int max_chars_per_print = MAX_PATH + 256;  // Max characters per Print() call
static const int symbol_max = 256;                      // Max size of a symbol (func symbol, var symbol, etc)
static const int max_static_buffer = 4096;              // Max static buffer for logging

// Stackdump constants
static const int stackdump_max_words = 60;              // max number of CPU words that the stackdump should dump
static const int stackdump_words_per_line = 6;          // max CPU words in a single line
static const int stackdump_line_count = (stackdump_max_words / stackdump_words_per_line) + 1;

// Backtrace constants
static const int max_backtrace_ever = 100;
static const int max_backtrace = 20;

// Maximum log size constants
static const int max_logsize_basic = (MAX_PATH + 200);      // module path + other text
static const int max_logsize_regs = 32 + (4 * 4 * 28);     // info + (regsPerLine * numLines * charsPerReg)
static const int max_logsize_stackdump = 32 + 80 + (stackdump_line_count * 32) + (10 * stackdump_words_per_line * stackdump_line_count);
static const int max_logsize_backtrace = 32 + max_backtrace_ever * (MAX_PATH + symbol_max + 90);
static const int max_logsize_ever = 32 + max_logsize_basic + max_logsize_regs + max_logsize_stackdump + max_logsize_backtrace;

// Internal
class ExceptionTracer;
class StackTrace;
static HMODULE GetModuleFromAddress(LPVOID address);
static const char* GetExceptionCodeString(unsigned int code);
static const char* FindModuleName(HMODULE module, char* output, DWORD size);
static int LogException(char* buffer, size_t max, LPEXCEPTION_POINTERS pException, bool bLogRegisters, bool bLogStack, bool bLogBacktrace);
static LPTOP_LEVEL_EXCEPTION_FILTER PrevFilter = nullptr;
static void(*ExceptionCallback)(const char* buffer) = nullptr;

// Exportable
int InstallExceptionCatcher(void(*OnException)(const char* log));

/*
 *  ExceptionTrace
 *      This class is responssible for tracing all possible informations about an LPEXCEPTION_POINTER
 */
class ExceptionTracer
{
public:
	ExceptionTracer(char* buffer, size_t max, LPEXCEPTION_POINTERS pException);
	void PrintUnhandledException();
	void PrintRegisters();
	void PrintStackdump();
	void PrintBacktrace();

	void EnterScope();
	void LeaveScope();
	void Print(const char* fmt, ...);
	void NewLine() { Print("\n%s", spc); }

protected:
	EXCEPTION_POINTERS& exception;
	EXCEPTION_RECORD& record;
	CONTEXT& context;
	HMODULE module;

	char* buffer;       // Logging buffer
	size_t len;         // Logged length
	size_t max;         // Maximum we can log in that buffer

	char spc[(10 * 4) + 1]; // Scope/spacing buffer, 4 spaces per scope, max 10 scopes
	size_t nspc;        // Number spaces used up there
};

/*
 *  StackTracer
 *      Responssible for backtracing an stack from a context
 */
class StackTracer
{
public:
	struct Trace
	{
		// The following values may be null (any)
		HMODULE module;     // The module the func related to this frame is located
		void* pc;           // Program counter at func related to this frame (EIP)
		void* ret;          // Return address for the frame
		void* frame;        // The frame address (EBP)
		void* stack;        // The stack pointer at the frame (ESP)
	};

	StackTracer(const CONTEXT& context);
	Trace* Walk();

private:
	Trace trace;
	DWORD old_options;
	CONTEXT context;
	STACKFRAME64 frame;
};

/*
 *  TheUnhandledExceptionFilter
 *      Logs an unhandled exception
 */
static LONG CALLBACK TheUnhandledExceptionFilter(LPEXCEPTION_POINTERS pException)
{
	// Logs exception into buffer and calls the callback
	auto Log = [pException](char* buffer, size_t size, bool reg, bool stack, bool trace)
	{
		if (LogException(buffer, size, (LPEXCEPTION_POINTERS)pException, reg, stack, trace))
			ExceptionCallback(buffer);
	};

	// Try to make a very descriptive exception, for that we need to malloc a huge buffer...
	if (auto buffer = (char*)malloc(max_logsize_ever))
	{
		Log(buffer, max_logsize_ever, true, true, true);
		free(buffer);
	}
	else
	{
		// Use a static buffer, no need for any allocation
		static const auto size = max_logsize_basic + max_logsize_regs + max_logsize_stackdump;
		static char static_buf[size];
		static_assert(size <= max_static_buffer, "Static buffer is too big");

		Log(buffer = static_buf, sizeof(static_buf), true, true, false);
	}

	// Continue exception propagation
	return (PrevFilter ? PrevFilter(pException) : EXCEPTION_CONTINUE_SEARCH);  // I'm not really sure about this return
}

/*
 *  InstallExceptionCatcher
 *      Installs a exception handler to call the specified callback when it happens with human readalbe information.
 */
int InstallExceptionCatcher(void(*cb)(const char* log))
{
	PrevFilter = SetUnhandledExceptionFilter(TheUnhandledExceptionFilter);
	ExceptionCallback = cb;
	return 1;
}

/*
 * LogException
 *      Takes an LPEXCEPTION_POINTERS and transforms in a string that is put in the logging steam
 */
static int LogException(char* buffer, size_t max, LPEXCEPTION_POINTERS pException, bool bLogRegisters, bool bLogStack, bool bLogBacktrace)
{
	ExceptionTracer trace(buffer, max, pException);
	trace.PrintUnhandledException();
	trace.EnterScope();
	if (bLogRegisters) trace.PrintRegisters();
	if (bLogStack) trace.PrintStackdump();
	if (bLogBacktrace) trace.PrintBacktrace();
	trace.LeaveScope();
	return 1;
}

/*
 *  ExceptionTracer
 *      Contructs a exception trace object, responssible for tracing informations about an exception
 */
ExceptionTracer::ExceptionTracer(char* buffer, size_t max, LPEXCEPTION_POINTERS pException) :
	buffer(buffer), exception(*pException), record(*pException->ExceptionRecord), context(*pException->ContextRecord)
{
	this->buffer = buffer;
	this->buffer[this->len = 0] = 0;
	this->spc[this->nspc = 0] = 0;
	this->max = max;

	// Acquiere common information that we'll access
	this->module = GetModuleFromAddress(record.ExceptionAddress);
}

/*
 *  Print
 *      Prints some formated text into the logging buffer
 */
void ExceptionTracer::Print(const char* fmt, ...)
{
	va_list va;
	va_start(va, fmt);
	if ((this->max - this->len) > max_chars_per_print)
		this->len += vsprintf(&this->buffer[len], fmt, va);
	va_end(va);
}

/*
 *  EnterScope
 *      Enters a new scope in the logging buffer (scope is related to indentation)
 *      This also prints a new line
 */
void ExceptionTracer::EnterScope()
{
	nspc += 4;
	spc[nspc - 4] = ' ';
	spc[nspc - 3] = ' ';
	spc[nspc - 2] = ' ';
	spc[nspc - 1] = ' ';
	spc[nspc - 0] = 0;
	NewLine();
}

/*
 *  LeaveScope
 *      Leaves the scope
 */
void ExceptionTracer::LeaveScope()
{
	assert(nspc > 0);
	nspc -= 4;
	spc[nspc] = 0;
	NewLine();
}

/*
 *  PrintUnhandledException
 *      Prints the well known "Unhandled exception at ..." into the logging buffer
 */
void ExceptionTracer::PrintUnhandledException()
{
	char module_name[MAX_PATH];
	auto dwExceptionCode = record.ExceptionCode;
	uintptr_t address = (uintptr_t)record.ExceptionAddress;

	// Find out our module name for logging
	if (!this->module || !GetModuleFileNameA(this->module, module_name, sizeof(module_name)))
		strcpy(module_name, "unknown");

	// Log the exception in a similar format similar to debuggers format
	Print("Unhandled exception at 0x%p in %s", address, FindModuleName(module, module_name, sizeof(module_name)));
	if (module) Print(" (+0x%x)", address - (uintptr_t)(module));
	Print(": 0x%X: %s", dwExceptionCode, GetExceptionCodeString(dwExceptionCode));

	// If exception is IN_PAGE_ERROR or ACCESS_VIOLATION, we have additional information such as an address
	if (dwExceptionCode == EXCEPTION_IN_PAGE_ERROR || dwExceptionCode == EXCEPTION_ACCESS_VIOLATION)
	{
		auto rw = (DWORD)record.ExceptionInformation[0];  // read or write?
		auto addr = (ULONG_PTR)record.ExceptionInformation[1];  // which address?

		Print(" %s 0x%p",
			rw == 0 ? "reading location" : rw == 1 ? "writing location" : rw == 8 ? "DEP at" : "",
			addr);

		// IN_PAGE_ERROR have another information...
		if (dwExceptionCode == EXCEPTION_IN_PAGE_ERROR)
		{
			NewLine();
			Print("Underlying NTSTATUS code that resulted in the exception is 0x%p",
				record.ExceptionInformation[2]);
		}
	}

	Print(".");
}

/*
 *  PrintRegisters
 *      Prints the content of the assembly registers into the logging buffer
 */
void ExceptionTracer::PrintRegisters()
{
	int regs_in_line = 0;       // Amount of registers currently printed on this line

	// Prints a register, followed by spaces
	auto PrintRegister = [this, &regs_in_line](const char* reg_name, size_t reg_value, const char* spaces)
	{
		Print("%s: 0x%p%s", reg_name, reg_value, spaces);
		if (++regs_in_line >= 4) { this->NewLine(); regs_in_line = 0; }
	};

	auto PrintFloatRegister = [this, &regs_in_line](const char* reg_name, int reg_num, uint32_t reg_value1, uint32_t reg_value2, uint32_t reg_value3, uint32_t reg_value4)
	{
		Print("%s%02d: 0x%08X 0x%08X 0x%08X 0x%08X  [ %f %f %f %f ]", reg_name, reg_num, reg_value1, reg_value2, reg_value3, reg_value4,
			*(float*)& reg_value1, *(float*)& reg_value2, *(float*)& reg_value3, *(float*)& reg_value4);
		if (++regs_in_line >= 1) { this->NewLine(); regs_in_line = 0; }
	};

	// Prints a general purposes register
	auto PrintIntRegister = [PrintRegister](const char* reg_name, size_t reg_value)
	{
		PrintRegister(reg_name, reg_value, "  ");
	};

	// Prints a segment register
	auto PrintSegRegister = [PrintRegister](const char* reg_name, size_t reg_value)
	{
		PrintRegister(reg_name, reg_value, "   ");
	};

	Print("Register dump:");
	EnterScope();
	{
		// Print main general purposes registers
		if (context.ContextFlags & CONTEXT_INTEGER)
		{
#if !_M_X64
			PrintIntRegister("EAX", context.Eax);
			PrintIntRegister("EBX", context.Ebx);
			PrintIntRegister("ECX", context.Ecx);
			PrintIntRegister("EDX", context.Edx);
			PrintIntRegister("EDI", context.Edi);
			PrintIntRegister("ESI", context.Esi);
#else
			PrintIntRegister("RAX", context.Rax);
			PrintIntRegister("RCX", context.Rcx);
			PrintIntRegister("RDX", context.Rdx);
			PrintIntRegister("RBX", context.Rbx);
			PrintIntRegister("RBP", context.Rbp);
			PrintIntRegister("RSI", context.Rsi);
			PrintIntRegister("RDI", context.Rdi);
			PrintIntRegister("R08", context.R8);
			PrintIntRegister("R09", context.R9);
			PrintIntRegister("R10", context.R10);
			PrintIntRegister("R11", context.R11);
			PrintIntRegister("R12", context.R12);
			PrintIntRegister("R13", context.R13);
			PrintIntRegister("R14", context.R14);
			PrintIntRegister("R15", context.R15);
#endif
		}

		// Print control registers
		if (context.ContextFlags & CONTEXT_CONTROL)
		{
#if !_M_X64
			PrintIntRegister("EBP", context.Ebp);
			PrintIntRegister("EIP", context.Eip);
			PrintIntRegister("ESP", context.Esp);
			PrintIntRegister("EFL", context.EFlags);
			this->NewLine(); this->NewLine(); regs_in_line = 0;
			PrintSegRegister("CS", context.SegCs);
			PrintSegRegister("SS", context.SegSs);
#else
			PrintIntRegister("RIP", context.Rip);
			PrintIntRegister("RSP", context.Rsp);
			PrintIntRegister("EFL", context.EFlags);
			this->NewLine(); this->NewLine(); regs_in_line = 0;
			PrintSegRegister("CS", context.SegCs);
			PrintSegRegister("SS", context.SegSs);
#endif
		}

		this->NewLine(); regs_in_line = 0;

		// Print segment registers
		if (context.ContextFlags & CONTEXT_SEGMENTS)
		{
			PrintSegRegister("GS", context.SegGs);
			PrintSegRegister("FS", context.SegFs);
			this->NewLine(); regs_in_line = 0;
			PrintSegRegister("ES", context.SegEs);
			PrintSegRegister("DS", context.SegDs);
		}

		this->NewLine(); this->NewLine(); regs_in_line = 0;

		// Print floating point registers
		if (context.ContextFlags & CONTEXT_FLOATING_POINT)
		{
			for (int i = 0; i < 8; i++)
			{
#if !_M_X64
				auto f = *(M128A*) & (context.FloatSave.RegisterArea[i * 10]);
				PrintFloatRegister("ST", i, LODWORD(f.Low), HIDWORD(f.Low), LODWORD(f.High), HIDWORD(f.High));
#else
				PrintFloatRegister("ST", i,
					LODWORD(context.FltSave.FloatRegisters[i].Low), HIDWORD(context.FltSave.FloatRegisters[i].Low),
					LODWORD(context.FltSave.FloatRegisters[i].High), HIDWORD(context.FltSave.FloatRegisters[i].High));
#endif
			}

			this->NewLine();

			for (int i = 0; i < 16; i++)
			{
#if !_M_X64
				auto f = *(M128A*) & (context.ExtendedRegisters[(i + 10) * 16]);
				PrintFloatRegister("XMM", i, LODWORD(f.Low), HIDWORD(f.Low), LODWORD(f.High), HIDWORD(f.High));

				if (i >= 7)
					break;
#else
				PrintFloatRegister("XMM", i,
					LODWORD(context.FltSave.XmmRegisters[i].Low), HIDWORD(context.FltSave.XmmRegisters[i].Low),
					LODWORD(context.FltSave.XmmRegisters[i].High), HIDWORD(context.FltSave.XmmRegisters[i].High));
#endif
			}
		}
	}
	LeaveScope();
}

/*
 *  PrintStackdump
 *      Prints the content of the stack into the logging buffer
 */
void ExceptionTracer::PrintStackdump()
{
	// We need the ESP of the exception context to execute a stack dump, make sure we have access to it
	if ((context.ContextFlags & CONTEXT_CONTROL) == 0)
		return;

	static const auto align = sizeof_word;      // Stack aligment
	static const auto max_words_in_line_magic = stackdump_words_per_line + 10;

	MEMORY_BASIC_INFORMATION mbi;
#if !_M_X64
	uintptr_t base, bottom, top = (uintptr_t)context.Esp;
#else
	uintptr_t base, bottom, top = (uintptr_t)context.Rsp;
#endif
	auto words_in_line = max_words_in_line_magic;

	// Finds the bottom of the stack from it's base pointer
	// Note: mbi will get overriden on this function
	auto GetStackBottom = [&mbi](uintptr_t base)
	{
		VirtualQuery((void*)base, &mbi, sizeof(mbi));                               // Find uncommited region of the stack
		VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));   // Find guard page
		VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));   // Find commited region of the stack
		auto last = (uintptr_t)mbi.BaseAddress;
		return (base + (last - base) + mbi.RegionSize);                             // base + distanceToLastRegion + lastRegionSize
	};

	// Prints an CPU word at the specified stack address
	auto PrintWord = [this, &words_in_line](uintptr_t addr)
	{
		if (words_in_line++ >= stackdump_words_per_line)
		{
			// Print new line only if it's not the first time we enter here (i.e. words_in_line has magical value)
			if (words_in_line != max_words_in_line_magic + 1) NewLine();
			words_in_line = 1;
			Print("0x%p: ", addr);
		}
		Print(" %p", *(size_t*)addr);
	};

	Print("Stack dump:");
	EnterScope();
	{
		// Makes sure the pointer at top (ESP) is valid and readable memory
		if (VirtualQuery((void*)(top), &mbi, sizeof(mbi))
			&& (mbi.State & MEM_COMMIT)
			&& (mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_READWRITE | PAGE_READONLY)) != 0)
		{
			base = (uintptr_t)mbi.AllocationBase;          // Base of the stack (uncommited)
			bottom = GetStackBottom(base);                 // Bottom of the stack (commited)

			// Align the stack top (esp) in a 4 bytes boundary
			auto remainder = top % align;
			uintptr_t current = remainder ? top + (align - remainder) : top;

			// on x86 stack grows downward! (i.e. from bottom to base)
			for (int n = 0; n < stackdump_max_words && current < bottom; ++n, current += align)
				PrintWord(current);

			NewLine();
			Print("base: 0x%p   top: 0x%p   bottom: 0x%p", base, top, bottom);
			NewLine();
		}
	}
	LeaveScope();
}

/*
 *  PrintBacktrace
 *      Prints a call backtrace into the logging buffer
 */
void ExceptionTracer::PrintBacktrace()
{
	StackTracer tracer(this->context);

	char module_name[MAX_PATH];
	char sym_buffer[sizeof(SYMBOL_INFO) + symbol_max];

	int backtrace_count = 0;        // Num of frames traced
	bool has_symbol_api = false;    // True if we have the symbol API available for use
	DWORD old_options;              // Saves old symbol API options

	SYMBOL_INFO& symbol = *(SYMBOL_INFO*)sym_buffer;
	symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
	symbol.MaxNameLen = symbol_max;

	// Tries to get the symbol api
	if (SymInitialize(GetCurrentProcess(), 0, TRUE))
	{
		has_symbol_api = true;
		old_options = SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_NO_PROMPTS | SYMOPT_FAIL_CRITICAL_ERRORS);
	}

	Print("Backtrace (may be wrong):");
	EnterScope();
	{
		// Walks on the stack until there's no frame to trace or we traced 'max_backtrace' frames
		while (auto trace = tracer.Walk())
		{
			if (++backtrace_count >= max_backtrace)
				break;

			bool has_sym = false;   // This EIP has a symbol associated with it?
			DWORD64 displacement;   // EIP displacement relative to symbol

			// If we have access to the symbol api, try to get symbol name from pc (eip)
			if (has_symbol_api)
				has_sym = trace->pc ? !!SymFromAddr(GetCurrentProcess(), (DWORD64)trace->pc, &displacement, &symbol) : false;

			// Print everything up, this.... Ew, this looks awful!
			Print(backtrace_count == 1 ? "=>" : "  ");                          // First line should have '=>' to specify where it crashed
			Print("0x%p ", trace->pc);                                          // Print EIP at frame
			if (has_sym) Print("%s+0x%x ", symbol.Name, (DWORD)displacement);   // Print frame func symbol
			Print("in %s (+0x%x) ",                                             // Print module
				trace->module ? FindModuleName(trace->module, module_name, sizeof(module_name)) : "unknown",
				(uintptr_t)(trace->pc) - (uintptr_t)(trace->module) // Module displacement
			);
			if (trace->frame) Print("(0x%p) ", trace->frame);                   // Print frame pointer

			NewLine();
		}
	}
	LeaveScope();

	// Cleanup the symbol api
	if (has_symbol_api)
	{
		SymSetOptions(old_options);
		SymCleanup(GetCurrentProcess());
	}
}

/*
 * GetExceptionCodeString
 *      Returns an description by an exception code
 */
static const char* GetExceptionCodeString(unsigned int code)
{
	switch (code)
	{
	case EXCEPTION_ACCESS_VIOLATION:         return "Access violation";
	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:    return "Array bounds exceeded";
	case EXCEPTION_BREAKPOINT:               return "Breakpoint exception";
	case EXCEPTION_DATATYPE_MISALIGNMENT:    return "Data type misalignment exception";
	case EXCEPTION_FLT_DENORMAL_OPERAND:     return "Denormal float operand";
	case EXCEPTION_FLT_DIVIDE_BY_ZERO:       return "Floating-point division by zero";
	case EXCEPTION_FLT_INEXACT_RESULT:       return "Floating-point inexact result";
	case EXCEPTION_FLT_INVALID_OPERATION:    return "Floating-point invalid operation";
	case EXCEPTION_FLT_OVERFLOW:             return "Floating-point overflow";
	case EXCEPTION_FLT_STACK_CHECK:          return "Floating-point stack check";
	case EXCEPTION_FLT_UNDERFLOW:            return "Floating-point underflow";
	case EXCEPTION_ILLEGAL_INSTRUCTION:      return "Illegal instruction.";
	case EXCEPTION_IN_PAGE_ERROR:            return "In page error";
	case EXCEPTION_INT_DIVIDE_BY_ZERO:       return "Integer division by zero";
	case EXCEPTION_INT_OVERFLOW:             return "Integer overflow";
	case EXCEPTION_INVALID_DISPOSITION:      return "Invalid disposition";
	case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Non-continuable exception";
	case EXCEPTION_PRIV_INSTRUCTION:         return "Privileged instruction";
	case EXCEPTION_SINGLE_STEP:              return "Single step exception";
	case EXCEPTION_STACK_OVERFLOW:           return "Stack overflow";
	default:                                 return "NO_DESCRIPTION";
	}
}

/*
 * FindModuleName
 *      Finds module filename or "unknown"
 */
static const char* FindModuleName(HMODULE module, char* output, DWORD maxsize)
{
	if (GetModuleFileNameA(module, output, maxsize))
	{
		// Finds the filename part in the output string
		char* filename = strrchr(output, '\\');
		if (!filename) filename = strrchr(output, '/');

		// If filename found (i.e. output isn't already a filename but full path), make output be filename
		if (filename)
		{
			size_t size = strlen(++filename);
			memmove(output, filename, size);
			output[size] = 0;
		}
	}
	else
	{
		// Unknown module
		strcpy(output, "unknown");
	}
	return output;
}

/*
 * GetModuleFromAddress
 *      Finds module handle from some address inside it
 */
static HMODULE GetModuleFromAddress(LPVOID address)
{
	HMODULE module;
	if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
		(char*)address, &module))
		return module;
	return nullptr;
}

/*
*  StackTracer
*      Constructs the tracer, we basically need to initialize the symbol api
*/
StackTracer::StackTracer(const CONTEXT& context)
{
	// Initialise basic values
	memset(&this->frame, 0, sizeof(frame));
	memcpy(&this->context, &context, sizeof(context));

	// Setup the initial frame context
#if !_M_X64
	frame.AddrPC.Mode = AddrModeFlat;
	frame.AddrPC.Offset = context.Eip;
	frame.AddrFrame.Mode = AddrModeFlat;
	frame.AddrFrame.Offset = context.Ebp;
	frame.AddrStack.Mode = AddrModeFlat;
	frame.AddrStack.Offset = context.Esp;
#else
	frame.AddrPC.Mode = AddrModeFlat;
	frame.AddrPC.Offset = context.Rip;
	frame.AddrFrame.Mode = AddrModeFlat;
	frame.AddrFrame.Offset = context.Rbp;
	frame.AddrStack.Mode = AddrModeFlat;
	frame.AddrStack.Offset = context.Rsp;
#endif
}

/*
 *   StackTracer::Walk
 *      Walks on the stack, each walk is one frame of backtrace
 *      Returns a frame or null if the walk on the park is not possible anymore
 */
StackTracer::Trace* StackTracer::Walk()
{
	if (StackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(),
		&frame, &context, NULL, NULL, NULL, NULL))
	{
		trace.module = GetModuleFromAddress((void*)frame.AddrPC.Offset);
		trace.frame = (void*)frame.AddrFrame.Offset;
		trace.stack = (void*)frame.AddrStack.Offset;
		trace.pc = (void*)frame.AddrPC.Offset;
		trace.ret = (void*)frame.AddrReturn.Offset;
		return &trace;
	}
	return nullptr;
}


code:
#pragma once

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
#pragma once
#include <string>
#include <shlobj.h>
#include <set>
#include "ModuleList.h"
#include <intrin.h>
#pragma intrinsic(_ReturnAddress)

struct shared
{
	FARPROC DllCanUnloadNow;
	FARPROC DllGetClassObject;
	FARPROC DllRegisterServer;
	FARPROC DllUnregisterServer;
	FARPROC DebugSetMute;

	void LoadOriginalLibrary(HMODULE dll)
	{
		DllCanUnloadNow = GetProcAddress(dll, "DllCanUnloadNow");
		DllGetClassObject = GetProcAddress(dll, "DllGetClassObject");
		DllRegisterServer = GetProcAddress(dll, "DllRegisterServer");
		DllUnregisterServer = GetProcAddress(dll, "DllUnregisterServer");
		DebugSetMute = GetProcAddress(dll, "DebugSetMute");
	}
} shared;

struct dnsapi_dll
{
	HMODULE dll;

	// only some functions are implemented.
	// PDAFT doesn't use many, so this should hopefully be fine
	FARPROC DnsFree;
	FARPROC DnsQuery_A;
	FARPROC DnsQueryEx;
	FARPROC DnsCancelQuery;
	FARPROC DnsDhcpRegisterTerm;
	FARPROC DnsNotifyResolver;
	FARPROC DnsDhcpRegisterHostAddrs;
	FARPROC DnsQueryConfigDword;
	FARPROC DnsDhcpRemoveRegistrations;
	FARPROC DnsDhcpRegisterInit;
	FARPROC DnsDhcpRegisterAddrs;

	// DnsQueryEx and DnsCancelQuery take pointers to structs as parameters
	// (three for DnsQueryEx and one for DnsCancelQuery)
	// fortunately they should fit in registers so the stack doesn't matter
	// hopefully this works fine... I have no clue what I'm doing
	static LONG WINAPI DnsQueryExStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}
	static LONG WINAPI DnsCancelQueryStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}
	/*static LONG WINAPI DnsDhcpRegisterTermStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}
	static LONG WINAPI DnsNotifyResolverStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}
	static LONG WINAPI DnsDhcpRegisterHostAddrsStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}
	static LONG WINAPI DnsQueryConfigDwordStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}
	static LONG WINAPI DnsDhcpRemoveRegistrationsStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}
	static LONG WINAPI DnsDhcpRegisterInitStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}
	static LONG WINAPI DnsDhcpRegisterAddrsStub()
	{
		return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED
	}*/

	void LoadOriginalLibrary(HMODULE module)
	{
		dll = module;
		shared.LoadOriginalLibrary(dll);
		DnsFree = GetProcAddress(dll, "DnsFree");
		DnsQuery_A = GetProcAddress(dll, "DnsQuery_A");
		DnsQueryEx = GetProcAddress(dll, "DnsQueryEx");
		DnsCancelQuery = GetProcAddress(dll, "DnsCancelQuery");
		DnsDhcpRegisterTerm = GetProcAddress(dll, "DnsDhcpRegisterTerm");
		DnsNotifyResolver = GetProcAddress(dll, "DnsNotifyResolver");
		DnsDhcpRegisterHostAddrs = GetProcAddress(dll, "DnsDhcpRegisterHostAddrs");
		DnsQueryConfigDword = GetProcAddress(dll, "DnsQueryConfigDword");
		DnsDhcpRemoveRegistrations = GetProcAddress(dll, "DnsDhcpRemoveRegistrations");
		DnsDhcpRegisterInit = GetProcAddress(dll, "DnsDhcpRegisterInit");
		DnsDhcpRegisterAddrs = GetProcAddress(dll, "DnsDhcpRegisterAddrs");

		// if entry points aren't found, GetProcAddress should return null
		// I hope this is correct
		// Thanks to somewhatlurker
		if (DnsQueryEx == NULL) { DnsQueryEx = (FARPROC)& DnsQueryExStub; };
		if (DnsCancelQuery == NULL) { DnsCancelQuery = (FARPROC)& DnsCancelQueryStub; };
		/*if (DnsDhcpRegisterTerm == NULL) { DnsDhcpRegisterTerm = (FARPROC)& DnsDhcpRegisterTermStub; };
		if (DnsNotifyResolver == NULL) { DnsNotifyResolver = (FARPROC)& DnsNotifyResolverStub; };
		if (DnsDhcpRegisterHostAddrs == NULL) { DnsDhcpRegisterHostAddrs = (FARPROC)& DnsDhcpRegisterHostAddrsStub; };
		if (DnsQueryConfigDword == NULL) { DnsQueryConfigDword = (FARPROC)& DnsQueryConfigDwordStub; };
		if (DnsDhcpRemoveRegistrations == NULL) { DnsDhcpRemoveRegistrations = (FARPROC)& DnsDhcpRemoveRegistrationsStub; };
		if (DnsDhcpRegisterInit == NULL) { DnsDhcpRegisterInit = (FARPROC)& DnsDhcpRegisterInitStub; };
		if (DnsDhcpRegisterAddrs == NULL) { DnsDhcpRegisterAddrs = (FARPROC)& DnsDhcpRegisterAddrsStub; };*/
	}
} dnsapi;

void _DnsFree() { dnsapi.DnsFree(); }
void _DnsQuery_A() { dnsapi.DnsQuery_A(); }
void _DnsQueryEx() { dnsapi.DnsQueryEx(); }
void _DnsCancelQuery() { dnsapi.DnsCancelQuery(); }
void _DnsDhcpRegisterTerm() { dnsapi.DnsDhcpRegisterTerm(); }
void _DnsNotifyResolver() { dnsapi.DnsNotifyResolver(); }
void _DnsDhcpRegisterHostAddrs() { dnsapi.DnsDhcpRegisterHostAddrs(); }
void _DnsQueryConfigDword() { dnsapi.DnsQueryConfigDword(); }
void _DnsDhcpRemoveRegistrations() { dnsapi.DnsDhcpRemoveRegistrations(); }
void _DnsDhcpRegisterInit() { dnsapi.DnsDhcpRegisterInit(); }
void _DnsDhcpRegisterAddrs() { dnsapi.DnsDhcpRegisterAddrs(); }

#pragma runtime_checks( "", off )

#ifdef _DEBUG
#pragma message ("You are compiling the code in Debug - be warned that wrappers for export functions may not have correct code generated")
#endif

void _DllRegisterServer() { shared.DllRegisterServer(); }
void _DllUnregisterServer() { shared.DllUnregisterServer(); }
void _DllCanUnloadNow() { shared.DllCanUnloadNow(); }
void _DllGetClassObject() { shared.DllGetClassObject(); }

#pragma runtime_checks( "", restore )


code:
LIBRARY "dnsapi"
EXPORTS
DnsFree = _DnsFree
DnsQuery_A = _DnsQuery_A
DnsQueryEx = _DnsQueryEx
DnsCancelQuery = _DnsCancelQuery
DnsDhcpRegisterTerm = _DnsDhcpRegisterTerm
DnsNotifyResolver = _DnsNotifyResolver
DnsDhcpRegisterHostAddrs = _DnsDhcpRegisterHostAddrs
DnsQueryConfigDword = _DnsQueryConfigDword
DnsDhcpRemoveRegistrations = _DnsDhcpRemoveRegistrations
DnsDhcpRegisterInit = _DnsDhcpRegisterInit
DnsDhcpRegisterAddrs = _DnsDhcpRegisterAddrs
DllCanUnloadNow = _DllCanUnloadNow PRIVATE
DllGetClassObject = _DllGetClassObject PRIVATE
DllRegisterServer = _DllRegisterServer PRIVATE
DllUnregisterServer = _DllUnregisterServer PRIVATE


code:
#pragma once

#include <vector>
#include <algorithm>
#include <cassert>
#include <string>
#include <tuple>

std::wstring GetModuleFileNameW(HMODULE hModule)
{
	static constexpr auto INITIAL_BUFFER_SIZE = MAX_PATH;
	static constexpr auto MAX_ITERATIONS = 7;
	std::wstring ret;
	auto bufferSize = INITIAL_BUFFER_SIZE;
	for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations)
	{
		ret.resize(bufferSize);
		auto charsReturned = GetModuleFileNameW(hModule, &ret[0], bufferSize);
		if (charsReturned < ret.length())
		{
			ret.resize(charsReturned);
			return ret;
		}
		else
		{
			bufferSize *= 2;
		}
	}
	return L"";
}

auto starts_with = [](const std::wstring& big_str, const std::wstring& small_str) -> auto
{
	return big_str.compare(0, small_str.length(), small_str) == 0;
};

// Stores a list of loaded modules with their names, WITHOUT extension
class ModuleList
{
public:
	enum class SearchLocation
	{
		All,
		LocalOnly,
		SystemOnly,
	};

	// Initializes module list
	// Needs to be called before any calls to Get or GetAll
	void Enumerate(SearchLocation location = SearchLocation::All)
	{
		constexpr size_t INITIAL_SIZE = sizeof(HMODULE) * 256;
		HMODULE* modules = static_cast<HMODULE*>(malloc(INITIAL_SIZE));
		if (modules != nullptr)
		{
			typedef BOOL(WINAPI * Func)(HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded);

			HMODULE hLib = LoadLibrary(TEXT("kernel32"));
			assert(hLib != nullptr); // If this fails then everything is probably broken anyway

			Func pEnumProcessModules = reinterpret_cast<Func>(GetProcAddress(hLib, "K32EnumProcessModules"));
			if (pEnumProcessModules == nullptr)
			{
				// Try psapi
				FreeLibrary(hLib);
				hLib = LoadLibrary(TEXT("psapi"));
				if (hLib != nullptr)
				{
					pEnumProcessModules = reinterpret_cast<Func>(GetProcAddress(hLib, "EnumProcessModules"));
				}
			}

			if (pEnumProcessModules != nullptr)
			{
				const HANDLE currentProcess = GetCurrentProcess();
				DWORD cbNeeded = 0;
				if (pEnumProcessModules(currentProcess, modules, INITIAL_SIZE, &cbNeeded) != 0)
				{
					if (cbNeeded > INITIAL_SIZE)
					{
						HMODULE* newModules = static_cast<HMODULE*>(realloc(modules, cbNeeded));
						if (newModules != nullptr)
						{
							modules = newModules;

							if (pEnumProcessModules(currentProcess, modules, cbNeeded, &cbNeeded) != 0)
							{
								EnumerateInternal(modules, location, cbNeeded / sizeof(HMODULE));
							}
						}
					}
					else
					{
						EnumerateInternal(modules, location, cbNeeded / sizeof(HMODULE));
					}
				}
			}

			if (hLib != nullptr)
			{
				FreeLibrary(hLib);
			}

			free(modules);
		}
	}

	// Recreates module list
	void ReEnumerate(SearchLocation location = SearchLocation::All)
	{
		Clear();
		Enumerate(location);
	}

	// Clears module list
	void Clear()
	{
		m_moduleList.clear();
	}

	// Gets handle of a loaded module with given name, NULL otherwise
	HMODULE Get(const wchar_t* moduleName) const
	{
		// If vector is empty then we're trying to call it without calling Enumerate first
		assert(m_moduleList.size() != 0);

		auto it = std::find_if(m_moduleList.begin(), m_moduleList.end(), [&](const auto& e) {
			return _wcsicmp(moduleName, std::get<1>(e).c_str()) == 0;
		});
		return it != m_moduleList.end() ? std::get<0>(*it) : nullptr;
	}

	// Gets handles to all loaded modules with given name
	std::vector<HMODULE> GetAll(const wchar_t* moduleName) const
	{
		// If vector is empty then we're trying to call it without calling Enumerate first
		assert(m_moduleList.size() != 0);

		std::vector<HMODULE> results;
		for (auto& e : m_moduleList)
		{
			if (_wcsicmp(moduleName, std::get<1>(e).c_str()) == 0)
			{
				results.push_back(std::get<0>(e));
			}
		}

		return results;
	}

private:
	void EnumerateInternal(HMODULE* modules, SearchLocation location, size_t numModules)
	{
		const auto exeModulePath = GetModuleFileNameW(NULL).substr(0, GetModuleFileNameW(NULL).find_last_of(L"/\\"));

		m_moduleList.reserve(numModules);
		for (size_t i = 0; i < numModules; i++)
		{
			// Obtain module name, with resizing if necessary
			auto moduleName = GetModuleFileNameW(*modules);

			if (!moduleName.empty())
			{
				const wchar_t* nameBegin = wcsrchr(moduleName.c_str(), '\\') + 1;
				const wchar_t* dotPos = wcsrchr(nameBegin, '.');
				bool isLocal = starts_with(std::wstring(moduleName), exeModulePath);

				if ((isLocal && location != SearchLocation::SystemOnly) || (!isLocal && location != SearchLocation::LocalOnly))
				{
					if (dotPos != nullptr)
					{
						m_moduleList.emplace_back(*modules, std::wstring(nameBegin, dotPos), isLocal);
					}
					else
					{
						m_moduleList.emplace_back(*modules, nameBegin, isLocal);
					}
				}
			}

			modules++;
		}
	}

public: std::vector< std::tuple<HMODULE, std::wstring, bool> > m_moduleList;
};


code:
#include "framework.h"
#include "exception.hpp"

HMODULE hm;
std::vector<std::wstring> iniPaths;

bool iequals(std::wstring_view s1, std::wstring_view s2)
{
	std::wstring str1(std::move(s1));
	std::wstring str2(std::move(s2));
	std::transform(str1.begin(), str1.end(), str1.begin(), [](wchar_t c) { return ::towlower(c); });
	std::transform(str2.begin(), str2.end(), str2.begin(), [](wchar_t c) { return ::towlower(c); });
	return (str1 == str2);
}

std::wstring to_wstring(std::string_view cstr)
{
	std::string str(std::move(cstr));
	auto charsReturned = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
	std::wstring wstrTo(charsReturned, 0);
	MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], charsReturned);
	return wstrTo;
}

std::wstring SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken)
{
	std::wstring r;
	WCHAR* szSystemPath = nullptr;
	if (SUCCEEDED(SHGetKnownFolderPath(rfid, dwFlags, hToken, &szSystemPath)))
	{
		r = szSystemPath;
	}
	CoTaskMemFree(szSystemPath);
	return r;
};

HMODULE LoadLibraryW(const std::wstring& lpLibFileName)
{
	return LoadLibraryW(lpLibFileName.c_str());
}

std::wstring GetCurrentDirectoryW()
{
	static constexpr auto INITIAL_BUFFER_SIZE = MAX_PATH;
	static constexpr auto MAX_ITERATIONS = 7;
	std::wstring ret;
	auto bufferSize = INITIAL_BUFFER_SIZE;
	for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations)
	{
		ret.resize(bufferSize);
		auto charsReturned = GetCurrentDirectoryW(bufferSize, &ret[0]);
		if (charsReturned < ret.length())
		{
			ret.resize(charsReturned);
			return ret;
		}
		else
		{
			bufferSize *= 2;
		}
	}
	return L"";
}

UINT GetPrivateProfileIntW(LPCWSTR lpAppName, LPCWSTR lpKeyName, INT nDefault, const std::vector<std::wstring>& fileNames)
{
	for (const auto& file : fileNames)
	{
		nDefault = GetPrivateProfileIntW(lpAppName, lpKeyName, nDefault, file.c_str());
	}
	return nDefault;
}

std::wstring GetSelfName()
{
	const std::wstring moduleFileName = GetModuleFileNameW(hm);
	return moduleFileName.substr(moduleFileName.find_last_of(L"/\\") + 1);
}

template<typename T, typename... Args>
void GetSections(T&& h, Args... args)
{
	const std::set< std::string_view, std::less<> > s = { args... };
	size_t dwLoadOffset = (size_t)GetModuleHandle(NULL);
	BYTE* pImageBase = reinterpret_cast<BYTE*>(dwLoadOffset);
	PIMAGE_DOS_HEADER   pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(dwLoadOffset);
	PIMAGE_NT_HEADERS   pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(pImageBase + pDosHeader->e_lfanew);
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHeader);
	for (int iSection = 0; iSection < pNtHeader->FileHeader.NumberOfSections; ++iSection, ++pSection)
	{
		auto pszSectionName = reinterpret_cast<const char*>(pSection->Name);
		if (s.find(pszSectionName) != s.end())
		{
			DWORD dwPhysSize = (pSection->Misc.VirtualSize + 4095) & ~4095;
			std::forward<T>(h)(pSection, dwLoadOffset, dwPhysSize);
		}
	}
}

enum Kernel32ExportsNames
{
	eGetStartupInfoA,
	eGetStartupInfoW,
	eGetModuleHandleA,
	eGetModuleHandleW,
	eGetProcAddress,
	eGetShortPathNameA,
	eFindNextFileA,
	eFindNextFileW,
	eLoadLibraryA,
	eLoadLibraryW,
	eFreeLibrary,
	eCreateEventA,
	eCreateEventW,
	eGetSystemInfo,
	eInterlockedCompareExchange,
	eSleep,

	Kernel32ExportsNamesCount
};

enum Kernel32ExportsData
{
	IATPtr,
	ProcAddress,

	Kernel32ExportsDataCount
};

size_t Kernel32Data[Kernel32ExportsNamesCount][Kernel32ExportsDataCount];

static LONG OriginalLibraryLoaded = 0;
void LoadOriginalLibrary()
{
	if (_InterlockedCompareExchange(&OriginalLibraryLoaded, 1, 0) != 0) return;

	auto szSelfName = GetSelfName();
	auto szSystemPath = SHGetKnownFolderPath(FOLDERID_System, 0, nullptr) + L'\\' + szSelfName;
	auto szLocalPath = GetModuleFileNameW(hm); szLocalPath = szLocalPath.substr(0, szLocalPath.find_last_of(L"/\\") + 1);

	if (iequals(szSelfName, L"dnsapi.dll")) {
		dnsapi.LoadOriginalLibrary(LoadLibraryW(szSystemPath));
	}
}

void FindFiles(WIN32_FIND_DATAW* fd)
{
	auto dir = GetCurrentDirectoryW();

	HANDLE dvaFile = FindFirstFileW(L"*.dva", fd);
	if (dvaFile != INVALID_HANDLE_VALUE)
	{
		do {
			if (!(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			{
				auto pos = wcslen(fd->cFileName);

				if (fd->cFileName[pos - 4] == '.' &&
					(fd->cFileName[pos - 3] == 'd' || fd->cFileName[pos - 3] == 'D') &&
					(fd->cFileName[pos - 2] == 'v' || fd->cFileName[pos - 2] == 'V') &&
					(fd->cFileName[pos - 1] == 'a' || fd->cFileName[pos - 1] == 'A'))
				{
					auto path = dir + L'\\' + fd->cFileName;

					if (GetModuleHandle(path.c_str()) == NULL)
					{
						auto h = LoadLibraryW(path);
						SetCurrentDirectoryW(dir.c_str()); //in case dva switched it

						if (h == NULL)
						{
							auto e = GetLastError();
							if (e != ERROR_DLL_INIT_FAILED) // in case dllmain returns false
							{
								std::wstring msg = L"Unable to load " + std::wstring(fd->cFileName) + L". Error: " + std::to_wstring(e);
								MessageBoxW(0, msg.c_str(), L"PD Loader", MB_ICONERROR);
							}
						}
						else
						{
							auto procedure = (void(*)())GetProcAddress(h, "InitializeDVA");

							if (procedure != NULL)
							{
								procedure();
							}
						}
					}
				}
			}
		} while (FindNextFileW(dvaFile, fd));
		FindClose(dvaFile);
	}
}

void LoadPlugins()
{
	auto oldDir = GetCurrentDirectoryW(); // store the current directory

	auto szSelfPath = GetModuleFileNameW(hm).substr(0, GetModuleFileNameW(hm).find_last_of(L"/\\") + 1);
	SetCurrentDirectoryW(szSelfPath.c_str());

	auto nWantsToLoadPlugins = GetPrivateProfileIntW(L"global", L"enable", TRUE, iniPaths);

	if (nWantsToLoadPlugins)
	{
		WIN32_FIND_DATAW fd;

		SetCurrentDirectoryW(szSelfPath.c_str());

		if (SetCurrentDirectoryW(L"plugins\\"))
			FindFiles(&fd);
	}

	SetCurrentDirectoryW(oldDir.c_str()); // Reset the current directory
}

void InjectCode(void* address, const std::vector<uint8_t> data)
{
	const size_t byteCount = data.size() * sizeof(uint8_t);

	DWORD oldProtect;
	VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect);
	memcpy(address, data.data(), byteCount);
	VirtualProtect(address, byteCount, oldProtect, nullptr);
}

static LONG LoadedPluginsYet = 0;
void LoadEverything()
{
	if (_InterlockedCompareExchange(&LoadedPluginsYet, 1, 0) != 0) return;

	LoadOriginalLibrary();
	//LoadPlugins();
	InjectCode((void*)0x00000001409FF58E, { 0x52, 0x54, 0x46, 0x4D });
	InjectCode((void*)0x00000001409FF730, { 0x53, 0x65, 0x65, 0x20, 0x52, 0x45, 0x41, 0x44, 0x4D, 0x45, 0x2E, 0x74, 0x78, 0x74, 0x2E, 0x00, 0x00 });
}

static LONG RestoredOnce = 0;
void LoadPluginsAndRestoreIAT(uintptr_t retaddr)
{
	bool calledFromBind = false;

	//steam drm check
	GetSections([&](PIMAGE_SECTION_HEADER pSection, size_t dwLoadOffset, DWORD dwPhysSize) {
		auto dwStart = static_cast<uintptr_t>(dwLoadOffset + pSection->VirtualAddress);
		auto dwEnd = dwStart + dwPhysSize;
		if (retaddr >= dwStart && retaddr <= dwEnd)
			calledFromBind = true;
	}, ".bind");

	if (calledFromBind) return;

	if (_InterlockedCompareExchange(&RestoredOnce, 1, 0) != 0) return;

	LoadEverything();

	for (size_t i = 0; i < Kernel32ExportsNamesCount; i++)
	{
		if (Kernel32Data[i][IATPtr] && Kernel32Data[i][ProcAddress])
		{
			auto ptr = (size_t*)Kernel32Data[i][IATPtr];
			DWORD dwProtect[2];
			VirtualProtect(ptr, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]);
			*ptr = Kernel32Data[i][ProcAddress];
			VirtualProtect(ptr, sizeof(size_t), dwProtect[0], &dwProtect[1]);
		}
	}
}

void WINAPI CustomGetStartupInfoA(LPSTARTUPINFOA lpStartupInfo)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetStartupInfoA(lpStartupInfo);
}

void WINAPI CustomGetStartupInfoW(LPSTARTUPINFOW lpStartupInfo)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetStartupInfoW(lpStartupInfo);
}

HMODULE WINAPI CustomGetModuleHandleA(LPCSTR lpModuleName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetModuleHandleA(lpModuleName);
}

HMODULE WINAPI CustomGetModuleHandleW(LPCWSTR lpModuleName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetModuleHandleW(lpModuleName);
}

FARPROC WINAPI CustomGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetProcAddress(hModule, lpProcName);
}

DWORD WINAPI CustomGetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetShortPathNameA(lpszLongPath, lpszShortPath, cchBuffer);
}

BOOL WINAPI CustomFindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return FindNextFileA(hFindFile, lpFindFileData);
}

BOOL WINAPI CustomFindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return FindNextFileW(hFindFile, lpFindFileData);
}

HMODULE WINAPI CustomLoadLibraryA(LPCSTR lpLibFileName)
{
	LoadOriginalLibrary();

	return LoadLibraryA(lpLibFileName);
}

HMODULE WINAPI CustomLoadLibraryW(LPCWSTR lpLibFileName)
{
	LoadOriginalLibrary();

	return LoadLibraryW(lpLibFileName);
}

BOOL WINAPI CustomFreeLibrary(HMODULE hLibModule)
{
	if (hLibModule != hm)
		return FreeLibrary(hLibModule);
	else
		return !NULL;
}

HANDLE WINAPI CustomCreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return CreateEventA(lpEventAttributes, bManualReset, bInitialState, lpName);
}

HANDLE WINAPI CustomCreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName);
}

void WINAPI CustomGetSystemInfo(LPSYSTEM_INFO lpSystemInfo)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return GetSystemInfo(lpSystemInfo);
}

LONG WINAPI CustomInterlockedCompareExchange(LONG volatile* Destination, LONG ExChange, LONG Comperand)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return _InterlockedCompareExchange(Destination, ExChange, Comperand);
}

void WINAPI CustomSleep(DWORD dwMilliseconds)
{
	LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress());
	return Sleep(dwMilliseconds);
}

bool HookKernel32IAT(HMODULE mod, bool exe)
{
	auto hExecutableInstance = (size_t)mod;
	IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(hExecutableInstance + ((IMAGE_DOS_HEADER*)hExecutableInstance)->e_lfanew);
	IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(hExecutableInstance + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
	size_t                      nNumImports = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;

	if (exe)
	{
		Kernel32Data[eGetStartupInfoA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetStartupInfoA");
		Kernel32Data[eGetStartupInfoW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetStartupInfoW");
		Kernel32Data[eGetModuleHandleA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetModuleHandleA");
		Kernel32Data[eGetModuleHandleW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetModuleHandleW");
		Kernel32Data[eGetProcAddress][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetProcAddress");
		Kernel32Data[eGetShortPathNameA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetShortPathNameA");
		Kernel32Data[eFindNextFileA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FindNextFileA");
		Kernel32Data[eFindNextFileW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FindNextFileW");
		Kernel32Data[eLoadLibraryA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "LoadLibraryA");
		Kernel32Data[eLoadLibraryW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "LoadLibraryW");
		Kernel32Data[eFreeLibrary][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FreeLibrary");
		Kernel32Data[eCreateEventA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "CreateEventA");
		Kernel32Data[eCreateEventW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "CreateEventW");
		Kernel32Data[eGetSystemInfo][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetSystemInfo");
		Kernel32Data[eInterlockedCompareExchange][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "InterlockedCompareExchange");
		Kernel32Data[eSleep][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "Sleep");
	}

	uint32_t matchedImports = 0;

	auto PatchIAT = [&](size_t start, size_t end, size_t exe_end)
	{
		for (size_t i = 0; i < nNumImports; i++)
		{
			if (hExecutableInstance + (pImports + i)->FirstThunk > start && !(end && hExecutableInstance + (pImports + i)->FirstThunk > end))
				end = hExecutableInstance + (pImports + i)->FirstThunk;
		}

		if (!end) { end = start + 0x100; }
		if (end > exe_end) //for very broken exes
		{
			start = hExecutableInstance;
			end = exe_end;
		}

		for (auto i = start; i < end; i += sizeof(size_t))
		{
			DWORD dwProtect[2];
			VirtualProtect((size_t*)i, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]);

			auto ptr = *(size_t*)i;
			if (!ptr)
				continue;

			if (ptr == Kernel32Data[eGetStartupInfoA][ProcAddress])
			{
				if (exe) Kernel32Data[eGetStartupInfoA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetStartupInfoA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetStartupInfoW][ProcAddress])
			{
				if (exe) Kernel32Data[eGetStartupInfoW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetStartupInfoW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetModuleHandleA][ProcAddress])
			{
				if (exe) Kernel32Data[eGetModuleHandleA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetModuleHandleA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetModuleHandleW][ProcAddress])
			{
				if (exe) Kernel32Data[eGetModuleHandleW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetModuleHandleW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetProcAddress][ProcAddress])
			{
				if (exe) Kernel32Data[eGetProcAddress][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetProcAddress;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetShortPathNameA][ProcAddress])
			{
				if (exe) Kernel32Data[eGetShortPathNameA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetShortPathNameA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eFindNextFileA][ProcAddress])
			{
				if (exe) Kernel32Data[eFindNextFileA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomFindNextFileA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eFindNextFileW][ProcAddress])
			{
				if (exe) Kernel32Data[eFindNextFileW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomFindNextFileW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eLoadLibraryA][ProcAddress])
			{
				if (exe) Kernel32Data[eLoadLibraryA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomLoadLibraryA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eLoadLibraryW][ProcAddress])
			{
				if (exe) Kernel32Data[eLoadLibraryW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomLoadLibraryW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eFreeLibrary][ProcAddress])
			{
				if (exe) Kernel32Data[eFreeLibrary][IATPtr] = i;
				*(size_t*)i = (size_t)CustomFreeLibrary;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eCreateEventA][ProcAddress])
			{
				if (exe) Kernel32Data[eCreateEventA][IATPtr] = i;
				*(size_t*)i = (size_t)CustomCreateEventA;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eCreateEventW][ProcAddress])
			{
				if (exe) Kernel32Data[eCreateEventW][IATPtr] = i;
				*(size_t*)i = (size_t)CustomCreateEventW;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eGetSystemInfo][ProcAddress])
			{
				if (exe) Kernel32Data[eGetSystemInfo][IATPtr] = i;
				*(size_t*)i = (size_t)CustomGetSystemInfo;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eInterlockedCompareExchange][ProcAddress])
			{
				if (exe) Kernel32Data[eInterlockedCompareExchange][IATPtr] = i;
				*(size_t*)i = (size_t)CustomInterlockedCompareExchange;
				matchedImports++;
			}
			else if (ptr == Kernel32Data[eSleep][ProcAddress])
			{
				if (exe) Kernel32Data[eSleep][IATPtr] = i;
				*(size_t*)i = (size_t)CustomSleep;
				matchedImports++;
			}

			VirtualProtect((size_t*)i, sizeof(size_t), dwProtect[0], &dwProtect[1]);
		}
	};

	static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER
	{
		return reinterpret_cast<PIMAGE_SECTION_HEADER>(
			(UCHAR*)nt_headers->OptionalHeader.DataDirectory +
			nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) +
			section * sizeof(IMAGE_SECTION_HEADER));
	};

	static auto getSectionEnd = [](IMAGE_NT_HEADERS* ntHeader, size_t inst) -> auto
	{
		auto sec = getSection(ntHeader, ntHeader->FileHeader.NumberOfSections - 1);
		auto secSize = max(sec->SizeOfRawData, sec->Misc.VirtualSize);
		auto end = inst + max(sec->PointerToRawData, sec->VirtualAddress) + secSize;
		return end;
	};

	auto hExecutableInstance_end = getSectionEnd(ntHeader, hExecutableInstance);

	// Find kernel32.dll
	for (size_t i = 0; i < nNumImports; i++)
	{
		if ((size_t)(hExecutableInstance + (pImports + i)->Name) < hExecutableInstance_end)
		{
			if (!_stricmp((const char*)(hExecutableInstance + (pImports + i)->Name), "KERNEL32.DLL"))
				PatchIAT(hExecutableInstance + (pImports + i)->FirstThunk, 0, hExecutableInstance_end);
		}
	}

	// Fixing ordinals
	auto szSelfName = GetSelfName();

	static auto PatchOrdinals = [&szSelfName](size_t hInstance)
	{
		IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(hInstance + ((IMAGE_DOS_HEADER*)hInstance)->e_lfanew);
		IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(hInstance + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
		size_t                      nNumImports = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;

		for (size_t i = 0; i < nNumImports; i++)
		{
			if ((size_t)(hInstance + (pImports + i)->Name) < getSectionEnd(ntHeader, (size_t)hInstance))
			{
				if (iequals(szSelfName, (to_wstring((const char*)(hInstance + (pImports + i)->Name)))))
				{
					PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(hInstance + (pImports + i)->OriginalFirstThunk);
					size_t j = 0;
					while (thunk->u1.Function)
					{
						if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
						{
							PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)(hInstance + thunk->u1.AddressOfData);
							void** p = (void**)(hInstance + (pImports + i)->FirstThunk);
						}
						++thunk;
					}
				}
			}
		}
	};

	ModuleList dlls;
	dlls.Enumerate(ModuleList::SearchLocation::LocalOnly);
	for (auto& e : dlls.m_moduleList)
	{
		PatchOrdinals((size_t)std::get<HMODULE>(e));
	}
	return matchedImports > 0;
}

LONG WINAPI CustomUnhandledExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo)
{
	// step 1: write minidump
	wchar_t		modulename[MAX_PATH];
	wchar_t		filename[MAX_PATH];
	wchar_t		timestamp[128];
	__time64_t	time;
	struct tm	ltime;
	HANDLE		hFile;
	HWND		hWnd;

	wchar_t* modulenameptr = NULL;
	if (GetModuleFileNameW(GetModuleHandle(NULL), modulename, _countof(modulename)) != 0)
	{
		modulenameptr = wcsrchr(modulename, '\\');
		*modulenameptr = L'\0';
		modulenameptr += 1;
	}
	else
	{
		*modulenameptr = L'err.err';
	}

	_time64(&time);
	_localtime64_s(&ltime, &time);
	wcsftime(timestamp, _countof(timestamp), L"%Y%m%d%H%M%S", &ltime);
	swprintf_s(filename, L"%s\\%s\\%s.%s.dmp", modulename, L"logs", modulenameptr, timestamp);

	hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile != INVALID_HANDLE_VALUE)
	{
		MINIDUMP_EXCEPTION_INFORMATION ex;
		memset(&ex, 0, sizeof(ex));
		ex.ThreadId = GetCurrentThreadId();
		ex.ExceptionPointers = ExceptionInfo;
		ex.ClientPointers = TRUE;

		if (FAILED(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithDataSegs, &ex, NULL, NULL)))
		{
		}

		CloseHandle(hFile);
	}

	// step 2: write log
	// Logs exception into buffer and writes to file
	swprintf_s(filename, L"%s\\%s\\%s.%s.log", modulename, L"logs", modulenameptr, timestamp);
	hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile != INVALID_HANDLE_VALUE)
	{
		auto Log = [ExceptionInfo, hFile](char* buffer, size_t size, bool reg, bool stack, bool trace)
		{
			if (LogException(buffer, size, (LPEXCEPTION_POINTERS)ExceptionInfo, reg, stack, trace))
			{
				DWORD NumberOfBytesWritten = 0;
				WriteFile(hFile, buffer, strlen(buffer), &NumberOfBytesWritten, NULL);
			}
		};

		// Try to make a very descriptive exception, for that we need to malloc a huge buffer...
		if (auto buffer = (char*)malloc(max_logsize_ever))
		{
			Log(buffer, max_logsize_ever, true, true, true);
			free(buffer);
		}
		else
		{
			// Use a static buffer, no need for any allocation
			static const auto size = max_logsize_basic + max_logsize_regs + max_logsize_stackdump;
			static char static_buf[size];
			static_assert(size <= max_static_buffer, "Static buffer is too big");

			Log(buffer = static_buf, sizeof(static_buf), true, true, false);
		}

		CloseHandle(hFile);
	}

	// step 3: exit the application
	ShowCursor(TRUE);
	hWnd = FindWindowW(0, L"");
	SetForegroundWindow(hWnd);

	return EXCEPTION_CONTINUE_SEARCH;
}

void Init()
{
	std::wstring modulePath = GetModuleFileNameW(hm);
	std::wstring moduleName = modulePath.substr(modulePath.find_last_of(L"/\\") + 1);
	moduleName.resize(moduleName.find_last_of(L'.'));
	modulePath.resize(modulePath.find_last_of(L"/\\") + 1);
	iniPaths.emplace_back(modulePath + moduleName + L".ini");
	iniPaths.emplace_back(modulePath + L"plugins\\config.ini");

	std::wstring m = GetModuleFileNameW(NULL);
	m = m.substr(0, m.find_last_of(L"/\\") + 1) + L"logs";

	auto FolderExists = [](LPCWSTR szPath) -> BOOL
	{
		DWORD dwAttrib = GetFileAttributes(szPath);
		return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
	};

	if (FolderExists(m.c_str()))
	{
		SetUnhandledExceptionFilter(CustomUnhandledExceptionFilter);
		// Now stub out CustomUnhandledExceptionFilter so NO ONE ELSE can set it!
		uint32_t ret = 0x909090C3; //ret
		DWORD protect[2];
		VirtualProtect(&SetUnhandledExceptionFilter, sizeof(ret), PAGE_EXECUTE_READWRITE, &protect[0]);
		memcpy(&SetUnhandledExceptionFilter, &ret, sizeof(ret));
		VirtualProtect(&SetUnhandledExceptionFilter, sizeof(ret), protect[0], &protect[1]);
	}

	LoadEverything();

}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID /*lpReserved*/)
{
	if (reason == DLL_PROCESS_ATTACH)
	{
		hm = hModule;
		Init();
	}
	return TRUE;
}


code:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>16.0</VCProjectVersion>
    <ProjectGuid>{B49C6ABE-931C-4F91-9A94-21A41A305FEE}</ProjectGuid>
    <Keyword>Win32Proj</Keyword>
    <RootNamespace>fakedll</RootNamespace>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
    <TargetFrameworkVersion>
    </TargetFrameworkVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
    <CLRSupport>false</CLRSupport>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <LinkIncremental>true</LinkIncremental>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LinkIncremental>true</LinkIncremental>
    <TargetName>fakednsapi</TargetName>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <LinkIncremental>false</LinkIncremental>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LinkIncremental>false</LinkIncremental>
    <TargetName>fakednsapi</TargetName>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <PrecompiledHeader>NotUsing</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>X64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <AdditionalIncludeDirectories>..\MemoryModule;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <ModuleDefinitionFile>x64.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <PrecompiledHeader>NotUsing</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <AdditionalIncludeDirectories>..\dependencies\MemoryModule;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <LanguageStandard>stdcpp17</LanguageStandard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <ModuleDefinitionFile>x64.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;NDEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <ModuleDefinitionFile>x64.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <PrecompiledHeader>NotUsing</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <AdditionalIncludeDirectories>..\dependencies\MemoryModule;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <LanguageStandard>stdcpp17</LanguageStandard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <ModuleDefinitionFile>x64.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClInclude Include="exception.hpp" />
    <ClInclude Include="framework.h" />
    <ClInclude Include="ModuleList.h" />
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="dllmain.cpp" />
  </ItemGroup>
  <ItemGroup>
    <None Include="x64.def" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>