├── .gitignore ├── README.md ├── drv-loader.sln ├── drv-loader ├── drv-loader.vcxproj ├── include │ ├── clara.hpp │ ├── clara_textflow.hpp │ ├── drv-loader.hpp │ ├── functor.hpp │ ├── helpers.hpp │ ├── lazy_loader_light.hpp │ └── ntstatus.hpp └── main.cpp └── img └── showtime.gif /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | */build/** 3 | *.filters 4 | *.user 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drv-loader 2 | 3 | Simple command line system driver (un)loader utility for Windows Operating system. 4 | 5 | ## Usage 6 | Simple usage, you need to specify the operation to perform and the device name (when loading the file path should be passed as an argument as well, it is optional for unload operation). 7 | 8 | To load a driver `.\drv-loader.exe -o load -d MyDesiredDisplayName ` 9 | To unload a driver `.\drv-loader.exe -o unload -d MyDesiredDisplayName` 10 | 11 | ![showtime](./img/showtime.gif) 12 | 13 | Use `-h` switch for details (below) 14 | ``` 15 | PS C:\Users\Dev\Desktop\Sources\drv-loader\drv-loader\build\x64\Debug> .\drv-loader.exe -h 16 | usage: 17 | drv-loader.exe [] options 18 | 19 | where options are: 20 | -?, -h, --help display usage information 21 | --display, -d Set the display name 22 | --operation, -o Load or unload the specified driver``` -------------------------------------------------------------------------------- /drv-loader.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30104.148 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "drv-loader", "drv-loader\drv-loader.vcxproj", "{92B5C748-A401-4B88-B3B9-745870EB4C4B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {92B5C748-A401-4B88-B3B9-745870EB4C4B}.Debug|x64.ActiveCfg = Debug|x64 17 | {92B5C748-A401-4B88-B3B9-745870EB4C4B}.Debug|x64.Build.0 = Debug|x64 18 | {92B5C748-A401-4B88-B3B9-745870EB4C4B}.Debug|x86.ActiveCfg = Debug|Win32 19 | {92B5C748-A401-4B88-B3B9-745870EB4C4B}.Debug|x86.Build.0 = Debug|Win32 20 | {92B5C748-A401-4B88-B3B9-745870EB4C4B}.Release|x64.ActiveCfg = Release|x64 21 | {92B5C748-A401-4B88-B3B9-745870EB4C4B}.Release|x64.Build.0 = Release|x64 22 | {92B5C748-A401-4B88-B3B9-745870EB4C4B}.Release|x86.ActiveCfg = Release|Win32 23 | {92B5C748-A401-4B88-B3B9-745870EB4C4B}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {09F64E54-24D5-4EEA-810F-10E6E3EFB089} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /drv-loader/drv-loader.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 16.0 35 | {92B5C748-A401-4B88-B3B9-745870EB4C4B} 36 | Win32Proj 37 | drvloader 38 | 10.0 39 | 40 | 41 | 42 | Application 43 | true 44 | v142 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v142 51 | true 52 | Unicode 53 | 54 | 55 | Application 56 | true 57 | v142 58 | Unicode 59 | 60 | 61 | Application 62 | false 63 | v142 64 | true 65 | Unicode 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | true 87 | $(SolutionDir)drv-loader\build\$(Platform)\$(Configuration)\ 88 | $(SolutionDir)drv-loader\build\$(Platform)\$(Configuration)\ 89 | 90 | 91 | true 92 | $(SolutionDir)drv-loader\build\$(Platform)\$(Configuration)\ 93 | $(SolutionDir)drv-loader\build\$(Platform)\$(Configuration)\ 94 | 95 | 96 | false 97 | $(SolutionDir)drv-loader\build\$(Platform)\$(Configuration)\ 98 | $(SolutionDir)drv-loader\build\$(Platform)\$(Configuration)\ 99 | 100 | 101 | false 102 | $(SolutionDir)drv-loader\build\$(Platform)\$(Configuration)\ 103 | $(SolutionDir)drv-loader\build\$(Platform)\$(Configuration)\ 104 | 105 | 106 | 107 | 108 | 109 | Level3 110 | true 111 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 112 | true 113 | stdcpp17 114 | 115 | 116 | Console 117 | true 118 | 119 | 120 | 121 | 122 | 123 | 124 | Level3 125 | true 126 | _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 127 | true 128 | stdcpp17 129 | 130 | 131 | Console 132 | true 133 | 134 | 135 | 136 | 137 | 138 | 139 | Level3 140 | true 141 | true 142 | true 143 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 144 | true 145 | stdcpp17 146 | 147 | 148 | Console 149 | true 150 | true 151 | true 152 | 153 | 154 | 155 | 156 | 157 | 158 | Level3 159 | true 160 | true 161 | true 162 | _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 163 | true 164 | stdcpp17 165 | 166 | 167 | Console 168 | true 169 | true 170 | true 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /drv-loader/include/clara.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Two Blue Cubes Ltd. All rights reserved. 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | // 6 | // See https://github.com/philsquared/Clara for more details 7 | 8 | // Clara v1.1.5 9 | 10 | #ifndef CLARA_HPP_INCLUDED 11 | #define CLARA_HPP_INCLUDED 12 | 13 | #ifndef CLARA_CONFIG_CONSOLE_WIDTH 14 | #define CLARA_CONFIG_CONSOLE_WIDTH 80 15 | #endif 16 | 17 | #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 18 | #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH 19 | #endif 20 | 21 | #ifndef CLARA_CONFIG_OPTIONAL_TYPE 22 | # ifdef __has_include 23 | # if __has_include() && __cplusplus >= 201703L 24 | # include 25 | # define CLARA_CONFIG_OPTIONAL_TYPE std::optional 26 | # endif 27 | # endif 28 | #endif 29 | 30 | #include "clara_textflow.hpp" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) 41 | #define CLARA_PLATFORM_WINDOWS 42 | #endif 43 | 44 | namespace clara { 45 | namespace detail { 46 | 47 | // Traits for extracting arg and return type of lambdas (for single argument lambdas) 48 | template 49 | struct UnaryLambdaTraits : UnaryLambdaTraits {}; 50 | 51 | template 52 | struct UnaryLambdaTraits { 53 | static const bool isValid = false; 54 | }; 55 | 56 | template 57 | struct UnaryLambdaTraits { 58 | static const bool isValid = true; 59 | using ArgType = typename std::remove_const::type>::type; 60 | using ReturnType = ReturnT; 61 | }; 62 | 63 | class TokenStream; 64 | 65 | // Transport for raw args (copied from main args, or supplied via init list for testing) 66 | class Args { 67 | friend TokenStream; 68 | std::string m_exeName; 69 | std::vector m_args; 70 | 71 | public: 72 | Args( int argc, char const* const* argv ) 73 | : m_exeName(argv[0]), 74 | m_args(argv + 1, argv + argc) {} 75 | 76 | Args( std::initializer_list args ) 77 | : m_exeName( *args.begin() ), 78 | m_args( args.begin()+1, args.end() ) 79 | {} 80 | 81 | auto exeName() const -> std::string { 82 | return m_exeName; 83 | } 84 | }; 85 | 86 | // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string 87 | // may encode an option + its argument if the : or = form is used 88 | enum class TokenType { 89 | Option, Argument 90 | }; 91 | struct Token { 92 | TokenType type; 93 | std::string token; 94 | }; 95 | 96 | inline auto isOptPrefix( char c ) -> bool { 97 | return c == '-' 98 | #ifdef CLARA_PLATFORM_WINDOWS 99 | || c == '/' 100 | #endif 101 | ; 102 | } 103 | 104 | // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled 105 | class TokenStream { 106 | using Iterator = std::vector::const_iterator; 107 | Iterator it; 108 | Iterator itEnd; 109 | std::vector m_tokenBuffer; 110 | 111 | void loadBuffer() { 112 | m_tokenBuffer.resize( 0 ); 113 | 114 | // Skip any empty strings 115 | while( it != itEnd && it->empty() ) 116 | ++it; 117 | 118 | if( it != itEnd ) { 119 | auto const &next = *it; 120 | if( isOptPrefix( next[0] ) ) { 121 | auto delimiterPos = next.find_first_of( " :=" ); 122 | if( delimiterPos != std::string::npos ) { 123 | m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); 124 | m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); 125 | } else { 126 | if( next[1] != '-' && next.size() > 2 ) { 127 | std::string opt = "- "; 128 | for( size_t i = 1; i < next.size(); ++i ) { 129 | opt[1] = next[i]; 130 | m_tokenBuffer.push_back( { TokenType::Option, opt } ); 131 | } 132 | } else { 133 | m_tokenBuffer.push_back( { TokenType::Option, next } ); 134 | } 135 | } 136 | } else { 137 | m_tokenBuffer.push_back( { TokenType::Argument, next } ); 138 | } 139 | } 140 | } 141 | 142 | public: 143 | explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} 144 | 145 | TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { 146 | loadBuffer(); 147 | } 148 | 149 | explicit operator bool() const { 150 | return !m_tokenBuffer.empty() || it != itEnd; 151 | } 152 | 153 | auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } 154 | 155 | auto operator*() const -> Token { 156 | assert( !m_tokenBuffer.empty() ); 157 | return m_tokenBuffer.front(); 158 | } 159 | 160 | auto operator->() const -> Token const * { 161 | assert( !m_tokenBuffer.empty() ); 162 | return &m_tokenBuffer.front(); 163 | } 164 | 165 | auto operator++() -> TokenStream & { 166 | if( m_tokenBuffer.size() >= 2 ) { 167 | m_tokenBuffer.erase( m_tokenBuffer.begin() ); 168 | } else { 169 | if( it != itEnd ) 170 | ++it; 171 | loadBuffer(); 172 | } 173 | return *this; 174 | } 175 | }; 176 | 177 | 178 | class ResultBase { 179 | public: 180 | enum Type { 181 | Ok, LogicError, RuntimeError 182 | }; 183 | 184 | protected: 185 | ResultBase( Type type ) : m_type( type ) {} 186 | virtual ~ResultBase() = default; 187 | 188 | virtual void enforceOk() const = 0; 189 | 190 | Type m_type; 191 | }; 192 | 193 | template 194 | class ResultValueBase : public ResultBase { 195 | public: 196 | auto value() const -> T const & { 197 | enforceOk(); 198 | return m_value; 199 | } 200 | 201 | protected: 202 | ResultValueBase( Type type ) : ResultBase( type ) {} 203 | 204 | ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { 205 | if( m_type == ResultBase::Ok ) 206 | new( &m_value ) T( other.m_value ); 207 | } 208 | 209 | ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { 210 | new( &m_value ) T( value ); 211 | } 212 | 213 | auto operator=( ResultValueBase const &other ) -> ResultValueBase & { 214 | if( m_type == ResultBase::Ok ) 215 | m_value.~T(); 216 | ResultBase::operator=(other); 217 | if( m_type == ResultBase::Ok ) 218 | new( &m_value ) T( other.m_value ); 219 | return *this; 220 | } 221 | 222 | ~ResultValueBase() override { 223 | if( m_type == Ok ) 224 | m_value.~T(); 225 | } 226 | 227 | union { 228 | T m_value; 229 | }; 230 | }; 231 | 232 | template<> 233 | class ResultValueBase : public ResultBase { 234 | protected: 235 | using ResultBase::ResultBase; 236 | }; 237 | 238 | template 239 | class BasicResult : public ResultValueBase { 240 | public: 241 | template 242 | explicit BasicResult( BasicResult const &other ) 243 | : ResultValueBase( other.type() ), 244 | m_errorMessage( other.errorMessage() ) 245 | { 246 | assert( type() != ResultBase::Ok ); 247 | } 248 | 249 | template 250 | static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } 251 | static auto ok() -> BasicResult { return { ResultBase::Ok }; } 252 | static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } 253 | static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } 254 | 255 | explicit operator bool() const { return m_type == ResultBase::Ok; } 256 | auto type() const -> ResultBase::Type { return m_type; } 257 | auto errorMessage() const -> std::string { return m_errorMessage; } 258 | 259 | protected: 260 | void enforceOk() const override { 261 | 262 | // Errors shouldn't reach this point, but if they do 263 | // the actual error message will be in m_errorMessage 264 | assert( m_type != ResultBase::LogicError ); 265 | assert( m_type != ResultBase::RuntimeError ); 266 | if( m_type != ResultBase::Ok ) 267 | std::abort(); 268 | } 269 | 270 | std::string m_errorMessage; // Only populated if resultType is an error 271 | 272 | BasicResult( ResultBase::Type type, std::string const &message ) 273 | : ResultValueBase(type), 274 | m_errorMessage(message) 275 | { 276 | assert( m_type != ResultBase::Ok ); 277 | } 278 | 279 | using ResultValueBase::ResultValueBase; 280 | using ResultBase::m_type; 281 | }; 282 | 283 | enum class ParseResultType { 284 | Matched, NoMatch, ShortCircuitAll, ShortCircuitSame 285 | }; 286 | 287 | class ParseState { 288 | public: 289 | 290 | ParseState( ParseResultType type, TokenStream const &remainingTokens ) 291 | : m_type(type), 292 | m_remainingTokens( remainingTokens ) 293 | {} 294 | 295 | auto type() const -> ParseResultType { return m_type; } 296 | auto remainingTokens() const -> TokenStream { return m_remainingTokens; } 297 | 298 | private: 299 | ParseResultType m_type; 300 | TokenStream m_remainingTokens; 301 | }; 302 | 303 | using Result = BasicResult; 304 | using ParserResult = BasicResult; 305 | using InternalParseResult = BasicResult; 306 | 307 | struct HelpColumns { 308 | std::string left; 309 | std::string right; 310 | }; 311 | 312 | template 313 | inline auto convertInto( std::string const &source, T& target ) -> ParserResult { 314 | std::stringstream ss; 315 | ss << source; 316 | ss >> target; 317 | if( ss.fail() ) 318 | return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); 319 | else 320 | return ParserResult::ok( ParseResultType::Matched ); 321 | } 322 | inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { 323 | target = source; 324 | return ParserResult::ok( ParseResultType::Matched ); 325 | } 326 | inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { 327 | std::string srcLC = source; 328 | std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( std::tolower(c) ); } ); 329 | if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") 330 | target = true; 331 | else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") 332 | target = false; 333 | else 334 | return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); 335 | return ParserResult::ok( ParseResultType::Matched ); 336 | } 337 | #ifdef CLARA_CONFIG_OPTIONAL_TYPE 338 | template 339 | inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { 340 | T temp; 341 | auto result = convertInto( source, temp ); 342 | if( result ) 343 | target = std::move(temp); 344 | return result; 345 | } 346 | #endif // CLARA_CONFIG_OPTIONAL_TYPE 347 | 348 | struct NonCopyable { 349 | NonCopyable() = default; 350 | NonCopyable( NonCopyable const & ) = delete; 351 | NonCopyable( NonCopyable && ) = delete; 352 | NonCopyable &operator=( NonCopyable const & ) = delete; 353 | NonCopyable &operator=( NonCopyable && ) = delete; 354 | }; 355 | 356 | struct BoundRef : NonCopyable { 357 | virtual ~BoundRef() = default; 358 | virtual auto isContainer() const -> bool { return false; } 359 | virtual auto isFlag() const -> bool { return false; } 360 | }; 361 | struct BoundValueRefBase : BoundRef { 362 | virtual auto setValue( std::string const &arg ) -> ParserResult = 0; 363 | }; 364 | struct BoundFlagRefBase : BoundRef { 365 | virtual auto setFlag( bool flag ) -> ParserResult = 0; 366 | virtual auto isFlag() const -> bool { return true; } 367 | }; 368 | 369 | template 370 | struct BoundValueRef : BoundValueRefBase { 371 | T &m_ref; 372 | 373 | explicit BoundValueRef( T &ref ) : m_ref( ref ) {} 374 | 375 | auto setValue( std::string const &arg ) -> ParserResult override { 376 | return convertInto( arg, m_ref ); 377 | } 378 | }; 379 | 380 | template 381 | struct BoundValueRef> : BoundValueRefBase { 382 | std::vector &m_ref; 383 | 384 | explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} 385 | 386 | auto isContainer() const -> bool override { return true; } 387 | 388 | auto setValue( std::string const &arg ) -> ParserResult override { 389 | T temp; 390 | auto result = convertInto( arg, temp ); 391 | if( result ) 392 | m_ref.push_back( temp ); 393 | return result; 394 | } 395 | }; 396 | 397 | struct BoundFlagRef : BoundFlagRefBase { 398 | bool &m_ref; 399 | 400 | explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} 401 | 402 | auto setFlag( bool flag ) -> ParserResult override { 403 | m_ref = flag; 404 | return ParserResult::ok( ParseResultType::Matched ); 405 | } 406 | }; 407 | 408 | template 409 | struct LambdaInvoker { 410 | static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); 411 | 412 | template 413 | static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { 414 | return lambda( arg ); 415 | } 416 | }; 417 | 418 | template<> 419 | struct LambdaInvoker { 420 | template 421 | static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { 422 | lambda( arg ); 423 | return ParserResult::ok( ParseResultType::Matched ); 424 | } 425 | }; 426 | 427 | template 428 | inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { 429 | ArgType temp{}; 430 | auto result = convertInto( arg, temp ); 431 | return !result 432 | ? result 433 | : LambdaInvoker::ReturnType>::invoke( lambda, temp ); 434 | } 435 | 436 | 437 | template 438 | struct BoundLambda : BoundValueRefBase { 439 | L m_lambda; 440 | 441 | static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); 442 | explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} 443 | 444 | auto setValue( std::string const &arg ) -> ParserResult override { 445 | return invokeLambda::ArgType>( m_lambda, arg ); 446 | } 447 | }; 448 | 449 | template 450 | struct BoundFlagLambda : BoundFlagRefBase { 451 | L m_lambda; 452 | 453 | static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); 454 | static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); 455 | 456 | explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} 457 | 458 | auto setFlag( bool flag ) -> ParserResult override { 459 | return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); 460 | } 461 | }; 462 | 463 | enum class Optionality { Optional, Required }; 464 | 465 | struct Parser; 466 | 467 | class ParserBase { 468 | public: 469 | virtual ~ParserBase() = default; 470 | virtual auto validate() const -> Result { return Result::ok(); } 471 | virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; 472 | virtual auto cardinality() const -> size_t { return 1; } 473 | 474 | auto parse( Args const &args ) const -> InternalParseResult { 475 | return parse( args.exeName(), TokenStream( args ) ); 476 | } 477 | }; 478 | 479 | template 480 | class ComposableParserImpl : public ParserBase { 481 | public: 482 | template 483 | auto operator|( T const &other ) const -> Parser; 484 | 485 | template 486 | auto operator+( T const &other ) const -> Parser; 487 | }; 488 | 489 | // Common code and state for Args and Opts 490 | template 491 | class ParserRefImpl : public ComposableParserImpl { 492 | protected: 493 | Optionality m_optionality = Optionality::Optional; 494 | std::shared_ptr m_ref; 495 | std::string m_hint; 496 | std::string m_description; 497 | 498 | explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} 499 | 500 | public: 501 | template 502 | ParserRefImpl( T &ref, std::string const &hint ) 503 | : m_ref( std::make_shared>( ref ) ), 504 | m_hint( hint ) 505 | {} 506 | 507 | template 508 | ParserRefImpl( LambdaT const &ref, std::string const &hint ) 509 | : m_ref( std::make_shared>( ref ) ), 510 | m_hint(hint) 511 | {} 512 | 513 | auto operator()( std::string const &description ) -> DerivedT & { 514 | m_description = description; 515 | return static_cast( *this ); 516 | } 517 | 518 | auto optional() -> DerivedT & { 519 | m_optionality = Optionality::Optional; 520 | return static_cast( *this ); 521 | }; 522 | 523 | auto required() -> DerivedT & { 524 | m_optionality = Optionality::Required; 525 | return static_cast( *this ); 526 | }; 527 | 528 | auto isOptional() const -> bool { 529 | return m_optionality == Optionality::Optional; 530 | } 531 | 532 | auto cardinality() const -> size_t override { 533 | if( m_ref->isContainer() ) 534 | return 0; 535 | else 536 | return 1; 537 | } 538 | 539 | auto hint() const -> std::string { return m_hint; } 540 | }; 541 | 542 | class ExeName : public ComposableParserImpl { 543 | std::shared_ptr m_name; 544 | std::shared_ptr m_ref; 545 | 546 | template 547 | static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { 548 | return std::make_shared>( lambda) ; 549 | } 550 | 551 | public: 552 | ExeName() : m_name( std::make_shared( "" ) ) {} 553 | 554 | explicit ExeName( std::string &ref ) : ExeName() { 555 | m_ref = std::make_shared>( ref ); 556 | } 557 | 558 | template 559 | explicit ExeName( LambdaT const& lambda ) : ExeName() { 560 | m_ref = std::make_shared>( lambda ); 561 | } 562 | 563 | // The exe name is not parsed out of the normal tokens, but is handled specially 564 | auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { 565 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); 566 | } 567 | 568 | auto name() const -> std::string { return *m_name; } 569 | auto set( std::string const& newName ) -> ParserResult { 570 | 571 | auto lastSlash = newName.find_last_of( "\\/" ); 572 | auto filename = ( lastSlash == std::string::npos ) 573 | ? newName 574 | : newName.substr( lastSlash+1 ); 575 | 576 | *m_name = filename; 577 | if( m_ref ) 578 | return m_ref->setValue( filename ); 579 | else 580 | return ParserResult::ok( ParseResultType::Matched ); 581 | } 582 | }; 583 | 584 | class Arg : public ParserRefImpl { 585 | public: 586 | using ParserRefImpl::ParserRefImpl; 587 | 588 | auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { 589 | auto validationResult = validate(); 590 | if( !validationResult ) 591 | return InternalParseResult( validationResult ); 592 | 593 | auto remainingTokens = tokens; 594 | auto const &token = *remainingTokens; 595 | if( token.type != TokenType::Argument ) 596 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); 597 | 598 | assert( !m_ref->isFlag() ); 599 | auto valueRef = static_cast( m_ref.get() ); 600 | 601 | auto result = valueRef->setValue( remainingTokens->token ); 602 | if( !result ) 603 | return InternalParseResult( result ); 604 | else 605 | return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); 606 | } 607 | }; 608 | 609 | inline auto normaliseOpt( std::string const &optName ) -> std::string { 610 | #ifdef CLARA_PLATFORM_WINDOWS 611 | if( optName[0] == '/' ) 612 | return "-" + optName.substr( 1 ); 613 | else 614 | #endif 615 | return optName; 616 | } 617 | 618 | class Opt : public ParserRefImpl { 619 | protected: 620 | std::vector m_optNames; 621 | 622 | public: 623 | template 624 | explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} 625 | 626 | explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} 627 | 628 | template 629 | Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} 630 | 631 | template 632 | Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} 633 | 634 | auto operator[]( std::string const &optName ) -> Opt & { 635 | m_optNames.push_back( optName ); 636 | return *this; 637 | } 638 | 639 | auto getHelpColumns() const -> std::vector { 640 | std::ostringstream oss; 641 | bool first = true; 642 | for( auto const &opt : m_optNames ) { 643 | if (first) 644 | first = false; 645 | else 646 | oss << ", "; 647 | oss << opt; 648 | } 649 | if( !m_hint.empty() ) 650 | oss << " <" << m_hint << ">"; 651 | return { { oss.str(), m_description } }; 652 | } 653 | 654 | auto isMatch( std::string const &optToken ) const -> bool { 655 | auto normalisedToken = normaliseOpt( optToken ); 656 | for( auto const &name : m_optNames ) { 657 | if( normaliseOpt( name ) == normalisedToken ) 658 | return true; 659 | } 660 | return false; 661 | } 662 | 663 | using ParserBase::parse; 664 | 665 | auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { 666 | auto validationResult = validate(); 667 | if( !validationResult ) 668 | return InternalParseResult( validationResult ); 669 | 670 | auto remainingTokens = tokens; 671 | if( remainingTokens && remainingTokens->type == TokenType::Option ) { 672 | auto const &token = *remainingTokens; 673 | if( isMatch(token.token ) ) { 674 | if( m_ref->isFlag() ) { 675 | auto flagRef = static_cast( m_ref.get() ); 676 | auto result = flagRef->setFlag( true ); 677 | if( !result ) 678 | return InternalParseResult( result ); 679 | if( result.value() == ParseResultType::ShortCircuitAll ) 680 | return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); 681 | } else { 682 | auto valueRef = static_cast( m_ref.get() ); 683 | ++remainingTokens; 684 | if( !remainingTokens ) 685 | return InternalParseResult::runtimeError( "Expected argument following " + token.token ); 686 | auto const &argToken = *remainingTokens; 687 | if( argToken.type != TokenType::Argument ) 688 | return InternalParseResult::runtimeError( "Expected argument following " + token.token ); 689 | auto result = valueRef->setValue( argToken.token ); 690 | if( !result ) 691 | return InternalParseResult( result ); 692 | if( result.value() == ParseResultType::ShortCircuitAll ) 693 | return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); 694 | } 695 | return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); 696 | } 697 | } 698 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); 699 | } 700 | 701 | auto validate() const -> Result override { 702 | if( m_optNames.empty() ) 703 | return Result::logicError( "No options supplied to Opt" ); 704 | for( auto const &name : m_optNames ) { 705 | if( name.empty() ) 706 | return Result::logicError( "Option name cannot be empty" ); 707 | #ifdef CLARA_PLATFORM_WINDOWS 708 | if( name[0] != '-' && name[0] != '/' ) 709 | return Result::logicError( "Option name must begin with '-' or '/'" ); 710 | #else 711 | if( name[0] != '-' ) 712 | return Result::logicError( "Option name must begin with '-'" ); 713 | #endif 714 | } 715 | return ParserRefImpl::validate(); 716 | } 717 | }; 718 | 719 | struct Help : Opt { 720 | Help( bool &showHelpFlag ) 721 | : Opt([&]( bool flag ) { 722 | showHelpFlag = flag; 723 | return ParserResult::ok( ParseResultType::ShortCircuitAll ); 724 | }) 725 | { 726 | static_cast( *this ) 727 | ("display usage information") 728 | ["-?"]["-h"]["--help"] 729 | .optional(); 730 | } 731 | }; 732 | 733 | 734 | struct Parser : ParserBase { 735 | 736 | mutable ExeName m_exeName; 737 | std::vector m_options; 738 | std::vector m_args; 739 | 740 | auto operator|=( ExeName const &exeName ) -> Parser & { 741 | m_exeName = exeName; 742 | return *this; 743 | } 744 | 745 | auto operator|=( Arg const &arg ) -> Parser & { 746 | m_args.push_back(arg); 747 | return *this; 748 | } 749 | 750 | auto operator|=( Opt const &opt ) -> Parser & { 751 | m_options.push_back(opt); 752 | return *this; 753 | } 754 | 755 | auto operator|=( Parser const &other ) -> Parser & { 756 | m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); 757 | m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); 758 | return *this; 759 | } 760 | 761 | template 762 | auto operator|( T const &other ) const -> Parser { 763 | return Parser( *this ) |= other; 764 | } 765 | 766 | // Forward deprecated interface with '+' instead of '|' 767 | template 768 | auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } 769 | template 770 | auto operator+( T const &other ) const -> Parser { return operator|( other ); } 771 | 772 | auto getHelpColumns() const -> std::vector { 773 | std::vector cols; 774 | for (auto const &o : m_options) { 775 | auto childCols = o.getHelpColumns(); 776 | cols.insert( cols.end(), childCols.begin(), childCols.end() ); 777 | } 778 | return cols; 779 | } 780 | 781 | void writeToStream( std::ostream &os ) const { 782 | if (!m_exeName.name().empty()) { 783 | os << "usage:\n" << " " << m_exeName.name() << " "; 784 | bool required = true, first = true; 785 | for( auto const &arg : m_args ) { 786 | if (first) 787 | first = false; 788 | else 789 | os << " "; 790 | if( arg.isOptional() && required ) { 791 | os << "["; 792 | required = false; 793 | } 794 | os << "<" << arg.hint() << ">"; 795 | if( arg.cardinality() == 0 ) 796 | os << " ... "; 797 | } 798 | if( !required ) 799 | os << "]"; 800 | if( !m_options.empty() ) 801 | os << " options"; 802 | os << "\n\nwhere options are:" << std::endl; 803 | } 804 | 805 | auto rows = getHelpColumns(); 806 | size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; 807 | size_t optWidth = 0; 808 | for( auto const &cols : rows ) 809 | optWidth = (std::max)(optWidth, cols.left.size() + 2); 810 | 811 | optWidth = (std::min)(optWidth, consoleWidth/2); 812 | 813 | for( auto const &cols : rows ) { 814 | auto row = 815 | TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + 816 | TextFlow::Spacer(4) + 817 | TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); 818 | os << row << std::endl; 819 | } 820 | } 821 | 822 | friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { 823 | parser.writeToStream( os ); 824 | return os; 825 | } 826 | 827 | auto validate() const -> Result override { 828 | for( auto const &opt : m_options ) { 829 | auto result = opt.validate(); 830 | if( !result ) 831 | return result; 832 | } 833 | for( auto const &arg : m_args ) { 834 | auto result = arg.validate(); 835 | if( !result ) 836 | return result; 837 | } 838 | return Result::ok(); 839 | } 840 | 841 | using ParserBase::parse; 842 | 843 | auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { 844 | 845 | struct ParserInfo { 846 | ParserBase const* parser = nullptr; 847 | size_t count = 0; 848 | }; 849 | const size_t totalParsers = m_options.size() + m_args.size(); 850 | assert( totalParsers < 512 ); 851 | // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do 852 | ParserInfo parseInfos[512]; 853 | 854 | { 855 | size_t i = 0; 856 | for (auto const &opt : m_options) parseInfos[i++].parser = &opt; 857 | for (auto const &arg : m_args) parseInfos[i++].parser = &arg; 858 | } 859 | 860 | m_exeName.set( exeName ); 861 | 862 | auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); 863 | while( result.value().remainingTokens() ) { 864 | bool tokenParsed = false; 865 | 866 | for( size_t i = 0; i < totalParsers; ++i ) { 867 | auto& parseInfo = parseInfos[i]; 868 | if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { 869 | result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); 870 | if (!result) 871 | return result; 872 | if (result.value().type() != ParseResultType::NoMatch) { 873 | tokenParsed = true; 874 | ++parseInfo.count; 875 | break; 876 | } 877 | } 878 | } 879 | 880 | if( result.value().type() == ParseResultType::ShortCircuitAll ) 881 | return result; 882 | if( !tokenParsed ) 883 | return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); 884 | } 885 | // !TBD Check missing required options 886 | return result; 887 | } 888 | }; 889 | 890 | template 891 | template 892 | auto ComposableParserImpl::operator|( T const &other ) const -> Parser { 893 | return Parser() | static_cast( *this ) | other; 894 | } 895 | } // namespace detail 896 | 897 | 898 | // A Combined parser 899 | using detail::Parser; 900 | 901 | // A parser for options 902 | using detail::Opt; 903 | 904 | // A parser for arguments 905 | using detail::Arg; 906 | 907 | // Wrapper for argc, argv from main() 908 | using detail::Args; 909 | 910 | // Specifies the name of the executable 911 | using detail::ExeName; 912 | 913 | // Convenience wrapper for option parser that specifies the help option 914 | using detail::Help; 915 | 916 | // enum of result types from a parse 917 | using detail::ParseResultType; 918 | 919 | // Result type for parser operation 920 | using detail::ParserResult; 921 | 922 | 923 | } // namespace clara 924 | 925 | #endif // CLARA_HPP_INCLUDED 926 | -------------------------------------------------------------------------------- /drv-loader/include/clara_textflow.hpp: -------------------------------------------------------------------------------- 1 | // TextFlowCpp 2 | // 3 | // A single-header library for wrapping and laying out basic text, by Phil Nash 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | // This project is hosted at https://github.com/philsquared/textflowcpp 9 | 10 | #ifndef CLARA_TEXTFLOW_HPP_INCLUDED 11 | #define CLARA_TEXTFLOW_HPP_INCLUDED 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 19 | #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 20 | #endif 21 | 22 | 23 | namespace clara { namespace TextFlow { 24 | 25 | inline auto isWhitespace( char c ) -> bool { 26 | static std::string chars = " \t\n\r"; 27 | return chars.find( c ) != std::string::npos; 28 | } 29 | inline auto isBreakableBefore( char c ) -> bool { 30 | static std::string chars = "[({<|"; 31 | return chars.find( c ) != std::string::npos; 32 | } 33 | inline auto isBreakableAfter( char c ) -> bool { 34 | static std::string chars = "])}>.,:;*+-=&/\\"; 35 | return chars.find( c ) != std::string::npos; 36 | } 37 | 38 | class Columns; 39 | 40 | class Column { 41 | std::vector m_strings; 42 | size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; 43 | size_t m_indent = 0; 44 | size_t m_initialIndent = std::string::npos; 45 | 46 | public: 47 | class iterator { 48 | friend Column; 49 | 50 | Column const& m_column; 51 | size_t m_stringIndex = 0; 52 | size_t m_pos = 0; 53 | 54 | size_t m_len = 0; 55 | size_t m_end = 0; 56 | bool m_suffix = false; 57 | 58 | iterator( Column const& column, size_t stringIndex ) 59 | : m_column( column ), 60 | m_stringIndex( stringIndex ) 61 | {} 62 | 63 | auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } 64 | 65 | auto isBoundary( size_t at ) const -> bool { 66 | assert( at > 0 ); 67 | assert( at <= line().size() ); 68 | 69 | return at == line().size() || 70 | ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || 71 | isBreakableBefore( line()[at] ) || 72 | isBreakableAfter( line()[at-1] ); 73 | } 74 | 75 | void calcLength() { 76 | assert( m_stringIndex < m_column.m_strings.size() ); 77 | 78 | m_suffix = false; 79 | auto width = m_column.m_width-indent(); 80 | m_end = m_pos; 81 | while( m_end < line().size() && line()[m_end] != '\n' ) 82 | ++m_end; 83 | 84 | if( m_end < m_pos + width ) { 85 | m_len = m_end - m_pos; 86 | } 87 | else { 88 | size_t len = width; 89 | while (len > 0 && !isBoundary(m_pos + len)) 90 | --len; 91 | while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) 92 | --len; 93 | 94 | if (len > 0) { 95 | m_len = len; 96 | } else { 97 | m_suffix = true; 98 | m_len = width - 1; 99 | } 100 | } 101 | } 102 | 103 | auto indent() const -> size_t { 104 | auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; 105 | return initial == std::string::npos ? m_column.m_indent : initial; 106 | } 107 | 108 | auto addIndentAndSuffix(std::string const &plain) const -> std::string { 109 | return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); 110 | } 111 | 112 | public: 113 | using difference_type = std::ptrdiff_t; 114 | using value_type = std::string; 115 | using pointer = value_type*; 116 | using reference = value_type&; 117 | using iterator_category = std::forward_iterator_tag; 118 | 119 | explicit iterator( Column const& column ) : m_column( column ) { 120 | assert( m_column.m_width > m_column.m_indent ); 121 | assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); 122 | calcLength(); 123 | if( m_len == 0 ) 124 | m_stringIndex++; // Empty string 125 | } 126 | 127 | auto operator *() const -> std::string { 128 | assert( m_stringIndex < m_column.m_strings.size() ); 129 | assert( m_pos <= m_end ); 130 | return addIndentAndSuffix(line().substr(m_pos, m_len)); 131 | } 132 | 133 | auto operator ++() -> iterator& { 134 | m_pos += m_len; 135 | if( m_pos < line().size() && line()[m_pos] == '\n' ) 136 | m_pos += 1; 137 | else 138 | while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) 139 | ++m_pos; 140 | 141 | if( m_pos == line().size() ) { 142 | m_pos = 0; 143 | ++m_stringIndex; 144 | } 145 | if( m_stringIndex < m_column.m_strings.size() ) 146 | calcLength(); 147 | return *this; 148 | } 149 | auto operator ++(int) -> iterator { 150 | iterator prev( *this ); 151 | operator++(); 152 | return prev; 153 | } 154 | 155 | auto operator ==( iterator const& other ) const -> bool { 156 | return 157 | m_pos == other.m_pos && 158 | m_stringIndex == other.m_stringIndex && 159 | &m_column == &other.m_column; 160 | } 161 | auto operator !=( iterator const& other ) const -> bool { 162 | return !operator==( other ); 163 | } 164 | }; 165 | using const_iterator = iterator; 166 | 167 | explicit Column( std::string const& text ) { m_strings.push_back( text ); } 168 | 169 | auto width( size_t newWidth ) -> Column& { 170 | assert( newWidth > 0 ); 171 | m_width = newWidth; 172 | return *this; 173 | } 174 | auto indent( size_t newIndent ) -> Column& { 175 | m_indent = newIndent; 176 | return *this; 177 | } 178 | auto initialIndent( size_t newIndent ) -> Column& { 179 | m_initialIndent = newIndent; 180 | return *this; 181 | } 182 | 183 | auto width() const -> size_t { return m_width; } 184 | auto begin() const -> iterator { return iterator( *this ); } 185 | auto end() const -> iterator { return { *this, m_strings.size() }; } 186 | 187 | inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { 188 | bool first = true; 189 | for( auto line : col ) { 190 | if( first ) 191 | first = false; 192 | else 193 | os << "\n"; 194 | os << line; 195 | } 196 | return os; 197 | } 198 | 199 | auto operator + ( Column const& other ) -> Columns; 200 | 201 | auto toString() const -> std::string { 202 | std::ostringstream oss; 203 | oss << *this; 204 | return oss.str(); 205 | } 206 | }; 207 | 208 | class Spacer : public Column { 209 | 210 | public: 211 | explicit Spacer( size_t spaceWidth ) : Column( "" ) { 212 | width( spaceWidth ); 213 | } 214 | }; 215 | 216 | class Columns { 217 | std::vector m_columns; 218 | 219 | public: 220 | 221 | class iterator { 222 | friend Columns; 223 | struct EndTag {}; 224 | 225 | std::vector const& m_columns; 226 | std::vector m_iterators; 227 | size_t m_activeIterators; 228 | 229 | iterator( Columns const& columns, EndTag ) 230 | : m_columns( columns.m_columns ), 231 | m_activeIterators( 0 ) 232 | { 233 | m_iterators.reserve( m_columns.size() ); 234 | 235 | for( auto const& col : m_columns ) 236 | m_iterators.push_back( col.end() ); 237 | } 238 | 239 | public: 240 | using difference_type = std::ptrdiff_t; 241 | using value_type = std::string; 242 | using pointer = value_type*; 243 | using reference = value_type&; 244 | using iterator_category = std::forward_iterator_tag; 245 | 246 | explicit iterator( Columns const& columns ) 247 | : m_columns( columns.m_columns ), 248 | m_activeIterators( m_columns.size() ) 249 | { 250 | m_iterators.reserve( m_columns.size() ); 251 | 252 | for( auto const& col : m_columns ) 253 | m_iterators.push_back( col.begin() ); 254 | } 255 | 256 | auto operator ==( iterator const& other ) const -> bool { 257 | return m_iterators == other.m_iterators; 258 | } 259 | auto operator !=( iterator const& other ) const -> bool { 260 | return m_iterators != other.m_iterators; 261 | } 262 | auto operator *() const -> std::string { 263 | std::string row, padding; 264 | 265 | for( size_t i = 0; i < m_columns.size(); ++i ) { 266 | auto width = m_columns[i].width(); 267 | if( m_iterators[i] != m_columns[i].end() ) { 268 | std::string col = *m_iterators[i]; 269 | row += padding + col; 270 | if( col.size() < width ) 271 | padding = std::string( width - col.size(), ' ' ); 272 | else 273 | padding = ""; 274 | } 275 | else { 276 | padding += std::string( width, ' ' ); 277 | } 278 | } 279 | return row; 280 | } 281 | auto operator ++() -> iterator& { 282 | for( size_t i = 0; i < m_columns.size(); ++i ) { 283 | if (m_iterators[i] != m_columns[i].end()) 284 | ++m_iterators[i]; 285 | } 286 | return *this; 287 | } 288 | auto operator ++(int) -> iterator { 289 | iterator prev( *this ); 290 | operator++(); 291 | return prev; 292 | } 293 | }; 294 | using const_iterator = iterator; 295 | 296 | auto begin() const -> iterator { return iterator( *this ); } 297 | auto end() const -> iterator { return { *this, iterator::EndTag() }; } 298 | 299 | auto operator += ( Column const& col ) -> Columns& { 300 | m_columns.push_back( col ); 301 | return *this; 302 | } 303 | auto operator + ( Column const& col ) -> Columns { 304 | Columns combined = *this; 305 | combined += col; 306 | return combined; 307 | } 308 | 309 | inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { 310 | 311 | bool first = true; 312 | for( auto line : cols ) { 313 | if( first ) 314 | first = false; 315 | else 316 | os << "\n"; 317 | os << line; 318 | } 319 | return os; 320 | } 321 | 322 | auto toString() const -> std::string { 323 | std::ostringstream oss; 324 | oss << *this; 325 | return oss.str(); 326 | } 327 | }; 328 | 329 | inline auto Column::operator + ( Column const& other ) -> Columns { 330 | Columns cols; 331 | cols += *this; 332 | cols += other; 333 | return cols; 334 | } 335 | }} 336 | 337 | #endif // CLARA_TEXTFLOW_HPP_INCLUDED 338 | -------------------------------------------------------------------------------- /drv-loader/include/drv-loader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "helpers.hpp" 4 | #include "lazy_loader_light.hpp" 5 | 6 | #include 7 | 8 | #define NOMINMAX 9 | #include 10 | 11 | #include "ntstatus.hpp" 12 | 13 | namespace drv_loader { 14 | 15 | typedef struct _UNICODE_STRING { 16 | USHORT Length; 17 | USHORT MaximumLength; 18 | PWSTR Buffer; 19 | } UNICODE_STRING, * PUNICODE_STRING; 20 | 21 | constexpr char prefix[] = "\\??\\"; 22 | constexpr char registry_subkey[] = "System\\CurrentControlSet\\Services\\"; 23 | constexpr char registry_prefix[] = "\\Registry\\Machine\\"; 24 | 25 | typedef enum _loader_operation_t { 26 | none, 27 | load, 28 | unload, 29 | 30 | // number of entries in enum 31 | n_loader_operation 32 | } loader_operation_t; 33 | 34 | typedef struct _config_t { 35 | std::string display_name; 36 | std::string file_path; 37 | loader_operation_t operation; 38 | } config_t, *pconfig_t; 39 | 40 | static std::uint32_t load_driver(const config_t& config) { 41 | if (config.operation != loader_operation_t::load) { 42 | return ERROR_INVALID_OPERATION; 43 | } 44 | 45 | std::filesystem::path path = config.file_path; 46 | std::string ntpath = std::string(prefix) + std::filesystem::absolute(path).string(); 47 | 48 | std::string reg_path = std::string(registry_subkey) + config.display_name; 49 | 50 | HKEY key_handle = nullptr; 51 | LSTATUS status = ::RegCreateKeyExA(HKEY_LOCAL_MACHINE, reg_path.c_str(), NULL, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, nullptr, &key_handle, nullptr); 52 | if (status != ERROR_SUCCESS) { 53 | return status; 54 | } 55 | 56 | status = ::RegSetValueExA(key_handle, "ImagePath", NULL, REG_EXPAND_SZ, reinterpret_cast(ntpath.c_str()), static_cast(ntpath.size())); 57 | if (status != ERROR_SUCCESS) { 58 | return status; 59 | } 60 | 61 | constexpr std::uint32_t type = 1; 62 | status = ::RegSetValueExA(key_handle, "Type", NULL, REG_DWORD, reinterpret_cast(&type), sizeof(type)); 63 | if (status != ERROR_SUCCESS) { 64 | return status; 65 | } 66 | 67 | constexpr std::uint32_t start = 3; 68 | status = ::RegSetValueExA(key_handle, "Start", NULL, REG_DWORD, reinterpret_cast(&start), sizeof(start)); 69 | if (status != ERROR_SUCCESS) { 70 | return status; 71 | } 72 | 73 | constexpr std::uint32_t error_control = 1; 74 | status = ::RegSetValueExA(key_handle, "ErrorControl", NULL, REG_DWORD, reinterpret_cast(&error_control), sizeof(error_control)); 75 | if (status != ERROR_SUCCESS) { 76 | return status; 77 | } 78 | 79 | status = ::RegSetValueExA(key_handle, "DisplayName", NULL, REG_SZ, reinterpret_cast(config.display_name.c_str()), static_cast(config.display_name.size())); 80 | if (status != ERROR_SUCCESS) { 81 | return status; 82 | } 83 | 84 | if (key_handle != nullptr) { 85 | ::RegCloseKey(key_handle); 86 | } 87 | 88 | UNICODE_STRING nt_reg_path = {}; 89 | 90 | std::wstring reg_path_copy = helpers::to_unicode(std::string(registry_prefix) + reg_path); 91 | LAZYCALL(void, "ntdll.dll!RtlInitUnicodeString", &nt_reg_path, reg_path_copy.c_str()); 92 | 93 | NTSTATUS nt_status = LAZYCALL(NTSTATUS, "ntdll.dll!NtLoadDriver", &nt_reg_path); 94 | 95 | if (nt_status == STATUS_IMAGE_ALREADY_LOADED) { 96 | LAZYCALL(NTSTATUS, "ntdll.dll!NtUnloadDriver", &nt_reg_path); 97 | LAZYCALL(NTSTATUS, "ntdll.dll!NtYieldExecution"); 98 | nt_status = LAZYCALL(NTSTATUS, "ntdll.dll!NtLoadDriver", &nt_reg_path); 99 | } 100 | 101 | if (nt_status == STATUS_SUCCESS) { 102 | return ERROR_SUCCESS; 103 | } 104 | 105 | ULONG converted_status = LAZYCALL(ULONG, "ntdll!RtlNtStatusToDosError", nt_status); 106 | if (converted_status == ERROR_MR_MID_NOT_FOUND) { 107 | return nt_status; 108 | } 109 | 110 | return static_cast(converted_status); 111 | } 112 | 113 | static std::uint32_t unload_driver(const config_t& config) { 114 | if (config.operation != loader_operation_t::unload) { 115 | return ERROR_INVALID_OPERATION; 116 | } 117 | 118 | std::string reg_path = std::string(registry_subkey) + config.display_name; 119 | 120 | UNICODE_STRING nt_reg_path = {}; 121 | 122 | std::wstring reg_path_copy = helpers::to_unicode(std::string(registry_prefix) + reg_path); 123 | LAZYCALL(void, "ntdll.dll!RtlInitUnicodeString", &nt_reg_path, reg_path_copy.c_str()); 124 | 125 | NTSTATUS nt_status = LAZYCALL(NTSTATUS, "ntdll.dll!NtUnloadDriver", &nt_reg_path); 126 | 127 | if (nt_status != STATUS_SUCCESS) { 128 | ULONG converted_status = LAZYCALL(ULONG, "ntdll!RtlNtStatusToDosError", nt_status); 129 | if (converted_status == ERROR_MR_MID_NOT_FOUND) { 130 | return nt_status; 131 | } 132 | 133 | return static_cast(converted_status); 134 | } 135 | 136 | return static_cast(::RegDeleteTreeA(HKEY_LOCAL_MACHINE, reg_path.c_str())); 137 | } 138 | 139 | static std::uint32_t load_unload(const config_t& config) { 140 | std::uint32_t ret = ERROR_INVALID_OPERATION; 141 | 142 | switch (config.operation) { 143 | case loader_operation_t::load: 144 | ret = load_driver(config); 145 | break; 146 | case loader_operation_t::unload: 147 | ret = unload_driver(config); 148 | break; 149 | default: 150 | break; 151 | } 152 | 153 | return ret; 154 | } 155 | } -------------------------------------------------------------------------------- /drv-loader/include/functor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | template 6 | struct args_pack_t { }; 7 | 8 | template 9 | class Functor; 10 | 11 | template 12 | class Functor { 13 | public: 14 | typedef ResultType(*type)(ArgumentTypes ...); 15 | typedef ResultType result_type; 16 | typedef args_pack_t args_type; 17 | 18 | Functor(std::uintptr_t ptr) : _ptr(ptr) {} 19 | 20 | std::uintptr_t get(void) const { 21 | return _ptr; 22 | } 23 | 24 | ResultType operator() (ArgumentTypes&& ...args) const { 25 | return reinterpret_cast(_ptr)(std::forward(args)...); 26 | } 27 | 28 | private: 29 | std::uintptr_t _ptr; 30 | }; -------------------------------------------------------------------------------- /drv-loader/include/helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define NOMINMAX 11 | #include 12 | 13 | namespace helpers { 14 | 15 | static std::string to_ansi(const std::wstring& wstr) { 16 | std::wstring_convert> converter; 17 | return converter.to_bytes(wstr); 18 | } 19 | 20 | static std::wstring to_unicode(const std::string& str) { 21 | std::wstring_convert> converter; 22 | return converter.from_bytes(str); 23 | } 24 | 25 | template 26 | static std::string to_hex_string(T number) { 27 | static_assert(std::is_integral::value, "helpers::to_hex_string is only available for integral types"); 28 | 29 | constexpr std::size_t buffer_size = std::is_signed::value ? (sizeof(T) * 2) + 1 : sizeof(T) * 2; 30 | std::array arr; 31 | 32 | auto res = std::to_chars(arr.data(), arr.data() + arr.size(), number, 16); 33 | 34 | return std::string(arr.data(), std::distance(arr.data(), res.ptr)); 35 | } 36 | 37 | template 38 | static T from_hex_string(const std::string& string) { 39 | static_assert(std::is_integral::value, "helpers::from_hex_string is only available for integral types"); 40 | 41 | T result; 42 | std::from_chars(string.data(), string.data() + string.size(), result, 16); 43 | 44 | return result; 45 | } 46 | 47 | template 48 | class scoped_ptr { 49 | private: 50 | T* _ptr = nullptr; 51 | public: 52 | typedef T element_type; 53 | 54 | explicit scoped_ptr(T* ptr = nullptr) : _ptr(ptr) {} 55 | scoped_ptr(const scoped_ptr&) = delete; // non copyable 56 | ~scoped_ptr(void) { if (_ptr != nullptr) { delete _ptr; } } 57 | 58 | void reset(T* ptr = nullptr) { _ptr = ptr; }; 59 | 60 | T& operator*(void) const { return *_ptr; } 61 | T* operator->(void) const { return _ptr; } 62 | T* get(void) const { return _ptr; } 63 | }; 64 | 65 | template<> 66 | class scoped_ptr { 67 | private: 68 | void* _ptr = nullptr; 69 | public: 70 | typedef void element_type; 71 | 72 | explicit scoped_ptr(void* ptr = nullptr) : _ptr(ptr) {} 73 | scoped_ptr(const scoped_ptr&) = delete; // non copyable 74 | ~scoped_ptr(void) { if (_ptr != nullptr) { delete _ptr; } } 75 | 76 | void reset(void* ptr = nullptr) { _ptr = ptr; }; 77 | 78 | void* operator->(void) const { return _ptr; } 79 | void* get(void) const { return _ptr; } 80 | }; 81 | 82 | template<> 83 | class scoped_ptr { 84 | private: 85 | void* _ptr = nullptr; 86 | public: 87 | typedef void element_type; 88 | 89 | explicit scoped_ptr(void* ptr = nullptr) : _ptr(ptr) {} 90 | scoped_ptr(const scoped_ptr&) = delete; // non copyable 91 | ~scoped_ptr(void) { if (_ptr != nullptr) { delete _ptr; } } 92 | 93 | void reset(void* ptr = nullptr) { 94 | if (_ptr != nullptr) { delete _ptr; } 95 | _ptr = ptr; 96 | }; 97 | 98 | void* operator->(void) const { return _ptr; } 99 | void* get(void) const { return _ptr; } 100 | }; 101 | 102 | class scoped_handle { 103 | private: 104 | HANDLE _handle = nullptr; 105 | 106 | void safe_close_handle(void) { 107 | if (_handle != INVALID_HANDLE_VALUE) { 108 | ::CloseHandle(_handle); 109 | _handle = INVALID_HANDLE_VALUE; 110 | } 111 | } 112 | public: 113 | typedef HANDLE element_type; 114 | 115 | explicit scoped_handle(HANDLE handle = INVALID_HANDLE_VALUE) : _handle(handle) {} 116 | scoped_handle(const scoped_handle&) = delete; // non copyable 117 | ~scoped_handle(void) { safe_close_handle(); } 118 | 119 | void reset(HANDLE handle = INVALID_HANDLE_VALUE) { 120 | safe_close_handle(); 121 | _handle = handle; 122 | } 123 | 124 | HANDLE get(void) const { return _handle; } 125 | HANDLE *get_ptr(void) { return &_handle; } 126 | }; 127 | 128 | bool add_privilege(const std::string& name) { 129 | bool ret = false; 130 | scoped_handle token_handle; 131 | TOKEN_PRIVILEGES privileges = {}; 132 | 133 | BOOL open_status = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, token_handle.get_ptr()); 134 | 135 | if (open_status) { 136 | BOOL lookup_status = ::LookupPrivilegeValueA(nullptr, name.c_str(), &privileges.Privileges[0].Luid); 137 | 138 | if (lookup_status) { 139 | privileges.PrivilegeCount = 1; 140 | privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 141 | 142 | ret = static_cast(::AdjustTokenPrivileges(token_handle.get(), false, &privileges, 0, nullptr, nullptr)); 143 | } 144 | } 145 | 146 | return ret; 147 | } 148 | } -------------------------------------------------------------------------------- /drv-loader/include/lazy_loader_light.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "functor.hpp" 4 | 5 | #include 6 | #include 7 | 8 | #define NOMINMAX 9 | #include 10 | 11 | // compact and light lazy loader version (no exception, no litterals, basic modules and imports management) 12 | 13 | namespace lazy_loader_light { 14 | 15 | static bool is_import_str(const std::string& str) { 16 | return !str.empty() && std::all_of(str.begin(), str.end(), [](const auto& c) -> bool { return isalnum(c) || c == '!' || c == '_' || c == '.' || c == '/'; /*todo : better check*/ }); 17 | } 18 | 19 | static std::pair split_import_string(const std::string& str) { 20 | //assert(is_import_str(str)); 21 | 22 | std::regex reg("!"); 23 | std::sregex_token_iterator iter(str.begin(), str.end(), reg, -1); 24 | std::sregex_token_iterator end; 25 | 26 | std::vector splitted(iter, end); 27 | 28 | assert(splitted.size() >= 1 || splitted.size() <= 2); 29 | 30 | std::pair ret = std::make_pair(splitted.at(0), ""); 31 | 32 | if (splitted.size() > 1) { 33 | ret = std::make_pair(splitted.at(0), splitted.at(1)); 34 | } 35 | 36 | return ret; 37 | } 38 | 39 | #if defined(_WIN64) 40 | struct WindowsLoader { 41 | static std::uintptr_t load_module(const std::string& module_name) { 42 | return reinterpret_cast(::LoadLibraryA(module_name.c_str())); 43 | } 44 | 45 | static std::uintptr_t get_symbol(const std::uintptr_t module_handle, const std::string& symbol_name) { 46 | return reinterpret_cast(::GetProcAddress(reinterpret_cast(module_handle), symbol_name.c_str())); 47 | } 48 | 49 | static std::uint32_t free_module(const std::uintptr_t module_handle) { 50 | return ::FreeLibrary(reinterpret_cast(module_handle)); 51 | } 52 | }; 53 | #elif defined(__linux__) or defined(__APPLE__) 54 | struct UnixLoader { 55 | static std::uintptr_t load_module(const std::string& module_name) { 56 | return reinterpret_cast(dlopen(module_name.c_str(), RTLD_NOW)); 57 | } 58 | 59 | static std::uintptr_t get_symbol(const std::uintptr_t module_handle, const std::string& symbol_name) { 60 | return reinterpret_cast(dlsym(reinterpret_cast(module_handle), symbol_name.c_str())); 61 | } 62 | 63 | static std::uint32_t free_module(const std::uintptr_t module_handle) { 64 | return dlclose(reinterpret_cast(module_handle)); 65 | } 66 | }; 67 | #endif 68 | 69 | class lazyimport { 70 | public: 71 | lazyimport() = default; 72 | 73 | lazyimport(const std::string& name, std::uintptr_t ptr) 74 | : _name(name), _ptr(ptr) 75 | { 76 | static std::hash hashfn; 77 | _hash = hashfn(name); 78 | } 79 | 80 | lazyimport(const lazyimport&) = default; 81 | 82 | lazyimport& operator= (const lazyimport&) = default; 83 | 84 | lazyimport(lazyimport&&) noexcept = default; 85 | 86 | ~lazyimport() = default; 87 | 88 | template 89 | ReturnType operator()(Args&&... args) const { 90 | Functor functor(_ptr); 91 | return functor(std::forward(args)...); 92 | } 93 | 94 | template 95 | ReturnType call(Args&&... args) const { 96 | Functor functor(_ptr); 97 | return functor(std::forward(args)...); 98 | } 99 | 100 | operator bool() const { 101 | return _ptr != 0; 102 | } 103 | 104 | std::string name() const { 105 | return _name; 106 | } 107 | 108 | std::uintptr_t ptr() const { 109 | return _ptr; 110 | } 111 | 112 | std::size_t hash() const { 113 | return _hash; 114 | } 115 | private: 116 | std::string _name; 117 | std::size_t _hash; 118 | std::uintptr_t _ptr = 0; 119 | }; 120 | 121 | template 122 | class basic_lazyimportcollection { 123 | public: 124 | basic_lazyimportcollection() = default; 125 | 126 | basic_lazyimportcollection(const basic_lazyimportcollection&) = default; 127 | 128 | basic_lazyimportcollection& operator= (const basic_lazyimportcollection&) = default; 129 | 130 | basic_lazyimportcollection(basic_lazyimportcollection&&) noexcept = default; 131 | 132 | ~basic_lazyimportcollection() = default; 133 | 134 | void find_or_load(const std::uintptr_t& handle, const std::string& function_name, lazyimport& elem) { 135 | auto it = std::find_if(_collection.begin(), _collection.end(), [&function_name](const lazyimport& import) -> bool { 136 | static std::hash hash_fn; 137 | return hash_fn(function_name) == import.hash(); 138 | }); 139 | 140 | if (it != _collection.end()) { 141 | elem = *it; 142 | } else { 143 | std::uintptr_t ptr = LoaderTraits::get_symbol(handle, function_name.c_str()); 144 | 145 | if (ptr == 0) { 146 | std::string err = "cannot load function " + function_name; 147 | 148 | #if defined(__linux__) or defined(__APPLE__) 149 | err += " (" + std::string(dlerror()) + ")"; 150 | #endif 151 | 152 | return; 153 | } 154 | 155 | elem = _collection.emplace_back(function_name, ptr); 156 | } 157 | } 158 | 159 | std::size_t size() const { 160 | return _collection.size(); 161 | } 162 | 163 | private: 164 | std::vector _collection; 165 | }; 166 | 167 | template 168 | class basic_lazymodule { 169 | public: 170 | basic_lazymodule() = default; 171 | 172 | basic_lazymodule(const std::string& name, std::uintptr_t hmod) 173 | : _name(name), _handle(hmod) 174 | { 175 | static std::hash hashfn; 176 | _hash = hashfn(name); 177 | } 178 | 179 | basic_lazymodule(const basic_lazymodule&) = default; 180 | 181 | basic_lazymodule& operator= (const basic_lazymodule& mod) = default; 182 | 183 | basic_lazymodule(basic_lazymodule&&) noexcept = default; 184 | 185 | ~basic_lazymodule() = default; 186 | 187 | std::uint32_t unload() { 188 | return LoaderTraits::free_module(_handle); 189 | } 190 | 191 | std::string name() const { 192 | return _name; 193 | } 194 | 195 | std::uintptr_t handle() const { 196 | return _handle; 197 | } 198 | 199 | std::size_t hash() const { 200 | return _hash; 201 | } 202 | 203 | basic_lazyimportcollection imports() const { 204 | return _imports; 205 | } 206 | 207 | void add(const std::string& function_name, lazyimport& import) { 208 | _imports.find_or_load(_handle, function_name, import); 209 | } 210 | private: 211 | std::string _name; 212 | std::uintptr_t _handle = 0; 213 | std::size_t _hash; 214 | basic_lazyimportcollection _imports; 215 | }; 216 | 217 | template 218 | class basic_lazymodulecollection { 219 | private: 220 | basic_lazymodulecollection() = default; 221 | 222 | ~basic_lazymodulecollection() { 223 | for (auto& mod : _collection) { 224 | mod.unload(); 225 | } 226 | }; 227 | 228 | basic_lazymodulecollection(const basic_lazymodulecollection&) = delete; 229 | basic_lazymodulecollection& operator= (const basic_lazymodulecollection&) = delete; 230 | basic_lazymodulecollection(basic_lazymodulecollection&&) noexcept = default; 231 | 232 | public: 233 | static basic_lazymodulecollection& instance() { 234 | static basic_lazymodulecollection instance; 235 | return instance; 236 | } 237 | 238 | std::size_t size() const { 239 | return _collection.size(); 240 | } 241 | 242 | lazyimport register_import(const std::string& module_name, const std::string& function_name) { 243 | lazyimport import; 244 | 245 | basic_lazymodule module; 246 | 247 | if (!module_name.empty()) { 248 | find_or_load(module_name, module); 249 | 250 | if (!function_name.empty()) { 251 | module.add(function_name, import); 252 | } 253 | } 254 | 255 | return import; 256 | } 257 | 258 | lazyimport register_import(const std::string& path) { 259 | auto import_data = split_import_string(path); 260 | return register_import(import_data.first, import_data.second); 261 | } 262 | 263 | void find_or_load(const std::string& name, basic_lazymodule& elem) { 264 | auto it = std::find_if(_collection.begin(), _collection.end(), [&name](const basic_lazymodule& module) -> bool { 265 | static std::hash hash_fn; 266 | return hash_fn(name) == module.hash(); 267 | }); 268 | 269 | if (it != _collection.end()) { 270 | elem = *it; 271 | } 272 | else { 273 | std::uintptr_t hmod = LoaderTraits::load_module(name.c_str()); 274 | 275 | if (hmod == 0) { 276 | std::string err = "cannot load module " + name; 277 | 278 | #if defined(__linux__) or defined(__APPLE__) 279 | err += " (" + std::string(dlerror()) + ")"; 280 | #endif 281 | return; 282 | } 283 | 284 | elem = _collection.emplace_back(basic_lazymodule(name, hmod)); 285 | } 286 | } 287 | 288 | void unload(const std::string& name) { 289 | auto it = std::find_if(_collection.begin(), _collection.end(), [&name](const basic_lazymodule& module) -> bool { 290 | static std::hash hash_fn; 291 | return hash_fn(name) == module.hash(); 292 | }); 293 | 294 | if (it != _collection.end()) { 295 | basic_lazymodule mod = *it; 296 | mod.unload(); 297 | _collection.erase(it); 298 | } 299 | } 300 | 301 | private: 302 | using modulecollection = std::vector>; 303 | modulecollection _collection; 304 | }; 305 | 306 | #if defined(_WIN64) 307 | using lazymodule = basic_lazymodule; 308 | using lazymodulecollection = basic_lazymodulecollection; 309 | #elif defined(__linux__) or defined(__APPLE__) 310 | using lazymodule = basic_lazymodule; 311 | using lazymodulecollection = basic_lazymodulecollection; 312 | #endif 313 | 314 | #define LAZYCALL(ReturnType, path, ...) \ 315 | ::lazy_loader_light::lazymodulecollection::instance().register_import(path).call(__VA_ARGS__) 316 | 317 | #define LAZYLOAD(path) \ 318 | ::lazy_loader_light::lazymodulecollection::instance().register_import(path) 319 | 320 | #define LAZYUNLOAD(path) \ 321 | ::lazy_loader_light::lazymodulecollection::instance().unload(path) 322 | } 323 | 324 | -------------------------------------------------------------------------------- /drv-loader/main.cpp: -------------------------------------------------------------------------------- 1 | #include "include/clara.hpp" 2 | #include "include/drv-loader.hpp" 3 | 4 | #include 5 | 6 | #define NOMINMAX 7 | #include 8 | 9 | static std::uint32_t print_last_error(void) { 10 | std::uint32_t last_error_code = ::GetLastError(); 11 | std::cerr << "[!] Failed with last error " << last_error_code << std::endl; 12 | 13 | return last_error_code; 14 | } 15 | 16 | static void banner(void) { 17 | std::cout << 18 | " _ _ _ \n" 19 | " | | | | | | \n" 20 | " __| |_ ____ __ | | ___ __ _ __| | ___ _ __ \n" 21 | " / _` | '__\\ \\ / /______| |/ _ \\ / _` |/ _` |/ _ \\ '__|\n" 22 | " | (_| | | \\ V //_____/| | (_) | (_| | (_| | __/ | \n" 23 | " \\__,_|_| \\_/ |_|\\___/ \\__,_|\\__,_|\\___|_| \n" 24 | " \n" 25 | " (c) Midi12 \n" 26 | << std::endl; 27 | } 28 | 29 | int main(int argc, char* argv[], char* envp[]) { 30 | bool show_help = false; 31 | drv_loader::config_t config = {}; 32 | 33 | auto cmd_parser = clara::Help(show_help) 34 | | clara::Opt( 35 | [&](const std::string& display_name) { config.display_name = display_name; }, 36 | "Display name" 37 | )["--display"]["-d"]("Set the display name").required() 38 | | clara::Opt( 39 | [&](const std::string& op_type) { 40 | auto ret = clara::ParserResult::runtimeError("Unrecognized operation"); 41 | 42 | if (op_type == "load") { 43 | config.operation = drv_loader::loader_operation_t::load; 44 | ret = clara::ParserResult::ok(clara::ParseResultType::Matched); 45 | } else if (op_type == "unload") { 46 | config.operation = drv_loader::loader_operation_t::unload; 47 | ret = clara::ParserResult::ok(clara::ParseResultType::Matched); 48 | } 49 | 50 | return ret; 51 | }, 52 | "load|unload" 53 | )["--operation"]["-o"]("Load or unload the specified driver").required() 54 | | clara::Arg( 55 | [&](const std::string& file_path) { config.file_path = file_path; }, 56 | "Driver file path" 57 | )("Driver file to load"); 58 | 59 | auto result = cmd_parser.parse(clara::detail::Args(argc, argv)); 60 | 61 | banner(); 62 | 63 | if (!result) { 64 | std::cerr << "[!] Error in command line: " << result.errorMessage() << std::endl; 65 | return 1; 66 | } 67 | 68 | if (show_help) { 69 | std::cout << cmd_parser << std::endl; 70 | return 0; 71 | } 72 | 73 | if (config.operation == drv_loader::loader_operation_t::load && config.file_path.empty()) { 74 | std::cerr << "[!] File path is empty" << std::endl; 75 | return 1; 76 | } 77 | 78 | if (config.display_name.empty()) { 79 | std::cerr << "[!] Display name is empty" << std::endl; 80 | return 1; 81 | } 82 | 83 | if (!helpers::add_privilege("SeLoadDriverPrivilege")) { 84 | std::cerr << "[!] Failed to add SeLoadDriverPrivilege privilege" << std::endl; 85 | return 1; 86 | } 87 | 88 | std::uint32_t ret = drv_loader::load_unload(config); 89 | 90 | if (ret != ERROR_SUCCESS) { 91 | std::cerr << "[!] An error happened (last error: " << helpers::to_hex_string(ret) << ")" << std::endl; 92 | } else { 93 | switch (config.operation) { 94 | case drv_loader::loader_operation_t::load: 95 | std::cout << "[+] Driver loaded successfully" << std::endl; 96 | break; 97 | case drv_loader::loader_operation_t::unload: 98 | std::cout << "[+] Driver unloaded successfully" << std::endl; 99 | break; 100 | default: 101 | break; 102 | } 103 | } 104 | 105 | return 0; 106 | } -------------------------------------------------------------------------------- /img/showtime.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Midi12/drv-loader/f555dd8f7b6355c638ed8224efc4760b30a10d11/img/showtime.gif --------------------------------------------------------------------------------