├── Docs ├── .nojekyll ├── _sidebar.md ├── gallery.md ├── faq.md ├── debugging.md ├── concept.md ├── index.html ├── setup.md ├── usage.md ├── crash-analysis.md ├── README.md └── fuzzing-vm-setup.md ├── .clang-tidy ├── Broker ├── Headers │ ├── Resource.h │ ├── Broker.hpp │ ├── pch.hpp │ ├── Connectors │ │ ├── Dummy.hpp │ │ ├── JsonQueue.hpp │ │ └── Base.hpp │ ├── ConnectorManager.hpp │ ├── BrokerUtils.hpp │ ├── States.hpp │ ├── IrpManager.hpp │ ├── ManagerBase.hpp │ ├── DriverManager.hpp │ ├── Context.hpp │ ├── Error.hpp │ └── ServiceManager.hpp ├── Source │ ├── Connectors │ │ ├── JsonQueue.cpp │ │ └── Dummy.cpp │ ├── Broker.rc.in │ ├── Main.cpp │ ├── ManagerBase.cpp │ ├── ConnectorManager.cpp │ └── Context.cpp ├── Tests │ └── CMakeLists.txt └── CMakeLists.txt ├── .github ├── FUNDING.yml ├── workflows │ ├── docs.yml │ ├── test.yml │ ├── build.yml │ └── notify.yml └── Invoke-VisualStudio.ps1 ├── Assets ├── Fonts │ ├── fa-solid-900.ttf │ ├── fa-brands-400.ttf │ └── fa-regular-400.ttf └── Images │ └── logo │ ├── logo.ico │ ├── Logo_v1.png │ ├── Logo_v2.png │ ├── Logo_v2_small.png │ └── Logo_v1.svg ├── .gitattributes ├── cmake ├── FindPhNt.cmake ├── FindCatch2.cmake ├── FindJson.cmake ├── FindWil.cmake ├── FindArgparse.cmake ├── FindImgui.cmake └── FindWdk.cmake ├── .editorconfig ├── Driver ├── Headers │ ├── Native.hpp │ ├── CapturedIrpManager.hpp │ ├── Context.hpp │ ├── HookedDriverManager.hpp │ ├── Callbacks.hpp │ ├── HookedDriver.hpp │ └── CapturedIrp.hpp ├── Source │ ├── Driver.rc.in │ ├── IrpMonitor.inf │ ├── CapturedIrpManager.cpp │ ├── HookedDriver.cpp │ ├── HookedDriverManager.cpp │ └── DriverUtils.cpp ├── Client │ └── CMakeLists.txt └── CMakeLists.txt ├── GUI ├── Headers │ ├── GuiUtils.hpp │ ├── Helpers.hpp │ └── Network.hpp ├── Source │ ├── GUI.rc.in │ ├── GuiUtils.cpp │ └── Helpers.cpp ├── Tests │ └── CMakeLists.txt └── CMakeLists.txt ├── Common ├── Headers │ ├── Comms.hpp │ ├── CompileInfo.hpp.in │ ├── Messages.hpp │ ├── Log.hpp │ ├── IoctlCodes.hpp │ ├── Common.hpp │ └── Utils.hpp ├── Tests │ ├── Utils.cpp │ ├── CMakeLists.txt │ └── Comms.cpp ├── Source │ ├── Messages.cpp │ ├── Log.cpp │ └── Comms.cpp └── CMakeLists.txt ├── CMakeLists.txt ├── README.md ├── .clang-format └── .gitignore /Docs/.nojekyll: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: 2 | modernize-*, 3 | portability-*, 4 | cppcoreguidelines-* 5 | -------------------------------------------------------------------------------- /Broker/Headers/Resource.h: -------------------------------------------------------------------------------- 1 | #define IDR_CFB_DRIVER 101 2 | #define CFB_DRIVER_DATAFILE 101 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [hugsy, ] 4 | 5 | -------------------------------------------------------------------------------- /Assets/Fonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/CFB/HEAD/Assets/Fonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /Assets/Images/logo/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/CFB/HEAD/Assets/Images/logo/logo.ico -------------------------------------------------------------------------------- /Assets/Fonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/CFB/HEAD/Assets/Fonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /Assets/Fonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/CFB/HEAD/Assets/Fonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /Assets/Images/logo/Logo_v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/CFB/HEAD/Assets/Images/logo/Logo_v1.png -------------------------------------------------------------------------------- /Assets/Images/logo/Logo_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/CFB/HEAD/Assets/Images/logo/Logo_v2.png -------------------------------------------------------------------------------- /Assets/Images/logo/Logo_v2_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/CFB/HEAD/Assets/Images/logo/Logo_v2_small.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.c text eol=crlf 2 | *.h text eol=crlf 3 | *.cpp text eol=crlf 4 | *.cc text eol=crlf 5 | *.cs text eol=crlf 6 | -------------------------------------------------------------------------------- /Broker/Headers/Broker.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #include "pch.hpp" 5 | 6 | #include "Native.hpp" 7 | #include "Common.hpp" 8 | #include "CompileInfo.hpp" 9 | #include "Error.hpp" 10 | // clang-format on 11 | -------------------------------------------------------------------------------- /Broker/Headers/pch.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | -------------------------------------------------------------------------------- /Docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | * CFB 2 | * [Overview](/) 3 | * [Concept](/concept.md) 4 | * [Setup](/setup.md) 5 | * [Usage](/usage.md) 6 | * [Debugging](/debugging.md) 7 | * [Fuzzing](/fuzzing-vm-setup.md) 8 | * [Crash Analysis](/crash-analysis.md) 9 | * [Gallery](/gallery.md) 10 | * [FAQ](/faq.md) 11 | -------------------------------------------------------------------------------- /cmake/FindPhNt.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | FetchContent_Declare( 4 | phnt 5 | GIT_REPOSITORY https://github.com/winsiderss/phnt.git 6 | GIT_TAG 7c1adb8a7391939dfd684f27a37e31f18d303944 7 | ) 8 | FetchContent_MakeAvailable(phnt) 9 | message(STATUS "Using PhNt in '${phnt_SOURCE_DIR}'") 10 | add_library(Deps::PHNT ALIAS phnt) 11 | -------------------------------------------------------------------------------- /cmake/FindCatch2.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | FetchContent_Declare( 4 | Catch2 5 | URL https://github.com/catchorg/Catch2/archive/refs/tags/v3.4.0.zip 6 | URL_HASH MD5=c426e77d4ee0055410bc930182959ae5 7 | ) 8 | 9 | FetchContent_MakeAvailable(Catch2) 10 | message(STATUS "Using Catch2 in '${Catch2_SOURCE_DIR}'") 11 | add_library(Deps::Catch2 ALIAS Catch2) 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{cpp,cc,h,hpp}] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | 17 | [CMakeLists.txt] 18 | indent_style = space 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /Docs/gallery.md: -------------------------------------------------------------------------------- 1 | # Screenshots 2 | 3 | ## Boker 4 | 5 | ## ImGUI Client 6 | 7 | ## UWP Client 8 | 9 | ### Intercepted IRP view 10 | 11 | ![Intercepted IRP view](https://i.imgur.com/xMOIIhC.png) 12 | 13 | ### IRP details 14 | 15 | ![IRP Metadata](https://i.imgur.com/zmh2QAw.png) 16 | 17 | ![IRP InputBuffer](https://i.imgur.com/j0W9ljL.png) 18 | 19 | ### IRP replay 20 | 21 | ![IRP Replay](https://i.imgur.com/9Ybq27G.png) 22 | 23 | -------------------------------------------------------------------------------- /Docs/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## LoadDriver(0) error on start 4 | 5 | Check that you're running in Debug mode, and have test signing enabled. 6 | 7 | 8 | 9 | ## Unable to load DLL 'Core.dll' (HRESULT: 0x8007007E) 10 | 11 | Missing Visual C++ Redist. Runtime 14.0 (`vcruntime140.dll`) 12 | 13 | Install x68 or x64 version from [here](https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) 14 | 15 | 16 | 17 | ## Where did you get those cool icons ? 18 | 19 | Over there : http://freeiconshop.com/ 20 | -------------------------------------------------------------------------------- /Broker/Headers/Connectors/Dummy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /// 4 | /// This is an example of connector to print out IRP to terminal 5 | /// 6 | 7 | #include "Connectors/Base.hpp" 8 | 9 | namespace CFB::Broker::Connectors 10 | { 11 | 12 | class Dummy : public ConnectorBase 13 | { 14 | public: 15 | Dummy(); 16 | 17 | ~Dummy(); 18 | 19 | std::string const 20 | Name() const override; 21 | 22 | Result 23 | IrpCallback(CFB::Comms::CapturedIrp const& Irp) override; 24 | 25 | private: 26 | }; 27 | 28 | } // namespace CFB::Broker::Connectors 29 | -------------------------------------------------------------------------------- /cmake/FindJson.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | FetchContent_Declare( 4 | nlohmann_json 5 | URL https://github.com/nlohmann/json/releases/download/v3.11.3/include.zip 6 | URL_HASH MD5=e2f46211f4cf5285412a63e8164d4ba6 7 | ) 8 | 9 | FetchContent_MakeAvailable(nlohmann_json) 10 | message(STATUS "Using Json in '${nlohmann_json_SOURCE_DIR}'") 11 | 12 | add_library(nlohmann_json INTERFACE EXCLUDE_FROM_ALL) 13 | target_include_directories(nlohmann_json INTERFACE ${nlohmann_json_SOURCE_DIR}/single_include) 14 | add_library(Deps::JSON ALIAS nlohmann_json) 15 | -------------------------------------------------------------------------------- /cmake/FindWil.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | set(FAST_BUILD OFF) 3 | set(WIL_BUILD_PACKAGING OFF) 4 | set(WIL_BUILD_TESTS OFF) 5 | 6 | FetchContent_Declare( 7 | WIL 8 | URL https://github.com/microsoft/wil/archive/refs/tags/v1.0.231028.1.zip 9 | URL_HASH MD5=48f04bde1b5d745ee2f6dedc9040fba7 10 | ) 11 | FetchContent_MakeAvailable(WIL) 12 | message(STATUS "Using WIL in '${WIL_SOURCE_DIR}'") 13 | 14 | add_library(Deps_WIL INTERFACE EXCLUDE_FROM_ALL) 15 | add_library(Deps::WIL ALIAS Deps_WIL) 16 | target_include_directories(Deps_WIL INTERFACE ${WIL_SOURCE_DIR}/include) 17 | -------------------------------------------------------------------------------- /cmake/FindArgparse.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | FetchContent_Declare( 4 | argparse 5 | URL https://github.com/p-ranav/argparse/archive/refs/tags/v3.0.zip 6 | URL_HASH MD5=a44c0401238e87239e31652b72fded20 7 | ) 8 | FetchContent_MakeAvailable(argparse) 9 | message(STATUS "Using ArgParse in '${argparse_SOURCE_DIR}'") 10 | 11 | add_library(Deps_Argparse INTERFACE EXCLUDE_FROM_ALL) 12 | target_compile_features(Deps_Argparse INTERFACE cxx_std_20) 13 | target_include_directories(Deps_Argparse INTERFACE ${argparse_SOURCE_DIR}/include) 14 | add_library(Deps::Argparse ALIAS Deps_Argparse) 15 | -------------------------------------------------------------------------------- /Docs/debugging.md: -------------------------------------------------------------------------------- 1 | 2 | # Debugging CFB 3 | 4 | ## The Driver 5 | 6 | 7 | ### Enumerate the hooked drivers 8 | 9 | ``` 10 | dx @$drivers=Debugger.Utility.Collections.FromListEntry( IrpMonitor!Globals->DriverManager.m_Entries.m_ListHead, "IrpMonitor!CFB::Driver::HookedDriver", "Next") 11 | ``` 12 | 13 | ### Check if a driver is set for data capture 14 | 15 | ``` 16 | dx @$drivers.First().Enabled == true && @$drivers.First().State == 1 17 | ``` 18 | 19 | ### Enumerate the captured IRPs 20 | 21 | ``` 22 | dx @$irps=Debugger.Utility.Collections.FromListEntry( IrpMonitor!Globals->IrpManager.m_Entries.m_ListHead, "IrpMonitor!CFB::Driver::CapturedIrp", "Next") 23 | ``` 24 | -------------------------------------------------------------------------------- /Driver/Headers/Native.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.hpp" 4 | 5 | EXTERN_C_START 6 | 7 | extern POBJECT_TYPE* IoDriverObjectType; 8 | extern POBJECT_TYPE* IoDeviceObjectType; 9 | 10 | extern NTSYSAPI NTSTATUS NTAPI 11 | ObReferenceObjectByName( 12 | _In_ PUNICODE_STRING ObjectPath, 13 | _In_ ULONG Attributes, 14 | _In_opt_ PACCESS_STATE PassedAccessState, 15 | _In_opt_ ACCESS_MASK DesiredAccess, 16 | _In_ POBJECT_TYPE ObjectType, 17 | _In_ KPROCESSOR_MODE AccessMode, 18 | _Inout_opt_ PVOID ParseContext, 19 | _Out_ PVOID* ObjectPtr); 20 | 21 | extern NTKERNELAPI PSTR 22 | PsGetProcessImageFileName(_In_ PEPROCESS Process); 23 | 24 | EXTERN_C_END 25 | -------------------------------------------------------------------------------- /Broker/Headers/Connectors/JsonQueue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Connectors/Base.hpp" 7 | 8 | namespace CFB::Broker::Connectors 9 | { 10 | 11 | class JsonQueue : public ConnectorBase 12 | { 13 | public: 14 | JsonQueue(); 15 | 16 | ~JsonQueue(); 17 | 18 | std::string const 19 | Name() const override; 20 | 21 | Result 22 | IrpCallback(CFB::Comms::CapturedIrp const& Irp) override; 23 | 24 | std::unique_ptr 25 | Pop(); 26 | 27 | private: 28 | std::queue> m_Queue; 29 | std::mutex m_Lock; 30 | }; 31 | 32 | } // namespace CFB::Broker::Connectors 33 | -------------------------------------------------------------------------------- /GUI/Headers/GuiUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace CFB::GUI::Utils 9 | { 10 | 11 | class FileManager 12 | { 13 | public: 14 | /// 15 | ///@brief 16 | /// 17 | ///@param Filter 18 | ///@return std::optional 19 | /// 20 | static std::optional 21 | OpenFile(std::string_view const& Filter); 22 | 23 | /// 24 | ///@brief 25 | /// 26 | ///@param Filter 27 | ///@return std::optional 28 | /// 29 | static std::optional 30 | SaveFile(std::string_view const& Filter); 31 | }; 32 | } // namespace CFB::GUI::Utils 33 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | concurrency: 14 | group: "pages" 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | deploy: 19 | environment: 20 | name: github-pages 21 | url: ${{ steps.deployment.outputs.page_url }} 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | - name: Setup Pages 27 | uses: actions/configure-pages@v4 28 | - name: Upload artifact 29 | uses: actions/upload-pages-artifact@v2 30 | with: 31 | path: './Docs' 32 | - name: Deploy to GitHub Pages 33 | id: deployment 34 | uses: actions/deploy-pages@v2 35 | -------------------------------------------------------------------------------- /Broker/Headers/Connectors/Base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.hpp" 4 | #include "Comms.hpp" 5 | #include "Error.hpp" 6 | 7 | namespace CFB::Broker::Connectors 8 | { 9 | class ConnectorBase 10 | { 11 | public: 12 | ConnectorBase() : m_Enabled {false} 13 | { 14 | } 15 | 16 | ~ConnectorBase() 17 | { 18 | } 19 | 20 | virtual std::string const 21 | Name() const = 0; 22 | 23 | void 24 | Enable() 25 | { 26 | m_Enabled = true; 27 | } 28 | 29 | void 30 | Disable() 31 | { 32 | m_Enabled = false; 33 | } 34 | 35 | bool 36 | IsEnabled() const 37 | { 38 | return m_Enabled; 39 | } 40 | 41 | virtual Result 42 | IrpCallback(CFB::Comms::CapturedIrp const& Irp) = 0; 43 | 44 | protected: 45 | bool m_Enabled; 46 | }; 47 | } // namespace CFB::Broker::Connectors 48 | -------------------------------------------------------------------------------- /cmake/FindImgui.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | FetchContent_Declare( 3 | imgui 4 | GIT_REPOSITORY https://github.com/ocornut/imgui.git 5 | GIT_TAG docking 6 | ) 7 | FetchContent_MakeAvailable(imgui) 8 | message(STATUS "Using ImGUI in '${imgui_SOURCE_DIR}'") 9 | add_library(imgui 10 | STATIC 11 | ${imgui_SOURCE_DIR}/imgui.cpp 12 | ${imgui_SOURCE_DIR}/imgui_demo.cpp 13 | ${imgui_SOURCE_DIR}/imgui_draw.cpp 14 | ${imgui_SOURCE_DIR}/imgui_tables.cpp 15 | ${imgui_SOURCE_DIR}/imgui_widgets.cpp 16 | 17 | ${imgui_SOURCE_DIR}/backends/imgui_impl_win32.cpp 18 | ${imgui_SOURCE_DIR}/backends/imgui_impl_dx12.cpp 19 | ${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.cpp 20 | 21 | # ${imgui_SOURCE_DIR}/backends/imgui_impl_dx10.cpp 22 | # ${imgui_SOURCE_DIR}/backends/imgui_impl_dx9.cpp 23 | ) 24 | add_library(Deps::ImGUI ALIAS imgui) 25 | target_include_directories(imgui 26 | PRIVATE 27 | ${imgui_SOURCE_DIR} 28 | ${imgui_SOURCE_DIR}/backends 29 | ) 30 | -------------------------------------------------------------------------------- /Docs/concept.md: -------------------------------------------------------------------------------- 1 | # Concept 2 | 3 | `IrpDumper.sys` is the driver part of the CFB Broker that will auto-extract and install when launched. The driver will be responsible for hooking the IRP Major Function table of 4 | the driver that is requested to be hooked, via an IOCTL passed from the Broker. 5 | Upon success, the IRP table of the driver will then be pointing to `IrpDumper.sys` interception routine, as we can easily see with a debugger or tools like [`WinObjEx64`](https://github.com/hfiref0x/WinObjEx64). 6 | 7 | ![img](https://i.imgur.com/dYqHE6q.png) 8 | 9 | `IrpDumper.sys` in itself then acts a rootkit, proxy-ing all calls to the targeted driver(s). When a `DeviceIoControl` is sent to a hooked driver, `IrpDumper` will simply capture the data if any, and push a message to the user-land agent (`Broker`), and yield the execution back to the legitimate drivers, allowing the intended code to continue as expected. 10 | The `Broker` stores all this data in user-land waiting for a event to ask for them. 11 | -------------------------------------------------------------------------------- /Broker/Source/Connectors/JsonQueue.cpp: -------------------------------------------------------------------------------- 1 | #include "Connectors/JsonQueue.hpp" 2 | 3 | #include "Log.hpp" 4 | 5 | 6 | namespace CFB::Broker::Connectors 7 | { 8 | JsonQueue::JsonQueue() 9 | { 10 | dbg("Initializing connector '%s'", Name().c_str()); 11 | } 12 | 13 | JsonQueue::~JsonQueue() 14 | { 15 | dbg("Terminating connector '%s'", Name().c_str()); 16 | } 17 | 18 | std::string const 19 | JsonQueue::Name() const 20 | { 21 | return "JsonQueue"; 22 | } 23 | 24 | Result 25 | JsonQueue::IrpCallback(CFB::Comms::CapturedIrp const& Irp) 26 | { 27 | std::scoped_lock(m_Lock); 28 | m_Queue.push(std::make_unique(Irp)); 29 | return Ok(0); 30 | } 31 | 32 | std::unique_ptr 33 | JsonQueue::Pop() 34 | { 35 | std::scoped_lock(m_Lock); 36 | if ( m_Queue.empty() ) 37 | { 38 | return nullptr; 39 | } 40 | 41 | auto Irp = std::move(m_Queue.front()); 42 | m_Queue.pop(); 43 | 44 | return Irp; 45 | } 46 | 47 | } // namespace CFB::Broker::Connectors 48 | -------------------------------------------------------------------------------- /GUI/Headers/Helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace CFB::GUI::Helpers 6 | { 7 | 8 | /// 9 | ///@brief From https://github.com/ocornut/imgui/issues/1537#issuecomment-355569554 10 | /// 11 | ///@param str_id 12 | ///@param v 13 | /// 14 | bool 15 | ToggleButton(const char* str_id, bool* v); 16 | 17 | 18 | /// 19 | ///@brief From https://github.com/ocornut/imgui/issues/1901#issue-335266223 20 | /// 21 | ///@param label 22 | ///@param value 23 | ///@param size_arg 24 | ///@param bg_col 25 | ///@param fg_col 26 | ///@return true 27 | ///@return false 28 | /// 29 | bool 30 | BufferingBar(const char* label, float value, const ImVec2& size_arg, const ImU32& bg_col, const ImU32& fg_col); 31 | 32 | 33 | /// 34 | ///@brief From https://github.com/ocornut/imgui/issues/1901#issue-335266223 35 | /// 36 | ///@param label 37 | ///@param radius 38 | ///@param thickness 39 | ///@param color 40 | ///@return true 41 | ///@return false 42 | /// 43 | bool 44 | Spinner(const char* label, float radius, int thickness, const ImU32& color); 45 | 46 | } // namespace CFB::GUI::Helpers 47 | -------------------------------------------------------------------------------- /Docs/index.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Docs/setup.md: -------------------------------------------------------------------------------- 1 | # Build CFB 2 | 3 | ## Pre-Build 4 | 5 | The easiest and fastest way to get started is simply to download the artifacts from the Github Actions build workflow. They can be found [here](https://github.com/hugsy/CFB/actions/workflows/build.yml). 6 | 7 | ## Build 8 | 9 | Building CFB requires only [`cmake`](https://cmake.org), and the Windows [SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/) and [WDK](https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk). In a developer prompt: 10 | 11 | ### To compile 12 | ```powershell 13 | cd \path\to\CFB.git 14 | mkdir build 15 | cmake -S . -B ./build -D CFB_BUILD_TOOLS:BOOL=ON -D CFB_BUILD_GUI:BOOL=ON -A $platform 16 | cmake --build ./build --verbose --parallel $env:NUMBER_OF_PROCESSORS --config $config 17 | ``` 18 | 19 | Where `$platform` can be: 20 | - `x64` 21 | - `arm64` 22 | 23 | `win32` may work to build the broker. However, ImGUI doesn't compile on 32-bit. 24 | 25 | ### To install 26 | 27 | After building: 28 | 29 | ```powershell 30 | mkdir artifact 31 | cmake --install ./build --config $config --prefix ./artifact --verbose 32 | ``` 33 | 34 | Where `$config` can be: 35 | - `RelWithDebInfo` (you probably want this one) 36 | - `Debug` for debugging (very verbose outputs) 37 | -------------------------------------------------------------------------------- /Common/Headers/Comms.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.hpp" 4 | 5 | #ifndef CFB_KERNEL_DRIVER 6 | // clang-format off 7 | #include 8 | // clang-format on 9 | #endif // CFB_KERNEL_DRIVER 10 | 11 | 12 | namespace CFB::Comms 13 | { 14 | #pragma pack(push, 1) 15 | struct CapturedIrpHeader 16 | { 17 | u64 TimeStamp; 18 | wchar_t DriverName[CFB_DRIVER_MAX_PATH]; 19 | wchar_t DeviceName[CFB_DRIVER_MAX_PATH]; 20 | u8 Irql; 21 | u8 Type; 22 | u8 MajorFunction; 23 | u8 MinorFunction; 24 | u32 IoctlCode; 25 | u32 Pid; 26 | u32 Tid; 27 | NTSTATUS Status; 28 | wchar_t ProcessName[CFB_DRIVER_MAX_PATH]; 29 | u32 InputBufferLength; 30 | u32 OutputBufferLength; 31 | }; 32 | #pragma pack(pop) 33 | 34 | #ifdef CFB_KERNEL_DRIVER 35 | #else 36 | struct CapturedIrp 37 | { 38 | CapturedIrpHeader Header; 39 | std::vector InputBuffer; 40 | std::vector OutputBuffer; 41 | }; 42 | 43 | void 44 | to_json(nlohmann::json& j, CapturedIrpHeader const& h); 45 | 46 | void 47 | from_json(const nlohmann::json& j, CapturedIrpHeader& h); 48 | 49 | void 50 | to_json(nlohmann::json& j, CapturedIrp const& i); 51 | 52 | void 53 | from_json(const nlohmann::json& j, CapturedIrp& i); 54 | #endif // CFB_KERNEL_DRIVER 55 | 56 | } // namespace CFB::Comms 57 | -------------------------------------------------------------------------------- /Driver/Source/Driver.rc.in: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | VS_VERSION_INFO VERSIONINFO 4 | FILEVERSION @DRIVER_VERSION_MAJOR@,@DRIVER_VERSION_MINOR@,@DRIVER_VERSION_PATCH@,0 5 | PRODUCTVERSION @CFB_VERSION_MAJOR@,@CFB_VERSION_MINOR@,@CFB_VERSION_PATCH@,0 6 | FILEOS VOS_NT_WINDOWS32 7 | FILETYPE VFT_DRV 8 | FILESUBTYPE VFT2_DRV_SYSTEM 9 | 10 | BEGIN 11 | BLOCK "StringFileInfo" 12 | BEGIN 13 | BLOCK "040904E4" 14 | BEGIN 15 | VALUE "CompanyName", "@CFB_COMPANY_NAME@\0" 16 | VALUE "FileDescription", "@PROJECT_DESCRIPTION@\0" 17 | VALUE "FileVersion", "@DRIVER_VERSION_MAJOR@,@DRIVER_VERSION_MINOR@,@DRIVER_VERSION_PATCH@,0\0" 18 | VALUE "LegalCopyright", "(c) @CFB_CURRENT_YEAR@ @CFB_COMPANY_NAME@ Release Under @PROJECT_LICENSE@\0" 19 | VALUE "OriginalFilename", "@PROJECT_NAME@.sys\0" 20 | VALUE "ProductName", "@CMAKE_PROJECT_NAME@\0" 21 | VALUE "ProductVersion", "@CFB_VERSION_MAJOR@,@CFB_VERSION_MINOR@,@CFB_VERSION_PATCH@,0\0" 22 | VALUE "Comments", "@PROJECT_DESCRIPTION@\0" 23 | END 24 | END 25 | BLOCK "VarFileInfo" 26 | BEGIN 27 | VALUE "Translation", 0x409, 1200 28 | END 29 | END 30 | 31 | -------------------------------------------------------------------------------- /Docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | A Windows 7+ machine ([Windows 10 SDK VM](https://developer.microsoft.com/en-us/windows/downloads/virtual-machines) is recommended) 4 | 5 | On this target machine, simply enable BCD test signing flag (in `cmd.exe` as Admin): 6 | 7 | ``` 8 | bcdedit.exe /set {whatever-profile} testsigning on 9 | ``` 10 | 11 | If using in Debug mode, `IrpDumper.sys` will provide a lot more valuable information as to what's being hooked (the price of performance). All those info can be visible via tools like `DebugView.exe` or a kernel debugger like WinDbg. In either case, you must enable kernel debug BCD flag (in `cmd.exe` as Admin): 12 | 13 | ``` 14 | bcdedit.exe /set {whatever-profile} debug on 15 | ``` 16 | 17 | It is also recommended to edit the KD verbosity level, via: 18 | - the registry for a permanent effect (`reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter" /v DEFAULT /t REG_DWORD /d 0xf`) 19 | - directly from WinDbg for only the current session (`ed nt!Kd_Default_Mask 0xf`) 20 | 21 | 22 | If you plan on (re-)compiling any of the tools, you must install VS (2019 preferred). If using the Release binaries, you only need VS C++ Redist installed(x86 or x64 depending on your VM architecture). 23 | 24 | Follow the indications in the `Docs/` folder to improve your setup. -------------------------------------------------------------------------------- /Broker/Headers/ConnectorManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #include "Common.hpp" 5 | #include "ManagerBase.hpp" 6 | 7 | #include "Connectors/Base.hpp" 8 | // clang-format on 9 | 10 | 11 | namespace CFB::Broker 12 | { 13 | class ConnectorManager : public ManagerBase 14 | { 15 | public: 16 | /// 17 | /// @brief Construct a new Connector Manager object 18 | /// 19 | /// 20 | ConnectorManager() 21 | { 22 | } 23 | 24 | /// 25 | /// @brief Destroy the Connector Manager object 26 | /// 27 | /// 28 | ~ConnectorManager() 29 | { 30 | } 31 | 32 | /// 33 | /// @brief 34 | /// 35 | /// @return std::string const 36 | /// 37 | std::string const 38 | Name(); 39 | 40 | /// 41 | /// @brief 42 | /// 43 | /// @return Result 44 | /// 45 | Result 46 | Setup(); 47 | 48 | /// 49 | /// @brief 50 | /// 51 | /// 52 | void 53 | Run(); 54 | 55 | /// 56 | ///@brief Get the Connector By Name object 57 | /// 58 | ///@param ConnectorName 59 | ///@return Result> 60 | /// 61 | Result> 62 | GetConnectorByName(std::string_view const& ConnectorName); 63 | 64 | private: 65 | }; 66 | } // namespace CFB::Broker 67 | -------------------------------------------------------------------------------- /Driver/Source/IrpMonitor.inf: -------------------------------------------------------------------------------- 1 | ; 2 | ; IrpMonitor.inf 3 | ; 4 | 5 | [Version] 6 | Signature="$WINDOWS NT$" 7 | Class=System 8 | ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} 9 | Provider=%ManufacturerName% 10 | DriverVer=01/01/1970 11 | CatalogFile=Driver.cat 12 | PnpLockdown=1 13 | 14 | [DestinationDirs] 15 | DefaultDestDir = 10 ; %SystemRoot% 16 | 17 | 18 | [SourceDisksNames] 19 | 1 = %DiskName%,,,"" 20 | 21 | [SourceDisksFiles] 22 | IrpMonitor.sys = 1,, 23 | 24 | 25 | [DefaultInstall] 26 | OptionDesc = %ServiceDesc% 27 | CopyFiles = IrpMonitor.DriverFiles 28 | 29 | [IrpMonitor.DriverFiles] 30 | IrpMonitor.sys,,,0x00000004 ; COPYFLG_NOVERSIONCHECK 31 | 32 | [DefaultInstall.Services] 33 | AddService = %ServiceName%,,IrpMonitor.ServiceInstall 34 | 35 | [IrpMonitor.ServiceInstall] 36 | DisplayName = %ServiceName% 37 | Description = %ServiceDesc% 38 | ServiceBinary = %10%\IrpMonitor.sys 39 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER 40 | StartType = 3 ; SERVICE_AUTO_START ; SERVICE_DEMAND_START = 3 41 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL 42 | 43 | [Strings] 44 | ManufacturerName = "BlahCat Corp" 45 | DiskName = "IrpMonitor Installation Disk" 46 | ServiceName = "IrpMonitor" 47 | ServiceDesc = "IrpMonitor: hooks/unhooks arbitrary drivers on the system to capture the IRPs sent to those drivers." 48 | -------------------------------------------------------------------------------- /Common/Tests/Utils.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | 3 | #include 4 | #define NS "CFB::Utils" 5 | 6 | #include "Utils.hpp" 7 | 8 | TEST_CASE("Strings", NS) 9 | { 10 | SECTION("ToString(WideString)") 11 | { 12 | CHECK(CFB::Utils::ToString(L"FOOBAR") == "FOOBAR"); 13 | CHECK(CFB::Utils::ToString(L"FOOBAR").size() == 6); 14 | } 15 | 16 | SECTION("ToWideString(String)") 17 | { 18 | CHECK(CFB::Utils::ToWideString("FOOBAR") == L"FOOBAR"); 19 | CHECK(CFB::Utils::ToWideString("FOOBAR").size() == 6); 20 | } 21 | 22 | SECTION("IRP") 23 | { 24 | for ( int i = 0; i < 0x1c; i++ ) 25 | { 26 | CHECK(std::string(CFB::Utils::IrpMajorToString(i)).starts_with("IRP_")); 27 | } 28 | 29 | CHECK(std::string(CFB::Utils::IrpMajorToString(0x4242)) == "UnknownIrpType"); 30 | } 31 | } 32 | 33 | TEST_CASE("Memory", NS) 34 | { 35 | CHECK(CFB::Utils::Memory::IsAligned(0x41414141, 0x10) == false); 36 | CHECK(CFB::Utils::Memory::IsAligned(0x41414140, 0x10) == true); 37 | 38 | CHECK(CFB::Utils::Memory::AlignValue(0x41414100, 0x100) == 0x41414100); 39 | for ( int i = 1; i < 0x100; i++ ) 40 | { 41 | CHECK(CFB::Utils::Memory::AlignValue(0x41414100 + i, 0x100) == 0x41414200); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Docs/crash-analysis.md: -------------------------------------------------------------------------------- 1 | # Crash analysis 2 | 3 | Found a crash? 4 | 5 | * Validate the cause 6 | 7 | ``` 8 | kd> !analyze -v 9 | kd> .load \path\to\msec.dll ; !exploitable 10 | ``` 11 | 12 | * IRPs are handled sequentially, therefore the crash has to be related to at least the last 13 | IRP sent. CFB will store the last IRP fuzzed in `IrpDumper!g_LastTestCase` in the following 14 | format: 15 | ``` 16 | struct { 17 | UINT32 SizeOfBuffer; 18 | BYTE Buffer[SizeOfBuffer]; 19 | } 20 | ``` 21 | 22 | So you can easily dump the last IRP from WinDbg in 3 simple steps (which can easily automated in 23 | a WinDbg JS script): 24 | 25 | * First get the length, for example: 26 | 27 | ``` 28 | kd> dd poi(IrpDumper!g_LastTestCase) l1 29 | ffffe088`f37ae000 00000218 30 | ``` 31 | 32 | The data length is 0x218 bytes. 33 | 34 | 35 | * Then you can confirm by viewing those data 36 | ``` 37 | kd> db poi(IrpDumper!g_LastTestCase+4) l218 38 | ffffe088`f37ae004 5c 00 44 00 6f 00 73 00-44 00 65 00 76 00 69 00 \.D.o.s.D.e.v.i. 39 | ffffe088`f37ae014 63 00 65 00 73 00 5c 00-50 00 68 00 79 00 73 00 c.e.s.\.P.h.y.s. 40 | [...] 41 | ``` 42 | 43 | * And finally store them on the debugger host. 44 | ``` 45 | kd> .writemem C:\Whatever.raw poi(IrpDumper!g_LastTestCase+4) l218 46 | ``` 47 | 48 | All the other info (like device name, ioctl number, etc.) can be retrieved from `analyze -v` above 49 | -------------------------------------------------------------------------------- /GUI/Source/GUI.rc.in: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | VS_VERSION_INFO VERSIONINFO 4 | FILEVERSION @GUI_VERSION_MAJOR@,@GUI_VERSION_MINOR@,@GUI_VERSION_PATCH@,0 5 | PRODUCTVERSION @CFB_VERSION_MAJOR@,@CFB_VERSION_MINOR@,@CFB_VERSION_PATCH@,0 6 | FILEOS VOS_NT_WINDOWS32 7 | FILETYPE VFT_APP 8 | FILESUBTYPE VFT2_UNKNOWN 9 | 10 | BEGIN 11 | BLOCK "StringFileInfo" 12 | BEGIN 13 | BLOCK "040904E4" 14 | BEGIN 15 | VALUE "CompanyName", "@CFB_COMPANY_NAME@\0" 16 | VALUE "FileDescription", "@PROJECT_DESCRIPTION@\0" 17 | VALUE "FileVersion", "@GUI_VERSION_MAJOR@,@GUI_VERSION_MINOR@,@GUI_VERSION_PATCH@,0\0" 18 | VALUE "LegalCopyright", "© @CFB_CURRENT_YEAR@ @CFB_COMPANY_NAME@ Release Under @PROJECT_LICENSE@\0" 19 | VALUE "OriginalFilename", "@PROJECT_NAME@.exe\0" 20 | VALUE "ProductName", "@CMAKE_PROJECT_NAME@\0" 21 | VALUE "ProductVersion", "@CFB_VERSION_MAJOR@,@CFB_VERSION_MINOR@,@CFB_VERSION_PATCH@,0\0" 22 | VALUE "Comments", "@PROJECT_DESCRIPTION@\0" 23 | END 24 | END 25 | BLOCK "VarFileInfo" 26 | BEGIN 27 | VALUE "Translation", 0x409, 1200 28 | END 29 | END 30 | 31 | 32 | ///////////////////////////////////////////////////////////////////////////// 33 | // 34 | // Icon 35 | // 36 | 37 | IDI_ICON1 ICON "@CFB_GUI_ICON_PATH@" 38 | -------------------------------------------------------------------------------- /Common/Headers/CompileInfo.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Those values are auto-generated by CMakeLists.txt. Don't edit them here. 5 | // 6 | 7 | 8 | // clang-format off 9 | #cmakedefine PROJECT_NAME "@CMAKE_PROJECT_NAME@" 10 | #cmakedefine PROJECT_DESCRIPTION "@CMAKE_PROJECT_DESCRIPTION@" 11 | #cmakedefine PROJECT_AUTHOR "@PROJECT_AUTHOR@" 12 | #cmakedefine PROJECT_LICENSE "@PROJECT_LICENSE@" 13 | #cmakedefine PROJECT_HOMEPAGE_URL "@CMAKE_PROJECT_HOMEPAGE_URL@" 14 | 15 | #define DRIVER_VERSION_MAJOR @DRIVER_VERSION_MAJOR@ 16 | #define DRIVER_VERSION_MINOR @DRIVER_VERSION_MINOR@ 17 | #define DRIVER_VERSION_PATCH @DRIVER_VERSION_PATCH@ 18 | #define DRIVER_VERSION "@DRIVER_VERSION_MAJOR@.@DRIVER_VERSION_MINOR@.@DRIVER_VERSION_PATCH@" 19 | 20 | #define BROKER_VERSION_MAJOR @BROKER_VERSION_MAJOR@ 21 | #define BROKER_VERSION_MINOR @BROKER_VERSION_MINOR@ 22 | #define BROKER_VERSION_PATCH @BROKER_VERSION_PATCH@ 23 | #define BROKER_VERSION "@BROKER_VERSION_MAJOR@.@BROKER_VERSION_MINOR@.@BROKER_VERSION_PATCH@" 24 | 25 | #define GUI_VERSION_MAJOR @GUI_VERSION_MAJOR@ 26 | #define GUI_VERSION_MINOR @GUI_VERSION_MINOR@ 27 | #define GUI_VERSION_PATCH @GUI_VERSION_PATCH@ 28 | #define GUI_VERSION "@GUI_VERSION_MAJOR@.@GUI_VERSION_MINOR@.@GUI_VERSION_PATCH@" 29 | // clang-format on 30 | -------------------------------------------------------------------------------- /Common/Source/Messages.cpp: -------------------------------------------------------------------------------- 1 | #include "Messages.hpp" 2 | 3 | #ifndef CFB_KERNEL_DRIVER 4 | #include 5 | #include 6 | 7 | #include "Utils.hpp" 8 | 9 | 10 | namespace CFB::Comms 11 | { 12 | // 13 | // JSON Converters 14 | // 15 | void 16 | to_json(json& dst, const DriverRequest& src) 17 | { 18 | dst["id"] = src.Id; 19 | switch ( src.Id ) 20 | { 21 | case RequestId::HookDriver: 22 | case RequestId::UnhookDriver: 23 | case RequestId::EnableMonitoring: 24 | case RequestId::DisableMonitoring: 25 | dst["driver_name"] = CFB::Utils::ToString(src.DriverName); 26 | break; 27 | 28 | case RequestId::GetPendingIrp: 29 | dst["number_of_irp"] = src.NumberOfIrp; 30 | break; 31 | 32 | default: 33 | break; 34 | } 35 | } 36 | 37 | void 38 | from_json(const json& js, DriverRequest& dst) 39 | { 40 | dst.Id = js["id"]; 41 | switch ( dst.Id ) 42 | { 43 | case RequestId::HookDriver: 44 | case RequestId::UnhookDriver: 45 | case RequestId::EnableMonitoring: 46 | case RequestId::DisableMonitoring: 47 | dst.DriverName = CFB::Utils::ToWideString(js["driver_name"].get()); 48 | break; 49 | 50 | case RequestId::GetPendingIrp: 51 | dst.NumberOfIrp = js["number_of_irp"]; 52 | break; 53 | 54 | default: 55 | break; 56 | } 57 | } 58 | 59 | } // namespace CFB::Comms 60 | #endif // CFB_KERNEL_DRIVER 61 | -------------------------------------------------------------------------------- /Driver/Client/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(DRIVER_CLIENT_SOURCE_DIR ${CFB_ROOT_DIR}/Driver/Client) 2 | 3 | set( 4 | SOURCE_FILES 5 | 6 | ${DRIVER_CLIENT_SOURCE_DIR}/Main.cpp 7 | ) 8 | 9 | add_executable(DriverClient WIN32) 10 | add_executable(CFB::Tools::DriverClient ALIAS DriverClient) 11 | target_sources(DriverClient PUBLIC ${SOURCE_FILES}) 12 | target_compile_features(DriverClient PRIVATE cxx_std_20) 13 | 14 | add_dependencies( 15 | DriverClient 16 | CFB::Kernel::Driver 17 | CFB::User::CommonLib 18 | ) 19 | 20 | if(DEBUG) 21 | target_compile_definitions(DriverClient PRIVATE _DEBUG=1 DEBUG=1) 22 | endif(DEBUG) 23 | 24 | target_include_directories( 25 | DriverClient 26 | PRIVATE 27 | ) 28 | 29 | target_link_libraries(DriverClient 30 | PRIVATE 31 | CFB::User::CommonLib 32 | argparse::argparse 33 | WIL::WIL 34 | ) 35 | 36 | target_compile_options(DriverClient 37 | PUBLIC 38 | $<$:/Zc:__cplusplus> 39 | 40 | PRIVATE 41 | $,/WX /Gm- /permissive-,/WX /permissive> 42 | $<$>:$<$:/fsanitize=address>> 43 | ) 44 | 45 | target_link_options(DriverClient 46 | PUBLIC 47 | /SUBSYSTEM:Console 48 | 49 | PRIVATE 50 | $<$>:$<$:/InferAsanLibs>> 51 | ) 52 | 53 | install(TARGETS DriverClient DESTINATION Tools) 54 | install(FILES $ DESTINATION Tools OPTIONAL) 55 | -------------------------------------------------------------------------------- /.github/Invoke-VisualStudio.ps1: -------------------------------------------------------------------------------- 1 | Function Invoke-CmdScript { 2 | param( 3 | [String] $scriptName 4 | ) 5 | $cmdLine = """$scriptName"" $args & set" 6 | & $env:SystemRoot\system32\cmd.exe /c $cmdLine | 7 | Select-String '^([^=]*)=(.*)$' | ForEach-Object { 8 | $varName = $_.Matches[0].Groups[1].Value 9 | $varValue = $_.Matches[0].Groups[2].Value 10 | Set-Item Env:$varName $varValue 11 | } 12 | } 13 | 14 | 15 | Function Invoke-VisualStudio2019win32 { 16 | Invoke-CmdScript "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars32.bat" 17 | } 18 | 19 | 20 | Function Invoke-VisualStudio2019x64 { 21 | Invoke-CmdScript "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat" 22 | } 23 | 24 | Function Invoke-VisualStudio2019arm64 { 25 | Invoke-CmdScript "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvarsamd64_arm64.bat" 26 | } 27 | 28 | Function Invoke-VisualStudio2022win32 { 29 | Invoke-CmdScript "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars32.bat" 30 | } 31 | 32 | 33 | Function Invoke-VisualStudio2022x64 { 34 | Invoke-CmdScript "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat" 35 | } 36 | 37 | Function Invoke-VisualStudio2022arm64 { 38 | Invoke-CmdScript "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvarsamd64_arm64.bat" 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Build",] 6 | 7 | jobs: 8 | test: 9 | name: "${{ matrix.platform }}/${{ matrix.configuration }}" 10 | 11 | env: 12 | CMAKE_FLAGS: "-DCFB_BUILD_TOOLS:BOOL=OFF -DCFB_BUILD_GUI:BOOL=OFF -DCFB_BUILD_TESTS:BOOL=ON" 13 | CMAKE_TOOLCHAIN_FILE: "C:\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake" 14 | TAG_NAME: "" 15 | NB_CPU: 1 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: ['windows-latest'] 21 | platform: ['x64'] # , 'arm64', 'win32' 22 | configuration: ['RelWithDebInfo'] # 'Debug', 23 | 24 | runs-on: ${{ matrix.os }} 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | 29 | - name: Setup 30 | shell: pwsh 31 | run: | 32 | echo "NB_CPU=$env:NUMBER_OF_PROCESSORS" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 33 | Import-Module .\.github\Invoke-VisualStudio.ps1 34 | Invoke-VisualStudio2022${{ matrix.platform }} 35 | 36 | - name: Build 37 | shell: pwsh 38 | run: | 39 | mkdir build 40 | cmake -S . -B ./build -A ${{ matrix.platform }} ${{ env.CMAKE_FLAGS }} 41 | cmake --build ./build --verbose --parallel ${{ env.NB_CPU }} --config ${{ matrix.configuration }} 42 | 43 | - name: Test 44 | shell: pwsh 45 | run: | 46 | ctest --parallel ${{ env.NB_CPU }} --build-config ${{ matrix.configuration }} -T test --test-dir ./build 47 | -------------------------------------------------------------------------------- /GUI/Tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | message(STATUS "Configuring tests for 'GUI'") 2 | 3 | enable_testing() 4 | 5 | set(GUI_HEADERS_DIR ${CFB_ROOT_DIR}/GUI/Headers) 6 | set(GUI_TEST_DIR ${CFB_ROOT_DIR}/GUI/Tests) 7 | 8 | list(APPEND TEST_SET 9 | ) 10 | 11 | foreach(TEST_FILE ${TEST_SET}) 12 | set(FILEPATH "${GUI_TEST_DIR}/${TEST_FILE}.cpp") 13 | set(TARGET_NAME Tests_Gui_${TEST_FILE}) 14 | add_executable(${TARGET_NAME} WIN32 ${FILEPATH}) 15 | list(APPEND ALL_GUI_TESTS ${TARGET_NAME}) 16 | add_dependencies(${TARGET_NAME} CFB::User::Gui) 17 | set_target_properties(${TARGET_NAME} PROPERTIES CXX_STANDARD 20) 18 | target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) 19 | target_include_directories(${TARGET_NAME} PUBLIC ${GUI_HEADERS_DIR}) 20 | target_compile_definitions(${TARGET_NAME} PRIVATE CATCH_CONFIG_NO_WINDOWS_SEH) 21 | target_compile_options(${TARGET_NAME} PRIVATE $) 22 | target_link_libraries(${TARGET_NAME} PRIVATE Catch2::Catch2 Catch2::Catch2WithMain imgui::imgui) 23 | target_link_options(${TARGET_NAME} PRIVATE /SUBSYSTEM:Console $) 24 | add_test(NAME ${TARGET_NAME} COMMAND $) 25 | install(TARGETS ${TARGET_NAME} DESTINATION Tests/GUI) 26 | install(FILES $ DESTINATION Tests/GUI OPTIONAL) 27 | endforeach() 28 | 29 | add_custom_target( 30 | Tests_Gui 31 | DEPENDS ${ALL_GUI_TESTS} 32 | COMMAND ${CMAKE_CTEST_COMMAND} -R Tests_Gui 33 | ) 34 | -------------------------------------------------------------------------------- /Broker/Tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | message(STATUS "Configuring tests for 'Broker'") 2 | 3 | enable_testing() 4 | 5 | set(BROKER_HEADERS_DIR ${CFB_ROOT_DIR}/Broker/Headers) 6 | set(BROKER_TEST_DIR ${CFB_ROOT_DIR}/Broker/Tests) 7 | 8 | set(TEST_SET 9 | ) 10 | 11 | foreach(TEST_FILE ${TEST_SET}) 12 | set(FILEPATH "${BROKER_TEST_DIR}/${TEST_FILE}.cpp") 13 | set(TARGET_NAME Tests_Broker_${TEST_FILE}) 14 | add_executable(${TARGET_NAME} WIN32 ${FILEPATH}) 15 | list(APPEND ALL_BROKER_TESTS ${TARGET_NAME}) 16 | add_dependencies(${TARGET_NAME} CFB::User::Broker) 17 | set_target_properties(${TARGET_NAME} PROPERTIES CXX_STANDARD 20) 18 | target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) 19 | target_include_directories(${TARGET_NAME} PUBLIC ${BROKER_HEADERS_DIR}) 20 | target_compile_definitions(${TARGET_NAME} PRIVATE CATCH_CONFIG_NO_WINDOWS_SEH) 21 | target_compile_options(${TARGET_NAME} PRIVATE $) 22 | target_link_libraries(${TARGET_NAME} PRIVATE Catch2::Catch2 Catch2::Catch2WithMain) 23 | target_link_options(${TARGET_NAME} PRIVATE /SUBSYSTEM:Console $) 24 | add_test(NAME ${TARGET_NAME} COMMAND $) 25 | install(TARGETS ${TARGET_NAME} DESTINATION Tests/Broker) 26 | install(FILES $ DESTINATION Tests/Broker OPTIONAL) 27 | endforeach() 28 | 29 | add_custom_target( 30 | Tests_Broker 31 | DEPENDS ${ALL_BROKER_TESTS} 32 | COMMAND ${CMAKE_CTEST_COMMAND} -R Tests_Broker 33 | ) 34 | -------------------------------------------------------------------------------- /Docs/README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | logo 4 |

5 | 6 |

7 | Build main 8 | Build dev 9 | Discord 10 |

11 | 12 | 13 | ## Idea 14 | 15 | **Canadian Furious Beaver** is a distributed tool for capturing IRPs sent to any Windows driver. It operates in 2 parts: 16 | 17 | 1. the "Broker" combines both a user-land agent and a self-extractable driver (`IrpMonitor.sys`) that will install itself on the targeted system. After installing the driver, the broker will expose a TCP port listening (by default, on TCP/1337) and start collecting IRP from hooked drivers. The communication protocol was made to be simple by design (i.e. not secure) allowing any [3rd party tool](https://github.com/hugsy/cfb-cli) to dump the driver IRPs from the same Broker easily (via simple JSON messages). 18 | 19 | 2. the clients can connect to the broker, and will receive IRPs as a JSON message making it easy to view, or convert to another format. 20 | 21 | ## Why the name? 22 | 23 | Because I had no idea for the name of this tool, so it was graciously generated by [a script of mine](https://github.com/hugsy/stuff/tree/master/random-word). 24 | 25 | -------------------------------------------------------------------------------- /Broker/Source/Broker.rc.in: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | VS_VERSION_INFO VERSIONINFO 4 | FILEVERSION @BROKER_VERSION_MAJOR@,@BROKER_VERSION_MINOR@,@BROKER_VERSION_PATCH@,0 5 | PRODUCTVERSION @CFB_VERSION_MAJOR@,@CFB_VERSION_MINOR@,@CFB_VERSION_PATCH@,0 6 | FILEOS VOS_NT_WINDOWS32 7 | FILETYPE VFT_APP 8 | FILESUBTYPE VFT2_UNKNOWN 9 | 10 | BEGIN 11 | BLOCK "StringFileInfo" 12 | BEGIN 13 | BLOCK "040904E4" 14 | BEGIN 15 | VALUE "CompanyName", "@CFB_COMPANY_NAME@\0" 16 | VALUE "FileDescription", "@PROJECT_DESCRIPTION@\0" 17 | VALUE "FileVersion", "@BROKER_VERSION_MAJOR@,@BROKER_VERSION_MINOR@,@BROKER_VERSION_PATCH@,0\0" 18 | VALUE "LegalCopyright", "(c) @CFB_CURRENT_YEAR@ @CFB_COMPANY_NAME@ Release Under @PROJECT_LICENSE@\0" 19 | VALUE "OriginalFilename", "@PROJECT_NAME@.exe\0" 20 | VALUE "ProductName", "@CMAKE_PROJECT_NAME@\0" 21 | VALUE "ProductVersion", "@CFB_VERSION_MAJOR@,@CFB_VERSION_MINOR@,@CFB_VERSION_PATCH@,0\0" 22 | VALUE "Comments", "@PROJECT_DESCRIPTION@\0" 23 | END 24 | END 25 | BLOCK "VarFileInfo" 26 | BEGIN 27 | VALUE "Translation", 0x409, 1200 28 | END 29 | END 30 | 31 | 32 | #include "Resource.h" 33 | 34 | ///////////////////////////////////////////////////////////////////////////// 35 | // 36 | // CFB_DRIVER 37 | // 38 | 39 | IDR_CFB_DRIVER CFB_DRIVER_DATAFILE "@CFB_BROKER_DRIVER_PATH@" 40 | 41 | 42 | 43 | ///////////////////////////////////////////////////////////////////////////// 44 | // 45 | // Icon 46 | // 47 | 48 | IDI_ICON1 ICON "@CFB_BROKER_ICON_PATH@" 49 | 50 | -------------------------------------------------------------------------------- /Broker/Headers/BrokerUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #include "Broker.hpp" 5 | 6 | 7 | // clang-format on 8 | 9 | 10 | namespace CFB::Broker::Utils 11 | { 12 | 13 | namespace Base64 14 | { 15 | 16 | /// 17 | /// @brief Encode a vector of bytes to base64 18 | /// 19 | /// @param bytes 20 | /// @return std::string 21 | /// 22 | std::string 23 | Encode(std::vector const& bytes); 24 | 25 | /// 26 | /// @brief Encode a char* to base64 27 | /// 28 | /// @param bytes_to_encode 29 | /// @param in_len 30 | /// @return std::string 31 | /// 32 | std::string 33 | Encode(const u8* bytes_to_encode, const usize in_len); 34 | 35 | /// 36 | /// @brief Decode a given base64-encoded string to an array of bytes 37 | /// 38 | /// @param encoded_string 39 | /// @return std::vector 40 | /// 41 | std::vector 42 | Decode(std::string const& encoded_string); 43 | } // namespace Base64 44 | 45 | 46 | /// 47 | /// @brief Helper function to enumerate objects from the Object Manager 48 | /// 49 | /// @param Root the root to start dumping objects 50 | /// @return Result>> 51 | /// 52 | Result>> 53 | EnumerateObjectDirectory(std::wstring const& Root = L"\\"); 54 | 55 | 56 | /// 57 | /// @brief Try to acquire a privilege from its name 58 | /// 59 | /// @param lpszPrivilegeName 60 | /// @return Result 61 | /// 62 | Result 63 | AcquirePrivileges(std::vector const& privilege_names); 64 | 65 | 66 | /// 67 | /// @brief 68 | /// 69 | /// @param lpszPrivilegeName 70 | /// @param lpHasPriv 71 | /// @return Result 72 | /// 73 | Result 74 | HasPrivilege(std::wstring_view const& privilege_name); 75 | 76 | 77 | } // namespace CFB::Broker::Utils 78 | -------------------------------------------------------------------------------- /Broker/Headers/States.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace CFB::Broker 4 | { 5 | enum class State : unsigned int 6 | { 7 | Uninitialized = 1, 8 | ServiceManagerReady = 2, 9 | IrpManagerReady = 3, 10 | ConnectorManagerReady = 4, 11 | AllManagerReady = ConnectorManagerReady, 12 | Running = 5, 13 | ConnectorManagerDone = 6, 14 | IrpManagerDone = 7, 15 | ServiceManagerDone = 8, 16 | AllManagerDone = ServiceManagerDone, 17 | Finished = 9 18 | }; 19 | } // namespace CFB::Broker 20 | 21 | 22 | namespace CFB::Broker::Utils 23 | { 24 | #define CaseToString(x) \ 25 | { \ 26 | case (x): \ 27 | return #x; \ 28 | } 29 | 30 | constexpr const char* 31 | ToString(State x) 32 | { 33 | switch ( x ) 34 | { 35 | CaseToString(State::Uninitialized); 36 | CaseToString(State::ServiceManagerReady); 37 | CaseToString(State::IrpManagerReady); 38 | CaseToString(State::ConnectorManagerReady); 39 | CaseToString(State::Running); 40 | CaseToString(State::ConnectorManagerDone); 41 | CaseToString(State::IrpManagerDone); 42 | CaseToString(State::ServiceManagerDone); 43 | CaseToString(State::Finished); 44 | default: 45 | throw std::invalid_argument("Unimplemented item"); 46 | } 47 | } 48 | #undef CaseAsString 49 | } // namespace CFB::Broker::Utils 50 | -------------------------------------------------------------------------------- /Common/Tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | message(STATUS "Configuring tests for 'CommonLib'") 2 | 3 | enable_testing() 4 | 5 | set(COMMON_HEADERS_DIR ${CFB_ROOT_DIR}/Common/Headers) 6 | set(COMMON_TEST_DIR ${CFB_ROOT_DIR}/Common/Tests) 7 | 8 | set(TEST_SET 9 | Comms 10 | Utils 11 | ) 12 | 13 | foreach(TEST_FILE ${TEST_SET}) 14 | set(FILEPATH "${COMMON_TEST_DIR}/${TEST_FILE}.cpp") 15 | set(TARGET_NAME Tests_Common_${TEST_FILE}) 16 | add_executable(${TARGET_NAME} WIN32 ${FILEPATH}) 17 | list(APPEND ALL_COMMON_TESTS ${TARGET_NAME}) 18 | add_dependencies(${TARGET_NAME} CFB::User::CommonLib) 19 | set_target_properties(${TARGET_NAME} PROPERTIES CXX_STANDARD 20) 20 | target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) 21 | target_include_directories(${TARGET_NAME} PUBLIC ${COMMON_HEADERS_DIR}) 22 | target_compile_definitions( 23 | ${TARGET_NAME} 24 | PRIVATE 25 | CATCH_CONFIG_NO_WINDOWS_SEH 26 | ) 27 | target_compile_options(${TARGET_NAME} PRIVATE $) 28 | target_link_libraries(${TARGET_NAME} PRIVATE Catch2::Catch2 Catch2::Catch2WithMain CFB::User::CommonLib) 29 | target_link_options( 30 | ${TARGET_NAME} 31 | PUBLIC 32 | /SUBSYSTEM:Console 33 | $<$>:$<$:/InferAsanLibs>> 34 | 35 | PRIVATE 36 | $) 37 | add_test(NAME ${TARGET_NAME} COMMAND $) 38 | install(TARGETS ${TARGET_NAME} DESTINATION Tests/Common) 39 | install(FILES $ DESTINATION Tests/Common OPTIONAL) 40 | endforeach() 41 | 42 | add_custom_target( 43 | Tests_Common 44 | DEPENDS ${ALL_COMMON_TESTS} 45 | COMMAND ${CMAKE_CTEST_COMMAND} -R Tests_Common 46 | ) 47 | -------------------------------------------------------------------------------- /Common/Headers/Messages.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /// 4 | /// This file defines the communication protocol and structures for Broker <-> Clients 5 | /// 6 | 7 | #include "Common.hpp" 8 | 9 | #ifdef CFB_KERNEL_DRIVER 10 | 11 | #else 12 | #include 13 | using json = nlohmann::json; 14 | 15 | namespace CFB::Comms 16 | { 17 | 18 | enum class RequestId : uptr 19 | { 20 | InvalidId = 0x00, 21 | 22 | // Command IDs for driver requests 23 | HookDriver = 0x01, 24 | UnhookDriver = 0x02, 25 | GetNumberOfDrivers = 0x03, 26 | GetNamesOfDrivers = 0x04, 27 | GetDriverInfo = 0x05, 28 | SetEventPointer = 0x06, 29 | EnableMonitoring = 0x07, 30 | DisableMonitoring = 0x08, 31 | 32 | // Command IDs for Broker 33 | EnumerateDriverObject = 0x11, 34 | EnumerateDeviceObject = 0x12, 35 | GetPendingIrpNumber = 0x13, 36 | GetPendingIrp = 0x14, 37 | EnumerateMinifilterObject = 0x15, 38 | }; 39 | 40 | 41 | struct DriverRequest 42 | { 43 | /// 44 | /// @brief Mandatory request type id 45 | /// 46 | RequestId Id = RequestId::InvalidId; 47 | 48 | /// 49 | /// @brief Driver name as a wstring, used for 50 | /// - HookDriver 51 | /// - UnhookDriver 52 | /// - EnableMonitoring 53 | /// - DisableMonitoring 54 | /// 55 | std::wstring DriverName; 56 | 57 | /// 58 | ///@brief used for 59 | /// - GetPendingIrp 60 | /// 61 | u16 NumberOfIrp; 62 | }; 63 | 64 | 65 | /// 66 | /// @brief DriverRequest -> JSON 67 | /// 68 | /// @param dst 69 | /// @param src 70 | /// 71 | void 72 | to_json(json& dst, const DriverRequest& src); 73 | 74 | /// 75 | /// @brief JSON -> DriverRequest 76 | /// 77 | /// @param src 78 | /// @param dst 79 | /// 80 | void 81 | from_json(const json& src, DriverRequest& dst); 82 | 83 | } // namespace CFB::Comms 84 | 85 | #endif // CFB_KERNEL_DRIVER 86 | -------------------------------------------------------------------------------- /Broker/Headers/IrpManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #include "Common.hpp" 5 | #include "ManagerBase.hpp" 6 | #include "Comms.hpp" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | // clang-format on 17 | 18 | namespace CFB::Broker 19 | { 20 | 21 | class IrpManager : public ManagerBase 22 | { 23 | public: 24 | /// 25 | /// @brief Construct a new IRP Manager object 26 | /// 27 | IrpManager(); 28 | 29 | /// 30 | /// @brief Destroy the IRP Manager object 31 | /// 32 | ~IrpManager(); 33 | 34 | /// 35 | /// @brief 36 | /// 37 | /// @return std::string const 38 | /// 39 | std::string const 40 | Name(); 41 | 42 | /// 43 | /// @brief 44 | /// 45 | void 46 | Run(); 47 | 48 | /// 49 | /// @brief 50 | /// 51 | /// @return Result 52 | /// 53 | Result 54 | Setup(); 55 | 56 | /// 57 | ///@brief 58 | /// 59 | ///@param cb 60 | ///@return true 61 | ///@return false 62 | /// 63 | bool 64 | SetCallback(std::function cb); 65 | 66 | private: 67 | /// 68 | /// @brief Pop the next IRP from the IrpMonitor and push it to the local queue `m_Irps` 69 | /// 70 | /// @return usize 71 | /// 72 | std::vector 73 | GetNextIrps(); 74 | 75 | /// 76 | /// @brief Handle to the device 77 | /// 78 | wil::unique_handle m_hDevice; 79 | 80 | /// 81 | /// @brief Handle to the notification event 82 | /// 83 | wil::unique_handle m_hNewIrpEvent; 84 | 85 | /// 86 | /// @brief 87 | /// 88 | std::function m_CallbackDispatcher; 89 | 90 | std::mutex m_CallbackLock; 91 | }; 92 | 93 | } // namespace CFB::Broker 94 | -------------------------------------------------------------------------------- /Docs/fuzzing-vm-setup.md: -------------------------------------------------------------------------------- 1 | *Note*: unless specified all commands should be run in an Admin Powershell prompt. 2 | 3 | ## Stop and disable useless services 4 | 5 | ```powershell 6 | # disable some features 7 | Set-MpPreference -DisableRealtimeMonitoring $true 8 | 9 | # disable the services 10 | $services = @("WinDefend", "WSearch", "WerSvc", "wuauserv", "TrustedInstaller", "TroubleShootingSvc") 11 | $services+= @("DiagTrack", "DiagSvc", "diagnosticshub.standardcollector.service") 12 | 13 | foreach($service in $services) 14 | { 15 | Stop-Service -Name $service 16 | Set-Service -StartupType Disabled -Name $service 17 | } 18 | ``` 19 | 20 | ## Define the crash behavior 21 | 22 | ```powershell 23 | $crash = Get-WmiObject Win32_OSRecoveryConfiguration -EnableAllPrivileges 24 | $crash | Set-WmiInstance -Arguments @{ AutoReboot=$False } 25 | # 0 = None 26 | # 1 = Complete memory dump 27 | # 2 = Kernel memory dump 28 | # 3 = Small memory dump 29 | $crash | Set-WmiInstance -Arguments @{ DebugInfoType=1 } 30 | $crash | Set-WmiInstance -Arguments @{ OverwriteExistingDebugFile=$False } 31 | New-Item -ItemType Directory c:\dumps 32 | $crash | Set-WmiInstance -Arguments @{ DebugFilePath="c:\dumps" } 33 | $crash | Set-WmiInstance -Arguments @{ WriteToSystemLog=$False } 34 | ``` 35 | 36 | 37 | ## Make sure the target time is synchro 38 | 39 | ```powershell 40 | Stop-Service -Name W32Time 41 | Push-Location 42 | Set-Location HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DateTime\Servers 43 | Set-ItemProperty . 0 "10.0.0.1" 44 | Set-ItemProperty . "(Default)" "0" 45 | Set-Location HKLM:\SYSTEM\CurrentControlSet\services\W32Time\Parameters 46 | Set-ItemProperty . NtpServer "10.0.0.1" 47 | Pop-Location 48 | Start-Service -Name W32Time 49 | ``` 50 | 51 | ## Blackhole some MS domains 52 | 53 | ```powershell 54 | Invoke-WebRequest -Uri "https://raw.githubusercontent.com/hugsy/modern.ie-vagrant/master/scripts/DisableWin10Telemetry.ps1" -OutFile "c:\temp\DisableWin10Telemetry.ps1" 55 | Set-ExecutionPolicy Bypass 56 | &"c:\temp\DisableWin10Telemetry.ps1" 57 | Remove-Item "c:\temp\DisableWin10Telemetry.ps1" 58 | ``` 59 | -------------------------------------------------------------------------------- /Driver/Headers/CapturedIrpManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CapturedIrp.hpp" 4 | #include "Common.hpp" 5 | #include "DriverUtils.hpp" 6 | 7 | namespace Utils = CFB::Driver::Utils; 8 | 9 | namespace CFB::Driver 10 | { 11 | class CapturedIrpManager 12 | { 13 | public: 14 | /// 15 | /// @brief Construct a new Data Collector object 16 | /// 17 | CapturedIrpManager(); 18 | 19 | /// 20 | /// @brief Destroy the Data Collector object 21 | /// 22 | ~CapturedIrpManager(); 23 | 24 | /// 25 | /// @brief Get a reference to the linked list items stored in this container 26 | /// 27 | /// @return Utils::LinkedList& 28 | /// 29 | Utils::LinkedList& 30 | Items(); 31 | 32 | /// 33 | /// @brief Set the Event object 34 | /// 35 | /// @param hEvent An Event handle 36 | /// 37 | /// @return NTSTATUS 38 | /// 39 | NTSTATUS 40 | SetEvent(const HANDLE hEvent); 41 | 42 | /// 43 | /// @brief Push data to the back of the queue 44 | /// 45 | /// @return true 46 | /// @return false 47 | /// 48 | bool 49 | Push(CapturedIrp* Item); 50 | 51 | /// 52 | /// @brief Pop the front of the queue 53 | /// 54 | /// @return true 55 | /// @return false 56 | /// 57 | CapturedIrp* 58 | Pop(); 59 | 60 | /// 61 | /// @brief Flush the data in the container 62 | /// 63 | void 64 | Clear(); 65 | 66 | /// 67 | /// @brief 68 | /// 69 | /// @return Utils::KCriticalRegion const& 70 | /// 71 | Utils::KCriticalRegion& 72 | CriticalRegion(); 73 | 74 | private: 75 | /// 76 | /// @brief Set when new data is pushed 77 | /// 78 | PKEVENT m_Event; 79 | 80 | /// 81 | /// @brief Item count 82 | /// 83 | usize m_Count; 84 | 85 | /// 86 | /// @brief 87 | /// 88 | Utils::LinkedList m_Entries; 89 | 90 | /// 91 | /// @brief 92 | /// 93 | Utils::KFastMutex m_Mutex; 94 | 95 | /// 96 | /// @brief 97 | /// 98 | Utils::KCriticalRegion m_CriticalRegion; 99 | }; 100 | 101 | } // namespace CFB::Driver 102 | -------------------------------------------------------------------------------- /Common/Headers/Log.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.hpp" 4 | 5 | namespace CFB::Log 6 | { 7 | #ifndef CFB_KERNEL_DRIVER 8 | static inline const ULONG LogLevelDebug = 0; 9 | static inline const ULONG LogLevelInfo = 1; 10 | #endif // CFB_KERNEL_DRIVER 11 | 12 | void 13 | Log(ULONG level, const char* fmtstr, ...); 14 | 15 | #ifndef CFB_KERNEL_DRIVER 16 | void 17 | perror(const char* msg); 18 | 19 | void 20 | ntperror(const char* msg, const NTSTATUS Status); 21 | #endif 22 | }; // namespace CFB::Log 23 | 24 | #ifndef CFB_NS 25 | /// @brief Customizable log prefix 26 | #define CFB_NS 27 | #endif // !CFB_NS 28 | 29 | #ifdef CFB_KERNEL_DRIVER 30 | 31 | #ifdef _DEBUG 32 | #define dbg(fmt, ...) CFB::Log::Log(DPFLTR_INFO_LEVEL, "[=] " CFB_NS " " fmt "\n", __VA_ARGS__) 33 | #else 34 | #define dbg(fmt, ...) 35 | #endif // _DEBUG 36 | 37 | #define ok(fmt, ...) CFB::Log::Log(DPFLTR_INFO_LEVEL, "[+] " CFB_NS " " fmt "\n", __VA_ARGS__) 38 | #define info(fmt, ...) CFB::Log::Log(DPFLTR_TRACE_LEVEL, "[*] " CFB_NS " " fmt "\n", __VA_ARGS__) 39 | #define warn(fmt, ...) CFB::Log::Log(DPFLTR_WARNING_LEVEL, "[!] " CFB_NS " " fmt "\n", __VA_ARGS__) 40 | #define err(fmt, ...) CFB::Log::Log(DPFLTR_ERROR_LEVEL, "[-] " CFB_NS " " fmt "\n", __VA_ARGS__) 41 | #define crit(fmt, ...) CFB::Log::Log(DPFLTR_ERROR_LEVEL, "[/!\\] " CFB_NS " " fmt "\n", __VA_ARGS__) 42 | 43 | #define DML(cmd, title) " " cmd "" 44 | 45 | #else // CFB_KERNEL_DRIVER 46 | 47 | #ifdef _DEBUG 48 | #define dbg(fmt, ...) CFB::Log::Log(CFB::Log::LogLevelDebug, "[=] " CFB_NS " " fmt "\n", __VA_ARGS__) 49 | #else 50 | #define dbg(fmt, ...) 51 | #endif // _DEBUG 52 | 53 | #define ok(fmt, ...) CFB::Log::Log(CFB::Log::LogLevelInfo, "[+] " CFB_NS " " fmt "\n", __VA_ARGS__) 54 | #define info(fmt, ...) CFB::Log::Log(CFB::Log::LogLevelInfo, "[*] " CFB_NS " " fmt "\n", __VA_ARGS__) 55 | #define warn(fmt, ...) CFB::Log::Log(CFB::Log::LogLevelInfo, "[!] " CFB_NS " " fmt "\n", __VA_ARGS__) 56 | #define err(fmt, ...) CFB::Log::Log(CFB::Log::LogLevelInfo, "[-] " CFB_NS " " fmt "\n", __VA_ARGS__) 57 | #define crit(fmt, ...) CFB::Log::Log(CFB::Log::LogLevelInfo, "[/!\\] " CFB_NS " " fmt "\n", __VA_ARGS__) 58 | 59 | #endif // CFB_KERNEL_DRIVER 60 | -------------------------------------------------------------------------------- /GUI/Source/GuiUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "GuiUtils.hpp" 2 | 3 | #ifdef _WIN32 4 | #include 5 | #endif 6 | 7 | #ifdef _WIN32 8 | std::optional 9 | OpenFileWindows(std::string_view const& Filter) 10 | { 11 | std::string filestr; 12 | filestr.resize(MAX_PATH); 13 | 14 | OPENFILENAMEA ofn {}; 15 | ofn.lStructSize = sizeof(OPENFILENAME); 16 | ofn.hwndOwner = ::GetActiveWindow(); 17 | ofn.lpstrFile = filestr.data(); 18 | ofn.nMaxFile = filestr.size(); 19 | ofn.lpstrFilter = Filter.data(); 20 | ofn.nFilterIndex = 1; 21 | ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; 22 | 23 | if ( ::GetOpenFileNameA(&ofn) != TRUE ) 24 | { 25 | return std::nullopt; 26 | } 27 | filestr.resize(::strlen(ofn.lpstrFile)); 28 | return std::filesystem::path(filestr); 29 | } 30 | 31 | std::optional 32 | SaveFileWindows(std::string_view const& Filter) 33 | { 34 | std::string filestr; 35 | filestr.resize(MAX_PATH); 36 | 37 | OPENFILENAMEA ofn {}; 38 | ofn.lStructSize = sizeof(OPENFILENAME); 39 | ofn.hwndOwner = ::GetActiveWindow(); 40 | ofn.lpstrFile = filestr.data(); 41 | ofn.nMaxFile = filestr.size(); 42 | ofn.lpstrFilter = Filter.data(); 43 | ofn.nFilterIndex = 1; 44 | ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; 45 | if ( ::GetSaveFileNameA(&ofn) != TRUE ) 46 | { 47 | return std::nullopt; 48 | } 49 | filestr.resize(::strlen(ofn.lpstrFile)); 50 | return std::filesystem::path(filestr); 51 | } 52 | #endif // _WIN32 53 | 54 | namespace CFB::GUI::Utils 55 | { 56 | 57 | std::optional 58 | FileManager::OpenFile(std::string_view const& Filter) 59 | { 60 | #ifdef _WIN32 61 | return OpenFileWindows(Filter); 62 | #else 63 | return std::nullopt; 64 | #endif 65 | } 66 | 67 | 68 | std::optional 69 | FileManager::SaveFile(std::string_view const& Filter) 70 | { 71 | #ifdef _WIN32 72 | return SaveFileWindows(Filter); 73 | #else 74 | return std::nullopt; 75 | #endif 76 | } 77 | 78 | 79 | } // namespace CFB::GUI::Utils 80 | -------------------------------------------------------------------------------- /Broker/Headers/ManagerBase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Error.hpp" 6 | #include "States.hpp" 7 | 8 | namespace CFB::Broker 9 | { 10 | class ManagerBase 11 | { 12 | public: 13 | /// 14 | /// @brief Construct a new Manager Base object 15 | /// 16 | /// 17 | ManagerBase(); 18 | 19 | /// 20 | /// @brief Destroy the Manager Base object 21 | /// 22 | /// 23 | ~ManagerBase(); 24 | 25 | /// 26 | /// @brief Synchronizes on the Global state semaphore to execute code only when in a specific state 27 | /// 28 | /// @param NewState 29 | /// @return true 30 | /// @return false 31 | /// 32 | bool 33 | WaitForState(CFB::Broker::State WantedState); 34 | 35 | /// 36 | /// @brief Simple wrapper of `Globals.NotifyNewState` 37 | /// 38 | /// @param NewState 39 | /// @return true 40 | /// @return false 41 | /// 42 | bool 43 | SetState(CFB::Broker::State NewState); 44 | 45 | /// 46 | /// @brief 47 | /// 48 | /// @return true 49 | /// @return false 50 | /// 51 | bool 52 | NotifyStateChange(); 53 | 54 | /// 55 | /// @brief 56 | /// 57 | /// @return true 58 | /// @return false 59 | /// 60 | bool 61 | NotifyTermination(); 62 | 63 | /// 64 | /// @brief 65 | /// 66 | /// 67 | virtual Result 68 | Setup() = 0; 69 | 70 | 71 | /// 72 | /// @brief 73 | /// 74 | /// 75 | virtual void 76 | Run() = 0; 77 | 78 | /// 79 | /// @brief 80 | /// 81 | /// @return std::string const& 82 | /// 83 | virtual std::string const 84 | Name() = 0; 85 | 86 | 87 | protected: 88 | /// 89 | /// @brief 90 | /// 91 | /// 92 | wil::unique_handle m_hChangedStateEvent; 93 | 94 | /// 95 | /// @brief 96 | /// 97 | /// 98 | wil::unique_handle m_hTerminationEvent; 99 | 100 | /// 101 | /// @brief 102 | /// 103 | /// 104 | bool m_bIsShuttingDown; 105 | }; 106 | 107 | 108 | } // namespace CFB::Broker 109 | -------------------------------------------------------------------------------- /Common/Headers/IoctlCodes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef CTL_CODE 4 | #define CTL_CODE(DeviceType, Function, Method, Access) \ 5 | (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) 6 | #endif // CTL_CODE 7 | 8 | #ifndef FILE_DEVICE_UNKNOWN 9 | #define FILE_DEVICE_UNKNOWN 0x0022 10 | #endif // FILE_DEVICE_UNKNOWN 11 | 12 | #ifndef METHOD_BUFFERED 13 | #define METHOD_BUFFERED 0 14 | #endif // METHOD_BUFFERED 15 | 16 | #ifndef FILE_ANY_ACCESS 17 | #define FILE_ANY_ACCESS 0 18 | #endif // FILE_ANY_ACCESS 19 | 20 | namespace CFB::Comms 21 | { 22 | 23 | /// 24 | ///@brief An enumeration class of the available IOCTL supported by the driver 25 | /// 26 | enum class Ioctl : u32 27 | { 28 | // clang-format off 29 | 30 | /// 31 | ///@brief ControlDriver - obsolete 32 | /// 33 | ControlDriver = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS), 34 | 35 | /// 36 | ///@brief HookDriver 37 | /// 38 | HookDriver = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS), 39 | 40 | /// 41 | ///@brief UnhookDriver 42 | /// 43 | UnhookDriver = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS), 44 | 45 | /// 46 | ///@brief GetNumberOfDrivers 47 | /// 48 | GetNumberOfDrivers = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS), 49 | 50 | /// 51 | ///@brief GetNamesOfDrivers 52 | /// 53 | GetNamesOfDrivers = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS), 54 | 55 | /// 56 | ///@brief GetDriverInfo 57 | /// 58 | GetDriverInfo = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS), 59 | 60 | /// 61 | ///@brief SetEventPointer 62 | /// 63 | SetEventPointer = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS), 64 | 65 | /// 66 | ///@brief EnableMonitoring 67 | /// 68 | EnableMonitoring = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS), 69 | 70 | /// 71 | ///@brief DisableMonitoring 72 | /// 73 | DisableMonitoring = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS), 74 | 75 | // clang-format on 76 | }; 77 | 78 | } // namespace CFB::Comms 79 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | set(CMAKE_CXX_STANDARD 20) 3 | 4 | set(CMAKE_CXX_STANDARD_REQUIRED True) 5 | set(CMAKE_CXX_EXTENSIONS OFF) 6 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) 7 | 8 | project( 9 | CFB 10 | LANGUAGES CXX 11 | VERSION 0.2.0 12 | DESCRIPTION "Canadian Furious Beaver - IRP interceptor and replayer" 13 | HOMEPAGE_URL https://github.com/hugsy/cfb 14 | ) 15 | 16 | set(PROJECT_AUTHOR hugsy) 17 | set(PROJECT_LICENSE MIT) 18 | 19 | set(CXX_STANDARD 20) 20 | 21 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") 22 | 23 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 24 | 25 | option(CFB_BUILD_GUI "Build GUI" ON) 26 | option(CFB_BUILD_TOOLS "Build Tools" OFF) 27 | option(CFB_BUILD_TESTS "Build Tests" OFF) 28 | 29 | if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") 30 | cmake_policy(SET CMP0135 NEW) 31 | endif() 32 | 33 | # 34 | # Check the dependencies 35 | # 36 | message(STATUS "Locating Windows Driver Kit") 37 | find_package(WDK REQUIRED) 38 | find_package(PhNt REQUIRED) 39 | find_package(WIL REQUIRED) 40 | find_package(Json REQUIRED) 41 | find_package(argparse REQUIRED) 42 | 43 | if(CFB_BUILD_GUI) 44 | find_package(ImGUI REQUIRED) 45 | endif(CFB_BUILD_GUI) 46 | 47 | if(CFB_BUILD_TESTS) 48 | find_package(Catch2 REQUIRED) 49 | endif(CFB_BUILD_TESTS) 50 | 51 | set(CFB_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) 52 | 53 | set(CFB_VERSION_MAJOR ${PROJECT_VERSION_MAJOR} CACHE INTERNAL "CFB_VERSION_MAJOR") 54 | set(CFB_VERSION_MINOR ${PROJECT_VERSION_MINOR} CACHE INTERNAL "CFB_VERSION_MINOR") 55 | set(CFB_VERSION_PATCH ${PROJECT_VERSION_PATCH} CACHE INTERNAL "CFB_VERSION_PATCH") 56 | set(CFB_COMPANY_NAME "BlahCat Corp." CACHE INTERNAL "CFB_COMPANY_NAME") 57 | string(TIMESTAMP CFB_CURRENT_YEAR "%Y") 58 | 59 | # 60 | # Build 61 | # 62 | add_subdirectory(${CFB_ROOT_DIR}/Common) 63 | add_subdirectory(${CFB_ROOT_DIR}/Driver) 64 | add_subdirectory(${CFB_ROOT_DIR}/Broker) 65 | 66 | if(CFB_BUILD_GUI) 67 | add_subdirectory(${CFB_ROOT_DIR}/GUI) 68 | endif(CFB_BUILD_GUI) 69 | 70 | if(CFB_BUILD_TOOLS) 71 | add_subdirectory(${CFB_ROOT_DIR}/Driver/Client) 72 | endif(CFB_BUILD_TOOLS) 73 | 74 | if(CFB_BUILD_TESTS) 75 | include(CTest) 76 | add_subdirectory(${CFB_ROOT_DIR}/Common/Tests) 77 | add_subdirectory(${CFB_ROOT_DIR}/Broker/Tests) 78 | add_subdirectory(${CFB_ROOT_DIR}/GUI/Tests) 79 | endif(CFB_BUILD_TESTS) 80 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | # schedule: 9 | # - cron: 0 0 * * * 10 | workflow_dispatch: 11 | inputs: 12 | tag_name: 13 | description: 'Tag name for release' 14 | required: false 15 | default: nightly 16 | 17 | 18 | jobs: 19 | build: 20 | name: "${{ matrix.platform }}/${{ matrix.configuration }}" 21 | 22 | env: 23 | CMAKE_FLAGS: "-DCFB_BUILD_TOOLS:BOOL=ON -DCFB_BUILD_GUI:BOOL=ON -DCFB_BUILD_TESTS:BOOL=OFF" 24 | CMAKE_TOOLCHAIN_FILE: "C:\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake" 25 | TAG_NAME: "" 26 | NB_CPU: 1 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | os: ['windows-latest'] 32 | platform: ['x64', 'arm64'] 33 | configuration: ['Debug', 'RelWithDebInfo'] 34 | 35 | runs-on: ${{ matrix.os }} 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v4 39 | 40 | - if: github.event_name == 'workflow_dispatch' 41 | run: echo "TAG_NAME=${{ github.event.inputs.tag_name }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 42 | 43 | - if: github.event_name == 'schedule' 44 | run: echo "TAG_NAME=nightly" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 45 | 46 | - name: Setup 47 | shell: pwsh 48 | run: | 49 | echo "NB_CPU=$env:NUMBER_OF_PROCESSORS" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 50 | Import-Module .\.github\Invoke-VisualStudio.ps1 51 | Invoke-VisualStudio2022${{ matrix.platform }} 52 | 53 | - name: Build 54 | shell: pwsh 55 | run: | 56 | mkdir build 57 | cmake -S . -B ./build -A ${{ matrix.platform }} ${{ env.CMAKE_FLAGS }} 58 | cmake --build ./build --verbose --parallel ${{ env.NB_CPU }} --config ${{ matrix.configuration }} 59 | 60 | - name: Install 61 | shell: pwsh 62 | if: matrix.configuration == 'RelWithDebInfo' 63 | run: | 64 | mkdir artifact 65 | cmake --install ./build --config ${{ matrix.configuration }} --prefix ./artifact --verbose 66 | 67 | - name: Upload 68 | uses: actions/upload-artifact@v3 69 | if: matrix.configuration == 'RelWithDebInfo' 70 | with: 71 | name: CFB_${{ matrix.platform }}_${{ matrix.configuration }}_${{ github.head_ref }}_${{ github.sha }} 72 | path: artifact/ 73 | retention-days: 1 74 | -------------------------------------------------------------------------------- /Driver/Headers/Context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #include "Common.hpp" 5 | #include "DriverUtils.hpp" 6 | #include "Log.hpp" 7 | 8 | #include "CapturedIrpManager.hpp" 9 | #include "HookedDriverManager.hpp" 10 | // clang-format on 11 | 12 | 13 | #define CFB_MAX_HEXDUMP_BYTE 64 14 | 15 | namespace Driver = CFB::Driver; 16 | namespace Utils = CFB::Driver::Utils; 17 | 18 | struct GlobalContext 19 | { 20 | /// 21 | /// @brief Any critical read/write operation to the global context structure must acquire this lock. 22 | /// 23 | Utils::KQueuedSpinLock ContextLock; 24 | 25 | /// 26 | /// @brief A pointer to the device object 27 | /// 28 | PDRIVER_OBJECT DriverObject; 29 | 30 | /// 31 | /// @brief A pointer to the device object 32 | /// 33 | PDEVICE_OBJECT DeviceObject; 34 | 35 | /// 36 | /// @brief A pointer to the EPROCESS of the broker. Not more than one handle to the 37 | /// device is allowed. 38 | /// 39 | PEPROCESS Owner; 40 | 41 | /// 42 | /// @brief Incremental session ID number. 43 | /// 44 | ULONG SessionId; 45 | 46 | /// 47 | /// @brief Manages the hooked drivers 48 | /// 49 | Driver::HookedDriverManager DriverManager; 50 | 51 | /// 52 | /// @brief Where all the intercepted IRPs are stored 53 | /// 54 | Driver::CapturedIrpManager IrpManager; 55 | 56 | 57 | GlobalContext() : DriverObject {nullptr}, DeviceObject {nullptr}, Owner {nullptr}, ContextLock {}, SessionId(-1) 58 | { 59 | dbg("Creating GlobalContext"); 60 | } 61 | 62 | 63 | ~GlobalContext() 64 | { 65 | dbg("Destroying GlobalContext"); 66 | DriverObject = nullptr; 67 | DeviceObject = nullptr; 68 | Owner = nullptr; 69 | } 70 | 71 | static void* 72 | operator new(usize sz) 73 | { 74 | void* Memory = ::ExAllocatePoolWithTag(NonPagedPoolNx, sz, CFB_DEVICE_TAG); 75 | if ( Memory ) 76 | { 77 | dbg("Allocated GlobalContext at %p", Memory); 78 | ::RtlSecureZeroMemory(Memory, sz); 79 | } 80 | return Memory; 81 | } 82 | 83 | static void 84 | operator delete(void* m) 85 | { 86 | dbg("Deallocating GlobalContext"); 87 | ::ExFreePoolWithTag(m, CFB_DEVICE_TAG); 88 | m = nullptr; 89 | return; 90 | } 91 | }; 92 | 93 | /// 94 | /// @brief Reference to the global driver context. 95 | /// 96 | extern struct GlobalContext* Globals; 97 | -------------------------------------------------------------------------------- /Common/Headers/Common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // clang-format off 3 | #ifdef CFB_KERNEL_DRIVER 4 | #include 5 | #include 6 | #include 7 | #else 8 | #include 9 | #endif // CFB_KERNEL_DRIVER 10 | // clang-format on 11 | 12 | #define __STR(x) #x 13 | #define STR(x) __STR(x) 14 | #define __WIDE(x) L#x 15 | #define WIDECHAR(x) __WIDE(x) 16 | #define __WIDE2(x) L##x 17 | #define WIDECHAR2(x) __WIDE2(x) 18 | #define CONCAT(x, y) (x##y) 19 | 20 | // 21 | // Types 22 | // 23 | 24 | /// 25 | /// Static types 26 | /// 27 | using u8 = UINT8; 28 | using u16 = UINT16; 29 | using u32 = UINT32; 30 | using u64 = UINT64; 31 | 32 | using i8 = INT8; 33 | using i16 = INT16; 34 | using i32 = INT32; 35 | using i64 = INT64; 36 | 37 | using usize = SIZE_T; 38 | using uptr = ULONG_PTR; 39 | 40 | // clang-format off 41 | #define CFB_DEVICE_NAME L"IrpMonitor" 42 | #define CFB_DEVICE_PATH L"\\Device\\" CFB_DEVICE_NAME 43 | #define CFB_DOS_DEVICE_PATH L"\\??\\" CFB_DEVICE_NAME 44 | #define CFB_USER_DEVICE_PATH L"\\\\?\\GlobalRoot\\Device\\" CFB_DEVICE_NAME 45 | #define CFB_DEVICE_TAG ' BFC' 46 | 47 | #define CFB_DRIVER_MAX_PATH 256 48 | #define CFB_DRIVER_BASENAME CFB_DEVICE_NAME L".sys" 49 | 50 | #define CFB_BROKER_DRIVER_SERVICE_NAME CFB_DEVICE_NAME 51 | #define CFB_BROKER_DRIVER_SERVICE_DESCRIPTION L"IRP monitor driver for Canadian Furious Beaver broker" 52 | 53 | #define CFB_BROKER_WIN32_SERVICE_NAME L"CfbBrokerSvc" 54 | #define CFB_BROKER_WIN32_SERVICE_DESCRIPTION L"Service for having the CFB broker in background" 55 | 56 | #ifndef countof 57 | #define countof(arr) ((sizeof(arr)) / (sizeof(arr[0]))) 58 | #endif 59 | 60 | #ifndef boolstr 61 | #define boolstr(x) ( (x) ? "true" : "false") 62 | #endif 63 | 64 | #ifndef boolstrw 65 | #define boolstrw(x) ( (x) ? L"true" : L"false") 66 | #endif 67 | 68 | #ifndef MIN 69 | #define MIN(a,b) ( (a) < (b) ? (a) : (b)) 70 | #endif // MIN 71 | 72 | #ifndef MAX 73 | #define MAX(a,b) ( (a) < (b) ? (b) : (a)) 74 | #endif // MAX 75 | 76 | #ifndef USHRT_MAX 77 | #define USHRT_MAX ((1 << (sizeof(u16) * 8)) - 1) 78 | #endif // USHRT_MAX 79 | 80 | #ifndef PLURAL_IF 81 | #define PLURAL_IF(x) ((x) ? "s" : "") 82 | #endif // PLURAL_IF 83 | 84 | #ifndef WPLURAL_IF 85 | #define WPLURAL_IF(x) ((x) ? L"s" : L"") 86 | #endif // WPLURAL_IF 87 | 88 | // clang-format on 89 | -------------------------------------------------------------------------------- /.github/workflows/notify.yml: -------------------------------------------------------------------------------- 1 | name: Notify 2 | 3 | on: 4 | pull_request: 5 | types: [ opened ] 6 | issues: 7 | types: [ opened ] 8 | workflow_run: 9 | workflows: ["Build", "Test"] 10 | types: [completed] 11 | branches: ["*"] 12 | 13 | permissions: 14 | contents: read 15 | 16 | env: 17 | DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} 18 | 19 | jobs: 20 | on_workflow_failure: 21 | runs-on: ubuntu-latest 22 | if: ${{ github.event.workflow_run.conclusion == 'failure' }} 23 | steps: 24 | - uses: sarisia/actions-status-discord@v1 25 | with: 26 | nodetail: true 27 | title: "⚠️ '${{ github.event.workflow_run.name }}' failed ${{ github.sha }} on ${{ github.ref }} ${{ github.actor }}" 28 | description: | 29 | Build failure for 30 | ● SHA ${{ github.sha }} 31 | ● REF ${{ github.ref }} 32 | ● ACTOR ${{ github.actor }} 33 | 34 | --- 35 | [Link](${{ github.event.workflow_run.html_url }}) 36 | color: 0xff0000 37 | username: ${{ github.actor }} on CFB 38 | avatar_url: https://i.imgur.com/O4RKNqj.png 39 | 40 | on_pull_requests: 41 | runs-on: ubuntu-latest 42 | if: github.event_name == 'pull_request' 43 | steps: 44 | - uses: sarisia/actions-status-discord@v1 45 | with: 46 | nodetail: true 47 | title: "🚧 ${{ github.actor }} ${{ github.event.action }} PR `#${{ github.event.pull_request.number }}`" 48 | description: | 49 | **${{ github.event.pull_request.title }}** 50 | 51 | ${{ github.event.pull_request.body }} 52 | 53 | --- 54 | [Link](${{ github.event.pull_request.html_url }}) 55 | color: 0x00ff00 56 | username: ${{ github.actor }} on CFB 57 | avatar_url: https://i.imgur.com/O4RKNqj.png 58 | 59 | on-issues: 60 | runs-on: ubuntu-latest 61 | if: github.event_name == 'issues' 62 | steps: 63 | - uses: sarisia/actions-status-discord@v1 64 | with: 65 | nodetail: true 66 | title: "❓ ${{ github.actor }} ${{ github.event.action }} Issue `#${{ github.event.issue.number }}`" 67 | description: | 68 | **${{ github.event.issue.title }}** 69 | 70 | ${{ github.event.issue.body }} 71 | 72 | --- 73 | [Link](${{ github.event.issue.html_url }}) 74 | color: 0x0000ff 75 | username: ${{ github.actor }} on CFB 76 | avatar_url: https://i.imgur.com/O4RKNqj.png 77 | -------------------------------------------------------------------------------- /GUI/Headers/Network.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WIN32_LEAN_AND_MEAN 3 | 4 | #pragma warning(push) 5 | #pragma warning(disable : 4005) // Macro redefinitions 6 | #include 7 | #pragma warning(pop) 8 | 9 | #include "App.hpp" 10 | 11 | namespace CFB::GUI::App 12 | { 13 | 14 | 15 | class Target 16 | { 17 | public: 18 | std::string Host {}; 19 | u16 Port {}; 20 | bool IsConnected {}; 21 | 22 | bool 23 | Connect() 24 | { 25 | if ( IsConnected ) 26 | { 27 | return true; 28 | } 29 | 30 | WSADATA WsaData {}; 31 | if ( ::WSAStartup(MAKEWORD(2, 2), &WsaData) ) 32 | { 33 | return false; 34 | } 35 | 36 | m_Socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 37 | if ( m_Socket == INVALID_SOCKET ) 38 | { 39 | ::WSACleanup(); 40 | return false; 41 | } 42 | 43 | SOCKADDR_IN sa; 44 | sa.sin_addr.s_addr = ::inet_addr(Host.c_str()); 45 | sa.sin_family = AF_INET; 46 | sa.sin_port = ::htons(Port); 47 | IsConnected = ::connect(m_Socket, (PSOCKADDR)&sa, sizeof(SOCKADDR_IN)) != SOCKET_ERROR; 48 | 49 | return IsConnected; 50 | } 51 | 52 | bool 53 | Disconnect() 54 | { 55 | if ( !IsConnected ) 56 | { 57 | return true; 58 | } 59 | 60 | IsConnected = (::closesocket(m_Socket) == 0) ? false : true; 61 | ::WSACleanup(); 62 | return IsConnected; 63 | } 64 | 65 | template 66 | bool 67 | Send(T const& Buffer) 68 | { 69 | if ( !IsConnected ) 70 | { 71 | return false; 72 | } 73 | 74 | int iResult = send(m_Socket, (const char*)Buffer.data(), Buffer.size(), 0); 75 | if ( iResult == SOCKET_ERROR ) 76 | { 77 | return false; 78 | } 79 | return true; 80 | } 81 | 82 | template 83 | T 84 | Receive(usize BufferSize) 85 | { 86 | if ( !IsConnected ) 87 | { 88 | return {}; 89 | } 90 | 91 | T Buffer; 92 | Buffer.resize(BufferSize); 93 | int iResult = recv(m_Socket, (char*)Buffer.data(), BufferSize, 0); 94 | if ( iResult == SOCKET_ERROR ) 95 | { 96 | return {}; 97 | } 98 | 99 | return Buffer; 100 | } 101 | 102 | private: 103 | SOCKET m_Socket; 104 | }; 105 | 106 | 107 | } // namespace CFB::GUI::App 108 | -------------------------------------------------------------------------------- /Common/Source/Log.cpp: -------------------------------------------------------------------------------- 1 | #include "Log.hpp" 2 | 3 | #ifdef CFB_KERNEL_DRIVER 4 | #include 5 | 6 | #else 7 | #include 8 | #include 9 | 10 | #include 11 | #endif // CFB_KERNEL_DRIVER 12 | 13 | #define __WFILE__ WIDEN(__FILE__) 14 | #define __WFUNCTION__ WIDEN(__FUNCTION__) 15 | 16 | 17 | namespace CFB::Log 18 | { 19 | void 20 | Log(ULONG level, const char* fmtstr, ...) 21 | { 22 | va_list args; 23 | va_start(args, fmtstr); 24 | 25 | #ifdef CFB_KERNEL_DRIVER 26 | // 27 | // Use `nt!Kd_IHVDRIVER_Mask` to control the level. Only log at PASSIVE & APC 28 | // 29 | if ( ::KeGetCurrentIrql() >= DISPATCH_LEVEL ) 30 | { 31 | return; 32 | } 33 | 34 | ::vDbgPrintEx(DPFLTR_IHVDRIVER_ID, level, fmtstr, args); 35 | 36 | #else 37 | std::string out; 38 | out.resize(1024); 39 | 40 | ::vsnprintf(out.data(), out.size(), fmtstr, args); 41 | 42 | if ( level == LogLevelDebug ) 43 | { 44 | ::OutputDebugStringA(out.c_str()); 45 | } 46 | else 47 | { 48 | out.resize(std::strlen(out.c_str())); 49 | std::cerr << out; 50 | } 51 | #endif // CFB_KERNEL_DRIVER 52 | 53 | va_end(args); 54 | } 55 | 56 | #ifndef CFB_KERNEL_DRIVER 57 | 58 | extern "C" void WINAPI 59 | SetLastError(_In_ DWORD dwErrCode); 60 | 61 | extern "C" ULONG WINAPI 62 | RtlNtStatusToDosError(_In_ NTSTATUS Status); 63 | 64 | 65 | // 66 | // User specific logging functions 67 | // 68 | 69 | void 70 | perror(const char* msg) 71 | { 72 | const u32 sysMsgSz = 2048; 73 | auto sysMsg = std::string(); 74 | sysMsg.resize(sysMsgSz); 75 | const auto errcode = ::GetLastError(); 76 | 77 | ::FormatMessageA( 78 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, // FORMAT_MESSAGE_FROM_HMODULE 79 | nullptr, 80 | errcode, 81 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 82 | sysMsg.data(), 83 | sysMsgSz, 84 | nullptr); 85 | 86 | const usize max_len = ::wcslen((wchar_t*)sysMsg.c_str()); 87 | err("%s, errcode=0x%08x: %s", msg, errcode, sysMsg.c_str()); 88 | } 89 | 90 | void 91 | ntperror(const char* msg, const NTSTATUS Status) 92 | { 93 | auto dwDosError = RtlNtStatusToDosError(Status); 94 | auto hResult = HRESULT_FROM_WIN32(dwDosError); 95 | ::SetLastError(hResult); 96 | CFB::Log::perror(msg); 97 | return; 98 | } 99 | #endif 100 | } // namespace CFB::Log 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | logo 4 |

5 | 6 |

7 | Build main 8 | Build dev 9 | Discord 10 |

11 | 12 | ## What is it? 13 | 14 | > [!CAUTION] 15 | > CFB is meant for research and debug purposes, it should never be used on production systems. Also BSoD may happen. You've been warned. 16 | 17 | **Canadian Furious Beaver** is a [`ProcMon`](https://learn.microsoft.com/en-us/sysinternals/downloads/procmon)-style tool designed only for capturing IRPs sent to any Windows driver. It operates in 2 parts: 18 | 19 | 1. the "Broker" combines both a user-land agent and a self-extractable driver (`IrpMonitor.sys`) that will install itself on the targeted system. After installing the driver, the broker will expose a TCP port listening (by default, on TCP/1337) and start collecting IRP from hooked drivers. The communication protocol was made to be simple by design (i.e. not secure) allowing any [3rd party tool](https://github.com/hugsy/cfb-cli) to dump the driver IRPs from the same Broker easily (via simple JSON messages). 20 | 21 | 2. the clients can connect to the broker, and will receive IRPs as a JSON message making it easy to view, or convert to another format. 22 | 23 | ![GUI](https://i.imgur.com/MUFYrL2.png) 24 | 25 | ![CLI](https://i.imgur.com/5MWjqLa.png) 26 | 27 | 3. IRPs (metadata, input/output buffers) can be stored to file on disk in the JSON format allowing for easy further scripting. 28 | 29 | > [!WARNING] 30 | > Although the CFB driver (`IrpMonitor.sys`) should not violate patchguard, it however is only self-signed and so requires [`TestSigning`](https://learn.microsoft.com/en-us/windows-hardware/drivers/install/the-testsigning-boot-configuration-option) enabled in the BCD 31 | 32 | ## Why the name? 33 | 34 | Because I had no idea for the name of this tool, so it was graciously generated by [a script of mine](https://github.com/hugsy/stuff/tree/master/random-word). 35 | 36 | ## Kudos 37 | 38 | * `processhacker` for their [`phnt` header files](https://github.com/processhacker/phnt) 39 | * `nlohmann` for their [`json` library](https://github.com/nlohmann/json) 40 | -------------------------------------------------------------------------------- /Broker/Source/Main.cpp: -------------------------------------------------------------------------------- 1 | #define NOMINMAX 2 | #undef _UNICODE 3 | #undef UNICODE 4 | 5 | // clang-format off 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "Broker.hpp" 16 | #include "Common.hpp" 17 | #include "BrokerUtils.hpp" 18 | #include "Context.hpp" 19 | #include "Log.hpp" 20 | // clang-format on 21 | 22 | using namespace ::std::literals; 23 | 24 | struct GlobalContext Globals; 25 | 26 | 27 | static bool 28 | CtrlHandler(const DWORD dwCtrlType) 29 | { 30 | switch ( dwCtrlType ) 31 | { 32 | case CTRL_CLOSE_EVENT: 33 | case CTRL_C_EVENT: 34 | dbg("Received Ctrl-C event, stopping..."); 35 | Globals.Stop(); 36 | return true; 37 | 38 | default: 39 | break; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | 46 | int 47 | main(int argc, const char** argv) 48 | { 49 | argparse::ArgumentParser program("Broker"); 50 | 51 | const std::vector valid_modes = {"run-standalone", "install-service", "run-as-service"}; 52 | program.add_argument("mode") 53 | .remaining() 54 | .default_value(valid_modes.front()) 55 | .action( 56 | [&valid_modes](const std::string& value) 57 | { 58 | if ( std::find(valid_modes.cbegin(), valid_modes.cend(), value) != valid_modes.cend() ) 59 | { 60 | return value; 61 | } 62 | return valid_modes.front(); 63 | }); 64 | 65 | try 66 | { 67 | program.parse_args(argc, argv); 68 | } 69 | catch ( const std::runtime_error& err ) 70 | { 71 | std::cerr << err.what() << std::endl; 72 | std::cerr << program; 73 | std::exit(1); 74 | } 75 | 76 | if ( Failed(CFB::Broker::Utils::AcquirePrivileges({L"SeDebugPrivilege", L"SeLoadDriverPrivilege"})) ) 77 | { 78 | err("Cannot required privileged, cannot continue"); 79 | std::exit(-2); 80 | } 81 | 82 | 83 | auto const& mode = program.get("mode"); 84 | 85 | if ( mode == "run-standalone" ) 86 | { 87 | info("Running in standalone..."); 88 | ::SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, true); 89 | ::WaitForSingleObject(Globals.TerminationEvent(), INFINITE); 90 | } 91 | 92 | else if ( mode == "install-service" ) 93 | { 94 | Globals.ServiceManager()->InstallBackgroundService(); 95 | } 96 | 97 | else if ( mode == "run-as-service" ) 98 | { 99 | Globals.ServiceManager()->RunAsBackgroundService(); 100 | } 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /Common/Headers/Utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.hpp" 4 | #include "Comms.hpp" 5 | 6 | #ifdef CFB_KERNEL_DRIVER 7 | #define XPRINTF(...) \ 8 | { \ 9 | ::DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, __VA_ARGS__); \ 10 | } 11 | 12 | #else 13 | 14 | #include 15 | 16 | #include 17 | 18 | #include "IoctlCodes.hpp" 19 | 20 | #ifdef CFB_KERNEL_DRIVER 21 | #else 22 | #include "Messages.hpp" 23 | #endif // CFB_KERNEL_DRIVER 24 | 25 | #define XPRINTF(...) \ 26 | { \ 27 | printf(__VA_ARGS__); \ 28 | } 29 | #endif // CFB_KERNEL_DRIVER 30 | 31 | #define WIDEN2(x) L##x 32 | #define WIDEN(x) WIDEN2(x) 33 | 34 | namespace CFB::Utils 35 | { 36 | /// 37 | /// @brief Print out an hexdump-like version of the buffer. 38 | /// 39 | /// @param data A pointer to the buffer to hexdump 40 | /// @param size The size of the buffer 41 | /// @param header If not null, an header to print before the hexdump 42 | /// @param base Specifies the base address 43 | /// 44 | void 45 | Hexdump(const PVOID data, SIZE_T size, PCSTR header = nullptr, SIZE_T base = 0); 46 | 47 | const char* 48 | IrqlToString(u32 type); 49 | 50 | 51 | const char* 52 | IrpMajorToString(u32 type); 53 | 54 | /// 55 | ///@brief 56 | /// 57 | ///@param Type 58 | ///@return const char* 59 | /// 60 | const char* 61 | IrpTypeToString(u8 Type); 62 | 63 | 64 | #ifdef CFB_KERNEL_DRIVER 65 | #else 66 | std::string 67 | ToString(std::wstring const& input); 68 | 69 | std::wstring 70 | ToWideString(std::string const& input); 71 | 72 | std::string 73 | ToString(CFB::Comms::Ioctl code); 74 | 75 | std::string 76 | ToString(CFB::Comms::RequestId id); 77 | 78 | std::string 79 | ToString(CFB::Comms::CapturedIrp const& Irp); 80 | 81 | 82 | #endif // CFB_KERNEL_DRIVER 83 | 84 | namespace Memory 85 | { 86 | /// 87 | /// @brief 88 | /// 89 | /// @param Value 90 | /// @param Base 91 | /// @return true 92 | /// @return false 93 | /// 94 | bool 95 | IsAligned(uptr const Value, usize const Base); 96 | 97 | /// 98 | ///@brief Align a value to a base 99 | /// 100 | ///@param Value 101 | ///@param Base 102 | ///@return uptr 103 | /// 104 | uptr 105 | AlignValue(uptr const Value, usize const Base); 106 | } // namespace Memory 107 | } // namespace CFB::Utils 108 | -------------------------------------------------------------------------------- /Driver/Source/CapturedIrpManager.cpp: -------------------------------------------------------------------------------- 1 | #include "CapturedIrpManager.hpp" 2 | namespace CFB::Driver 3 | { 4 | 5 | 6 | CapturedIrpManager::CapturedIrpManager() : m_Event(nullptr), m_Count(0), m_Entries(), m_Mutex() 7 | { 8 | } 9 | 10 | CapturedIrpManager::~CapturedIrpManager() 11 | { 12 | Clear(); 13 | 14 | if ( m_Event ) 15 | { 16 | // 17 | // Free the Event object 18 | // 19 | ::KeResetEvent(m_Event); 20 | ObDereferenceObject(m_Event); 21 | m_Event = nullptr; 22 | } 23 | } 24 | 25 | 26 | Utils::LinkedList& 27 | CapturedIrpManager::Items() 28 | { 29 | return m_Entries; 30 | } 31 | 32 | NTSTATUS 33 | CapturedIrpManager::SetEvent(const HANDLE hEvent) 34 | { 35 | NTSTATUS Status = STATUS_UNSUCCESSFUL; 36 | PKEVENT NewEvent = nullptr; 37 | 38 | auto lock = Utils::ScopedLock(m_Mutex); 39 | 40 | // 41 | // Get a reference to the Event object 42 | // 43 | Status = 44 | ::ObReferenceObjectByHandle(hEvent, EVENT_ALL_ACCESS, *ExEventObjectType, UserMode, (PVOID*)&NewEvent, nullptr); 45 | if ( !NT_SUCCESS(Status) ) 46 | { 47 | return Status; 48 | } 49 | 50 | // 51 | // If an event object was already assigned, replace it 52 | // 53 | if ( m_Event != nullptr ) 54 | { 55 | ObDereferenceObject(m_Event); 56 | m_Event = nullptr; 57 | } 58 | 59 | m_Event = NewEvent; 60 | 61 | return Status; 62 | } 63 | 64 | 65 | bool 66 | CapturedIrpManager::Push(CapturedIrp* Item) 67 | { 68 | Utils::ScopedLock lock(m_Mutex); 69 | 70 | // 71 | // Push the item to the back of the queue 72 | // 73 | m_Entries.PushBack(Item); 74 | m_Count++; 75 | 76 | // 77 | // Set the event to notify the broker some data is ready 78 | // 79 | if ( m_Event ) 80 | { 81 | ::KeSetEvent(m_Event, 2, false); 82 | } 83 | 84 | return true; 85 | } 86 | 87 | 88 | CapturedIrp* 89 | CapturedIrpManager::Pop() 90 | { 91 | Utils::ScopedLock lock(m_Mutex); 92 | 93 | // 94 | // Stored data are treated as a FIFO queue 95 | // 96 | CapturedIrp* Item = m_Entries.PopFront(); 97 | if ( Item == nullptr ) 98 | { 99 | return nullptr; 100 | } 101 | 102 | m_Count--; 103 | 104 | // 105 | // Unset the event is no data is ready 106 | // 107 | if ( m_Count == 0 && m_Event ) 108 | { 109 | ::KeClearEvent(m_Event); 110 | } 111 | 112 | return Item; 113 | } 114 | 115 | 116 | void 117 | CapturedIrpManager::Clear() 118 | { 119 | do 120 | { 121 | auto Item = Pop(); 122 | if ( !Item ) 123 | { 124 | break; 125 | } 126 | } while ( true ); 127 | } 128 | 129 | 130 | Utils::KCriticalRegion& 131 | CapturedIrpManager::CriticalRegion() 132 | { 133 | return m_CriticalRegion; 134 | } 135 | 136 | 137 | } // namespace CFB::Driver 138 | -------------------------------------------------------------------------------- /Driver/Headers/HookedDriverManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.hpp" 4 | #include "DriverUtils.hpp" 5 | #include "HookedDriver.hpp" 6 | #include "Log.hpp" 7 | 8 | #define CFB_MAX_HOOKED_DRIVERS 32 9 | 10 | namespace Utils = CFB::Driver::Utils; 11 | 12 | namespace CFB::Driver 13 | { 14 | class HookedDriverManager 15 | { 16 | public: 17 | /// 18 | /// @brief Construct a new Hooked Driver Manager object 19 | /// 20 | HookedDriverManager(); 21 | 22 | /// 23 | /// @brief Destroy the Hooked Driver Manager object 24 | /// 25 | ~HookedDriverManager(); 26 | 27 | /// 28 | ///@brief HookedDriverManager memory allocator 29 | /// 30 | ///@param sz 31 | ///@return void* 32 | /// 33 | static void* 34 | operator new(usize sz) 35 | { 36 | void* Memory = ::ExAllocatePoolWithTag(NonPagedPoolNx, sz, CFB_DEVICE_TAG); 37 | if ( Memory ) 38 | { 39 | dbg("Allocating HookedDriverManager at %p", Memory); 40 | ::RtlSecureZeroMemory(Memory, sz); 41 | } 42 | return Memory; 43 | } 44 | 45 | /// 46 | ///@brief HookedDriverManager memory destructor 47 | /// 48 | ///@param Memory 49 | /// 50 | static void 51 | operator delete(void* Memory) 52 | { 53 | dbg("Deallocating HookedDriverManager"); 54 | return ::ExFreePoolWithTag(Memory, CFB_DEVICE_TAG); 55 | } 56 | 57 | /// 58 | /// @brief 59 | /// 60 | /// @param UnicodePath 61 | /// @return NTSTATUS 62 | /// 63 | NTSTATUS 64 | InsertDriver(Utils::KUnicodeString const& UnicodePath); 65 | 66 | /// 67 | /// @brief 68 | /// 69 | /// @param UnicodePath 70 | /// @return NTSTATUS 71 | /// 72 | NTSTATUS 73 | RemoveDriver(Utils::KUnicodeString const& UnicodePath); 74 | 75 | /// 76 | /// @brief 77 | /// 78 | /// @return NTSTATUS 79 | /// 80 | NTSTATUS 81 | RemoveAllDrivers(); 82 | 83 | /// 84 | /// @brief Set the monitoring state for the driver given in argument. If `true`, monitoring will become active, 85 | /// effectively capturing all the IRPs to the driver. Setting to `false` disables the monitoring. 86 | /// 87 | /// @param UnicodePath Path to the driver (as a Unicode string) 88 | /// @param bEnable `true` to enable, `false` to disable 89 | /// @return NTSTATUS the error code returned from the function. 90 | /// 91 | NTSTATUS 92 | SetMonitoringState(const PUNICODE_STRING UnicodePath, bool bEnable); 93 | 94 | 95 | Utils::LinkedList& 96 | Items(); 97 | 98 | private: 99 | /// 100 | /// @brief A pointer to the head of hooked drivers 101 | /// 102 | Utils::LinkedList m_Entries; 103 | 104 | /// 105 | /// @brief A mutex to protect access to the critical resources 106 | /// 107 | Utils::KFastMutex m_Mutex; 108 | }; 109 | 110 | } // namespace CFB::Driver 111 | -------------------------------------------------------------------------------- /Broker/Source/Connectors/Dummy.cpp: -------------------------------------------------------------------------------- 1 | #define CFB_NS "[CFB::Broker::Connectors::Dummy]" 2 | 3 | #include "Connectors/Dummy.hpp" 4 | 5 | #include 6 | 7 | #include "Log.hpp" 8 | #include "Utils.hpp" 9 | 10 | #define MAX_HEXDUMP_BYTES 256 11 | 12 | 13 | namespace CFB::Broker::Connectors 14 | { 15 | 16 | Dummy::Dummy() 17 | { 18 | dbg("Initializing connector '%s'", Name().c_str()); 19 | } 20 | 21 | Dummy::~Dummy() 22 | { 23 | dbg("Terminating connector '%s'", Name().c_str()); 24 | } 25 | 26 | std::string const 27 | Dummy::Name() const 28 | { 29 | return "Dummy"; 30 | } 31 | 32 | Result 33 | Dummy::IrpCallback(CFB::Comms::CapturedIrp const& Irp) 34 | { 35 | SYSTEMTIME SystemTime {}; 36 | if ( ::FileTimeToSystemTime((FILETIME*)&Irp.Header.TimeStamp, &SystemTime) ) 37 | { 38 | std::ostringstream info, details; 39 | info << "New IRP: "; 40 | info << SystemTime.wYear << "/"; 41 | info << SystemTime.wMonth << "/"; 42 | info << SystemTime.wDay << " "; 43 | info << SystemTime.wHour << ":"; 44 | info << SystemTime.wMinute << ":"; 45 | info << SystemTime.wSecond << ":"; 46 | info << SystemTime.wMilliseconds << " UTC"; 47 | info("%s", info.str().c_str()); 48 | } 49 | 50 | std::ostringstream details; 51 | details << "Details:" << std::endl; 52 | details << " - Driver: " << CFB::Utils::ToString(Irp.Header.DriverName) << std::endl; 53 | details << " - Device: " << CFB::Utils::ToString(Irp.Header.DeviceName) << std::endl; 54 | details << " - Process: " << CFB::Utils::ToString(Irp.Header.ProcessName) << " (PID:" << Irp.Header.Pid 55 | << ", TID:" << Irp.Header.Tid << ")" << std::endl; 56 | if ( Irp.Header.MajorFunction == 0xe || Irp.Header.MajorFunction == 0xf ) 57 | { 58 | details << " - IOCTL code: " << CFB::Utils::ToString(CFB::Comms::Ioctl {Irp.Header.IoctlCode}) << std::endl; 59 | } 60 | details << std::hex; 61 | details << " - Major: " << CFB::Utils::IrpMajorToString((u32)Irp.Header.MajorFunction) << std::endl; 62 | details << " - Minor: " << (u32)Irp.Header.MinorFunction << std::endl; 63 | details << " - InLen: " << Irp.Header.InputBufferLength << std::endl; 64 | details << " - OutLen: " << Irp.Header.OutputBufferLength << std::endl; 65 | details << " - Status: " << Irp.Header.Status << std::endl; 66 | dbg("%s", details.str().c_str()); 67 | if ( Irp.Header.Status ) 68 | { 69 | CFB::Log::ntperror(" - NTSTATUS", Irp.Header.Status); 70 | } 71 | 72 | if ( Irp.Header.InputBufferLength ) 73 | { 74 | CFB::Utils::Hexdump( 75 | (PVOID)Irp.InputBuffer.data(), 76 | MIN(Irp.InputBuffer.size(), MAX_HEXDUMP_BYTES), 77 | "InputBuffer"); 78 | } 79 | 80 | if ( Irp.Header.OutputBufferLength ) 81 | { 82 | CFB::Utils::Hexdump( 83 | (PVOID)Irp.OutputBuffer.data(), 84 | MIN(Irp.OutputBuffer.size(), MAX_HEXDUMP_BYTES), 85 | "Output Buffer"); 86 | } 87 | 88 | return Ok(0); 89 | } 90 | 91 | } // namespace CFB::Broker::Connectors 92 | -------------------------------------------------------------------------------- /Common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | message(STATUS "Configuring 'CommonLib'") 2 | 3 | set(COMMON_SOURCE_DIR ${CFB_ROOT_DIR}/Common/Source) 4 | set(COMMON_HEADER_DIR ${CFB_ROOT_DIR}/Common/Headers) 5 | 6 | set( 7 | COMMON_HEADER_FILES 8 | 9 | ${COMMON_HEADER_DIR}/CompileInfo.hpp 10 | 11 | ${COMMON_HEADER_DIR}/Common.hpp 12 | ${COMMON_HEADER_DIR}/Comms.hpp 13 | ${COMMON_HEADER_DIR}/IoctlCodes.hpp 14 | ${COMMON_HEADER_DIR}/Log.hpp 15 | ${COMMON_HEADER_DIR}/Messages.hpp 16 | ${COMMON_HEADER_DIR}/Utils.hpp 17 | ) 18 | 19 | set( 20 | COMMON_SOURCE_FILES 21 | 22 | ${COMMON_SOURCE_DIR}/Comms.cpp 23 | ${COMMON_SOURCE_DIR}/Log.cpp 24 | ${COMMON_SOURCE_DIR}/Utils.cpp 25 | ) 26 | 27 | # 28 | # Create the compilation information file 29 | # 30 | configure_file( 31 | ${COMMON_HEADER_DIR}/CompileInfo.hpp.in 32 | ${COMMON_HEADER_DIR}/CompileInfo.hpp 33 | NEWLINE_STYLE WIN32 34 | ) 35 | 36 | # 37 | # Build CommonLib for driver 38 | # 39 | set( 40 | COMMON_DRIVER_SOURCE_FILES 41 | 42 | ${COMMON_SOURCE_FILES} 43 | ) 44 | 45 | set( 46 | COMMON_DRIVER_HEADER_FILES 47 | 48 | ${COMMON_HEADER_FILES} 49 | ) 50 | 51 | wdk_add_library(CommonLibDriver 52 | STATIC 53 | KMDF 54 | 1.15 55 | 56 | ${COMMON_DRIVER_SOURCE_FILES} 57 | ${COMMON_DRIVER_HEADER_FILES} 58 | ) 59 | 60 | add_library(CFB::Kernel::CommonLib ALIAS CommonLibDriver) 61 | 62 | target_include_directories(CommonLibDriver PUBLIC ${COMMON_HEADER_DIR}) 63 | target_compile_definitions(CommonLibDriver PRIVATE CFB_KERNEL_DRIVER=1) 64 | 65 | # 66 | # Build CommonLib for user 67 | # 68 | set( 69 | COMMON_USER_SOURCE_FILES 70 | 71 | ${COMMON_SOURCE_FILES} 72 | ${COMMON_SOURCE_DIR}/Messages.cpp 73 | ) 74 | 75 | set( 76 | COMMON_USER_HEADER_FILES 77 | 78 | ${COMMON_HEADER_FILES} 79 | ${COMMON_HEADER_DIR}/Messages.hpp 80 | ) 81 | 82 | add_library(CommonLibUser 83 | STATIC 84 | 85 | ${COMMON_USER_SOURCE_FILES} 86 | ${COMMON_USER_HEADER_FILES} 87 | ) 88 | 89 | add_library(CFB::User::CommonLib ALIAS CommonLibUser) 90 | 91 | target_include_directories(CommonLibUser 92 | PUBLIC 93 | ${COMMON_HEADER_DIR} 94 | 95 | $ 96 | ) 97 | 98 | target_compile_options(CommonLibUser 99 | PUBLIC 100 | $<$:/Zc:__cplusplus> 101 | $<$>:$<$:/fsanitize=address>> 102 | 103 | PRIVATE 104 | $,/WX /Gm- /permissive-,/WX /permissive> 105 | ) 106 | 107 | target_compile_definitions(CommonLibUser 108 | PUBLIC 109 | $<$>:$<$:_DISABLE_VECTOR_ANNOTATION>> 110 | $<$>:$<$:_DISABLE_STRING_ANNOTATION>> 111 | 112 | PRIVATE 113 | ) 114 | 115 | target_link_options(CommonLibUser 116 | PUBLIC 117 | $<$>:$<$:/InferAsanLibs>> 118 | 119 | PRIVATE 120 | ) 121 | 122 | target_link_libraries(CommonLibUser PRIVATE kernel32.lib ntdll.lib) 123 | -------------------------------------------------------------------------------- /GUI/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project( 2 | GUI 3 | LANGUAGES CXX 4 | VERSION 0.2.0 5 | DESCRIPTION "ImGui-based GUI for CFB" 6 | HOMEPAGE_URL https://github.com/hugsy/cfb 7 | ) 8 | 9 | message(STATUS "Configuring '${PROJECT_NAME}'") 10 | 11 | set(GUI_SOURCE_DIR ${CFB_ROOT_DIR}/GUI/Source) 12 | set(GUI_HEADER_DIR ${CFB_ROOT_DIR}/GUI/Headers) 13 | 14 | set(GUI_VERSION_MAJOR ${PROJECT_VERSION_MAJOR} CACHE INTERNAL "GUI_VERSION_MAJOR") 15 | set(GUI_VERSION_MINOR ${PROJECT_VERSION_MINOR} CACHE INTERNAL "GUI_VERSION_MINOR") 16 | set(GUI_VERSION_PATCH ${PROJECT_VERSION_PATCH} CACHE INTERNAL "GUI_VERSION_PATCH") 17 | 18 | set( 19 | GUI_HEADER_FILES 20 | 21 | ${GUI_HEADER_DIR}/App.hpp 22 | ${GUI_HEADER_DIR}/GuiUtils.hpp 23 | ${GUI_HEADER_DIR}/Helpers.hpp 24 | ${GUI_HEADER_DIR}/Network.hpp 25 | ) 26 | 27 | set( 28 | GUI_SOURCE_FILES 29 | 30 | ${GUI_SOURCE_DIR}/main.cpp 31 | ${GUI_SOURCE_DIR}/App.cpp 32 | ${GUI_SOURCE_DIR}/GuiUtils.cpp 33 | ${GUI_SOURCE_DIR}/Helpers.cpp 34 | ) 35 | 36 | # 37 | # Create the broker rc file 38 | # 39 | set(GUI_RC_FILE ${GUI_SOURCE_DIR}/GUI.rc) 40 | set(GUI_RES_FILE ${GUI_SOURCE_DIR}/GUI.res) 41 | cmake_path(SET CFB_GUI_ICON_PATH "${CFB_ROOT_DIR}/Assets/Images/logo/logo.ico") 42 | configure_file(${GUI_RC_FILE}.in ${GUI_RC_FILE} NEWLINE_STYLE WIN32 ESCAPE_QUOTES) 43 | 44 | # 45 | # Build the GUI 46 | # 47 | add_executable(${PROJECT_NAME} ${GUI_SOURCE_FILES}) 48 | add_dependencies(${PROJECT_NAME} CFB::User::CommonLib) 49 | add_executable(CFB::User::Gui ALIAS ${PROJECT_NAME}) 50 | 51 | # 52 | # Compilation directives 53 | # 54 | target_include_directories( 55 | ${PROJECT_NAME} 56 | PUBLIC 57 | 58 | PRIVATE 59 | ${GUI_HEADER_DIR} 60 | $ 61 | ) 62 | 63 | target_compile_definitions(${PROJECT_NAME} PUBLIC _UNICODE UNICODE ImTextureID=ImU64) 64 | 65 | target_compile_options( 66 | ${PROJECT_NAME} 67 | PUBLIC 68 | $<$:/Zc:__cplusplus> 69 | $<$>:$<$:/fsanitize=address>> 70 | 71 | PRIVATE 72 | $,/WX /Gm- /permissive-,/WX /permissive> 73 | ) 74 | 75 | # 76 | # Linking directives 77 | # 78 | target_link_options( 79 | ${PROJECT_NAME} 80 | PUBLIC 81 | /SUBSYSTEM:WINDOWS 82 | $<$>:$<$:/InferAsanLibs>> 83 | 84 | PRIVATE 85 | ) 86 | 87 | target_link_libraries( 88 | ${PROJECT_NAME} 89 | 90 | PRIVATE 91 | ${GUI_RES_FILE} 92 | CFB::User::CommonLib 93 | Deps::ImGUI 94 | Deps::JSON 95 | Ws2_32.lib 96 | D3D12.lib 97 | DXGI.lib 98 | ) 99 | 100 | # 101 | # Compile the resource file 102 | # 103 | add_custom_command( 104 | TARGET ${PROJECT_NAME} PRE_BUILD 105 | COMMAND 106 | rc /nologo /fo ${GUI_RES_FILE} /r ${GUI_RC_FILE} 107 | COMMENT 108 | "Compiling '${GUI_RES_FILE}' resource file" 109 | ) 110 | 111 | # 112 | # Install directives 113 | # 114 | install(TARGETS ${PROJECT_NAME} DESTINATION Tools) 115 | install(FILES $ DESTINATION Tools OPTIONAL) 116 | -------------------------------------------------------------------------------- /Common/Source/Comms.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Comms.hpp" 4 | 5 | #include "Utils.hpp" 6 | 7 | #ifndef CFB_KERNEL_DRIVER 8 | // clang-format off 9 | #include 10 | #include 11 | #include 12 | 13 | // clang-format on 14 | #endif // CFB_KERNEL_DRIVER 15 | 16 | 17 | namespace CFB::Comms 18 | { 19 | #ifdef CFB_KERNEL_DRIVER 20 | 21 | #else 22 | void 23 | to_json(nlohmann::json& j, CapturedIrpHeader const& h) 24 | { 25 | j["TimeStamp"] = h.TimeStamp; 26 | j["DriverName"] = CFB::Utils::ToString(h.DriverName); 27 | j["DeviceName"] = CFB::Utils::ToString(h.DeviceName); 28 | j["Irql"] = h.Irql; 29 | j["Type"] = h.Type; 30 | j["MajorFunction"] = h.MajorFunction; 31 | j["MinorFunction"] = h.MinorFunction; 32 | j["IoctlCode"] = h.IoctlCode; 33 | j["Pid"] = h.Pid; 34 | j["Tid"] = h.Tid; 35 | j["Status"] = h.Status; 36 | j["ProcessName"] = CFB::Utils::ToString(h.ProcessName); 37 | j["InputBufferLength"] = h.InputBufferLength; 38 | j["OutputBufferLength"] = h.OutputBufferLength; 39 | } 40 | 41 | void 42 | from_json(const nlohmann::json& j, CapturedIrpHeader& h) 43 | { 44 | j.at("TimeStamp").get_to(h.TimeStamp); 45 | j.at("Irql").get_to(h.Irql); 46 | j.at("Type").get_to(h.Type); 47 | j.at("MajorFunction").get_to(h.MajorFunction); 48 | j.at("MinorFunction").get_to(h.MinorFunction); 49 | j.at("IoctlCode").get_to(h.IoctlCode); 50 | j.at("Pid").get_to(h.Pid); 51 | j.at("Tid").get_to(h.Tid); 52 | j.at("Status").get_to(h.Status); 53 | j.at("InputBufferLength").get_to(h.InputBufferLength); 54 | j.at("OutputBufferLength").get_to(h.OutputBufferLength); 55 | 56 | auto const& ProcessName = CFB::Utils::ToWideString(j.at("ProcessName").get()); 57 | auto const& DriverName = CFB::Utils::ToWideString(j.at("DriverName").get()); 58 | auto const& DeviceName = CFB::Utils::ToWideString(j.at("DeviceName").get()); 59 | ::memcpy( 60 | h.ProcessName, 61 | ProcessName.c_str(), 62 | MIN(ProcessName.size() * sizeof(wchar_t), sizeof(h.ProcessName) - sizeof(wchar_t))); 63 | ::memcpy( 64 | h.DeviceName, 65 | DeviceName.c_str(), 66 | MIN(DeviceName.size() * sizeof(wchar_t), sizeof(h.DeviceName) - sizeof(wchar_t))); 67 | ::memcpy( 68 | h.DriverName, 69 | DriverName.c_str(), 70 | MIN(DriverName.size() * sizeof(wchar_t), sizeof(h.DriverName) - sizeof(wchar_t))); 71 | } 72 | 73 | void 74 | to_json(nlohmann::json& j, CapturedIrp const& i) 75 | { 76 | j["Header"] = i.Header; 77 | j["InputBuffer"] = i.InputBuffer; 78 | j["OutputBuffer"] = i.OutputBuffer; 79 | } 80 | 81 | void 82 | from_json(const nlohmann::json& j, CapturedIrp& i) 83 | { 84 | j.at("Header").get_to(i.Header); 85 | j.at("InputBuffer").get_to(i.InputBuffer); 86 | j.at("OutputBuffer").get_to(i.OutputBuffer); 87 | 88 | assert(i.InputBuffer.size() == i.Header.InputBufferLength); 89 | assert(i.OutputBuffer.size() == i.Header.OutputBufferLength); 90 | } 91 | #endif // CFB_KERNEL_DRIVER 92 | 93 | } // namespace CFB::Comms 94 | -------------------------------------------------------------------------------- /Driver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(IrpMonitor 2 | LANGUAGES CXX 3 | VERSION 0.2.0 4 | DESCRIPTION "Driver part of CFB" 5 | ) 6 | 7 | message(STATUS "Configuring '${PROJECT_NAME}'") 8 | 9 | set(DRIVER_SOURCE_DIR ${CFB_ROOT_DIR}/Driver/Source) 10 | set(DRIVER_HEADER_DIR ${CFB_ROOT_DIR}/Driver/Headers) 11 | 12 | set(DRIVER_VERSION_MAJOR ${PROJECT_VERSION_MAJOR} CACHE INTERNAL "DRIVER_VERSION_MAJOR") 13 | set(DRIVER_VERSION_MINOR ${PROJECT_VERSION_MINOR} CACHE INTERNAL "DRIVER_VERSION_MINOR") 14 | set(DRIVER_VERSION_PATCH ${PROJECT_VERSION_PATCH} CACHE INTERNAL "DRIVER_VERSION_PATCH") 15 | 16 | set(DRIVER_RC_FILE ${DRIVER_SOURCE_DIR}/Driver.rc) 17 | set(DRIVER_RES_FILE ${DRIVER_SOURCE_DIR}/Driver.res) 18 | set(CFB_CERT_CN "${CFB_COMPANY_NAME}") 19 | set(CFB_CERT_STORE "PrivateCertStore") 20 | set(CFB_DRIVER_INF_FILE "${DRIVER_SOURCE_DIR}/${PROJECT_NAME}.inf") 21 | 22 | configure_file(${DRIVER_RC_FILE}.in ${DRIVER_RC_FILE} NEWLINE_STYLE WIN32 ESCAPE_QUOTES) 23 | 24 | set( 25 | DRIVER_HEADER_FILES 26 | 27 | ${DRIVER_HEADER_DIR}/Context.hpp 28 | ${DRIVER_HEADER_DIR}/Native.hpp 29 | ${DRIVER_HEADER_DIR}/DriverUtils.hpp 30 | ${DRIVER_HEADER_DIR}/HookedDriverManager.hpp 31 | ${DRIVER_HEADER_DIR}/HookedDriver.hpp 32 | ${DRIVER_HEADER_DIR}/Callbacks.hpp 33 | ${DRIVER_HEADER_DIR}/CapturedIrpManager.hpp 34 | ${DRIVER_HEADER_DIR}/CapturedIrp.hpp 35 | ) 36 | 37 | set( 38 | DRIVER_SOURCE_FILES 39 | 40 | ${DRIVER_SOURCE_DIR}/Entry.cpp 41 | ${DRIVER_SOURCE_DIR}/DriverUtils.cpp 42 | ${DRIVER_SOURCE_DIR}/HookedDriverManager.cpp 43 | ${DRIVER_SOURCE_DIR}/HookedDriver.cpp 44 | ${DRIVER_SOURCE_DIR}/Callbacks.cpp 45 | ${DRIVER_SOURCE_DIR}/CapturedIrpManager.cpp 46 | ${DRIVER_SOURCE_DIR}/CapturedIrp.cpp 47 | ) 48 | 49 | wdk_add_driver( 50 | ${PROJECT_NAME} 51 | KMDF 52 | 1.15 53 | 54 | ${DRIVER_HEADER_FILES} 55 | ${DRIVER_SOURCE_FILES} 56 | ) 57 | 58 | add_executable(CFB::Kernel::Driver ALIAS ${PROJECT_NAME}) 59 | 60 | target_include_directories(${PROJECT_NAME} PRIVATE ${DRIVER_HEADER_DIR}) 61 | target_compile_definitions(${PROJECT_NAME} PRIVATE CFB_KERNEL_DRIVER=1) 62 | target_link_options(${PROJECT_NAME} PUBLIC /integritycheck) 63 | target_link_libraries(${PROJECT_NAME} CFB::Kernel::CommonLib ${DRIVER_RES_FILE}) 64 | 65 | # 66 | # Custom command directives 67 | # 68 | add_custom_command( 69 | TARGET ${PROJECT_NAME} PRE_BUILD 70 | COMMAND 71 | rc /nologo /fo ${DRIVER_RES_FILE} /r ${DRIVER_RC_FILE} 72 | COMMENT 73 | "Compiling '${DRIVER_RES_FILE}' resource file" 74 | ) 75 | 76 | add_custom_command( 77 | TARGET ${PROJECT_NAME} POST_BUILD 78 | COMMAND 79 | makecert.exe -r -pe -ss ${CFB_CERT_STORE} -n CN="${CFB_CERT_CN}" -eku 1.3.6.1.5.5.7.3.3 "$/BlahCatTest.cer" 80 | COMMAND 81 | signtool.exe sign /v /a /fd SHA256 /s ${CFB_CERT_STORE} /n "${CFB_CERT_CN}" $ 82 | COMMAND 83 | certmgr.exe -del -c -n "${CFB_CERT_CN}" -s -r currentUser ${CFB_CERT_STORE} 84 | COMMAND 85 | "${CMAKE_COMMAND}" -E copy_if_different $ "$ENV{TEMP}" 86 | COMMENT 87 | "Sign driver, copy to tempdir" 88 | ) 89 | 90 | # 91 | # Install directives 92 | # 93 | install(TARGETS ${PROJECT_NAME} DESTINATION Driver) 94 | install(FILES $ DESTINATION Driver OPTIONAL) 95 | install(FILES ${CFB_DRIVER_INF_FILE} DESTINATION Driver OPTIONAL) 96 | install(FILES "$/BlahCatTest.cer" DESTINATION Driver OPTIONAL) 97 | -------------------------------------------------------------------------------- /Broker/Source/ManagerBase.cpp: -------------------------------------------------------------------------------- 1 | #define CFB_NS "[CFB::Broker::ManagerBase]" 2 | 3 | #include "ManagerBase.hpp" 4 | 5 | #include 6 | 7 | #include "Context.hpp" 8 | #include "Log.hpp" 9 | 10 | namespace CFB::Broker 11 | { 12 | 13 | ManagerBase::ManagerBase() : m_bIsShuttingDown(false) 14 | { 15 | { 16 | wil::unique_handle hEvent(::CreateEventW(nullptr, true, false, nullptr)); 17 | if ( !hEvent ) 18 | { 19 | CFB::Log::perror("CreateEventW(StateChanged)"); 20 | throw std::runtime_error("ManagerBase::CreateEvent() failed, cannot continue"); 21 | } 22 | 23 | m_hChangedStateEvent = std::move(hEvent); 24 | } 25 | 26 | { 27 | wil::unique_handle hEvent(::CreateEventW(nullptr, true, false, nullptr)); 28 | if ( !hEvent ) 29 | { 30 | CFB::Log::perror("CreateEventW(TerminationEvent)"); 31 | throw std::runtime_error("ManagerBase::CreateEvent() failed, cannot continue"); 32 | } 33 | 34 | m_hTerminationEvent = std::move(hEvent); 35 | } 36 | } 37 | 38 | 39 | ManagerBase::~ManagerBase() 40 | { 41 | } 42 | 43 | 44 | bool 45 | ManagerBase::WaitForState(CFB::Broker::State WantedState) 46 | { 47 | const HANDLE handles[] = {m_hChangedStateEvent.get(), m_hTerminationEvent.get()}; 48 | 49 | while ( true ) 50 | { 51 | const CFB::Broker::State CurrentState = Globals.State(); 52 | 53 | // 54 | // if the wanted state current or already, leave 55 | // 56 | if ( CurrentState >= WantedState ) 57 | { 58 | break; 59 | } 60 | 61 | info("Waiting for state '%s' (current '%s')", Utils::ToString(WantedState), Utils::ToString(CurrentState)); 62 | 63 | // 64 | // Otherwise wait to be signaled 65 | // 66 | DWORD dwIndex = ::WaitForMultipleObjects(countof(handles), handles, false, 10'000); 67 | 68 | if ( dwIndex == WAIT_TIMEOUT ) 69 | { 70 | continue; 71 | } 72 | 73 | if ( handles[dwIndex] == m_hTerminationEvent.get() ) 74 | { 75 | break; 76 | } 77 | 78 | if ( handles[dwIndex] == m_hChangedStateEvent.get() ) 79 | { 80 | m_bIsShuttingDown = true; 81 | break; 82 | } 83 | 84 | CFB::Log::perror("ManagerBase::WaitForState::WaitForSingleObject"); 85 | return false; 86 | } 87 | 88 | return true; 89 | } 90 | 91 | 92 | bool 93 | ManagerBase::SetState(CFB::Broker::State NewState) 94 | { 95 | info("Notifying state change '%s' -> '%s'", Utils::ToString(Globals.State()), Utils::ToString(NewState)); 96 | 97 | auto bRes = Globals.SetState(NewState); 98 | return bRes; 99 | } 100 | 101 | 102 | bool 103 | ManagerBase::NotifyStateChange() 104 | { 105 | auto bRes = ::SetEvent(m_hChangedStateEvent.get()); 106 | if ( false == bRes ) 107 | { 108 | CFB::Log::perror("ManagerBase::NotifyStateChange::SetEvent"); 109 | } 110 | return bRes; 111 | } 112 | 113 | bool 114 | ManagerBase::NotifyTermination() 115 | { 116 | auto bRes = ::SetEvent(m_hTerminationEvent.get()); 117 | if ( false == bRes ) 118 | { 119 | CFB::Log::perror("ManagerBase::NotifyTermination::SetEvent"); 120 | } 121 | return bRes; 122 | } 123 | 124 | 125 | } // namespace CFB::Broker 126 | -------------------------------------------------------------------------------- /Driver/Headers/Callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include "Common.hpp" 5 | 6 | namespace CFB::Driver::Callbacks 7 | { 8 | /// 9 | /// @brief This is the main interception routine: it will find the HookedDriver associated to a DeviceObject. If 10 | /// any is found, and capture mode is enabled the IRP data will be pushed to the queue of captured data. 11 | /// 12 | /// @param DeviceObject 13 | /// @param Irp 14 | /// @return NTSTATUS 15 | /// 16 | NTSTATUS 17 | InterceptGenericRoutine(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp); 18 | 19 | /// 20 | /// @brief 21 | /// 22 | /// @param DeviceObject 23 | /// @param Irp 24 | /// @return NTSTATUS 25 | /// 26 | NTSTATUS 27 | InterceptedDeviceControlRoutine(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp); 28 | 29 | /// 30 | /// @brief The ReadFile() interception routine wrapper. 31 | /// 32 | /// @param DeviceObject 33 | /// @param Irp 34 | /// @return NTSTATUS STATUS_SUCCESS on success. 35 | /// 36 | NTSTATUS 37 | InterceptedReadRoutine(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp); 38 | 39 | /// 40 | /// @brief The WriteFile() interception routine wrapper. 41 | /// 42 | /// @param DeviceObject 43 | /// @param Irp 44 | /// @return NTSTATUS 45 | /// 46 | NTSTATUS 47 | InterceptedWriteRoutine(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp); 48 | 49 | /// 50 | ///@brief 51 | /// 52 | ///@param DeviceObject 53 | ///@param Type 54 | ///@param Buffer 55 | ///@param BufferLength 56 | ///@param IoControlCode 57 | ///@param Flags 58 | ///@param pIrpOut 59 | ///@return BOOLEAN 60 | /// 61 | BOOLEAN 62 | InterceptGenericFastIoRoutine( 63 | _In_ PDEVICE_OBJECT DeviceObject, 64 | _In_ UINT32 Type, 65 | _In_ PVOID Buffer, 66 | _In_ ULONG BufferLength, 67 | _In_ ULONG IoControlCode, 68 | _In_ UINT32 Flags, 69 | _Inout_ PVOID* pIrpOut); 70 | 71 | /// 72 | /// @brief The `InterceptGenericFastIoDeviceControl()` interception routine wrapper. 73 | /// 74 | /// @param FileObject 75 | /// @param Wait 76 | /// @param InputBuffer 77 | /// @param InputBufferLength 78 | /// @param OutputBuffer 79 | /// @param OutputBufferLength 80 | /// @param IoControlCode 81 | /// @param IoStatus 82 | /// @param DeviceObject 83 | /// @return BOOLEAN 84 | /// 85 | BOOLEAN 86 | InterceptGenericFastIoDeviceControl( 87 | _In_ PFILE_OBJECT FileObject, 88 | _In_ BOOLEAN Wait, 89 | _In_opt_ PVOID InputBuffer, 90 | _In_ ULONG InputBufferLength, 91 | _Out_opt_ PVOID OutputBuffer, 92 | _In_ ULONG OutputBufferLength, 93 | _In_ ULONG IoControlCode, 94 | _Out_ PIO_STATUS_BLOCK IoStatus, 95 | _In_ PDEVICE_OBJECT DeviceObject); 96 | 97 | /// 98 | /// @brief The `InterceptGenericFastIoRead()` interception routine wrapper. 99 | /// 100 | /// @param FileObject 101 | /// @param FileOffset 102 | /// @param BufferLength 103 | /// @param Wait 104 | /// @param LockKey 105 | /// @param Buffer 106 | /// @param IoStatus 107 | /// @param DeviceObject 108 | /// 109 | /// @return BOOLEAN 110 | /// 111 | BOOLEAN 112 | InterceptGenericFastIoRead( 113 | _In_ PFILE_OBJECT FileObject, 114 | _In_ PLARGE_INTEGER FileOffset, 115 | _In_ ULONG Length, 116 | _In_ BOOLEAN Wait, 117 | _In_ ULONG LockKey, 118 | _Out_ PVOID Buffer, 119 | _Out_ PIO_STATUS_BLOCK IoStatus, 120 | _In_ PDEVICE_OBJECT DeviceObject); 121 | 122 | /// 123 | /// @brief The InterceptGenericFastIoWrite() interception routine wrapper. 124 | /// 125 | /// @param FileObject 126 | /// @param FileOffset 127 | /// @param Length 128 | /// @param Wait 129 | /// @param LockKey 130 | /// @param Buffer 131 | /// @param IoStatus 132 | /// @param DeviceObject 133 | /// 134 | /// @return BOOLEAN 135 | /// 136 | BOOLEAN 137 | InterceptGenericFastIoWrite( 138 | _In_ PFILE_OBJECT FileObject, 139 | _In_ PLARGE_INTEGER FileOffset, 140 | _In_ ULONG Length, 141 | _In_ BOOLEAN Wait, 142 | _In_ ULONG LockKey, 143 | _Out_ PVOID Buffer, 144 | _Out_ PIO_STATUS_BLOCK IoStatus, 145 | _In_ PDEVICE_OBJECT DeviceObject); 146 | } // namespace CFB::Driver::Callbacks 147 | -------------------------------------------------------------------------------- /Common/Tests/Comms.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #define NS "CFB::Comms" 3 | 4 | // clang-format off 5 | #include 6 | #include 7 | 8 | #include "Comms.hpp" 9 | // clang-format on 10 | 11 | using json = nlohmann::json; 12 | using namespace nlohmann::literals; 13 | 14 | TEST_CASE("Common/Communication", NS) 15 | { 16 | SECTION("IRP from JS") 17 | { 18 | json j1 = R"( 19 | [ 20 | { 21 | "Header": 22 | { 23 | "DeviceName":"\\Device\\HackSysExtremeVulnerableDriver", 24 | "DriverName":"\\driver\\HEVD", 25 | "InputBufferLength":0, 26 | "IoctlCode":0, 27 | "Irql":0, 28 | "MajorFunction":0, 29 | "MinorFunction":0, 30 | "OutputBufferLength":0, 31 | "Pid":6596, 32 | "ProcessName": "DriverClient.e", 33 | "Status":0, 34 | "Tid":8056, 35 | "TimeStamp":133194954083127683, 36 | "Type":0 37 | }, 38 | "InputBuffer":[], 39 | "OutputBuffer":[] 40 | }, 41 | { 42 | "Header": 43 | { 44 | "DeviceName":"\\Device\\HackSysExtremeVulnerableDriver", 45 | "DriverName":"\\driver\\HEVD", 46 | "InputBufferLength":32, 47 | "IoctlCode":2236419, 48 | "Irql":0, 49 | "MajorFunction":14, 50 | "MinorFunction":0, 51 | "OutputBufferLength":0, 52 | "Pid":6596, 53 | "ProcessName": "DriverClient.e", 54 | "Status":0, 55 | "Tid":8056, 56 | "TimeStamp":133194954083127682, 57 | "Type":0 58 | }, 59 | "InputBuffer":[65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65], 60 | "OutputBuffer":[] 61 | } 62 | ] 63 | )"_json; 64 | 65 | for ( auto const& x : j1 ) 66 | { 67 | auto const& i = x.get(); 68 | CHECK(i.InputBuffer.size() == i.Header.InputBufferLength); 69 | CHECK(i.OutputBuffer.size() == i.Header.OutputBufferLength); 70 | } 71 | 72 | // 73 | // Too long device, driver or process name -> trimmed 74 | // 75 | json j2 = R"( 76 | { 77 | "Header": 78 | { 79 | "DeviceName":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 80 | "DriverName":"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", 81 | "InputBufferLength":0, 82 | "IoctlCode":0, 83 | "Irql":0, 84 | "MajorFunction":0, 85 | "MinorFunction":0, 86 | "OutputBufferLength":0, 87 | "Pid":6596, 88 | "ProcessName": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", 89 | "Status":0, 90 | "Tid":8056, 91 | "TimeStamp":133194954083127683, 92 | "Type":0 93 | }, 94 | "InputBuffer":[], 95 | "OutputBuffer":[] 96 | } 97 | )"_json; 98 | 99 | auto const& i = j2.get(); 100 | CHECK(::wcslen(i.Header.DeviceName) == (CFB_DRIVER_MAX_PATH - 1)); 101 | CHECK(::wcslen(i.Header.DriverName) == (CFB_DRIVER_MAX_PATH - 1)); 102 | CHECK(::wcslen(i.Header.ProcessName) == (CFB_DRIVER_MAX_PATH - 1)); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Format Style Options - Created with Clang Power Tools 2 | --- 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignConsecutiveAssignments: true 6 | AlignConsecutiveBitFields: true 7 | AlignConsecutiveMacros: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllArgumentsOnNextLine: false 12 | AllowAllConstructorInitializersOnNextLine: false 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: false 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortLambdasOnASingleLine: Empty 17 | AllowShortEnumsOnASingleLine: false 18 | AllowShortFunctionsOnASingleLine: None 19 | AllowShortIfStatementsOnASingleLine: Never 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterReturnType: All 22 | AlwaysBreakBeforeMultilineStrings: true 23 | AlwaysBreakTemplateDeclarations: Yes 24 | BasedOnStyle: Google 25 | BinPackArguments: false 26 | BinPackParameters: false 27 | BitFieldColonSpacing: Both 28 | BraceWrapping: 29 | AfterCaseLabel: true 30 | AfterClass: true 31 | AfterControlStatement: true 32 | AfterEnum: true 33 | AfterFunction: true 34 | AfterNamespace: true 35 | AfterObjCDeclaration: true 36 | AfterStruct: true 37 | AfterUnion: true 38 | AfterExternBlock: true 39 | BeforeCatch: true 40 | BeforeElse: true 41 | IndentBraces: true 42 | SplitEmptyFunction: true 43 | SplitEmptyRecord: true 44 | SplitEmptyNamespace: true 45 | BeforeLambdaBody: false 46 | BeforeWhile: false 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Allman 49 | BreakInheritanceList: AfterColon 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializers: AfterColon 52 | ColumnLimit: 120 53 | CommentPragmas: '^ IWYU pragma:' 54 | CompactNamespaces: false 55 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 56 | ConstructorInitializerIndentWidth : 4 57 | ContinuationIndentWidth: 4 58 | Cpp11BracedListStyle: true 59 | DerivePointerAlignment: false 60 | ExperimentalAutoDetectBinPacking: false 61 | FixNamespaceComments: true 62 | ForEachMacros: 63 | [foreach, Q_FOREACH, BOOST_FOREACH] 64 | IndentCaseBlocks: false 65 | IndentCaseLabels: false 66 | IndentExternBlock: NoIndent 67 | IndentGotoLabels: false 68 | IndentPPDirectives: None 69 | IndentWidth: 4 70 | IndentWrappedFunctionNames: false 71 | KeepEmptyLinesAtTheStartOfBlocks: true 72 | Language: Cpp 73 | MaxEmptyLinesToKeep: 2 74 | NamespaceIndentation: None 75 | PenaltyBreakBeforeFirstCallParameter: 1 76 | PenaltyBreakComment: 300 77 | PenaltyBreakFirstLessLess: 120 78 | PenaltyBreakString: 1000 79 | PenaltyExcessCharacter: 1000000 80 | PenaltyReturnTypeOnItsOwnLine: 200 81 | PointerAlignment: Left 82 | ReflowComments: true 83 | SortUsingDeclarations: true 84 | SpaceAfterCStyleCast: false 85 | SpaceAfterLogicalNot: false 86 | SpaceAfterTemplateKeyword: false 87 | SpaceAroundPointerQualifiers: Before 88 | SpaceBeforeAssignmentOperators: true 89 | SpaceBeforeCpp11BracedList: true 90 | SpaceBeforeParens: ControlStatements 91 | SpaceBeforeRangeBasedForLoopColon: true 92 | SpaceBeforeSquareBrackets: false 93 | SpaceInEmptyBlock: false 94 | SpaceInEmptyParentheses: false 95 | SpacesBeforeTrailingComments: 1 96 | SpacesInAngles: false 97 | SpacesInContainerLiterals: false 98 | SpacesInCStyleCastParentheses: false 99 | SpacesInConditionalStatement: true 100 | SpacesInParentheses: false 101 | SpacesInSquareBrackets: false 102 | Standard: Auto 103 | StatementMacros: 104 | ['EXTERN_C', 'PAGED', 'PAGEDX', 'NONPAGED', 'PNPCODE', 'INITCODE', '_At_', '_When_', '_Success_', '_Check_return_', '_Must_inspect_result_', '_IRQL_requires_', '_IRQL_requires_max_', '_IRQL_requires_min_', '_IRQL_saves_', '_IRQL_restores_', '_IRQL_saves_global_', '_IRQL_restores_global_', '_IRQL_raises_', '_IRQL_lowers_', '_Acquires_lock_', '_Releases_lock_', '_Acquires_exclusive_lock_', '_Releases_exclusive_lock_', '_Acquires_shared_lock_', '_Releases_shared_lock_', '_Requires_lock_held_', '_Use_decl_annotations_', '_Guarded_by_', '__drv_preferredFunction', '__drv_allocatesMem', '__drv_freesMem'] 105 | TabWidth: 4 106 | UseCRLF: true 107 | UseTab: Never 108 | ... 109 | -------------------------------------------------------------------------------- /Broker/Source/ConnectorManager.cpp: -------------------------------------------------------------------------------- 1 | #define CFB_NS "[CFB::ConnectorManager::CallbackDispatcher]" 2 | 3 | // clang-format off 4 | #include "ConnectorManager.hpp" 5 | 6 | #include "Context.hpp" 7 | #include "Log.hpp" 8 | 9 | #include "Connectors/Dummy.hpp" // for test 10 | #include "Connectors/JsonQueue.hpp" 11 | // clang-format on 12 | 13 | namespace CFB::Broker 14 | { 15 | 16 | /// 17 | ///@brief 18 | /// 19 | static std::vector> g_Connectors {}; 20 | 21 | /// 22 | ///@brief 23 | /// 24 | ///@param Irp 25 | ///@return true 26 | ///@return false 27 | /// 28 | bool 29 | CallbackDispatcher(CFB::Comms::CapturedIrp const& Irp) 30 | { 31 | const usize nbTotal = g_Connectors.size(); 32 | dbg("[ConnectorManager::CallbackDispatcher] Dispatching IRP received at TS=%llu to %u connector%s", 33 | Irp.Header.TimeStamp, 34 | nbTotal, 35 | PLURAL_IF(nbTotal > 1), 36 | &g_Connectors); 37 | 38 | usize nbSuccess = 0; 39 | 40 | for ( auto& Connector : g_Connectors ) 41 | { 42 | if ( Connector->IsEnabled() == false ) 43 | { 44 | continue; 45 | } 46 | 47 | dbg("[ConnectorManager::CallbackDispatcher] Sending IRP to Connector:'%s'", Connector->Name().c_str()); 48 | if ( Success(Connector->IrpCallback(Irp)) ) 49 | { 50 | nbSuccess++; 51 | } 52 | } 53 | 54 | dbg("[ConnectorManager::CallbackDispatcher] %u/%u executed successfully", nbSuccess, nbTotal); 55 | return true; 56 | } 57 | 58 | 59 | std::string const 60 | ConnectorManager::Name() 61 | { 62 | return "ConnectorManager"; 63 | } 64 | 65 | 66 | Result 67 | ConnectorManager::Setup() 68 | { 69 | // 70 | // Wait for the service to be ready 71 | // 72 | WaitForState(CFB::Broker::State::IrpManagerReady); 73 | 74 | // 75 | // Register the callback dispatcher 76 | // 77 | dbg("Register the connector dispatcher to the IRP manager"); 78 | if ( Globals.IrpManager()->SetCallback(&CallbackDispatcher) ) 79 | { 80 | info("Connector dispatcher successfully registered"); 81 | 82 | // 83 | // Add new connector to that list 84 | // 85 | { 86 | auto conn = std::make_shared(); 87 | conn->Enable(); 88 | g_Connectors.push_back(conn); 89 | } 90 | 91 | { 92 | auto conn = std::make_shared(); 93 | conn->Enable(); 94 | g_Connectors.push_back(conn); 95 | } 96 | 97 | dbg("%u connector%s registered to %p", g_Connectors.size(), PLURAL_IF(g_Connectors.size() > 1), &g_Connectors); 98 | } 99 | else 100 | { 101 | info("Failed to register the connector dispatcher"); 102 | return Err(ErrorCode::InitializationError); 103 | } 104 | 105 | // 106 | // Notify other threads that the Collector Manager is ready 107 | // This will also effectively start the collection by the IrpManager from the driver 108 | // 109 | SetState(CFB::Broker::State::ConnectorManagerReady); 110 | 111 | return Ok(true); 112 | } 113 | 114 | 115 | Result> 116 | ConnectorManager::GetConnectorByName(std::string_view const& ConnectorName) 117 | { 118 | auto res = std::find_if( 119 | g_Connectors.cbegin(), 120 | g_Connectors.cend(), 121 | [&ConnectorName](auto const& Conn) 122 | { 123 | return Conn->Name() == ConnectorName; 124 | }); 125 | 126 | if ( res == std::end(g_Connectors) ) 127 | { 128 | return Err(ErrorCode::NotFound); 129 | } 130 | 131 | return Ok(*res); 132 | } 133 | 134 | 135 | void 136 | ConnectorManager::Run() 137 | { 138 | WaitForState(CFB::Broker::State::Running); 139 | 140 | // 141 | // Wait for termination event 142 | // 143 | ::WaitForSingleObject(m_hTerminationEvent.get(), INFINITE); 144 | dbg("TerminationEvent received"); 145 | 146 | // 147 | // Propagate the notification to the other managers 148 | // 149 | SetState(CFB::Broker::State::ConnectorManagerDone); 150 | } 151 | 152 | 153 | } // namespace CFB::Broker 154 | -------------------------------------------------------------------------------- /Broker/Headers/DriverManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #include 5 | 6 | #include "Common.hpp" 7 | #include "Broker.hpp" 8 | #include "Error.hpp" 9 | #include "ManagerBase.hpp" 10 | #include "Messages.hpp" 11 | 12 | #include 13 | 14 | #include 15 | using json = nlohmann::json; 16 | // clang-format on 17 | 18 | namespace CFB::Broker 19 | { 20 | 21 | class DriverManager : public ManagerBase 22 | { 23 | public: 24 | class TcpClient 25 | { 26 | public: 27 | TcpClient() : m_Id {0}, m_Socket {0}, m_IpAddress {}, m_Port {0}, m_ThreadId {0}, m_hThread {nullptr} 28 | { 29 | } 30 | 31 | ~TcpClient(); 32 | 33 | /// 34 | /// @brief Synchronous send 35 | /// 36 | /// @return Result 37 | /// 38 | Result 39 | SendSynchronous(json const&); 40 | 41 | /// 42 | /// @brief 43 | /// 44 | /// @return Result> 45 | /// 46 | Result 47 | ReceiveSynchronous(); 48 | 49 | std::string 50 | Name(); 51 | 52 | usize m_Id = 0; 53 | SOCKET m_Socket = 0; 54 | std::string m_IpAddress; 55 | u16 m_Port = 0; 56 | u32 m_ThreadId = 0; 57 | wil::unique_handle m_hThread = nullptr; 58 | }; 59 | 60 | 61 | class TcpListener 62 | { 63 | public: 64 | /// 65 | /// @brief Construct a new Tcp Listener object 66 | /// 67 | TcpListener(); 68 | 69 | /// 70 | /// @brief Destroy the Tcp Listener object 71 | /// 72 | ~TcpListener(); 73 | 74 | /// 75 | /// @brief Create the listening socket 76 | /// 77 | /// @return Result 78 | /// 79 | Result 80 | Initialize(); 81 | 82 | /// 83 | /// @brief 84 | /// 85 | /// @return Result 86 | /// 87 | Result 88 | Listen(); 89 | 90 | /// 91 | /// @brief Accept a client socket 92 | /// 93 | /// @return SOCKET 94 | /// 95 | Result> 96 | Accept(); 97 | 98 | /// 99 | /// @brief 100 | /// 101 | /// @return true 102 | /// @return false 103 | /// 104 | bool 105 | Reconnect(); 106 | 107 | /// 108 | /// @brief 109 | /// 110 | /// @return true 111 | /// @return false 112 | /// 113 | Result 114 | Terminate(); 115 | 116 | /// 117 | /// @brief 118 | /// 119 | /// @return Result 120 | /// 121 | Result 122 | RunForever(); 123 | 124 | std::string 125 | Name(); 126 | 127 | 128 | private: 129 | SOCKET m_ServerSocket; 130 | 131 | std::vector> m_Clients; 132 | }; 133 | 134 | 135 | /// 136 | /// @brief Construct a new Driver Manager object 137 | /// 138 | DriverManager(); 139 | 140 | /// 141 | /// @brief Destroy the Driver Manager object 142 | /// 143 | ~DriverManager(); 144 | 145 | /// 146 | /// @brief 147 | /// 148 | /// @return std::string const 149 | /// 150 | std::string const 151 | Name(); 152 | 153 | /// 154 | /// @brief 155 | /// 156 | /// @return Result 157 | /// 158 | Result 159 | Setup(); 160 | 161 | /// 162 | /// @brief 163 | /// 164 | void 165 | Run(); 166 | 167 | /// 168 | /// @brief Execute command directly on the broker. This can be used to have the broker build commands directly 169 | /// to the driver. 170 | /// 171 | /// @return Result 172 | /// 173 | Result 174 | ExecuteCommand(json const& Request); 175 | 176 | private: 177 | /// 178 | /// @brief Handle to the device 179 | /// 180 | wil::unique_handle m_hDevice; 181 | 182 | /// 183 | /// @brief For now only use TCP 184 | /// 185 | TcpListener m_Listener; 186 | 187 | /// 188 | /// @brief 189 | /// 190 | /// 191 | std::mutex m_ManagerLock; 192 | 193 | usize m_RequestNumber; 194 | }; 195 | 196 | } // namespace CFB::Broker 197 | -------------------------------------------------------------------------------- /Broker/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Broker 2 | LANGUAGES CXX 3 | VERSION 0.2.0 4 | ) 5 | 6 | message(STATUS "Configuring '${PROJECT_NAME}'") 7 | 8 | set(BROKER_SOURCE_DIR ${CFB_ROOT_DIR}/Broker/Source) 9 | set(BROKER_HEADER_DIR ${CFB_ROOT_DIR}/Broker/Headers) 10 | 11 | set(BROKER_VERSION_MAJOR ${PROJECT_VERSION_MAJOR} CACHE INTERNAL "BROKER_VERSION_MAJOR") 12 | set(BROKER_VERSION_MINOR ${PROJECT_VERSION_MINOR} CACHE INTERNAL "BROKER_VERSION_MINOR") 13 | set(BROKER_VERSION_PATCH ${PROJECT_VERSION_PATCH} CACHE INTERNAL "BROKER_VERSION_PATCH") 14 | 15 | set(BROKER_RC_FILE ${BROKER_SOURCE_DIR}/Broker.rc) 16 | set(BROKER_RES_FILE ${BROKER_SOURCE_DIR}/Broker.res) 17 | 18 | # 19 | # Source files 20 | # 21 | set( 22 | HEADER_FILES 23 | 24 | ${BROKER_HEADER_DIR}/Resource.h 25 | ${BROKER_HEADER_DIR}/Error.hpp 26 | ${BROKER_HEADER_DIR}/Broker.hpp 27 | ${BROKER_HEADER_DIR}/Native.hpp 28 | ${BROKER_HEADER_DIR}/Context.hpp 29 | ${BROKER_HEADER_DIR}/States.hpp 30 | ${BROKER_HEADER_DIR}/BrokerUtils.hpp 31 | ${BROKER_HEADER_DIR}/ManagerBase.hpp 32 | ${BROKER_HEADER_DIR}/ServiceManager.hpp 33 | ${BROKER_HEADER_DIR}/IrpManager.hpp 34 | ${BROKER_HEADER_DIR}/ConnectorManager.hpp 35 | ${BROKER_HEADER_DIR}/DriverManager.hpp 36 | 37 | ${BROKER_HEADER_DIR}/Connectors/Base.hpp 38 | ${BROKER_HEADER_DIR}/Connectors/Dummy.hpp 39 | ${BROKER_HEADER_DIR}/Connectors/JsonQueue.hpp 40 | ) 41 | 42 | set( 43 | SOURCE_FILES 44 | 45 | ${BROKER_SOURCE_DIR}/Main.cpp 46 | ${BROKER_SOURCE_DIR}/Context.cpp 47 | ${BROKER_SOURCE_DIR}/BrokerUtils.cpp 48 | ${BROKER_SOURCE_DIR}/ManagerBase.cpp 49 | ${BROKER_SOURCE_DIR}/ServiceManager.cpp 50 | ${BROKER_SOURCE_DIR}/IrpManager.cpp 51 | ${BROKER_SOURCE_DIR}/ConnectorManager.cpp 52 | ${BROKER_SOURCE_DIR}/DriverManager.cpp 53 | ${BROKER_SOURCE_DIR}/DriverManagerListener.cpp 54 | 55 | ${BROKER_SOURCE_DIR}/Connectors/Dummy.cpp 56 | ${BROKER_SOURCE_DIR}/Connectors/JsonQueue.cpp 57 | ) 58 | 59 | # 60 | # Create the broker rc file 61 | # 62 | cmake_path(SET CFB_BROKER_DRIVER_PATH "$ENV{TEMP}/IrpMonitor.sys") 63 | cmake_path(SET CFB_BROKER_ICON_PATH "${CFB_ROOT_DIR}/Assets/Images/logo/logo.ico") 64 | configure_file(${BROKER_RC_FILE}.in ${BROKER_RC_FILE} NEWLINE_STYLE WIN32 ESCAPE_QUOTES) 65 | 66 | # 67 | # Create the executable target 68 | # 69 | add_executable(${PROJECT_NAME} WIN32 ${SOURCE_FILES}) 70 | add_executable(CFB::User::Broker ALIAS ${PROJECT_NAME}) 71 | add_dependencies(${PROJECT_NAME} CFB::Kernel::Driver) 72 | 73 | target_include_directories(${PROJECT_NAME} 74 | PRIVATE 75 | ${BROKER_HEADER_DIR} 76 | 77 | $ 78 | $ 79 | $ 80 | ) 81 | 82 | target_precompile_headers(${PROJECT_NAME} 83 | PRIVATE 84 | ${BROKER_HEADER_DIR}/pch.hpp 85 | ) 86 | 87 | # 88 | # Compilation directives 89 | # 90 | target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20) 91 | 92 | target_compile_options( 93 | ${PROJECT_NAME} 94 | PUBLIC 95 | $<$:/Zc:__cplusplus> 96 | $<$>:$<$:/fsanitize=address>> 97 | 98 | PRIVATE 99 | $,/WX /Gm- /permissive-,/WX /permissive> 100 | ) 101 | 102 | # 103 | # Linking directives 104 | # 105 | target_link_options( 106 | ${PROJECT_NAME} 107 | PUBLIC 108 | /SUBSYSTEM:Console 109 | $<$>:$<$:/InferAsanLibs>> 110 | 111 | PRIVATE 112 | ) 113 | 114 | target_link_libraries(${PROJECT_NAME} 115 | ${BROKER_RES_FILE} 116 | 117 | CFB::User::CommonLib 118 | 119 | Deps::WIL 120 | Deps::JSON 121 | 122 | ws2_32.lib 123 | Userenv.lib 124 | Rpcrt4.lib 125 | kernel32.lib 126 | ntdll.lib 127 | Advapi32.lib 128 | ) 129 | 130 | set_target_properties(${PROJECT_NAME} 131 | PROPERTIES 132 | LINK_FLAGS " /level='requireAdministrator' /uiAccess='false' " 133 | ) 134 | 135 | # 136 | # Install directives 137 | # 138 | install(TARGETS ${PROJECT_NAME} DESTINATION Tools) 139 | install(FILES $ DESTINATION Tools OPTIONAL) 140 | 141 | # 142 | # Custom command directives 143 | # 144 | add_custom_command( 145 | TARGET ${PROJECT_NAME} PRE_BUILD 146 | COMMAND 147 | rc /nologo /I ${BROKER_HEADER_DIR} /fo ${BROKER_RES_FILE} /r ${BROKER_RC_FILE} 148 | COMMENT 149 | "Compiling '${BROKER_RES_FILE}' resource file" 150 | ) 151 | -------------------------------------------------------------------------------- /Broker/Headers/Context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "States.hpp" 10 | 11 | #include "ServiceManager.hpp" 12 | #include "IrpManager.hpp" 13 | #include "ConnectorManager.hpp" 14 | #include "DriverManager.hpp" 15 | // clang-format on 16 | 17 | namespace fs = std::filesystem; 18 | 19 | class GlobalContext 20 | { 21 | public: 22 | /// 23 | /// @brief Construct a new Global Context object 24 | /// 25 | GlobalContext(); 26 | 27 | /// 28 | /// @brief Destroy the Global Context object 29 | /// 30 | /// 31 | ~GlobalContext(); 32 | 33 | /// 34 | /// @brief 35 | /// 36 | /// @return true 37 | /// @return false 38 | /// 39 | bool 40 | Stop(); 41 | 42 | /// 43 | /// @brief Set new state 44 | /// 45 | /// @return true 46 | /// @return false 47 | /// 48 | bool SetState(CFB::Broker::State); 49 | 50 | /// 51 | /// @brief 52 | /// 53 | /// @return true 54 | /// @return false 55 | /// 56 | bool 57 | WaitForState(CFB::Broker::State WantedState); 58 | 59 | /// 60 | /// @brief 61 | /// 62 | /// @return CFB::Broker::State const 63 | /// 64 | CFB::Broker::State const 65 | State() const; 66 | 67 | /// 68 | /// @brief Read-only access to the current process ID 69 | /// 70 | /// @return u32 const 71 | /// 72 | u32 const 73 | Pid() const; 74 | 75 | /// 76 | /// @brief Read-only access to the broker path 77 | /// 78 | /// @return fs::path& 79 | /// 80 | fs::path const& 81 | Path() const; 82 | 83 | /// 84 | /// @brief Get a shared pointer to the service manager object 85 | /// 86 | /// @return std::shared_ptr 87 | /// 88 | std::shared_ptr 89 | ServiceManager() const; 90 | 91 | /// 92 | /// @brief Get a shared pointer to the driver manager 93 | /// 94 | /// @return std::shared_ptr 95 | /// 96 | std::shared_ptr 97 | DriverManager() const; 98 | 99 | /// 100 | ///@brief 101 | /// 102 | ///@return std::shared_ptr 103 | /// 104 | std::shared_ptr 105 | IrpManager() const; 106 | 107 | /// 108 | ///@brief 109 | /// 110 | ///@return std::shared_ptr 111 | /// 112 | std::shared_ptr 113 | ConnectorManager() const; 114 | 115 | /// 116 | /// @brief 117 | /// 118 | /// @return const HANDLE 119 | /// 120 | const HANDLE 121 | TerminationEvent() const; 122 | 123 | private: 124 | /// 125 | /// @brief This mutex protects state changes 126 | /// 127 | std::mutex m_StateMutex; 128 | 129 | /// 130 | /// @brief The manager current state 131 | /// 132 | CFB::Broker::State m_State; 133 | 134 | /// 135 | /// @brief The current process ID 136 | /// 137 | u32 m_Pid; 138 | 139 | /// 140 | /// @brief Broker path 141 | /// 142 | fs::path m_BrokerPath; 143 | 144 | /// 145 | /// @brief 146 | /// 147 | /// 148 | wil::unique_handle m_hTerminationEvent; 149 | 150 | /// 151 | /// @brief 152 | /// 153 | /// 154 | bool m_bIsShuttingDown; 155 | 156 | 157 | ////////////////////////////////////////////////////////////////////////////// 158 | /// 159 | /// Manager declaration below: each manager is its own jthread 160 | /// 161 | 162 | /// 163 | /// @brief A global thread interruption token: if set all the threads will attempt 164 | /// to exit cleanly 165 | /// 166 | std::stop_token m_InterruptToken; 167 | 168 | /// 169 | /// @brief The service manager self-extracts and creates the driver service 170 | /// 171 | std::jthread m_ServiceManagerThread; 172 | 173 | /// 174 | /// @brief 175 | /// 176 | std::jthread m_IrpManagerThread; 177 | 178 | /// 179 | /// @brief 180 | /// 181 | std::jthread m_ConnectorManagerThread; 182 | 183 | /// 184 | /// @brief 185 | /// 186 | std::jthread m_DriverManagerThread; 187 | 188 | /// 189 | /// @brief 190 | /// 191 | std::shared_ptr m_ServiceManager; 192 | 193 | /// 194 | /// @brief 195 | /// 196 | std::shared_ptr m_IrpManager; 197 | 198 | /// 199 | /// @brief 200 | /// 201 | std::shared_ptr m_ConnectorManager; 202 | 203 | /// 204 | /// @brief 205 | /// 206 | std::shared_ptr m_DriverManager; 207 | }; 208 | 209 | 210 | extern GlobalContext Globals; 211 | -------------------------------------------------------------------------------- /Driver/Headers/HookedDriver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Callbacks.hpp" 4 | #include "Common.hpp" 5 | #include "DriverUtils.hpp" 6 | #include "Log.hpp" 7 | 8 | namespace Utils = CFB::Driver::Utils; 9 | 10 | 11 | namespace CFB::Driver 12 | { 13 | class HookedDriver 14 | { 15 | public: 16 | enum class HookState 17 | { 18 | Invalid, 19 | Unhooked, 20 | Hooked 21 | }; 22 | 23 | /// 24 | /// @brief The next HookedDriver entry (if any) 25 | /// 26 | LIST_ENTRY Next; 27 | 28 | /// 29 | /// @brief The absolute path of to the driver underneath 30 | /// 31 | Utils::KUnicodeString Path; 32 | 33 | /// 34 | /// @brief A pointer to the driver object. The object refcount has been incremented by the constructor. 35 | /// The destructor makes sure to release it. 36 | /// 37 | PDRIVER_OBJECT OriginalDriverObject; 38 | 39 | /// 40 | ///@brief Unique smart pointer to the native `DRIVER_OBJECT` associated to the current hooked driver object. 41 | /// 42 | Utils::UniquePointer HookedDriverObject; 43 | 44 | /// 45 | ///@brief Construct a new Hooked Driver object 46 | /// 47 | ///@param UnicodePath the unicode name of the driver 48 | /// 49 | HookedDriver(Utils::KUnicodeString const& UnicodePath); 50 | 51 | /// 52 | ///@brief Destroy the Hooked Driver object 53 | /// 54 | ~HookedDriver(); 55 | 56 | /// 57 | ///@brief HookedDriver memory allocator 58 | /// 59 | ///@param sz 60 | ///@return void* 61 | /// 62 | static void* 63 | operator new(const usize sz) 64 | { 65 | void* Memory = ::ExAllocatePoolWithTag(NonPagedPoolNx, sz, CFB_DEVICE_TAG); 66 | if ( Memory ) 67 | { 68 | ::RtlSecureZeroMemory(Memory, sz); 69 | dbg("Allocated HookedDriver at %p", Memory); 70 | } 71 | return Memory; 72 | } 73 | 74 | /// 75 | ///@brief HookedDriver memory destructor 76 | /// 77 | ///@param Memory 78 | /// 79 | static void 80 | operator delete(void* Memory) 81 | { 82 | dbg("Deallocating HookedDriver at %p", Memory); 83 | return ::ExFreePoolWithTag(Memory, CFB_DEVICE_TAG); 84 | } 85 | 86 | /// 87 | ///@brief Is the current driver capturing IRP? 88 | /// 89 | ///@return true 90 | ///@return false 91 | /// 92 | bool 93 | CanCapture() const; 94 | 95 | /// 96 | ///@brief Enable the IRP capture mode for the driver 97 | /// 98 | ///@return true 99 | ///@return false 100 | /// 101 | bool 102 | EnableCapturing(); 103 | 104 | /// 105 | ///@brief Disable the IRP capture mode for the driver 106 | /// 107 | ///@return true 108 | ///@return false 109 | /// 110 | bool 111 | DisableCapturing(); 112 | 113 | /// 114 | ///@brief Get the number of IRP currently in the stack 115 | /// 116 | ///@return usize const 117 | /// 118 | usize const 119 | IrpCount() const; 120 | 121 | /// 122 | ///@brief Increment the stacked IRP count 123 | /// 124 | void 125 | IncrementIrpCount(); 126 | 127 | /// 128 | ///@brief Alias to `IncrementIrpCount()` 129 | /// 130 | ///@return HookedDriver& 131 | /// 132 | HookedDriver& 133 | operator++(); 134 | 135 | /// 136 | ///@brief Decrement the stacked IRP count 137 | /// 138 | void 139 | DecrementIrpCount(); 140 | 141 | /// 142 | ///@brief Alias to `DecrementIrpCount()` 143 | /// 144 | ///@return HookedDriver& 145 | /// 146 | HookedDriver& 147 | operator--(); 148 | 149 | void 150 | FlagAsInvalid(); 151 | 152 | private: 153 | /// 154 | /// @brief Swap the callbacks of the driver with the interception routines of IrpMonitor 155 | /// The code of those routines is located in `Driver/Callbacks.cpp`. The function will 156 | /// also set the object state as `Hooked`. 157 | /// 158 | void 159 | SwapCallbacks(); 160 | 161 | /// 162 | /// @brief Restore the original callbacks and set the object state as `Unhooked`. 163 | /// 164 | void 165 | RestoreCallbacks(); 166 | 167 | /// 168 | /// @brief If `true`, any IRP targetting the driver object underneath will be pushed to the queue of 169 | /// intercepted IRPs 170 | /// 171 | bool m_CapturingEnabled; 172 | 173 | /// 174 | /// @brief The total number of intercepted IRPs 175 | /// 176 | u64 m_InterceptedIrpsCount; 177 | 178 | /// 179 | /// @brief CallbackLock to guard callback access 180 | /// 181 | Utils::KQueuedSpinLock m_CallbackLock; 182 | 183 | /// 184 | /// @brief 185 | /// 186 | HookState m_State; 187 | }; 188 | } // namespace CFB::Driver 189 | -------------------------------------------------------------------------------- /Broker/Headers/Error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | #include 6 | 7 | #include "Common.hpp" 8 | 9 | 10 | enum class ErrorCode : u32 11 | { 12 | GenericError, 13 | RuntimeError, 14 | UnexpectedStateError, 15 | InvalidInput, 16 | InvalidParameter, 17 | InitializationError, 18 | UnexpectedType, 19 | ArithmeticError, 20 | OverflowError, 21 | UnderflowError, 22 | IllegalValue, 23 | NotImplementedError, 24 | PendingIoError, 25 | NetworkError, 26 | ConnectionError, 27 | TerminationError, 28 | ServiceError, 29 | FilesystemError, 30 | InsufficientPrivilegeError, 31 | SocketInitializationFailed, 32 | LookupError, 33 | DeviceNotInitialized, 34 | InvalidRequestId, 35 | NotFound, 36 | }; 37 | 38 | /// 39 | /// @brief Rust-like type of error handling 40 | /// 41 | struct ErrorType 42 | { 43 | const ErrorCode code; 44 | const u32 number; 45 | }; 46 | 47 | 48 | template 49 | using SuccessType = std::optional; 50 | 51 | template 52 | using Result = std::variant, ErrorType>; 53 | 54 | struct Err : ErrorType 55 | { 56 | #ifdef _WIN32 57 | Err(ErrorCode ErrCode = ErrorCode::GenericError) : ErrorType {ErrCode, ::GetLastError()} 58 | #else 59 | Err(ErrorCode ErrCode = ErrorCode::GenericError) : ErrorType {ErrCode, ::errno} 60 | #endif // _WIN32 61 | { 62 | } 63 | 64 | bool 65 | operator==(const Err& rhs) const 66 | { 67 | return this->code == rhs.code; 68 | } 69 | 70 | bool 71 | operator==(ErrorCode code) const 72 | { 73 | return this->code == code; 74 | } 75 | }; 76 | 77 | template 78 | struct Ok : SuccessType 79 | { 80 | Ok(T value) : SuccessType(value) 81 | { 82 | } 83 | }; 84 | 85 | template 86 | constexpr bool 87 | Success(Result const& f) 88 | { 89 | if ( const SuccessType* c = std::get_if>(&f); c != nullptr ) 90 | { 91 | return true; 92 | } 93 | return false; 94 | } 95 | 96 | template 97 | constexpr bool 98 | Failed(Result const& f) 99 | { 100 | if ( Success(f) ) 101 | { 102 | return false; 103 | } 104 | 105 | if ( const ErrorType* c = std::get_if(&f); c != nullptr ) 106 | { 107 | return true; 108 | } 109 | 110 | throw std::bad_variant_access(); 111 | } 112 | 113 | template 114 | constexpr T const& 115 | Value(Result const& f) 116 | { 117 | if ( const SuccessType* c = std::get_if>(&f); c != nullptr && c->has_value() ) 118 | { 119 | return c->value(); 120 | } 121 | throw std::bad_variant_access(); 122 | } 123 | 124 | template 125 | constexpr ErrorType const& 126 | Error(Result const& f) 127 | { 128 | if ( const ErrorType* c = std::get_if(&f); c != nullptr ) 129 | { 130 | return *c; 131 | } 132 | throw std::bad_variant_access(); 133 | } 134 | 135 | 136 | namespace CFB::Broker::Utils 137 | { 138 | #define CaseToString(x) \ 139 | { \ 140 | case (x): \ 141 | return #x; \ 142 | } 143 | 144 | constexpr const char* 145 | ToString(ErrorType const& x) 146 | { 147 | switch ( x.code ) 148 | { 149 | CaseToString(ErrorCode::GenericError); 150 | CaseToString(ErrorCode::RuntimeError); 151 | CaseToString(ErrorCode::UnexpectedStateError); 152 | CaseToString(ErrorCode::InvalidInput); 153 | CaseToString(ErrorCode::InvalidParameter); 154 | CaseToString(ErrorCode::InitializationError); 155 | CaseToString(ErrorCode::UnexpectedType); 156 | CaseToString(ErrorCode::ArithmeticError); 157 | CaseToString(ErrorCode::OverflowError); 158 | CaseToString(ErrorCode::UnderflowError); 159 | CaseToString(ErrorCode::IllegalValue); 160 | CaseToString(ErrorCode::NotImplementedError); 161 | CaseToString(ErrorCode::PendingIoError); 162 | CaseToString(ErrorCode::NetworkError); 163 | CaseToString(ErrorCode::ConnectionError); 164 | CaseToString(ErrorCode::TerminationError); 165 | CaseToString(ErrorCode::ServiceError); 166 | CaseToString(ErrorCode::FilesystemError); 167 | CaseToString(ErrorCode::InsufficientPrivilegeError); 168 | CaseToString(ErrorCode::SocketInitializationFailed); 169 | CaseToString(ErrorCode::LookupError); 170 | CaseToString(ErrorCode::DeviceNotInitialized); 171 | CaseToString(ErrorCode::InvalidRequestId); 172 | CaseToString(ErrorCode::NotFound); 173 | 174 | default: 175 | throw std::invalid_argument("Unimplemented item"); 176 | } 177 | } 178 | #undef CaseAsString 179 | } // namespace CFB::Broker::Utils 180 | -------------------------------------------------------------------------------- /Broker/Headers/ServiceManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Common.hpp" 10 | #include "ManagerBase.hpp" 11 | 12 | namespace fs = std::filesystem; 13 | 14 | namespace CFB::Broker 15 | { 16 | 17 | class Win32Service 18 | { 19 | enum class ServiceState 20 | { 21 | Uninitialized, 22 | Initialized, 23 | Running, 24 | ShuttingDown, 25 | Shutdown 26 | }; 27 | 28 | 29 | public: 30 | /// 31 | /// @brief Construct a new Win32Service object 32 | /// 33 | Win32Service(); 34 | 35 | /// 36 | /// @brief Destroy the Win32Service object 37 | /// 38 | ~Win32Service(); 39 | 40 | /// 41 | /// @brief Run forever until told to stop 42 | /// 43 | void 44 | RunForever(); 45 | 46 | /// 47 | /// @brief Stops the service, this method is invoked from the SCM 48 | /// 49 | /// @return true 50 | /// @return false 51 | /// 52 | bool 53 | Stop(); 54 | 55 | private: 56 | /// 57 | /// @brief 58 | /// 59 | /// @param lpServiceStatus 60 | /// @return true 61 | /// @return false 62 | /// 63 | bool 64 | ReportServiceStatus(LPSERVICE_STATUS lpServiceStatus); 65 | 66 | /// 67 | /// @brief Set the Status Handle object 68 | /// 69 | /// @param hServiceStatus 70 | /// @return true 71 | /// @return false 72 | /// 73 | bool 74 | InitializeRoutine(); 75 | 76 | /// 77 | /// @brief Notification dispatcher 78 | /// 79 | /// @return true 80 | /// @return false 81 | /// 82 | bool Notify(ServiceState); 83 | 84 | /// 85 | /// @brief This mutex protects state changes 86 | /// 87 | std::mutex m_Mutex; 88 | 89 | /// 90 | /// @brief The manager current state 91 | /// 92 | ServiceState m_State; 93 | 94 | /// 95 | /// @brief Changed state notification event. 96 | /// 97 | HANDLE m_ServiceStateChangedEvent; 98 | 99 | /// 100 | /// @brief Handle to the service status 101 | /// 102 | SERVICE_STATUS_HANDLE m_StatusHandle; 103 | 104 | // SERVICE_STATUS m_ServiceStatus; 105 | 106 | usize m_StatusCheckPoint; 107 | }; 108 | 109 | class ServiceManager : public ManagerBase 110 | { 111 | 112 | public: 113 | /// 114 | /// @brief Construct a new Service Manager:: Service Manager object 115 | /// 116 | /// 117 | ServiceManager(); 118 | 119 | /// 120 | /// @brief Destroy the Service Manager:: Service Manager object 121 | /// 122 | /// 123 | ~ServiceManager(); 124 | 125 | /// 126 | /// @brief 127 | /// 128 | void 129 | Run(); 130 | 131 | /// 132 | /// @brief 133 | /// 134 | /// 135 | Result 136 | Setup(); 137 | 138 | /// 139 | /// @brief Get the Manager naem 140 | /// 141 | /// @return std::string const 142 | /// 143 | std::string const 144 | Name(); 145 | 146 | /// 147 | /// @brief 148 | /// 149 | /// @return std::optional& 150 | /// 151 | std::shared_ptr 152 | BackgroundService(); 153 | 154 | /// 155 | /// @brief 156 | /// 157 | /// @return true 158 | /// @return false 159 | /// 160 | bool 161 | InstallBackgroundService(); 162 | 163 | /// 164 | /// @brief 165 | /// 166 | /// @return true 167 | /// @return false 168 | /// 169 | bool 170 | RunAsBackgroundService(); 171 | 172 | 173 | private: 174 | /// 175 | /// @brief Extracts the IrpMonitor driver embedded in the PE resource section. 176 | /// 177 | /// @return `true` upon successful extraction of the driver from the resource of the driver 178 | /// @return `false` f any error occured. 179 | /// 180 | bool 181 | ExtractDriverFromResource(); 182 | 183 | /// 184 | /// @brief Delete the driver extracted from the PE resources from the disk. 185 | /// 186 | /// @return `true` upon successful deletion of the driver from the disk. 187 | /// @return `false` 188 | /// 189 | bool 190 | DeleteDriverFromDisk(); 191 | 192 | /// 193 | /// @brief Connects to the Windows Service Manager to create and start a service for the IrpMonitor driver. 194 | /// 195 | /// @return true if the service was successfully created, and the driver loaded 196 | /// @return false in any other case 197 | /// 198 | bool 199 | LoadDriver(); 200 | 201 | /// 202 | /// @brief Unloads the driver and deletes the service from the Windows Service Manager. 203 | /// 204 | /// @return `true `if the driver was successfully unloaded 205 | /// @return `false` in any other case 206 | /// 207 | bool 208 | UnloadDriver(); 209 | 210 | /// 211 | /// @brief The fs::path of the driver on disk 212 | /// 213 | fs::path m_DriverTempPath; 214 | 215 | /// 216 | /// @brief Unique pointer to the service control manager 217 | /// 218 | wil::unique_schandle m_hSCManager; 219 | 220 | /// 221 | /// @brief Unique pointer to the service manager 222 | /// 223 | wil::unique_schandle m_hService; 224 | 225 | /// 226 | /// @brief Background service (if set by globals) 227 | /// 228 | std::shared_ptr m_BackgroundService; 229 | }; 230 | 231 | 232 | } // namespace CFB::Broker 233 | -------------------------------------------------------------------------------- /GUI/Source/Helpers.cpp: -------------------------------------------------------------------------------- 1 | #include "Helpers.hpp" 2 | 3 | #include 4 | 5 | #include "imgui_internal.h" 6 | 7 | bool 8 | CFB::GUI::Helpers::ToggleButton(const char* str_id, bool* v) 9 | { 10 | bool item_is_clicked = false; 11 | ImVec2 p = ImGui::GetCursorScreenPos(); 12 | ImDrawList* draw_list = ImGui::GetWindowDrawList(); 13 | 14 | float height = ImGui::GetFrameHeight(); 15 | float width = height * 1.55f; 16 | float radius = height * 0.50f; 17 | 18 | ImGui::InvisibleButton(str_id, ImVec2(width, height)); 19 | if ( ImGui::IsItemClicked() ) 20 | { 21 | *v = !*v; 22 | item_is_clicked = true; 23 | } 24 | 25 | float t = *v ? 1.0f : 0.0f; 26 | 27 | ImGuiContext& g = *GImGui; 28 | float ANIM_SPEED = 0.08f; 29 | if ( g.LastActiveId == g.CurrentWindow->GetID(str_id) ) // && g.LastActiveIdTimer < ANIM_SPEED) 30 | { 31 | float t_anim = ImSaturate(g.LastActiveIdTimer / ANIM_SPEED); 32 | t = *v ? (t_anim) : (1.0f - t_anim); 33 | } 34 | 35 | ImU32 col_bg; 36 | if ( ImGui::IsItemHovered() ) 37 | col_bg = ImGui::GetColorU32(ImLerp(ImVec4(0.78f, 0.78f, 0.78f, 1.0f), ImVec4(0.64f, 0.83f, 0.34f, 1.0f), t)); 38 | else 39 | col_bg = ImGui::GetColorU32(ImLerp(ImVec4(0.85f, 0.85f, 0.85f, 1.0f), ImVec4(0.56f, 0.83f, 0.26f, 1.0f), t)); 40 | 41 | draw_list->AddRectFilled(p, ImVec2(p.x + width, p.y + height), col_bg, height * 0.5f); 42 | draw_list->AddCircleFilled( 43 | ImVec2(p.x + radius + t * (width - radius * 2.0f), p.y + radius), 44 | radius - 1.5f, 45 | IM_COL32(255, 255, 255, 255)); 46 | 47 | return item_is_clicked; 48 | } 49 | 50 | 51 | bool 52 | CFB::GUI::Helpers::BufferingBar( 53 | const char* label, 54 | float value, 55 | const ImVec2& size_arg, 56 | const ImU32& bg_col, 57 | const ImU32& fg_col) 58 | { 59 | ImGuiWindow* window = ImGui::GetCurrentWindow(); 60 | if ( window->SkipItems ) 61 | return false; 62 | 63 | ImGuiContext& g = *GImGui; 64 | const ImGuiStyle& style = g.Style; 65 | const ImGuiID id = window->GetID(label); 66 | 67 | ImVec2 pos = window->DC.CursorPos; 68 | ImVec2 size = size_arg; 69 | size.x -= style.FramePadding.x * 2; 70 | 71 | const ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y)); 72 | ImGui::ItemSize(bb, style.FramePadding.y); 73 | if ( !ImGui::ItemAdd(bb, id) ) 74 | return false; 75 | 76 | // Render 77 | const float circleStart = size.x * 0.7f; 78 | const float circleEnd = size.x; 79 | const float circleWidth = circleEnd - circleStart; 80 | 81 | window->DrawList->AddRectFilled(bb.Min, ImVec2(pos.x + circleStart, bb.Max.y), bg_col); 82 | window->DrawList->AddRectFilled(bb.Min, ImVec2(pos.x + circleStart * value, bb.Max.y), fg_col); 83 | 84 | const float t = g.Time; 85 | const float r = size.y / 2; 86 | const float speed = 1.5f; 87 | 88 | const float a = speed * 0; 89 | const float b = speed * 0.333f; 90 | const float c = speed * 0.666f; 91 | 92 | const float o1 = (circleWidth + r) * (t + a - speed * (int)((t + a) / speed)) / speed; 93 | const float o2 = (circleWidth + r) * (t + b - speed * (int)((t + b) / speed)) / speed; 94 | const float o3 = (circleWidth + r) * (t + c - speed * (int)((t + c) / speed)) / speed; 95 | 96 | window->DrawList->AddCircleFilled(ImVec2(pos.x + circleEnd - o1, bb.Min.y + r), r, bg_col); 97 | window->DrawList->AddCircleFilled(ImVec2(pos.x + circleEnd - o2, bb.Min.y + r), r, bg_col); 98 | window->DrawList->AddCircleFilled(ImVec2(pos.x + circleEnd - o3, bb.Min.y + r), r, bg_col); 99 | 100 | return true; 101 | } 102 | 103 | bool 104 | CFB::GUI::Helpers::Spinner(const char* label, float radius, int thickness, const ImU32& color) 105 | { 106 | ImGuiWindow* window = ImGui::GetCurrentWindow(); 107 | if ( window->SkipItems ) 108 | return false; 109 | 110 | ImGuiContext& g = *GImGui; 111 | const ImGuiStyle& style = g.Style; 112 | const ImGuiID id = window->GetID(label); 113 | 114 | ImVec2 pos = window->DC.CursorPos; 115 | ImVec2 size((radius)*2, (radius + style.FramePadding.y) * 2); 116 | 117 | const ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y)); 118 | ImGui::ItemSize(bb, style.FramePadding.y); 119 | if ( !ImGui::ItemAdd(bb, id) ) 120 | return false; 121 | 122 | // Render 123 | window->DrawList->PathClear(); 124 | 125 | int num_segments = 30; 126 | int start = std::abs(ImSin(g.Time * 1.8f) * (num_segments - 5)); 127 | 128 | const float a_min = IM_PI * 2.0f * ((float)start) / (float)num_segments; 129 | const float a_max = IM_PI * 2.0f * ((float)num_segments - 3) / (float)num_segments; 130 | 131 | const ImVec2 centre = ImVec2(pos.x + radius, pos.y + radius + style.FramePadding.y); 132 | 133 | for ( int i = 0; i < num_segments; i++ ) 134 | { 135 | const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); 136 | window->DrawList->PathLineTo( 137 | ImVec2(centre.x + ImCos(a + g.Time * 8) * radius, centre.y + ImSin(a + g.Time * 8) * radius)); 138 | } 139 | 140 | window->DrawList->PathStroke(color, false, thickness); 141 | 142 | return true; 143 | } 144 | -------------------------------------------------------------------------------- /Broker/Source/Context.cpp: -------------------------------------------------------------------------------- 1 | #include "Context.hpp" 2 | 3 | #include "Log.hpp" 4 | 5 | 6 | GlobalContext::GlobalContext() : 7 | m_State(CFB::Broker::State::Uninitialized), 8 | m_Pid(::GetCurrentProcessId()), 9 | m_bIsShuttingDown(false) 10 | { 11 | // 12 | // Get the broker executable absolute path 13 | // 14 | { 15 | std::wstring wsPath; 16 | wsPath.resize(MAX_PATH); 17 | ::GetModuleFileNameW(nullptr, wsPath.data(), MAX_PATH); 18 | m_BrokerPath = wsPath; 19 | } 20 | 21 | // 22 | // Create a termination event 23 | // 24 | { 25 | wil::unique_handle hEvent(::CreateEventW(nullptr, true, false, nullptr)); 26 | if ( !hEvent ) 27 | { 28 | CFB::Log::perror("CreateEventW(TerminationEvent)"); 29 | throw std::runtime_error("GlobalContext()"); 30 | } 31 | 32 | m_hTerminationEvent = std::move(hEvent); 33 | } 34 | 35 | // 36 | // Initialiazes the managers 37 | // 38 | m_ServiceManager = std::make_shared(); 39 | m_IrpManager = std::make_shared(); 40 | m_ConnectorManager = std::make_shared(); 41 | m_DriverManager = std::make_shared(); 42 | 43 | // 44 | // Start the individual threads 45 | // 46 | m_ServiceManagerThread = std::jthread( 47 | [this]() 48 | { 49 | if ( Success(m_ServiceManager->Setup()) ) 50 | { 51 | m_ServiceManager->Run(); 52 | } 53 | }); 54 | 55 | m_IrpManagerThread = std::jthread( 56 | [this]() 57 | { 58 | if ( Success(m_IrpManager->Setup()) ) 59 | { 60 | m_IrpManager->Run(); 61 | } 62 | }); 63 | 64 | m_ConnectorManagerThread = std::jthread( 65 | [this]() 66 | { 67 | if ( Success(m_ConnectorManager->Setup()) ) 68 | { 69 | m_ConnectorManager->Run(); 70 | } 71 | }); 72 | 73 | m_DriverManagerThread = std::jthread( 74 | [this]() 75 | { 76 | if ( Success(m_DriverManager->Setup()) ) 77 | { 78 | m_DriverManager->Run(); 79 | } 80 | }); 81 | } 82 | 83 | 84 | GlobalContext::~GlobalContext() 85 | { 86 | info("Destroying global context."); 87 | 88 | m_ServiceManagerThread.join(); 89 | m_IrpManagerThread.join(); 90 | m_ConnectorManagerThread.join(); 91 | m_DriverManagerThread.join(); 92 | } 93 | 94 | 95 | CFB::Broker::State const 96 | GlobalContext::State() const 97 | { 98 | return m_State; 99 | } 100 | 101 | 102 | bool 103 | GlobalContext::SetState(CFB::Broker::State NewState) 104 | { 105 | bool res = true; 106 | 107 | { 108 | std::scoped_lock lock(m_StateMutex); 109 | 110 | if ( NewState == CFB::Broker::State::AllManagerReady ) 111 | { 112 | NewState = CFB::Broker::State::Running; 113 | ok("All managers are ready!"); 114 | } 115 | 116 | if ( NewState == CFB::Broker::State::AllManagerDone ) 117 | { 118 | NewState = CFB::Broker::State::Finished; 119 | ok("All managers have finished!"); 120 | } 121 | 122 | // 123 | // This shouldn't happen, so print a warning if it does for investigate 124 | // 125 | if ( NewState < m_State ) 126 | { 127 | warn( 128 | "Suspicious state transition asked: Current: %s -> New: %s", 129 | CFB::Broker::Utils::ToString(NewState), 130 | CFB::Broker::Utils::ToString(m_State)); 131 | res = false; 132 | } 133 | 134 | // 135 | // Apply the global state change 136 | // 137 | m_State = NewState; 138 | } 139 | 140 | // 141 | // Notify all managers 142 | // 143 | res &= m_ServiceManager->NotifyStateChange(); 144 | res &= m_ConnectorManager->NotifyStateChange(); 145 | res &= m_IrpManager->NotifyStateChange(); 146 | res &= m_DriverManager->NotifyStateChange(); 147 | 148 | return res; 149 | } 150 | 151 | u32 const 152 | GlobalContext::Pid() const 153 | { 154 | return m_Pid; 155 | } 156 | 157 | 158 | fs::path const& 159 | GlobalContext::Path() const 160 | { 161 | return m_BrokerPath; 162 | } 163 | 164 | 165 | std::shared_ptr 166 | GlobalContext::ServiceManager() const 167 | { 168 | return m_ServiceManager; 169 | } 170 | 171 | 172 | std::shared_ptr 173 | GlobalContext::DriverManager() const 174 | { 175 | return m_DriverManager; 176 | } 177 | 178 | 179 | std::shared_ptr 180 | GlobalContext::IrpManager() const 181 | { 182 | return m_IrpManager; 183 | } 184 | 185 | std::shared_ptr 186 | GlobalContext::ConnectorManager() const 187 | { 188 | return m_ConnectorManager; 189 | } 190 | 191 | 192 | const HANDLE 193 | GlobalContext::TerminationEvent() const 194 | { 195 | return m_hTerminationEvent.get(); 196 | } 197 | 198 | 199 | bool 200 | GlobalContext::Stop() 201 | { 202 | bool res = true; 203 | 204 | m_bIsShuttingDown = true; 205 | 206 | res &= m_ServiceManager->NotifyTermination(); 207 | res &= m_ConnectorManager->NotifyTermination(); 208 | res &= m_IrpManager->NotifyTermination(); 209 | res &= m_DriverManager->NotifyTermination(); 210 | res &= (::SetEvent(m_hTerminationEvent.get()) == TRUE); 211 | 212 | return res; 213 | } 214 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | 263 | .vscode 264 | /Assets/img/logo/bear.png 265 | .gitignore 266 | Common/Headers/CompileInfo.hpp 267 | Broker/Source/Broker.rc 268 | Broker/Source/Broker.res 269 | Driver/Source/Driver.rc 270 | Driver/Source/Driver.res 271 | GUI/Source/GUI.rc 272 | GUI/Source/GUI.res 273 | -------------------------------------------------------------------------------- /Assets/Images/logo/Logo_v1.svg: -------------------------------------------------------------------------------- 1 | Logo_v1Canadian Furious Beaver -------------------------------------------------------------------------------- /Driver/Headers/CapturedIrp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #include "Common.hpp" 5 | #include "Comms.hpp" 6 | 7 | #include "DriverUtils.hpp" 8 | #include "HookedDriver.hpp" 9 | // clang-format on 10 | 11 | namespace Comms = CFB::Comms; 12 | namespace Utils = CFB::Driver::Utils; 13 | 14 | namespace CFB::Driver 15 | { 16 | 17 | /// 18 | ///@brief `CapturedIrp` contains all the information stored from the IRP hijacking 19 | /// 20 | class CapturedIrp 21 | { 22 | public: 23 | enum class IrpType : u8 24 | { 25 | Irp = 0x00, 26 | FastIo_Ioctl = 0x80, 27 | FastIo_Read = 0x81, 28 | FastIo_Write = 0x82, 29 | }; 30 | 31 | enum class Flags : u32 32 | { 33 | UseOutputBuffer = (1 << 0), 34 | UseInputBuffer = (1 << 1), 35 | InitQueueMessage = (1 << 2), 36 | }; 37 | 38 | 39 | /// 40 | ///@brief Construct a new Captured Irp object 41 | /// 42 | ///@param Type 43 | ///@param DeviceObject 44 | /// 45 | CapturedIrp(const IrpType Type, const PDEVICE_OBJECT DeviceObject); 46 | 47 | /// 48 | ///@brief Destroy the Captured Irp object 49 | /// 50 | /// 51 | ~CapturedIrp(); 52 | 53 | /// 54 | ///@brief CapturedIrp memory allocator 55 | /// 56 | ///@param sz 57 | ///@return void* 58 | /// 59 | static void* 60 | operator new(usize sz) 61 | { 62 | void* Memory = ::ExAllocatePoolWithTag(NonPagedPoolNx, sz, CFB_DEVICE_TAG); 63 | if ( !Memory ) 64 | { 65 | ::ExRaiseStatus(STATUS_NO_MEMORY); 66 | } 67 | dbg("Allocated CapturedIrp at %p", Memory); 68 | ::RtlSecureZeroMemory(Memory, sz); 69 | return Memory; 70 | } 71 | 72 | /// 73 | ///@brief CapturedIrp memory destructor 74 | /// 75 | ///@param Memory 76 | /// 77 | static void 78 | operator delete(void* Memory) 79 | { 80 | dbg("Deallocating CapturedIrp at %p", Memory); 81 | return ::ExFreePoolWithTag(Memory, CFB_DEVICE_TAG); 82 | } 83 | 84 | /// 85 | ///@brief Capture the meta-data and content of the IRP *before* passing it down to the legit driver 86 | /// 87 | ///@param Irp 88 | ///@return NTSTATUS 89 | /// 90 | NTSTATUS 91 | CapturePreCallData(_In_ PIRP Irp); 92 | 93 | /// 94 | ///@brief Capture the meta-data and content of the IRP *after* passing it down to the legit driver 95 | /// 96 | ///@param Irp 97 | ///@param ReturnedIoctlStatus 98 | ///@return NTSTATUS 99 | /// 100 | NTSTATUS 101 | CapturePostCallData(_In_ PIRP Irp, _In_ NTSTATUS ReturnedIoctlStatus); 102 | 103 | /// 104 | ///@brief Capture the meta-data and content of the Fast IRP *before* passing it down to the legit driver 105 | /// 106 | ///@param InputBuffer 107 | ///@param IoControlCode 108 | ///@return NTSTATUS 109 | /// 110 | NTSTATUS 111 | CapturePreCallFastIoData(_In_opt_ PVOID InputBuffer, _In_ ULONG IoControlCode); 112 | 113 | /// 114 | ///@brief Capture the meta-data and content of the Fast IRP *after* passing it down to the legit driver 115 | /// 116 | ///@param OutputBuffer 117 | ///@return NTSTATUS 118 | /// 119 | NTSTATUS 120 | CapturePostCallFastIoData(_Out_opt_ PVOID OutputBuffer); 121 | 122 | /// 123 | ///@brief Total size of captured data (i.e. sizeof(in) + sizeof(out)) including the header 124 | /// 125 | ///@return usize const 126 | /// 127 | usize const 128 | Size() const; 129 | 130 | /// 131 | ///@brief Total size of of captured data (i.e. sizeof(in) + sizeof(out)) 132 | /// 133 | ///@return usize const 134 | /// 135 | usize const 136 | DataSize() const; 137 | 138 | /// 139 | ///@brief Size of of captured input data 140 | /// 141 | ///@return usize const 142 | /// 143 | usize const 144 | InputDataSize() const; 145 | 146 | /// 147 | ///@brief Size of of captured output data 148 | /// 149 | ///@return usize const 150 | /// 151 | usize const 152 | OutputDataSize() const; 153 | 154 | /// 155 | ///@brief Return a raw pointer to the hooked driver 156 | /// 157 | ///@return HookedDriver* const 158 | /// 159 | HookedDriver* const 160 | AssociatedDriver() const; 161 | 162 | /// 163 | ///@brief Entry to the next catpured IRP 164 | /// 165 | /// 166 | LIST_ENTRY Next; 167 | 168 | /// 169 | ///@brief Generate an exportable CapturedIrp header object 170 | /// 171 | ///@return Comms::CapturedIrpHeader 172 | /// 173 | Comms::CapturedIrpHeader 174 | ExportHeader() const; 175 | 176 | /// 177 | ///@brief Raw pointer to the captured input buffer 178 | /// 179 | ///@return u8* 180 | /// 181 | u8* 182 | InputBuffer() const; 183 | 184 | /// 185 | ///@brief Raw pointer to the captured output buffer 186 | /// 187 | ///@return u8* 188 | /// 189 | u8* 190 | OutputBuffer() const; 191 | 192 | /// 193 | ///@brief Indicates whether the CapturedIrp was successfully initialized 194 | /// 195 | ///@return true 196 | ///@return false 197 | /// 198 | bool 199 | IsValid() const; 200 | 201 | private: 202 | bool m_Valid; 203 | LARGE_INTEGER m_TimeStamp; 204 | u8 m_Irql; 205 | IrpType m_Type; 206 | u8 m_MajorFunction; 207 | u8 m_MinorFunction; 208 | u32 m_IoctlCode; 209 | u32 m_Pid; 210 | u32 m_Tid; 211 | NTSTATUS m_Status; 212 | Utils::KAlloc m_InputBuffer; 213 | Utils::KAlloc m_OutputBuffer; 214 | PDEVICE_OBJECT m_DeviceObject; 215 | Utils::KUnicodeString m_DriverName; 216 | Utils::KUnicodeString m_DeviceName; 217 | Utils::KUnicodeString m_ProcessName; 218 | HookedDriver* m_Driver; 219 | }; 220 | 221 | } // namespace CFB::Driver 222 | -------------------------------------------------------------------------------- /Driver/Source/HookedDriver.cpp: -------------------------------------------------------------------------------- 1 | #define CFB_NS "[CFB::Driver::HookedDriver]" 2 | 3 | #include "HookedDriver.hpp" 4 | 5 | #include "Native.hpp" 6 | 7 | namespace Callbacks = CFB::Driver::Callbacks; 8 | 9 | 10 | namespace CFB::Driver 11 | { 12 | HookedDriver::HookedDriver(Utils::KUnicodeString const& UnicodePath) : 13 | Next {}, 14 | OriginalDriverObject {nullptr}, 15 | HookedDriverObject {new(NonPagedPoolNx) DRIVER_OBJECT}, 16 | Path {UnicodePath}, 17 | m_CapturingEnabled {false}, 18 | m_State {HookState::Unhooked}, 19 | m_InterceptedIrpsCount {0} 20 | { 21 | dbg("Creating HookedDriver('%wZ')", Path.get()); 22 | 23 | // 24 | // Find the driver from its name, link reference to the DRIVER_OBJECT to the lifetime of this HookedDriver 25 | // 26 | NTSTATUS Status = ::ObReferenceObjectByName( 27 | Path.get(), 28 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 29 | nullptr, 30 | 0, 31 | *IoDriverObjectType, 32 | KernelMode, 33 | nullptr, 34 | (PVOID*)&OriginalDriverObject); 35 | 36 | NT_ASSERT(NT_SUCCESS(Status)); 37 | NT_ASSERT(OriginalDriverObject != nullptr); 38 | 39 | // 40 | // Create a copy DRIVER_OBJECT 41 | // 42 | ::memcpy(HookedDriverObject.get(), OriginalDriverObject, sizeof(DRIVER_OBJECT)); 43 | 44 | // 45 | // Swap the IRP major function callbacks of the HookedDriver, avoiding to trigger PatchGuard 46 | // 47 | SwapCallbacks(); 48 | 49 | NT_ASSERT(m_State == HookState::Hooked); 50 | } 51 | 52 | 53 | HookedDriver::~HookedDriver() 54 | { 55 | dbg("Destroying HookedDriver '%wZ'", Path.get()); 56 | 57 | DisableCapturing(); 58 | RestoreCallbacks(); 59 | 60 | ObDereferenceObject(OriginalDriverObject); 61 | } 62 | 63 | 64 | void 65 | HookedDriver::SwapCallbacks() 66 | { 67 | Utils::ScopedLock lock(m_CallbackLock); 68 | 69 | if ( m_State != HookState::Unhooked ) 70 | { 71 | warn("Invalid state: expecting 'Unhooked'"); 72 | return; 73 | } 74 | 75 | // 76 | // Hook all `IRP_MJ_*` 77 | // 78 | for ( u16 i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++ ) 79 | { 80 | HookedDriverObject->MajorFunction[i] = (PDRIVER_DISPATCH)Callbacks::InterceptGenericRoutine; 81 | } 82 | 83 | // 84 | // Hook FastIo too 85 | // 86 | if ( OriginalDriverObject->FastIoDispatch ) 87 | { 88 | HookedDriverObject->FastIoDispatch->FastIoDeviceControl = 89 | (PFAST_IO_DEVICE_CONTROL)Callbacks::InterceptGenericFastIoDeviceControl; 90 | HookedDriverObject->FastIoDispatch->FastIoRead = (PFAST_IO_READ)Callbacks::InterceptGenericFastIoRead; 91 | HookedDriverObject->FastIoDispatch->FastIoWrite = (PFAST_IO_WRITE)Callbacks::InterceptGenericFastIoWrite; 92 | } 93 | 94 | // 95 | // This is where the hooking takes places: for each device object of the original driver, replace 96 | // the driver object pointer member making it point to the hooked version. 97 | // 98 | for ( PDEVICE_OBJECT DeviceObject = OriginalDriverObject->DeviceObject; DeviceObject != nullptr; 99 | DeviceObject = DeviceObject->NextDevice ) 100 | { 101 | if ( DeviceObject->DriverObject == OriginalDriverObject ) 102 | { 103 | InterlockedExchangePointer((PVOID*)&DeviceObject->DriverObject, (PVOID)HookedDriverObject.get()); 104 | } 105 | } 106 | 107 | // 108 | // Switch state flag to hooked 109 | // 110 | m_State = HookState::Hooked; 111 | } 112 | 113 | 114 | void 115 | HookedDriver::RestoreCallbacks() 116 | { 117 | Utils::ScopedLock lock(m_CallbackLock); 118 | 119 | if ( m_State != HookState::Hooked ) 120 | { 121 | warn("Invalid state: expecting 'Hooked', got %#x", m_State); 122 | return; 123 | } 124 | 125 | // 126 | // Switch back state flag 127 | // 128 | m_State = HookState::Unhooked; 129 | 130 | // 131 | // For each device object, restore the original driver object pointer 132 | // 133 | for ( PDEVICE_OBJECT DeviceObject = OriginalDriverObject->DeviceObject; DeviceObject != nullptr; 134 | DeviceObject = DeviceObject->NextDevice ) 135 | { 136 | InterlockedExchangePointer((PVOID*)&DeviceObject->DriverObject, (PVOID)OriginalDriverObject); 137 | } 138 | } 139 | 140 | 141 | bool 142 | HookedDriver::EnableCapturing() 143 | { 144 | Utils::ScopedLock lock(m_CallbackLock); 145 | if ( m_State != HookState::Hooked ) 146 | { 147 | return false; 148 | } 149 | if ( m_CapturingEnabled == false ) 150 | { 151 | m_CapturingEnabled = true; 152 | dbg("Enabled IRP capturing for %wZ", Path.get()); 153 | } 154 | return true; 155 | } 156 | 157 | 158 | bool 159 | HookedDriver::DisableCapturing() 160 | { 161 | Utils::ScopedLock lock(m_CallbackLock); 162 | m_CapturingEnabled = false; 163 | dbg("Disabled IRP capturing for %wZ", Path.get()); 164 | return true; 165 | } 166 | 167 | void 168 | HookedDriver::FlagAsInvalid() 169 | { 170 | Utils::ScopedLock lock(m_CallbackLock); 171 | m_State = HookState::Invalid; 172 | } 173 | 174 | 175 | bool 176 | HookedDriver::CanCapture() const 177 | { 178 | return m_State == HookState::Hooked && m_CapturingEnabled == true; 179 | } 180 | 181 | 182 | usize const 183 | HookedDriver::IrpCount() const 184 | { 185 | return m_InterceptedIrpsCount; 186 | } 187 | 188 | 189 | void 190 | HookedDriver::IncrementIrpCount() 191 | { 192 | InterlockedIncrement64((PLONG64)&m_InterceptedIrpsCount); 193 | } 194 | 195 | 196 | void 197 | HookedDriver::DecrementIrpCount() 198 | { 199 | InterlockedDecrement64((PLONG64)&m_InterceptedIrpsCount); 200 | } 201 | 202 | 203 | HookedDriver& 204 | HookedDriver::operator++() 205 | { 206 | IncrementIrpCount(); 207 | return *this; 208 | } 209 | 210 | 211 | HookedDriver& 212 | HookedDriver::operator--() 213 | { 214 | DecrementIrpCount(); 215 | return *this; 216 | } 217 | 218 | 219 | } // namespace CFB::Driver 220 | -------------------------------------------------------------------------------- /Driver/Source/HookedDriverManager.cpp: -------------------------------------------------------------------------------- 1 | #define CFB_NS "[CFB::Driver::HookedDriverManager]" 2 | 3 | #include "HookedDriverManager.hpp" 4 | 5 | #include "Context.hpp" 6 | #include "Native.hpp" 7 | 8 | 9 | namespace Utils = CFB::Driver::Utils; 10 | 11 | namespace CFB::Driver 12 | { 13 | HookedDriverManager::HookedDriverManager() 14 | { 15 | dbg("Creating HookedDriverManager"); 16 | } 17 | 18 | HookedDriverManager::~HookedDriverManager() 19 | { 20 | dbg("Destroying HookedDriverManager"); 21 | 22 | HookedDriverManager::RemoveAllDrivers(); 23 | } 24 | 25 | NTSTATUS 26 | HookedDriverManager::InsertDriver(Utils::KUnicodeString const& UnicodePath) 27 | { 28 | NTSTATUS Status = STATUS_UNSUCCESSFUL; 29 | PDRIVER_OBJECT pDriver = nullptr; 30 | 31 | dbg("HookedDriverManager::InsertDriver('%wZ')", UnicodePath.get()); 32 | 33 | // 34 | // Resolve the given `Path` parameter as name for Driver Object 35 | // 36 | { 37 | Status = ::ObReferenceObjectByName( 38 | UnicodePath.get(), 39 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 40 | nullptr, 41 | 0, 42 | *IoDriverObjectType, 43 | KernelMode, 44 | nullptr, 45 | (PVOID*)&pDriver); 46 | 47 | if ( !NT_SUCCESS(Status) ) 48 | { 49 | return Status; 50 | } 51 | } 52 | 53 | 54 | dbg("HookedDriverManager::InsertDriver(): Found driver at %p", pDriver); 55 | 56 | // 57 | // On any failure, make sure to dereference the object 58 | // 59 | Utils::ScopedWrapper ScopedDriverObject( 60 | pDriver, 61 | [&pDriver]() 62 | { 63 | // 64 | // Always dereference of scope-leave. `HookedDriver` manages its own driver reference 65 | // 66 | dbg("HookedDriverManager::InsertDriver(): dereferencing object %p", pDriver); 67 | ObDereferenceObject(pDriver); 68 | }); 69 | 70 | // 71 | // Refuse to hook IrpMonitor 72 | // 73 | if ( pDriver == Globals->DriverObject ) 74 | { 75 | err("HookedDriverManager::InsertDriver(): refusing to hook %S", CFB_DRIVER_BASENAME); 76 | return STATUS_ACCESS_DENIED; 77 | } 78 | 79 | 80 | Utils::ScopedLock lock(m_Mutex); 81 | 82 | // 83 | // Check if the driver is already hooked 84 | // 85 | auto FromDriverAddress = [&ScopedDriverObject](const HookedDriver* h) 86 | { 87 | return h->OriginalDriverObject == ScopedDriverObject.get(); 88 | }; 89 | 90 | if ( m_Entries.Find(FromDriverAddress) != nullptr ) 91 | { 92 | return STATUS_ALREADY_REGISTERED; 93 | } 94 | 95 | // 96 | // Check if there's space 97 | // 98 | if ( m_Entries.Size() >= CFB_MAX_HOOKED_DRIVERS ) 99 | { 100 | return STATUS_INSUFFICIENT_RESOURCES; 101 | } 102 | 103 | dbg("HookedDriverManager::InsertDriver(): Driver '%wZ' (%p) is not hooked, hooking now...", 104 | UnicodePath.get(), 105 | ScopedDriverObject.get()); 106 | 107 | // 108 | // Allocate the new HookedDriver, this will result in all the `IRP_MJ_*` of the driver being 109 | // redirected to IrpMonitor 110 | // 111 | auto NewHookedDriver = new HookedDriver(UnicodePath); 112 | 113 | // 114 | // Last, insert the driver to the linked list 115 | // 116 | m_Entries.PushBack(NewHookedDriver); 117 | 118 | dbg("Added '%wZ' to the hooked driver list, TotalEntries=%d", NewHookedDriver->Path.get(), m_Entries.Size()); 119 | 120 | info("Driver '%wZ' is hooked", NewHookedDriver->Path.get()); 121 | 122 | return STATUS_SUCCESS; 123 | } 124 | 125 | NTSTATUS 126 | HookedDriverManager::RemoveDriver(Utils::KUnicodeString const& UnicodePath) 127 | { 128 | Utils::ScopedLock lock(m_Mutex); 129 | 130 | const usize PathMaxLength = min(UnicodePath.size(), CFB_DRIVER_MAX_PATH); 131 | auto FromDriverPath = [&UnicodePath, &PathMaxLength](HookedDriver* h) 132 | { 133 | return h->Path == UnicodePath; 134 | }; 135 | 136 | auto MatchedDriver = m_Entries.Find(FromDriverPath); 137 | if ( MatchedDriver == nullptr ) 138 | { 139 | return STATUS_NOT_FOUND; 140 | } 141 | 142 | dbg("Removing HookedDriver '%wZ' (%p) ...", MatchedDriver->Path.get(), MatchedDriver); 143 | 144 | m_Entries -= MatchedDriver; 145 | 146 | ObDereferenceObject(MatchedDriver->OriginalDriverObject); 147 | 148 | info("Driver '%wZ' is unhooked", MatchedDriver->Path.get()); 149 | delete MatchedDriver; 150 | 151 | return STATUS_SUCCESS; 152 | } 153 | 154 | NTSTATUS 155 | HookedDriverManager::RemoveAllDrivers() 156 | { 157 | dbg("Removing all drivers"); 158 | 159 | Utils::ScopedLock lock(m_Mutex); 160 | do 161 | { 162 | auto Entry = m_Entries.PopBack(); 163 | if ( Entry == nullptr ) 164 | { 165 | break; 166 | } 167 | delete Entry; 168 | } while ( true ); 169 | 170 | return STATUS_SUCCESS; 171 | } 172 | 173 | NTSTATUS 174 | HookedDriverManager::SetMonitoringState(const PUNICODE_STRING UnicodePath, bool bEnable) 175 | { 176 | Utils::ScopedLock lock(m_Mutex); 177 | 178 | const usize PathMaxLength = min(UnicodePath->Length, CFB_DRIVER_MAX_PATH); 179 | auto FromDriverPath = [&UnicodePath, &PathMaxLength](HookedDriver* h) 180 | { 181 | return h->Path == UnicodePath; 182 | }; 183 | 184 | auto MatchedDriver = m_Entries.Find(FromDriverPath); 185 | if ( MatchedDriver == nullptr ) 186 | { 187 | return STATUS_NOT_FOUND; 188 | } 189 | 190 | const bool OldState = MatchedDriver->CanCapture(); 191 | (bEnable) ? MatchedDriver->EnableCapturing() : MatchedDriver->DisableCapturing(); 192 | const bool DriverStateChanged = OldState != MatchedDriver->CanCapture(); 193 | 194 | dbg("HookedDriverManager::SetMonitoringState('%wZ', %s): state %schanged, CanCapture=%s", 195 | MatchedDriver->Path.get(), 196 | boolstr(bEnable), 197 | (DriverStateChanged ? "" : "un"), 198 | boolstr(MatchedDriver->CanCapture())); 199 | 200 | info("IRPs to driver '%wZ' are %scaptured", MatchedDriver->Path.get(), MatchedDriver->CanCapture() ? "" : "not "); 201 | 202 | return STATUS_SUCCESS; 203 | } 204 | 205 | Utils::LinkedList& 206 | HookedDriverManager::Items() 207 | { 208 | return m_Entries; 209 | } 210 | } // namespace CFB::Driver 211 | -------------------------------------------------------------------------------- /cmake/FindWdk.cmake: -------------------------------------------------------------------------------- 1 | # Redistribution and use is allowed under the OSI-approved 3-clause BSD license. 2 | # Copyright (c) 2018 Sergey Podobry (sergey.podobry at gmail.com). All rights reserved. 3 | 4 | # .rst: 5 | # FindWDK 6 | # ---------- 7 | # 8 | # This module searches for the installed Windows Development Kit (WDK) and 9 | # exposes commands for creating kernel drivers and kernel libraries. 10 | # 11 | # Output variables: 12 | # - `WDK_FOUND` -- if false, do not try to use WDK 13 | # - `WDK_ROOT` -- where WDK is installed 14 | # - `WDK_VERSION` -- the version of the selected WDK 15 | # - `WDK_WINVER` -- the WINVER used for kernel drivers and libraries 16 | # (default value is `0x0601` and can be changed per target or globally) 17 | # 18 | # Example usage: 19 | # 20 | # find_package(WDK REQUIRED) 21 | # 22 | # wdk_add_library(KmdfCppLib STATIC KMDF 1.15 23 | # KmdfCppLib.h 24 | # KmdfCppLib.cpp 25 | # ) 26 | # target_include_directories(KmdfCppLib INTERFACE .) 27 | # 28 | # wdk_add_driver(KmdfCppDriver KMDF 1.15 29 | # Main.cpp 30 | # ) 31 | # target_link_libraries(KmdfCppDriver KmdfCppLib) 32 | # 33 | cmake_minimum_required(VERSION 3.20) 34 | 35 | if(DEFINED ENV{WDKContentRoot}) 36 | file(GLOB WDK_NTDDK_FILES 37 | "$ENV{WDKContentRoot}/Include/*/km/ntddk.h" 38 | ) 39 | else() 40 | file(GLOB WDK_NTDDK_FILES 41 | "C:/Program Files*/Windows Kits/10/Include/*/km/ntddk.h" 42 | ) 43 | endif() 44 | 45 | if(WDK_NTDDK_FILES) 46 | list(GET WDK_NTDDK_FILES -1 WDK_LATEST_NTDDK_FILE) 47 | endif() 48 | 49 | include(FindPackageHandleStandardArgs) 50 | find_package_handle_standard_args(WDK REQUIRED_VARS WDK_LATEST_NTDDK_FILE) 51 | 52 | if(NOT WDK_LATEST_NTDDK_FILE) 53 | return() 54 | endif() 55 | 56 | get_filename_component(WDK_ROOT ${WDK_LATEST_NTDDK_FILE} DIRECTORY) 57 | get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) 58 | get_filename_component(WDK_VERSION ${WDK_ROOT} NAME) 59 | get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) 60 | get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) 61 | 62 | message(STATUS "WDK_ROOT: " ${WDK_ROOT}) 63 | message(STATUS "WDK_VERSION: " ${WDK_VERSION}) 64 | 65 | set(WDK_WINVER "0x0601" CACHE STRING "Default WINVER for WDK targets") 66 | 67 | set(WDK_ADDITIONAL_FLAGS_FILE "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/wdkflags.h") 68 | file(WRITE ${WDK_ADDITIONAL_FLAGS_FILE} "#pragma runtime_checks(\"suc\", off)") 69 | 70 | set(WDK_COMPILE_FLAGS 71 | "/Zp8" # set struct alignment 72 | "/GF" # enable string pooling 73 | "/GR-" # disable RTTI 74 | "/Gz" # __stdcall by default 75 | "/kernel" # create kernel mode binary 76 | "/FIwarning.h" # disable warnings in WDK headers 77 | "/FI${WDK_ADDITIONAL_FLAGS_FILE}" # include file to disable RTC 78 | ) 79 | 80 | set(WDK_COMPILE_DEFINITIONS "WINNT=1") 81 | set(WDK_COMPILE_DEFINITIONS_DEBUG "MSC_NOOPT;DEPRECATE_DDK_FUNCTIONS=1;DBG=1") 82 | 83 | if(CMAKE_GENERATOR_PLATFORM STREQUAL "win32") 84 | list(APPEND WDK_COMPILE_DEFINITIONS "_X86_=1;i386=1;STD_CALL") 85 | set(WDK_PLATFORM "x86") 86 | elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "x64") 87 | list(APPEND WDK_COMPILE_DEFINITIONS "_WIN64;_AMD64_;AMD64") 88 | set(WDK_PLATFORM "x64") 89 | elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "arm64") 90 | list(APPEND WDK_COMPILE_DEFINITIONS "_WIN64;_ARM64_;ARM64") 91 | set(WDK_COMPILE_FLAGS ${WDK_COMPILE_FLAGS} /GS-) # TODO: fixes missing symbol __security_pop_cookie, fix 92 | set(WDK_PLATFORM "arm64") 93 | else() 94 | message(FATAL_ERROR "Unsupported architecture") 95 | endif() 96 | 97 | string(CONCAT WDK_LINK_FLAGS 98 | "/MANIFEST:NO " # 99 | "/DRIVER " # 100 | "/OPT:REF " # 101 | "/INCREMENTAL:NO " # 102 | "/OPT:ICF " # 103 | "/SUBSYSTEM:NATIVE " # 104 | "/MERGE:_TEXT=.text;_PAGE=PAGE " # 105 | "/NODEFAULTLIB " # do not link default CRT 106 | "/SECTION:INIT,d " # 107 | "/VERSION:10.0 " # 108 | ) 109 | 110 | # Generate imported targets for WDK lib files 111 | file(GLOB WDK_LIBRARIES "${WDK_ROOT}/Lib/${WDK_VERSION}/km/${WDK_PLATFORM}/*.lib") 112 | 113 | foreach(LIBRARY IN LISTS WDK_LIBRARIES) 114 | get_filename_component(LIBRARY_NAME ${LIBRARY} NAME_WE) 115 | string(TOUPPER ${LIBRARY_NAME} LIBRARY_NAME) 116 | add_library(WDK::${LIBRARY_NAME} INTERFACE IMPORTED) 117 | set_property(TARGET WDK::${LIBRARY_NAME} PROPERTY INTERFACE_LINK_LIBRARIES ${LIBRARY}) 118 | endforeach(LIBRARY) 119 | 120 | unset(WDK_LIBRARIES) 121 | 122 | function(wdk_add_driver _target) 123 | cmake_parse_arguments(WDK "" "KMDF;WINVER" "" ${ARGN}) 124 | 125 | add_executable(${_target} ${WDK_UNPARSED_ARGUMENTS}) 126 | 127 | set_target_properties(${_target} PROPERTIES SUFFIX ".sys") 128 | set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}") 129 | set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS 130 | "${WDK_COMPILE_DEFINITIONS};$<$:${WDK_COMPILE_DEFINITIONS_DEBUG}>;_WIN32_WINNT=${WDK_WINVER}" 131 | ) 132 | set_target_properties(${_target} PROPERTIES LINK_FLAGS "${WDK_LINK_FLAGS}") 133 | 134 | target_include_directories(${_target} SYSTEM PRIVATE 135 | "${WDK_ROOT}/Include/${WDK_VERSION}/shared" 136 | "${WDK_ROOT}/Include/${WDK_VERSION}/km" 137 | "${WDK_ROOT}/Include/${WDK_VERSION}/km/crt" 138 | ) 139 | 140 | target_link_libraries(${_target} 141 | WDK::NTOSKRNL 142 | WDK::HAL 143 | WDK::WMILIB 144 | 145 | $<$:WDK::BUFFEROVERFLOWFASTFAILK> 146 | $<$:WDK::BUFFEROVERFLOWK> 147 | $<$:WDK::BUFFEROVERFLOWK WDK::MEMCMP> 148 | ) 149 | 150 | if(DEFINED WDK_KMDF) 151 | target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/kmdf/${WDK_KMDF}") 152 | target_link_libraries(${_target} 153 | "${WDK_ROOT}/Lib/wdf/kmdf/${WDK_PLATFORM}/${WDK_KMDF}/WdfDriverEntry.lib" 154 | "${WDK_ROOT}/Lib/wdf/kmdf/${WDK_PLATFORM}/${WDK_KMDF}/WdfLdr.lib" 155 | ) 156 | 157 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 158 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:FxDriverEntry@8") 159 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 160 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:FxDriverEntry") 161 | endif() 162 | else() 163 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 164 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:GsDriverEntry@8") 165 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 166 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:GsDriverEntry") 167 | endif() 168 | endif() 169 | endfunction() 170 | 171 | function(wdk_add_library _target) 172 | cmake_parse_arguments(WDK "" "KMDF;WINVER" "" ${ARGN}) 173 | 174 | add_library(${_target} ${WDK_UNPARSED_ARGUMENTS}) 175 | 176 | set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}") 177 | set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS 178 | "${WDK_COMPILE_DEFINITIONS};$<$:${WDK_COMPILE_DEFINITIONS_DEBUG};>_WIN32_WINNT=${WDK_WINVER}" 179 | ) 180 | 181 | target_include_directories(${_target} SYSTEM PRIVATE 182 | "${WDK_ROOT}/Include/${WDK_VERSION}/shared" 183 | "${WDK_ROOT}/Include/${WDK_VERSION}/km" 184 | "${WDK_ROOT}/Include/${WDK_VERSION}/km/crt" 185 | ) 186 | 187 | if(DEFINED WDK_KMDF) 188 | target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/kmdf/${WDK_KMDF}") 189 | endif() 190 | endfunction() 191 | -------------------------------------------------------------------------------- /Driver/Source/DriverUtils.cpp: -------------------------------------------------------------------------------- 1 | #define CFB_NS "[CFB::Driver::Utils]" 2 | 3 | #include "DriverUtils.hpp" 4 | 5 | 6 | /// 7 | /// @brief Basic allocator/deallocator for the kernel 8 | /// 9 | /// 10 | void* __cdecl 11 | operator new(usize Size, POOL_TYPE PoolType, ULONG PoolTag) 12 | { 13 | void* Memory = ::ExAllocatePoolWithTag(PoolType, Size, PoolTag); 14 | dbg("new(Size=%d, Type=%d, Tag=%x)=%p", Size, PoolType, PoolTag, Memory); 15 | return Memory; 16 | } 17 | 18 | void __cdecl 19 | operator delete(void* Memory, usize Size) 20 | { 21 | dbg("delete(Memory=%p, Size=%d)", Memory, Size); 22 | ::ExFreePoolWithTag(Memory, 0); 23 | } 24 | 25 | void* __cdecl 26 | operator new[](size_t Size, POOL_TYPE PoolType) 27 | { 28 | return operator new(Size, PoolType); 29 | } 30 | 31 | void __cdecl 32 | operator delete[](void* Memory, usize Size) 33 | { 34 | return operator delete(Memory, Size); 35 | } 36 | 37 | 38 | /// 39 | /// @brief This namespace contains the kernel specific utility functions and classes 40 | /// 41 | 42 | 43 | namespace CFB::Driver::Utils 44 | { 45 | 46 | #pragma region Logging helper functions 47 | const char* 48 | ToString(KIRQL const Level) 49 | { 50 | switch ( Level ) 51 | { 52 | case PASSIVE_LEVEL: 53 | return "PASSIVE_LEVEL"; 54 | case APC_LEVEL: 55 | return "APC_LEVEL"; 56 | case DISPATCH_LEVEL: 57 | return "DISPATCH_LEVEL"; 58 | #ifdef CMCI_LEVEL 59 | case CMCI_LEVEL: 60 | return "CMCI_LEVEL"; 61 | #endif // CMCI_LEVEL 62 | case CLOCK_LEVEL: 63 | return "CLOCK_LEVEL"; 64 | case POWER_LEVEL: 65 | return "POWER_LEVEL"; 66 | case HIGH_LEVEL: 67 | return "HIGH_LEVEL"; 68 | } 69 | return "INVALID_LEVEL"; 70 | } 71 | #pragma endregion 72 | 73 | #pragma region KUnicodeString 74 | 75 | KUnicodeString::KUnicodeString(const wchar_t* src, const u16 srcsz, const POOL_TYPE type) : 76 | m_UnicodeString {}, 77 | m_StringBuffer {KAlloc(sizeof(wchar_t) + srcsz, CFB_DEVICE_TAG, type)} 78 | { 79 | m_UnicodeString.Buffer = m_StringBuffer.get(); 80 | m_UnicodeString.Length = srcsz; 81 | m_UnicodeString.MaximumLength = srcsz + sizeof(wchar_t); 82 | 83 | ::memset(m_StringBuffer.get(), 0, capacity()); 84 | ::memcpy(m_StringBuffer.get(), src, srcsz); 85 | 86 | dbg("KUnicodeString::KUnicodeString((wchar_t*)L'%wZ', length=%lluB, capacity=%lluB)", 87 | &m_UnicodeString, 88 | size(), 89 | capacity()); 90 | } 91 | 92 | KUnicodeString::KUnicodeString(PUNICODE_STRING&& src, const POOL_TYPE type) : m_UnicodeString {*src}, m_StringBuffer {} 93 | { 94 | dbg("KUnicodeString::KUnicodeString((&&)L'%wZ', length=%lluB, capacity=%lluB)", 95 | &m_UnicodeString, 96 | size(), 97 | capacity()); 98 | } 99 | 100 | 101 | KUnicodeString::KUnicodeString(PUNICODE_STRING const& src, const POOL_TYPE type) : 102 | m_UnicodeString {}, 103 | m_StringBuffer {KAlloc(src->MaximumLength, CFB_DEVICE_TAG, type)} 104 | { 105 | ::memcpy(m_StringBuffer.get(), src->Buffer, MIN(src->Length, src->MaximumLength)); 106 | m_UnicodeString.Buffer = m_StringBuffer.get(); 107 | m_UnicodeString.Length = src->Length; 108 | m_UnicodeString.MaximumLength = src->MaximumLength; 109 | 110 | dbg("KUnicodeString::KUnicodeString((const&)L'%wZ', length=%lluB, capacity=%lluB)", 111 | &m_UnicodeString, 112 | size(), 113 | capacity()); 114 | } 115 | 116 | 117 | KUnicodeString::~KUnicodeString() 118 | { 119 | dbg("KUnicodeString::~KUnicodeString(%p)", m_UnicodeString); 120 | } 121 | 122 | 123 | KUnicodeString::KUnicodeString(const KUnicodeString& other) 124 | { 125 | m_StringBuffer = KAlloc(other.capacity()); 126 | ::memcpy(m_StringBuffer.get(), other.data(), other.size()); 127 | 128 | m_UnicodeString.Buffer = m_StringBuffer.get(); 129 | m_UnicodeString.Length = other.size(); 130 | m_UnicodeString.MaximumLength = other.capacity(); 131 | } 132 | 133 | KUnicodeString& 134 | KUnicodeString::operator=(const KUnicodeString& other) noexcept 135 | { 136 | if ( this != &other ) 137 | { 138 | m_StringBuffer = KAlloc(other.capacity()); 139 | ::memcpy(m_StringBuffer.get(), other.get(), other.size()); 140 | 141 | m_UnicodeString.Buffer = m_StringBuffer.get(); 142 | m_UnicodeString.Length = other.size(); 143 | m_UnicodeString.MaximumLength = other.capacity(); 144 | } 145 | return *this; 146 | } 147 | 148 | 149 | KUnicodeString& 150 | KUnicodeString::operator=(KUnicodeString&& other) noexcept 151 | { 152 | if ( this != &other ) 153 | { 154 | m_StringBuffer = static_cast&&>(other.m_StringBuffer); 155 | ::memcpy(&m_UnicodeString, other.get(), sizeof(UNICODE_STRING)); 156 | ::memset(&other.m_UnicodeString, 0, sizeof(UNICODE_STRING)); 157 | } 158 | return *this; 159 | } 160 | 161 | 162 | bool 163 | KUnicodeString::operator==(KUnicodeString const& other) 164 | { 165 | return size() == other.size() && ::RtlCompareUnicodeString(get(), other.get(), true) == 0; 166 | } 167 | 168 | 169 | bool 170 | KUnicodeString::operator==(PUNICODE_STRING const& other) 171 | { 172 | return size() == other->Length && ::RtlCompareUnicodeString(get(), other, true) == 0; 173 | } 174 | 175 | 176 | const wchar_t* 177 | KUnicodeString::data() const 178 | { 179 | return m_UnicodeString.Buffer; 180 | } 181 | 182 | 183 | const PUNICODE_STRING 184 | KUnicodeString::get() const 185 | { 186 | return const_cast(&m_UnicodeString); 187 | } 188 | 189 | 190 | const usize 191 | KUnicodeString::size() const 192 | { 193 | return m_UnicodeString.Length; 194 | } 195 | 196 | 197 | const usize 198 | KUnicodeString::capacity() const 199 | { 200 | return m_UnicodeString.MaximumLength; 201 | } 202 | 203 | #pragma endregion KUnicodeString 204 | 205 | #pragma region KMutex 206 | KMutex::KMutex() 207 | { 208 | ::KeInitializeMutex(&_mutex, 0); 209 | } 210 | 211 | KMutex::~KMutex() 212 | { 213 | } 214 | 215 | void 216 | KMutex::Lock() 217 | { 218 | ::KeWaitForSingleObject(&_mutex, Executive, KernelMode, false, nullptr); 219 | } 220 | 221 | void 222 | KMutex::Unlock() 223 | { 224 | if ( !::KeReleaseMutex(&_mutex, true) ) 225 | ::KeWaitForSingleObject(&_mutex, Executive, KernelMode, false, nullptr); 226 | } 227 | #pragma endregion 228 | 229 | #pragma region KFastMutex 230 | KFastMutex::KFastMutex() 231 | { 232 | ::ExInitializeFastMutex(&_mutex); 233 | } 234 | 235 | KFastMutex::~KFastMutex() 236 | { 237 | } 238 | 239 | void 240 | KFastMutex::Lock() 241 | { 242 | ::ExAcquireFastMutex(&_mutex); 243 | } 244 | 245 | void 246 | KFastMutex::Unlock() 247 | { 248 | ::ExReleaseFastMutex(&_mutex); 249 | } 250 | 251 | #pragma endregion 252 | 253 | #pragma region KCriticalRegion 254 | 255 | 256 | KCriticalRegion::KCriticalRegion() 257 | { 258 | ::ExInitializeResourceLite(&_mutex); 259 | } 260 | 261 | 262 | KCriticalRegion::~KCriticalRegion() 263 | { 264 | ::ExDeleteResourceLite(&_mutex); 265 | } 266 | 267 | void 268 | KCriticalRegion::Lock() 269 | { 270 | (void)::ExEnterCriticalRegionAndAcquireResourceExclusive(&_mutex); 271 | } 272 | 273 | 274 | void 275 | KCriticalRegion::Unlock() 276 | { 277 | ::ExReleaseResourceAndLeaveCriticalRegion(&_mutex); 278 | } 279 | 280 | 281 | #pragma endregion 282 | 283 | #pragma region KSpinLock 284 | KSpinLock::KSpinLock() 285 | { 286 | KeInitializeSpinLock(&_SpinLock); 287 | } 288 | 289 | KSpinLock::~KSpinLock() 290 | { 291 | } 292 | 293 | void 294 | KSpinLock::Lock() 295 | { 296 | KeAcquireSpinLock(&_SpinLock, &_OldIrql); 297 | } 298 | 299 | void 300 | KSpinLock::Unlock() 301 | { 302 | ::KeReleaseSpinLock(&_SpinLock, _OldIrql); 303 | } 304 | #pragma endregion 305 | 306 | #pragma region KQueuedSpinLock 307 | KQueuedSpinLock::KQueuedSpinLock() 308 | { 309 | ::KeInitializeSpinLock(&_SpinLock); 310 | } 311 | 312 | KQueuedSpinLock::~KQueuedSpinLock() 313 | { 314 | } 315 | 316 | void 317 | KQueuedSpinLock::Lock() 318 | { 319 | ::KeAcquireInStackQueuedSpinLock(&_SpinLock, &_LockQueueHandle); 320 | } 321 | 322 | 323 | void 324 | KQueuedSpinLock::Unlock() 325 | { 326 | ::KeReleaseInStackQueuedSpinLock(&_LockQueueHandle); 327 | } 328 | #pragma endregion 329 | 330 | 331 | } // namespace CFB::Driver::Utils 332 | --------------------------------------------------------------------------------