├── DriverAnalyzer ├── src │ ├── pch.cc │ ├── utils │ │ └── utils.h │ ├── pch.h │ ├── analyzer │ │ ├── api │ │ │ ├── MmMapIoSpace.cc │ │ │ └── MmMapIoSpace.h │ │ ├── disassembler.h │ │ ├── disassembler.cc │ │ └── analyzer.h │ ├── data_types.h │ └── main.cc ├── clang-format.bat ├── DriverAnalyzer.vcxproj.filters ├── .clang-format └── DriverAnalyzer.vcxproj ├── .gitattributes ├── .gituhb_assets ├── MmMapIoSpace.png └── MmMapIoSpaceJson.png ├── msvc-cleaner.bat ├── LICENSE ├── DriverAnalyzer.sln ├── readme.md └── .gitignore /DriverAnalyzer/src/pch.cc: -------------------------------------------------------------------------------- 1 | #include "pch.h" -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gituhb_assets/MmMapIoSpace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BehroozAbbassi/DriverAnalyzer/HEAD/.gituhb_assets/MmMapIoSpace.png -------------------------------------------------------------------------------- /.gituhb_assets/MmMapIoSpaceJson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BehroozAbbassi/DriverAnalyzer/HEAD/.gituhb_assets/MmMapIoSpaceJson.png -------------------------------------------------------------------------------- /msvc-cleaner.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo. 4 | echo Removing files ... 5 | echo. 6 | 7 | del *.VC.db /s/q 8 | del *.bsc /s/q 9 | del *.pdb /s/q 10 | del *.iobj /s/q 11 | del *.ipdb /s/q 12 | del *.ilk /s/q 13 | del *.ipch /s/q 14 | del *.obj /s/q 15 | del *.sbr /s/q 16 | del *.tlog /s/q 17 | del *.suo /s/q 18 | 19 | echo. 20 | echo Removing build folders ... 21 | echo. 22 | 23 | for /d /r . %%d in (.vs __history Debug Release ipch build Intermediate x64) do ( 24 | 25 | if exist "%%d" ( 26 | rd /s/q "%%d" 27 | if not exist "%%d" ( echo [%%d] Removed! ) 28 | echo. 29 | ) 30 | ) 31 | 32 | echo Done! 33 | pause -------------------------------------------------------------------------------- /DriverAnalyzer/clang-format.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" 4 | set clang_format= 5 | 6 | cls 7 | 8 | setlocal enableextensions enabledelayedexpansion 9 | 10 | for /f "tokens=*" %%i in ('%vswhere% -latest -find VC\Tools\LLVM\**\bin\clang-format.exe') do ( 11 | echo clang-format found : %%i 12 | %%i --version 13 | set "clang_format="%%i"" 14 | ) 15 | 16 | for /r %%f in ( *.cc *.cpp *.hpp *.h *.c ) do ( 17 | set file_path=%%~pf 18 | 19 | :: discard the third_party directory 20 | if /I "!file_path!"=="!file_path:third_party%=!" ( 21 | echo formatting [%%f] 22 | call !clang_format! -i -style=file "%%f" 23 | ) 24 | ) 25 | 26 | endlocal 27 | 28 | echo Finish 29 | pause -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 BehroozAbbassi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /DriverAnalyzer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31825.309 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DriverAnalyzer", "DriverAnalyzer\DriverAnalyzer.vcxproj", "{92CD85E9-5155-414C-B605-DFFAA5375831}" 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 | {92CD85E9-5155-414C-B605-DFFAA5375831}.Debug|x64.ActiveCfg = Debug|x64 17 | {92CD85E9-5155-414C-B605-DFFAA5375831}.Debug|x64.Build.0 = Debug|x64 18 | {92CD85E9-5155-414C-B605-DFFAA5375831}.Debug|x86.ActiveCfg = Debug|Win32 19 | {92CD85E9-5155-414C-B605-DFFAA5375831}.Debug|x86.Build.0 = Debug|Win32 20 | {92CD85E9-5155-414C-B605-DFFAA5375831}.Release|x64.ActiveCfg = Release|x64 21 | {92CD85E9-5155-414C-B605-DFFAA5375831}.Release|x64.Build.0 = Release|x64 22 | {92CD85E9-5155-414C-B605-DFFAA5375831}.Release|x86.ActiveCfg = Release|Win32 23 | {92CD85E9-5155-414C-B605-DFFAA5375831}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {F9E43B83-02AB-4CD0-858D-3A8FD4C92B70} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Driver Analyzer 2 | 3 | A static analysis tool that helps security researchers scan a list of Windows kernel drivers for common vulnerability patterns in drivers (CVE makers!) 4 | 5 | The generic scan is not robust. It just suggests the potential drivers, but you can write more complex scans for specific APIs. (There is one example in the code tree for [MmMapIoSpace](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmmapiospace) API) 6 | 7 | For example, in the following picture, you can see a call to the `MmMapIoSpace` API and its first parameter that is controllable through `rcx` register (first argument in the `fastcall` calling convention), so this one has the potential to be a vulnerable call in the driver, you need to do more investigations manually by reversing the driver. 8 | 9 | In the end, if you can find a direct path from the `IOCTL` handler to this function call, congregates you have just found another stupid driver to be exploited. 10 | 11 | 12 | ![MmMapIoSpace](.gituhb_assets/MmMapIoSpaceJson.png) 13 | ![MmMapIoSpace](.gituhb_assets/MmMapIoSpace.png) 14 | 15 | 16 | > Note that this project was part of a larger project, and I just separated it as a standalone tool, so there are some inconsistencies in the code style like namings! 17 | 18 | # How to build 19 | 20 | You need to have installed these dependencies. 21 | 22 | ```bat 23 | vcpkg.exe install cereal:x64- indows cereal:x86-windows 24 | vcpkg.exe install zydis:x64-windows zydis:x86-windows 25 | vcpkg.exe install cxxopts:x64-windows cxxopts:x86-windows 26 | vcpkg.exe install lief[pe]:x64-windows lief[pe]:x86-windows 27 | ``` 28 | 29 | 30 | # Usage 31 | 32 | ```bat 33 | Usage: 34 | Vulnerable Driver Scanner [OPTION...] 35 | 36 | -i, --input arg Path of directory that contains Driver files (*.sys) 37 | -o, --output arg Full name of JSON report 38 | -b, --backup arg Path of backup directory to have a copy of suspicious 39 | driver files 40 | ``` -------------------------------------------------------------------------------- /DriverAnalyzer/DriverAnalyzer.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | -------------------------------------------------------------------------------- /DriverAnalyzer/src/utils/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace utils { 4 | struct Chrono 5 | { 6 | Chrono() : 7 | start(std::chrono::system_clock::now()) 8 | { 9 | } 10 | 11 | void Start() 12 | { 13 | start = std::chrono::system_clock::now(); 14 | } 15 | 16 | void Stop() 17 | { 18 | end = std::chrono::system_clock::now(); 19 | } 20 | 21 | uint32_t GetElapsedTime() const 22 | { 23 | return std::chrono::duration_cast(end - start).count(); 24 | } 25 | 26 | void PrintElapsedTime() 27 | { 28 | int elapsed_seconds = GetElapsedTime(); 29 | std::time_t end_time = std::chrono::system_clock::to_time_t(end); 30 | 31 | std::cout << "finished computation at " << std::ctime(&end_time) 32 | << "elapsed time: " << elapsed_seconds << "s\n"; 33 | } 34 | 35 | ~Chrono() 36 | { 37 | } 38 | 39 | private: 40 | std::chrono::time_point start, end; 41 | }; 42 | 43 | inline std::string 44 | FormatFileSize(const size_t fileSize) 45 | { 46 | const int MAX_FILE_SIZE_BUFFER = 255; 47 | char szFileSize[MAX_FILE_SIZE_BUFFER]; 48 | StrFormatByteSizeA(fileSize, 49 | szFileSize, 50 | MAX_FILE_SIZE_BUFFER); 51 | 52 | return szFileSize; 53 | } 54 | 55 | inline std::string 56 | GetFileName(const std::string & filePath) 57 | { 58 | std::filesystem::path path(filePath); 59 | return path.filename().string(); 60 | } 61 | 62 | inline std::string 63 | GeFormattedtFileSize(const std::string & filePath) 64 | { 65 | auto fileSize = std::filesystem::file_size(filePath); 66 | return std::to_string(fileSize) + " (" + utils::FormatFileSize(fileSize) + ")"; 67 | } 68 | 69 | template 70 | std::string 71 | IntToHex(T i) 72 | { 73 | std::stringstream stream; 74 | stream << "0x" 75 | << std::setfill('0') << std::setw(sizeof(T) * 2) 76 | << std::hex << i; 77 | return stream.str(); 78 | } 79 | 80 | inline std::string 81 | str_tolower(std::string s) 82 | { 83 | std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); } // correct 84 | ); 85 | return s; 86 | } 87 | 88 | } // namespace utils -------------------------------------------------------------------------------- /DriverAnalyzer/src/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Windows API headers 5 | // 6 | 7 | #pragma region Win32 API Headers 8 | 9 | #define NOMINMAX 10 | //#define CINTERFACE 11 | #define _SCL_SECURE_NO_WARNINGS 12 | #define _CRT_SECURE_NO_WARNINGS 13 | 14 | #ifndef WIN32_LEAN_AND_MEAN 15 | # define WIN32_LEAN_AND_MEAN 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | #include "shlwapi.h" 22 | #pragma comment(lib, "Shlwapi.lib") 23 | 24 | #pragma endregion = > Win32 API Headers 25 | 26 | // 27 | // std headers 28 | // 29 | 30 | #pragma region C++ Standrad Headers 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | namespace fs = std::filesystem; 47 | 48 | #pragma endregion = > C++ Standrad Headers 49 | 50 | // 51 | // third-party library headers 52 | // 53 | 54 | // 55 | // cereal - A C++11 library for serialization 56 | // vcpkg.exe install cereal:x64- indows cereal:x86-windows 57 | // 58 | #define USE_CEREAL_SERIALIZE 59 | #ifdef USE_CEREAL_SERIALIZE 60 | # ifndef CINTERFACE 61 | # define CINTERFACE 62 | # endif 63 | 64 | # include 65 | # include 66 | # include 67 | # include 68 | 69 | # ifdef CINTERFACE 70 | # undef CINTERFACE 71 | # endif 72 | #endif 73 | 74 | // 75 | // zydis - Fast and lightweight x86/x86-64 disassembler library https://zydis.re 76 | // vcpkg.exe install zydis:x64-windows zydis:x86-windows 77 | // 78 | 79 | #include 80 | #include 81 | #define MIN_FUNC_SIZE 0x20 82 | #define MAX_FUNC_SIZE 0x100 83 | 84 | // Lightweight C++ command line option parser 85 | // vcpkg.exe install cxxopts : x64 - windows cxxopts : x86 - windows 86 | #include 87 | 88 | // 89 | // LIEF 90 | // 91 | #pragma warning(push) 92 | #pragma warning(disable : 4146) // C4146: unary minus operator applied to \ 93 | // unsigned type, result still unsigned 94 | #pragma warning(disable : 4996) 95 | #pragma warning(disable : 4267) 96 | 97 | #include 98 | 99 | #pragma comment(lib, "LIEF.lib") 100 | 101 | #pragma warning(pop) 102 | 103 | #include "utils/utils.h" -------------------------------------------------------------------------------- /DriverAnalyzer/src/analyzer/api/MmMapIoSpace.cc: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "data_types.h" 3 | #include "analyzer/disassembler.h" 4 | #include "analyzer/api/MmMapIoSpace.h" 5 | 6 | namespace driver_analyzer { 7 | 8 | namespace static_analyzer { 9 | namespace api { 10 | data_types::Parameter 11 | MmMapIoSpaceAnalazer::GetArgumentInfo(size_t current_index, 12 | uint32_t arg_number) 13 | { 14 | data_types::Parameter result = {}; 15 | result.Index = arg_number; 16 | result.Name = GetParamterName(arg_number); 17 | 18 | // 19 | // Backward travers instruction before Call to find arguments (fastcall) 20 | // 21 | auto back_step = current_index + 10; 22 | 23 | //if ((instructions.size() - back_step) < back_step) 24 | 25 | for (size_t rev_index = current_index; rev_index < back_step; rev_index--) 26 | { 27 | auto & rev_inst = instruction_list_[rev_index].Instruction; 28 | 29 | auto & rev_first_operand = rev_inst.operands[0]; 30 | auto & rev_second_operand = rev_inst.operands[1]; 31 | 32 | auto argument_register = m_disassembler->GetFastCallArgumentRegister(arg_number); 33 | 34 | if (m_disassembler->IsSameRegister(rev_first_operand.reg.value, argument_register) == false) 35 | continue; 36 | 37 | if (arg_number == 3) 38 | { 39 | result.Value = GetMmMapIoSpaceCacheType(instruction_list_[rev_index]); 40 | break; 41 | } 42 | 43 | if (rev_inst.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_XOR) 44 | { 45 | if (m_disassembler->IsRegXoredByItSelf(rev_inst, argument_register)) 46 | { 47 | result.Value = "0x0"; 48 | break; 49 | } 50 | } 51 | 52 | if (rev_inst.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_MOV || 53 | rev_inst.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_SETNZ) 54 | { 55 | if (m_disassembler->IsImmMovToReg(rev_inst, argument_register)) 56 | { 57 | result.ValueType = data_types::ValueType::kImmediate; 58 | result.Value = utils::IntToHex(rev_second_operand.imm.is_signed ? rev_second_operand.imm.value.s : rev_second_operand.imm.value.u); 59 | 60 | break; 61 | } 62 | 63 | if (m_disassembler->IsRegMovToReg(rev_inst, argument_register)) 64 | { 65 | result.ValueType = data_types::ValueType::kRegister; 66 | result.Value = ZydisRegisterGetString(rev_second_operand.reg.value); 67 | 68 | break; 69 | } 70 | 71 | if (m_disassembler->IsMemMovToReg(rev_inst, argument_register)) 72 | { 73 | result.ValueType = data_types::ValueType::kMemoryAddress; 74 | result.Value = m_disassembler->FormatOperand(instruction_list_[rev_index], 1); 75 | 76 | break; 77 | } 78 | } 79 | } 80 | 81 | return result; 82 | } 83 | } // namespace api 84 | } // namespace static_analyzer 85 | } // namespace driver_analyzer -------------------------------------------------------------------------------- /DriverAnalyzer/src/analyzer/disassembler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace driver_analyzer { 4 | 5 | namespace static_analyzer { 6 | 7 | class Diassembler 8 | { 9 | public: 10 | Diassembler( 11 | const std::vector & raw_data, 12 | const data_types::disassemlber::DisassmblerOptions & disassmbler_options) : 13 | m_rawData(raw_data), m_disassmblerOptions(disassmbler_options) {} 14 | 15 | std::vector GetDisassembledCode(); 16 | 17 | std::string FormatInstruction( 18 | const data_types::disassemlber::DisInstruction & inst); 19 | 20 | std::string 21 | FormatOperand( 22 | const data_types::disassemlber::DisInstruction & inst, 23 | const uint8_t & operand_index) 24 | { 25 | char formattedBuffer[MAX_PATH]; 26 | char address[MAX_PATH]; 27 | ZydisFormatter formatter; 28 | ZyanU64 runtime_address = inst.Address; 29 | if (!ZYAN_SUCCESS( 30 | ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) 31 | return ""; 32 | 33 | ZydisFormatterSetProperty( 34 | &formatter, 35 | ZydisFormatterProperty::ZYDIS_FORMATTER_PROP_FORCE_SEGMENT, 36 | ZYAN_TRUE); 37 | 38 | memset(formattedBuffer, 0, sizeof(formattedBuffer)); 39 | if (ZYAN_SUCCESS(ZydisFormatterFormatOperand( 40 | &formatter, 41 | &inst.Instruction, 42 | operand_index, 43 | formattedBuffer, 44 | sizeof(formattedBuffer), 45 | runtime_address))) 46 | { 47 | sprintf_s(address, MAX_PATH, "%s", formattedBuffer); 48 | } 49 | 50 | return address; 51 | } 52 | 53 | bool GetPritableDisassembledCode( 54 | const std::vector & instructions, 55 | std::vector & result); 56 | 57 | bool IsSameRegister(ZydisRegister a, ZydisRegister b); 58 | 59 | ZydisRegister GetFastCallArgumentRegister(uint32_t arg_number); 60 | 61 | bool IsRegXoredByItSelf(const ZydisDecodedInstruction & inst, 62 | const ZydisRegister & reg); 63 | 64 | bool IsRegMovToReg(const ZydisDecodedInstruction & inst, 65 | const ZydisRegister & reg); 66 | 67 | bool IsImmMovToReg(const ZydisDecodedInstruction & inst, 68 | const ZydisRegister & reg); 69 | 70 | bool IsMemMovToReg(const ZydisDecodedInstruction & inst, 71 | const ZydisRegister & reg); 72 | 73 | private: 74 | std::vector m_rawData; 75 | std::vector m_instructions; 76 | data_types::disassemlber::DisassmblerOptions m_disassmblerOptions; 77 | 78 | bool DisassembleCode( 79 | const uint8_t * pRawData, 80 | const size_t rawDataSize, 81 | std::vector & result); 82 | }; 83 | 84 | } // namespace static_analyzer 85 | } // namespace driver_analyzer -------------------------------------------------------------------------------- /DriverAnalyzer/.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: google 3 | ColumnLimit: 80 4 | AccessModifierOffset: -4 5 | 6 | AlignAfterOpenBracket: Align 7 | AlignConsecutiveAssignments: true 8 | 9 | AlignConsecutiveBitFields: true 10 | 11 | AlignConsecutiveDeclarations: true 12 | 13 | AlignConsecutiveMacros: true 14 | 15 | AlignEscapedNewlines: Left 16 | AlignOperands: true 17 | 18 | AlignTrailingComments: true 19 | 20 | AllowAllArgumentsOnNextLine: false 21 | AllowAllParametersOfDeclarationOnNextLine: false 22 | 23 | AllowShortBlocksOnASingleLine: false 24 | AllowShortCaseLabelsOnASingleLine: false 25 | AllowShortFunctionsOnASingleLine: Inline 26 | AllowShortIfStatementsOnASingleLine: false 27 | AllowShortLoopsOnASingleLine: false 28 | AlwaysBreakAfterReturnType: TopLevel 29 | AlwaysBreakBeforeMultilineStrings: false 30 | 31 | AlwaysBreakTemplateDeclarations: true #false 32 | 33 | BinPackArguments: false 34 | BinPackParameters: false 35 | 36 | BreakBeforeBraces: Custom 37 | BraceWrapping: 38 | AfterCaseLabel: true 39 | AfterClass: true 40 | AfterControlStatement: true 41 | AfterEnum: true 42 | AfterFunction: true 43 | AfterNamespace: false 44 | AfterStruct: true 45 | AfterUnion: true 46 | AfterExternBlock: false 47 | BeforeCatch: true 48 | BeforeElse: true 49 | 50 | BreakBeforeBinaryOperators: None 51 | BreakBeforeTernaryOperators: true 52 | BreakConstructorInitializers: AfterColon 53 | BreakStringLiterals: false 54 | 55 | ColumnLimit: 0 56 | CommentPragmas: '^begin_wpp|^end_wpp|^FUNC |^USESUFFIX |^USESUFFIX ' 57 | 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 59 | ConstructorInitializerIndentWidth: 4 60 | ContinuationIndentWidth: 4 61 | Cpp11BracedListStyle: true 62 | 63 | DerivePointerAlignment: false 64 | ExperimentalAutoDetectBinPacking: false 65 | 66 | IndentCaseLabels: false 67 | IndentPPDirectives: AfterHash 68 | IndentWidth: 4 69 | 70 | KeepEmptyLinesAtTheStartOfBlocks: false 71 | Language: Cpp 72 | 73 | MacroBlockBegin: '^BEGIN_MODULE$|^BEGIN_TEST_CLASS$|^BEGIN_TEST_METHOD$' 74 | MacroBlockEnd: '^END_MODULE$|^END_TEST_CLASS$|^END_TEST_METHOD$' 75 | 76 | MaxEmptyLinesToKeep: 1 77 | NamespaceIndentation: None #All 78 | PointerAlignment: Middle 79 | ReflowComments: true 80 | SortIncludes: false 81 | 82 | SpaceAfterCStyleCast: false 83 | SpaceBeforeAssignmentOperators: true 84 | SpaceBeforeCtorInitializerColon: true 85 | SpaceBeforeCtorInitializerColon: true 86 | SpaceBeforeParens: ControlStatements 87 | SpaceBeforeRangeBasedForLoopColon: true 88 | SpaceInEmptyParentheses: false 89 | SpacesInAngles: false 90 | SpacesInCStyleCastParentheses: false 91 | SpacesInParentheses: false 92 | SpacesInSquareBrackets: false 93 | 94 | Standard: Cpp11 95 | StatementMacros: [ 96 | 'EXTERN_C', 97 | # 'CALLBACK', 98 | # 'VOID', 99 | 'PAGED', 100 | 'PAGEDX', 101 | 'NONPAGED', 102 | 'PNPCODE', 103 | 'INITCODE', 104 | '_At_', 105 | '_When_', 106 | '_Success_', 107 | '_Check_return_', 108 | '_Must_inspect_result_', 109 | '_IRQL_requires_', 110 | '_IRQL_requires_max_', 111 | '_IRQL_requires_min_', 112 | '_IRQL_saves_', 113 | '_IRQL_restores_', 114 | '_IRQL_saves_global_', 115 | '_IRQL_restores_global_', 116 | '_IRQL_raises_', 117 | '_IRQL_lowers_', 118 | '_Acquires_lock_', 119 | '_Releases_lock_', 120 | '_Acquires_exclusive_lock_', 121 | '_Releases_exclusive_lock_', 122 | '_Acquires_shared_lock_', 123 | '_Releases_shared_lock_', 124 | '_Requires_lock_held_', 125 | '_Use_decl_annotations_', 126 | '_Guarded_by_', 127 | '__drv_preferredFunction', 128 | '__drv_allocatesMem', 129 | '__drv_freesMem', 130 | ] 131 | 132 | TabWidth: '4' 133 | UseTab: Never -------------------------------------------------------------------------------- /DriverAnalyzer/src/data_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace data_types { 4 | 5 | namespace disassemlber { 6 | 7 | struct DisassmblerOptions 8 | { 9 | ZydisAddressWidth AddressWidth; 10 | ZydisMachineMode MachineMode; 11 | uint64_t BaseRuntimeAddr; 12 | }; 13 | 14 | struct DisInstruction 15 | { 16 | uint64_t Address; 17 | ZydisDecodedInstruction Instruction; 18 | 19 | // bool operator==(const DisInstruction & left, const DisInstruction & right) 20 | //{ 21 | // return &left == &right; 22 | //} 23 | }; 24 | } // namespace disassemlber 25 | 26 | namespace analyzer { 27 | 28 | struct ExecutableSectionDisassembly 29 | { 30 | std::string Name; 31 | std::vector Instructions; 32 | }; 33 | 34 | } // namespace analyzer 35 | 36 | struct PeFileVersionInfo 37 | { 38 | std::string CompanyName; 39 | std::string FileDescription; 40 | std::string FileVersion; 41 | std::string InternalName; 42 | std::string LegalCopyright; 43 | std::string OriginalFileName; 44 | std::string ProductName; 45 | std::string ProductVersion; 46 | 47 | template 48 | void serialize(Archive & ar) 49 | { 50 | ar(CEREAL_NVP(CompanyName), 51 | CEREAL_NVP(FileDescription), 52 | CEREAL_NVP(FileVersion), 53 | CEREAL_NVP(InternalName), 54 | CEREAL_NVP(LegalCopyright), 55 | CEREAL_NVP(OriginalFileName), 56 | CEREAL_NVP(ProductName), 57 | CEREAL_NVP(ProductVersion)); 58 | } 59 | }; 60 | 61 | struct PeFileInfo 62 | { 63 | std::string Name; 64 | std::string ImagePath; 65 | std::string FileSize; 66 | 67 | PeFileVersionInfo Version; 68 | 69 | template 70 | void serialize(Archive & ar) 71 | { 72 | ar(CEREAL_NVP(Name), 73 | CEREAL_NVP(ImagePath), 74 | CEREAL_NVP(FileSize), 75 | CEREAL_NVP(Version)); 76 | } 77 | }; 78 | 79 | struct Instruction_t 80 | { 81 | std::uint64_t Offset = 0; 82 | disassemlber::DisInstruction Instruction; 83 | std::string FormattedInstruction; 84 | 85 | template 86 | void serialize(Archive & ar) 87 | { 88 | ar(CEREAL_NVP(Offset), 89 | // CEREAL_NVP(Instruction), 90 | CEREAL_NVP(FormattedInstruction)); 91 | } 92 | }; 93 | 94 | struct SuspiciousInstructions 95 | { 96 | std::vector Instructions; 97 | std::uint64_t Count = 0; 98 | 99 | template 100 | void serialize(Archive & ar) 101 | { 102 | ar(CEREAL_NVP(Instructions), 103 | CEREAL_NVP(Count)); 104 | } 105 | }; 106 | 107 | enum class ValueType : uint8_t 108 | { 109 | kImmediate, 110 | kRegister, 111 | kMemoryAddress 112 | }; 113 | 114 | struct Parameter 115 | { 116 | std::string Name; 117 | std::uint32_t Index; 118 | std::string Value; 119 | ValueType ValueType; 120 | 121 | std::string to_string() const 122 | { 123 | std::stringstream ss; 124 | 125 | ss << Name << "= " << Value; 126 | 127 | return ss.str(); 128 | } 129 | 130 | template 131 | void serialize(Archive & ar) 132 | { 133 | ar(CEREAL_NVP(Name), 134 | CEREAL_NVP(Index), 135 | CEREAL_NVP(Value)); 136 | } 137 | }; 138 | 139 | struct FunctionCall 140 | { 141 | std::string Name; 142 | std::vector Parameters; 143 | data_types::Instruction_t Instruction; 144 | 145 | template 146 | void serialize(Archive & ar) 147 | { 148 | ar(CEREAL_NVP(Name), 149 | CEREAL_NVP(Instruction), 150 | CEREAL_NVP(Parameters)); 151 | } 152 | }; 153 | 154 | struct SuspiciousSection 155 | { 156 | std::string Name; 157 | 158 | SuspiciousInstructions Cr3; 159 | SuspiciousInstructions Cr4; 160 | SuspiciousInstructions Msr; 161 | 162 | std::vector ApiCalls; 163 | 164 | template 165 | void serialize(Archive & ar) 166 | { 167 | ar(CEREAL_NVP(Name), 168 | CEREAL_NVP(Cr3), 169 | CEREAL_NVP(Cr4), 170 | CEREAL_NVP(Msr), 171 | CEREAL_NVP(ApiCalls)); 172 | } 173 | }; 174 | 175 | struct SuspiciousDriver 176 | { 177 | PeFileInfo FileInfo; 178 | uint32_t IatScore = 0; 179 | uint32_t Cr3Score = 0; 180 | uint32_t Cr4Score = 0; 181 | uint32_t MsrScore = 0; 182 | uint32_t ApiCallScore = 0; 183 | std::vector Imports; 184 | std::vector Sections; 185 | 186 | template 187 | void serialize(Archive & ar) 188 | { 189 | ar(CEREAL_NVP(FileInfo), 190 | CEREAL_NVP(IatScore), 191 | CEREAL_NVP(Cr3Score), 192 | CEREAL_NVP(Cr4Score), 193 | CEREAL_NVP(MsrScore), 194 | CEREAL_NVP(Imports), 195 | CEREAL_NVP(Sections)); 196 | } 197 | }; 198 | 199 | } // namespace data_types -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_h.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *_wpftmp.csproj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush personal settings 296 | .cr/personal 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | 333 | # Local History for Visual Studio 334 | .localhistory/ 335 | -------------------------------------------------------------------------------- /DriverAnalyzer/src/analyzer/api/MmMapIoSpace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace driver_analyzer { 4 | 5 | namespace static_analyzer { 6 | namespace api { 7 | 8 | class MmMapIoSpaceAnalazer 9 | { 10 | public: 11 | MmMapIoSpaceAnalazer(const std::vector & instructions, 12 | const size_t & call_inst_index) : 13 | instruction_list_(instructions), 14 | call_inst_index_(call_inst_index) 15 | { 16 | } 17 | 18 | struct Parameters 19 | { 20 | data_types::Parameter PhysicalAddress; 21 | data_types::Parameter NumberOfBytes; 22 | data_types::Parameter CacheType; 23 | }; 24 | 25 | data_types::FunctionCall GetFunctionCall() 26 | { 27 | data_types::FunctionCall result; 28 | result.Name = Name(); 29 | 30 | result.Parameters.push_back(parameters_.PhysicalAddress); 31 | result.Parameters.push_back(parameters_.NumberOfBytes); 32 | result.Parameters.push_back(parameters_.CacheType); 33 | 34 | const auto & curr = instruction_list_[call_inst_index_]; 35 | 36 | data_types::Instruction_t i; 37 | i.Offset = curr.Address; 38 | i.Instruction = curr; 39 | i.FormattedInstruction = m_disassembler->FormatInstruction(curr); 40 | 41 | result.Instruction = i; 42 | 43 | return result; 44 | } 45 | 46 | std::string to_string() const 47 | { 48 | //return fmt::format("MmMapIoSpace (PhysicalAddress : {0}, NumberOfBytes : {1}, CacheType : {2} );", 49 | // parameters_.PhysicalAddress.to_string(), 50 | // parameters_.NumberOfBytes.to_string(), 51 | // parameters_.CacheType.to_string()); 52 | 53 | auto s1 = parameters_.PhysicalAddress.to_string(); 54 | auto s2 = parameters_.NumberOfBytes.to_string(); 55 | auto s3 = parameters_.CacheType.to_string(); 56 | 57 | //return fmt::format("MmMapIoSpace (PhysicalAddress : {0}, NumberOfBytes : {1}, CacheType : {2} );", 58 | std::stringstream ss; 59 | ss << "MmMapIoSpace( "; 60 | 61 | ss << s1 << " ," 62 | << s2 << " ," << s3 << " );\n"; 63 | 64 | return ss.str(); 65 | return ""; 66 | } 67 | 68 | std::string Name() const 69 | { 70 | return "MmMapIoSpace"; 71 | } 72 | 73 | bool Analyze(const std::string & function_name) 74 | { 75 | const auto & curr_inst = instruction_list_[call_inst_index_]; 76 | const ZydisDecodedOperand & first_operand = curr_inst.Instruction.operands[0]; 77 | 78 | parameters_.PhysicalAddress = GetArgumentInfo(call_inst_index_, 1); // PhysicalAddress 79 | 80 | parameters_.NumberOfBytes = GetArgumentInfo(call_inst_index_, 2); // NumberOfBytes 81 | 82 | parameters_.CacheType = GetArgumentInfo(call_inst_index_, 3); // CacheType 83 | 84 | //printf("Call %s ; %s\n\n", m_disassembler->FormatInstruction(curr_inst).c_str(), function_name.c_str()); 85 | 86 | return false; 87 | } 88 | 89 | data_types::Parameter GetArgumentInfo(size_t current_index, 90 | uint32_t arg_number); 91 | 92 | private: 93 | std::string GetParamterName(const uint32_t & index) 94 | { 95 | switch (index) 96 | { 97 | case 1: 98 | return "PhysicalAddress"; 99 | case 2: 100 | return "NumberOfBytes"; 101 | case 3: 102 | return "CacheType"; 103 | default: 104 | break; 105 | } 106 | 107 | return "N/A"; 108 | } 109 | 110 | typedef enum _MEMORY_CACHING_TYPE 111 | { 112 | MmNonCached, 113 | MmCached, 114 | MmWriteCombined, 115 | MmHardwareCoherentCached, 116 | MmNonCachedUnordered, 117 | MmUSWCCached, 118 | MmMaximumCacheType, 119 | MmNotMapped 120 | } MEMORY_CACHING_TYPE; 121 | 122 | std::string GetMmMapIoSpaceCacheType(const data_types::disassemlber::DisInstruction & inst) 123 | { 124 | auto & rev_inst = inst.Instruction; 125 | 126 | auto & rev_first_operand = rev_inst.operands[0]; 127 | auto & rev_second_operand = rev_inst.operands[1]; 128 | 129 | uint64_t cache_type = -1; 130 | 131 | auto argument_register = m_disassembler->GetFastCallArgumentRegister(3); 132 | 133 | if (rev_inst.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_XOR || 134 | rev_inst.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_MOV || 135 | rev_inst.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_SETNZ) 136 | { 137 | if (m_disassembler->IsRegXoredByItSelf(rev_inst, argument_register)) 138 | { 139 | cache_type = 0; 140 | } 141 | 142 | if (m_disassembler->IsImmMovToReg(rev_inst, argument_register)) 143 | { 144 | cache_type = rev_second_operand.imm.is_signed ? rev_second_operand.imm.value.s : rev_second_operand.imm.value.u; 145 | } 146 | 147 | if (m_disassembler->IsRegMovToReg(rev_inst, argument_register)) 148 | { 149 | //std::cout << "Reg " << m_disassembler->FormatInstruction(inst) << "\n"; 150 | } 151 | 152 | if (m_disassembler->IsMemMovToReg(rev_inst, argument_register)) 153 | { 154 | //std::cout << "Mem " << m_disassembler->FormatInstruction(inst) << "\n"; 155 | } 156 | 157 | switch (cache_type) 158 | { 159 | case MEMORY_CACHING_TYPE::MmNonCached: 160 | return "MmNonCached"; 161 | case MEMORY_CACHING_TYPE::MmCached: 162 | return "MmCached"; 163 | case MEMORY_CACHING_TYPE::MmWriteCombined: 164 | return "MmWriteCombined"; 165 | case MEMORY_CACHING_TYPE::MmHardwareCoherentCached: 166 | return "MmHardwareCoherentCached"; 167 | case MEMORY_CACHING_TYPE::MmNonCachedUnordered: 168 | return "MmNonCachedUnordered"; 169 | case MEMORY_CACHING_TYPE::MmUSWCCached: 170 | return "MmUSWCCached"; 171 | case MEMORY_CACHING_TYPE::MmMaximumCacheType: 172 | return "MmMaximumCacheType"; 173 | case MEMORY_CACHING_TYPE::MmNotMapped: 174 | return "MmNotMapped"; 175 | default: 176 | //printf("ccc\n"); 177 | break; 178 | } 179 | } 180 | 181 | return "N/A"; 182 | } 183 | 184 | private: 185 | std::string m_fileName; 186 | std::shared_ptr m_peFile; 187 | std::unique_ptr m_disassembler; 188 | std::vector m_disassembledCodes; 189 | std::vector instruction_list_; 190 | size_t call_inst_index_; 191 | Parameters parameters_; 192 | }; 193 | } // namespace api 194 | 195 | } // namespace static_analyzer 196 | } // namespace driver_analyzer -------------------------------------------------------------------------------- /DriverAnalyzer/src/analyzer/disassembler.cc: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "data_types.h" 3 | #include "analyzer/disassembler.h" 4 | 5 | namespace driver_analyzer { 6 | 7 | namespace static_analyzer { 8 | std::vector 9 | Diassembler::GetDisassembledCode() 10 | { 11 | DisassembleCode(reinterpret_cast(m_rawData.data()), 12 | m_rawData.size(), 13 | m_instructions); 14 | return m_instructions; 15 | } 16 | std::string 17 | Diassembler::FormatInstruction( 18 | const data_types::disassemlber::DisInstruction & inst) 19 | { 20 | char formattedBuffer[MAX_PATH]; 21 | char address[MAX_PATH]; 22 | ZydisFormatter formatter; 23 | ZyanU64 runtime_address = inst.Address; 24 | if (!ZYAN_SUCCESS( 25 | ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) 26 | return ""; 27 | 28 | ZydisFormatterSetProperty( 29 | &formatter, 30 | ZydisFormatterProperty::ZYDIS_FORMATTER_PROP_FORCE_SEGMENT, 31 | ZYAN_TRUE); 32 | 33 | memset(formattedBuffer, 0, sizeof(formattedBuffer)); 34 | if (ZYAN_SUCCESS(ZydisFormatterFormatInstruction( 35 | &formatter, 36 | &inst.Instruction, 37 | formattedBuffer, 38 | sizeof(formattedBuffer), 39 | runtime_address))) 40 | { 41 | sprintf_s(address, MAX_PATH, "0x%016" PRIX64 " %s", runtime_address, formattedBuffer); 42 | } 43 | 44 | return address; 45 | } 46 | bool 47 | Diassembler::GetPritableDisassembledCode( 48 | const std::vector & instructions, 49 | std::vector & result) 50 | { 51 | char formattedBuffer[MAX_PATH]; 52 | ZydisFormatter formatter; 53 | ZyanU64 runtime_address = m_disassmblerOptions.BaseRuntimeAddr; 54 | if (!ZYAN_SUCCESS( 55 | ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) 56 | return false; 57 | 58 | for (auto && instruction : instructions) 59 | { 60 | memset(formattedBuffer, 0, sizeof(formattedBuffer)); 61 | if (ZYAN_SUCCESS(ZydisFormatterFormatInstruction( 62 | &formatter, 63 | &instruction, 64 | formattedBuffer, 65 | sizeof(formattedBuffer), 66 | runtime_address))) 67 | { 68 | char address[MAX_PATH]; 69 | sprintf_s(address, MAX_PATH, "0x%016" PRIX64 " %s", runtime_address, formattedBuffer); 70 | 71 | result.push_back(address); 72 | 73 | runtime_address += instruction.length; 74 | } 75 | } 76 | 77 | return true; 78 | } 79 | bool 80 | Diassembler::IsSameRegister(ZydisRegister a, ZydisRegister b) 81 | { 82 | #define GP_REGISTER_COUNT ((ZYDIS_REGISTER_R15 - ZYDIS_REGISTER_RAX) + 1) 83 | if (a == b) 84 | { 85 | return TRUE; 86 | } 87 | #undef max 88 | if (a <= ZYDIS_REGISTER_R15 && a >= ZYDIS_REGISTER_AX) 89 | { 90 | for (auto i = ((a - ZYDIS_REGISTER_AX) % GP_REGISTER_COUNT) + 91 | ZYDIS_REGISTER_AX; 92 | i <= std::max(a, b); 93 | i += GP_REGISTER_COUNT) 94 | { 95 | if (b == i) 96 | { 97 | return TRUE; 98 | } 99 | } 100 | } 101 | 102 | return FALSE; 103 | } 104 | ZydisRegister 105 | Diassembler::GetFastCallArgumentRegister(uint32_t arg_number) 106 | { 107 | switch (arg_number) 108 | { 109 | case 1: 110 | return ZydisRegister::ZYDIS_REGISTER_RCX; 111 | case 2: 112 | return ZydisRegister::ZYDIS_REGISTER_RDX; 113 | case 3: 114 | return ZydisRegister::ZYDIS_REGISTER_R8; 115 | case 4: 116 | return ZydisRegister::ZYDIS_REGISTER_R9; 117 | default: 118 | break; 119 | } 120 | 121 | return ZydisRegister::ZYDIS_REGISTER_NONE; 122 | } 123 | bool 124 | Diassembler::IsRegXoredByItSelf(const ZydisDecodedInstruction & inst, const ZydisRegister & reg) 125 | { 126 | if (inst.mnemonic != ZydisMnemonic::ZYDIS_MNEMONIC_XOR) 127 | return false; 128 | 129 | auto & first_operand = inst.operands[0]; 130 | auto & sec_operand = inst.operands[1]; 131 | 132 | // mov reg, reg 133 | if ((first_operand.type == ZydisOperandType::ZYDIS_OPERAND_TYPE_REGISTER && 134 | IsSameRegister(first_operand.reg.value, reg)) && 135 | (sec_operand.type == first_operand.type && 136 | sec_operand.reg.value == first_operand.reg.value)) 137 | { 138 | return true; 139 | } 140 | 141 | return false; 142 | } 143 | bool 144 | Diassembler::IsRegMovToReg(const ZydisDecodedInstruction & inst, const ZydisRegister & reg) 145 | { 146 | if (inst.mnemonic != ZydisMnemonic::ZYDIS_MNEMONIC_MOV) 147 | return false; 148 | 149 | auto & first_operand = inst.operands[0]; 150 | auto & sec_operand = inst.operands[1]; 151 | 152 | // mov reg, ??? 153 | if (first_operand.type != ZydisOperandType::ZYDIS_OPERAND_TYPE_REGISTER || 154 | !IsSameRegister(first_operand.reg.value, reg)) 155 | return false; 156 | 157 | if (sec_operand.type == ZydisOperandType::ZYDIS_OPERAND_TYPE_REGISTER) 158 | return true; 159 | return false; 160 | } 161 | bool 162 | Diassembler::IsImmMovToReg(const ZydisDecodedInstruction & inst, const ZydisRegister & reg) 163 | { 164 | if (inst.mnemonic != ZydisMnemonic::ZYDIS_MNEMONIC_MOV) 165 | return false; 166 | 167 | auto & first_operand = inst.operands[0]; 168 | auto & sec_operand = inst.operands[1]; 169 | 170 | // mov reg, ??? 171 | if (first_operand.type != ZydisOperandType::ZYDIS_OPERAND_TYPE_REGISTER || 172 | !IsSameRegister(first_operand.reg.value, reg)) 173 | return false; 174 | 175 | if (sec_operand.type == ZydisOperandType::ZYDIS_OPERAND_TYPE_IMMEDIATE) 176 | return true; 177 | return false; 178 | } 179 | bool 180 | Diassembler::IsMemMovToReg(const ZydisDecodedInstruction & inst, const ZydisRegister & reg) 181 | { 182 | if (inst.mnemonic != ZydisMnemonic::ZYDIS_MNEMONIC_MOV) 183 | return false; 184 | 185 | auto & first_operand = inst.operands[0]; 186 | auto & sec_operand = inst.operands[1]; 187 | 188 | // mov reg, ??? 189 | if (first_operand.type != ZydisOperandType::ZYDIS_OPERAND_TYPE_REGISTER || 190 | !IsSameRegister(first_operand.reg.value, reg)) 191 | return false; 192 | 193 | if (sec_operand.type == ZydisOperandType::ZYDIS_OPERAND_TYPE_MEMORY) 194 | return true; 195 | 196 | return false; 197 | } 198 | bool 199 | Diassembler::DisassembleCode(const uint8_t * pRawData, const size_t rawDataSize, std::vector & result) 200 | { 201 | if (pRawData == nullptr || rawDataSize <= 0) 202 | return false; 203 | 204 | ZydisDecodedInstruction instruction = {}; 205 | ZydisDecoder decoder; 206 | ZyanStatus status = ZYAN_STATUS_SUCCESS; 207 | size_t offset = 0; 208 | ZyanU64 instruction_address = m_disassmblerOptions.BaseRuntimeAddr; 209 | 210 | if (not ZYAN_SUCCESS(ZydisDecoderInit(&decoder, 211 | m_disassmblerOptions.MachineMode, 212 | m_disassmblerOptions.AddressWidth))) 213 | return false; 214 | 215 | while (offset < rawDataSize) 216 | { 217 | status = ZydisDecoderDecodeBuffer(&decoder, pRawData + offset, rawDataSize - offset, &instruction); 218 | if (ZYAN_SUCCESS(status)) 219 | { 220 | // printf("0x%016" PRIX64 "\n", instruction_address); 221 | 222 | result.push_back({instruction_address, instruction}); 223 | 224 | offset += instruction.length; 225 | instruction_address += instruction.length; 226 | } 227 | else 228 | { 229 | // printf("%" PRIXPTR "\n", pRawData[offset]); 230 | offset++; 231 | instruction_address++; 232 | } 233 | } 234 | 235 | return true; 236 | } 237 | } // namespace static_analyzer 238 | } // namespace driver_analyzer -------------------------------------------------------------------------------- /DriverAnalyzer/DriverAnalyzer.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 | 16.0 23 | Win32Proj 24 | {92cd85e9-5155-414c-b605-dffaa5375831} 25 | DriverAnalyzer 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)Build\bin\ 76 | $(SolutionDir)Build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ 77 | $(ProjectName)_$(PlatformShortName)d 78 | 79 | 80 | false 81 | $(SolutionDir)Build\bin\ 82 | $(SolutionDir)Build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ 83 | $(ProjectName)_$(PlatformShortName) 84 | 85 | 86 | true 87 | $(SolutionDir)Build\bin\ 88 | $(SolutionDir)Build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ 89 | $(ProjectName)_$(PlatformShortName)d 90 | 91 | 92 | false 93 | $(SolutionDir)Build\bin\ 94 | $(SolutionDir)Build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ 95 | $(ProjectName)_$(PlatformShortName) 96 | 97 | 98 | 99 | Level3 100 | true 101 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 102 | true 103 | $(ProjectDir)src;$(ProjectDir)third_party;%(AdditionalIncludeDirectories) 104 | stdcpp17 105 | Use 106 | pch.h 107 | MultiThreadedDebugDLL 108 | 109 | 110 | Console 111 | true 112 | $(ProjectDir)third_party;%(AdditionalLibraryDirectories) 113 | 114 | 115 | 116 | 117 | Level3 118 | true 119 | true 120 | true 121 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | $(ProjectDir)src;$(ProjectDir)third_party;%(AdditionalIncludeDirectories) 124 | stdcpp17 125 | Use 126 | pch.h 127 | 128 | 129 | Console 130 | true 131 | true 132 | true 133 | $(ProjectDir)third_party;%(AdditionalLibraryDirectories) 134 | 135 | 136 | 137 | 138 | Level3 139 | true 140 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | true 142 | $(ProjectDir)src;$(ProjectDir)third_party;%(AdditionalIncludeDirectories) 143 | stdcpp17 144 | Use 145 | pch.h 146 | MultiThreadedDebugDLL 147 | 148 | 149 | Console 150 | true 151 | $(ProjectDir)third_party;%(AdditionalLibraryDirectories) 152 | 153 | 154 | 155 | 156 | Level3 157 | true 158 | true 159 | true 160 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 161 | true 162 | $(ProjectDir)src;$(ProjectDir)third_party;%(AdditionalIncludeDirectories) 163 | stdcpp17 164 | Use 165 | pch.h 166 | 167 | 168 | Console 169 | true 170 | true 171 | true 172 | $(ProjectDir)third_party;%(AdditionalLibraryDirectories) 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | Create 181 | pch.h 182 | Create 183 | pch.h 184 | Create 185 | pch.h 186 | Create 187 | pch.h 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /DriverAnalyzer/src/main.cc: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "utils/utils.h" 3 | #include "analyzer/analyzer.h" 4 | 5 | namespace driver_analyzer { 6 | namespace options { 7 | struct CommandLineOptions 8 | { 9 | std::string input_directory_path; 10 | std::string backup_directory_path; 11 | std::string json_report_file_name; 12 | }; 13 | 14 | namespace detail { 15 | std::optional 16 | ParseCommandLine(int argc, char * argv[]) 17 | { 18 | cxxopts::Options options("Vulnerable Driver Scanner", ""); 19 | 20 | options.add_options()("i,input", "Path of directory that contains Driver files (*.sys)", cxxopts::value()); 21 | options.add_options()("o,output", "Full name of JSON report", cxxopts::value()); 22 | options.add_options()("b,backup", 23 | "Path of backup directory to have a copy of suspicious driver files", 24 | cxxopts::value()); 25 | 26 | auto cmd_result = options.parse(argc, argv); 27 | 28 | CommandLineOptions commandline_options = {}; 29 | 30 | try 31 | { 32 | commandline_options.input_directory_path = 33 | cmd_result["input"].as(); 34 | commandline_options.json_report_file_name = 35 | cmd_result["output"].as(); 36 | commandline_options.backup_directory_path = 37 | cmd_result["backup"].as(); 38 | 39 | return commandline_options; 40 | } 41 | catch (...) 42 | { 43 | std::cout << options.help(); 44 | return std::nullopt; 45 | } 46 | } 47 | 48 | } // namespace detail 49 | } // namespace options 50 | 51 | namespace program { 52 | 53 | static_analyzer::SuspiciousProperties suspicious_properties; 54 | 55 | enum class AnalyzeSeverity 56 | { 57 | kRestrict, 58 | kHigh, 59 | kMedium, 60 | kLow, 61 | 62 | kOnlyReadCr3, 63 | kOnlyWriteCr3, 64 | kReadWriteCr3, 65 | 66 | kOnlyReadCr4, 67 | kOnlyWriteCr4, 68 | kReadWriteCr4, 69 | 70 | kOnlyRdmsr, 71 | kOnlyWrmsr, 72 | kRdmsrWrmsr, 73 | kAll 74 | }; 75 | 76 | enum class FilterCriteria 77 | { 78 | kImportTable, 79 | kControlRegister3, 80 | kControlRegister4, 81 | kReadMsr, 82 | kWriteMsr, 83 | }; 84 | 85 | bool 86 | StringVectorContains(const std::vector & v, std::string entry) 87 | { 88 | return (std::find(v.begin(), v.end(), entry) != v.end()); 89 | } 90 | 91 | bool 92 | RestrictCheck(const data_types::SuspiciousDriver & driver_file) 93 | { 94 | bool status = false; 95 | 96 | // 97 | // Ioctl handler (IRP) 98 | // 99 | if (not StringVectorContains(driver_file.Imports, "IoCreateDevice") && 100 | not StringVectorContains(driver_file.Imports, "IoCreateSymbolicLink") && 101 | not StringVectorContains(driver_file.Imports, "IofCompleteRequest")) 102 | { 103 | status = false; 104 | return status; 105 | } 106 | 107 | // 108 | // We need read/write to cr3 for physical address translation 109 | // 110 | if (not driver_file.Cr3Score) 111 | { 112 | status = false; 113 | return status; 114 | } 115 | 116 | // 117 | // Memory management 118 | // 119 | if (StringVectorContains(driver_file.Imports, "MmMapIoSpace") || 120 | StringVectorContains(driver_file.Imports, "MmMapIoSpaceEx") || 121 | StringVectorContains(driver_file.Imports, "MmGetPhysicalAddress")) 122 | { 123 | status = true; 124 | } 125 | 126 | //for (const auto & section : driver_file.ImportantSections) 127 | //{ 128 | // for (const auto & cr3 : section.Cr3.Instructions) 129 | // { 130 | // // 131 | // // mov cr3 , ??? 132 | // // 133 | // if (cr3.Instruction.Instruction.operands[0].reg.value == ZYDIS_REGISTER_CR3) 134 | // } 135 | //} 136 | 137 | return status; 138 | } 139 | 140 | bool 141 | BackupFiles(const std::vector & important_driver_list, 142 | const std::string & backup_dir, 143 | AnalyzeSeverity severity) 144 | { 145 | if (backup_dir.empty()) 146 | return false; 147 | 148 | fs::path backup_dir_path(backup_dir); 149 | 150 | if (!fs::is_directory(backup_dir_path)) 151 | fs::create_directory(backup_dir_path); 152 | 153 | if (fs::is_directory(backup_dir_path)) 154 | { 155 | for (const auto & file : important_driver_list) 156 | { 157 | auto dest_path = backup_dir_path / file.FileInfo.Name; 158 | try 159 | { 160 | // 161 | // Only copy files with dangerous imported APIs and Control Register/MSR modifications 162 | // 163 | if (severity == AnalyzeSeverity::kRestrict) 164 | { 165 | if (RestrictCheck(file)) 166 | { 167 | printf("[+] Found [%s] \n", file.FileInfo.Name.c_str()); 168 | fs::copy_file(file.FileInfo.ImagePath, dest_path, fs::copy_options::update_existing); 169 | } 170 | } 171 | else if (severity == AnalyzeSeverity::kHigh) 172 | { 173 | if (file.Imports.size() >= 2 && file.Cr3Score && file.MsrScore) 174 | { 175 | fs::copy_file(file.FileInfo.ImagePath, dest_path, fs::copy_options::update_existing); 176 | } 177 | } 178 | else if (severity == AnalyzeSeverity::kMedium) 179 | { 180 | if (file.Imports.size() >= 2 && file.Cr3Score && file.MsrScore) 181 | { 182 | fs::copy_file(file.FileInfo.ImagePath, dest_path, fs::copy_options::update_existing); 183 | } 184 | } 185 | else if (severity == AnalyzeSeverity::kLow) 186 | { 187 | //if (file.ImportantImports.size() >= 2 && file.Cr3Score && file.MsrScore) 188 | { 189 | fs::copy_file(file.FileInfo.ImagePath, dest_path, fs::copy_options::update_existing); 190 | } 191 | } 192 | else if (severity == AnalyzeSeverity::kAll) 193 | { 194 | fs::copy_file(file.FileInfo.ImagePath, dest_path, fs::copy_options::update_existing); 195 | } 196 | } 197 | catch (fs::filesystem_error & e) 198 | { 199 | std::cout << "\nCopy File ERROR: " << e.what() << '\n'; 200 | continue; 201 | } 202 | } 203 | } 204 | 205 | return true; 206 | } 207 | 208 | class DirectoryScan 209 | { 210 | public: 211 | DirectoryScan(const std::string & directory_path) : 212 | directory_path_(directory_path) 213 | { 214 | } 215 | 216 | bool Scan() 217 | { 218 | if (fs::exists(directory_path_) == false || fs::is_directory(directory_path_) == false) 219 | return false; 220 | 221 | const auto normalized_path = fs::path(directory_path_).string() + "\\"; 222 | 223 | try 224 | { 225 | const auto dir_iterator = fs::recursive_directory_iterator( 226 | directory_path_, 227 | fs::directory_options::skip_permission_denied); 228 | 229 | for (const auto & file : dir_iterator) 230 | { 231 | try 232 | { 233 | if (fs::is_regular_file(file) && 234 | utils::str_tolower(file.path().extension().string()) == kKernelDriverFileExtension) 235 | { 236 | const auto file_path = file.path().string(); 237 | 238 | try 239 | { 240 | static_analyzer::Vulnerability vs(file_path, 241 | suspicious_properties); 242 | vs.AnalazeFile(); 243 | if (vs.IsSuspicious()) 244 | { 245 | suspicious_drivers_.push_back(vs.GetSuspiciousDriver()); 246 | } 247 | } 248 | catch (const LIEF::exception & err) 249 | { 250 | std::cerr << err.what() << std::endl; 251 | } 252 | 253 | std::cout << "[!] Analyzing file: " << file_path << "\n"; 254 | analyzed_file_count_++; 255 | } 256 | } 257 | catch (const std::exception & err) 258 | { 259 | std::wcerr << L"Error: " << err.what() << std::endl; 260 | continue; 261 | } 262 | } 263 | } 264 | catch (std::filesystem::filesystem_error & err) 265 | 266 | { 267 | std::wcerr << L"Error: " << err.what() << std::endl; 268 | return false; 269 | } 270 | 271 | return true; 272 | } 273 | 274 | std::uint32_t GetAnalyzedFileCount() const { return analyzed_file_count_; } 275 | 276 | std::vector GetSuspiciousDrivers() const 277 | { 278 | return suspicious_drivers_; 279 | } 280 | 281 | private: 282 | std::string directory_path_; 283 | std::vector suspicious_drivers_; 284 | std::uint32_t analyzed_file_count_ = 0; 285 | const char * kKernelDriverFileExtension = ".sys"; 286 | }; 287 | 288 | bool 289 | main(int argc, char * argv[]) 290 | { 291 | const auto parsed_options = options::detail::ParseCommandLine(argc, argv); 292 | if (parsed_options.has_value() == false) 293 | return false; 294 | 295 | options::CommandLineOptions commandline_options = parsed_options.value(); 296 | 297 | utils::Chrono chrono; 298 | chrono.Start(); 299 | 300 | // 301 | // TODO: read from config file 302 | // 303 | suspicious_properties.imported_functions = { 304 | 305 | // 306 | // Memory management 307 | // 308 | "MmMapIoSpace", 309 | "MmMapIoSpaceEx", 310 | "MmUnmapIoSpace", 311 | "MmMapLockedPages", 312 | "MmGetPhysicalAddress", 313 | "MmMapLockedPagesSpecifyCache", 314 | "MmMapLockedPagesWithReservedMapping", 315 | 316 | // 317 | // Ioctl / Create Device Name 318 | // 319 | "IoCreateDevice", 320 | "IoCreateSymbolicLink", 321 | "IofCompleteRequest", 322 | 323 | // 324 | // The function verifies that the sender of an IRP_MJ_DEVICE_CONTROL or IRP_MJ_FILE_SYSTEM_CONTROL 325 | // IRP has the specified access to the device object. 326 | // 327 | "IoValidateDeviceIoControlAccess", 328 | "WdmlibIoValidateDeviceIoControlAccess", 329 | 330 | }; 331 | 332 | // AnalyzeFilesInDirectory(commandline_options.input_directory_path); 333 | 334 | std::vector suspicious_drivers = {}; 335 | 336 | DirectoryScan directory_scan(commandline_options.input_directory_path); 337 | if (directory_scan.Scan()) 338 | { 339 | suspicious_drivers = directory_scan.GetSuspiciousDrivers(); 340 | // 341 | // Create Json report 342 | // 343 | { 344 | std::ofstream outStr(commandline_options.json_report_file_name); 345 | cereal::JSONOutputArchive archive(outStr); 346 | archive(CEREAL_NVP(suspicious_drivers)); 347 | } 348 | 349 | // 350 | // Copy all important/vaulnerable files to desired directory 351 | // 352 | 353 | if (commandline_options.backup_directory_path.empty() == false) 354 | { 355 | BackupFiles(suspicious_drivers, commandline_options.backup_directory_path, AnalyzeSeverity::kAll); 356 | } 357 | } 358 | 359 | std::cout << "\n---------------------------------------------------\n"; 360 | 361 | std::cout << "[!] Analyzed file count : " << directory_scan.GetAnalyzedFileCount() << std::endl; 362 | std::cout << "[!] Suspicious file count : " << suspicious_drivers.size() << std::endl; 363 | std::cout << "[!] Json report saved in : " << commandline_options.json_report_file_name << std::endl; 364 | std::cout << "[!] Backup path : " << commandline_options.backup_directory_path << std::endl; 365 | 366 | std::cout << "\n---------------------------------------------------\n"; 367 | 368 | chrono.Stop(); 369 | std::cout << "[!] Finished!\n" 370 | << "[!] Elapsed time: " << chrono.GetElapsedTime() << "s\n"; 371 | return true; 372 | } 373 | } // namespace program 374 | } // namespace driver_analyzer 375 | 376 | int 377 | main(int argc, char * argv[]) 378 | { 379 | driver_analyzer::program::main(argc, argv); 380 | } 381 | -------------------------------------------------------------------------------- /DriverAnalyzer/src/analyzer/analyzer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "data_types.h" 4 | #include "analyzer/disassembler.h" 5 | #include "analyzer/api/MmMapIoSpace.h" 6 | 7 | namespace driver_analyzer { 8 | 9 | namespace static_analyzer { 10 | 11 | struct SuspiciousProperties 12 | { 13 | std::vector imported_functions; 14 | }; 15 | 16 | class Vulnerability 17 | { 18 | public: 19 | Vulnerability(const std::string & pe_file_name, 20 | const SuspiciousProperties & suspicious_properties) : 21 | pe_file_name_(pe_file_name), 22 | suspicious_properties_(suspicious_properties) 23 | 24 | { 25 | try 26 | { 27 | m_peFile = LIEF::PE::Parser::parse(pe_file_name); 28 | } 29 | catch (const LIEF::exception & err) 30 | { 31 | std::cerr << err.what() << std::endl; 32 | } 33 | } 34 | 35 | bool AnalazeFile() 36 | { 37 | const auto imports = GetImportTable(); 38 | if (imports.has_value()) 39 | import_table_ = imports.value(); 40 | 41 | GetExecutableSectionDisassembly(ExecutableSectionDisassembly); 42 | 43 | is_analyzed_ = true; 44 | 45 | return is_analyzed_; 46 | } 47 | 48 | data_types::SuspiciousInstructions 49 | CheckForReadWriteOnMsr(const std::vector & instructions) 50 | { 51 | data_types::SuspiciousInstructions result = {}; 52 | 53 | for (const auto & inst : instructions) 54 | { 55 | if (IsInstructionMsrAccess(inst)) 56 | { 57 | data_types::Instruction_t entry; 58 | entry.Offset = inst.Address; 59 | entry.Instruction = inst; 60 | entry.FormattedInstruction = m_disassembler->FormatInstruction(inst); 61 | 62 | result.Instructions.push_back(entry); 63 | } 64 | } 65 | 66 | return result; 67 | } 68 | 69 | data_types::SuspiciousInstructions 70 | CheckForCr3(const std::vector & instructions) 71 | { 72 | data_types::SuspiciousInstructions result = {}; 73 | data_types::Instruction_t entry = {}; 74 | for (auto & instruction : instructions) 75 | { 76 | // intrinsic 77 | // __readcr3() mov rax, cr3 78 | // __writecr3() mov cr3, rcx 79 | // 80 | 81 | //if (instruction.mnemonic == ZYDIS_MNEMONIC_MOV)// && instruction.operand_count >= 2) 82 | { 83 | if (instruction.Instruction.operands[0].reg.value == ZYDIS_REGISTER_CR3 || 84 | instruction.Instruction.operands[1].reg.value == ZYDIS_REGISTER_CR3) 85 | { 86 | entry.Offset = instruction.Address; 87 | entry.Instruction = instruction; 88 | entry.FormattedInstruction = m_disassembler->FormatInstruction(instruction); 89 | 90 | result.Instructions.push_back(entry); 91 | } 92 | } 93 | } 94 | 95 | return result; 96 | } 97 | 98 | data_types::SuspiciousInstructions 99 | CheckForCr4(const std::vector & instructions) 100 | { 101 | data_types::SuspiciousInstructions result = {}; 102 | data_types::Instruction_t entry = {}; 103 | for (auto & instruction : instructions) 104 | { 105 | // 106 | // Can be used with bitwise instructions like BTS ... 107 | // 108 | 109 | //if (instruction.mnemonic == ZYDIS_MNEMONIC_MOV && instruction.operand_count >= 2) 110 | { 111 | if (instruction.Instruction.operands[0].reg.value == ZYDIS_REGISTER_CR4 || 112 | instruction.Instruction.operands[1].reg.value == ZYDIS_REGISTER_CR4) 113 | { 114 | entry.Offset = instruction.Address; 115 | entry.Instruction = instruction; 116 | entry.FormattedInstruction = m_disassembler->FormatInstruction(instruction); 117 | 118 | result.Instructions.push_back(entry); 119 | } 120 | } 121 | } 122 | 123 | return result; 124 | } 125 | 126 | data_types::SuspiciousInstructions 127 | CheckForGs(const std::vector & instructions) 128 | { 129 | data_types::SuspiciousInstructions result = {}; 130 | data_types::Instruction_t entry = {}; 131 | for (auto & instruction : instructions) 132 | { 133 | // 134 | // dt _KPRCB poi(gs:[0x20]) ProcessorState.SpecialRegisters.Cr3 135 | // 136 | // gs:20 points to _KPCRB and can be used to obtain system CR3 (PML4) base address. 137 | // 138 | 139 | //if (instruction.mnemonic == ZYDIS_MNEMONIC_MOV && instruction.operand_count >= 2) 140 | { 141 | if (instruction.Instruction.operands[0].reg.value == ZYDIS_REGISTER_GS || 142 | instruction.Instruction.operands[1].reg.value == ZYDIS_REGISTER_GS) 143 | { 144 | entry.Offset = instruction.Address; 145 | entry.Instruction = instruction; 146 | entry.FormattedInstruction = m_disassembler->FormatInstruction(instruction); 147 | 148 | result.Instructions.push_back(entry); 149 | } 150 | } 151 | } 152 | 153 | return std::move(result); 154 | } 155 | 156 | std::optional> GetImportTable() const 157 | { 158 | if (IsPeKernelDriver() == false) 159 | return std::nullopt; 160 | 161 | if (m_peFile->has_imports() == false) 162 | return std::nullopt; 163 | 164 | std::vector result = {}; 165 | for (const auto fn : m_peFile->imported_functions()) 166 | { 167 | result.push_back(fn.name()); 168 | } 169 | 170 | return result; 171 | } 172 | 173 | std::vector GetMatchedSuspiciousImports() 174 | { 175 | std::vector result = {}; 176 | for (const auto & import : import_table_) 177 | { 178 | if (std::find(suspicious_properties_.imported_functions.begin(), 179 | suspicious_properties_.imported_functions.end(), 180 | import) != suspicious_properties_.imported_functions.end()) 181 | { 182 | //std::clog << "Import " << import << "\n"; 183 | 184 | result.push_back(import); 185 | } 186 | } 187 | return result; 188 | } 189 | 190 | bool 191 | GetExecutableSectionDisassembly(std::vector & result) 192 | { 193 | if (!IsPeKernelDriver()) 194 | return false; 195 | 196 | for (const auto & section : m_peFile->sections()) 197 | { 198 | if (section.has_characteristic(LIEF::PE::SECTION_CHARACTERISTICS::IMAGE_SCN_MEM_EXECUTE)) 199 | { 200 | const std::vector section_raw_data = section.content(); 201 | if (section_raw_data.size() == 0) 202 | continue; 203 | 204 | data_types::disassemlber::DisassmblerOptions disassmbler_options = {}; 205 | 206 | auto image_base = m_peFile->optional_header().imagebase(); 207 | auto section_va = section.virtual_address(); 208 | 209 | //std::cout << "[+] Section name " << section.name() << " base va " << section.virtual_address() << " \n"; 210 | 211 | // 212 | // Base address for the disassembler 213 | // 214 | disassmbler_options.BaseRuntimeAddr = (image_base + section_va); 215 | 216 | if (m_peFile->header().machine() == LIEF::PE::MACHINE_TYPES::IMAGE_FILE_MACHINE_AMD64) 217 | { 218 | disassmbler_options.AddressWidth = ZYDIS_ADDRESS_WIDTH_64; 219 | disassmbler_options.MachineMode = ZYDIS_MACHINE_MODE_LONG_64; 220 | } 221 | else if (m_peFile->header().machine() == LIEF::PE::MACHINE_TYPES::IMAGE_FILE_MACHINE_I386) 222 | { 223 | disassmbler_options.AddressWidth = ZYDIS_ADDRESS_WIDTH_32; 224 | disassmbler_options.MachineMode = ZYDIS_MACHINE_MODE_LEGACY_32; 225 | } 226 | 227 | m_disassembler = std::make_unique(section_raw_data, disassmbler_options); 228 | m_disassembledCodes = m_disassembler->GetDisassembledCode(); 229 | 230 | data_types::analyzer::ExecutableSectionDisassembly item = {}; 231 | item.Name = section.name(); 232 | item.Instructions = m_disassembledCodes; 233 | 234 | result.push_back(item); 235 | } 236 | } 237 | 238 | return true; 239 | } 240 | 241 | std::vector GetSuspiciousSections() 242 | { 243 | std::vector result = {}; 244 | 245 | for (const auto & section : ExecutableSectionDisassembly) 246 | { 247 | data_types::SuspiciousSection section_info = {}; 248 | 249 | section_info.ApiCalls = GetSuspiciousApiCalls(section.Instructions); 250 | 251 | section_info.Name = section.Name; 252 | section_info.Cr3 = CheckForCr3(section.Instructions); 253 | section_info.Cr4 = CheckForCr4(section.Instructions); 254 | section_info.Msr = CheckForReadWriteOnMsr(section.Instructions); 255 | 256 | if (section_info.Cr3.Instructions.size() || 257 | section_info.Cr4.Instructions.size() || section_info.ApiCalls.size() || 258 | section_info.Msr.Instructions.size()) 259 | { 260 | result.push_back(std::move(section_info)); 261 | } 262 | } 263 | 264 | return result; 265 | } 266 | 267 | bool IsSuspicious() 268 | { 269 | if (is_analyzed_ == false) 270 | AnalazeFile(); 271 | 272 | const auto & suspicious_imports = GetMatchedSuspiciousImports(); 273 | const auto & suspicious_sections = GetSuspiciousSections(); 274 | data_types::SuspiciousDriver driver_score_ = {}; 275 | 276 | driver_score_.IatScore = suspicious_imports.size(); 277 | ImportTableRate = ((suspicious_imports.size() * 100) / suspicious_properties_.imported_functions.size()); 278 | //std::cout << "[*] Import table rate : " << ImportTableRate << "% \n"; 279 | 280 | std::for_each(suspicious_sections.begin(), suspicious_sections.end(), [&](const data_types::SuspiciousSection & section_info) { 281 | driver_score_.Cr3Score += section_info.Cr3.Instructions.size(); 282 | driver_score_.Cr4Score += section_info.Cr4.Instructions.size(); 283 | driver_score_.MsrScore += section_info.Msr.Instructions.size(); 284 | driver_score_.ApiCallScore += section_info.ApiCalls.size(); 285 | }); 286 | 287 | if (driver_score_.IatScore || 288 | driver_score_.Cr3Score || 289 | driver_score_.ApiCallScore || 290 | driver_score_.Cr4Score || driver_score_.Sections.size()) 291 | 292 | { 293 | // 294 | // TODO: Add more meaningful criteria 295 | // 296 | return true; 297 | } 298 | 299 | return false; 300 | } 301 | 302 | data_types::SuspiciousDriver GetSuspiciousDriver() 303 | { 304 | data_types::SuspiciousDriver result = {}; 305 | 306 | const auto & suspicious_imports = GetMatchedSuspiciousImports(); 307 | const auto & suspicious_sections = GetSuspiciousSections(); 308 | 309 | result.IatScore = suspicious_imports.size(); 310 | ImportTableRate = ((suspicious_imports.size() * 100) / suspicious_properties_.imported_functions.size()); 311 | //std::cout << "[*] Import table rate : " << ImportTableRate << "% \n"; 312 | 313 | std::for_each(suspicious_sections.begin(), suspicious_sections.end(), [&](const data_types::SuspiciousSection & section_info) { 314 | result.Cr3Score += section_info.Cr3.Instructions.size(); 315 | result.Cr4Score += section_info.Cr4.Instructions.size(); 316 | result.MsrScore += section_info.Msr.Instructions.size(); 317 | result.ApiCallScore += section_info.ApiCalls.size(); 318 | }); 319 | 320 | if (result.IatScore || 321 | result.Cr3Score || 322 | result.Cr4Score || 323 | result.ApiCallScore || 324 | result.MsrScore) 325 | 326 | { 327 | result.Imports = suspicious_imports; 328 | result.Sections = suspicious_sections; 329 | result.FileInfo = GetPeFileInfo(); 330 | } 331 | 332 | return result; 333 | } 334 | 335 | std::vector 336 | GetSuspiciousApiCalls(const std::vector & instructions) 337 | { 338 | data_types::SuspiciousInstructions result = {}; 339 | data_types::Instruction_t entry = {}; 340 | 341 | std::vector api_calls = {}; 342 | 343 | for (size_t inst_index = 0; inst_index < instructions.size(); inst_index++) 344 | { 345 | const auto & curr_inst = instructions[inst_index]; 346 | if (curr_inst.Instruction.mnemonic != ZydisMnemonic::ZYDIS_MNEMONIC_CALL) 347 | continue; 348 | 349 | const ZydisDecodedOperand & first_operand = curr_inst.Instruction.operands[0]; 350 | if (first_operand.type == ZydisOperandType::ZYDIS_OPERAND_TYPE_MEMORY && first_operand.mem.segment == ZydisRegister::ZYDIS_REGISTER_DS) 351 | { 352 | // 353 | // TODO: add more APIs 354 | // 355 | api::MmMapIoSpaceAnalazer mm_map_io(instructions, inst_index); 356 | 357 | ZyanU64 target_fun_addr = 0; 358 | ZydisCalcAbsoluteAddress(&curr_inst.Instruction, 359 | &first_operand, 360 | curr_inst.Address, 361 | &target_fun_addr); 362 | 363 | const auto image_base = m_peFile->optional_header().imagebase(); 364 | for (auto const & iat_fn : m_peFile->imported_functions()) 365 | { 366 | auto iat_func_addr = image_base + iat_fn.address(); 367 | if (iat_func_addr == target_fun_addr) 368 | { 369 | if (iat_fn.name() == mm_map_io.Name()) 370 | { 371 | mm_map_io.Analyze(iat_fn.name()); 372 | 373 | //std::cout << mm_map_io.to_string(); 374 | 375 | api_calls.push_back(mm_map_io.GetFunctionCall()); 376 | } 377 | } 378 | } 379 | 380 | entry.Offset = curr_inst.Address; 381 | entry.Instruction = curr_inst; 382 | entry.FormattedInstruction = m_disassembler->FormatInstruction(curr_inst); 383 | 384 | result.Instructions.push_back(entry); 385 | } 386 | } 387 | return api_calls; 388 | } 389 | 390 | private: 391 | inline bool IsInstructionMsrAccess(const data_types::disassemlber::DisInstruction & inst) const 392 | { 393 | return inst.Instruction.mnemonic == ZYDIS_MNEMONIC_RDMSR || 394 | inst.Instruction.mnemonic == ZYDIS_MNEMONIC_WRMSR; 395 | } 396 | 397 | inline bool IsInstructionGsAccess(const data_types::disassemlber::DisInstruction & inst) const 398 | { 399 | return inst.Instruction.operands[0].reg.value == ZYDIS_REGISTER_GS || 400 | inst.Instruction.operands[1].reg.value == ZYDIS_REGISTER_GS; 401 | } 402 | 403 | inline bool IsInstructionCr3Access(const data_types::disassemlber::DisInstruction & inst) const 404 | { 405 | return inst.Instruction.operands[0].reg.value == ZYDIS_REGISTER_CR3 || 406 | inst.Instruction.operands[1].reg.value == ZYDIS_REGISTER_CR3; 407 | } 408 | 409 | inline bool IsInstructionCr4Access(const data_types::disassemlber::DisInstruction & inst) const 410 | { 411 | return inst.Instruction.operands[0].reg.value == ZYDIS_REGISTER_CR4 || 412 | inst.Instruction.operands[1].reg.value == ZYDIS_REGISTER_CR4; 413 | } 414 | 415 | bool IsPeKernelDriver() const 416 | { 417 | return m_peFile->optional_header().subsystem() == LIEF::PE::SUBSYSTEM::IMAGE_SUBSYSTEM_NATIVE; 418 | } 419 | 420 | data_types::PeFileInfo GetPeFileInfo() 421 | { 422 | data_types::PeFileInfo result = {}; 423 | 424 | result.ImagePath = pe_file_name_; 425 | result.Name = utils::GetFileName(pe_file_name_); 426 | result.FileSize = utils::GeFormattedtFileSize(pe_file_name_); 427 | 428 | // if (m_peFile->has_signature()) 429 | //{ 430 | // auto sign = m_peFile->signature(); 431 | //} 432 | 433 | if (m_peFile->has_resources() and m_peFile->resources_manager().has_version()) 434 | { 435 | auto version = m_peFile->resources_manager().version(); 436 | for (auto const & item : version.string_file_info().langcode_items()) 437 | { 438 | data_types::PeFileVersionInfo versionInfo = {}; 439 | for (const auto /*std::pair*/ & p : item.items()) 440 | { 441 | auto key = LIEF::u16tou8(p.first); 442 | auto value = LIEF::u16tou8(p.second); 443 | 444 | if (key == "CompanyName") 445 | versionInfo.CompanyName = value; 446 | else if (key == "FileDescription") 447 | versionInfo.FileDescription = value; 448 | else if (key == "FileVersion") 449 | versionInfo.FileVersion = value; 450 | else if (key == "InternalName") 451 | versionInfo.InternalName = value; 452 | else if (key == "LegalCopyright") 453 | versionInfo.LegalCopyright = value; 454 | else if (key == "OriginalFileName") 455 | versionInfo.OriginalFileName = value; 456 | else if (key == "ProductName") 457 | versionInfo.ProductName = value; 458 | else if (key == "ProductVersion") 459 | versionInfo.ProductVersion = value; 460 | } 461 | 462 | result.Version = versionInfo; 463 | } 464 | } 465 | 466 | return result; 467 | } 468 | 469 | private: 470 | std::string pe_file_name_; 471 | SuspiciousProperties suspicious_properties_; 472 | 473 | std::shared_ptr m_peFile; 474 | std::unique_ptr m_disassembler; 475 | std::vector m_disassembledCodes; 476 | 477 | std::vector import_table_; 478 | std::vector ExecutableSectionDisassembly; 479 | 480 | uint64_t ImportTableRate = 0; 481 | uint64_t danger_import_count = 0; 482 | std::vector imprted_functions = {}; 483 | 484 | bool is_analyzed_ = false; 485 | }; 486 | 487 | } // namespace static_analyzer 488 | } // namespace driver_analyzer --------------------------------------------------------------------------------