diff options
Diffstat (limited to 'libs/usvfs/src/usvfs_dll/hookmanager.cpp')
| -rw-r--r-- | libs/usvfs/src/usvfs_dll/hookmanager.cpp | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/libs/usvfs/src/usvfs_dll/hookmanager.cpp b/libs/usvfs/src/usvfs_dll/hookmanager.cpp new file mode 100644 index 0000000..731b856 --- /dev/null +++ b/libs/usvfs/src/usvfs_dll/hookmanager.cpp @@ -0,0 +1,333 @@ +/* +Userspace Virtual Filesystem + +Copyright (C) 2015 Sebastian Herbord. All rights reserved. + +This file is part of usvfs. + +usvfs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +usvfs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with usvfs. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "hookmanager.h" +#include "../thooklib/ttrampolinepool.h" +#include "../thooklib/utility.h" +#include "exceptionex.h" +#include "hooks/kernel32.h" +#include "hooks/ntdll.h" +#include "usvfs.h" +#include <VersionHelpers.h> +#include <directory_tree.h> +#include <logging.h> +#include <shmlogger.h> +#include <usvfsparameters.h> +#include <winapi.h> + +using namespace HookLib; +namespace bf = boost::filesystem; + +namespace usvfs +{ + +HookManager* HookManager::s_Instance = nullptr; + +HookManager::HookManager(const usvfsParameters& params, HMODULE module) + : m_Context(params, module) +{ + if (s_Instance != nullptr) { + throw std::runtime_error("singleton duplicate instantiation (HookManager)"); + } + + s_Instance = this; + + m_Context.registerProcess(::GetCurrentProcessId()); + spdlog::get("usvfs")->info("Process registered in shared process list : {}", + ::GetCurrentProcessId()); + + winapi::ex::OSVersion version = winapi::ex::getOSVersion(); + spdlog::get("usvfs")->info( + "Windows version {}.{}.{} sp {} platform {} ({})", version.major, version.minor, + version.build, version.servicpack, version.platformid, + shared::string_cast<std::string>(winapi::ex::wide::getWindowsBuildLab(true)) + .c_str()); + + initHooks(); + + if (params.debugMode) { + while (!::IsDebuggerPresent()) { + // wait for debugger to attach + ::Sleep(100); + } + } +} + +HookManager::~HookManager() +{ + spdlog::get("hooks")->debug("end hook of process {}", GetCurrentProcessId()); + removeHooks(); + m_Context.unregisterCurrentProcess(); +} + +HookManager& HookManager::instance() +{ + if (s_Instance == nullptr) { + throw std::runtime_error("singleton not instantiated"); + } + + return *s_Instance; +} + +LPVOID HookManager::detour(const char* functionName) +{ + auto iter = m_Hooks.find(functionName); + if (iter != m_Hooks.end()) { + return GetDetour(iter->second); + } else { + return nullptr; + } +} + +void HookManager::removeHook(const std::string& functionName) +{ + auto iter = m_Hooks.find(functionName); + if (iter != m_Hooks.end()) { + try { + RemoveHook(iter->second); + m_Hooks.erase(iter); + spdlog::get("usvfs")->info("removed hook for {}", functionName); + } catch (const std::exception& e) { + spdlog::get("usvfs")->critical("failed to remove hook of {}: {}", functionName, + e.what()); + } + } else { + spdlog::get("usvfs")->info("{} wasn't hooked", functionName); + } +} + +void HookManager::logStubInt(LPVOID address) +{ + if (m_Stubs.find(address) != m_Stubs.end()) { + spdlog::get("hooks")->warn("{0} called", m_Stubs[address]); + } else { + spdlog::get("hooks")->warn("unknown function at {0} called", address); + } +} + +void HookManager::logStub(LPVOID address) +{ + try { + instance().logStubInt(address); + } catch (const std::exception& e) { + spdlog::get("hooks")->debug("function at {0} called after shutdown: {1}", address, + e.what()); + } +} + +void HookManager::installHook(HMODULE module1, HMODULE module2, + const std::string& functionName, LPVOID hook, + LPVOID* fillFuncAddr = nullptr) +{ + BOOST_ASSERT(hook != nullptr); + HOOKHANDLE handle = INVALID_HOOK; + HookError err = ERR_NONE; + LPVOID funcAddr = nullptr; + HMODULE usedModule = nullptr; + // both module1 and module2 are allowed to be null + if (module1 != nullptr) { + funcAddr = MyGetProcAddress(module1, functionName.c_str()); + if (funcAddr != nullptr) { + handle = InstallHook(funcAddr, hook, &err); + } + if (handle != INVALID_HOOK) + usedModule = module1; + } + + if ((handle == INVALID_HOOK) && (module2 != nullptr)) { + funcAddr = MyGetProcAddress(module2, functionName.c_str()); + if (funcAddr != nullptr) { + handle = InstallHook(funcAddr, hook, &err); + } + if (handle != INVALID_HOOK) + usedModule = module2; + } + + if (fillFuncAddr) + *fillFuncAddr = funcAddr; + + if (handle == INVALID_HOOK) { + spdlog::get("usvfs")->error("failed to hook {0}: {1}", functionName, + GetErrorString(err)); + } else { + m_Stubs.insert(make_pair(funcAddr, functionName)); + m_Hooks.insert(make_pair(std::string(functionName), handle)); + spdlog::get("usvfs")->info("hooked {0} ({1}) in {2} type {3}", functionName, + funcAddr, winapi::ansi::getModuleFileName(usedModule), + GetHookType(handle)); + } +} + +void HookManager::installStub(HMODULE module1, HMODULE module2, + const std::string& functionName) +{ + HOOKHANDLE handle = INVALID_HOOK; + HookError err = ERR_NONE; + LPVOID funcAddr = nullptr; + HMODULE usedModule = nullptr; + // both module1 and module2 are allowed to be null + if (module1 != nullptr) { + funcAddr = MyGetProcAddress(module1, functionName.c_str()); + if (funcAddr != nullptr) { + handle = InstallStub(funcAddr, logStub, &err); + } else { + spdlog::get("usvfs")->debug("{} doesn't contain {}", + winapi::ansi::getModuleFileName(module1), + functionName); + } + if (handle != INVALID_HOOK) + usedModule = module1; + } + + if ((handle == INVALID_HOOK) && (module2 != nullptr)) { + funcAddr = MyGetProcAddress(module2, functionName.c_str()); + if (funcAddr != nullptr) { + handle = InstallStub(funcAddr, logStub, &err); + } else { + spdlog::get("usvfs")->debug("{} doesn't contain {}", + winapi::ansi::getModuleFileName(module2), + functionName); + } + if (handle != INVALID_HOOK) + usedModule = module2; + } + + if (handle == INVALID_HOOK) { + spdlog::get("usvfs")->error("failed to stub {0}: {1}", functionName, + GetErrorString(err)); + } else { + m_Stubs.insert(make_pair(funcAddr, functionName)); + m_Hooks.insert(make_pair(std::string(functionName), handle)); + spdlog::get("usvfs")->info("stubbed {0} ({1}) in {2} type {3}", functionName, + funcAddr, winapi::ansi::getModuleFileName(usedModule), + GetHookType(handle)); + } +} + +void HookManager::initHooks() +{ + TrampolinePool::initialize(); + + HookLib::TrampolinePool::instance().setBlock(true); + + HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); + spdlog::get("usvfs")->debug("kernel32.dll at {0:x}", + reinterpret_cast<uintptr_t>(k32Mod)); + // kernelbase.dll contains the actual implementation for functions formerly in + // kernel32.dll and advapi32.dll, starting with Windows 7 + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd371752(v=vs.85).aspx + HMODULE kbaseMod = GetModuleHandleA("kernelbase.dll"); + spdlog::get("usvfs")->debug("kernelbase.dll at {0:x}", + reinterpret_cast<uintptr_t>(kbaseMod)); + + installHook(kbaseMod, k32Mod, "GetFileAttributesExA", hook_GetFileAttributesExA); + installHook(kbaseMod, k32Mod, "GetFileAttributesA", hook_GetFileAttributesA); + installHook(kbaseMod, k32Mod, "GetFileAttributesExW", hook_GetFileAttributesExW); + installHook(kbaseMod, k32Mod, "GetFileAttributesW", hook_GetFileAttributesW); + installHook(kbaseMod, k32Mod, "SetFileAttributesW", hook_SetFileAttributesW); + + installHook(kbaseMod, k32Mod, "CreateDirectoryW", hook_CreateDirectoryW); + installHook(kbaseMod, k32Mod, "RemoveDirectoryW", hook_RemoveDirectoryW); + installHook(kbaseMod, k32Mod, "DeleteFileW", hook_DeleteFileW); + installHook(kbaseMod, k32Mod, "GetCurrentDirectoryA", hook_GetCurrentDirectoryA); + installHook(kbaseMod, k32Mod, "GetCurrentDirectoryW", hook_GetCurrentDirectoryW); + installHook(kbaseMod, k32Mod, "SetCurrentDirectoryA", hook_SetCurrentDirectoryA); + installHook(kbaseMod, k32Mod, "SetCurrentDirectoryW", hook_SetCurrentDirectoryW); + + installHook(kbaseMod, k32Mod, "ExitProcess", hook_ExitProcess); + + installHook(kbaseMod, k32Mod, "CreateProcessInternalW", hook_CreateProcessInternalW, + reinterpret_cast<LPVOID*>(&CreateProcessInternalW)); + + installHook(kbaseMod, k32Mod, "MoveFileA", hook_MoveFileA); + installHook(kbaseMod, k32Mod, "MoveFileW", hook_MoveFileW); + installHook(kbaseMod, k32Mod, "MoveFileExA", hook_MoveFileExA); + installHook(kbaseMod, k32Mod, "MoveFileExW", hook_MoveFileExW); + installHook(kbaseMod, k32Mod, "MoveFileWithProgressA", hook_MoveFileWithProgressA); + installHook(kbaseMod, k32Mod, "MoveFileWithProgressW", hook_MoveFileWithProgressW); + + installHook(kbaseMod, k32Mod, "CopyFileExW", hook_CopyFileExW); + if (IsWindows8OrGreater()) + installHook(kbaseMod, k32Mod, "CopyFile2", hook_CopyFile2, + reinterpret_cast<LPVOID*>(&CopyFile2)); + + installHook(kbaseMod, k32Mod, "GetPrivateProfileStringA", + hook_GetPrivateProfileStringA); + installHook(kbaseMod, k32Mod, "GetPrivateProfileStringW", + hook_GetPrivateProfileStringW); + installHook(kbaseMod, k32Mod, "GetPrivateProfileSectionA", + hook_GetPrivateProfileSectionA); + installHook(kbaseMod, k32Mod, "GetPrivateProfileSectionW", + hook_GetPrivateProfileSectionW); + installHook(kbaseMod, k32Mod, "WritePrivateProfileStringA", + hook_WritePrivateProfileStringA); + installHook(kbaseMod, k32Mod, "WritePrivateProfileStringW", + hook_WritePrivateProfileStringW); + + installHook(kbaseMod, k32Mod, "GetFullPathNameA", hook_GetFullPathNameA); + installHook(kbaseMod, k32Mod, "GetFullPathNameW", hook_GetFullPathNameW); + + installHook(kbaseMod, k32Mod, "FindFirstFileExW", hook_FindFirstFileExW); + + HMODULE ntdllMod = GetModuleHandleA("ntdll.dll"); + spdlog::get("usvfs")->debug("ntdll.dll at {0:x}", + reinterpret_cast<uintptr_t>(ntdllMod)); + installHook(ntdllMod, nullptr, "NtQueryFullAttributesFile", + hook_NtQueryFullAttributesFile); + installHook(ntdllMod, nullptr, "NtQueryAttributesFile", hook_NtQueryAttributesFile); + installHook(ntdllMod, nullptr, "NtQueryDirectoryFile", hook_NtQueryDirectoryFile); + installHook(ntdllMod, nullptr, "NtQueryDirectoryFileEx", hook_NtQueryDirectoryFileEx); + installHook(ntdllMod, nullptr, "NtQueryObject", hook_NtQueryObject); + installHook(ntdllMod, nullptr, "NtQueryInformationFile", hook_NtQueryInformationFile); + installHook(ntdllMod, nullptr, "NtQueryInformationByName", + hook_NtQueryInformationByName); + installHook(ntdllMod, nullptr, "NtOpenFile", hook_NtOpenFile); + installHook(ntdllMod, nullptr, "NtCreateFile", hook_NtCreateFile); + installHook(ntdllMod, nullptr, "NtClose", hook_NtClose); + installHook(ntdllMod, nullptr, "NtTerminateProcess", hook_NtTerminateProcess); + + installHook(kbaseMod, k32Mod, "LoadLibraryExA", hook_LoadLibraryExA); + installHook(kbaseMod, k32Mod, "LoadLibraryExW", hook_LoadLibraryExW); + + // install this hook late as usvfs is calling it itself for debugging purposes + installHook(kbaseMod, k32Mod, "GetModuleFileNameA", hook_GetModuleFileNameA); + installHook(kbaseMod, k32Mod, "GetModuleFileNameW", hook_GetModuleFileNameW); + + spdlog::get("usvfs")->debug("hooks installed"); + HookLib::TrampolinePool::instance().setBlock(false); +} + +void HookManager::removeHooks() +{ + while (m_Hooks.size() > 0) { + auto iter = m_Hooks.begin(); + try { + RemoveHook(iter->second); + spdlog::get("usvfs")->debug("removed hook {}", iter->first); + } catch (const std::exception& e) { + spdlog::get("usvfs")->critical("failed to remove hook: {}", e.what()); + } + + // remove either way, otherwise this is an endless loop + m_Hooks.erase(iter); + } +} + +} // namespace usvfs |
