Add fake dnsapi.dll
This commit is contained in:
+3
-2
@@ -1,2 +1,3 @@
|
||||
This is the source code!
|
||||
If you just want to play PDAFT, please read the wiki at https://notabug.org/nastys/PD-Loader/wiki
|
||||
This is the source code! It can NOT be used to play the game!
|
||||
If you just want to play PDAFT, you downloaded the wrong thing.
|
||||
Please read the wiki at https://notabug.org/nastys/PD-Loader/wiki for information on getting and installing the latest release.
|
||||
BIN
Binary file not shown.
@@ -13,32 +13,64 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Launcher", "source\plugins\
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Render", "source\plugins\Render\Render.vcxproj", "{89F87459-768F-4638-9267-0F90CD74452D}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fakedll", "source\fakedll\fakedll.vcxproj", "{B49C6ABE-931C-4F91-9A94-21A41A305FEE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CA479467-D518-46A2-AC86-3098ADA99FE5}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CA479467-D518-46A2-AC86-3098ADA99FE5}.Debug|x64.Build.0 = Debug|x64
|
||||
{CA479467-D518-46A2-AC86-3098ADA99FE5}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CA479467-D518-46A2-AC86-3098ADA99FE5}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CA479467-D518-46A2-AC86-3098ADA99FE5}.Release|x64.ActiveCfg = Release|x64
|
||||
{CA479467-D518-46A2-AC86-3098ADA99FE5}.Release|x64.Build.0 = Release|x64
|
||||
{CA479467-D518-46A2-AC86-3098ADA99FE5}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA479467-D518-46A2-AC86-3098ADA99FE5}.Release|x86.Build.0 = Release|Win32
|
||||
{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Debug|x64.Build.0 = Debug|x64
|
||||
{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Debug|x86.Build.0 = Debug|Win32
|
||||
{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Release|x64.ActiveCfg = Release|x64
|
||||
{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Release|x64.Build.0 = Release|x64
|
||||
{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Release|x86.ActiveCfg = Release|Win32
|
||||
{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Release|x86.Build.0 = Release|Win32
|
||||
{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Debug|x64.Build.0 = Debug|x64
|
||||
{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Release|x64.ActiveCfg = Release|x64
|
||||
{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Release|x64.Build.0 = Release|x64
|
||||
{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Release|x86.Build.0 = Release|Win32
|
||||
{60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Debug|x64.Build.0 = Debug|x64
|
||||
{60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Debug|x86.Build.0 = Debug|Win32
|
||||
{60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Release|x64.ActiveCfg = Release|x64
|
||||
{60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Release|x64.Build.0 = Release|x64
|
||||
{60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Release|x86.Build.0 = Release|Win32
|
||||
{89F87459-768F-4638-9267-0F90CD74452D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{89F87459-768F-4638-9267-0F90CD74452D}.Debug|x64.Build.0 = Debug|x64
|
||||
{89F87459-768F-4638-9267-0F90CD74452D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{89F87459-768F-4638-9267-0F90CD74452D}.Debug|x86.Build.0 = Debug|Win32
|
||||
{89F87459-768F-4638-9267-0F90CD74452D}.Release|x64.ActiveCfg = Release|x64
|
||||
{89F87459-768F-4638-9267-0F90CD74452D}.Release|x64.Build.0 = Release|x64
|
||||
{89F87459-768F-4638-9267-0F90CD74452D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{89F87459-768F-4638-9267-0F90CD74452D}.Release|x86.Build.0 = Release|Win32
|
||||
{B49C6ABE-931C-4F91-9A94-21A41A305FEE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B49C6ABE-931C-4F91-9A94-21A41A305FEE}.Debug|x64.Build.0 = Debug|x64
|
||||
{B49C6ABE-931C-4F91-9A94-21A41A305FEE}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{B49C6ABE-931C-4F91-9A94-21A41A305FEE}.Debug|x86.Build.0 = Debug|Win32
|
||||
{B49C6ABE-931C-4F91-9A94-21A41A305FEE}.Release|x64.ActiveCfg = Release|x64
|
||||
{B49C6ABE-931C-4F91-9A94-21A41A305FEE}.Release|x64.Build.0 = Release|x64
|
||||
{B49C6ABE-931C-4F91-9A94-21A41A305FEE}.Release|x86.ActiveCfg = Release|Win32
|
||||
{B49C6ABE-931C-4F91-9A94-21A41A305FEE}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
#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;
|
||||
};
|
||||
@@ -0,0 +1,721 @@
|
||||
#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*)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(<ime, &time);
|
||||
wcsftime(timestamp, _countof(timestamp), L"%Y%m%d%H%M%S", <ime);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,678 @@
|
||||
#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, ®s_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, ®s_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;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,89 @@
|
||||
#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;
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
// 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; };
|
||||
}
|
||||
} dnsapi;
|
||||
|
||||
void _DnsFree() { dnsapi.DnsFree(); }
|
||||
void _DnsQuery_A() { dnsapi.DnsQuery_A(); }
|
||||
void _DnsQueryEx() { dnsapi.DnsQueryEx(); }
|
||||
void _DnsCancelQuery() { dnsapi.DnsCancelQuery(); }
|
||||
|
||||
#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 )
|
||||
@@ -0,0 +1,10 @@
|
||||
LIBRARY "dnsapi"
|
||||
EXPORTS
|
||||
DnsFree = _DnsFree
|
||||
DnsQuery_A = _DnsQuery_A
|
||||
DnsQueryEx = _DnsQueryEx
|
||||
DnsCancelQuery = _DnsCancelQuery
|
||||
DllCanUnloadNow = _DllCanUnloadNow PRIVATE
|
||||
DllGetClassObject = _DllGetClassObject PRIVATE
|
||||
DllRegisterServer = _DllRegisterServer PRIVATE
|
||||
DllUnregisterServer = _DllUnregisterServer PRIVATE
|
||||
Reference in New Issue
Block a user