From a2f0cc29de2dd4f626f6f711eb30efbbbd899808 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sun, 6 Feb 2022 18:49:54 +0100 Subject: [PATCH] NOISSUE Add ntstatus-gen to systeminfo library --- libraries/systeminfo/CMakeLists.txt | 52 ++++- libraries/systeminfo/build-src/win32/main.cpp | 208 ++++++++++++++++++ .../build-src/win32/ntstatus.preprocess.h | 5 + libraries/systeminfo/include/sys.h | 2 + libraries/systeminfo/src/sys_apple.cpp | 5 + libraries/systeminfo/src/sys_unix.cpp | 5 + libraries/systeminfo/src/sys_win32.cpp | 8 + 7 files changed, 280 insertions(+), 5 deletions(-) create mode 100644 libraries/systeminfo/build-src/win32/main.cpp create mode 100644 libraries/systeminfo/build-src/win32/ntstatus.preprocess.h diff --git a/libraries/systeminfo/CMakeLists.txt b/libraries/systeminfo/CMakeLists.txt index 548a589c..a05a3de0 100644 --- a/libraries/systeminfo/CMakeLists.txt +++ b/libraries/systeminfo/CMakeLists.txt @@ -3,13 +3,55 @@ project(systeminfo) find_package(Qt5Core) set(systeminfo_SOURCES -include/sys.h -include/distroutils.h -src/distroutils.cpp + include/sys.h + include/distroutils.h + src/distroutils.cpp ) +set(systeminfo_INCLUDE_DIRS + ${CMAKE_CURRENT_LIST_DIR}/include) + if (WIN32) - list(APPEND systeminfo_SOURCES src/sys_win32.cpp) + set(systeminfo_BUILD_SOURCES + build-src/win32/main.cpp) + + set(systeminfo_PREPROCESSOR_INPUT + build-src/win32/ntstatus.preprocess.h) + + set(systeminfo_PREPROCESSOR_OUT + ${CMAKE_CURRENT_BINARY_DIR}/ntstatus.preprocess.h.target) + + get_filename_component(systeminfo_PREPROCESSOR_INPUT_FULL + ${systeminfo_PREPROCESSOR_INPUT} + REALPATH BASE_DIR ${CMAKE_CURRENT_LIST_DIR} + ) + + add_custom_target(systeminfo-preprocess-nstatus) + add_custom_command( + COMMAND ${CMAKE_C_COMPILER} -MF ${systeminfo_PREPROCESSOR_OUT} -M ${systeminfo_PREPROCESSOR_INPUT_FULL} + DEPENDS ${systeminfo_PREPROCESSOR_INPUT} + COMMENT "Generating path to ntstatus.h" + TARGET "systeminfo-preprocess-nstatus" + ) + + add_executable(systeminfo-ntstatus-gen ${systeminfo_BUILD_SOURCES}) + add_dependencies(systeminfo-ntstatus-gen systeminfo-preprocess-nstatus) + target_compile_definitions(systeminfo-ntstatus-gen PRIVATE + NTSTATUS_PREPROCESSOR_OUT=\"${systeminfo_PREPROCESSOR_OUT}\") + + set(NTSTATUS_GEN_HEADER "${CMAKE_CURRENT_BINARY_DIR}/NtStatsuGen.h") + set(NTSTATUS_GEN_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/NtStatusGen.cpp") + + add_custom_command( + COMMAND systeminfo-ntstatus-gen ${NTSTATUS_GEN_HEADER} ${NTSTATUS_GEN_SOURCE} + COMMENT "Generating NTSTATUS lookup table" + OUTPUT ${NTSTATUS_GEN_HEADER} ${NTSTATUS_GEN_SOURCE} + ) + set_property(SOURCE ${NTSTATUS_GEN_HEADER} PROPERTY SKIP_AUTOMOC ON) + set_property(SOURCE ${NTSTATUS_GEN_SOURCE} PROPERTY SKIP_AUTOMOC ON) + + list(APPEND systeminfo_SOURCES src/sys_win32.cpp ${NTSTATUS_GEN_SOURCE} ${NTSTATUS_GEN_HEADER}) + list(APPEND systeminfo_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) elseif (UNIX) if(APPLE) list(APPEND systeminfo_SOURCES src/sys_apple.cpp) @@ -20,7 +62,7 @@ endif() add_library(systeminfo STATIC ${systeminfo_SOURCES}) target_link_libraries(systeminfo Qt5::Core Qt5::Gui Qt5::Network) -target_include_directories(systeminfo PUBLIC include) +target_include_directories(systeminfo PUBLIC ${systeminfo_INCLUDE_DIRS}) include (UnitTest) add_unit_test(sys diff --git a/libraries/systeminfo/build-src/win32/main.cpp b/libraries/systeminfo/build-src/win32/main.cpp new file mode 100644 index 00000000..7ed5abde --- /dev/null +++ b/libraries/systeminfo/build-src/win32/main.cpp @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include + +static void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { + return !std::isspace(c); + })); +} + +static void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](char c) { + return !std::isspace(c); + }).base(), s.end()); +} + +static void trim(std::string &s) { + ltrim(s); + rtrim(s); +} + +static bool startsWith(const std::string &s, const std::string &start) { + return s.rfind(start, 0) == 0; +} + +static bool endsWith(const std::string &s, const std::string &end) { + if (s.length() >= end.length()) { + return s.compare(s.length() - end.length(), end.length(), end) == 0; + } else { + return false; + } +} + +static bool extractNumber(std::string macroValue, uint64_t &output) { + while (startsWith(macroValue, "(") && endsWith(macroValue, ")")) { + macroValue = macroValue.substr(1, macroValue.length() - 2); + } + + if (startsWith(macroValue, "(NTSTATUS)")) { + macroValue = macroValue.substr(10); + ltrim(macroValue); + } + + errno = 0; + auto value = std::strtoull(¯oValue[0], nullptr, 0); + if (errno != 0) { + return false; + } + + output = value; + return true; +} + +struct NtStatusCode { + explicit NtStatusCode() = default; + + explicit NtStatusCode(std::string name, uint64_t value) : name(std::move(name)), value(value) {} + + std::string name; + uint64_t value = 0; +}; + +static std::vector predefinedCodes() { + // Some codes are not in ntstatus.h for some reason... + // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/specific-exceptions + return { + NtStatusCode{"STATUS_APPLICATION_HANG", 0xCFFFFFFF}, + NtStatusCode{"STATUS_CPP_EH_EXCEPTION", 0xE06D7363}, + NtStatusCode{"STATUS_CLR_EXCEPTION", 0xE0434f4D}, + }; +} + +int main(int argc, const char **argv) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + + std::ifstream ntstatusTarget(NTSTATUS_PREPROCESSOR_OUT); + if (!ntstatusTarget.is_open()) { + std::cerr << "Failed to open preprocessor output at " << NTSTATUS_PREPROCESSOR_OUT << std::endl; + return 1; + } + + std::string ntstatusPath; + + std::string line; + while (std::getline(ntstatusTarget, line)) { + trim(line); + + if (endsWith(line, "ntstatus.h")) { + ntstatusPath = line; + break; + } + } + + ntstatusTarget.close(); + + if (ntstatusPath.empty()) { + std::cerr << "Failed to find path to ntstatus.h in generated preprocessor output" << std::endl; + return 1; + } + + + std::cout << "nstatus.h at " << ntstatusPath << std::endl; + std::ifstream ntstatusHeader(ntstatusPath); + if (!ntstatusHeader.is_open()) { + std::cerr << "Failed to open ntstatus.h" << std::endl; + return 1; + } + + std::vector codes = predefinedCodes(); + + while (std::getline(ntstatusHeader, line)) { + trim(line); + + if (startsWith(line, "#define") && line.find("NTSTATUS") != std::string::npos) { + line = line.substr(7); + ltrim(line); + + auto space = line.find(' '); + if (space == std::string::npos) { + std::cerr << "Skipping #define " << line << " as the macro has no value" << std::endl; + continue; + } + + auto name = line.substr(0, space); + auto value = line.substr(space + 1); + ltrim(value); + + NtStatusCode code; + code.name = name; + + if (!extractNumber(value, code.value)) { + std::cerr << "Skipping #define " << line << " because its value couldn't be parsed" << std::endl; + } else { + codes.emplace_back(std::move(code)); + } + } + } + + std::cout << "Found " << codes.size() << " NTSTATUS codes" << std::endl; + + std::ofstream outputHeader(argv[1]); + if (!outputHeader.is_open()) { + std::cerr << "Failed to open header output file " << argv[1] << std::endl; + return 1; + } + + outputHeader << "// AUTO GENERATED FILE, DO NOT EDIT!" << std::endl; + outputHeader << "// This file has been generated by nstatus-gen from the systeminfo library" << std::endl; + outputHeader << std::endl; + outputHeader << "#pragma once" << std::endl; + outputHeader << std::endl; + outputHeader << "#include " << std::endl; + outputHeader << "#include " << std::endl; + outputHeader << std::endl; + outputHeader << "namespace Sys {" << std::endl; + outputHeader << "namespace Win32 {" << std::endl; + outputHeader << "bool lookupNtStatusCodeName(uint64_t code, std::string &nameOut);" << std::endl; + outputHeader << "}" << std::endl; + outputHeader << "}" << std::endl; + + std::ofstream outputSource(argv[2]); + if (!outputSource.is_open()) { + std::cerr << "Failed to open source output file " << argv[2] << std::endl; + return 1; + } + + outputSource << "// AUTO GENERATED FILE, DO NOT EDIT!" << std::endl; + outputSource << "// This file has been generated by nstatus-gen from the systeminfo library" << std::endl; + outputSource << std::endl; + outputSource << "#include " << std::endl; + outputSource << "#include " << std::endl; + outputSource << "#include " << std::endl; + outputSource << std::endl; + outputSource << "namespace Sys {" << std::endl; + outputSource << "namespace Win32 {" << std::endl; + outputSource << "static std::unordered_map NTSTATUS_CODES = {" << std::endl; + + bool first = true; + for (const auto &status: codes) { + if (first) { + first = false; + } else { + outputSource << "," << std::endl; + } + + outputSource << " {0x" << std::hex << status.value << std::dec << ", \"" << status.name << "\"}"; + } + + outputSource << std::endl; + outputSource << "};" << std::endl; + outputSource << "bool lookupNtStatusCodeName(uint64_t code, std::string &nameOut) {" << std::endl; + outputSource << " auto it = NTSTATUS_CODES.find(code);" << std::endl; + outputSource << " if(it != NTSTATUS_CODES.end()) {" << std::endl; + outputSource << " nameOut = it->second;" << std::endl; + outputSource << " return true;" << std::endl; + outputSource << " }" << std::endl; + outputSource << " return false;" << std::endl; + outputSource << "}" << std::endl; + outputSource << "}" << std::endl; + outputSource << "}" << std::endl; + + return 0; +} \ No newline at end of file diff --git a/libraries/systeminfo/build-src/win32/ntstatus.preprocess.h b/libraries/systeminfo/build-src/win32/ntstatus.preprocess.h new file mode 100644 index 00000000..f71326d7 --- /dev/null +++ b/libraries/systeminfo/build-src/win32/ntstatus.preprocess.h @@ -0,0 +1,5 @@ +// This file exists so that CMake can run the compiler in preprocessor only mode over it +// in order to generate a preprocesses ntstatus header, which then can be read by status +// generator. + +#include diff --git a/libraries/systeminfo/include/sys.h b/libraries/systeminfo/include/sys.h index bd6e2486..c1d4ecf8 100644 --- a/libraries/systeminfo/include/sys.h +++ b/libraries/systeminfo/include/sys.h @@ -60,4 +60,6 @@ uint64_t getSystemRam(); bool isSystem64bit(); bool isCPU64bit(); + +bool lookupSystemStatusCode(uint64_t code, std::string &name, std::string &description); } diff --git a/libraries/systeminfo/src/sys_apple.cpp b/libraries/systeminfo/src/sys_apple.cpp index 6353b747..b1ec6760 100644 --- a/libraries/systeminfo/src/sys_apple.cpp +++ b/libraries/systeminfo/src/sys_apple.cpp @@ -72,3 +72,8 @@ Sys::DistributionInfo Sys::getDistributionInfo() DistributionInfo result; return result; } + +bool Sys::lookupSystemStatusCode(uint64_t code, std::string &name, std::string &description) +{ + return false; +} diff --git a/libraries/systeminfo/src/sys_unix.cpp b/libraries/systeminfo/src/sys_unix.cpp index b3098522..66928ae3 100644 --- a/libraries/systeminfo/src/sys_unix.cpp +++ b/libraries/systeminfo/src/sys_unix.cpp @@ -116,3 +116,8 @@ Sys::DistributionInfo Sys::getDistributionInfo() } return result; } + +bool Sys::lookupSystemStatusCode(uint64_t code, std::string &name, std::string &description) +{ + return false; +} diff --git a/libraries/systeminfo/src/sys_win32.cpp b/libraries/systeminfo/src/sys_win32.cpp index 430b87e4..6c3b2642 100644 --- a/libraries/systeminfo/src/sys_win32.cpp +++ b/libraries/systeminfo/src/sys_win32.cpp @@ -2,6 +2,8 @@ #include +#include "NtStatsuGen.h" + Sys::KernelInfo Sys::getKernelInfo() { Sys::KernelInfo out; @@ -54,3 +56,9 @@ Sys::DistributionInfo Sys::getDistributionInfo() DistributionInfo result; return result; } + +bool Sys::lookupSystemStatusCode(uint64_t code, std::string &name, std::string &description) +{ + + return false; +}