diff --git a/Vutils.vcxproj b/Vutils.vcxproj index e40d454..c42ca49 100644 --- a/Vutils.vcxproj +++ b/Vutils.vcxproj @@ -329,6 +329,11 @@ + + + + + @@ -401,6 +406,7 @@ + diff --git a/Vutils.vcxproj.filters b/Vutils.vcxproj.filters index 25fa723..16f51fc 100644 --- a/Vutils.vcxproj.filters +++ b/Vutils.vcxproj.filters @@ -64,6 +64,9 @@ {0e80967e-0e61-4762-98d0-e9774dcee22f} + + {09804e65-3ba8-4b2d-ac74-8f7e22ab2be0} + @@ -156,6 +159,21 @@ Header Files\Third Files\EP + + Header Files\Third Files\CH + + + Header Files\Third Files\CH + + + Header Files\Third Files\CH + + + Header Files\Third Files\CH + + + Header Files\Third Files\CH + @@ -358,5 +376,8 @@ Header Files\Template Files + + Header Files\Template Files + \ No newline at end of file diff --git a/include/3rdparty/CH/cpp-hooking/common.h b/include/3rdparty/CH/cpp-hooking/common.h new file mode 100644 index 0000000..02d9593 --- /dev/null +++ b/include/3rdparty/CH/cpp-hooking/common.h @@ -0,0 +1,73 @@ +/** + * @file common.h + * @author Vic P. + * @brief Header for common use. + */ + +#pragma once + +#include "invokable.h" + +#include +#include + +/** + * @brief The structure that holds the hooking information of a function. + */ +struct Hooked +{ + void* m_function; + void* m_trampoline; + std::shared_ptr m_invoker; + + Hooked() : m_function(nullptr), m_trampoline(nullptr) {} + + Hooked(const Hooked& right) + { + *this = right; + } + + const Hooked& operator=(const Hooked& right) + { + if (this != &right) + { + m_function = right.m_function; + m_trampoline = right.m_trampoline; + m_invoker = right.m_invoker; + } + + return *this; + } +}; + +template +struct LibraryT; + +template <> +struct LibraryT +{ + typedef vu::LibraryA self; +}; + +template <> +struct LibraryT +{ + typedef vu::LibraryW self; +}; + +/** + * @brief Get the address of an exported function from the given dynamic-link module. + * @param module The module name. + * @param function The function name. + * @returns The address of the function or null. + */ +template +inline void* get_proc_address(const StdString& module, const StdString& function) +{ + if (module.empty() || function.empty()) + { + return nullptr; + } + + return LibraryT::self::quick_get_proc_address(module, function); +} diff --git a/include/3rdparty/CH/cpp-hooking/hooking.h b/include/3rdparty/CH/cpp-hooking/hooking.h new file mode 100644 index 0000000..eea2b87 --- /dev/null +++ b/include/3rdparty/CH/cpp-hooking/hooking.h @@ -0,0 +1,10 @@ +/** + * @file hooking.h + * @author Vic P. + * @brief Header for Inline Hooking Manager & IAT Hooking Manager. + */ + +#pragma once + +#include "inl_hooking.h" +#include "iat_hooking.h" diff --git a/include/3rdparty/CH/cpp-hooking/iat_hooking.h b/include/3rdparty/CH/cpp-hooking/iat_hooking.h new file mode 100644 index 0000000..7a6d507 --- /dev/null +++ b/include/3rdparty/CH/cpp-hooking/iat_hooking.h @@ -0,0 +1,134 @@ +/** + * @file iat_hooking.h + * @author Vic P. + * @brief Header/Implementation for IAT Hooking Manager. + */ + +#pragma once + +#include "common.h" + +#include + +/** + * @brief IAT Hooking Manager. + */ +class IATHookingManager : public vu::SingletonT +{ + struct IAT_Hooked : public Hooked {}; + + std::unordered_map m_list; + + using Entry = vu::IATHookingA::Entry; + + vu::IATHookingA& hooker() + { + return vu::IATHookingA::instance(); + } + + void* get_proc_address(const Entry& entry) + { + Entry temp; + + if (!this->hooker().exist(entry.target, entry.module, entry.function, &temp)) + { + return nullptr; + } + + return temp.original; + } + +public: + /** + * @brief Hook a given function. + * @param[in] entry The entry of function that want to hook ({ tartget name, module name, function name }). + * @param[in] hk_function The hooking function. + * @return true if succeeds otherwise return false. + */ + template + bool hook(const Entry& entry, Function&& hk_function) + { + IAT_Hooked hooked; + + auto ret = this->hooker().install(entry.target, entry.module, entry.function, + (void*)hk_function, &hooked.m_trampoline); + if (ret != vu::VU_OK) + { + return false; + } + + auto function = this->get_proc_address(entry); + if (function == nullptr) + { + return false; + } + + using FunctionPtr = decltype(&hk_function); + + hooked.m_function = function; + hooked.m_invoker.reset(new Invokable(FunctionPtr(hooked.m_trampoline))); + + void* key = hooked.m_function; + m_list[key] = std::move(hooked); + + return true; + } + + /** + * @brief Unhook a given function that was hooked. + * @param[in] entry The entry of function that want to un-hook ({ tartget name, module name, function name }). + * @return true if succeeds otherwise return false. + */ + bool unhook(const Entry& entry) + { + auto function = this->get_proc_address(entry); + if (function == nullptr) + { + return false; + } + + auto it = m_list.find(function); + if (it == m_list.cend()) + { + return false; + } + + auto ret = this->hooker().uninstall(entry.target, entry.module, entry.function); + if (ret != vu::VU_OK) + { + return false; + } + + m_list.erase(it); + + return true; + } + + /** + * @brief Invoke the original function. + * @param[in] entry The entry of function that want to invoke ({ tartget name, module name, function name }). + * @param[in] args The arguments that pass to the original function. + * @return Based on the result of the original function. + */ + template + Return invoke(const Entry& entry, Args ... args) + { + auto function = this->get_proc_address(entry); + auto it = m_list.find(function); + if (it == m_list.cend()) + { + throw "invoke the function that did not hook"; + } + + auto& hooked = it->second; + + if (std::is_void::value) + { + hooked.m_invoker->invoke(std::forward(args)...); + } + else + { + return hooked.m_invoker->invoke(std::forward(args)...); + } + } +}; diff --git a/include/3rdparty/CH/cpp-hooking/inl_hooking.h b/include/3rdparty/CH/cpp-hooking/inl_hooking.h new file mode 100644 index 0000000..5d3fb65 --- /dev/null +++ b/include/3rdparty/CH/cpp-hooking/inl_hooking.h @@ -0,0 +1,154 @@ +/** + * @file inl_hooking.h + * @author Vic P. + * @brief Header/Implementation for Inline Hooking Manager. + */ + +#pragma once + +#include "common.h" + +#include + +/** + * @brief Inline Hooking Manager. + */ +class INLHookingManager : public vu::SingletonT +{ + struct INL_Hooked: public Hooked + { + vu::INLHooking m_hooker; + + INL_Hooked() : Hooked() {} + + INL_Hooked(const INL_Hooked& right) + { + *this = right; + } + + const INL_Hooked& operator=(const INL_Hooked& right) + { + if (this != &right) + { + Hooked::operator=(right); + m_hooker = right.m_hooker; + } + + return *this; + } + }; + + std::unordered_map m_list; + +public: + /** + * @brief Hook a given function. + * @param[in] function The function that want to hook. + * @param[in] hk_function The hooking function. + * @return true if succeeds otherwise return false. + */ + template + bool hook(void* function, Function&& hk_function) + { + auto it = m_list.find(function); + if (it != m_list.cend()) + { + return false; + } + + INL_Hooked hooked; + if (!hooked.m_hooker.attach(function, (void*)hk_function, &hooked.m_trampoline)) + { + return false; + } + + using FunctionPtr = decltype(&hk_function); + + hooked.m_function = function; + hooked.m_invoker.reset(new Invokable(FunctionPtr(hooked.m_trampoline))); + + void* key = hooked.m_function; + m_list[key] = std::move(hooked); + + return true; + } + + /** + * @brief Unhook a given function that was hooked. + * @param[in] entry The function that want to un-hook. + * @return true if succeeds otherwise return false. + */ + bool unhook(void* function) + { + auto it = m_list.find(function); + if (it == m_list.cend()) + { + return false; + } + + auto& hooked = it->second; + auto result = hooked.m_hooker.detach(function, &hooked.m_trampoline); + if (result) + { + m_list.erase(it); + } + + return result; + } + + /** + * @brief Hook a given function. + * @param[in] module The module name. + * @param[in] function The function name. + * @param[in] hk_function The hooking function. + * @return true if succeeds otherwise return false. + */ + template + bool hook(const StdString& module, const StdString& function, Function&& hk_function) + { + auto ptr = get_proc_address(module, function); + return ptr != nullptr ? this->hook(ptr, hk_function) : false; + } + + /** + * @brief Unhook a given function that was hooked. + * @param[in] module The module name. + * @param[in] function The function name. + * @return true if succeeds otherwise return false. + */ + template + bool unhook(const StdString& module, const StdString& function) + { + auto ptr = get_proc_address(module, function); + return ptr != nullptr ? this->unhook(ptr) : false; + } + + /** + * @brief Invoke the original function. + * @param[in] entry The function that want to invoke. + * @param[in] args The arguments that pass to the original function. + * @return Based on the result of the original function. + */ + template + Return invoke(void* function, Args ... args) + { + auto it = m_list.find(function); + if (it == m_list.cend()) + { + throw "invoke the function that did not hook"; + } + + auto& hooked = it->second; + + if (std::is_void::value) + { + hooked.m_invoker->invoke(std::forward(args)...); + } + else + { + return hooked.m_invoker->invoke(std::forward(args)...); + } + + return 0; + } +}; diff --git a/include/3rdparty/CH/cpp-hooking/invokable.h b/include/3rdparty/CH/cpp-hooking/invokable.h new file mode 100644 index 0000000..b7a1056 --- /dev/null +++ b/include/3rdparty/CH/cpp-hooking/invokable.h @@ -0,0 +1,46 @@ +/** + * @file invokable.h + * @author Vic P. + * @brief Header/Implementation for the function-container that can hold any function prototype. + */ + +#pragma once + +#ifndef _ANY_ +#include +#include +#endif // _ANY_ + +/** + * @brief The function-container that can hold any function prototype. + */ +struct Invokable +{ + Invokable() {} + + template + Invokable(Function&& function) : Invokable(std::function(std::forward(function))) {} + + template + Invokable(std::function function) : m_function(function) + { + m_function = function; + } + + template + Return invoke(Args ... args) + { + auto function = std::any_cast>(m_function); + + if (std::is_void::value) + { + std::invoke(function, std::forward(args)...); + } + else + { + return std::invoke(function, std::forward(args)...); + } + } + + std::any m_function; +}; diff --git a/include/Vutils.h b/include/Vutils.h index f127e8f..d0ccbc5 100644 --- a/include/Vutils.h +++ b/include/Vutils.h @@ -38,6 +38,16 @@ #error Vutils required C++11 or newer #endif +// C++14 (MSVC 2015+ or MinGW 5.1+) +#if (defined(_MSC_VER) && _MSVC_LANG >= 201402L) || (defined(__MINGW32__) && __cplusplus >= 201402L) +#define VU_HAS_CXX14 +#endif // C++17 (MSVC 2017+ or MinGW 7.1+) + +// C++17 (MSVC 2017+ or MinGW 7.1+) +#if (defined(_MSC_VER) && _MSVC_LANG >= 201703L) || (defined(__MINGW32__) && __cplusplus >= 201703L) +#define VU_HAS_CXX17 +#endif // C++17 (MSVC 2017+ or MinGW 7.1+) + /* Vutils Configurations */ // VU_NO_EX - To disable all extended utilities @@ -105,6 +115,9 @@ #include #include #include +#if defined(VU_HAS_CXX17) +#include +#endif // VU_HAS_CXX17 #ifdef _MSC_VER #pragma warning(push) @@ -3695,6 +3708,10 @@ class Debouncer : public SingletonT #define RESTClient RESTClientA #endif // _UNICODE +// C++ Hooking + +#include "template/fnhooking.tpl" + } // namespace vu #ifdef _MSC_VER diff --git a/include/template/fnhooking.tpl b/include/template/fnhooking.tpl new file mode 100644 index 0000000..cf1b8fa --- /dev/null +++ b/include/template/fnhooking.tpl @@ -0,0 +1,20 @@ +/** + * @file easyprint.tpl + * @author Vic P. + * @brief Template for Easy Print + */ + + /** + * Easy Print + */ + +#pragma once + +// C++17 (MSVC 2017+ or MinGW 7.1+) +#if (defined(_MSC_VER) && _MSVC_LANG >= 201703L) || (defined(__MINGW32__) && __cplusplus >= 201703L) + +#include "3rdparty/CH/cpp-hooking/hooking.h" + +#define VU_HAS_CPP_HOOKING + +#endif // C++17 (MSVC 2017+ or MinGW 7.1+) \ No newline at end of file