├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── update.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── amalgamate.py ├── out └── CMakeLists.txt ├── phnt_amalgamate.h ├── test ├── CMakeLists.txt ├── cmake │ ├── FindWDK.cmake │ └── TestSigning.pfx ├── driver.cpp ├── test.c ├── test.cpp └── test_impl.h └── update.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [mrexodia] -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | jobs: 6 | build: 7 | # Skip building pull requests from the same repository 8 | if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository }} 9 | runs-on: windows-latest 10 | permissions: 11 | contents: write 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | submodules: true 17 | 18 | - name: Amalgamate headers 19 | run: python amalgamate.py 20 | 21 | - name: Upload artifact 22 | uses: actions/upload-artifact@v4 23 | with: 24 | name: ${{ github.event.repository.name }}-${{ github.sha }} 25 | path: out/ 26 | 27 | - uses: TheMrMilchmann/setup-msvc-dev@fb19abb8a41b3cf0340f5d1be17d420309232be6 # v3.0.1 28 | with: 29 | arch: x64 30 | 31 | - name: Test (add_subdirectory) 32 | run: | 33 | cmake -B build -S test -G Ninja 34 | cmake --build build 35 | echo Running test-c.exe 36 | .\build\test-c.exe 37 | echo Running test-cpp.exe 38 | .\build\test-cpp.exe 39 | 40 | - name: Test (find_package) 41 | run: | 42 | cmake -B build-pkg -S test -G Ninja -DPHNT_TEST_PACKAGE=ON 43 | cmake --build build-pkg 44 | echo Running test-c.exe 45 | .\build-pkg\test-c.exe 46 | echo Running test-cpp.exe 47 | .\build-pkg\test-cpp.exe 48 | 49 | - name: Compress artifacts 50 | uses: vimtor/action-zip@26a249fb00d43ca98dad77a4b3838025fc226aa1 # v1.1 51 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 52 | with: 53 | files: out/ 54 | dest: phnt.zip 55 | 56 | - name: Release 57 | uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 58 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 59 | with: 60 | files: | 61 | out/phnt.h 62 | phnt.zip 63 | generate_release_notes: true 64 | env: 65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 66 | -------------------------------------------------------------------------------- /.github/workflows/update.yml: -------------------------------------------------------------------------------- 1 | name: Update systeminformer 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 15 * * 5' 7 | 8 | permissions: 9 | pull-requests: write 10 | 11 | jobs: 12 | update: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | submodules: true 19 | fetch-depth: 0 20 | ssh-key: ${{ secrets.UPDATE_SSH_KEY }} 21 | 22 | - name: Bump the systeminformer version 23 | run: ./update.sh 24 | shell: bash 25 | env: 26 | GH_TOKEN: ${{ github.token }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out/* 2 | !/out/CMakeLists.txt 3 | /cpp-amalgamate.exe 4 | build*/ 5 | /test/phnt.h 6 | /*.zip -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "systeminformer"] 2 | path = systeminformer 3 | url = https://github.com/winsiderss/systeminformer 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Duncan Ogilvie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phnt-single-header 2 | 3 | This repository automatically generates a single-header version of [System Informer](https://github.com/winsiderss/systeminformer)'s [phnt](https://github.com/winsiderss/systeminformer/tree/master/phnt) library. This repository was created because the original library is separated in many headers and can be annoying to integrate into your project. 4 | 5 | ## Usage 6 | 7 | This is a simple example of using phnt 8 | 9 | ```c 10 | #define PHNT_VERSION PHNT_WIN11 11 | #include "phnt.h" // Instead of Windows.h 12 | 13 | // Imports for ntdll.dll 14 | #pragma comment(lib, "ntdll.lib") 15 | 16 | static char message[] = "Hello, phnt!\r\n"; 17 | 18 | int main() 19 | { 20 | IO_STATUS_BLOCK IoStatusBlock = { 0, 0 }; 21 | NtWriteFile( 22 | NtCurrentPeb()->ProcessParameters->StandardOutput, 23 | NULL, 24 | NULL, 25 | NULL, 26 | &IoStatusBlock, 27 | message, 28 | strlen(message) - 1, 29 | NULL, 30 | NULL 31 | ); 32 | return 0; 33 | } 34 | ``` 35 | 36 | ## Download 37 | 38 | [`phnt.h`](https://github.com/mrexodia/phnt-single-header/releases/latest/download/phnt.h) (direct link to the [latest release](https://github.com/mrexodia/phnt-single-header/releases/latest)). 39 | 40 | ## CMake 41 | 42 | To quickly use this library from CMake, use [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html): 43 | 44 | ```cmake 45 | cmake_minimum_required(VERSION 3.24) 46 | cmake_policy(SET CMP0135 NEW) 47 | project(phnt-example) 48 | 49 | include(FetchContent) 50 | set(phnt_TAG "v1.4-ed73b907") 51 | message(STATUS "Fetching phnt (${phnt_TAG})...") 52 | FetchContent_Declare(phnt 53 | URL "https://github.com/mrexodia/phnt-single-header/releases/download/${phnt_TAG}/phnt.zip" 54 | URL_HASH "SHA256=a41def8d91204dc8c1d322a9d20b5fa107f99138ed0ad8bf52d6353137000dd5" 55 | ) 56 | FetchContent_MakeAvailable(phnt) 57 | 58 | add_executable(example main.cpp) 59 | target_link_libraries(example PRIVATE phnt::phnt) 60 | ``` 61 | 62 | Instead of `FetchContent` you can also extract [`phnt.zip`](https://github.com/mrexodia/phnt-single-header/releases/latest/download/phnt.zip) to `third_party/phnt` in your project and do: 63 | 64 | ```cmake 65 | add_subdirectory(third_party/phnt) 66 | ``` 67 | 68 | The target `phnt::phnt` also links to `ntdll.lib`. If you want to avoid this you can link to `phnt::headers` instead. 69 | 70 | _Note_: The CMake project in `phnt.zip` also works as a CMake package. After configuring and installing it, you can do `find_package(phnt REQUIRED)` and everything should work out of the box. 71 | 72 | ## Older SDKs 73 | 74 | To use phnt with older SDK versions, change the `PHNT_VERSION` to one of the following: 75 | 76 | ``` 77 | #define PHNT_VERSION PHNT_WIN2K 78 | #define PHNT_VERSION PHNT_WINXP 79 | #define PHNT_VERSION PHNT_WS03 80 | #define PHNT_VERSION PHNT_VISTA 81 | #define PHNT_VERSION PHNT_WIN7 82 | #define PHNT_VERSION PHNT_WIN8 83 | #define PHNT_VERSION PHNT_WINBLUE 84 | #define PHNT_VERSION PHNT_THRESHOLD 85 | #define PHNT_VERSION PHNT_THRESHOLD2 86 | #define PHNT_VERSION PHNT_REDSTONE 87 | #define PHNT_VERSION PHNT_REDSTONE2 88 | #define PHNT_VERSION PHNT_REDSTONE3 89 | #define PHNT_VERSION PHNT_REDSTONE4 90 | #define PHNT_VERSION PHNT_REDSTONE5 91 | #define PHNT_VERSION PHNT_19H1 92 | #define PHNT_VERSION PHNT_19H2 93 | #define PHNT_VERSION PHNT_20H1 94 | #define PHNT_VERSION PHNT_20H2 95 | #define PHNT_VERSION PHNT_21H1 96 | #define PHNT_VERSION PHNT_WIN10_21H2 97 | #define PHNT_VERSION PHNT_WIN10_22H2 98 | #define PHNT_VERSION PHNT_WIN11 99 | #define PHNT_VERSION PHNT_WIN11_22H2 100 | ``` 101 | -------------------------------------------------------------------------------- /amalgamate.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import hashlib 4 | import subprocess 5 | import urllib.request 6 | 7 | def exec(command, working_directory=None): 8 | result = subprocess.run( 9 | command, 10 | shell=True, 11 | cwd=working_directory 12 | ) 13 | if result.returncode != 0: 14 | raise Exception(f"Command '{command}' failed with exit code {result.returncode}") 15 | 16 | def download_file(url, save_path): 17 | with urllib.request.urlopen(url) as response: 18 | with open(save_path, 'wb') as file: 19 | file.write(response.read()) 20 | 21 | def sha256(file_path): 22 | hash = hashlib.sha256() 23 | with open(file_path, "rb") as file: 24 | # Read the file in chunks to handle large files efficiently 25 | chunk = 0 26 | while chunk := file.read(8192): 27 | hash.update(chunk) 28 | return hash.hexdigest() 29 | 30 | def main(): 31 | # Sanity checks 32 | if not sys.platform.startswith("win"): 33 | raise Exception("This script only works on Windows...") 34 | if not os.path.exists(".git"): 35 | raise Exception("Clone this repository with git to use it...") 36 | 37 | # Get the current System Informer commit hash 38 | if not os.path.exists("systeminformer"): 39 | exec("git submodule update --init") 40 | commit = subprocess.run( 41 | "git rev-parse HEAD", 42 | stdout=subprocess.PIPE, 43 | text=True, 44 | shell=True, 45 | cwd="systeminformer" 46 | ) 47 | if commit.returncode != 0: 48 | raise Exception(f"Failed to get commit hash, exit code {commit.returncode}") 49 | commit_hash = commit.stdout.strip() 50 | 51 | # Download cpp-amalgamate 52 | cpp_amalgamate = os.path.join(os.getcwd(), "cpp-amalgamate.exe") 53 | if not os.path.exists(cpp_amalgamate): 54 | print(f"Downloading cpp-amalgamate...") 55 | download_file("https://github.com/Felerius/cpp-amalgamate/releases/download/1.0.1/cpp-amalgamate-x86_64-pc-windows-gnu.exe", cpp_amalgamate) 56 | actual_hash = sha256(cpp_amalgamate) 57 | expected_hash = "cdb689a610b67f267a1b28733f975431083150f6cd01adfd5914f989508b0522" 58 | if actual_hash != expected_hash: 59 | raise Exception(f"cpp-amalgamate.exe hash mismatch (actual: {actual_hash}, expected: {expected_hash})") 60 | 61 | # Extract System Informer license 62 | license_file = "systeminformer/LICENSE.txt" 63 | if not os.path.exists(license_file): 64 | raise Exception(f"License file not found: {license_file}") 65 | with open(license_file, "r") as f: 66 | license = f.read().strip() 67 | 68 | # Create output folder 69 | os.makedirs("out", exist_ok=True) 70 | with open("out/LICENSE", "w") as f: 71 | f.write(license) 72 | out_path = "out/phnt.h" 73 | exec(f"\"{cpp_amalgamate}\" -d systeminformer/phnt/include phnt_amalgamate.h -o \"{out_path}\"") 74 | with open(out_path, "r") as f: 75 | header = f.read() 76 | with open(out_path, "w") as f: 77 | f.write("/*\n\n") 78 | f.write("+===========================================================+\n") 79 | f.write("| THIS FILE WAS AUTOMATICALLY GENERATED |\n") 80 | f.write("+===========================================================+\n") 81 | f.write("| Source: https://github.com/winsiderss/systeminformer |\n") 82 | f.write(f"| Commit: {commit_hash} |\n") 83 | f.write("| Generator: https://github.com/mrexodia/phnt-single-header |\n") 84 | f.write("+===========================================================+\n\n") 85 | f.write(license) 86 | f.write("\n*/\n\n") 87 | f.write(header) 88 | 89 | if __name__ == "__main__": 90 | main() 91 | -------------------------------------------------------------------------------- /out/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) 4 | message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build") 5 | endif() 6 | 7 | set(PROJECT_IS_TOP_LEVEL OFF) 8 | if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 9 | set(PROJECT_IS_TOP_LEVEL ON) 10 | endif() 11 | 12 | project(phnt VERSION 3.0) 13 | 14 | if(PROJECT_IS_TOP_LEVEL) 15 | set(tgt_headers headers) 16 | else() 17 | set(tgt_headers phnt_headers) 18 | endif() 19 | 20 | add_library(${tgt_headers} INTERFACE) 21 | add_library(${PROJECT_NAME}::headers ALIAS ${tgt_headers}) 22 | target_include_directories(${tgt_headers} SYSTEM INTERFACE 23 | $ 24 | ) 25 | target_include_directories(${tgt_headers} INTERFACE 26 | $/include> 27 | ) 28 | 29 | add_library(phnt INTERFACE) 30 | add_library(${PROJECT_NAME}::phnt ALIAS phnt) 31 | target_link_libraries(phnt INTERFACE phnt::headers) 32 | 33 | if(MSVC) 34 | target_link_libraries(phnt INTERFACE "ntdll.lib") 35 | endif() 36 | 37 | # References: 38 | # - https://dominikberner.ch/cmake-interface-lib/ 39 | # - https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html 40 | if(PROJECT_IS_TOP_LEVEL) 41 | include(GNUInstallDirs) 42 | install( 43 | TARGETS 44 | phnt 45 | ${tgt_headers} 46 | EXPORT 47 | ${PROJECT_NAME}_Targets 48 | ARCHIVE 49 | DESTINATION ${CMAKE_INSTALL_LIBDIR} 50 | LIBRARY 51 | DESTINATION ${CMAKE_INSTALL_LIBDIR} 52 | RUNTIME 53 | DESTINATION ${CMAKE_INSTALL_BINDIR} 54 | ) 55 | include(CMakePackageConfigHelpers) 56 | write_basic_package_version_file( 57 | "${PROJECT_NAME}ConfigVersion.cmake" 58 | VERSION 59 | ${PROJECT_VERSION} 60 | COMPATIBILITY 61 | SameMajorVersion 62 | ) 63 | set(CONFIG_IN_CONTENTS "@PACKAGE_INIT@\n\ninclude(\"\$\{CMAKE_CURRENT_LIST_DIR\}/@PROJECT_NAME@Targets.cmake\")\n\ncheck_required_components(\"@PROJECT_NAME@\")") 64 | set(CONFIG_IN_PATH "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in") 65 | file(WRITE "${CONFIG_IN_PATH}" "${CONFIG_IN_CONTENTS}") 66 | configure_package_config_file( 67 | "${CONFIG_IN_PATH}" "${PROJECT_NAME}Config.cmake" 68 | INSTALL_DESTINATION 69 | "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 70 | ) 71 | install( 72 | EXPORT 73 | ${PROJECT_NAME}_Targets 74 | FILE 75 | "${PROJECT_NAME}Targets.cmake" 76 | NAMESPACE 77 | ${PROJECT_NAME}:: 78 | DESTINATION 79 | "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 80 | ) 81 | install( 82 | FILES 83 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 84 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 85 | DESTINATION 86 | "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 87 | ) 88 | install( 89 | FILES 90 | "${PROJECT_SOURCE_DIR}/phnt.h" 91 | DESTINATION 92 | "${CMAKE_INSTALL_INCLUDEDIR}" 93 | ) 94 | endif() 95 | -------------------------------------------------------------------------------- /phnt_amalgamate.h: -------------------------------------------------------------------------------- 1 | #ifndef _PHNT_AMALGAMATE_H 2 | #define _PHNT_AMALGAMATE_H 3 | 4 | #ifdef _WINTERNL_ 5 | #error Do not mix Winternl.h and phnt.h 6 | #endif // _WINTERNL_ 7 | #define _WINTERNL_ // Pretend the header was included 8 | 9 | #ifdef _KERNEL_MODE 10 | #define PHNT_DETECTED_MODE PHNT_MODE_KERNEL 11 | #else 12 | #define PHNT_DETECTED_MODE PHNT_MODE_USER 13 | #include 14 | #endif // _KERNEL_MODE 15 | 16 | #ifndef PHNT_MODE 17 | #define PHNT_MODE PHNT_DETECTED_MODE 18 | #endif // PHNT_MODE 19 | 20 | #include 21 | 22 | #endif // _PHNT_AMALGAMATE_H 23 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # NOTE: This code is hacky because it's used for tests. 2 | # If you are looking for an example, check the README. 3 | 4 | cmake_minimum_required(VERSION 3.15) 5 | 6 | # Check that amalgamate.py was run 7 | set(phnt_DIR "${CMAKE_SOURCE_DIR}/../out") 8 | if(NOT EXISTS "${phnt_DIR}/phnt.h") 9 | message(FATAL_ERROR "Could not find ../out/phnt.h, to generate: python amalgamate.py") 10 | endif() 11 | 12 | # Default to a Release build 13 | get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 14 | if(GENERATOR_IS_MULTI_CONFIG) 15 | set(CMAKE_CONFIGURATION_TYPES "Release" CACHE STRING "") 16 | elseif(NOT DEFINED CACHE{CMAKE_BUILD_TYPE}) 17 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "") 18 | endif() 19 | 20 | option(PHNT_TEST_PACKAGE "Test package mode" OFF) 21 | 22 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 23 | 24 | project(phnt-test) 25 | 26 | if(PHNT_TEST_PACKAGE) 27 | set(PACKAGE_DIR "${PROJECT_BINARY_DIR}/phnt") 28 | file(REMOVE_RECURSE "${PACKAGE_DIR}") 29 | message(STATUS "Configuring phnt package...") 30 | execute_process( 31 | COMMAND "${CMAKE_COMMAND}" -B "${PACKAGE_DIR}/build" -S "${phnt_DIR}" -G Ninja "-DCMAKE_BUILD_TYPE=Release" 32 | RESULT_VARIABLE PACKAGE_STATUS 33 | ) 34 | if(NOT PACKAGE_STATUS EQUAL 0) 35 | message(FATAL_ERROR "Failed to configure phnt package!") 36 | endif() 37 | message(STATUS "Installing phnt package...") 38 | execute_process( 39 | COMMAND "${CMAKE_COMMAND}" --install "${PACKAGE_DIR}/build" --prefix "${PACKAGE_DIR}/install" 40 | RESULT_VARIABLE PACKAGE_STATUS 41 | ) 42 | if(NOT PACKAGE_STATUS EQUAL 0) 43 | message(FATAL_ERROR "Failed to install phnt package!") 44 | endif() 45 | set(CMAKE_PREFIX_PATH "${PACKAGE_DIR}/install") 46 | find_package(phnt REQUIRED) 47 | else() 48 | add_subdirectory("${phnt_DIR}" "${PROJECT_BINARY_DIR}/phnt") 49 | endif() 50 | 51 | function(phnt_test target) 52 | add_executable(${target} ${ARGN}) 53 | target_link_libraries(${target} PRIVATE phnt::phnt) 54 | target_link_options(${target} PRIVATE "/NODEFAULTLIB") 55 | target_link_options(${target} PRIVATE "/ENTRY:EntryPoint") 56 | endfunction() 57 | 58 | # User mode tests 59 | phnt_test(test-c test.c) 60 | phnt_test(test-cpp test.cpp) 61 | 62 | # Kernel mode test 63 | find_package(WDK REQUIRED) 64 | wdk_add_driver(test-driver driver.cpp) 65 | target_link_libraries(test-driver PRIVATE phnt::headers) 66 | -------------------------------------------------------------------------------- /test/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 | # - `WDK_NTDDI_VERSION` -- the NTDDI_VERSION used for kernel drivers and libraries, 18 | # if not set, the value will be automatically calculated by WINVER 19 | # (default value is left blank and can be changed per target or globally) 20 | # 21 | # Example usage: 22 | # 23 | # find_package(WDK REQUIRED) 24 | # 25 | # wdk_add_library(KmdfCppLib STATIC KMDF 1.15 26 | # KmdfCppLib.h 27 | # KmdfCppLib.cpp 28 | # ) 29 | # target_include_directories(KmdfCppLib INTERFACE .) 30 | # 31 | # wdk_add_driver(KmdfCppDriver KMDF 1.15 32 | # Main.cpp 33 | # ) 34 | # target_link_libraries(KmdfCppDriver KmdfCppLib) 35 | # 36 | 37 | # Thanks to yousif for this trick with the registry! 38 | # Query Windows SDK root directory. 39 | get_filename_component(WDK_ROOT 40 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot10]" 41 | ABSOLUTE) 42 | 43 | # Find all ntdkk.h files, then sort for the latest. 44 | file(GLOB WDK_NTDDK_FILES ${WDK_ROOT}/Include/*/km/ntddk.h) 45 | 46 | if(WDK_NTDDK_FILES) 47 | if (NOT CMAKE_VERSION VERSION_LESS 3.18.0) 48 | list(SORT WDK_NTDDK_FILES COMPARE NATURAL) # sort to use the latest available WDK 49 | endif() 50 | list(GET WDK_NTDDK_FILES -1 WDK_LATEST_NTDDK_FILE) 51 | endif() 52 | 53 | include(FindPackageHandleStandardArgs) 54 | find_package_handle_standard_args(WDK REQUIRED_VARS WDK_LATEST_NTDDK_FILE) 55 | 56 | if (NOT WDK_LATEST_NTDDK_FILE) 57 | return() 58 | endif() 59 | 60 | get_filename_component(WDK_ROOT ${WDK_LATEST_NTDDK_FILE} DIRECTORY) 61 | get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) 62 | get_filename_component(WDK_VERSION ${WDK_ROOT} NAME) 63 | get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) 64 | if (NOT WDK_ROOT MATCHES ".*/[0-9][0-9.]*$") # WDK 10 has a deeper nesting level 65 | get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) # go up once more 66 | set(WDK_LIB_VERSION "${WDK_VERSION}") 67 | set(WDK_INC_VERSION "${WDK_VERSION}") 68 | else() # WDK 8.0, 8.1 69 | set(WDK_INC_VERSION "") 70 | foreach(VERSION winv6.3 win8 win7) 71 | if (EXISTS "${WDK_ROOT}/Lib/${VERSION}/") 72 | set(WDK_LIB_VERSION "${VERSION}") 73 | break() 74 | endif() 75 | endforeach() 76 | set(WDK_VERSION "${WDK_LIB_VERSION}") 77 | endif() 78 | 79 | message(STATUS "WDK_ROOT: ${WDK_ROOT}") 80 | message(STATUS "WDK_VERSION: ${WDK_VERSION}") 81 | 82 | find_program(WDK_SIGNTOOL signtool 83 | HINTS "${WDK_ROOT}/bin" 84 | PATH_SUFFIXES 85 | "${WDK_VERSION}/x64" 86 | "${WDK_VERSION}/x86" 87 | "x64" 88 | "x86" 89 | REQUIRED 90 | ) 91 | message(STATUS "WDK_SIGNTOOL: ${WDK_SIGNTOOL}") 92 | 93 | get_filename_component(PACKAGE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 94 | set(WDK_PFX "${PACKAGE_DIR}/TestSigning.pfx" CACHE STRING "Private key used for signing the driver") 95 | if(NOT EXISTS "${WDK_PFX}") 96 | message(FATAL_ERROR "PFX not found: ${WDK_PFX}") 97 | else() 98 | message(STATUS "PFX: ${WDK_PFX}") 99 | endif() 100 | 101 | set(WDK_WINVER "0x0601" CACHE STRING "Default WINVER for WDK targets") 102 | set(WDK_NTDDI_VERSION "" CACHE STRING "Specified NTDDI_VERSION for WDK targets if needed") 103 | 104 | set(WDK_ADDITIONAL_FLAGS_FILE "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/wdkflags.h") 105 | file(WRITE ${WDK_ADDITIONAL_FLAGS_FILE} "#pragma runtime_checks(\"suc\", off)") 106 | 107 | set(WDK_COMPILE_FLAGS 108 | "/Zp8" # set struct alignment 109 | "/GF" # enable string pooling 110 | "/GR-" # disable RTTI 111 | "/Gz" # __stdcall by default 112 | "/kernel" # create kernel mode binary 113 | "/FIwarning.h" # disable warnings in WDK headers 114 | "/FI${WDK_ADDITIONAL_FLAGS_FILE}" # include file to disable RTC 115 | ) 116 | 117 | set(WDK_COMPILE_DEFINITIONS "WINNT=1") 118 | set(WDK_COMPILE_DEFINITIONS_DEBUG "MSC_NOOPT;DEPRECATE_DDK_FUNCTIONS=1;DBG=1") 119 | 120 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 121 | list(APPEND WDK_COMPILE_DEFINITIONS "_X86_=1;i386=1;STD_CALL") 122 | set(WDK_PLATFORM "x86") 123 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 124 | list(APPEND WDK_COMPILE_DEFINITIONS "_WIN64;_AMD64_;AMD64") 125 | set(WDK_PLATFORM "x64") 126 | else() 127 | message(FATAL_ERROR "Unsupported architecture") 128 | endif() 129 | 130 | string(CONCAT WDK_LINK_FLAGS 131 | "/MANIFEST:NO " # 132 | "/DRIVER " # 133 | "/OPT:REF " # 134 | "/INCREMENTAL:NO " # 135 | "/OPT:ICF " # 136 | "/SUBSYSTEM:NATIVE " # 137 | "/MERGE:_TEXT=.text;_PAGE=PAGE " # 138 | "/NODEFAULTLIB " # do not link default CRT 139 | "/SECTION:INIT,d " # 140 | "/VERSION:10.0 " # 141 | ) 142 | 143 | # Generate imported targets for WDK lib files 144 | file(GLOB WDK_LIBRARIES "${WDK_ROOT}/Lib/${WDK_LIB_VERSION}/km/${WDK_PLATFORM}/*.lib") 145 | foreach(LIBRARY IN LISTS WDK_LIBRARIES) 146 | get_filename_component(LIBRARY_NAME ${LIBRARY} NAME_WE) 147 | string(TOUPPER ${LIBRARY_NAME} LIBRARY_NAME) 148 | add_library(WDK::${LIBRARY_NAME} INTERFACE IMPORTED) 149 | set_property(TARGET WDK::${LIBRARY_NAME} PROPERTY INTERFACE_LINK_LIBRARIES ${LIBRARY}) 150 | endforeach(LIBRARY) 151 | unset(WDK_LIBRARIES) 152 | 153 | function(wdk_add_driver _target) 154 | cmake_parse_arguments(WDK "" "KMDF;WINVER;NTDDI_VERSION" "" ${ARGN}) 155 | 156 | add_executable(${_target} ${WDK_UNPARSED_ARGUMENTS}) 157 | 158 | set_target_properties(${_target} PROPERTIES SUFFIX ".sys") 159 | set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}") 160 | set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS 161 | "${WDK_COMPILE_DEFINITIONS};$<$:${WDK_COMPILE_DEFINITIONS_DEBUG}>;_WIN32_WINNT=${WDK_WINVER}" 162 | ) 163 | set_target_properties(${_target} PROPERTIES LINK_FLAGS "${WDK_LINK_FLAGS}") 164 | if(WDK_NTDDI_VERSION) 165 | target_compile_definitions(${_target} PRIVATE NTDDI_VERSION=${WDK_NTDDI_VERSION}) 166 | endif() 167 | 168 | target_include_directories(${_target} SYSTEM PRIVATE 169 | "${WDK_ROOT}/Include/${WDK_INC_VERSION}/shared" 170 | "${WDK_ROOT}/Include/${WDK_INC_VERSION}/km" 171 | "${WDK_ROOT}/Include/${WDK_INC_VERSION}/km/crt" 172 | ) 173 | 174 | target_link_libraries(${_target} PRIVATE WDK::NTOSKRNL WDK::HAL WDK::BUFFEROVERFLOWK WDK::WMILIB) 175 | 176 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 177 | target_link_libraries(${_target} WDK::MEMCMP) 178 | endif() 179 | 180 | if(DEFINED WDK_KMDF) 181 | target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/kmdf/${WDK_KMDF}") 182 | target_link_libraries(${_target} 183 | "${WDK_ROOT}/Lib/wdf/kmdf/${WDK_PLATFORM}/${WDK_KMDF}/WdfDriverEntry.lib" 184 | "${WDK_ROOT}/Lib/wdf/kmdf/${WDK_PLATFORM}/${WDK_KMDF}/WdfLdr.lib" 185 | ) 186 | 187 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 188 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:FxDriverEntry@8") 189 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 190 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:FxDriverEntry") 191 | endif() 192 | else() 193 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 194 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:GsDriverEntry@8") 195 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 196 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:GsDriverEntry") 197 | endif() 198 | endif() 199 | 200 | # Code signing 201 | add_custom_command( 202 | TARGET ${_target} POST_BUILD 203 | COMMAND "${WDK_SIGNTOOL}" sign /fd SHA256 /f "${WDK_PFX}" "$" 204 | VERBATIM 205 | ) 206 | endfunction() 207 | 208 | function(wdk_add_library _target) 209 | cmake_parse_arguments(WDK "" "KMDF;WINVER;NTDDI_VERSION" "" ${ARGN}) 210 | 211 | add_library(${_target} ${WDK_UNPARSED_ARGUMENTS}) 212 | 213 | set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}") 214 | set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS 215 | "${WDK_COMPILE_DEFINITIONS};$<$:${WDK_COMPILE_DEFINITIONS_DEBUG};>_WIN32_WINNT=${WDK_WINVER}" 216 | ) 217 | if(WDK_NTDDI_VERSION) 218 | target_compile_definitions(${_target} PRIVATE NTDDI_VERSION=${WDK_NTDDI_VERSION}) 219 | endif() 220 | 221 | target_include_directories(${_target} SYSTEM PRIVATE 222 | "${WDK_ROOT}/Include/${WDK_INC_VERSION}/shared" 223 | "${WDK_ROOT}/Include/${WDK_INC_VERSION}/km" 224 | "${WDK_ROOT}/Include/${WDK_INC_VERSION}/km/crt" 225 | ) 226 | 227 | if(DEFINED WDK_KMDF) 228 | target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/kmdf/${WDK_KMDF}") 229 | endif() 230 | endfunction() 231 | -------------------------------------------------------------------------------- /test/cmake/TestSigning.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrexodia/phnt-single-header/HEAD/test/cmake/TestSigning.pfx -------------------------------------------------------------------------------- /test/driver.cpp: -------------------------------------------------------------------------------- 1 | extern "C" 2 | { 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | } 13 | 14 | #define PHNT_VERSION PHNT_WIN11 15 | #include 16 | 17 | static UNICODE_STRING Win32Device; 18 | 19 | _Function_class_(DRIVER_UNLOAD) 20 | _IRQL_requires_(PASSIVE_LEVEL) 21 | _IRQL_requires_same_ 22 | static void DriverUnload(_In_ PDRIVER_OBJECT DriverObject) 23 | { 24 | IoDeleteSymbolicLink(&Win32Device); 25 | IoDeleteDevice(DriverObject->DeviceObject); 26 | } 27 | 28 | _Function_class_(DRIVER_DISPATCH) 29 | _IRQL_requires_max_(DISPATCH_LEVEL) 30 | _IRQL_requires_same_ 31 | static NTSTATUS DriverCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 32 | { 33 | UNREFERENCED_PARAMETER(DeviceObject); 34 | Irp->IoStatus.Status = STATUS_SUCCESS; 35 | Irp->IoStatus.Information = 0; 36 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 37 | return STATUS_SUCCESS; 38 | } 39 | 40 | _Function_class_(DRIVER_DISPATCH) 41 | _IRQL_requires_max_(DISPATCH_LEVEL) 42 | _IRQL_requires_same_ 43 | static NTSTATUS DriverDefaultHandler(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) 44 | { 45 | UNREFERENCED_PARAMETER(DeviceObject); 46 | Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; 47 | Irp->IoStatus.Information = 0; 48 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 49 | return STATUS_NOT_SUPPORTED; 50 | } 51 | 52 | _Function_class_(DRIVER_INITIALIZE) 53 | _IRQL_requires_same_ 54 | _IRQL_requires_(PASSIVE_LEVEL) 55 | extern "C" 56 | NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) 57 | { 58 | UNREFERENCED_PARAMETER(RegistryPath); 59 | 60 | // Set callback functions 61 | DriverObject->DriverUnload = DriverUnload; 62 | for (unsigned int i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) 63 | DriverObject->MajorFunction[i] = DriverDefaultHandler; 64 | DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose; 65 | DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverCreateClose; 66 | 67 | // Create a device 68 | UNICODE_STRING DeviceName; 69 | RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDriver"); 70 | RtlInitUnicodeString(&Win32Device, L"\\DosDevices\\MyDriver"); 71 | PDEVICE_OBJECT DeviceObject = nullptr; 72 | auto status = IoCreateDevice(DriverObject, 73 | 0, 74 | &DeviceName, 75 | FILE_DEVICE_UNKNOWN, 76 | FILE_DEVICE_SECURE_OPEN, 77 | FALSE, 78 | &DeviceObject); 79 | if (!NT_SUCCESS(status)) 80 | { 81 | return status; 82 | } 83 | if (!DeviceObject) 84 | { 85 | return STATUS_UNEXPECTED_IO_ERROR; 86 | } 87 | 88 | DbgPrint("BeingDebugged Offset: 0x%X", (ULONG)offsetof(PEB, BeingDebugged)); 89 | 90 | return STATUS_SUCCESS; 91 | } -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include "test_impl.h" 2 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "test_impl.h" 2 | -------------------------------------------------------------------------------- /test/test_impl.h: -------------------------------------------------------------------------------- 1 | #define PHNT_VERSION PHNT_WIN11 2 | #include "phnt.h" 3 | 4 | #ifdef __cplusplus 5 | static char message[] = "Hello from phnt in C++\r\n"; 6 | #else 7 | static char message[] = "Hello from phnt in C\r\n"; 8 | #endif // __cplusplus 9 | 10 | extern int EntryPoint(PPEB peb) 11 | { 12 | IO_STATUS_BLOCK IoStatusBlock = { 0, 0 }; 13 | NtWriteFile( 14 | peb->ProcessParameters->StandardOutput, 15 | NULL, 16 | NULL, 17 | NULL, 18 | &IoStatusBlock, 19 | message, 20 | sizeof(message) - 1, 21 | NULL, 22 | NULL 23 | ); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pushd systeminformer 3 | git fetch 4 | echo "\`\`\`diff" > ../pr-body.md 5 | git diff --exit-code HEAD origin/master -- phnt >> ../pr-body.md 6 | if [ $? -eq 0 ]; then 7 | echo "[INFO] No changes to commit" 8 | popd 9 | exit 0 10 | fi 11 | if [ $(stat --format=%s ../pr-body.md) -gt 65450 ]; then 12 | truncate -s 65450 ../pr-body.md 13 | fi 14 | echo "\`\`\`" >> ../pr-body.md 15 | 16 | NEW_HASH=$(git rev-parse --short origin/master) 17 | echo "[INFO] Bumping systeminformer to $NEW_HASH" 18 | git checkout origin/master 19 | popd 20 | 21 | echo "[INFO] Checking if bump-$NEW_HASH already exists" 22 | git ls-remote --exit-code --heads origin bump-$NEW_HASH > /dev/null 2>&1 23 | if [ $? -eq 0 ]; then 24 | echo "[INFO] Already bumped" 25 | exit 0 26 | fi 27 | 28 | echo "[INFO] Creating pull request" 29 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 30 | git config user.name "github-actions[bot]" 31 | git checkout -b bump-$NEW_HASH 32 | git commit -am "Bump systeminformer to $NEW_HASH" 33 | git push --set-upstream origin bump-$NEW_HASH 34 | gh pr create --title "Bump systeminformer to $NEW_HASH" --body-file pr-body.md 35 | --------------------------------------------------------------------------------