├── .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 |
--------------------------------------------------------------------------------