diff --git a/README.md b/README.md index be976e6..52c1a3f 100644 --- a/README.md +++ b/README.md @@ -11,217 +11,199 @@ ![C++ 14+](https://img.shields.io/badge/C++-14+-white.svg?logo=cplusplus&logoColor=blue) ![Architecture](https://img.shields.io/badge/CPU-x86%20%2F%20x64%20%2F%20ARM%20%2F%20ARM64-blue.svg?logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTIgNTIiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MiA1MiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8Zz4KICAgICAgICA8cGF0aCBmaWxsPSJ3aGl0ZSIgZD0iTTQ5LDE5LjV2LTJoLTYuOTk4NDEzMXYtNy40OTU3ODg2SDE0LjQwMTYxMTNsLTQuNDAwMDI0NCw0LjUzMDAyOTN2MjcuNDY5OTcwN0gxNy41VjQ5aDJ2LTYuOTk1Nzg4NmgzVjQ5aDJ2LTYuOTk1Nzg4NmgzICAgVjQ5aDJ2LTYuOTk1Nzg4NmgzVjQ5aDJ2LTYuOTk1Nzg4NmgyLjgxMTU4NDVsNC42OTAwMDI0LTUuNjQwMDE0NlYzNC41SDQ5di0yaC02Ljk5ODQxMzF2LTNINDl2LTJoLTYuOTk4NDEzMXYtM0g0OXYtMmgtNi45OTg0MTMxICAgdi0zSDQ5eiBNMzYuMDAxNTg2OSwzMy41MDQyMTE0YzAsMS42NTAwMjQ0LTEuMzQ5OTc1NiwzLTMsM2gtMTRjLTEuNjU5OTczMSwwLTMtMS4zNDk5NzU2LTMtM3YtMTRjMC0xLjY1OTk3MzEsMS4zNDAwMjY5LTMsMy0zaDE0ICAgYzEuNjUwMDI0NCwwLDMsMS4zNDAwMjY5LDMsM1YzMy41MDQyMTE0eiIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSIyIiB3aWR0aD0iNyIgeD0iMyIgeT0iMTcuNSIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSIyIiB3aWR0aD0iNyIgeD0iMyIgeT0iMjIuNSIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSIyIiB3aWR0aD0iNyIgeD0iMyIgeT0iMjcuNSIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSIyIiB3aWR0aD0iNyIgeD0iMyIgeT0iMzIuNSIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSI3IiB3aWR0aD0iMiIgeD0iMzIuNSIgeT0iMyIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSI3IiB3aWR0aD0iMiIgeD0iMjcuNSIgeT0iMyIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSI3IiB3aWR0aD0iMiIgeD0iMjIuNSIgeT0iMyIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSI3IiB3aWR0aD0iMiIgeD0iMTcuNSIgeT0iMyIgLz4KICAgIDwvZz4KPC9zdmc+) -커널 드라이버에서 C++ 및 STL 기능을 사용할 수 있도록 도와주는 CRT 라이브러리 입니다. +* [한국어 (Korean) 보기](./docs/ko-kr.md) + +[This document has been **machine translated.**] + +**crtsys** is open source library that helps you use C++ CRT and STL features in your kernel drivers. - [crtsys](#crtsys) - [Overview](#overview) - - [Requirements](#requirements) - - [Test Environments](#test-environments) - [Goal](#goal) - [C++ Standard](#c-standard) - [STL](#stl) - [C Standard](#c-standard-1) - [NTL (NT Template Library)](#ntl-nt-template-library) - - [Build](#build) - - [Test](#test) + - [Requirements](#requirements) + - [Test Environments](#test-environments) + - [Build & Test](#build--test) - [Usage](#usage) - [TODO](#todo) ## Overview -커널 드라이버에서 C++ 및 STL을 사용할수있도록 도와주는 기능을 조사해보았는데, 그중 사용하기 가장 잘 구현되어있는 프로젝트는 아래와 같습니다. - -(22-04-26 기준) - -- [UCXXRT](https://github.com/MiroKaku/ucxxrt) - - 장점 - - 타 프로젝트와는 다르게 [Microsoft STL](https://github.com/microsoft/STL)를 직접 사용한다는 개념 자체가 좋아보임. - - 단점 - - 현재까지는 Win32 API와 연관성이 적은 std::string, std::vector 클래스 등 자료형이나 자료구조 관련 기능만 지원됨. - - vcxproj이 개발 환경에 종속되기 때문에 VS 버전이 바뀌면 일일이 수작업으로 수정해줘야함. - - x86에서 [이 코드](https://en.cppreference.com/w/cpp/language/throw#Example)를 실행하는 중 Hang이 발생함 - - Microsoft의 소스 코드를 그대로 복사하여 상단에 자신의 라이센스 주석만 넣어서 프로젝트에 포함시킨것으로 보이며 라이센스 문제에서 자유롭지 못해보임 - - 라이센스를 변경하는 것과 **(Microsoft CRT 및 STL 소스 코드를 재배포하는 행위는 허가되지 않은것으로 파악됨)** - -- [KTL](https://github.com/DymOK93/KTL) - - 장점 - - CMake 사용됨 - - 개발 환경에 맞는 프로젝트 파일 생성 및 자동화가 용이함 - - CRT를 포함한 모든 코드를 독자적으로 구현 - - 라이센스로부터 자유로우며, 예외처리 코드 부분이 경량화되어 스택을 절약할 수 있음 - - 커널에 특화된 템플릿 라이브러리 제공 - - 커널에서만 존재하는 매커니즘에 대한 템플릿 라이브러리를 제공하기 때문에, 실제 드라이버 제작 시에는 STL을 보다도 더 애용할만한 기능이 많음 - - 높은 품질의 코드 - - 단점 - - Microsoft STL을 사용하는 것을 고려해서 작성된것으로 보이나 실제로 Microsoft STL을 사용하도록 변경하는 것에 어려움을 느낌 (오탈자 및 ktl 만 사용하는 코드가 생각보다 많았음) - - 러시아어 주석인데 파일이 UTF8+BOM로 인코딩되어있지 않아서 러시아 외 국가 환경에서 빌드를 실패함 - - x86 미지원 - -각 라이브러리의 단점을 보완하는 작업을 진행하던 중 - -1. UCXXRT은 Microsoft CRT 소스 코드를 그대로 복사하여 프로젝트에 포함시켰다는 점 -2. KTL은 x86을 지원하지 않는 점 - -으로 인해서 새로운 라이브러리를 개발하게 되었습니다. - -crtsys의 장점은 아래와 같습니다. - -1. Micosoft CRT와 STL을 최대한 비슷하게 지원하기 위해서 Microsoft CRT 소스코드를 사용하긴 하지만 Microsoft Visual Studio가 설치되어있는 디렉토리 내의 소스를 직접 빌드하는 방법으로 처리하여, Visual Studio를 합법적으로 사용하는 사용자는 라이센스 문제 없이 사용 가능합니다. -2. Win32 API를 구현한 [Ldk](https://github.com/ntoskrnl7/Ldk)를 활용하여 많은 범위의 STL 기능을 지원합니다. -3. CMake로 프로젝트를 구성할 수 있으며, [CPM](https://github.com/cpm-cmake/CPM.cmake)을 사용하여 정말 간편하게 라이브러리를 사용할 수 있도록 지원합니다. - -## Requirements - -- Windows 8 or later -- Visual Studio 2017 or later -- CMake 3.16 or later -- Git +This project has the most support for C++ STL features among similar projects. -## Test Environments +
+more + +We investigated the functions that help us to use C++ and STL in the kernel driver, and among them, the best implemented project to use is as follows. + +(22-04-26) + +* [UCXXRT](https://github.com/MiroKaku/ucxxrt) + * Pros + * The concept of using the [Microsoft STL](https://github.com/microsoft/STL) directly looks good. + * Cons + * Until now, only functions related to data types or data structures such as std::string and std::vector classes, which have little relevance to Win32 API, are supported. + * Since vcxproj is dependent on the development environment, if the VS version changes, you have to manually modify it. + * Hanging occurs on x86. [this code](https://en.cppreference.com/w/cpp/language/throw#Example) + * It seems that they copied Microsoft's source code as it is and put their own license comments at the top and included it in the project, and it does not seem to be free from licensing problems. + * Changing the license and redistributing the Microsoft CRT and STL source code are not permitted. + +* [KTL](https://github.com/DymOK93/KTL) + * Pros + * CMake + * Easy to create and automate project files suitable for development environment. + * Independent implementation of all code including CRT + * It is free from license, and the exception handling code part is lightweight, which can save the stack. + * Provides a template library specialized for the kernel + * Because it provides a template library for mechanisms that exist only in the kernel, there are many functions that can be used more favorably than STL when making an actual driver. + * high quality code. + * Cons + * MIt seems that it was written with consideration to using Microsoft STL, but I feel it is difficult to actually change to use Microsoft STL (there were more typos and code using only ktl than I thought) + * Russian comments, but the file is not UTF8+BOM encoded, so the build fails in a non-Russian environment. + * x86 not supported. + +While I was working to make up for the shortcomings of each library, I developed a new library for the following reasons. + +1. UCXXRT copied the Microsoft CRT source code as is and included it in the project. +2. KTL does not support x86. + +The advantages of crtsys are as follows. + +1. Microsoft CRT source code is used to support the Microsoft CRT and STL as closely as possible, but it builds and processes the source directly in the directory where Microsoft Visual Studio or Build Tools are installed, so users who use Visual Studio legally can be licensed. . You can use it without any problems. +2. It supports a wide range of STL features by leveraging [Ldk](https://github.com/ntoskrnl7/Ldk) that implement the Win32 API. +3. You can organize your projects with CMake, and [CPM](https://github.com/cpm-cmake/CPM.cmake) makes it really easy to use the library. -- Windows 10 x64 - - **x86, x64, ARM, ARM64**로 빌드 가능하지만, 실제 테스트는 x86, x64 모듈에 대해서만 검증되었습니다. -- CMake 3.21.4 -- Git 2.23.0 -- Visual Studio 2017 - - Visual Studio 2017의 CRT 소스 코드는 일부 헤더가 누락되어 빌드가 되지 않아서, UCXXRT의 코드를 일부 사용하여 지원합니다. - - 추후 누락된 헤더를 직접 작성하여 지원할 예정입니다. -- Visual Studio 2019 -- Visual Studio 2022 -- VC Tools - - 14.16.27023 - - 14.24.28314 - - 14.26.28801 - - 14.29.30133 - - 14.31.31103 -- Windows Kit (SDK, WDK) - - 10.0.17763.0 - - 10.0.18362.0 - - 10.0.22000.0 - -SDK와 WDK의 버전이 다르면 빌드가 실패할 가능성이 높으므로 **가능하다면 SDK와 WDK의 버전이 같은 환경에서 빌드하는것을 권장합니다.** +
## Goal -이 프로젝트는 커널 드라이벌르 작성할 때, 응용 프로그램에서 C++ 및 STL을 사용하는 것에 근접한 개발 경험을 제공하는것을 목표로합니다. +This project aims to provide a development experience similar to using C++ and STL in your applications when writing kernel drivers. -현재 지원되거나 앞으로 지원할 기능은 아래와 같습니다. +Features currently supported or supported in the future are listed below. -- 체크된 항목은 구현 완료된 항목입니다. -- 테스트 코드가 작성된 항목은 테스트 코드의 링크를 추가하였습니다. +* Checked items are items that have been implemented. +* For the item where the test code is written, a link to the test code is added. ### C++ Standard -[C++ reference](https://en.cppreference.com)를 기준으로 작성하였습니다. - -- [Initialization](https://en.cppreference.com/w/cpp/language/initialization) - - [x] [Non-local variables](https://en.cppreference.com/w/cpp/language/initialization#Non-local_variables) - - [x] [Static initialization](https://en.cppreference.com/w/cpp/language/initialization#Static_initialization) - - [x] [Constant initialization](https://en.cppreference.com/w/cpp/language/constant_initialization) [(tested)](./test/src/cpp/lang/initialization.cpp#L13) - - [x] [Zero initialization](https://en.cppreference.com/w/cpp/language/zero_initialization) [(tested)](./test/src/cpp/lang/initialization.cpp#L41) - - [x] [Dynamic initialization](https://en.cppreference.com/w/cpp/language/initialization#Dynamic_initialization) [(tested)](./test/src/cpp/lang/initialization.cpp#L65) - - [ ] [Static local variables](https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables) - - [ ] thread_local - - [ ] static -- [Exceptions](https://en.cppreference.com/w/cpp/language/exceptions) - - [x] [throw](https://en.cppreference.com/w/cpp/language/throw) [(tested)](./test/src/cpp/lang/exceptions.cpp#L42) - - [x] [try-block](https://en.cppreference.com/w/cpp/language/try_catch) [(tested)](./test/src/cpp/lang/exceptions.cpp#L60) - - [x] [Function-try-block](https://en.cppreference.com/w/cpp/language/function-try-block) [(tested)](./test/src/cpp/lang/exceptions.cpp#L98) +It was written based on the [C++ reference](https://en.cppreference.com) + +* [Initialization](https://en.cppreference.com/w/cpp/language/initialization) + * [x] [Non-local variables](https://en.cppreference.com/w/cpp/language/initialization#Non-local_variables) + * [x] [Static initialization](https://en.cppreference.com/w/cpp/language/initialization#Static_initialization) + * [x] [Constant initialization](https://en.cppreference.com/w/cpp/language/constant_initialization) [(tested)](./test/src/cpp/lang/initialization.cpp#L13) + * [x] [Zero initialization](https://en.cppreference.com/w/cpp/language/zero_initialization) [(tested)](./test/src/cpp/lang/initialization.cpp#L41) + * [x] [Dynamic initialization](https://en.cppreference.com/w/cpp/language/initialization#Dynamic_initialization) [(tested)](./test/src/cpp/lang/initialization.cpp#L65) + * [ ] [Static local variables](https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables) + * [ ] thread_local + * [ ] static +* [Exceptions](https://en.cppreference.com/w/cpp/language/exceptions) + * [x] [throw](https://en.cppreference.com/w/cpp/language/throw) [(tested)](./test/src/cpp/lang/exceptions.cpp#L42) + * [x] [try-block](https://en.cppreference.com/w/cpp/language/try_catch) [(tested)](./test/src/cpp/lang/exceptions.cpp#L60) + * [x] [Function-try-block](https://en.cppreference.com/w/cpp/language/function-try-block) [(tested)](./test/src/cpp/lang/exceptions.cpp#L98) #### STL -- [x] [std::chrono](https://en.cppreference.com/w/cpp/chrono) [(tested)](./test/src/cpp/stl/chrono.cpp#L15) -- [x] [std::thread](https://en.cppreference.com/w/cpp/thread) [(tested)](./test/src/cpp/stl/thread.cpp#L35) -- [x] [std::condition_variable](https://en.cppreference.com/w/cpp/thread/condition_variable) [(tested)](./test/src/cpp/stl/thread.cpp#L35) -- [x] [std::mutex](https://en.cppreference.com/w/cpp/thread/mutex) [(tested)](./test/src/cpp/stl/thread.cpp#L81) -- [x] [std::shared_mutex](https://en.cppreference.com/w/cpp/thread/shared_mutex) [(tested)](./test/src/cpp/stl/thread.cpp#L129) -- [x] [std::future](https://en.cppreference.com/w/cpp/thread/future) [(tested)](./test/src/cpp/stl/thread.cpp#L157) -- [x] [std::promise](https://en.cppreference.com/w/cpp/thread/promise) [(tested)](./test/src/cpp/stl/thread.cpp#L203) -- [x] [std::packaged_task](https://en.cppreference.com/w/cpp/thread/packaged_task) [(tested)](./test/src/cpp/stl/thread.cpp#L267) -- [x] [std::cin](https://en.cppreference.com/w/cpp/io/cin) -- [x] [std::clog](https://en.cppreference.com/w/cpp/io/clog) -- [x] [std::cerr](https://en.cppreference.com/w/cpp/io/cerr) -- [x] [std::cout](https://en.cppreference.com/w/cpp/io/cout) (tested) -- [x] [std::wcin](https://en.cppreference.com/w/cpp/io/cin) -- [x] [std::wclog](https://en.cppreference.com/w/cpp/io/clog) -- [x] [std::wcerr](https://en.cppreference.com/w/cpp/io/cerr) -- [x] [std::wcout](https://en.cppreference.com/w/cpp/io/cout) (tested) +* [x] [std::chrono](https://en.cppreference.com/w/cpp/chrono) [(tested)](./test/src/cpp/stl/chrono.cpp#L15) +* [x] [std::thread](https://en.cppreference.com/w/cpp/thread) [(tested)](./test/src/cpp/stl/thread.cpp#L35) +* [x] [std::condition_variable](https://en.cppreference.com/w/cpp/thread/condition_variable) [(tested)](./test/src/cpp/stl/thread.cpp#L35) +* [x] [std::mutex](https://en.cppreference.com/w/cpp/thread/mutex) [(tested)](./test/src/cpp/stl/thread.cpp#L81) +* [x] [std::shared_mutex](https://en.cppreference.com/w/cpp/thread/shared_mutex) [(tested)](./test/src/cpp/stl/thread.cpp#L129) +* [x] [std::future](https://en.cppreference.com/w/cpp/thread/future) [(tested)](./test/src/cpp/stl/thread.cpp#L157) +* [x] [std::promise](https://en.cppreference.com/w/cpp/thread/promise) [(tested)](./test/src/cpp/stl/thread.cpp#L203) +* [x] [std::packaged_task](https://en.cppreference.com/w/cpp/thread/packaged_task) [(tested)](./test/src/cpp/stl/thread.cpp#L267) +* [x] [std::cin](https://en.cppreference.com/w/cpp/io/cin) +* [x] [std::clog](https://en.cppreference.com/w/cpp/io/clog) +* [x] [std::cerr](https://en.cppreference.com/w/cpp/io/cerr) +* [x] [std::cout](https://en.cppreference.com/w/cpp/io/cout) (tested) +* [x] [std::wcin](https://en.cppreference.com/w/cpp/io/cin) +* [x] [std::wclog](https://en.cppreference.com/w/cpp/io/clog) +* [x] [std::wcerr](https://en.cppreference.com/w/cpp/io/cerr) +* [x] [std::wcout](https://en.cppreference.com/w/cpp/io/cout) (tested) ### C Standard -- [x] math functions - - 직접 구현하기에는 시간이 부족하여 아래 프로젝트의 내용을 참고하엿습니다. 감사합니다! :-) - - [RetrievAL](https://github.com/SpoilerScriptsGroup/RetrievAL) - - [musl](https://github.com/bminor/musl) +* [x] math functions + * I did not have enough time to implement it myself, so I referred to the contents of the project below. thank you! :-) + * [RetrievAL](https://github.com/SpoilerScriptsGroup/RetrievAL) + * [musl](https://github.com/bminor/musl) ### NTL (NT Template Library) -커널에서 더 나은 개발 환경을 지원하기 위한 기능을 제공합니다. - -- ntl::expand_stack [(tested)](./test/src/ntl.cpp#L5) - - 스택 크기를 확장하기 위한 함수 - - 기본적으로 커널 스택은 사용자 스레드 스택보다 훨씬 작은 크기를 할당받기 때문에 STL 기능을 사용하거나, 특히 throw를 수행할때 사용하는것이 좋습니다. -- ntl::status - - NTSTATUS에 대한 클래스 -- ntl::driver - - DRIVER_OBJECT에 대한 클래스 - - 기능 - - [x] DriverUnload [(tested)](./test/driver/src/main.cpp#L30) - - [ ] DriverDispatch -- ntl::driver_main [(tested)](./test/driver/src/main.cpp#L22) - - C++ 용 드라이버 진입점 - - ntl::expand_stack 함수로 스택을 최대 크기로 확장하여 호출됩니다. -- ntl::rpc - - User Mode App과 Kernel Driver간 손쉬운 통신 방법을 제공합니다. - - ntl::rpc::server [(tested)](./test/common/rpc/server.hpp) [(tested)](./test/common/rpc/procedures.cpp) - - ntl::rpc::client [(tested)](./test/common/rpc/client.hpp) [(tested)](./test/common/rpc/procedures.cpp) - - 데이터 직렬화 부분은 직접 구현하기에는 시간이 부족하여 아래 프로젝트의 내용을 참고하엿습니다. 감사합니다! :-) - - [Eyal Z/zpp serializer](https://github.com/eyalz800/serializer) - -## Build - -이 프로젝트를 직접 빌드하여 lib와 include를 사용하시려면 Microsoft STL 사용을 위해서 포함 경로 설정 및 전처리기 설정 등 복잡한 사전 작업이 필요하므로 **직접 빌드하여 사용하는것보다는 [Usage](#usage)을 참고하여 CPM을 통해서 사용하시는것을 권장합니다.** - -
-빌드 방법 보기 - -빌드 방법은 아래와 같습니다. - -```Batch -git clone https://github.com/ntoskrnl7/crtsys -cd crtsys -cmake -S . -B build -cmake --build build --config Release -cmake -S . -B build_x86 -A Win32 -cmake --build build_x86 --config Release -``` +Provides features to support a better development environment in the kernel. + +* ntl::expand_stack [(tested)](./test/src/ntl.cpp#L5) + * Function to extend the stack size + * By default, the kernel stack is allocated a much smaller size than the user thread stack, so it is recommended to use the STL function or especially when performing throw. +* ntl::status + * Class for NTSTATUS +* ntl::driver + * Class for DRIVER_OBJECT + * Features + * [x] DriverUnload [(tested)](./test/driver/src/main.cpp#L30) + * [x] Create device [(tested)](./test/driver/src/main.cpp#L39) +* ntl::device + * Class for DEVICE_OBJECT + * Features + * [x] Device Extension [(tested)](./test/driver/src/main.cpp#L39) + * [ ] IRP_MJ_CREATE + * [ ] IRP_MJ_CLOSE + * [x] IRP_MJ_DEVICE_CONTROL [(tested)](./test/app/src/main.cpp#L77) [(tested)](./test/driver/src/main.cpp#L47) +* ntl::driver_main [(tested)](./test/driver/src/main.cpp#L22) + * Driver entry point for C++. + * Called by expanding the stack to its maximum size with the ntl::expand_stack function. +* ntl::rpc + * Provides an easy communication method between User Mode Application and Kernel Driver. + * ntl::rpc::server [(tested)](./test/driver/src/main.cpp#L19) [(tested)](./test/common/rpc.hpp) + * ntl::rpc::client [(tested)](./test/app/src/main.cpp#L4) [(tested)](./test/common/rpc.hpp) + * I did not have enough time to implement the data serialization part myself, so I referred to the contents of the project below. thank you! :-) + * [Eyal Z/zpp serializer](https://github.com/eyalz800/serializer) -혹은 - -```Batch -git clone https://github.com/ntoskrnl7/crtsys -cd crtsys -build.bat . x86 release -build.bat . x64 release -``` - -위 명령을 실행하여 빌드를 하시면 lib/x64/crtsys.lib와 lib/x86/crtsys.lib가 생성됩니다. - -lib 디렉토리와 include 디렉토리를 타 프로젝트에서 사용하시면 됩니다. -이 라이브러리를 사용할 프로젝트의 전처리기 정의나 포함 경로 설정 등 프로젝트 설정은 [CMakeLists.txt](./CMakeLists.txt)에서 PUBLIC으로 설정된 항목들을 참고하여 설정하시기 바랍니다. +## Requirements -
+* Windows 7 or later +* [Visual Studio / Build Tools 2017 or later](https://visualstudio.microsoft.com/ko/downloads/) +* [CMake 3.16 or later](https://cmake.org/download/) +* [Git](https://git-scm.com/downloads) -## Test +## Test Environments -1. 아래 명령을 수행하여 라이브러리 및 테스트 코드를 빌드하시기 바랍니다. +* Windows 10 x64 + * It can be built with **x86, x64, ARM, ARM64**, but the actual test has only been validated against x86 and x64 modules. + +* CMake 3.21.4 +* Git 2.23.0 +* Visual Studio 2017 + * Visual Studio 2017's CRT source code was missing some headers and could not be built, so it is supported using some of UCXXRT code. + * In the future, we plan to support by manually writing the missing header. +* Visual Studio 2019 +* Visual Studio 2022 +* VC Tools + * 14.16.27023 + * 14.24.28314 + * 14.26.28801 + * 14.29.30133 + * 14.31.31103 +* Windows Kit (SDK, WDK) + * 10.0.17763.0 + * 10.0.18362.0 + * 10.0.22000.0 + +If the SDK and WDK versions are different, builds are more likely to fail. **If possible, it is recommended to build in the same environment as the SDK and WDK versions.** + +## Build & Test + +1. Please build the library and test code by executing the command below. ```Batch git clone https://github.com/ntoskrnl7/crtsys cd crtsys\test\build.bat ``` - 혹은 아래 명령을 수행하시면 지원되는 모든 아키텍쳐에 대해서 Debug, Release 구성을 모두 빌드합니다. + Alternatively, if you execute the command below, both Debug and Release configurations are built for all supported architectures. ```Batch git clone https://github.com/ntoskrnl7/crtsys @@ -230,21 +212,21 @@ lib 디렉토리와 include 디렉토리를 타 프로젝트에서 사용하시 build_all.bat test\driver ``` -2. build\Debug\crtsys_test.sys를 설치 및 로드하시기 바랍니다. +2. Install and load build\Debug\crtsys_test.sys. - - driver + * driver x64 : test\driver\build_x64\Debug\crtsys_test.sys x86 : test\driver\build_x86\Debug\crtsys_test.sys ARM : test\driver\build_ARM\Debug\crtsys_test.sys ARM64 : test\driver\build_ARM64\Debug\crtsys_test.sys - - app + * app x64 : test\driver\build_x64\Debug\crtsys_test_app.exe x86 : test\driver\build_x86\Debug\crtsys_test_app.exe ARM : test\driver\build_ARM\Debug\crtsys_test_app.exe ARM64 : test\driver\build_ARM64\Debug\crtsys_test_app.exe ```batch - sc create CrtSysTest binpath= "빌드된 crtsys_test.sys의 전체 경로" displayname= "crtsys test" start= demand type= kernel + sc create CrtSysTest binpath= "crtsys_test.sys full path" displayname= "crtsys test" start= demand type= kernel sc start CrtSysTest crtsys_test.app.exe @@ -253,38 +235,34 @@ lib 디렉토리와 include 디렉토리를 타 프로젝트에서 사용하시 sc delete CrtSysTest ``` -3. 정상적으로 로드, Google Test 통과/언로드가 되었다면 테스트 성공한 것이며, 테스트 내용은 DebugView나 WinDbg를 통해서 확인 가능합니다. +3. If it loads normally and passes/unloads Google Test, the test is successful, and the test contents can be checked through DebugView or WinDbg. ## Usage -CMake를 사용하는것을 권장합니다. - -1. 프로젝트 디렉토리를 생성 후 이동하시기 바랍니다. +1. Please move after creating the project directory. ```batch mkdir test-project cd test-project ``` -2. CPM을 프로젝트 디렉토리에 다운로드 받으시기 바랍니다. +2. Download CPM to your project directory. ```batch mkdir cmake wget -O cmake/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake ``` - 혹은 + or ```batch mkdir cmake curl -o cmake/CPM.cmake -LJO https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake ``` - 로 CPM.cmake를 다운로드 받으시기 바랍니다. - -3. 프로젝트 디렉토리에 아래와 같은 파일을 작성해주시기 바랍니다. +3. Please write the following files in the project directory. - - 디렉토리 구조 + * Directory structure ```tree 📦test-project @@ -293,7 +271,7 @@ CMake를 사용하는것을 권장합니다. ┗ 📜CMakeLists.txt ``` - - CMakeLists.txt + * CMakeLists.txt ```CMake cmake_minimum_required(VERSION 3.14 FATAL_ERROR) @@ -303,28 +281,28 @@ CMake를 사용하는것을 권장합니다. include(cmake/CPM.cmake) set(CRTSYS_NTL_MAIN ON) # use ntl::main - CPMAddPackage("gh:ntoskrnl7/crtsys@0.1.6") + CPMAddPackage("gh:ntoskrnl7/crtsys@0.1.7") include(${crtsys_SOURCE_DIR}/cmake/CrtSys.cmake) # add driver crtsys_add_driver(crtsys_test src/main.cpp) ``` - - src/main.cpp + * src/main.cpp - - 아래와 같이 CRTSYS_NTL_MAIN을 활성화한다면 ntl::main을 진입점으로 정의하시기 바랍니다. **(권장)** + * If you enable CRTSYS_NTL_MAIN as shown below, define ntl::main as the entry point. **(recommend)** ```CMake set(CRTSYS_NTL_MAIN ON) ``` - - 만약 아래와 같이 CRTSYS_NTL_MAIN을 비활성화한다면 기존과 깉이 DriverEntry를 진입점으로 정의하시기 바랍니다. + * If you disable CRTSYS_NTL_MAIN as shown below, define DriverEntry as the entry point, which is different from the previous one. ```CMake set(CRTSYS_NTL_MAIN OFF) ``` - 아래는 ntl::main를 진입점으로 설정한 프로젝트의 예제 코드입니다. + Below is example code from a project with ntl::main set as entry point. ```C #include @@ -344,17 +322,17 @@ CMake를 사용하는것을 권장합니다. } ``` -4. 빌드를 수행합니다. +4. Perform a build. ```batch cmake -S . -B build cmake --build build ``` -5. 드라이버가 정상적으로 시작되고 종료되는지 확인하시기 바랍니다. +5. Please check if the driver starts up and shuts down normally. ```batch - sc create CrtSysTest binpath= "빌드된 crtsys_test.sys의 전체 경로" displayname= "crtsys test" start= demand type= kernel + sc create CrtSysTest binpath= "crtsys_test.sys full path" displayname= "crtsys test" start= demand type= kernel sc start CrtSysTest sc stop CrtSysTest sc delete CrtSysTest @@ -362,6 +340,7 @@ CMake를 사용하는것을 권장합니다. ## TODO -- CMake Install 구현 -- 아직 구현되지 않은 C++ 및 STL 기능 구현 -- Visual Studio 2017의 CRT 소스 코드 빌드 +* CMake install handling. +* Implementing C++ STL features not yet implemented. +* Build CRT source code in Visual Studio 2017. +* Running unit tests in GitHub Action. diff --git a/docs/ko-kr.md b/docs/ko-kr.md new file mode 100644 index 0000000..868cf37 --- /dev/null +++ b/docs/ko-kr.md @@ -0,0 +1,346 @@ +# crtsys + +**C**/C++ Run**t**ime library for **sys**tem file (Windows Kernel Driver) + +[![CMake](https://github.com/ntoskrnl7/crtsys/actions/workflows/cmake.yml/badge.svg)](https://github.com/ntoskrnl7/crtsys/actions/workflows/cmake.yml) +![GitHub](https://img.shields.io/github/license/ntoskrnl7/crtsys) +![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/ntoskrnl7/crtsys) +![Windows 7+](https://img.shields.io/badge/Windows-7+-blue?logo=windows&logoColor=white) +![Visual Studio 2017+](https://img.shields.io/badge/Visual%20Studio-2017+-682270?logo=visualstudio&logoColor=682270) +![CMake 3.14+](https://img.shields.io/badge/CMake-3.14+-yellow.svg?logo=cmake&logoColor=white) +![C++ 14+](https://img.shields.io/badge/C++-14+-white.svg?logo=cplusplus&logoColor=blue) +![Architecture](https://img.shields.io/badge/CPU-x86%20%2F%20x64%20%2F%20ARM%20%2F%20ARM64-blue.svg?logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTIgNTIiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MiA1MiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8Zz4KICAgICAgICA8cGF0aCBmaWxsPSJ3aGl0ZSIgZD0iTTQ5LDE5LjV2LTJoLTYuOTk4NDEzMXYtNy40OTU3ODg2SDE0LjQwMTYxMTNsLTQuNDAwMDI0NCw0LjUzMDAyOTN2MjcuNDY5OTcwN0gxNy41VjQ5aDJ2LTYuOTk1Nzg4NmgzVjQ5aDJ2LTYuOTk1Nzg4NmgzICAgVjQ5aDJ2LTYuOTk1Nzg4NmgzVjQ5aDJ2LTYuOTk1Nzg4NmgyLjgxMTU4NDVsNC42OTAwMDI0LTUuNjQwMDE0NlYzNC41SDQ5di0yaC02Ljk5ODQxMzF2LTNINDl2LTJoLTYuOTk4NDEzMXYtM0g0OXYtMmgtNi45OTg0MTMxICAgdi0zSDQ5eiBNMzYuMDAxNTg2OSwzMy41MDQyMTE0YzAsMS42NTAwMjQ0LTEuMzQ5OTc1NiwzLTMsM2gtMTRjLTEuNjU5OTczMSwwLTMtMS4zNDk5NzU2LTMtM3YtMTRjMC0xLjY1OTk3MzEsMS4zNDAwMjY5LTMsMy0zaDE0ICAgYzEuNjUwMDI0NCwwLDMsMS4zNDAwMjY5LDMsM1YzMy41MDQyMTE0eiIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSIyIiB3aWR0aD0iNyIgeD0iMyIgeT0iMTcuNSIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSIyIiB3aWR0aD0iNyIgeD0iMyIgeT0iMjIuNSIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSIyIiB3aWR0aD0iNyIgeD0iMyIgeT0iMjcuNSIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSIyIiB3aWR0aD0iNyIgeD0iMyIgeT0iMzIuNSIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSI3IiB3aWR0aD0iMiIgeD0iMzIuNSIgeT0iMyIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSI3IiB3aWR0aD0iMiIgeD0iMjcuNSIgeT0iMyIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSI3IiB3aWR0aD0iMiIgeD0iMjIuNSIgeT0iMyIgLz4KICAgICAgICA8cmVjdCBzdHlsZT0iZmlsbDp3aGl0ZSIgaGVpZ2h0PSI3IiB3aWR0aD0iMiIgeD0iMTcuNSIgeT0iMyIgLz4KICAgIDwvZz4KPC9zdmc+) + +* [한국어](../docs/ko-kr.md) + +**crtsys**는 커널 드라이버에서 C++ CRT 및 STL 기능을 사용할 수 있도록 도와주는 오픈소스 라이브러리입니다. + +- [crtsys](#crtsys) + - [Overview](#overview) + - [Goal](#goal) + - [C++ Standard](#c-standard) + - [STL](#stl) + - [C Standard](#c-standard-1) + - [NTL (NT Template Library)](#ntl-nt-template-library) + - [Requirements](#requirements) + - [Test Environments](#test-environments) + - [Build & Test](#build--test) + - [Usage](#usage) + - [TODO](#todo) + +## Overview + +이 프로젝트는 유사한 프로젝트 중 가장 많은 C++ STL 기능을 지원합니다. + +
+more + +커널 드라이버에서 C++ 및 STL을 사용할수있도록 도와주는 기능을 조사해보았는데, 그중 사용하기 가장 잘 구현되어있는 프로젝트는 아래와 같습니다. + +(22-04-26 기준) + +* [UCXXRT](https://github.com/MiroKaku/ucxxrt) + * 장점 + * 타 프로젝트와는 다르게 [Microsoft STL](https://github.com/microsoft/STL)를 직접 사용한다는 개념 자체가 좋아보임. + * 단점 + * 현재까지는 Win32 API와 연관성이 적은 std::string, std::vector 클래스 등 자료형이나 자료구조 관련 기능만 지원됨. + * vcxproj이 개발 환경에 종속되기 때문에 VS 버전이 바뀌면 일일이 수작업으로 수정해줘야함. + * x86에서 [이 코드](https://en.cppreference.com/w/cpp/language/throw#Example)를 실행하는 중 Hang이 발생함 + * Microsoft의 소스 코드를 그대로 복사하여 상단에 자신의 라이센스 주석만 넣어서 프로젝트에 포함시킨것으로 보이며 라이센스 문제에서 자유롭지 못해보임 + * 라이센스를 변경하는 것과 Microsoft CRT 및 STL 소스 코드를 재배포하는 행위는 허가되지 않은것으로 파악됨 + +* [KTL](https://github.com/DymOK93/KTL) + * 장점 + * CMake 사용됨 + * 개발 환경에 맞는 프로젝트 파일 생성 및 자동화가 용이함 + * CRT를 포함한 모든 코드를 독자적으로 구현 + * 라이센스로부터 자유로우며, 예외처리 코드 부분이 경량화되어 스택을 절약할 수 있음 + * 커널에 특화된 템플릿 라이브러리 제공 + * 커널에서만 존재하는 매커니즘에 대한 템플릿 라이브러리를 제공하기 때문에, 실제 드라이버 제작 시에는 STL을 보다도 더 애용할만한 기능이 많음 + * 높은 품질의 코드 + * 단점 + * Microsoft STL을 사용하는 것을 고려해서 작성된것으로 보이나 실제로 Microsoft STL을 사용하도록 변경하는 것에 어려움을 느낌 (오탈자 및 ktl 만 사용하는 코드가 생각보다 많았음) + * 러시아어 주석인데 파일이 UTF8+BOM로 인코딩되어있지 않아서 러시아 외 국가 환경에서 빌드를 실패함 + * x86 미지원 + +각 라이브러리의 단점을 보완하는 작업을 진행하던 중 + +1. UCXXRT은 Microsoft CRT 소스 코드를 그대로 복사하여 프로젝트에 포함시켰다는 점 +2. KTL은 x86을 지원하지 않는 점 + +으로 인해서 새로운 라이브러리를 개발하게 되었습니다. + +crtsys의 장점은 아래와 같습니다. + +1. Micosoft CRT와 STL을 최대한 비슷하게 지원하기 위해서 Microsoft CRT 소스코드를 사용하긴 하지만 Microsoft Visual Studio 혹은 Build Tools가 설치되어있는 디렉토리 내의 소스를 직접 빌드하는 방법으로 처리하여, Visual Studio를 합법적으로 사용하는 사용자는 라이센스 문제 없이 사용 가능합니다. +2. Win32 API를 구현한 [Ldk](https://github.com/ntoskrnl7/Ldk)를 활용하여 많은 범위의 STL 기능을 지원합니다. +3. CMake로 프로젝트를 구성할 수 있으며, [CPM](https://github.com/cpm-cmake/CPM.cmake)을 사용하여 정말 간편하게 라이브러리를 사용할 수 있도록 지원합니다. + +
+ +## Goal + +이 프로젝트는 커널 드라이버를 작성할 때 애플리케이션에서 C++ 및 STL을 사용하는 것과 유사한 개발 경험을 제공하는 것을 목표로 합니다. + +현재 지원되거나 향후 지원되는 기능은 다음과 같습니다. + +* 체크된 항목은 구현된 항목입니다. +* 테스트 코드가 작성된 항목의 경우 테스트 코드에 대한 링크가 추가되었습니다. + +### C++ Standard + +테스트 코드는 [C++ reference](https://en.cppreference.com)를 기반으로 작성되었습니다. + +* [Initialization](https://en.cppreference.com/w/cpp/language/initialization) + * [x] [Non-local variables](https://en.cppreference.com/w/cpp/language/initialization#Non-local_variables) + * [x] [Static initialization](https://en.cppreference.com/w/cpp/language/initialization#Static_initialization) + * [x] [Constant initialization](https://en.cppreference.com/w/cpp/language/constant_initialization) [(tested)](../test/src/cpp/lang/initialization.cpp#L13) + * [x] [Zero initialization](https://en.cppreference.com/w/cpp/language/zero_initialization) [(tested)](../test/src/cpp/lang/initialization.cpp#L41) + * [x] [Dynamic initialization](https://en.cppreference.com/w/cpp/language/initialization#Dynamic_initialization) [(tested)](../test/src/cpp/lang/initialization.cpp#L65) + * [ ] [Static local variables](https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables) + * [ ] thread_local + * [ ] static +* [Exceptions](https://en.cppreference.com/w/cpp/language/exceptions) + * [x] [throw](https://en.cppreference.com/w/cpp/language/throw) [(tested)](../test/src/cpp/lang/exceptions.cpp#L42) + * [x] [try-block](https://en.cppreference.com/w/cpp/language/try_catch) [(tested)](../test/src/cpp/lang/exceptions.cpp#L60) + * [x] [Function-try-block](https://en.cppreference.com/w/cpp/language/function-try-block) [(tested)](../test/src/cpp/lang/exceptions.cpp#L98) + +#### STL + +* [x] [std::chrono](https://en.cppreference.com/w/cpp/chrono) [(tested)](../test/src/cpp/stl/chrono.cpp#L15) +* [x] [std::thread](https://en.cppreference.com/w/cpp/thread) [(tested)](../test/src/cpp/stl/thread.cpp#L35) +* [x] [std::condition_variable](https://en.cppreference.com/w/cpp/thread/condition_variable) [(tested)](../test/src/cpp/stl/thread.cpp#L35) +* [x] [std::mutex](https://en.cppreference.com/w/cpp/thread/mutex) [(tested)](../test/src/cpp/stl/thread.cpp#L81) +* [x] [std::shared_mutex](https://en.cppreference.com/w/cpp/thread/shared_mutex) [(tested)](../test/src/cpp/stl/thread.cpp#L129) +* [x] [std::future](https://en.cppreference.com/w/cpp/thread/future) [(tested)](../test/src/cpp/stl/thread.cpp#L157) +* [x] [std::promise](https://en.cppreference.com/w/cpp/thread/promise) [(tested)](../test/src/cpp/stl/thread.cpp#L203) +* [x] [std::packaged_task](https://en.cppreference.com/w/cpp/thread/packaged_task) [(tested)](../test/src/cpp/stl/thread.cpp#L267) +* [x] [std::cin](https://en.cppreference.com/w/cpp/io/cin) +* [x] [std::clog](https://en.cppreference.com/w/cpp/io/clog) +* [x] [std::cerr](https://en.cppreference.com/w/cpp/io/cerr) +* [x] [std::cout](https://en.cppreference.com/w/cpp/io/cout) (tested) +* [x] [std::wcin](https://en.cppreference.com/w/cpp/io/cin) +* [x] [std::wclog](https://en.cppreference.com/w/cpp/io/clog) +* [x] [std::wcerr](https://en.cppreference.com/w/cpp/io/cerr) +* [x] [std::wcout](https://en.cppreference.com/w/cpp/io/cout) (tested) + +### C Standard + +* [x] math functions + * 직접 구현하기에는 시간이 부족하여 아래 프로젝트의 내용을 참고하엿습니다. 감사합니다! :-) + * [RetrievAL](https://github.com/SpoilerScriptsGroup/RetrievAL) + * [musl](https://github.com/bminor/musl) + +### NTL (NT Template Library) + +커널에서 더 나은 개발 환경을 지원하기 위한 기능을 제공합니다. + +* ntl::expand_stack [(tested)](../test/src/ntl.cpp#L5) + * 스택 크기를 확장하기 위한 함수 + * 기본적으로 커널 스택은 사용자 스레드 스택보다 훨씬 작은 크기를 할당받기 때문에 STL 기능을 사용하거나, 특히 throw를 수행할때 사용하는것이 좋습니다. +* ntl::status + * NTSTATUS에 대한 클래스 +* ntl::driver + * DRIVER_OBJECT에 대한 클래스 + * 기능 + * [x] DriverUnload [(tested)](../test/driver/src/main.cpp#L30) + * [x] Create device [(tested)](../test/driver/src/main.cpp#L39) +* ntl::device + * DEVICE_OBJECT에 대한 클래스 + * Features + * [x] Device Extension [(tested)](../test/driver/src/main.cpp#L39) + * [ ] IRP_MJ_CREATE + * [ ] IRP_MJ_CLOSE + * [x] IRP_MJ_DEVICE_CONTROL [(tested)](../test/app/src/main.cpp#L77) [(tested)](../test/driver/src/main.cpp#L47) +* ntl::driver_main [(tested)](../test/driver/src/main.cpp#L22) + * C++ 용 드라이버 진입점 + * ntl::expand_stack 함수로 스택을 최대 크기로 확장하여 호출됩니다. +* ntl::rpc + * User Mode App과 Kernel Driver간 손쉬운 통신 방법을 제공합니다. + * ntl::rpc::server [(tested)](../test/driver/src/main.cpp#L19) [(tested)](../test/common/rpc.hpp) + * ntl::rpc::client [(tested)](../test/app/src/main.cpp#L4) [(tested)](../test/common/rpc.hpp) + * 데이터 직렬화 부분을 직접 구현하기에는 시간이 부족하여 아래 프로젝트 내용을 참고했습니다. 감사합니다! :-) + * [Eyal Z/zpp serializer](https://github.com/eyalz800/serializer) + +## Requirements + +* Windows 7 이상 +* [Visual Studio / Build Tools 2017 이상](https://visualstudio.microsoft.com/ko/downloads/) +* [CMake 3.16 이상](https://cmake.org/download/) +* [Git](https://git-scm.com/downloads) + +## Test Environments + +* Windows 10 x64 + * **x86, x64, ARM, ARM64**로 빌드 가능하지만, 실제 테스트는 x86, x64 모듈에 대해서만 검증되었습니다. + +* CMake 3.21.4 +* Git 2.23.0 +* Visual Studio 2017 + * Visual Studio 2017의 CRT 소스 코드는 일부 헤더가 누락되어 빌드가 되지 않아서, UCXXRT의 코드를 일부 사용하여 지원합니다. + * 추후 누락된 헤더를 직접 작성하여 지원할 예정입니다. +* Visual Studio 2019 +* Visual Studio 2022 +* VC Tools + * 14.16.27023 + * 14.24.28314 + * 14.26.28801 + * 14.29.30133 + * 14.31.31103 +* Windows Kit (SDK, WDK) + * 10.0.17763.0 + * 10.0.18362.0 + * 10.0.22000.0 + +SDK와 WDK의 버전이 다르면 빌드가 실패할 가능성이 높으므로 **가능하다면 SDK와 WDK의 버전이 같은 환경에서 빌드하는것을 권장합니다.** + +## Build & Test + +1. 아래 명령을 수행하여 라이브러리 및 테스트 코드를 빌드하시기 바랍니다. + + ```Batch + git clone https://github.com/ntoskrnl7/crtsys + cd crtsys\test\build.bat + ``` + + 혹은 아래 명령을 수행하시면 지원되는 모든 아키텍쳐에 대해서 Debug, Release 구성을 모두 빌드합니다. + + ```Batch + git clone https://github.com/ntoskrnl7/crtsys + cd crtsys/test + build_all.bat test\app + build_all.bat test\driver + ``` + +2. build\Debug\crtsys_test.sys를 설치 및 로드하시기 바랍니다. + + * driver + x64 : test\driver\build_x64\Debug\crtsys_test.sys + x86 : test\driver\build_x86\Debug\crtsys_test.sys + ARM : test\driver\build_ARM\Debug\crtsys_test.sys + ARM64 : test\driver\build_ARM64\Debug\crtsys_test.sys + * app + x64 : test\driver\build_x64\Debug\crtsys_test_app.exe + x86 : test\driver\build_x86\Debug\crtsys_test_app.exe + ARM : test\driver\build_ARM\Debug\crtsys_test_app.exe + ARM64 : test\driver\build_ARM64\Debug\crtsys_test_app.exe + + ```batch + sc create CrtSysTest binpath= "crtsys_test.sys full path" displayname= "crtsys test" start= demand type= kernel + sc start CrtSysTest + + crtsys_test.app.exe + + sc stop CrtSysTest + sc delete CrtSysTest + ``` + +3. 정상적으로 로드, Google Test 통과/언로드가 되었다면 테스트 성공한 것이며, 테스트 내용은 DebugView나 WinDbg를 통해서 확인 가능합니다. + +## Usage + +1. 프로젝트 디렉토리를 생성 후 이동하시기 바랍니다. + + ```batch + mkdir test-project + cd test-project + ``` + +2. CPM을 프로젝트 디렉토리에 다운로드 받으시기 바랍니다. + + ```batch + mkdir cmake + wget -O cmake/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake + ``` + + 혹은 + + ```batch + mkdir cmake + curl -o cmake/CPM.cmake -LJO https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake + ``` + +3. 프로젝트 디렉토리에 아래와 같은 파일을 작성해주시기 바랍니다. + + * 디렉토리 구조 + + ```tree + 📦test-project + ┣ 📂src + ┃ ┗ 📜main.cpp + ┗ 📜CMakeLists.txt + ``` + + * CMakeLists.txt + + ```CMake + cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + + project(crtsys_test LANGUAGES C CXX) + + include(cmake/CPM.cmake) + + set(CRTSYS_NTL_MAIN ON) # use ntl::main + CPMAddPackage("gh:ntoskrnl7/crtsys@0.1.7") + include(${crtsys_SOURCE_DIR}/cmake/CrtSys.cmake) + + # add driver + crtsys_add_driver(crtsys_test src/main.cpp) + ``` + + * src/main.cpp + + * 아래와 같이 CRTSYS_NTL_MAIN을 활성화한다면 ntl::main을 진입점으로 정의하시기 바랍니다. **(권장)** + + ```CMake + set(CRTSYS_NTL_MAIN ON) + ``` + + * 만약 아래와 같이 CRTSYS_NTL_MAIN을 비활성화한다면 기존과 깉이 DriverEntry를 진입점으로 정의하시기 바랍니다. + + ```CMake + set(CRTSYS_NTL_MAIN OFF) + ``` + + 아래는 ntl::main를 진입점으로 설정한 프로젝트의 예제 코드입니다. + + ```C + #include + #include + + ntl::status ntl::main(ntl::driver &driver, const std::wstring ®istry_path) { + + std::wcout << "load (registry_path :" << registry_path << ")\n"; + + // TODO + + driver.on_unload([registry_path]() { + std::wcout << "unload (registry_path :" << registry_path << ")\n"; + }); + + return status::ok(); + } + ``` + +4. 빌드를 수행합니다. + + ```batch + cmake -S . -B build + cmake --build build + ``` + +5. 드라이버가 정상적으로 시작되고 종료되는지 확인하시기 바랍니다. + + ```batch + sc create CrtSysTest binpath= "crtsys_test.sys full path" displayname= "crtsys test" start= demand type= kernel + sc start CrtSysTest + sc stop CrtSysTest + sc delete CrtSysTest + ``` + +## TODO + +* CMake install 처리. +* 아직 구현되지 않은 C++ 및 STL 기능 구현 +* Visual Studio 2017의 CRT 소스 코드 빌드 +* GitHub Action에서 단위 테스트 실행 diff --git a/include/.internal/version b/include/.internal/version index 71b4932..20bedbb 100644 --- a/include/.internal/version +++ b/include/.internal/version @@ -26,6 +26,6 @@ Environment: #define CRTSYS_VERSION_MAJOR 0 #define CRTSYS_VERSION_MINOR 1 -#define CRTSYS_VERSION_PATCH 6 +#define CRTSYS_VERSION_PATCH 7 #endif // _CRTSYS_VERSION_ \ No newline at end of file diff --git a/include/ntl/device b/include/ntl/device new file mode 100644 index 0000000..bef5464 --- /dev/null +++ b/include/ntl/device @@ -0,0 +1,146 @@ +/** + * @file device + * @author jungkwang.lee (ntoskrnl7@google.com) + * @brief + * + * @copyright Copyright (c) 2022 NT Template Library Authoers. + * + */ +#pragma once + +#include "except" +#include +#include +#include + +#ifndef _NTDDK_ +#include +#endif + +namespace ntl { +class device { + friend class device_dispatch_invoker; + +public: + using on_device_control_fn = std::function; + +private: + friend class driver; + + struct context_base { + context_base(const std::type_info &type) : type(type) {} + const std::type_info &type; + on_device_control_fn on_device_control; + }; + + template class context; + + template <> struct context : public context_base { + context() : context_base(typeid(void)) {} + }; + + template struct context : public context_base { + context() : context_base(typeid(T)) {} + T data; + }; + +public: + using type_t = DEVICE_TYPE; + + class options { + friend class driver; + friend class device; + + public: + options() : type_(FILE_DEVICE_UNKNOWN), exclusive_(false) {} + + options &name(const std::wstring &name) { + name_ = name; + return *this; + } + options &type(type_t type) { + type_ = type; + return *this; + } + options &exclusive(bool exclusive = true) { + exclusive_ = exclusive; + return *this; + } + + private: + bool exclusive_; + std::wstring name_; + type_t type_; + }; + +protected: + device(PDEVICE_OBJECT device, const options &opts, + std::function finalizer) + : object_(device), name_(opts.name_), finalizer_(finalizer) {} + +private: + device(const device &) = delete; + +public: + device() : object_(nullptr) {} + + device(device &&other) { *this = std::move(other); } + + device &operator=(device &&rhs) { + object_ = rhs.detach(); + name_.swap(rhs.name_); + finalizer_ = std::move(rhs.finalizer_); + return *this; + } + + ~device() { + auto obj = detach(); + if (obj) { + if (finalizer_) + finalizer_(); + IoDeleteDevice(obj); + } + } + +private: + template context *get_context() { + return reinterpret_cast *>(object_->DeviceExtension); + } + +public: + template Extension &extension() { + auto ext = get_context(); + if (ext->type != typeid(Extension)) + throw std::runtime_error("bad type"); + return ext->data; + } + + device &on_device_control(on_device_control_fn &&f) { + get_context()->on_device_control = f; + return *this; + } + + const std::wstring &name() const { + if (!object_) + throw std::runtime_error("invalid device object."); + return name_; + } + + type_t type() const { + if (!object_) + throw std::runtime_error("invalid device object."); + return object_->DeviceType; + } + + PDEVICE_OBJECT detach() { + return reinterpret_cast( + InterlockedExchangePointer(reinterpret_cast(&object_), NULL)); + } + +private: + PDEVICE_OBJECT object_; + std::wstring name_; + std::function finalizer_; +}; +} // namespace ntl \ No newline at end of file diff --git a/include/ntl/driver b/include/ntl/driver index 77c441a..e4c5ca5 100644 --- a/include/ntl/driver +++ b/include/ntl/driver @@ -11,128 +11,109 @@ #ifndef _NTDDK_ #include #endif +#ifndef ClearFlag +#define ClearFlag(_F, _SF) ((_F) &= ~(_SF)) +#endif + +#include "device" +#include "except" #include "status" +#include "unicode_string" +#include #include -#include +#include +#include namespace ntl { -class unicode_string { -public: - unicode_string(std::wstring &&str) : str_(std::move(str)) { - value_.Buffer = &str_[0]; - value_.MaximumLength = value_.Length = (USHORT)str_.size() * sizeof(WCHAR); - } - unicode_string(const std::wstring &str) : str_(str) { - value_.Buffer = &str_[0]; - value_.MaximumLength = value_.Length = (USHORT)str_.size() * sizeof(WCHAR); - } - unicode_string(std::wstring &str) { - value_.Buffer = &str[0]; - value_.MaximumLength = value_.Length = (USHORT)str.size() * sizeof(WCHAR); - } - - UNICODE_STRING &operator*() { return value_; } - -private: - std::wstring str_; - UNICODE_STRING value_; -}; - -class device { - friend class driver; - -public: - using type = ULONG; - -protected: - device(PDEVICE_OBJECT device) : object_(device) {} - -private: - device(const device &) = delete; - -public: - device() : object_(NULL) {} - - device(device &&other) { *this = std::move(other); } - - device &operator=(device &&rhs) { - object_ = rhs.object_; - rhs.object_ = NULL; - return *this; - } - - ~device() { - if (object_) - IoDeleteDevice(object_); - } - -public: - PDEVICE_OBJECT detach() { - return (PDEVICE_OBJECT)InterlockedExchangePointer((PVOID *)&object_, NULL); +namespace detail { +namespace driver { +inline ntl::except make_exception(NTSTATUS status) { + const char *message; + switch (status) { + case STATUS_INSUFFICIENT_RESOURCES: + message = " Insufficient system resources exist to complete the API."; + break; + case STATUS_OBJECT_NAME_COLLISION: + message = "Object Name already exists."; + break; + default: + message = "Unknown error."; + break; } - -#ifdef NTL_EXPORT_OBJECT_PTR -public: -#else -protected: -#endif - PDEVICE_OBJECT ptr() const { return object_; } - -private: - PDEVICE_OBJECT object_; -}; + return std::move(ntl::except(status, message)); +} +} // namespace driver +} // namespace detail class driver { -public: + friend class driver_initializer; friend class driver_unload_invoker; - friend class device_dispatch_invoker; - - using unload_routine = std::function; - - using devcie_control_routine = - std::function; +private: driver(PDRIVER_OBJECT driver) : object_(driver), name_(driver->DriverName.Buffer, driver->DriverName.Length / sizeof(WCHAR)) {} - device create_device(const std::wstring &name, device::type type) { - unicode_string dev_name(L"\\Device\\" + name); - PDEVICE_OBJECT dev = NULL; - status s = IoCreateDevice(object_, 0, &*dev_name, type, 0, FALSE, &dev); - return dev; - } +public: + using unload_routine = std::function; + + using devcie_control_routine = std::function; - void on_device_control(devcie_control_routine &&f) { - devcie_control_routine_ = f; + template + std::shared_ptr create_device(device::options &opts) { + PDEVICE_OBJECT dev = NULL; + status s = [&dev, &opts, this]() { + ULONG extension_size = (ULONG)sizeof(device::context); + if (opts.name_.empty()) { + return IoCreateDevice(object_, extension_size, NULL, opts.type_, 0, + opts.exclusive_, &dev); + } else { + unicode_string dev_name(L"\\Device\\" + opts.name_); + return IoCreateDevice(object_, extension_size, &*dev_name, opts.type_, + 0, opts.exclusive_, &dev); + } + }(); + if (s.is_err()) + throw detail::driver::make_exception(s); + + new (dev->DeviceExtension) device::context(); + + auto ptr = std::make_shared(std::move(device(dev, opts, [dev]() { + reinterpret_cast *>(dev->DeviceExtension) + ->~context(); + }))); + devices_.insert({dev, ptr}); + + ClearFlag(dev->Flags, DO_DEVICE_INITIALIZING); + + return ptr; } void on_unload(unload_routine &&f) { unload_routine_ = f; } - PDRIVER_OBJECT detach() { - return (PDRIVER_OBJECT)InterlockedExchangePointer((PVOID *)&object_, NULL); + std::shared_ptr devices(PDEVICE_OBJECT ptr) noexcept { + try { + return devices_[ptr]; + } catch (...) { + return nullptr; + } + }; + + std::vector> devices() { + std::vector> vec; + std::transform(devices_.begin(), devices_.end(), vec.end(), + [](auto &item) { return item.second; }); + return vec; } const std::wstring &name() const { return name_; } -#ifdef NTL_EXPORT_OBJECT_PTR -public: -#else -protected: -#endif - PDRIVER_OBJECT ptr() const { return object_; } - -private: - void unload() { - if (unload_routine_) - unload_routine_(); - } - private: PDRIVER_OBJECT object_; std::wstring name_; - devcie_control_routine devcie_control_routine_; + std::unordered_map> devices_; unload_routine unload_routine_; }; diff --git a/include/ntl/expand_stack b/include/ntl/expand_stack index 7ae4e49..37e3939 100644 --- a/include/ntl/expand_stack +++ b/include/ntl/expand_stack @@ -115,10 +115,11 @@ inline ntl::except make_exception(NTSTATUS status) { const char *message; switch (status) { case STATUS_INVALID_PARAMETER_3: - message = "The stack_size parameter is greater than MAXIMUM_EXPANSION_SIZE"; + message = + "The stack_size parameter is greater than MAXIMUM_EXPANSION_SIZE."; break; case STATUS_INVALID_PARAMETER_4: - message = "The routine was called at IRQL = DISPATCH_LEVEL"; + message = "The routine was called at IRQL = DISPATCH_LEVEL."; break; case STATUS_NO_MEMORY: message = "Not enough memory is available to expand the stack."; @@ -128,7 +129,7 @@ inline ntl::except make_exception(NTSTATUS status) { "system's internal limits on stack space."; break; default: - message = "Unknown error"; + message = "Unknown error."; break; } return std::move(ntl::except(status, message)); diff --git a/include/ntl/rpc/client b/include/ntl/rpc/client index 9a0ebee..57c5865 100644 --- a/include/ntl/rpc/client +++ b/include/ntl/rpc/client @@ -66,62 +66,66 @@ private: } // namespace rpc } // namespace ntl -#define NTL_ADD_CALLBACK_0(_ret_, _name_, ...) \ +#define NTL_RPC_BEGIN(_rpc_name_) namespace _rpc_name_ { +#define NTL_RPC_END(_rpc_name_) } + +#define NTL_ADD_CALLBACK_0(_rpc_name_, _ret_, _name_, ...) \ static const int _name_##_0_index = __LINE__; \ inline _ret_ _name_() { \ - return ntl::rpc::client(NTL_RPC_NAME).invoke<_ret_>(__LINE__); \ + return ntl::rpc::client(L#_rpc_name_).invoke<_ret_>(__LINE__); \ } -#define NTL_ADD_CALLBACK_1(_ret_, _name_, _arg0_type_, _arg0_name_, ...) \ +#define NTL_ADD_CALLBACK_1(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, ...) \ static const int _name_##_1_index = __LINE__; \ inline _ret_ _name_(_arg0_type_ _arg0_name_) { \ - return ntl::rpc::client(NTL_RPC_NAME) \ + return ntl::rpc::client(L#_rpc_name_) \ .invoke<_ret_>(__LINE__, std::forward<_arg0_type_>(_arg0_name_)); \ } -#define NTL_ADD_CALLBACK_2(_ret_, _name_, _arg0_type_, _arg0_name_, \ - _arg1_type_, _arg1_name_, ...) \ +#define NTL_ADD_CALLBACK_2(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, _arg1_type_, _arg1_name_, ...) \ static const int _name_##_2_index = __LINE__; \ inline _ret_ _name_(_arg0_type_ _arg0_name_, _arg1_type_ _arg1_name_) { \ - return ntl::rpc::client(NTL_RPC_NAME) \ + return ntl::rpc::client(L#_rpc_name_) \ .invoke<_ret_>(__LINE__, std::forward<_arg0_type_>(_arg0_name_), \ std::forward<_arg1_type_>(_arg1_name_)); \ } -#define NTL_ADD_CALLBACK_3(_ret_, _name_, _arg0_type_, _arg0_name_, \ - _arg1_type_, _arg1_name_, _arg2_type_, _arg2_name_, \ - ...) \ +#define NTL_ADD_CALLBACK_3(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, _arg1_type_, _arg1_name_, _arg2_type_, \ + _arg2_name_, ...) \ static const int _name_##_3_index = __LINE__; \ inline _ret_ _name_(_arg0_type_ _arg0_name_, _arg1_type_ _arg1_name_, \ _arg2_type_ _arg2_name_) { \ - return ntl::rpc::client(NTL_RPC_NAME) \ + return ntl::rpc::client(L#_rpc_name_) \ .invoke<_ret_>(__LINE__, std::forward<_arg0_type_>(_arg0_name_), \ std::forward<_arg1_type_>(_arg1_name_), \ std::forward<_arg2_type_>(_arg2_name_)); \ } -#define NTL_ADD_CALLBACK_4(_ret_, _name_, _arg0_type_, _arg0_name_, \ - _arg1_type_, _arg1_name_, _arg2_type_, _arg2_name_, \ - _arg3_type_, _arg3_name_, ...) \ +#define NTL_ADD_CALLBACK_4(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, _arg1_type_, _arg1_name_, _arg2_type_, \ + _arg2_name_, _arg3_type_, _arg3_name_, ...) \ static const int _name_##_4_index = __LINE__; \ inline _ret_ _name_(_arg0_type_ _arg0_name_, _arg1_type_ _arg1_name_, \ _arg2_type_ _arg2_name_, _arg3_type_ _arg3_name_) { \ - return ntl::rpc::client(NTL_RPC_NAME) \ + return ntl::rpc::client(L#_rpc_name_) \ .invoke<_ret_>(__LINE__, std::forward<_arg0_type_>(_arg0_name_), \ std::forward<_arg1_type_>(_arg1_name_), \ std::forward<_arg2_type_>(_arg2_name_), \ std::forward<_arg3_type_>(_arg3_name_)); \ } -#define NTL_ADD_CALLBACK_5(_ret_, _name_, _arg0_type_, _arg0_name_, \ - _arg1_type_, _arg1_name_, _arg2_type_, _arg2_name_, \ - _arg3_type_, _arg3_name_, _arg4_type_, _arg4_name_, \ - ...) \ +#define NTL_ADD_CALLBACK_5(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, _arg1_type_, _arg1_name_, _arg2_type_, \ + _arg2_name_, _arg3_type_, _arg3_name_, _arg4_type_, \ + _arg4_name_, ...) \ static const int _name_##_5_index = __LINE__; \ inline _ret_ _name_(_arg0_type_ _arg0_name_, _arg1_type_ _arg1_name_, \ _arg2_type_ _arg2_name_, _arg3_type_ _arg3_name_, \ _arg4_type_ _arg4_name_) { \ - return ntl::rpc::client(NTL_RPC_NAME) \ + return ntl::rpc::client(L#_rpc_name_) \ .invoke<_ret_>(__LINE__, std::forward<_arg0_type_>(_arg0_name_), \ std::forward<_arg1_type_>(_arg1_name_), \ std::forward<_arg2_type_>(_arg2_name_), \ diff --git a/include/ntl/rpc/server b/include/ntl/rpc/server index b2b9e80..93608ab 100644 --- a/include/ntl/rpc/server +++ b/include/ntl/rpc/server @@ -11,7 +11,20 @@ #include "common" -#define NTL_ADD_CALLBACK_0(_ret_, _name_, ...) \ +#define NTL_RPC_BEGIN(_rpc_name_) \ + namespace _rpc_name_ { \ + std::shared_ptr init(ntl::driver &driver) { \ + auto dev = driver.create_device( \ + ntl::device::options().name(L#_rpc_name_).type(FILE_DEVICE_NTL_RPC)); \ + dev->on_device_control([](uint32_t ctl_code, const uint8_t *in_buffer, size_t in_buffer_length, uint8_t *out_buffer, size_t *out_buffer_length) { + +#define NTL_RPC_END(_rpc_name_) \ + }); \ + return std::make_shared(dev); \ + } \ + } + +#define NTL_ADD_CALLBACK_0(_rpc_name_, _ret_, _name_, ...) \ if (IoGetFunctionCodeFromCtlCode(ctl_code) == __LINE__) { \ auto _name_ = []() -> _ret_ __VA_ARGS__; \ if constexpr (std::is_void_v<_ret_>) { \ @@ -24,7 +37,8 @@ return; \ } -#define NTL_ADD_CALLBACK_1(_ret_, _name_, _arg0_type_, _arg0_name_, ...) \ +#define NTL_ADD_CALLBACK_1(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, ...) \ if (IoGetFunctionCodeFromCtlCode(ctl_code) == __LINE__) { \ auto _name_ = [](_arg0_type_ _arg0_name_) -> _ret_ __VA_ARGS__; \ zpp::serializer::memory_view_input_archive in(in_buffer, \ @@ -45,8 +59,8 @@ return; \ } -#define NTL_ADD_CALLBACK_2(_ret_, _name_, _arg0_type_, _arg0_name_, \ - _arg1_type_, _arg1_name_, ...) \ +#define NTL_ADD_CALLBACK_2(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, _arg1_type_, _arg1_name_, ...) \ if (IoGetFunctionCodeFromCtlCode(ctl_code) == __LINE__) { \ auto _name_ = [](_arg0_type_ _arg0_name_, \ _arg1_type_ _arg1_name_) -> _ret_ __VA_ARGS__; \ @@ -71,9 +85,9 @@ return; \ } -#define NTL_ADD_CALLBACK_3(_ret_, _name_, _arg0_type_, _arg0_name_, \ - _arg1_type_, _arg1_name_, _arg2_type_, _arg2_name_, \ - ...) \ +#define NTL_ADD_CALLBACK_3(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, _arg1_type_, _arg1_name_, _arg2_type_, \ + _arg2_name_, ...) \ if (IoGetFunctionCodeFromCtlCode(ctl_code) == __LINE__) { \ auto _name_ = [](_arg0_type_ _arg0_name_, _arg1_type_ _arg1_name_, \ _arg2_type_ _arg2_name_) -> _ret_ __VA_ARGS__; \ @@ -102,9 +116,9 @@ return; \ } -#define NTL_ADD_CALLBACK_4(_ret_, _name_, _arg0_type_, _arg0_name_, \ - _arg1_type_, _arg1_name_, _arg2_type_, _arg2_name_, \ - _arg3_type_, _arg3_name_, ...) \ +#define NTL_ADD_CALLBACK_4(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, _arg1_type_, _arg1_name_, _arg2_type_, \ + _arg2_name_, _arg3_type_, _arg3_name_, ...) \ if (IoGetFunctionCodeFromCtlCode(ctl_code) == __LINE__) { \ auto _name_ = [](_arg0_type_ _arg0_name_, _arg1_type_ _arg1_name_, \ _arg2_type_ _arg2_name_, \ @@ -137,10 +151,10 @@ return; \ } -#define NTL_ADD_CALLBACK_5(_ret_, _name_, _arg0_type_, _arg0_name_, \ - _arg1_type_, _arg1_name_, _arg2_type_, _arg2_name_, \ - _arg3_type_, _arg3_name_, _arg4_type_, _arg4_name_, \ - ...) \ +#define NTL_ADD_CALLBACK_5(_rpc_name_, _ret_, _name_, _arg0_type_, \ + _arg0_name_, _arg1_type_, _arg1_name_, _arg2_type_, \ + _arg2_name_, _arg3_type_, _arg3_name_, _arg4_type_, \ + _arg4_name_, ...) \ if (IoGetFunctionCodeFromCtlCode(ctl_code) == __LINE__) { \ auto _name_ = [](_arg0_type_ _arg0_name_, _arg1_type_ _arg1_name_, \ _arg2_type_ _arg2_name_, _arg3_type_ _arg3_name_, \ @@ -183,8 +197,7 @@ namespace ntl { namespace rpc { class server { public: - server(driver &driver, const std::wstring &name) - : dev_(driver.create_device(name, FILE_DEVICE_NTL_RPC)) {} + server(std::shared_ptr &device) : dev_(device) {} server(server &&other) { *this = std::move(other); } @@ -197,7 +210,7 @@ private: server(const server &) = delete; private: - device dev_; + std::shared_ptr dev_; }; } // namespace rpc } // namespace ntl diff --git a/include/ntl/status b/include/ntl/status index f20cf36..2c8f063 100644 --- a/include/ntl/status +++ b/include/ntl/status @@ -14,6 +14,7 @@ namespace ntl { class status { public: status(NTSTATUS status) : status_(status) {} + bool is_ok() const { return NT_SUCCESS(status_); } bool is_info() const { return NT_INFORMATION(status_); } bool is_warn() const { return NT_WARNING(status_); } diff --git a/include/ntl/unicode_string b/include/ntl/unicode_string new file mode 100644 index 0000000..4cb8e1f --- /dev/null +++ b/include/ntl/unicode_string @@ -0,0 +1,41 @@ +/** + * @file unicode_string + * @author jungkwang.lee (ntoskrnl7@google.com) + * @brief + * + * @copyright Copyright (c) 2022 NT Template Library Authoers. + * + */ +#pragma once + +#include + +#ifndef _NTDDK_ +#include +#endif + +namespace ntl { +class unicode_string { +public: + unicode_string(std::wstring &&str) : str_(std::move(str)) { + value_.Buffer = &str_[0]; + value_.MaximumLength = value_.Length = (USHORT)str_.size() * sizeof(WCHAR); + } + unicode_string(const std::wstring &str) : str_(str) { + value_.Buffer = &str_[0]; + value_.MaximumLength = value_.Length = (USHORT)str_.size() * sizeof(WCHAR); + } + unicode_string(std::wstring &str) { + value_.Buffer = &str[0]; + value_.MaximumLength = value_.Length = (USHORT)str.size() * sizeof(WCHAR); + } + + const std::wstring &c_str() const { return str_; } + + UNICODE_STRING &operator*() { return value_; } + +private: + std::wstring str_; + UNICODE_STRING value_; +}; +} // namespace ntl \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 4bd4a1b..590bc9c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,50 +24,42 @@ std::unique_ptr this_driver; namespace ntl { class device_dispatch_invoker { public: - static status invoke(ntl::driver &driver, PDEVICE_OBJECT DeviceObject, - PIRP Irp) { + static status invoke(ntl::device &device, PIRP irp) { + auto ctx = device.get_context(); NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; - Irp->IoStatus.Information = 0; - - PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); - DeviceObject; - switch (IrpSp->MajorFunction) { + irp->IoStatus.Information = 0; + PIO_STACK_LOCATION irp_sp = IoGetCurrentIrpStackLocation(irp); + switch (irp_sp->MajorFunction) { case IRP_MJ_CREATE: - /// - if (driver.devcie_control_routine_) { + if (ctx->on_device_control) Status = STATUS_SUCCESS; - break; - } break; case IRP_MJ_CLOSE: - // - if (driver.devcie_control_routine_) { + if (ctx->on_device_control) Status = STATUS_SUCCESS; - break; - } break; case IRP_MJ_DEVICE_CONTROL: - if (driver.devcie_control_routine_) { + if (ctx->on_device_control) { __try { - Irp->IoStatus.Information = - IrpSp->Parameters.DeviceIoControl.OutputBufferLength; - size_t out_len = (size_t)Irp->IoStatus.Information; - driver.devcie_control_routine_( - IrpSp->Parameters.DeviceIoControl.IoControlCode, - (const uint8_t *)Irp->AssociatedIrp.SystemBuffer, - (size_t)IrpSp->Parameters.DeviceIoControl.InputBufferLength, - (uint8_t *)Irp->AssociatedIrp.SystemBuffer, &out_len); - Irp->IoStatus.Information = (ULONG_PTR)out_len; + irp->IoStatus.Information = + irp_sp->Parameters.DeviceIoControl.OutputBufferLength; + size_t out_len = (size_t)irp->IoStatus.Information; + ctx->on_device_control( + irp_sp->Parameters.DeviceIoControl.IoControlCode, + (const uint8_t *)irp->AssociatedIrp.SystemBuffer, + (size_t)irp_sp->Parameters.DeviceIoControl.InputBufferLength, + (uint8_t *)irp->AssociatedIrp.SystemBuffer, &out_len); + irp->IoStatus.Information = (ULONG_PTR)out_len; Status = STATUS_SUCCESS; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); - Irp->IoStatus.Information = 0; + irp->IoStatus.Information = 0; } } break; } - Irp->IoStatus.Status = Status; - IoCompleteRequest(Irp, IO_NO_INCREMENT); + irp->IoStatus.Status = Status; + IoCompleteRequest(irp, IO_NO_INCREMENT); return Status; } }; @@ -170,7 +162,7 @@ CrtSysInitializeTebThreadLocalStoragePointer ( #include NTSTATUS -CrtSysDispatchCreate ( +CrtSysDispatchRoutine ( _In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp ) @@ -178,131 +170,104 @@ CrtSysDispatchCreate ( #if CRTSYS_USE_NTL_MAIN // clang-format on if (this_driver) { - return ntl::device_dispatch_invoker::invoke(*this_driver.get(), - DeviceObject, Irp); + auto dev = this_driver->devices(DeviceObject); + if (dev) + return ntl::device_dispatch_invoker::invoke(*dev.get(), Irp); } -// clang-format off #else - if (CrtsyspDispatchCreate) { - return CrtsyspDispatchCreate( DeviceObject, - Irp ); - } -#endif - return STATUS_INVALID_DEVICE_REQUEST; -} - -NTSTATUS -CrtSysDispatchClose ( - _In_ PDEVICE_OBJECT DeviceObject, - _Inout_ PIRP Irp - ) -{ -#if CRTSYS_USE_NTL_MAIN - // clang-format on - if (this_driver) { - return ntl::device_dispatch_invoker::invoke(*this_driver.get(), - DeviceObject, Irp); + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + switch (IrpSp->MajorFunction) { + case IRP_MJ_CREATE: + if (CrtsyspDispatchCreate) + return CrtsyspDispatchCreate(DeviceObject, Irp); + break; + + case IRP_MJ_CLOSE: + if (CrtsyspDispatchClose) + return CrtsyspDispatchClose(DeviceObject, Irp); + break; + case IRP_MJ_DEVICE_CONTROL: + if (CrtsyspDispatchDeviceControl) + return CrtsyspDispatchDeviceControl(DeviceObject, Irp); + break; } -// clang-format off -#else - if (CrtsyspDispatchClose) { - return CrtsyspDispatchClose( DeviceObject, - Irp ); - } #endif - return STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_DEVICE_REQUEST; } -NTSTATUS -CrtSysDispatchDeviceControl ( - _In_ PDEVICE_OBJECT DeviceObject, - _Inout_ PIRP Irp - ) -{ -#if CRTSYS_USE_NTL_MAIN - // clang-format on - if (this_driver) { - return ntl::device_dispatch_invoker::invoke(*this_driver.get(), - DeviceObject, Irp); +namespace ntl { +class driver_initializer { +public: + static std::unique_ptr make_driver(PDRIVER_OBJECT object) { + return std::make_unique(std::move(ntl::driver(object))); } -// clang-format off -#else - if (CrtsyspDispatchDeviceControl) { - return CrtsyspDispatchDeviceControl( DeviceObject, - Irp ); - } -#endif - return STATUS_INVALID_DEVICE_REQUEST; -} +}; +} // namespace ntl EXTERN_C NTSTATUS -CrtSysDriverEntry ( - _In_ PDRIVER_OBJECT DriverObject, - _In_ PUNICODE_STRING RegistryPath - ) -{ - PAGED_CODE(); +CrtSysDriverEntry(_In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath) { + PAGED_CODE(); - NTSTATUS status = CrtSysInitializeTebThreadLocalStoragePointer(); - if (! NT_SUCCESS(status)) { - return status; - } + NTSTATUS status = CrtSysInitializeTebThreadLocalStoragePointer(); + if (!NT_SUCCESS(status)) { + return status; + } - status = LdkInitialize( DriverObject, - RegistryPath, - 0 ); - if (! NT_SUCCESS(status)) { - return status; - } + status = LdkInitialize(DriverObject, RegistryPath, 0); + if (!NT_SUCCESS(status)) { + return status; + } - NTSTATUS - CrtSysInitializeFlsXState ( - VOID - ); - status = CrtSysInitializeFlsXState(); - if (! NT_SUCCESS(status)) { - LdkTerminate(); - return status; - } + NTSTATUS + CrtSysInitializeFlsXState(VOID); + status = CrtSysInitializeFlsXState(); + if (!NT_SUCCESS(status)) { + LdkTerminate(); + return status; + } #if CRTSYS_USE_LIBCNTPR - NTSTATUS - CrtSyspInitializeForLibcntpr ( - VOID - ); - status = CrtSyspInitializeForLibcntpr(); - if (! NT_SUCCESS(status)) { - LdkTerminate(); - return status; - } + NTSTATUS + CrtSyspInitializeForLibcntpr(VOID); + status = CrtSyspInitializeForLibcntpr(); + if (!NT_SUCCESS(status)) { + LdkTerminate(); + return status; + } #endif - if (!__scrt_initialize_onexit_tables(__scrt_module_type::exe)) { - KdBreakPoint(); - LdkTerminate(); - return STATUS_FAILED_DRIVER_ENTRY; - } + if (!__scrt_initialize_onexit_tables(__scrt_module_type::exe)) { + KdBreakPoint(); + LdkTerminate(); + return STATUS_FAILED_DRIVER_ENTRY; + } - if (!__scrt_initialize_crt(__scrt_module_type::exe)) { - KdBreakPoint(); - LdkTerminate(); - return STATUS_FAILED_DRIVER_ENTRY; - } + if (!__scrt_initialize_crt(__scrt_module_type::exe)) { + KdBreakPoint(); + LdkTerminate(); + return STATUS_FAILED_DRIVER_ENTRY; + } - __scrt_current_native_startup_state = __scrt_native_startup_state::initializing; + __scrt_current_native_startup_state = + __scrt_native_startup_state::initializing; - if (_initterm_e(__xi_a, __xi_z) != 0) { - CrtSysDriverUnload( DriverObject ); - return STATUS_FAILED_DRIVER_ENTRY; - } - - _initterm(__xc_a, __xc_z); + if (_initterm_e(__xi_a, __xi_z) != 0) { + CrtSysDriverUnload(DriverObject); + return STATUS_FAILED_DRIVER_ENTRY; + } + + _initterm(__xc_a, __xc_z); - __scrt_current_native_startup_state = __scrt_native_startup_state::initialized; + __scrt_current_native_startup_state = + __scrt_native_startup_state::initialized; #if CRTSYS_USE_NTL_MAIN // clang-format on - std::unique_ptr driver = - std::make_unique(DriverObject); + + auto driver = ntl::driver_initializer::make_driver(DriverObject); + if (!driver) { return STATUS_INSUFFICIENT_RESOURCES; } @@ -326,9 +291,8 @@ CrtSysDriverEntry ( CrtsyspDispatchClose = DriverObject->MajorFunction[IRP_MJ_CLOSE]; CrtsyspDispatchDeviceControl = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]; #endif - DriverObject->MajorFunction[IRP_MJ_CREATE] = CrtSysDispatchCreate; - DriverObject->MajorFunction[IRP_MJ_CLOSE] = CrtSysDispatchClose; - DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CrtSysDispatchDeviceControl; + for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) + DriverObject->MajorFunction[i] = CrtSysDispatchRoutine; DriverObject->DriverUnload = CrtSysDriverUnload; return status; } @@ -338,7 +302,10 @@ CrtSysDriverEntry ( namespace ntl { class driver_unload_invoker { public: - static void unload(driver &driver) { driver.unload(); } + static void unload(driver &driver) { + if (driver.unload_routine_) + driver.unload_routine_(); + } }; } // namespace ntl // clang-format off @@ -354,10 +321,11 @@ CrtSysDriverUnload ( #if CRTSYS_USE_NTL_MAIN // clang-format on UNREFERENCED_PARAMETER(DriverObject); - if (this_driver) { + + if (this_driver) ntl::driver_unload_invoker::unload(*this_driver.get()); - } - // clang-format off + + // clang-format off #else if (CrtsyspDriverUnload) { CrtsyspDriverUnload( DriverObject ); diff --git a/test/app/src/main.cpp b/test/app/src/main.cpp index 5dec6ae..7ecb5a3 100644 --- a/test/app/src/main.cpp +++ b/test/app/src/main.cpp @@ -1,11 +1,9 @@ -#include "common/rpc/client.hpp" #include #include -int main() { - testing::InitGoogleTest(); - return RUN_ALL_TESTS(); -} +#include +// rpc client stub code +#include "common/rpc.hpp" TEST(ntl_rpc_client, invoke_callback_by_invoke_method) { ntl::rpc::client cli(L"test_rpc", 1024 * 1024); @@ -74,4 +72,31 @@ TEST(ntl_rpc_client, invoke_callback_by_symbol) { auto test_point_class_ret = test_point_class(point(1, 1), point(4, 1)); EXPECT_EQ(test_point_class_ret.get_x(), 4); EXPECT_EQ(test_point_class_ret.get_y(), 1); -} \ No newline at end of file +} + +TEST(ntl_device, device_io_control) { + HANDLE hDevice = CreateFileW( + L"\\\\?\\Global\\GLOBALROOT\\Device\\test_device", + GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + EXPECT_NE(hDevice, INVALID_HANDLE_VALUE); + + if (hDevice != INVALID_HANDLE_VALUE) { + DWORD bytes_returned; + char buffer[sizeof("world")]; + + EXPECT_TRUE(DeviceIoControl( + hDevice, + CTL_CODE(FILE_DEVICE_NTL_RPC, 0, METHOD_BUFFERED, FILE_ANY_ACCESS), + "hello", 5, buffer, sizeof("world"), &bytes_returned, NULL)); + + EXPECT_EQ(bytes_returned, sizeof("world")); + EXPECT_STREQ(buffer, "world"); + + CloseHandle(hDevice); + } +} + +int main() { + testing::InitGoogleTest(); + return RUN_ALL_TESTS(); +} diff --git a/test/common/rpc.hpp b/test/common/rpc.hpp new file mode 100644 index 0000000..f546b8f --- /dev/null +++ b/test/common/rpc.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include + +#include + +class point { +public: + point() = default; + point(int x, int y) noexcept : m_x(x), m_y(y) {} + friend zpp::serializer::access; + template + static void serialize(Archive &archive, Self &self) { + archive(self.m_x, self.m_y); + } + int get_x() const noexcept { return m_x; } + int get_y() const noexcept { return m_y; } + +private: + int m_x = 0; + int m_y = 0; +}; + +NTL_RPC_BEGIN(test_rpc) + +NTL_ADD_CALLBACK_1(test_rpc, int, test_inc, int, i, { + i = i; + return i + 1; +}) + +NTL_ADD_CALLBACK_1(test_rpc, int, test_dec, int, i, { + i = i; + return i - 1; +}) + +NTL_ADD_CALLBACK_2(test_rpc, int, test_sum, int, a, int, b, { return a + b; }) + +NTL_ADD_CALLBACK_3(test_rpc, int, test_sum, int, a, int, b, int, c, + { return a + b + c; }) + +NTL_ADD_CALLBACK_4(test_rpc, int, test_sum, int, a, int, b, int, c, int, d, + { return a + b + c + d; }) + +NTL_ADD_CALLBACK_5(test_rpc, int, test_sum, int, a, int, b, int, c, int, d, int, + e, { return a + b + c + d + e; }) + +NTL_ADD_CALLBACK_0(test_rpc, void, test_void, { return; }) + +NTL_ADD_CALLBACK_1(test_rpc, bool, test_vec, const std::vector &, vec, + { return vec.empty(); }) + +NTL_ADD_CALLBACK_1(test_rpc, bool, test_set, const std::set &, set, + { return set.empty(); }) + +NTL_ADD_CALLBACK_1(test_rpc, std::vector, test_list, + const std::list &, list, { + std::vector ret; + for (auto i : list) + ret.push_back(std::to_string(i)); + return ret; + }) + +NTL_ADD_CALLBACK_1(test_rpc, NTL_RPC_ARG_PACK(std::map), + test_map, NTL_RPC_ARG_PACK(const std::map &), map, + { + std::map ret; + for (auto e : map) + ret.insert({e.first, std::to_string(e.first)}); + return ret; + ; + }) + +NTL_ADD_CALLBACK_2(test_rpc, bool, test_map2, + NTL_RPC_ARG_PACK(const std::map &), map, + NTL_RPC_ARG_PACK(const std::map &), map2, + { return map.size() > map2.size(); }) + +NTL_ADD_CALLBACK_2(test_rpc, point, test_point_class, const point &, p1, + const point &, p2, + { return p1.get_x() > p2.get_x() ? p1 : p2; }) + +NTL_RPC_END(test_rpc) \ No newline at end of file diff --git a/test/common/rpc/client.hpp b/test/common/rpc/client.hpp deleted file mode 100644 index 595bfdf..0000000 --- a/test/common/rpc/client.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -#include "declare.hpp" - -namespace test_rpc { -#include "procedures.cpp" -} // namespace test_rpc diff --git a/test/common/rpc/declare.hpp b/test/common/rpc/declare.hpp deleted file mode 100644 index b8f3ab4..0000000 --- a/test/common/rpc/declare.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#define NTL_RPC_NAME L"test_rpc" - -class point { -public: - point() = default; - point(int x, int y) noexcept : m_x(x), m_y(y) {} - friend zpp::serializer::access; - template - static void serialize(Archive &archive, Self &self) { - archive(self.m_x, self.m_y); - } - int get_x() const noexcept { return m_x; } - int get_y() const noexcept { return m_y; } - -private: - int m_x = 0; - int m_y = 0; -}; \ No newline at end of file diff --git a/test/common/rpc/procedures.cpp b/test/common/rpc/procedures.cpp deleted file mode 100644 index 6195a2c..0000000 --- a/test/common/rpc/procedures.cpp +++ /dev/null @@ -1,53 +0,0 @@ - -NTL_ADD_CALLBACK_1(int, test_inc, int, i, { - i = i; - return i + 1; -}) - -NTL_ADD_CALLBACK_1(int, test_dec, int, i, { - i = i; - return i - 1; -}) - -NTL_ADD_CALLBACK_2(int, test_sum, int, a, int, b, { return a + b; }) - -NTL_ADD_CALLBACK_3(int, test_sum, int, a, int, b, int, c, { return a + b + c; }) - -NTL_ADD_CALLBACK_4(int, test_sum, int, a, int, b, int, c, int, d, - { return a + b + c + d; }) - -NTL_ADD_CALLBACK_5(int, test_sum, int, a, int, b, int, c, int, d, int, e, - { return a + b + c + d + e; }) - -NTL_ADD_CALLBACK_0(void, test_void, { return; }) - -NTL_ADD_CALLBACK_1(bool, test_vec, const std::vector &, vec, - { return vec.empty(); }) - -NTL_ADD_CALLBACK_1(bool, test_set, const std::set &, set, - { return set.empty(); }) - -NTL_ADD_CALLBACK_1(std::vector, test_list, const std::list &, - list, { - std::vector ret; - for (auto i : list) - ret.push_back(std::to_string(i)); - return ret; - }) - -NTL_ADD_CALLBACK_1(NTL_RPC_ARG_PACK(std::map), test_map, - NTL_RPC_ARG_PACK(const std::map &), map, { - std::map ret; - for (auto e : map) - ret.insert({e.first, std::to_string(e.first)}); - return ret; - ; - }) - -NTL_ADD_CALLBACK_2(bool, test_map2, - NTL_RPC_ARG_PACK(const std::map &), map, - NTL_RPC_ARG_PACK(const std::map &), map2, - { return map.size() > map2.size(); }) - -NTL_ADD_CALLBACK_2(point, test_point_class, const point &, p1, const point &, - p2, { return p1.get_x() > p2.get_x() ? p1 : p2; }) diff --git a/test/common/rpc/server.hpp b/test/common/rpc/server.hpp deleted file mode 100644 index 2c2c8b2..0000000 --- a/test/common/rpc/server.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#include "declare.hpp" - -std::shared_ptr init_rpc(ntl::driver &driver) { - driver.on_device_control([](uint32_t ctl_code, const uint8_t *in_buffer, - size_t in_buffer_length, uint8_t *out_buffer, - size_t *out_buffer_length) { -#include "procedures.cpp" - }); - - return std::make_shared(driver, NTL_RPC_NAME); -} \ No newline at end of file diff --git a/test/driver/src/main.cpp b/test/driver/src/main.cpp index 1d04603..5d708a1 100644 --- a/test/driver/src/main.cpp +++ b/test/driver/src/main.cpp @@ -1,5 +1,4 @@ -#include -#include +#include EXTERN_C DRIVER_INITIALIZE DriverEntry; EXTERN_C DRIVER_UNLOAD DriverUnload; @@ -17,7 +16,9 @@ void test_all(); #include #include -#include "common/rpc/server.hpp" +#include +// rpc server stub code +#include "common/rpc.hpp" ntl::status ntl::main(ntl::driver &driver, const std::wstring ®istry_path) { @@ -27,10 +28,51 @@ ntl::status ntl::main(ntl::driver &driver, const std::wstring ®istry_path) { test_all(); - driver.on_unload([registry_path, rpc_svr = init_rpc(driver)]() { - std::wcout << "unload (registry_path :" << registry_path << ")\n"; + struct test_extension { + test_extension() : val(0) { + std::cout << "constructor - val : " << val << '\n'; + } + ~test_extension() { std::cout << "destroctor - val : " << val << '\n'; } + int val; + }; + + auto test_dev = + driver.create_device(ntl::device::options() + .name(L"test_device") + .type(FILE_DEVICE_UNKNOWN) + .exclusive()); + + test_dev->extension().val = 100; + + test_dev->on_device_control([](uint32_t ctl_code, const uint8_t *in_buffer, + size_t in_buffer_length, uint8_t *out_buffer, + size_t *out_buffer_length) { +#define TEST_DEVICE_CTL \ + CTL_CODE(FILE_DEVICE_NTL_RPC, 0, METHOD_BUFFERED, FILE_ANY_ACCESS) + if (ctl_code == TEST_DEVICE_CTL) { + in_buffer; + in_buffer_length; + } + std::string actual(reinterpret_cast(in_buffer), + in_buffer_length); + if (actual != "hello") + std::cout << "[FAILED] expect : hello, actual : " << actual << '\n'; + + if (out_buffer) { + strcpy_s(reinterpret_cast(out_buffer), *out_buffer_length, + "world"); + } else { + std::cout << "[FAILED] out_buffer == null\n"; + } }); + driver.on_unload( + [registry_path, test_dev, rpc_svr = test_rpc::init(driver)]() { + if (test_dev) + std::wcout << L"delete device :" << test_dev->name() << L'\n'; + std::wcout << L"unload (registry_path :" << registry_path << L")\n"; + }); + testing::InitGoogleTest(); return RUN_ALL_TESTS() == 0 ? status::ok() : status(STATUS_UNSUCCESSFUL); }