├── .idea
├── .name
├── PolyHook_2_0.iml
├── .gitignore
├── codeStyles
│ └── codeStyleConfig.xml
├── dictionaries
│ └── project.xml
├── vcs.xml
├── modules.xml
├── misc.xml
└── inspectionProfiles
│ └── Project_Default.xml
├── UnitTests
├── linux
│ ├── TestEatHook.cpp
│ ├── TestIatHook.cpp
│ ├── TestBreakpointHook.cpp
│ ├── TestDetourNoTDx64.cpp
│ ├── TestDetourNoTDx86.cpp
│ ├── TestVFuncSwapHook.cpp
│ ├── TestVTableSwapHook.cpp
│ ├── TestDetourSchemex64.cpp
│ ├── TestHWBreakpointHook.cpp
│ ├── TestDetourTranslationx64.cpp
│ ├── TestMemProtector.cpp
│ ├── TestDetourx64.cpp
│ └── TestDetourx86.cpp
├── TestUtils.cpp
├── windows
│ ├── TestIatHook.cpp
│ ├── TestBreakpointHook.cpp
│ ├── TestHWBreakpointHook.cpp
│ ├── TestVTableSwapHook.cpp
│ ├── TestVFuncSwapHook.cpp
│ ├── TestMemProtector.cpp
│ ├── TestDetourTranslationx64.cpp
│ ├── TestEatHook.cpp
│ └── TestDetourSchemex64.cpp
└── TestUtils.hpp
├── _config.yml
├── docs
└── _config.yml
├── Examples
├── VCPKG_DynamicLink
│ ├── .gitignore
│ ├── vcpkg.json
│ ├── CMakeLists.txt
│ ├── main.cpp
│ └── CMakeSettings.json
└── VCPKG_StaticLink
│ ├── .gitignore
│ ├── vcpkg.json
│ ├── CMakeLists.txt
│ ├── main.cpp
│ └── CMakeSettings.json
├── .github
├── FUNDING.yml
└── workflows
│ └── main.yml
├── Seal_of_Approval.png
├── polyhook2
├── Tests
│ ├── StackCanary.hpp
│ └── TestEffectTracker.hpp
├── UID.hpp
├── Detour
│ ├── NatDetour.hpp
│ ├── x86Detour.hpp
│ ├── x64Detour.hpp
│ └── ILCallback.hpp
├── EventDispatcher.hpp
├── PolyHookOsIncludes.hpp
├── Exceptions
│ ├── BreakPointHook.hpp
│ ├── HWBreakPointHook.hpp
│ └── AVehHook.hpp
├── Virtuals
│ ├── VFuncSwapHook.hpp
│ └── VTableSwapHook.hpp
├── ErrorLog.hpp
├── PE
│ ├── IatHook.hpp
│ ├── EatHook.hpp
│ └── PEB.hpp
├── MemProtector.hpp
├── MemAccessor.hpp
├── RangeAllocator.hpp
├── PolyHookOs.hpp
├── FBAllocator.hpp
├── Enums.hpp
├── ZydisDisassembler.hpp
├── IHook.hpp
└── Misc.hpp
├── .gitmodules
├── sources
├── UID.cpp
├── InternalUtils.hpp
├── PolyHookOs.cpp
├── StackCanary.cpp
├── TestEffectTracker.cpp
├── ErrorLog.cpp
├── BreakPointHook.cpp
├── VFuncSwapHook.cpp
├── AVehHook.cpp
├── VTableSwapHook.cpp
├── HWBreakPointHook.cpp
├── MemProtector.cpp
├── FBAllocator.cpp
├── RangeAllocator.cpp
├── IatHook.cpp
├── MemAccessor.cpp
├── EatHook.cpp
└── ZydisDisassembler.cpp
├── .clang-format
├── MainTests.cpp
├── polyhook_2-config.cmake.in
├── LICENSE
├── CMakeSettings.json
├── .gitignore
└── README.md
/.idea/.name:
--------------------------------------------------------------------------------
1 | PolyHook_2
--------------------------------------------------------------------------------
/UnitTests/linux/TestEatHook.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/UnitTests/linux/TestIatHook.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/UnitTests/linux/TestBreakpointHook.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/UnitTests/linux/TestDetourNoTDx64.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/UnitTests/linux/TestDetourNoTDx86.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/UnitTests/linux/TestVFuncSwapHook.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/UnitTests/linux/TestVTableSwapHook.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-slate
--------------------------------------------------------------------------------
/UnitTests/linux/TestDetourSchemex64.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/UnitTests/linux/TestHWBreakpointHook.cpp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-slate
--------------------------------------------------------------------------------
/Examples/VCPKG_DynamicLink/.gitignore:
--------------------------------------------------------------------------------
1 | .vs
2 | out
3 |
4 |
--------------------------------------------------------------------------------
/Examples/VCPKG_StaticLink/.gitignore:
--------------------------------------------------------------------------------
1 | .vs
2 | out
3 |
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [stevemk14ebr]
4 |
--------------------------------------------------------------------------------
/Seal_of_Approval.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stevemk14ebr/PolyHook_2_0/HEAD/Seal_of_Approval.png
--------------------------------------------------------------------------------
/.idea/PolyHook_2_0.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 |
--------------------------------------------------------------------------------
/Examples/VCPKG_DynamicLink/vcpkg.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "testpolyhook2",
3 | "version-string": "1.0.0.1",
4 | "dependencies": [
5 | "polyhook2"
6 | ]
7 | }
--------------------------------------------------------------------------------
/Examples/VCPKG_StaticLink/vcpkg.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "testpolyhook2",
3 | "version-string": "1.0.0.1",
4 | "dependencies": [
5 | "polyhook2"
6 | ]
7 | }
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/dictionaries/project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | trmp
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/polyhook2/Tests/StackCanary.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace PLH {
4 | class StackCanary {
5 | public:
6 | StackCanary();
7 | bool isStackGood();
8 | ~StackCanary() noexcept(false);
9 | private:
10 | unsigned char buf[50];
11 | };
12 | }
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "zydis"]
2 | path = zydis
3 | url = https://github.com/zyantific/zydis.git
4 | [submodule "asmjit"]
5 | path = asmjit
6 | url = https://github.com/asmjit/asmjit.git
7 | [submodule "asmtk"]
8 | path = asmtk
9 | url = https://github.com/asmjit/asmtk.git
10 |
--------------------------------------------------------------------------------
/sources/UID.cpp:
--------------------------------------------------------------------------------
1 | #include "polyhook2/UID.hpp"
2 |
3 | PLH::UID::UID(long val) {
4 | this->val = val;
5 | }
6 |
7 | std::atomic_long& PLH::UID::singleton() {
8 | static std::atomic_long base = { -1 };
9 | base++;
10 | return base;
11 | }
12 |
13 | PLH::UID::UID() {
14 | this->val = -1;
15 | }
16 |
--------------------------------------------------------------------------------
/sources/InternalUtils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // This header defines utilities that are not meant to be used by the users of this library
4 |
5 | #ifdef PLH_DIAGNOSTICS
6 | #define PLH_SET_DIAGNOSTIC(DIAGNOSTIC) setDiagnostic(DIAGNOSTIC)
7 | #else
8 | #define PLH_SET_DIAGNOSTIC(DIAGNOSTIC)
9 | #endif
10 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/sources/PolyHookOs.cpp:
--------------------------------------------------------------------------------
1 | #include "polyhook2/PolyHookOs.hpp"
2 | #include "polyhook2/PolyHookOsIncludes.hpp"
3 |
4 | #ifdef POLYHOOK2_OS_WINDOWS
5 |
6 | void PolyHook2DebugBreak() {
7 | DebugBreak();
8 | }
9 |
10 | #else
11 |
12 | void PolyHook2DebugBreak() {
13 | __asm__("int3");
14 | }
15 |
16 | #endif
17 |
--------------------------------------------------------------------------------
/UnitTests/TestUtils.cpp:
--------------------------------------------------------------------------------
1 | #include "./TestUtils.hpp"
2 |
3 | #include "polyhook2/ErrorLog.hpp"
4 |
5 | #include
6 |
7 | namespace PLH::test {
8 |
9 | void registerTestLogger() {
10 | const auto logger = std::make_shared();
11 | logger->setLogLevel(PLH::ErrorLevel::INFO);
12 | PLH::Log::registerLogger(logger);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/polyhook2/UID.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by steve on 6/23/17.
3 | //
4 |
5 | #ifndef POLYHOOK_2_UID_HPP
6 | #define POLYHOOK_2_UID_HPP
7 |
8 | #include "polyhook2/PolyHookOs.hpp"
9 | namespace PLH {
10 | class UID {
11 | public:
12 | UID();
13 | UID(long val);
14 | static std::atomic_long& singleton();
15 |
16 | long val;
17 | };
18 | }
19 | #endif //POLYHOOK_2_UID_HPP
--------------------------------------------------------------------------------
/polyhook2/Detour/NatDetour.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "polyhook2/PolyHookOs.hpp"
4 |
5 | #ifdef POLYHOOK2_ARCH_X64
6 | #include "polyhook2/Detour/x64Detour.hpp"
7 | #else
8 | #include "polyhook2/Detour/x86Detour.hpp"
9 | #endif
10 |
11 | namespace PLH {
12 | #ifdef POLYHOOK2_ARCH_X64
13 | using NatDetour = x64Detour;
14 | #else
15 | using NatDetour = x86Detour;
16 | #endif
17 | }
--------------------------------------------------------------------------------
/.clang-format:
--------------------------------------------------------------------------------
1 | # Settings for clang-format 20
2 | # See: https://releases.llvm.org/20.1.0/tools/clang/docs/ClangFormatStyleOptions.html
3 |
4 | BasedOnStyle: LLVM
5 | ColumnLimit: 120
6 | UseTab: Always
7 | IndentWidth: 4
8 | TabWidth: 4
9 |
10 | BinPackArguments: false
11 | BinPackParameters: OnePerLine
12 | AlignAfterOpenBracket: BlockIndent
13 | BracedInitializerIndentWidth: 4
14 | FixNamespaceComments: false
15 | KeepEmptyLines:
16 | AtEndOfFile: true
--------------------------------------------------------------------------------
/sources/StackCanary.cpp:
--------------------------------------------------------------------------------
1 | #include "polyhook2/Tests/StackCanary.hpp"
2 |
3 | PLH::StackCanary::StackCanary() {
4 | for (int i = 0; i < 50; i++) {
5 | buf[i] = 0xCE;
6 | }
7 | }
8 |
9 | bool PLH::StackCanary::isStackGood() {
10 | for (int i = 0; i < 50; i++) {
11 | if (buf[i] != 0xCE)
12 | return false;
13 | }
14 | return true;
15 | }
16 |
17 | PLH::StackCanary::~StackCanary() noexcept(false) {
18 | if (!isStackGood())
19 | throw "Stack corruption detected";
20 | }
--------------------------------------------------------------------------------
/Examples/VCPKG_StaticLink/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.3.0)
2 | cmake_policy(SET CMP0074 NEW)
3 | cmake_policy(SET CMP0091 NEW)
4 |
5 | project(test_polyhook2)
6 |
7 | find_package(PolyHook_2 CONFIG REQUIRED)
8 |
9 | set(CMAKE_CXX_FLAGS "/std:c++latest ${CMAKE_CXX_FLAGS}")
10 |
11 | add_executable(test_polyhook2 main.cpp)
12 | set_property(TARGET test_polyhook2 PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
13 | target_link_libraries(test_polyhook2 PRIVATE PolyHook_2::PolyHook_2)
14 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Examples/VCPKG_DynamicLink/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.3.0)
2 | cmake_policy(SET CMP0074 NEW)
3 | if(POLICY CMP0091)
4 | cmake_policy(SET CMP0091 NEW)
5 | endif()
6 |
7 | project(test_polyhook2)
8 |
9 | find_package(PolyHook_2 CONFIG REQUIRED)
10 |
11 | set(CMAKE_CXX_FLAGS "/std:c++latest ${CMAKE_CXX_FLAGS}")
12 |
13 | add_executable(test_polyhook2 main.cpp)
14 | set_property(TARGET test_polyhook2 PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL")
15 | target_link_libraries(test_polyhook2 PRIVATE PolyHook_2::PolyHook_2)
16 |
--------------------------------------------------------------------------------
/polyhook2/Tests/TestEffectTracker.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYHOOK_2_0_EFFECTSTRACKER_HPP
2 | #define POLYHOOK_2_0_EFFECTSTRACKER_HPP
3 |
4 | #include "../UID.hpp"
5 |
6 | class Effect {
7 | public:
8 | Effect();
9 |
10 | Effect& operator=(const Effect& rhs);
11 |
12 | void trigger();
13 |
14 | bool didExecute();
15 | private:
16 | bool m_executed;
17 | PLH::UID m_uid;
18 | };
19 |
20 | /**Track if some side effect happened.**/
21 | class EffectTracker {
22 | public:
23 | void PushEffect();
24 | Effect PopEffect();
25 | Effect& PeakEffect();
26 | private:
27 | std::vector m_effectQ;
28 | };
29 | #endif
--------------------------------------------------------------------------------
/MainTests.cpp:
--------------------------------------------------------------------------------
1 | #define CATCH_CONFIG_RUNNER
2 | #include "Catch.hpp"
3 | #include
4 |
5 | #include "polyhook2/ErrorLog.hpp"
6 | int main(int argc, char* const argv[]) {
7 | #if defined(POLYHOOK2_OS_WINDOWS) && !defined(__GNUC__)
8 | _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
9 | #endif
10 | std::cout << "Welcome to PolyHook -By- Stevemk14ebr" << std::endl;
11 | auto logger = std::make_shared();
12 | logger->setLogLevel(PLH::ErrorLevel::INFO);
13 | PLH::Log::registerLogger(logger);
14 | int result = Catch::Session().run(argc, argv);
15 |
16 | // getchar();
17 | return result;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/Examples/VCPKG_DynamicLink/main.cpp:
--------------------------------------------------------------------------------
1 | #include "polyhook2/IHook.hpp"
2 | #include "polyhook2/Detour/NatDetour.hpp"
3 |
4 | #include
5 | uint64_t hookPrintfTramp = NULL;
6 | NOINLINE int __cdecl h_hookPrintf(const char* format, ...) {
7 | char buffer[512];
8 | va_list args;
9 | va_start(args, format);
10 | vsprintf_s(buffer, format, args);
11 | va_end(args);
12 |
13 | return PLH::FnCast(hookPrintfTramp, &printf)("INTERCEPTED YO:%s", buffer);
14 | }
15 |
16 | int main()
17 | {
18 | PLH::NatDetour detour = PLH::NatDetour((uint64_t)&printf, (uint64_t)h_hookPrintf, &hookPrintfTramp);
19 | detour.hook();
20 |
21 | printf("%s %f\n", "hi", .5f);
22 | detour.unHook();
23 | return 0;
24 | }
--------------------------------------------------------------------------------
/Examples/VCPKG_StaticLink/main.cpp:
--------------------------------------------------------------------------------
1 | #include "polyhook2/IHook.hpp"
2 | #include "polyhook2/Detour/NatDetour.hpp"
3 |
4 | #include
5 | uint64_t hookPrintfTramp = NULL;
6 | NOINLINE int __cdecl h_hookPrintf(const char* format, ...) {
7 | char buffer[512];
8 | va_list args;
9 | va_start(args, format);
10 | vsprintf_s(buffer, format, args);
11 | va_end(args);
12 |
13 | return PLH::FnCast(hookPrintfTramp, &printf)("INTERCEPTED YO:%s", buffer);
14 | }
15 |
16 | int main()
17 | {
18 | PLH::NatDetour detour = PLH::NatDetour((uint64_t)&printf, (uint64_t)h_hookPrintf, &hookPrintfTramp);
19 | detour.hook();
20 |
21 | printf("%s %f\n", "hi", .5f);
22 | detour.unHook();
23 | return 0;
24 | }
--------------------------------------------------------------------------------
/polyhook2/EventDispatcher.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "polyhook2/PolyHookOs.hpp"
4 |
5 | namespace PLH {
6 | template
7 | class EventDispatcher
8 | {
9 | public:
10 | typedef std::function Event;
11 | void operator+=(const Event& event);
12 |
13 | template
14 | typename Event::result_type Invoke(Args&& ...Params)
15 | {
16 | assert(m_Event);
17 | return m_Event(std::forward(Params)...);
18 | }
19 |
20 | operator bool() const
21 | {
22 | return m_Event != nullptr;
23 | }
24 | private:
25 | Event m_Event;
26 | };
27 |
28 | template
29 | void EventDispatcher::operator+=(const Event& event)
30 | {
31 | m_Event = event;
32 | }
33 | }
--------------------------------------------------------------------------------
/polyhook2/PolyHookOsIncludes.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYHOOK_2_OS_INCLUDES_HPP
2 | #define POLYHOOK_2_OS_INCLUDES_HPP
3 |
4 | // This file is used to not include os specific functions that might break other projects
5 | // You should use it in sources
6 |
7 | #if defined(POLYHOOK2_OS_WINDOWS)
8 |
9 | #ifndef WIN32_LEAN_AND_MEAN
10 | #define WIN32_LEAN_AND_MEAN
11 | #endif
12 | #ifndef NOMINMAX
13 | #define NOMINMAX
14 | #endif
15 | #include
16 |
17 | #elif defined(POLYHOOK2_OS_LINUX)
18 |
19 | #include
20 | #include
21 |
22 | #elif defined(POLYHOOK2_OS_APPLE)
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #endif
30 |
31 | #endif
--------------------------------------------------------------------------------
/sources/TestEffectTracker.cpp:
--------------------------------------------------------------------------------
1 | #include "polyhook2/Tests/TestEffectTracker.hpp"
2 |
3 | Effect::Effect() : m_uid(PLH::UID::singleton()) {
4 | m_executed = false;
5 | }
6 |
7 | Effect& Effect::operator=(const Effect& rhs) {
8 | m_uid = rhs.m_uid;
9 | m_executed = rhs.m_executed;
10 | return *this;
11 | }
12 |
13 | void Effect::trigger() {
14 | m_executed = true;
15 | }
16 |
17 | bool Effect::didExecute() {
18 | return m_executed;
19 | }
20 |
21 | void EffectTracker::PushEffect() {
22 | m_effectQ.push_back(Effect());
23 | }
24 |
25 | Effect EffectTracker::PopEffect() {
26 | Effect effect = m_effectQ.back();
27 | m_effectQ.pop_back();
28 | return effect;
29 | }
30 |
31 | Effect& EffectTracker::PeakEffect() {
32 | if (m_effectQ.size() <= 0) {
33 | PolyHook2DebugBreak();
34 | PushEffect();
35 | }
36 |
37 | return m_effectQ.back();
38 | }
39 |
--------------------------------------------------------------------------------
/polyhook2/Detour/x86Detour.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by steve on 7/4/17.
3 | //
4 | #pragma once
5 |
6 | #include "polyhook2/PolyHookOs.hpp"
7 | #include "polyhook2/Detour/ADetour.hpp"
8 | #include "polyhook2/Enums.hpp"
9 | #include "polyhook2/Instruction.hpp"
10 |
11 | using namespace std::placeholders;
12 |
13 | namespace PLH {
14 |
15 | class x86Detour : public Detour {
16 | public:
17 | x86Detour(uint64_t fnAddress, uint64_t fnCallback, uint64_t* userTrampVar);
18 |
19 | virtual ~x86Detour() = default;
20 |
21 | virtual bool hook() override;
22 |
23 | Mode getArchType() const override;
24 |
25 | protected:
26 | bool fixSpecialCases(insts_t& prologue);
27 | bool fixCallRoutineReturningSP(Instruction& callInst, const insts_t& routine);
28 | bool fixCallInlineReturningSP(Instruction& callInst);
29 |
30 | bool makeTrampoline(insts_t& prologue, insts_t& trampolineOut);
31 | };
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/polyhook_2-config.cmake.in:
--------------------------------------------------------------------------------
1 | @PACKAGE_INIT@
2 |
3 | set(POLYHOOK_BUILD_SHARED_LIBS @POLYHOOK_BUILD_SHARED_LIBS@)
4 | set(POLYHOOK_BUILD_DLL @POLYHOOK_BUILD_SHARED_LIBS@)
5 | set(POLYHOOK_BUILD_STATIC_RUNTIME @POLYHOOK_BUILD_STATIC_RUNTIME@)
6 |
7 | set(POLYHOOK_FEATURE_EXCEPTION @POLYHOOK_FEATURE_EXCEPTION@)
8 | set(POLYHOOK_FEATURE_DETOURS @POLYHOOK_FEATURE_DETOURS@)
9 | set(POLYHOOK_FEATURE_INLINENTD @POLYHOOK_FEATURE_INLINENTD@)
10 | set(POLYHOOK_FEATURE_PE @POLYHOOK_FEATURE_PE@)
11 | set(POLYHOOK_FEATURE_VIRTUALS @POLYHOOK_FEATURE_VIRTUALS@)
12 |
13 | include(CMakeFindDependencyMacro)
14 | find_dependency(Zydis)
15 | if(POLYHOOK_FEATURE_DETOURS)
16 | find_dependency(asmjit)
17 | endif()
18 | if(POLYHOOK_FEATURE_INLINENTD)
19 | find_dependency(asmtk)
20 | endif()
21 |
22 | get_filename_component(POLYHOOK_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
23 | include("${POLYHOOK_CMAKE_DIR}/PolyHook_2-targets.cmake")
24 |
--------------------------------------------------------------------------------
/polyhook2/Exceptions/BreakPointHook.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYHOOK_2_0_BPHOOK_HPP
2 | #define POLYHOOK_2_0_BPHOOK_HPP
3 |
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/Exceptions/AVehHook.hpp"
6 | #include "polyhook2/Misc.hpp"
7 |
8 | namespace PLH {
9 |
10 | class BreakPointHook : public AVehHook {
11 | public:
12 | BreakPointHook(const uint64_t fnAddress, const uint64_t fnCallback);
13 | BreakPointHook(const char* fnAddress, const char* fnCallback);
14 | ~BreakPointHook() {
15 | m_impls.erase(AVehHookImpEntry(m_fnAddress, this));
16 | if (m_hooked) {
17 | unHook();
18 | }
19 | }
20 |
21 | virtual bool hook() override;
22 | virtual bool unHook() override;
23 | auto getProtectionObject() {
24 | return finally([&] () {
25 | hook();
26 | });
27 | }
28 | protected:
29 | uint64_t m_fnCallback;
30 | uint64_t m_fnAddress;
31 | uint8_t m_origByte;
32 |
33 | LONG OnException(EXCEPTION_POINTERS* ExceptionInfo) override;
34 | };
35 | }
36 | #endif
--------------------------------------------------------------------------------
/polyhook2/Exceptions/HWBreakPointHook.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYHOOK_2_0_HWBPHOOK_HPP
2 | #define POLYHOOK_2_0_HWBPHOOK_HPP
3 |
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/Exceptions/AVehHook.hpp"
6 | #include "polyhook2/Misc.hpp"
7 |
8 | namespace PLH {
9 |
10 | class HWBreakPointHook : public AVehHook {
11 | public:
12 | HWBreakPointHook(const uint64_t fnAddress, const uint64_t fnCallback, HANDLE hThread);
13 | HWBreakPointHook(const char* fnAddress, const char* fnCallback, HANDLE hThread);
14 | ~HWBreakPointHook() {
15 | m_impls.erase(AVehHookImpEntry(m_fnAddress, this));
16 | if (m_hooked) {
17 | unHook();
18 | }
19 | }
20 |
21 | virtual bool hook() override;
22 | virtual bool unHook() override;
23 | auto getProtectionObject() {
24 | return finally([&] () {
25 | hook();
26 | });
27 | }
28 | protected:
29 | uint64_t m_fnCallback;
30 | uint64_t m_fnAddress;
31 | uint8_t m_regIdx;
32 |
33 | HANDLE m_hThread;
34 |
35 | LONG OnException(EXCEPTION_POINTERS* ExceptionInfo) override;
36 | };
37 | }
38 |
39 | #endif
--------------------------------------------------------------------------------
/polyhook2/Virtuals/VFuncSwapHook.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYHOOK_2_0_VFUNCSWAPHOOK_HPP
2 | #define POLYHOOK_2_0_VFUNCSWAPHOOK_HPP
3 |
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/IHook.hpp"
6 | #include "polyhook2/MemProtector.hpp"
7 | #include "polyhook2/Misc.hpp"
8 |
9 | namespace PLH {
10 | typedef std::map VFuncMap;
11 |
12 |
13 | class VFuncSwapHook : public PLH::IHook {
14 | public:
15 | VFuncSwapHook(const uint64_t Class, const VFuncMap& redirectMap, VFuncMap* origVFuncs);
16 | VFuncSwapHook(const char* Class, const VFuncMap& redirectMap, VFuncMap* origVFuncs);
17 | virtual ~VFuncSwapHook() {
18 | if (m_hooked) {
19 | unHook();
20 | }
21 | }
22 |
23 | virtual bool hook() override;
24 | virtual bool unHook() override;
25 | virtual HookType getType() const override {
26 | return HookType::VTableSwap;
27 | }
28 | protected:
29 | uint16_t countVFuncs();
30 | uint64_t m_class;
31 | uintptr_t* m_vtable;
32 |
33 | uint16_t m_vFuncCount;
34 |
35 | // index -> ptr val
36 | VFuncMap m_redirectMap;
37 | VFuncMap* m_userOrigMap;
38 | };
39 | }
40 | #endif
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Stephen Eckels
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/polyhook2/Virtuals/VTableSwapHook.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYHOOK_2_0_VTBLSWAPHOOK_HPP
2 | #define POLYHOOK_2_0_VTBLSWAPHOOK_HPP
3 |
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/IHook.hpp"
6 | #include "polyhook2/MemProtector.hpp"
7 | #include "polyhook2/Misc.hpp"
8 |
9 | namespace PLH {
10 |
11 | typedef std::map VFuncMap;
12 |
13 | class VTableSwapHook : public PLH::IHook {
14 | public:
15 | VTableSwapHook(const uint64_t Class, VFuncMap* origVFuncs);
16 | VTableSwapHook(const uint64_t Class, const VFuncMap& redirectMap, VFuncMap* origVFuncs);
17 | VTableSwapHook(const char* Class, const VFuncMap& redirectMap, VFuncMap* origVFuncs);
18 |
19 | virtual ~VTableSwapHook() {
20 | if (m_hooked) {
21 | unHook();
22 | }
23 | }
24 |
25 | virtual bool hook() override;
26 | virtual bool unHook() override;
27 | virtual HookType getType() const override {
28 | return HookType::VTableSwap;
29 | }
30 | protected:
31 | uint16_t countVFuncs();
32 |
33 | std::unique_ptr m_newVtable;
34 | uintptr_t* m_origVtable;
35 |
36 | uint64_t m_class;
37 |
38 | uint16_t m_vFuncCount;
39 |
40 | // index -> ptr val
41 | VFuncMap m_redirectMap;
42 | VFuncMap* m_userOrigMap;
43 | };
44 |
45 | }
46 |
47 | #endif
--------------------------------------------------------------------------------
/polyhook2/ErrorLog.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYHOOK_2_0_ERRORLOG_HPP
2 | #define POLYHOOK_2_0_ERRORLOG_HPP
3 |
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/Enums.hpp"
6 |
7 | namespace PLH {
8 |
9 | // abstract base class for logging, clients should subclass this to intercept log messages
10 | class Logger
11 | {
12 | public:
13 | // copy
14 | virtual void log(const std::string& msg, ErrorLevel level) = 0;
15 | virtual ~Logger() {};
16 | };
17 |
18 | // class for registering client loggers
19 | class Log
20 | {
21 | private:
22 | static std::shared_ptr m_logger;
23 | public:
24 | static void registerLogger(std::shared_ptr logger);
25 | static void log(std::string msg, ErrorLevel level);
26 | };
27 |
28 | // simple logger implementation
29 |
30 | struct Error {
31 | std::string msg;
32 | ErrorLevel lvl;
33 | };
34 |
35 | class ErrorLog : public Logger {
36 | public:
37 | void setLogLevel(ErrorLevel level);
38 |
39 | //copy
40 | void log(const std::string& msg, ErrorLevel level) override;
41 |
42 | // copy
43 | void push(const Error& err);
44 |
45 | Error pop();
46 | static ErrorLog& singleton();
47 | private:
48 | std::vector m_log;
49 | ErrorLevel m_logLevel = ErrorLevel::INFO;
50 | };
51 |
52 | }
53 |
54 | #endif
55 |
--------------------------------------------------------------------------------
/sources/ErrorLog.cpp:
--------------------------------------------------------------------------------
1 | #include "polyhook2/ErrorLog.hpp"
2 |
3 | std::shared_ptr PLH::Log::m_logger = nullptr;
4 |
5 | void PLH::Log::registerLogger(std::shared_ptr logger) {
6 | m_logger = logger;
7 | }
8 |
9 | void PLH::Log::log(std::string msg, ErrorLevel level) {
10 | if (m_logger) {
11 | m_logger->log(std::move(msg), level);
12 | }
13 | }
14 |
15 | void PLH::ErrorLog::setLogLevel(PLH::ErrorLevel level) {
16 | m_logLevel = level;
17 | }
18 |
19 | void PLH::ErrorLog::log(const std::string& msg, ErrorLevel level)
20 | {
21 | push({ msg, level });
22 | }
23 |
24 | void PLH::ErrorLog::push(const PLH::Error& err) {
25 | if (err.lvl >= m_logLevel) {
26 | switch (err.lvl) {
27 | case ErrorLevel::INFO:
28 | std::cout << "[+] Info: " << err.msg << std::endl;
29 | break;
30 | case ErrorLevel::WARN:
31 | std::cout << "[!] Warn: " << err.msg << std::endl;
32 | break;
33 | case ErrorLevel::SEV:
34 | std::cout << "[!] Error: " << err.msg << std::endl;
35 | break;
36 | default:
37 | std::cout << "Unsupported error message logged " << err.msg << std::endl;
38 | }
39 | }
40 |
41 | m_log.push_back(err);
42 | }
43 |
44 | PLH::Error PLH::ErrorLog::pop() {
45 | Error err{};
46 | if (!m_log.empty()) {
47 | err = m_log.back();
48 | m_log.pop_back();
49 | }
50 | return err;
51 | }
52 |
53 | PLH::ErrorLog& PLH::ErrorLog::singleton() {
54 | static ErrorLog log;
55 | return log;
56 | }
57 |
--------------------------------------------------------------------------------
/polyhook2/PE/IatHook.hpp:
--------------------------------------------------------------------------------
1 | //https://github.com/odzhan/shellcode/blob/master/os/win/getapi/dynamic/getapi.c
2 | //https://modexp.wordpress.com/2017/01/15/shellcode-resolving-api-addresses/
3 |
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/ErrorLog.hpp"
6 | #include "polyhook2/IHook.hpp"
7 | #include "polyhook2/MemProtector.hpp"
8 | #include "polyhook2/Misc.hpp"
9 | #include "polyhook2/PE/PEB.hpp"
10 |
11 | #define RVA2VA(type, base, rva) (type)((ULONG_PTR) base + rva)
12 |
13 | namespace PLH {
14 | class IatHook : public IHook {
15 | public:
16 | IatHook(const std::string& dllName, const std::string& apiName, const char* fnCallback, uint64_t* userOrigVar, const std::wstring& moduleName);
17 | IatHook(const std::string& dllName, const std::string& apiName, const uint64_t fnCallback, uint64_t* userOrigVar, const std::wstring& moduleName);
18 | virtual ~IatHook() {
19 | if (m_hooked) {
20 | unHook();
21 | }
22 | }
23 |
24 | virtual bool hook() override;
25 | virtual bool unHook() override;
26 | virtual HookType getType() const override {
27 | return HookType::IAT;
28 | }
29 | protected:
30 | IMAGE_THUNK_DATA* FindIatThunk(const std::string& dllName, const std::string& apiName, const std::wstring& moduleName = L"");
31 | IMAGE_THUNK_DATA* FindIatThunkInModule(void* moduleBase, const std::string& dllName, const std::string& apiName);
32 |
33 | std::string m_dllName;
34 | std::string m_apiName;
35 | std::wstring m_moduleName;
36 |
37 | uint64_t m_fnCallback;
38 | uint64_t m_origFunc;
39 | uint64_t* m_userOrigVar;
40 | };
41 | }
--------------------------------------------------------------------------------
/polyhook2/MemProtector.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by steve on 7/10/17.
3 | //
4 |
5 | #ifndef POLYHOOK_2_MEMORYPROTECTOR_HPP
6 | #define POLYHOOK_2_MEMORYPROTECTOR_HPP
7 |
8 | #include "polyhook2/PolyHookOs.hpp"
9 | #include "polyhook2/MemAccessor.hpp"
10 | #include "polyhook2/Enums.hpp"
11 |
12 | std::ostream& operator<<(std::ostream& os, const PLH::ProtFlag v);
13 |
14 | // prefer enum class over enum
15 | #pragma warning( disable : 26812)
16 |
17 | namespace PLH {
18 | int TranslateProtection(const PLH::ProtFlag flags);
19 | ProtFlag TranslateProtection(const int prot);
20 |
21 | class MemoryProtector {
22 | public:
23 | MemoryProtector(const uint64_t address, const uint64_t length, const PLH::ProtFlag prot, MemAccessor& accessor, bool unsetOnDestroy = true) : m_accessor(accessor) {
24 | m_address = address;
25 | m_length = length;
26 | unsetLater = unsetOnDestroy;
27 |
28 | m_origProtection = PLH::ProtFlag::UNSET;
29 | m_origProtection = m_accessor.mem_protect(address, length, prot, status);
30 | }
31 |
32 | PLH::ProtFlag originalProt() {
33 | return m_origProtection;
34 | }
35 |
36 | bool isGood() {
37 | return status;
38 | }
39 |
40 | ~MemoryProtector() {
41 | if (m_origProtection == PLH::ProtFlag::UNSET || !unsetLater)
42 | return;
43 |
44 | m_accessor.mem_protect(m_address, m_length, m_origProtection, status);
45 | }
46 | private:
47 | PLH::ProtFlag m_origProtection;
48 | MemAccessor& m_accessor;
49 |
50 | uint64_t m_address;
51 | uint64_t m_length;
52 | bool status;
53 | bool unsetLater;
54 | };
55 | }
56 | #endif //POLYHOOK_2_MEMORYPROTECTOR_HPP
57 |
--------------------------------------------------------------------------------
/UnitTests/windows/TestIatHook.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "polyhook2/PE/IatHook.hpp"
3 | #include "polyhook2/Tests/TestEffectTracker.hpp"
4 | #include "polyhook2/Tests/StackCanary.hpp"
5 | #include "polyhook2/PolyHookOsIncludes.hpp"
6 |
7 | EffectTracker iatEffectTracker;
8 |
9 | typedef DWORD(__stdcall* tGetCurrentThreadId)();
10 | uint64_t oGetCurrentThreadID;
11 |
12 | NOINLINE DWORD __stdcall hkGetCurrentThreadId() {
13 | iatEffectTracker.PeakEffect().trigger();
14 | return ((tGetCurrentThreadId)oGetCurrentThreadID)();
15 | }
16 |
17 | TEST_CASE("Iat Hook Tests", "[IatHook]") {
18 | SECTION("Verify api thunk is found and hooked") {
19 | PLH::StackCanary canary;
20 | volatile DWORD thrdId2 = GetCurrentThreadId();
21 | UNREFERENCED_PARAMETER(thrdId2);
22 | PLH::IatHook hook("kernel32.dll", "GetCurrentThreadId", (char*)&hkGetCurrentThreadId, (uint64_t*)&oGetCurrentThreadID, L"");
23 | REQUIRE(hook.hook());
24 |
25 | iatEffectTracker.PushEffect();
26 | REQUIRE(canary.isStackGood());
27 | volatile DWORD thrdId = GetCurrentThreadId();
28 | thrdId++;
29 | REQUIRE(iatEffectTracker.PopEffect().didExecute());
30 | REQUIRE(hook.unHook());
31 | }
32 |
33 | SECTION("Verify api thunk is found and hooked when module explicitly named") {
34 | PLH::StackCanary canary;
35 | PLH::IatHook hook("kernel32.dll", "GetCurrentThreadId", (char*)&hkGetCurrentThreadId, (uint64_t*)&oGetCurrentThreadID, L"polyhook_2.exe");
36 | REQUIRE(hook.hook());
37 |
38 | iatEffectTracker.PushEffect();
39 | volatile DWORD thrdId = GetCurrentThreadId();
40 | thrdId++;
41 | REQUIRE(hook.unHook());
42 | }
43 | }
--------------------------------------------------------------------------------
/UnitTests/linux/TestDetourTranslationx64.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 |
6 | #include "polyhook2/Detour/x64Detour.hpp"
7 | #include "polyhook2/PolyHookOsIncludes.hpp"
8 | #include "polyhook2/Tests/StackCanary.hpp"
9 | #include "polyhook2/Tests/TestEffectTracker.hpp"
10 |
11 | #include "../TestUtils.hpp"
12 |
13 | namespace {
14 | EffectTracker effects;
15 | }
16 |
17 | // TODO: Translation + INPLACE scheme
18 |
19 | PLH_TEST_DETOUR_CALLBACK(dlmopen, {
20 | printf("Hooked dlmopen\n");
21 | });
22 |
23 | TEST_CASE("Testing Detours with Translations", "[Translation][ADetour]") {
24 | PLH::test::registerTestLogger();
25 |
26 | // dlmopen may or may not have instructions requiring translations.
27 | // Hence, this test was disabled. We need to construct reliable synthetic tests instead.
28 | #if 0
29 | SECTION("dlmopen (INPLACE)") {
30 | PLH::StackCanary canary;
31 |
32 | const auto* handleBefore = dlmopen(LM_ID_BASE, LIBM_SO, RTLD_NOW);
33 |
34 | PLH::x64Detour detour((uint64_t)dlmopen, (uint64_t)dlmopen_hooked, &dlmopen_trmp);
35 | // Only INPLACE creates conditions for translation, since
36 | // trampoline will be close to 0x0, where as
37 | // dlmopen will be close to 0x00007F__________
38 | detour.setDetourScheme(PLH::x64Detour::detour_scheme_t::INPLACE);
39 | REQUIRE(detour.hook());
40 |
41 | effects.PushEffect();
42 | const auto* handleAfter = dlmopen(LM_ID_BASE, LIBM_SO, RTLD_NOW);
43 |
44 | REQUIRE(detour.hasDiagnostic(PLH::Diagnostic::TranslatedInstructions));
45 | REQUIRE(effects.PopEffect().didExecute());
46 | REQUIRE(handleAfter == handleBefore);
47 |
48 | REQUIRE(detour.unHook());
49 | }
50 | #endif
51 | }
52 |
--------------------------------------------------------------------------------
/sources/BreakPointHook.cpp:
--------------------------------------------------------------------------------
1 | #include "polyhook2/Exceptions/BreakPointHook.hpp"
2 |
3 | PLH::BreakPointHook::BreakPointHook(const uint64_t fnAddress, const uint64_t fnCallback) : AVehHook() {
4 | m_fnCallback = fnCallback;
5 | m_fnAddress = fnAddress;
6 |
7 | auto entry = AVehHookImpEntry(fnAddress, this);
8 | assert(m_impls.find(entry) == m_impls.end());
9 | m_impls.insert(entry);
10 | }
11 |
12 | PLH::BreakPointHook::BreakPointHook(const char* fnAddress, const char* fnCallback) : AVehHook() {
13 | m_fnCallback = (uint64_t)fnCallback;
14 | m_fnAddress = (uint64_t)fnAddress;
15 |
16 | auto entry = AVehHookImpEntry((uint64_t)fnAddress, this);
17 | assert(m_impls.find(entry) == m_impls.end());
18 | m_impls.insert(entry);
19 | }
20 |
21 | bool PLH::BreakPointHook::hook() {
22 | MemoryProtector prot(m_fnAddress, 1, ProtFlag::R | ProtFlag::W | ProtFlag::X, *this);
23 | m_origByte = *(uint8_t*)m_fnAddress;
24 | *(uint8_t*)m_fnAddress = 0xCC;
25 | m_hooked = true;
26 | return true;
27 | }
28 |
29 | bool PLH::BreakPointHook::unHook() {
30 | assert(m_hooked);
31 | if (!m_hooked) {
32 | Log::log("BPHook unhook failed: no hook present", ErrorLevel::SEV);
33 | return false;
34 | }
35 |
36 | MemoryProtector prot(m_fnAddress, 1, ProtFlag::R | ProtFlag::W | ProtFlag::X, *this);
37 | *(uint8_t*)m_fnAddress = m_origByte;
38 | m_hooked = false;
39 | return true;
40 | }
41 |
42 | LONG PLH::BreakPointHook::OnException(EXCEPTION_POINTERS* ExceptionInfo) {
43 | if (ExceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_BREAKPOINT)
44 | return EXCEPTION_CONTINUE_SEARCH;
45 |
46 | // restored via getProtectionObject()
47 | unHook();
48 | ExceptionInfo->ContextRecord->XIP = (decltype(ExceptionInfo->ContextRecord->XIP))m_fnCallback;
49 | return EXCEPTION_CONTINUE_EXECUTION;
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/polyhook2/MemAccessor.hpp:
--------------------------------------------------------------------------------
1 |
2 | #ifndef POLYHOOK_2_MEMORYACCESSOR_HPP
3 | #define POLYHOOK_2_MEMORYACCESSOR_HPP
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/Enums.hpp"
6 |
7 | #define MEMORY_ROUND(_numToRound_, _multiple_) \
8 | (_numToRound_ & (((size_t)-1) ^ (_multiple_ - 1)))
9 |
10 | // Round _numToRound_ to the next higher _multiple_
11 | #define MEMORY_ROUND_UP(_numToRound_, _multiple_) \
12 | ((_numToRound_ + (_multiple_ - 1)) & (((size_t)-1) ^ (_multiple_ - 1)))
13 |
14 | namespace PLH {
15 | /**
16 | Overriding these routines can allow cross-process/cross-arch hooks
17 | **/
18 | class MemAccessor {
19 | public:
20 | virtual ~MemAccessor() = default;
21 |
22 | /**
23 | Defines a memory read/write routine that may fail ungracefully. It's expected
24 | this library will only ever use this routine in cases that are expected to succeed.
25 | **/
26 | virtual bool mem_copy(uint64_t dest, uint64_t src, uint64_t size) const;
27 |
28 | /**
29 | Defines a memory write routine that will not throw exceptions, and can handle potential
30 | writes to NO_ACCESS or otherwise innaccessible memory pages. Defaults to writeprocessmemory.
31 | Must fail gracefully
32 | **/
33 | virtual bool safe_mem_write(uint64_t dest, uint64_t src, uint64_t size, size_t& written) const noexcept;
34 |
35 | /**
36 | Defines a memory read routine that will not throw exceptions, and can handle potential
37 | reads from NO_ACCESS or otherwise innaccessible memory pages. Defaults to readprocessmemory.
38 | Must fail gracefully
39 | **/
40 | virtual bool safe_mem_read(uint64_t src, uint64_t dest, size_t size, size_t& read) const noexcept;
41 |
42 | virtual PLH::ProtFlag mem_protect(uint64_t dest, uint64_t size, PLH::ProtFlag newProtection, bool& status) const;
43 | };
44 | }
45 | #endif
46 |
--------------------------------------------------------------------------------
/UnitTests/windows/TestBreakpointHook.cpp:
--------------------------------------------------------------------------------
1 | ////
2 | //// Created by steve on 7/9/18.
3 | ////
4 | //#include
5 | //
6 | //#include
7 | //
8 | //#include "polyhook2/Exceptions/BreakPointHook.hpp"
9 | //#include "polyhook2/tests/TestEffectTracker.hpp"
10 | //
11 | //EffectTracker effects2;
12 | //
13 | //NOINLINE int hookMe() {
14 | // volatile int i = 0;
15 | // i += 1;
16 | // i += 2;
17 | // return i;
18 | //}
19 | //
20 | //std::shared_ptr bpHook; // must be ptr because we need to call getProtectionObject
21 | //NOINLINE int hookMeCallback() {
22 | // auto protObj = bpHook->getProtectionObject();
23 | // volatile int i = 0;
24 | // i += 1;
25 | //
26 | // effects2.PeakEffect().trigger();
27 | // return hookMe(); // just call original yourself now
28 | //}
29 | //
30 | //TEST_CASE("Testing Software Breakpoint", "[AVehHook],[BreakpointHook]") {
31 | // SECTION("Verify callback is executed") {
32 | // bpHook = std::make_shared((char*)&hookMe, (char*)&hookMeCallback);
33 | // REQUIRE(bpHook->hook() == true);
34 | //
35 | // effects2.PushEffect();
36 | //
37 | // REQUIRE(hookMe() == 3);
38 | // REQUIRE(effects2.PopEffect().didExecute());
39 | // bpHook->unHook();
40 | // bpHook.reset();
41 | // }
42 | //
43 | // SECTION("Verify multiple calls in a row reprotect") {
44 | // bpHook = std::make_shared((char*)&hookMe, (char*)&hookMeCallback);
45 | // REQUIRE(bpHook->hook() == true);
46 | //
47 | // effects2.PushEffect();
48 | // REQUIRE(hookMe() == 3);
49 | // REQUIRE(effects2.PopEffect().didExecute());
50 | //
51 | // effects2.PushEffect();
52 | // REQUIRE(hookMe() == 3);
53 | // REQUIRE(effects2.PopEffect().didExecute());
54 | //
55 | // effects2.PushEffect();
56 | // REQUIRE(hookMe() == 3);
57 | // REQUIRE(effects2.PopEffect().didExecute());
58 | // bpHook->unHook();
59 | // }
60 | //}
--------------------------------------------------------------------------------
/polyhook2/RangeAllocator.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYHOOK_2_PAGEALLOCATOR_HPP
2 | #define POLYHOOK_2_PAGEALLOCATOR_HPP
3 |
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/Misc.hpp"
6 | #include "polyhook2/FBAllocator.hpp"
7 |
8 | namespace PLH {
9 |
10 | // wrapper over fb_allocator in C, with heap backing from VirtualAlloc2 to enforce range
11 | class FBAllocator
12 | {
13 | public:
14 | FBAllocator(uint64_t min, uint64_t max, uint8_t blockSize, uint8_t blockCount);
15 | ~FBAllocator();
16 | bool initialize();
17 |
18 | char* allocate();
19 |
20 | char* callocate(uint8_t num);
21 |
22 | void deallocate(char* mem);
23 |
24 | bool inRange(uint64_t addr);
25 |
26 | bool intersectsRange(uint64_t min, uint64_t max);
27 |
28 | // if a range intersections, by what % of the given range is the overlap
29 | uint8_t intersectionLoadFactor(uint64_t min, uint64_t max);
30 | private:
31 | bool m_alloc2Supported;
32 | uint8_t m_usedBlocks;
33 | uint8_t m_maxBlocks;
34 | uint8_t m_blockSize;
35 | uint64_t m_min;
36 | uint64_t m_max;
37 | uint64_t m_dataPool;
38 |
39 | ALLOC_Allocator* m_allocator;
40 | ALLOC_HANDLE m_hAllocator;
41 | };
42 |
43 | class RangeAllocator
44 | {
45 | public:
46 | RangeAllocator(uint8_t blockSize, uint8_t blockCount);
47 | ~RangeAllocator() = default;
48 |
49 | char* allocate(uint64_t min, uint64_t max);
50 | void deallocate(uint64_t addr);
51 | private:
52 | std::shared_ptr findOrInsertAllocator(uint64_t min, uint64_t max);
53 |
54 | uint8_t m_maxBlocks;
55 | uint8_t m_blockSize;
56 | std::mutex m_mutex;
57 | std::vector> m_allocators;
58 | std::unordered_map> m_allocMap;
59 | };
60 |
61 | }
62 |
63 | #endif
--------------------------------------------------------------------------------
/UnitTests/windows/TestHWBreakpointHook.cpp:
--------------------------------------------------------------------------------
1 | ////
2 | //// Created by steve on 7/9/18.
3 | ////
4 | //#include
5 | //
6 | //#include
7 | //
8 | //#include "polyhook2/Exceptions/HWBreakPointHook.hpp"
9 | //#include "polyhook2/tests/TestEffectTracker.hpp"
10 | //
11 | //EffectTracker effects3;
12 | //
13 | //NOINLINE int hookMeHWBP() {
14 | // volatile int i = 0;
15 | // i += 1;
16 | // i += 2;
17 | // return i;
18 | //}
19 | //
20 | //std::shared_ptr hwBpHook; // must be ptr because we need to call getProtectionObject
21 | //NOINLINE int hookMeCallbackHWBP() {
22 | // auto protObj = hwBpHook->getProtectionObject();
23 | // volatile int i = 0;
24 | // i += 1;
25 | //
26 | // effects3.PeakEffect().trigger();
27 | // return hookMeHWBP(); // just call original yourself now
28 | //}
29 | //
30 | //TEST_CASE("Testing Hardware Breakpoints", "[AVehHook],[HWBreakPointHook]") {
31 | // SECTION("Verify callback is executed") {
32 | // hwBpHook = std::make_shared((char*)&hookMeHWBP, (char*)&hookMeCallbackHWBP);
33 | // REQUIRE(hwBpHook->hook() == true);
34 | //
35 | // effects3.PushEffect();
36 | // REQUIRE(hookMeHWBP() == 3);
37 | // REQUIRE(effects3.PopEffect().didExecute());
38 | // hwBpHook->unHook();
39 | // hwBpHook.reset();
40 | // }
41 | //
42 | // SECTION("Verify multiple calls in a row reprotect") {
43 | // hwBpHook = std::make_shared((char*)&hookMeHWBP, (char*)&hookMeCallbackHWBP);
44 | // REQUIRE(hwBpHook->hook() == true);
45 | //
46 | // effects3.PushEffect();
47 | // REQUIRE(hookMeHWBP() == 3);
48 | // REQUIRE(effects3.PopEffect().didExecute());
49 | //
50 | // effects3.PushEffect();
51 | // REQUIRE(hookMeHWBP() == 3);
52 | // REQUIRE(effects3.PopEffect().didExecute());
53 | //
54 | // effects3.PushEffect();
55 | // REQUIRE(hookMeHWBP() == 3);
56 | // REQUIRE(effects3.PopEffect().didExecute());
57 | // hwBpHook->unHook();
58 | // }
59 | //}
--------------------------------------------------------------------------------
/polyhook2/PE/EatHook.hpp:
--------------------------------------------------------------------------------
1 | //https://github.com/odzhan/shellcode/blob/master/os/win/getapi/dynamic/getapi.c
2 | //https://modexp.wordpress.com/2017/01/15/shellcode-resolving-api-addresses/
3 |
4 | #include "polyhook2/PolyHookOs.hpp"
5 | #include "polyhook2/ErrorLog.hpp"
6 | #include "polyhook2/IHook.hpp"
7 | #include "polyhook2/MemProtector.hpp"
8 | #include "polyhook2/Misc.hpp"
9 | #include "polyhook2/PE/PEB.hpp"
10 | #include "polyhook2/ZydisDisassembler.hpp"
11 | #include "polyhook2/RangeAllocator.hpp"
12 |
13 | #define RVA2VA(type, base, rva) (type)((ULONG_PTR) base + rva)
14 |
15 | namespace PLH {
16 | class EatHook : public IHook {
17 | public:
18 | EatHook(const std::string& apiName, const std::wstring& moduleName, const char* fnCallback, uint64_t* userOrigVar);
19 | EatHook(const std::string& apiName, const std::wstring& moduleName, const uint64_t fnCallback, uint64_t* userOrigVar);
20 | EatHook(const std::string& apiName, const HMODULE moduleHandle, const char* fnCallback, uint64_t* userOrigVar);
21 | EatHook(const std::string& apiName, const HMODULE moduleHandle, const uint64_t fnCallback, uint64_t* userOrigVar);
22 | virtual ~EatHook()
23 | {
24 | if (m_trampoline) {
25 | m_allocator.deallocate(m_trampoline);
26 | m_trampoline = 0;
27 | }
28 | }
29 |
30 | virtual bool hook() override;
31 | virtual bool unHook() override;
32 |
33 | virtual HookType getType() const override {
34 | return HookType::EAT;
35 | }
36 | protected:
37 | EatHook(std::string apiName, std::wstring moduleName, HMODULE moduleHandle, uint64_t fnCallback, uint64_t* userOrigVar);
38 |
39 | uint32_t* FindEatFunction();
40 | uint32_t* FindEatFunctionInModule() const;
41 | uint64_t FindModule();
42 |
43 | const uint16_t m_trampolineSize = 32;
44 |
45 | std::wstring m_moduleName;
46 | std::string m_apiName;
47 |
48 | uint64_t m_fnCallback;
49 | uint64_t m_origFunc;
50 | uint64_t* m_userOrigVar;
51 |
52 | // only used if EAT offset points >= 2GB
53 | RangeAllocator m_allocator;
54 | uint64_t m_trampoline;
55 |
56 | uint64_t m_moduleBase;
57 | };
58 | }
--------------------------------------------------------------------------------
/CMakeSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "name": "x86-Debug",
5 | "generator": "Visual Studio 17 2022",
6 | "configurationType": "Debug",
7 | "buildRoot": "${projectDir}\\build32",
8 | "cmakeCommandArgs": "",
9 | "buildCommandArgs": "-m -v:minimal",
10 | "ctestCommandArgs": "",
11 | "inheritEnvironments": []
12 | },
13 | {
14 | "name": "x86-Release",
15 | "generator": "Visual Studio 17 2022",
16 | "configurationType": "Release",
17 | "buildRoot": "${projectDir}\\build32",
18 | "cmakeCommandArgs": "",
19 | "buildCommandArgs": "-m -v:minimal",
20 | "ctestCommandArgs": "",
21 | "inheritEnvironments": []
22 | },
23 | {
24 | "name": "x86-ReleaseWithDebInfo",
25 | "generator": "Visual Studio 17 2022",
26 | "configurationType": "RelWithDebInfo",
27 | "buildRoot": "${projectDir}\\build32",
28 | "cmakeCommandArgs": "",
29 | "buildCommandArgs": "-m -v:minimal",
30 | "ctestCommandArgs": "",
31 | "inheritEnvironments": []
32 | },
33 | {
34 | "name": "x64-Debug",
35 | "generator": "Visual Studio 17 2022 Win64",
36 | "configurationType": "Debug",
37 | "buildRoot": "${projectDir}\\build64",
38 | "cmakeCommandArgs": "",
39 | "buildCommandArgs": "-m -v:minimal",
40 | "ctestCommandArgs": "",
41 | "inheritEnvironments": []
42 | },
43 | {
44 | "name": "x64-Release",
45 | "generator": "Visual Studio 17 2022 Win64",
46 | "configurationType": "Release",
47 | "buildRoot": "${projectDir}\\build64",
48 | "cmakeCommandArgs": "",
49 | "buildCommandArgs": "-m -v:minimal",
50 | "ctestCommandArgs": "",
51 | "inheritEnvironments": []
52 | },
53 | {
54 | "name": "x64-ReleaseWithDebInfo",
55 | "generator": "Visual Studio 17 2022 Win64",
56 | "configurationType": "RelWithDebInfo",
57 | "buildRoot": "${projectDir}\\build64",
58 | "cmakeCommandArgs": "",
59 | "buildCommandArgs": "-m -v:minimal",
60 | "ctestCommandArgs": "",
61 | "inheritEnvironments": []
62 | }
63 | ]
64 | }
--------------------------------------------------------------------------------
/UnitTests/windows/TestVTableSwapHook.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 |
5 | #include "polyhook2/Virtuals/VTableSwapHook.hpp"
6 | #include "polyhook2/Tests/StackCanary.hpp"
7 | #include "polyhook2/Tests/TestEffectTracker.hpp"
8 |
9 | EffectTracker vTblSwapEffects;
10 |
11 | class VirtualTest {
12 | public:
13 | virtual ~VirtualTest() {}
14 |
15 | virtual int __stdcall NoParamVirt() {
16 | return 4;
17 | }
18 |
19 | virtual int __stdcall NoParamVirt2() {
20 | return 7;
21 | }
22 | };
23 |
24 | #pragma warning(disable: 4100)
25 | PLH::VFuncMap origVFuncs;
26 | HOOK_CALLBACK(&VirtualTest::NoParamVirt, hkVirtNoParams, {
27 | vTblSwapEffects.PeakEffect().trigger();
28 | return ((hkVirtNoParams_t)origVFuncs.at(1))(_args...);
29 | });
30 |
31 | HOOK_CALLBACK(&VirtualTest::NoParamVirt2, hkVirt2NoParams, {
32 | vTblSwapEffects.PeakEffect().trigger();
33 | return ((hkVirt2NoParams_t)origVFuncs.at(2))(_args...);
34 | });
35 |
36 | TEST_CASE("VTableSwap tests", "[VTableSwap]") {
37 | std::shared_ptr ClassToHook(new VirtualTest);
38 |
39 | SECTION("Verify vtable redirected") {
40 | PLH::StackCanary canary;
41 | PLH::VFuncMap redirect = {{(uint16_t)1, (uint64_t)hkVirtNoParams}};
42 | PLH::VTableSwapHook hook((char*)ClassToHook.get(), redirect, &origVFuncs);
43 | REQUIRE(hook.hook());
44 | REQUIRE(origVFuncs.size() == 1);
45 |
46 | vTblSwapEffects.PushEffect();
47 | ClassToHook->NoParamVirt();
48 | REQUIRE(vTblSwapEffects.PopEffect().didExecute());
49 | REQUIRE(hook.unHook());
50 | }
51 |
52 | SECTION("Verify multiple vtable redirected") {
53 | PLH::StackCanary canary;
54 | PLH::VFuncMap redirect = {{(uint16_t)1, (uint64_t)hkVirtNoParams},{(uint16_t)2, (uint64_t)hkVirtNoParams}};
55 | PLH::VTableSwapHook hook((char*)ClassToHook.get(), redirect, &origVFuncs);
56 | REQUIRE(hook.hook());
57 | REQUIRE(origVFuncs.size() == 2);
58 |
59 | vTblSwapEffects.PushEffect();
60 | ClassToHook->NoParamVirt();
61 | REQUIRE(vTblSwapEffects.PopEffect().didExecute());
62 |
63 | vTblSwapEffects.PushEffect();
64 | ClassToHook->NoParamVirt2();
65 | REQUIRE(vTblSwapEffects.PopEffect().didExecute());
66 | REQUIRE(hook.unHook());
67 | }
68 | }
--------------------------------------------------------------------------------
/polyhook2/PolyHookOs.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #if defined(WIN64) || defined(_WIN64) || defined(__MINGW64__)
4 | #define POLYHOOK2_OS_WINDOWS
5 | #define POLYHOOK2_ARCH_X64
6 |
7 | #ifdef __GNUC__
8 |
9 | // VirtualAlloc2 requires NTDII_WIN10_RS4 on my distrubition of mingw
10 | #define NTDDI_VERSION NTDDI_WIN10_RS4
11 |
12 | // This was taken from Microsofts Detours library
13 | #define ERROR_DYNAMIC_CODE_BLOCKED 1655L
14 |
15 | #endif
16 |
17 | #elif defined(WIN32) || defined(_WIN32) || defined(__MINGW32__)
18 | #define POLYHOOK2_OS_WINDOWS
19 | #define POLYHOOK2_ARCH_X86
20 |
21 | #ifdef __GNUC__
22 |
23 | // VirtualAlloc2 requires NTDII_WIN10_RS4 on my distrubition of mingw
24 | #define NTDDI_VERSION NTDDI_WIN10_RS4
25 |
26 | // This was taken from Microsofts Detours library
27 | #define ERROR_DYNAMIC_CODE_BLOCKED 1655L
28 |
29 | #endif
30 |
31 | #elif defined(__linux__) || defined(linux)
32 | #if defined(__x86_64__)
33 | #define POLYHOOK2_OS_LINUX
34 | #define POLYHOOK2_ARCH_X64
35 | #else
36 | #define POLYHOOK2_OS_LINUX
37 | #define POLYHOOK2_ARCH_X86
38 | #endif
39 | #elif defined(__APPLE__)
40 | #if defined(__x86_64__)
41 | #define POLYHOOK2_OS_APPLE
42 | #define POLYHOOK2_ARCH_X64
43 | #else
44 | #define POLYHOOK2_OS_APPLE
45 | #define POLYHOOK2_ARCH_X86
46 | #endif
47 | #endif
48 |
49 | #if defined(_MSC_VER)
50 | #define PLH_INLINE __forceinline
51 | #elif defined(__GNUC__)
52 | #define PLH_INLINE inline __attribute__((always_inline))
53 | #else
54 | #define PLH_INLINE inline
55 | #endif
56 |
57 | #include //for debug printing
58 | #include
59 | #include //setw
60 | #include
61 |
62 | #include
63 | #include