/* 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 . */ #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 #include #include #include #include #include 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(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(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(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(&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(&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(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