diff --git a/README.txt b/README.txt index a0ad512..7403f6f 100644 --- a/README.txt +++ b/README.txt @@ -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 \ No newline at end of file +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. \ No newline at end of file diff --git a/dnsapi.dll b/dnsapi.dll new file mode 100644 index 0000000..fa691fb Binary files /dev/null and b/dnsapi.dll differ diff --git a/source-code/PD-Loader.sln b/source-code/PD-Loader.sln index 9f87970..2514e87 100644 --- a/source-code/PD-Loader.sln +++ b/source-code/PD-Loader.sln @@ -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 diff --git a/source-code/source/fakedll/ModuleList.h b/source-code/source/fakedll/ModuleList.h new file mode 100644 index 0000000..72c79e3 --- /dev/null +++ b/source-code/source/fakedll/ModuleList.h @@ -0,0 +1,186 @@ +#pragma once + +#include +#include +#include +#include +#include + +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(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(GetProcAddress(hLib, "K32EnumProcessModules")); + if (pEnumProcessModules == nullptr) + { + // Try psapi + FreeLibrary(hLib); + hLib = LoadLibrary(TEXT("psapi")); + if (hLib != nullptr) + { + pEnumProcessModules = reinterpret_cast(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(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 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 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 > m_moduleList; +}; \ No newline at end of file diff --git a/source-code/source/fakedll/dllmain.cpp b/source-code/source/fakedll/dllmain.cpp new file mode 100644 index 0000000..febfc22 --- /dev/null +++ b/source-code/source/fakedll/dllmain.cpp @@ -0,0 +1,721 @@ +#include "framework.h" +#include "exception.hpp" + +HMODULE hm; +std::vector 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& 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 +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(dwLoadOffset); + PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast(dwLoadOffset); + PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast(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(pSection->Name); + if (s.find(pszSectionName) != s.end()) + { + DWORD dwPhysSize = (pSection->Misc.VirtualSize + 4095) & ~4095; + std::forward(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 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(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( + (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(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; +} diff --git a/source-code/source/fakedll/exception.hpp b/source-code/source/fakedll/exception.hpp new file mode 100644 index 0000000..ff7958c --- /dev/null +++ b/source-code/source/fakedll/exception.hpp @@ -0,0 +1,678 @@ +#pragma once +/* + * Unhandled Exception Tracer + * by LINK/2012 + * + * 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 +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/source-code/source/fakedll/fakedll.vcxproj b/source-code/source/fakedll/fakedll.vcxproj new file mode 100644 index 0000000..fc4f3f0 --- /dev/null +++ b/source-code/source/fakedll/fakedll.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {B49C6ABE-931C-4F91-9A94-21A41A305FEE} + Win32Proj + fakedll + 10.0 + + + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + fakednsapi + + + false + + + false + fakednsapi + + + + NotUsing + Level3 + Disabled + true + X64;%(PreprocessorDefinitions) + true + pch.h + stdcpp17 + ..\MemoryModule;%(AdditionalIncludeDirectories) + + + Windows + true + false + x64.def + + + + + NotUsing + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;_DEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + ..\dependencies\MemoryModule;%(AdditionalIncludeDirectories) + stdcpp17 + + + Windows + true + false + x64.def + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + x64.def + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;NDEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + ..\dependencies\MemoryModule;%(AdditionalIncludeDirectories) + stdcpp17 + + + Windows + true + true + true + false + x64.def + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source-code/source/fakedll/fakedll.vcxproj.filters b/source-code/source/fakedll/fakedll.vcxproj.filters new file mode 100644 index 0000000..046fb1b --- /dev/null +++ b/source-code/source/fakedll/fakedll.vcxproj.filters @@ -0,0 +1,38 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Source Files + + + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/source-code/source/fakedll/framework.h b/source-code/source/fakedll/framework.h new file mode 100644 index 0000000..97f1cea --- /dev/null +++ b/source-code/source/fakedll/framework.h @@ -0,0 +1,89 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +#pragma once +#include +#include +#include +#include "ModuleList.h" +#include +#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 ) \ No newline at end of file diff --git a/source-code/source/fakedll/x64.def b/source-code/source/fakedll/x64.def new file mode 100644 index 0000000..9fa3e38 --- /dev/null +++ b/source-code/source/fakedll/x64.def @@ -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 \ No newline at end of file