├── .clang-format ├── .gitattributes ├── .gitignore ├── .gitmodules ├── DdiMon.sln ├── DdiMon ├── DdiMon.vcxproj ├── DdiMon.vcxproj.filters ├── ddi_mon.cpp ├── ddi_mon.h ├── shadow_hook.cpp └── shadow_hook.h ├── LICENSE ├── README.md └── clean.bat /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: true 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | BeforeCatch: false 32 | BeforeElse: false 33 | IndentBraces: false 34 | BreakBeforeBinaryOperators: None 35 | BreakBeforeBraces: Attach 36 | BreakBeforeTernaryOperators: true 37 | BreakConstructorInitializersBeforeComma: false 38 | ColumnLimit: 80 39 | CommentPragmas: '^ IWYU pragma:' 40 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 41 | ConstructorInitializerIndentWidth: 4 42 | ContinuationIndentWidth: 4 43 | Cpp11BracedListStyle: true 44 | DerivePointerAlignment: true 45 | DisableFormat: false 46 | ExperimentalAutoDetectBinPacking: false 47 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 48 | IncludeCategories: 49 | - Regex: '^<.*\.h>' 50 | Priority: 1 51 | - Regex: '^<.*' 52 | Priority: 2 53 | - Regex: '.*' 54 | Priority: 3 55 | IndentCaseLabels: true 56 | IndentWidth: 2 57 | IndentWrappedFunctionNames: false 58 | KeepEmptyLinesAtTheStartOfBlocks: false 59 | MacroBlockBegin: '' 60 | MacroBlockEnd: '' 61 | MaxEmptyLinesToKeep: 1 62 | NamespaceIndentation: None 63 | ObjCBlockIndentWidth: 2 64 | ObjCSpaceAfterProperty: false 65 | ObjCSpaceBeforeProtocolList: false 66 | PenaltyBreakBeforeFirstCallParameter: 1 67 | PenaltyBreakComment: 300 68 | PenaltyBreakFirstLessLess: 120 69 | PenaltyBreakString: 1000 70 | PenaltyExcessCharacter: 1000000 71 | PenaltyReturnTypeOnItsOwnLine: 200 72 | SortIncludes: false 73 | PointerAlignment: Left 74 | SpaceAfterCStyleCast: false 75 | SpaceBeforeAssignmentOperators: true 76 | SpaceBeforeParens: ControlStatements 77 | SpaceInEmptyParentheses: false 78 | SpacesBeforeTrailingComments: 2 79 | SpacesInAngles: false 80 | SpacesInContainerLiterals: true 81 | SpacesInCStyleCastParentheses: false 82 | SpacesInParentheses: false 83 | SpacesInSquareBrackets: false 84 | Standard: Auto 85 | TabWidth: 8 86 | UseTab: Never 87 | ... 88 | 89 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | *.VC.db 5 | *.VC.opendb 6 | 7 | # User-specific files 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 | [Dd]ebug_WDK/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | [Rr]elease_WDK/ 23 | x64/ 24 | x86/ 25 | build/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | 30 | # Visual Studio 2015 cache/options directory 31 | .vs/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | 115 | # MightyMoose 116 | *.mm.* 117 | AutoTest.Net/ 118 | 119 | # Web workbench (sass) 120 | .sass-cache/ 121 | 122 | # Installshield output folder 123 | [Ee]xpress/ 124 | 125 | # DocProject is a documentation generator add-in 126 | DocProject/buildhelp/ 127 | DocProject/Help/*.HxT 128 | DocProject/Help/*.HxC 129 | DocProject/Help/*.hhc 130 | DocProject/Help/*.hhk 131 | DocProject/Help/*.hhp 132 | DocProject/Help/Html2 133 | DocProject/Help/html 134 | 135 | # Click-Once directory 136 | publish/ 137 | 138 | # Publish Web Output 139 | *.[Pp]ublish.xml 140 | *.azurePubxml 141 | ## TODO: Comment the next line if you want to checkin your 142 | ## web deploy settings but do note that will include unencrypted 143 | ## passwords 144 | #*.pubxml 145 | 146 | *.publishproj 147 | 148 | # NuGet Packages 149 | *.nupkg 150 | # The packages folder can be ignored because of Package Restore 151 | **/packages/* 152 | # except build/, which is used as an MSBuild target. 153 | !**/packages/build/ 154 | # Uncomment if necessary however generally it will be regenerated when needed 155 | #!**/packages/repositories.config 156 | 157 | # Windows Azure Build Output 158 | csx/ 159 | *.build.csdef 160 | 161 | # Windows Store app package directory 162 | AppPackages/ 163 | 164 | # Visual Studio cache files 165 | # files ending in .cache can be ignored 166 | *.[Cc]ache 167 | # but keep track of directories ending in .cache 168 | !*.[Cc]ache/ 169 | 170 | # Others 171 | ClientBin/ 172 | [Ss]tyle[Cc]op.* 173 | ~$* 174 | *~ 175 | *.dbmdl 176 | *.dbproj.schemaview 177 | *.pfx 178 | *.publishsettings 179 | node_modules/ 180 | orleans.codegen.cs 181 | 182 | # RIA/Silverlight projects 183 | Generated_Code/ 184 | 185 | # Backup & report files from converting an old project file 186 | # to a newer Visual Studio version. Backup files are not needed, 187 | # because we have git ;-) 188 | _UpgradeReport_Files/ 189 | Backup*/ 190 | UpgradeLog*.XML 191 | UpgradeLog*.htm 192 | 193 | # SQL Server files 194 | *.mdf 195 | *.ldf 196 | 197 | # Business Intelligence projects 198 | *.rdl.data 199 | *.bim.layout 200 | *.bim_*.settings 201 | 202 | # Microsoft Fakes 203 | FakesAssemblies/ 204 | 205 | # Node.js Tools for Visual Studio 206 | .ntvs_analysis.dat 207 | 208 | # Visual Studio 6 build log 209 | *.plg 210 | 211 | # Visual Studio 6 workspace options file 212 | *.opt 213 | 214 | # LightSwitch generated files 215 | GeneratedArtifacts/ 216 | _Pvt_Extensions/ 217 | ModelManifest.xml 218 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "HyperPlatform"] 2 | path = HyperPlatform 3 | url = https://github.com/tandasat/HyperPlatform.git 4 | [submodule "capstone"] 5 | path = capstone 6 | url = https://github.com/tandasat/capstone.git 7 | -------------------------------------------------------------------------------- /DdiMon.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DdiMon", "DdiMon\DdiMon.vcxproj", "{B20D17DD-453E-4420-B691-4EB4B9AE3A15}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B} = {FE197816-EF84-4E8D-B29D-E0A6BA2B144B} 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3F4B87F6-9967-4C6F-B1A4-010B6C19ED8D}" 12 | ProjectSection(SolutionItems) = preProject 13 | .clang-format = .clang-format 14 | .gitattributes = .gitattributes 15 | .gitignore = .gitignore 16 | LICENSE = LICENSE 17 | README.md = README.md 18 | EndProjectSection 19 | EndProject 20 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "capstone_static_winkernel", "capstone\msvc\capstone_static_winkernel\capstone_static_winkernel.vcxproj", "{FE197816-EF84-4E8D-B29D-E0A6BA2B144B}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|x64 = Debug|x64 25 | Debug|x86 = Debug|x86 26 | Release|x64 = Release|x64 27 | Release|x86 = Release|x86 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Debug|x64.ActiveCfg = Debug|x64 31 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Debug|x64.Build.0 = Debug|x64 32 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Debug|x64.Deploy.0 = Debug|x64 33 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Debug|x86.ActiveCfg = Debug|Win32 34 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Debug|x86.Build.0 = Debug|Win32 35 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Debug|x86.Deploy.0 = Debug|Win32 36 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Release|x64.ActiveCfg = Release|x64 37 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Release|x64.Build.0 = Release|x64 38 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Release|x64.Deploy.0 = Release|x64 39 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Release|x86.ActiveCfg = Release|Win32 40 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Release|x86.Build.0 = Release|Win32 41 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15}.Release|x86.Deploy.0 = Release|Win32 42 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Debug|x64.ActiveCfg = Debug|x64 43 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Debug|x64.Build.0 = Debug|x64 44 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Debug|x64.Deploy.0 = Debug|x64 45 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Debug|x86.ActiveCfg = Debug|Win32 46 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Debug|x86.Build.0 = Debug|Win32 47 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Debug|x86.Deploy.0 = Debug|Win32 48 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Release|x64.ActiveCfg = Release|x64 49 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Release|x64.Build.0 = Release|x64 50 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Release|x64.Deploy.0 = Release|x64 51 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Release|x86.ActiveCfg = Release|Win32 52 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Release|x86.Build.0 = Release|Win32 53 | {FE197816-EF84-4E8D-B29D-E0A6BA2B144B}.Release|x86.Deploy.0 = Release|Win32 54 | EndGlobalSection 55 | GlobalSection(SolutionProperties) = preSolution 56 | HideSolutionNode = FALSE 57 | EndGlobalSection 58 | EndGlobal 59 | -------------------------------------------------------------------------------- /DdiMon/DdiMon.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 | {B20D17DD-453E-4420-B691-4EB4B9AE3A15} 23 | {497e31cb-056b-4f31-abb8-447fd55ee5a5} 24 | v4.5 25 | 12.0 26 | Debug 27 | Win32 28 | DdiMon 29 | 30 | 31 | 32 | Windows7 33 | true 34 | Driver 35 | KMDF 36 | Desktop 37 | WindowsKernelModeDriver10.0 38 | 39 | 40 | Windows7 41 | false 42 | Driver 43 | KMDF 44 | Desktop 45 | WindowsKernelModeDriver10.0 46 | 47 | 48 | Windows7 49 | true 50 | Driver 51 | KMDF 52 | Desktop 53 | WindowsKernelModeDriver10.0 54 | 55 | 56 | Windows7 57 | false 58 | Driver 59 | KMDF 60 | Desktop 61 | WindowsKernelModeDriver10.0 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | DbgengKernelDebugger 73 | $(VC_IncludePath);$(IncludePath) 74 | false 75 | 76 | 77 | DbgengKernelDebugger 78 | $(VC_IncludePath);$(IncludePath) 79 | false 80 | 81 | 82 | DbgengKernelDebugger 83 | $(VC_IncludePath);$(IncludePath) 84 | false 85 | 86 | 87 | DbgengKernelDebugger 88 | $(VC_IncludePath);$(IncludePath) 89 | false 90 | 91 | 92 | 93 | true 94 | trace.h 95 | true 96 | $(SolutionDir)capstone\include;$(IntDir);%(AdditionalIncludeDirectories) 97 | false 98 | true 99 | stdcpp17 100 | 101 | 102 | $(OutDir)capstone_static_winkernel.lib;ntstrsafe.lib;%(AdditionalDependencies) 103 | 104 | 105 | SHA256 106 | 107 | 108 | 109 | 110 | true 111 | trace.h 112 | true 113 | $(SolutionDir)capstone\include;$(IntDir);%(AdditionalIncludeDirectories) 114 | true 115 | stdcpp17 116 | 117 | 118 | $(OutDir)capstone_static_winkernel.lib;%(AdditionalDependencies) 119 | 120 | 121 | SHA256 122 | 123 | 124 | 125 | 126 | true 127 | trace.h 128 | true 129 | $(SolutionDir)capstone\include;$(IntDir);%(AdditionalIncludeDirectories) 130 | false 131 | true 132 | stdcpp17 133 | 134 | 135 | $(OutDir)capstone_static_winkernel.lib;ntstrsafe.lib;%(AdditionalDependencies) 136 | 137 | 138 | SHA256 139 | 140 | 141 | 142 | 143 | true 144 | trace.h 145 | true 146 | $(SolutionDir)capstone\include;$(IntDir);%(AdditionalIncludeDirectories) 147 | true 148 | stdcpp17 149 | 150 | 151 | $(OutDir)capstone_static_winkernel.lib;ntstrsafe.lib;%(AdditionalDependencies) 152 | 153 | 154 | SHA256 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | true 196 | true 197 | 198 | 199 | true 200 | true 201 | true 202 | true 203 | true 204 | true 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /DdiMon/DdiMon.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;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 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Source Files 24 | 25 | 26 | Source Files 27 | 28 | 29 | Source Files 30 | 31 | 32 | Source Files 33 | 34 | 35 | Source Files 36 | 37 | 38 | Source Files 39 | 40 | 41 | Source Files 42 | 43 | 44 | Source Files 45 | 46 | 47 | Source Files 48 | 49 | 50 | Source Files 51 | 52 | 53 | Source Files 54 | 55 | 56 | Source Files 57 | 58 | 59 | Source Files 60 | 61 | 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | Header Files 71 | 72 | 73 | Header Files 74 | 75 | 76 | Header Files 77 | 78 | 79 | Header Files 80 | 81 | 82 | Header Files 83 | 84 | 85 | Header Files 86 | 87 | 88 | Header Files 89 | 90 | 91 | Header Files 92 | 93 | 94 | Header Files 95 | 96 | 97 | Header Files 98 | 99 | 100 | Header Files 101 | 102 | 103 | Header Files 104 | 105 | 106 | Header Files 107 | 108 | 109 | Header Files 110 | 111 | 112 | 113 | 114 | Source Files 115 | 116 | 117 | Source Files 118 | 119 | 120 | -------------------------------------------------------------------------------- /DdiMon/ddi_mon.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2018, Satoshi Tanda. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /// @file 6 | /// Implements DdiMon functions. 7 | 8 | #include "ddi_mon.h" 9 | #include 10 | #define NTSTRSAFE_NO_CB_FUNCTIONS 11 | #include 12 | #include "../HyperPlatform/HyperPlatform/common.h" 13 | #include "../HyperPlatform/HyperPlatform/log.h" 14 | #include "../HyperPlatform/HyperPlatform/util.h" 15 | #include "../HyperPlatform/HyperPlatform/ept.h" 16 | #undef _HAS_EXCEPTIONS 17 | #define _HAS_EXCEPTIONS 0 18 | #include 19 | #include "shadow_hook.h" 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // 23 | // macro utilities 24 | // 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | // 28 | // constants and macros 29 | // 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | // 33 | // types 34 | // 35 | 36 | // A helper type for parsing a PoolTag value 37 | union PoolTag { 38 | ULONG value; 39 | UCHAR chars[4]; 40 | }; 41 | 42 | // A callback type for EnumExportedSymbols() 43 | using EnumExportedSymbolsCallbackType = bool (*)( 44 | ULONG index, ULONG_PTR base_address, PIMAGE_EXPORT_DIRECTORY directory, 45 | ULONG_PTR directory_base, ULONG_PTR directory_end, void* context); 46 | 47 | // For SystemProcessInformation 48 | enum SystemInformationClass { 49 | kSystemProcessInformation = 5, 50 | }; 51 | 52 | // For NtQuerySystemInformation 53 | struct SystemProcessInformation { 54 | ULONG next_entry_offset; 55 | ULONG number_of_threads; 56 | LARGE_INTEGER working_set_private_size; 57 | ULONG hard_fault_count; 58 | ULONG number_of_threads_high_watermark; 59 | ULONG64 cycle_time; 60 | LARGE_INTEGER create_time; 61 | LARGE_INTEGER user_time; 62 | LARGE_INTEGER kernel_time; 63 | UNICODE_STRING image_name; 64 | // omitted. see ole32!_SYSTEM_PROCESS_INFORMATION 65 | }; 66 | 67 | //////////////////////////////////////////////////////////////////////////////// 68 | // 69 | // prototypes 70 | // 71 | 72 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C 73 | static void DdimonpFreeAllocatedTrampolineRegions(); 74 | 75 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C static NTSTATUS 76 | DdimonpEnumExportedSymbols(_In_ ULONG_PTR base_address, 77 | _In_ EnumExportedSymbolsCallbackType callback, 78 | _In_opt_ void* context); 79 | 80 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C 81 | static bool DdimonpEnumExportedSymbolsCallback( 82 | _In_ ULONG index, _In_ ULONG_PTR base_address, 83 | _In_ PIMAGE_EXPORT_DIRECTORY directory, _In_ ULONG_PTR directory_base, 84 | _In_ ULONG_PTR directory_end, _In_opt_ void* context); 85 | 86 | static std::array DdimonpTagToString(_In_ ULONG tag_value); 87 | 88 | template 89 | static T DdimonpFindOrignal(_In_ T handler); 90 | 91 | static VOID DdimonpHandleExQueueWorkItem(_Inout_ PWORK_QUEUE_ITEM work_item, 92 | _In_ WORK_QUEUE_TYPE queue_type); 93 | 94 | static PVOID DdimonpHandleExAllocatePoolWithTag(_In_ POOL_TYPE pool_type, 95 | _In_ SIZE_T number_of_bytes, 96 | _In_ ULONG tag); 97 | 98 | static VOID DdimonpHandleExFreePool(_Pre_notnull_ PVOID p); 99 | 100 | static VOID DdimonpHandleExFreePoolWithTag(_Pre_notnull_ PVOID p, 101 | _In_ ULONG tag); 102 | 103 | static NTSTATUS DdimonpHandleNtQuerySystemInformation( 104 | _In_ SystemInformationClass SystemInformationClass, 105 | _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, 106 | _Out_opt_ PULONG ReturnLength); 107 | 108 | #if defined(ALLOC_PRAGMA) 109 | #pragma alloc_text(PAGE, DdimonInitialization) 110 | #pragma alloc_text(PAGE, DdimonpEnumExportedSymbols) 111 | #pragma alloc_text(PAGE, DdimonpEnumExportedSymbolsCallback) 112 | #pragma alloc_text(PAGE, DdimonTermination) 113 | #pragma alloc_text(PAGE, DdimonpFreeAllocatedTrampolineRegions) 114 | #endif 115 | 116 | //////////////////////////////////////////////////////////////////////////////// 117 | // 118 | // variables 119 | // 120 | 121 | // Defines where to install shadow hooks and their handlers 122 | // 123 | // Because of simplified implementation of DdiMon, DdiMon is unable to handle 124 | // any of following exports properly: 125 | // - already unmapped exports (eg, ones on the INIT section) because it no 126 | // longer exists on memory 127 | // - exported data because setting 0xcc does not make any sense in this case 128 | // - functions does not comply x64 calling conventions, for example Zw* 129 | // functions. Because contents of stack do not hold expected values leading 130 | // handlers to failure of parameter analysis that may result in bug check. 131 | // 132 | // Also the following care should be taken: 133 | // - Function parameters may be an user-address space pointer and not 134 | // trusted. Even a kernel-address space pointer should not be trusted for 135 | // production level security. Verity and capture all contents from user 136 | // supplied address to VMM, then use them. 137 | static ShadowHookTarget g_ddimonp_hook_targets[] = { 138 | { 139 | RTL_CONSTANT_STRING(L"EXQUEUEWORKITEM"), 140 | DdimonpHandleExQueueWorkItem, 141 | nullptr, 142 | }, 143 | { 144 | RTL_CONSTANT_STRING(L"EXALLOCATEPOOLWITHTAG"), 145 | DdimonpHandleExAllocatePoolWithTag, 146 | nullptr, 147 | }, 148 | { 149 | RTL_CONSTANT_STRING(L"EXFREEPOOL"), 150 | DdimonpHandleExFreePool, 151 | nullptr, 152 | }, 153 | { 154 | RTL_CONSTANT_STRING(L"EXFREEPOOLWITHTAG"), 155 | DdimonpHandleExFreePoolWithTag, 156 | nullptr, 157 | }, 158 | { 159 | RTL_CONSTANT_STRING(L"NTQUERYSYSTEMINFORMATION"), 160 | DdimonpHandleNtQuerySystemInformation, 161 | nullptr, 162 | }, 163 | }; 164 | 165 | //////////////////////////////////////////////////////////////////////////////// 166 | // 167 | // implementations 168 | // 169 | 170 | // Initializes DdiMon 171 | _Use_decl_annotations_ EXTERN_C NTSTATUS 172 | DdimonInitialization(SharedShadowHookData* shared_sh_data) { 173 | PAGED_CODE(); 174 | 175 | // Get a base address of ntoskrnl 176 | auto nt_base = UtilPcToFileHeader(KdDebuggerEnabled); 177 | if (!nt_base) { 178 | return STATUS_UNSUCCESSFUL; 179 | } 180 | 181 | // Install hooks by enumerating exports of ntoskrnl, but not activate them yet 182 | auto status = DdimonpEnumExportedSymbols(reinterpret_cast(nt_base), 183 | DdimonpEnumExportedSymbolsCallback, 184 | shared_sh_data); 185 | if (!NT_SUCCESS(status)) { 186 | return status; 187 | } 188 | 189 | // Activate installed hooks 190 | status = ShEnableHooks(); 191 | if (!NT_SUCCESS(status)) { 192 | DdimonpFreeAllocatedTrampolineRegions(); 193 | return status; 194 | } 195 | 196 | HYPERPLATFORM_LOG_INFO("DdiMon has been initialized."); 197 | return status; 198 | } 199 | 200 | // Terminates DdiMon 201 | _Use_decl_annotations_ EXTERN_C void DdimonTermination() { 202 | PAGED_CODE(); 203 | 204 | ShDisableHooks(); 205 | UtilSleep(1000); 206 | DdimonpFreeAllocatedTrampolineRegions(); 207 | HYPERPLATFORM_LOG_INFO("DdiMon has been terminated."); 208 | } 209 | 210 | // Frees trampoline code allocated and stored in g_ddimonp_hook_targets by 211 | // DdimonpEnumExportedSymbolsCallback() 212 | _Use_decl_annotations_ EXTERN_C static void 213 | DdimonpFreeAllocatedTrampolineRegions() { 214 | PAGED_CODE(); 215 | 216 | for (auto& target : g_ddimonp_hook_targets) { 217 | if (target.original_call) { 218 | ExFreePoolWithTag(target.original_call, kHyperPlatformCommonPoolTag); 219 | target.original_call = nullptr; 220 | } 221 | } 222 | } 223 | 224 | // Enumerates all exports in a module specified by base_address. 225 | _Use_decl_annotations_ EXTERN_C static NTSTATUS DdimonpEnumExportedSymbols( 226 | ULONG_PTR base_address, EnumExportedSymbolsCallbackType callback, 227 | void* context) { 228 | PAGED_CODE(); 229 | 230 | auto dos = reinterpret_cast(base_address); 231 | auto nt = reinterpret_cast(base_address + dos->e_lfanew); 232 | auto dir = reinterpret_cast( 233 | &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]); 234 | if (!dir->Size || !dir->VirtualAddress) { 235 | return STATUS_SUCCESS; 236 | } 237 | 238 | auto dir_base = base_address + dir->VirtualAddress; 239 | auto dir_end = base_address + dir->VirtualAddress + dir->Size - 1; 240 | auto exp_dir = reinterpret_cast(base_address + 241 | dir->VirtualAddress); 242 | for (auto i = 0ul; i < exp_dir->NumberOfNames; i++) { 243 | if (!callback(i, base_address, exp_dir, dir_base, dir_end, context)) { 244 | return STATUS_SUCCESS; 245 | } 246 | } 247 | return STATUS_SUCCESS; 248 | } 249 | 250 | // Checks if the export is listed as a hook target, and if so install a hook. 251 | _Use_decl_annotations_ EXTERN_C static bool DdimonpEnumExportedSymbolsCallback( 252 | ULONG index, ULONG_PTR base_address, PIMAGE_EXPORT_DIRECTORY directory, 253 | ULONG_PTR directory_base, ULONG_PTR directory_end, void* context) { 254 | PAGED_CODE(); 255 | 256 | if (!context) { 257 | return false; 258 | } 259 | 260 | auto functions = 261 | reinterpret_cast(base_address + directory->AddressOfFunctions); 262 | auto ordinals = reinterpret_cast(base_address + 263 | directory->AddressOfNameOrdinals); 264 | auto names = 265 | reinterpret_cast(base_address + directory->AddressOfNames); 266 | 267 | auto ord = ordinals[index]; 268 | auto export_address = base_address + functions[ord]; 269 | auto export_name = reinterpret_cast(base_address + names[index]); 270 | 271 | // Check if an export is forwarded one? If so, ignore it. 272 | if (UtilIsInBounds(export_address, directory_base, directory_end)) { 273 | return true; 274 | } 275 | 276 | // convert the name to UNICODE_STRING 277 | wchar_t name[100]; 278 | auto status = 279 | RtlStringCchPrintfW(name, RTL_NUMBER_OF(name), L"%S", export_name); 280 | if (!NT_SUCCESS(status)) { 281 | return true; 282 | } 283 | UNICODE_STRING name_u = {}; 284 | RtlInitUnicodeString(&name_u, name); 285 | 286 | for (auto& target : g_ddimonp_hook_targets) { 287 | // Is this export listed as a target 288 | if (!FsRtlIsNameInExpression(&target.target_name, &name_u, TRUE, nullptr)) { 289 | continue; 290 | } 291 | 292 | // Yes, install a hook to the export 293 | if (!ShInstallHook(reinterpret_cast(context), 294 | reinterpret_cast(export_address), &target)) { 295 | // This is an error which should not happen 296 | DdimonpFreeAllocatedTrampolineRegions(); 297 | return false; 298 | } 299 | HYPERPLATFORM_LOG_INFO("Hook has been installed at %016Ix %s.", 300 | export_address, export_name); 301 | } 302 | return true; 303 | } 304 | 305 | // Converts a pool tag in integer to a printable string 306 | _Use_decl_annotations_ static std::array DdimonpTagToString( 307 | ULONG tag_value) { 308 | PoolTag tag = {tag_value}; 309 | for (auto& c : tag.chars) { 310 | if (!c && isspace(c)) { 311 | c = ' '; 312 | } 313 | if (!isprint(c)) { 314 | c = '.'; 315 | } 316 | } 317 | 318 | std::array str; 319 | auto status = 320 | RtlStringCchPrintfA(str.data(), str.size(), "%c%c%c%c", tag.chars[0], 321 | tag.chars[1], tag.chars[2], tag.chars[3]); 322 | NT_VERIFY(NT_SUCCESS(status)); 323 | return str; 324 | } 325 | 326 | // Finds a handler to call an original function 327 | template 328 | static T DdimonpFindOrignal(T handler) { 329 | for (const auto& target : g_ddimonp_hook_targets) { 330 | if (target.handler == handler) { 331 | NT_ASSERT(target.original_call); 332 | return reinterpret_cast(target.original_call); 333 | } 334 | } 335 | NT_ASSERT(false); 336 | return nullptr; 337 | } 338 | 339 | // The hook handler for ExFreePool(). Logs if ExFreePool() is called from where 340 | // not backed by any image 341 | _Use_decl_annotations_ static VOID DdimonpHandleExFreePool(PVOID p) { 342 | const auto original = DdimonpFindOrignal(DdimonpHandleExFreePool); 343 | original(p); 344 | 345 | // Is inside image? 346 | auto return_addr = _ReturnAddress(); 347 | if (UtilPcToFileHeader(return_addr)) { 348 | return; 349 | } 350 | 351 | HYPERPLATFORM_LOG_INFO_SAFE("%p: ExFreePool(P= %p)", return_addr, p); 352 | } 353 | 354 | // The hook handler for ExFreePoolWithTag(). Logs if ExFreePoolWithTag() is 355 | // called from where not backed by any image. 356 | _Use_decl_annotations_ static VOID DdimonpHandleExFreePoolWithTag(PVOID p, 357 | ULONG tag) { 358 | const auto original = DdimonpFindOrignal(DdimonpHandleExFreePoolWithTag); 359 | original(p, tag); 360 | 361 | // Is inside image? 362 | auto return_addr = _ReturnAddress(); 363 | if (UtilPcToFileHeader(return_addr)) { 364 | return; 365 | } 366 | 367 | HYPERPLATFORM_LOG_INFO_SAFE("%p: ExFreePoolWithTag(P= %p, Tag= %s)", 368 | return_addr, p, DdimonpTagToString(tag).data()); 369 | } 370 | 371 | // The hook handler for ExQueueWorkItem(). Logs if a WorkerRoutine points to 372 | // where not backed by any image. 373 | _Use_decl_annotations_ static VOID DdimonpHandleExQueueWorkItem( 374 | PWORK_QUEUE_ITEM work_item, WORK_QUEUE_TYPE queue_type) { 375 | const auto original = DdimonpFindOrignal(DdimonpHandleExQueueWorkItem); 376 | 377 | // Is inside image? 378 | if (UtilPcToFileHeader(work_item->WorkerRoutine)) { 379 | // Call an original after checking parameters. It is common that a work 380 | // routine frees a work_item object resulting in wrong analysis. 381 | original(work_item, queue_type); 382 | return; 383 | } 384 | 385 | auto return_addr = _ReturnAddress(); 386 | HYPERPLATFORM_LOG_INFO_SAFE( 387 | "%p: ExQueueWorkItem({Routine= %p, Parameter= %p}, %d)", return_addr, 388 | work_item->WorkerRoutine, work_item->Parameter, queue_type); 389 | 390 | original(work_item, queue_type); 391 | } 392 | 393 | // The hook handler for ExAllocatePoolWithTag(). Logs if ExAllocatePoolWithTag() 394 | // is called from where not backed by any image. 395 | _Use_decl_annotations_ static PVOID DdimonpHandleExAllocatePoolWithTag( 396 | POOL_TYPE pool_type, SIZE_T number_of_bytes, ULONG tag) { 397 | const auto original = DdimonpFindOrignal(DdimonpHandleExAllocatePoolWithTag); 398 | const auto result = original(pool_type, number_of_bytes, tag); 399 | 400 | // Is inside image? 401 | auto return_addr = _ReturnAddress(); 402 | if (UtilPcToFileHeader(return_addr)) { 403 | return result; 404 | } 405 | 406 | HYPERPLATFORM_LOG_INFO_SAFE( 407 | "%p: ExAllocatePoolWithTag(POOL_TYPE= %08x, NumberOfBytes= %08Ix, Tag= " 408 | "%s) => %p", 409 | return_addr, pool_type, number_of_bytes, DdimonpTagToString(tag).data(), 410 | result); 411 | return result; 412 | } 413 | 414 | // The hook handler for NtQuerySystemInformation(). Removes an entry for cmd.exe 415 | // and hides it from being listed. 416 | _Use_decl_annotations_ static NTSTATUS DdimonpHandleNtQuerySystemInformation( 417 | SystemInformationClass system_information_class, PVOID system_information, 418 | ULONG system_information_length, PULONG return_length) { 419 | const auto original = 420 | DdimonpFindOrignal(DdimonpHandleNtQuerySystemInformation); 421 | const auto result = original(system_information_class, system_information, 422 | system_information_length, return_length); 423 | if (!NT_SUCCESS(result)) { 424 | return result; 425 | } 426 | if (system_information_class != kSystemProcessInformation) { 427 | return result; 428 | } 429 | 430 | auto next = reinterpret_cast(system_information); 431 | while (next->next_entry_offset) { 432 | auto curr = next; 433 | next = reinterpret_cast( 434 | reinterpret_cast(curr) + curr->next_entry_offset); 435 | if (_wcsnicmp(next->image_name.Buffer, L"cmd.exe", 7) == 0) { 436 | if (next->next_entry_offset) { 437 | curr->next_entry_offset += next->next_entry_offset; 438 | } else { 439 | curr->next_entry_offset = 0; 440 | } 441 | next = curr; 442 | } 443 | } 444 | return result; 445 | } 446 | -------------------------------------------------------------------------------- /DdiMon/ddi_mon.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2018, Satoshi Tanda. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /// @file 6 | /// @brief Declares interfaces to DdiMon functions. 7 | 8 | #ifndef DDIMON_DDI_MON_H_ 9 | #define DDIMON_DDI_MON_H_ 10 | 11 | #include 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | // 15 | // macro utilities 16 | // 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // 20 | // constants and macros 21 | // 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // 25 | // types 26 | // 27 | 28 | struct SharedShadowHookData; 29 | 30 | //////////////////////////////////////////////////////////////////////////////// 31 | // 32 | // prototypes 33 | // 34 | 35 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C NTSTATUS 36 | DdimonInitialization(_In_ SharedShadowHookData* shared_sh_data); 37 | 38 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C void DdimonTermination(); 39 | 40 | //////////////////////////////////////////////////////////////////////////////// 41 | // 42 | // variables 43 | // 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | // 47 | // implementations 48 | // 49 | 50 | #endif // DDIMON_DDI_MON_H_ 51 | -------------------------------------------------------------------------------- /DdiMon/shadow_hook.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2018, Satoshi Tanda. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /// @file 6 | /// Implements shadow hook functions. 7 | 8 | #include "shadow_hook.h" 9 | #include 10 | #define NTSTRSAFE_NO_CB_FUNCTIONS 11 | #include 12 | #include "../HyperPlatform/HyperPlatform/common.h" 13 | #include "../HyperPlatform/HyperPlatform/log.h" 14 | #include "../HyperPlatform/HyperPlatform/util.h" 15 | #include "../HyperPlatform/HyperPlatform/ept.h" 16 | #undef _HAS_EXCEPTIONS 17 | #define _HAS_EXCEPTIONS 0 18 | #include 19 | #include 20 | #include 21 | #include "capstone.h" 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // 25 | // macro utilities 26 | // 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | // 30 | // constants and macros 31 | // 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | // 35 | // types 36 | // 37 | 38 | // Copy of a page seen by a guest as a result of memory shadowing 39 | struct Page { 40 | UCHAR* page; // A page aligned copy of a page 41 | Page(); 42 | ~Page(); 43 | }; 44 | 45 | // Contains a single steal hook information 46 | struct HookInformation { 47 | void* patch_address; // An address where a hook is installed 48 | void* handler; // An address of the handler routine 49 | 50 | // A copy of a pages where patch_address belongs to. shadow_page_base_for_rw 51 | // is exposed to a guest for read and write operation against the page of 52 | // patch_address, and shadow_page_base_for_exec is exposed for execution. 53 | std::shared_ptr shadow_page_base_for_rw; 54 | std::shared_ptr shadow_page_base_for_exec; 55 | 56 | // Physical address of the above two copied pages 57 | ULONG64 pa_base_for_rw; 58 | ULONG64 pa_base_for_exec; 59 | }; 60 | 61 | // Data structure shared across all processors 62 | struct SharedShadowHookData { 63 | std::vector> hooks; // Hold installed hooks 64 | }; 65 | 66 | // Data structure for each processor 67 | struct ShadowHookData { 68 | const HookInformation* last_hook_info; // Remember which hook hit the last 69 | }; 70 | 71 | // A structure reflects inline hook code. 72 | #include 73 | #if defined(_AMD64_) 74 | 75 | struct TrampolineCode { 76 | UCHAR nop; 77 | UCHAR jmp[6]; 78 | void* address; 79 | }; 80 | static_assert(sizeof(TrampolineCode) == 15, "Size check"); 81 | 82 | #else 83 | 84 | struct TrampolineCode { 85 | UCHAR nop; 86 | UCHAR push; 87 | void* address; 88 | UCHAR ret; 89 | }; 90 | static_assert(sizeof(TrampolineCode) == 7, "Size check"); 91 | 92 | #endif 93 | #include 94 | 95 | //////////////////////////////////////////////////////////////////////////////// 96 | // 97 | // prototypes 98 | // 99 | 100 | _IRQL_requires_max_(PASSIVE_LEVEL) static std::unique_ptr< 101 | HookInformation> ShpCreateHookInformation(_In_ SharedShadowHookData* 102 | shared_sh_data, 103 | _In_ void* address, 104 | _In_ ShadowHookTarget* target); 105 | 106 | _IRQL_requires_max_(PASSIVE_LEVEL) _Success_(return ) EXTERN_C 107 | static bool ShpSetupInlineHook(_In_ void* patch_address, 108 | _In_ UCHAR* shadow_exec_page, 109 | _Out_ void** original_call_ptr); 110 | 111 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C static SIZE_T 112 | ShpGetInstructionSize(_In_ void* address); 113 | 114 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C static TrampolineCode 115 | ShpMakeTrampolineCode(_In_ void* hook_handler); 116 | 117 | static HookInformation* ShpFindPatchInfoByPage( 118 | _In_ const SharedShadowHookData* shared_sh_data, _In_opt_ void* address); 119 | 120 | static HookInformation* ShpFindPatchInfoByAddress( 121 | _In_ const SharedShadowHookData* shared_sh_data, _In_ void* address); 122 | 123 | static void ShpEnablePageShadowingForExec(_In_ const HookInformation& info, 124 | _In_ EptData* ept_data); 125 | 126 | static void ShpEnablePageShadowingForRW(_In_ const HookInformation& info, 127 | _In_ EptData* ept_data); 128 | 129 | static void ShpDisablePageShadowing(_In_ const HookInformation& info, 130 | _In_ EptData* ept_data); 131 | 132 | static void ShpSetMonitorTrapFlag(_In_ ShadowHookData* sh_data, 133 | _In_ bool enable); 134 | 135 | static void ShpSaveLastHookInfo(_In_ ShadowHookData* sh_data, 136 | _In_ const HookInformation& info); 137 | 138 | static const HookInformation* ShpRestoreLastHookInfo( 139 | _In_ ShadowHookData* sh_data); 140 | 141 | static bool ShpIsShadowHookActive( 142 | _In_ const SharedShadowHookData* shared_sh_data); 143 | 144 | #if defined(ALLOC_PRAGMA) 145 | #pragma alloc_text(PAGE, ShAllocateShadowHookData) 146 | #pragma alloc_text(PAGE, ShAllocateSharedShaowHookData) 147 | #pragma alloc_text(PAGE, ShEnableHooks) 148 | #pragma alloc_text(PAGE, ShInstallHook) 149 | #pragma alloc_text(PAGE, ShpSetupInlineHook) 150 | #pragma alloc_text(PAGE, ShpGetInstructionSize) 151 | #pragma alloc_text(PAGE, ShpMakeTrampolineCode) 152 | #pragma alloc_text(PAGE, ShFreeShadowHookData) 153 | #pragma alloc_text(PAGE, ShFreeSharedShadowHookData) 154 | #pragma alloc_text(PAGE, ShDisableHooks) 155 | #endif 156 | 157 | //////////////////////////////////////////////////////////////////////////////// 158 | // 159 | // variables 160 | // 161 | 162 | //////////////////////////////////////////////////////////////////////////////// 163 | // 164 | // implementations 165 | // 166 | 167 | // Workarounds https://github.com/tandasat/DdiMon/issues/34 168 | EXTERN_C 169 | _ACRTIMP void __cdecl _invoke_watson(_In_opt_z_ wchar_t const*, 170 | _In_opt_z_ wchar_t const*, 171 | _In_opt_z_ wchar_t const*, 172 | _In_ unsigned int, 173 | _In_ uintptr_t) {} 174 | 175 | // Workarounds https://github.com/tandasat/DdiMon/issues/34 176 | namespace std { 177 | _Prhand _Raise_handler; 178 | } 179 | 180 | // Allocates per-processor shadow hook data 181 | _Use_decl_annotations_ EXTERN_C ShadowHookData* ShAllocateShadowHookData() { 182 | PAGED_CODE(); 183 | 184 | auto p = new ShadowHookData(); 185 | RtlFillMemory(p, sizeof(ShadowHookData), 0); 186 | return p; 187 | } 188 | 189 | // Frees per-processor shadow hook data 190 | _Use_decl_annotations_ EXTERN_C void ShFreeShadowHookData( 191 | ShadowHookData* sh_data) { 192 | PAGED_CODE(); 193 | 194 | delete sh_data; 195 | } 196 | 197 | // Allocates processor-shared shadow hook data 198 | _Use_decl_annotations_ EXTERN_C SharedShadowHookData* 199 | ShAllocateSharedShaowHookData() { 200 | PAGED_CODE(); 201 | 202 | auto p = new SharedShadowHookData(); 203 | RtlFillMemory(p, sizeof(SharedShadowHookData), 0); 204 | return p; 205 | } 206 | 207 | // Frees processor-shared shadow hook data 208 | _Use_decl_annotations_ EXTERN_C void ShFreeSharedShadowHookData( 209 | SharedShadowHookData* shared_sh_data) { 210 | PAGED_CODE(); 211 | 212 | delete shared_sh_data; 213 | } 214 | 215 | // Enables page shadowing for all hooks 216 | _Use_decl_annotations_ EXTERN_C NTSTATUS ShEnableHooks() { 217 | PAGED_CODE(); 218 | 219 | return UtilForEachProcessor( 220 | [](void* context) { 221 | UNREFERENCED_PARAMETER(context); 222 | return UtilVmCall(HypercallNumber::kShEnablePageShadowing, nullptr); 223 | }, 224 | nullptr); 225 | } 226 | 227 | // Disables page shadowing for all hooks 228 | _Use_decl_annotations_ EXTERN_C NTSTATUS ShDisableHooks() { 229 | PAGED_CODE(); 230 | 231 | return UtilForEachProcessor( 232 | [](void* context) { 233 | UNREFERENCED_PARAMETER(context); 234 | return UtilVmCall(HypercallNumber::kShDisablePageShadowing, nullptr); 235 | }, 236 | nullptr); 237 | } 238 | 239 | // Enables page shadowing for all hooks 240 | _Use_decl_annotations_ NTSTATUS ShEnablePageShadowing( 241 | EptData* ept_data, const SharedShadowHookData* shared_sh_data) { 242 | //HYPERPLATFORM_COMMON_DBG_BREAK(); 243 | 244 | for (auto& info : shared_sh_data->hooks) { 245 | ShpEnablePageShadowingForExec(*info, ept_data); 246 | } 247 | return STATUS_SUCCESS; 248 | } 249 | 250 | // Disables page shadowing for all hooks 251 | _Use_decl_annotations_ void ShVmCallDisablePageShadowing( 252 | EptData* ept_data, const SharedShadowHookData* shared_sh_data) { 253 | //HYPERPLATFORM_COMMON_DBG_BREAK(); 254 | 255 | for (auto& info : shared_sh_data->hooks) { 256 | ShpDisablePageShadowing(*info, ept_data); 257 | } 258 | } 259 | 260 | // Handles #BP. Checks if the #BP happened on where DdiMon set a break point, 261 | // and if so, modifies the contents of guest's IP to execute a corresponding 262 | // hook handler. 263 | _Use_decl_annotations_ bool ShHandleBreakpoint( 264 | ShadowHookData* sh_data, const SharedShadowHookData* shared_sh_data, 265 | void* guest_ip) { 266 | UNREFERENCED_PARAMETER(sh_data); 267 | 268 | if (!ShpIsShadowHookActive(shared_sh_data)) { 269 | return false; 270 | } 271 | 272 | const auto info = ShpFindPatchInfoByAddress(shared_sh_data, guest_ip); 273 | if (!info) { 274 | return false; 275 | } 276 | 277 | // Update guest's IP 278 | UtilVmWrite(VmcsField::kGuestRip, reinterpret_cast(info->handler)); 279 | return true; 280 | } 281 | 282 | // Handles MTF VM-exit. Re-enables the shadow hook and clears MTF. 283 | _Use_decl_annotations_ void ShHandleMonitorTrapFlag( 284 | ShadowHookData* sh_data, const SharedShadowHookData* shared_sh_data, 285 | EptData* ept_data) { 286 | NT_VERIFY(ShpIsShadowHookActive(shared_sh_data)); 287 | 288 | const auto info = ShpRestoreLastHookInfo(sh_data); 289 | ShpEnablePageShadowingForExec(*info, ept_data); 290 | ShpSetMonitorTrapFlag(sh_data, false); 291 | } 292 | 293 | // Handles EPT violation VM-exit. 294 | _Use_decl_annotations_ void ShHandleEptViolation( 295 | ShadowHookData* sh_data, const SharedShadowHookData* shared_sh_data, 296 | EptData* ept_data, void* fault_va) { 297 | if (!ShpIsShadowHookActive(shared_sh_data)) { 298 | return; 299 | } 300 | 301 | const auto info = ShpFindPatchInfoByPage(shared_sh_data, fault_va); 302 | if (!info) { 303 | return; 304 | } 305 | 306 | // EPT violation was caused because a guest tried to read or write to a page 307 | // where currently set as execute only for protecting a hook. Let a guest 308 | // read or write a page from a read/write shadow page and run a single 309 | // instruction. 310 | ShpEnablePageShadowingForRW(*info, ept_data); 311 | ShpSetMonitorTrapFlag(sh_data, true); 312 | ShpSaveLastHookInfo(sh_data, *info); 313 | } 314 | 315 | // Set up inline hook at the address without activating it 316 | _Use_decl_annotations_ EXTERN_C bool ShInstallHook( 317 | SharedShadowHookData* shared_sh_data, void* address, 318 | ShadowHookTarget* target) { 319 | PAGED_CODE(); 320 | 321 | auto info = ShpCreateHookInformation( 322 | shared_sh_data, reinterpret_cast(address), target); 323 | if (!info) { 324 | return false; 325 | } 326 | 327 | if (!ShpSetupInlineHook(info->patch_address, 328 | info->shadow_page_base_for_exec->page, 329 | &target->original_call)) { 330 | return false; 331 | } 332 | 333 | HYPERPLATFORM_LOG_DEBUG( 334 | "Patch = %p, Exec = %p, RW = %p, Trampoline = %p", info->patch_address, 335 | info->shadow_page_base_for_exec->page + BYTE_OFFSET(info->patch_address), 336 | info->shadow_page_base_for_rw->page + BYTE_OFFSET(info->patch_address), 337 | target->original_call); 338 | 339 | shared_sh_data->hooks.push_back(std::move(info)); 340 | return true; 341 | } 342 | 343 | // Creates or reuses a couple of copied pages and initializes HookInformation 344 | _Use_decl_annotations_ static std::unique_ptr 345 | ShpCreateHookInformation(SharedShadowHookData* shared_sh_data, void* address, 346 | ShadowHookTarget* target) { 347 | auto info = std::make_unique(); 348 | auto reusable_info = ShpFindPatchInfoByPage(shared_sh_data, address); 349 | if (reusable_info) { 350 | // Found an existing HookInformation object targeting the same page as this 351 | // one. re-use shadow pages. 352 | info->shadow_page_base_for_rw = reusable_info->shadow_page_base_for_rw; 353 | info->shadow_page_base_for_exec = reusable_info->shadow_page_base_for_exec; 354 | } else { 355 | // This hook is for a page that is not currently have any hooks (i.e., not 356 | // shadowed). Creates shadow pages. 357 | info->shadow_page_base_for_rw = std::make_shared(); 358 | info->shadow_page_base_for_exec = std::make_shared(); 359 | auto page_base = PAGE_ALIGN(address); 360 | RtlCopyMemory(info->shadow_page_base_for_rw->page, page_base, PAGE_SIZE); 361 | RtlCopyMemory(info->shadow_page_base_for_exec->page, page_base, PAGE_SIZE); 362 | } 363 | info->patch_address = address; 364 | info->pa_base_for_rw = UtilPaFromVa(info->shadow_page_base_for_rw->page); 365 | info->pa_base_for_exec = UtilPaFromVa(info->shadow_page_base_for_exec->page); 366 | info->handler = target->handler; 367 | return info; 368 | } 369 | 370 | // Builds a trampoline code for calling an original code and embeds 0xcc on the 371 | // shadow_exec_page 372 | _Use_decl_annotations_ EXTERN_C static bool ShpSetupInlineHook( 373 | void* patch_address, UCHAR* shadow_exec_page, void** original_call_ptr) { 374 | PAGED_CODE(); 375 | 376 | const auto patch_size = ShpGetInstructionSize(patch_address); 377 | if (!patch_size) { 378 | return false; 379 | } 380 | 381 | // Build trampoline code (copied stub -> in the middle of original) 382 | const auto jmp_to_original = ShpMakeTrampolineCode( 383 | reinterpret_cast(patch_address) + patch_size); 384 | #pragma warning(push) 385 | #pragma warning(disable : 30030) // Allocating executable POOL_TYPE memory 386 | const auto original_call = ExAllocatePoolWithTag( 387 | NonPagedPoolExecute, patch_size + sizeof(jmp_to_original), 388 | kHyperPlatformCommonPoolTag); 389 | #pragma warning(pop) 390 | if (!original_call) { 391 | return false; 392 | } 393 | 394 | // Copy original code and embed jump code following original code 395 | RtlCopyMemory(original_call, patch_address, patch_size); 396 | #pragma warning(push) 397 | #pragma warning(disable : 6386) 398 | // Buffer overrun while writing to 'reinterpret_castoriginal_call+patch_size': the writable size is 400 | // 'patch_size+sizeof((jmp_to_original))' bytes, but '15' bytes might be 401 | // written. 402 | RtlCopyMemory(reinterpret_cast(original_call) + patch_size, 403 | &jmp_to_original, sizeof(jmp_to_original)); 404 | #pragma warning(pop) 405 | 406 | // install patch to shadow page 407 | static const UCHAR kBreakpoint[] = { 408 | 0xcc, 409 | }; 410 | RtlCopyMemory(shadow_exec_page + BYTE_OFFSET(patch_address), kBreakpoint, 411 | sizeof(kBreakpoint)); 412 | 413 | KeInvalidateAllCaches(); 414 | 415 | *original_call_ptr = original_call; 416 | return true; 417 | } 418 | 419 | // Returns a size of an instruction at the address 420 | _Use_decl_annotations_ EXTERN_C static SIZE_T ShpGetInstructionSize( 421 | void* address) { 422 | PAGED_CODE(); 423 | 424 | // Save floating point state 425 | KFLOATING_SAVE float_save = {}; 426 | auto status = KeSaveFloatingPointState(&float_save); 427 | if (!NT_SUCCESS(status)) { 428 | return 0; 429 | } 430 | 431 | // Disassemble at most 15 bytes to get an instruction size 432 | csh handle = {}; 433 | const auto mode = IsX64() ? CS_MODE_64 : CS_MODE_32; 434 | if (cs_open(CS_ARCH_X86, mode, &handle) != CS_ERR_OK) { 435 | KeRestoreFloatingPointState(&float_save); 436 | return 0; 437 | } 438 | 439 | static const auto kLongestInstSize = 15; 440 | cs_insn* instructions = nullptr; 441 | const auto count = 442 | cs_disasm(handle, reinterpret_cast(address), kLongestInstSize, 443 | reinterpret_cast(address), 1, &instructions); 444 | if (count == 0) { 445 | cs_close(&handle); 446 | KeRestoreFloatingPointState(&float_save); 447 | return 0; 448 | } 449 | 450 | // Get a size of the first instruction 451 | const auto size = instructions[0].size; 452 | cs_free(instructions, count); 453 | cs_close(&handle); 454 | 455 | // Restore floating point state 456 | KeRestoreFloatingPointState(&float_save); 457 | return size; 458 | } 459 | 460 | // Returns code bytes for inline hooking 461 | _Use_decl_annotations_ EXTERN_C static TrampolineCode ShpMakeTrampolineCode( 462 | void* hook_handler) { 463 | PAGED_CODE(); 464 | 465 | #if defined(_AMD64_) 466 | // 90 nop 467 | // ff2500000000 jmp qword ptr cs:jmp_addr 468 | // jmp_addr: 469 | // 0000000000000000 dq 0 470 | return { 471 | 0x90, 472 | { 473 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 474 | }, 475 | hook_handler, 476 | }; 477 | #else 478 | // 90 nop 479 | // 6832e30582 push offset nt!ExFreePoolWithTag + 0x2 (8205e332) 480 | // c3 ret 481 | return { 482 | 0x90, 0x68, hook_handler, 0xc3, 483 | }; 484 | #endif 485 | } 486 | 487 | // Find a HookInformation instance by address 488 | _Use_decl_annotations_ static HookInformation* ShpFindPatchInfoByPage( 489 | const SharedShadowHookData* shared_sh_data, void* address) { 490 | const auto found = std::find_if( 491 | shared_sh_data->hooks.cbegin(), shared_sh_data->hooks.cend(), 492 | [address](const auto& info) { 493 | return PAGE_ALIGN(info->patch_address) == PAGE_ALIGN(address); 494 | }); 495 | if (found == shared_sh_data->hooks.cend()) { 496 | return nullptr; 497 | } 498 | return found->get(); 499 | } 500 | 501 | // Find a HookInformation instance that are on the same page as the address 502 | _Use_decl_annotations_ static HookInformation* ShpFindPatchInfoByAddress( 503 | const SharedShadowHookData* shared_sh_data, void* address) { 504 | auto found = std::find_if( 505 | shared_sh_data->hooks.cbegin(), shared_sh_data->hooks.cend(), 506 | [address](const auto& info) { return info->patch_address == address; }); 507 | if (found == shared_sh_data->hooks.cend()) { 508 | return nullptr; 509 | } 510 | return found->get(); 511 | } 512 | 513 | // Show a shadowed page for execution 514 | _Use_decl_annotations_ static void ShpEnablePageShadowingForExec( 515 | const HookInformation& info, EptData* ept_data) { 516 | const auto ept_pt_entry = 517 | EptGetEptPtEntry(ept_data, UtilPaFromVa(info.patch_address)); 518 | 519 | // Allow the VMM to redirect read and write access to the address by denying 520 | // those accesses and handling them on EPT violation 521 | ept_pt_entry->fields.write_access = false; 522 | ept_pt_entry->fields.read_access = false; 523 | 524 | // Only execution is allowed on the address. Show the copied page for exec 525 | // that has an actual breakpoint to the guest. 526 | ept_pt_entry->fields.physial_address = UtilPfnFromPa(info.pa_base_for_exec); 527 | 528 | UtilInveptGlobal(); 529 | } 530 | 531 | // Show a shadowed page for read and write 532 | _Use_decl_annotations_ static void ShpEnablePageShadowingForRW( 533 | const HookInformation& info, EptData* ept_data) { 534 | const auto ept_pt_entry = 535 | EptGetEptPtEntry(ept_data, UtilPaFromVa(info.patch_address)); 536 | 537 | // Allow a guest to read and write as well as execute the address. Show the 538 | // copied page for read/write that does not have an breakpoint but reflects 539 | // all modification by a guest if that happened. 540 | ept_pt_entry->fields.write_access = true; 541 | ept_pt_entry->fields.read_access = true; 542 | ept_pt_entry->fields.physial_address = UtilPfnFromPa(info.pa_base_for_rw); 543 | 544 | UtilInveptGlobal(); 545 | } 546 | 547 | // Stop showing a shadow page 548 | _Use_decl_annotations_ static void ShpDisablePageShadowing( 549 | const HookInformation& info, EptData* ept_data) { 550 | const auto pa_base = UtilPaFromVa(PAGE_ALIGN(info.patch_address)); 551 | const auto ept_pt_entry = EptGetEptPtEntry(ept_data, pa_base); 552 | ept_pt_entry->fields.write_access = true; 553 | ept_pt_entry->fields.read_access = true; 554 | ept_pt_entry->fields.physial_address = UtilPfnFromPa(pa_base); 555 | 556 | UtilInveptGlobal(); 557 | } 558 | 559 | // Set MTF on the current processor 560 | _Use_decl_annotations_ static void ShpSetMonitorTrapFlag( 561 | ShadowHookData* sh_data, bool enable) { 562 | VmxProcessorBasedControls vm_procctl = { 563 | static_cast(UtilVmRead(VmcsField::kCpuBasedVmExecControl))}; 564 | vm_procctl.fields.monitor_trap_flag = enable; 565 | UtilVmWrite(VmcsField::kCpuBasedVmExecControl, vm_procctl.all); 566 | } 567 | 568 | // Saves HookInformation as the last one for reusing it on up coming MTF VM-exit 569 | _Use_decl_annotations_ static void ShpSaveLastHookInfo( 570 | ShadowHookData* sh_data, const HookInformation& info) { 571 | NT_ASSERT(!sh_data->last_hook_info); 572 | sh_data->last_hook_info = &info; 573 | } 574 | 575 | // Retrieves the last HookInformation 576 | _Use_decl_annotations_ static const HookInformation* ShpRestoreLastHookInfo( 577 | ShadowHookData* sh_data) { 578 | NT_ASSERT(sh_data->last_hook_info); 579 | auto info = sh_data->last_hook_info; 580 | sh_data->last_hook_info = nullptr; 581 | return info; 582 | } 583 | 584 | // Checks if DdiMon is already initialized 585 | _Use_decl_annotations_ static bool ShpIsShadowHookActive( 586 | const SharedShadowHookData* shared_sh_data) { 587 | return !!(shared_sh_data); 588 | } 589 | 590 | // Allocates a non-paged, page-aligned page. Issues bug check on failure 591 | Page::Page() 592 | : page(reinterpret_cast(ExAllocatePoolWithTag( 593 | NonPagedPool, PAGE_SIZE, kHyperPlatformCommonPoolTag))) { 594 | if (!page) { 595 | HYPERPLATFORM_COMMON_BUG_CHECK( 596 | HyperPlatformBugCheck::kCritialPoolAllocationFailure, 0, 0, 0); 597 | } 598 | } 599 | 600 | // De-allocates the allocated page 601 | Page::~Page() { ExFreePoolWithTag(page, kHyperPlatformCommonPoolTag); } 602 | -------------------------------------------------------------------------------- /DdiMon/shadow_hook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2018, Satoshi Tanda. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /// @file 6 | /// @brief Declares interfaces to shadow hook functions. 7 | 8 | #ifndef DDIMON_SHADOW_HOOK_H_ 9 | #define DDIMON_SHADOW_HOOK_H_ 10 | 11 | #include 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | // 15 | // macro utilities 16 | // 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // 20 | // constants and macros 21 | // 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // 25 | // types 26 | // 27 | 28 | struct EptData; 29 | struct ShadowHookData; 30 | struct SharedShadowHookData; 31 | 32 | // Expresses where to install hooks by a function name, and its handlers 33 | struct ShadowHookTarget { 34 | UNICODE_STRING target_name; // An export name to hook 35 | void* handler; // An address of a hook handler 36 | 37 | // An address of a trampoline code to call original function. Initialized by 38 | // a successful call of ShInstallHook(). 39 | void* original_call; 40 | }; 41 | 42 | //////////////////////////////////////////////////////////////////////////////// 43 | // 44 | // prototypes 45 | // 46 | 47 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C 48 | ShadowHookData* ShAllocateShadowHookData(); 49 | 50 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C 51 | void ShFreeShadowHookData(_In_ ShadowHookData* sh_data); 52 | 53 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C 54 | SharedShadowHookData* ShAllocateSharedShaowHookData(); 55 | 56 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C 57 | void ShFreeSharedShadowHookData(_In_ SharedShadowHookData* shared_sh_data); 58 | 59 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C NTSTATUS ShEnableHooks(); 60 | 61 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C NTSTATUS ShDisableHooks(); 62 | 63 | _IRQL_requires_min_(DISPATCH_LEVEL) NTSTATUS 64 | ShEnablePageShadowing(_In_ EptData* ept_data, 65 | _In_ const SharedShadowHookData* shared_sh_data); 66 | 67 | _IRQL_requires_min_(DISPATCH_LEVEL) void ShVmCallDisablePageShadowing( 68 | _In_ EptData* ept_data, _In_ const SharedShadowHookData* shared_sh_data); 69 | 70 | _IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C 71 | bool ShInstallHook(_In_ SharedShadowHookData* shared_sh_data, 72 | _In_ void* address, _In_ ShadowHookTarget* target); 73 | 74 | _IRQL_requires_min_(DISPATCH_LEVEL) bool ShHandleBreakpoint( 75 | _In_ ShadowHookData* sh_data, 76 | _In_ const SharedShadowHookData* shared_sh_data, _In_ void* guest_ip); 77 | 78 | _IRQL_requires_min_(DISPATCH_LEVEL) void ShHandleMonitorTrapFlag( 79 | _In_ ShadowHookData* sh_data, 80 | _In_ const SharedShadowHookData* shared_sh_data, _In_ EptData* ept_data); 81 | 82 | _IRQL_requires_min_(DISPATCH_LEVEL) void ShHandleEptViolation( 83 | _In_ ShadowHookData* sh_data, 84 | _In_ const SharedShadowHookData* shared_sh_data, _In_ EptData* ept_data, 85 | _In_opt_ void* fault_va); 86 | 87 | //////////////////////////////////////////////////////////////////////////////// 88 | // 89 | // variables 90 | // 91 | 92 | //////////////////////////////////////////////////////////////////////////////// 93 | // 94 | // implementations 95 | // 96 | 97 | #endif // DDIMON_SHADOW_HOOK_H_ 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2018 Satoshi Tanda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DdiMon 2 | ======= 3 | 4 | Introduction 5 | ------------- 6 | DdiMon is a hypervisor performing inline hooking that is invisible to a guest 7 | (ie, any code other than DdiMon) by using extended page table (EPT). 8 | 9 | DdiMon is meant to be an educational tool for understanding how to use EPT from 10 | a programming perspective for research. To demonstrate it, DdiMon installs the 11 | invisible inline hooks on the following device driver interfaces (DDIs) to 12 | monitor activities of the Windows built-in kernel patch protection, a.k.a. 13 | PatchGuard, and hide certain processes without being detected by PatchGuard. 14 | - ExQueueWorkItem 15 | - ExAllocatePoolWithTag 16 | - ExFreePool 17 | - ExFreePoolWithTag 18 | - NtQuerySystemInformation 19 | 20 | Those stealth shadow hooks are hidden from guest's read and write memory 21 | operations and exposed only on execution of the memory. Therefore, they are 22 | neither visible nor overwritable from a guest, while they function as ordinal 23 | hooks. It is accomplished by making use of EPT enforcing a guest to see 24 | different memory contents from what it would see if EPT is not in use. This 25 | technique is often called memory shadowing. For more details, see the Design 26 | section below. 27 | 28 | Here is a movie demonstrating that shadow hooks allow you to monitor and 29 | control DDI calls without being notified by PatchGuard. 30 | - https://www.youtube.com/watch?v=UflyX3GeYkw 31 | 32 | DdiMon is implemented on the top of HyperPlatform. See a project page for 33 | more details of HyperPlatform: 34 | - https://github.com/tandasat/HyperPlatform 35 | 36 | 37 | Installation and Uninstallation 38 | -------------------------------- 39 | Clone full source code from Github with a below command and compile it on Visual 40 | Studio. 41 | 42 | $ git clone --recursive https://github.com/tandasat/DdiMon.git 43 | 44 | On the x64 platform, you have to enable test signing to install the driver. 45 | To do that, open the command prompt with the administrator privilege and type 46 | the following command, and then restart the system to activate the change: 47 | 48 | >bcdedit /set testsigning on 49 | 50 | To install and uninstall the driver, use the 'sc' command. For installation: 51 | 52 | >sc create DdiMon type= kernel binPath= C:\Users\user\Desktop\DdiMon.sys 53 | >sc start DdiMon 54 | 55 | And for uninstallation: 56 | 57 | >sc stop DdiMon 58 | >sc delete DdiMon 59 | >bcdedit /deletevalue testsigning 60 | 61 | Note that the system must support the Intel VT-x and EPT technology to 62 | successfully install the driver. 63 | 64 | To install the driver on a virtual machine on VMware Workstation, see an "Using 65 | VMware Workstation" section in the HyperPlatform User Document. 66 | - http://tandasat.github.io/HyperPlatform/userdocument/ 67 | 68 | 69 | Output 70 | ------- 71 | All logs are printed out to DbgView and saved in C:\Windows\DdiMon.log. 72 | 73 | 74 | Motivation 75 | ----------- 76 | Despite existence of plenty of academic research projects[1,2,3] and production 77 | software[4,5], EPT (a.k.a. SLAT; second-level-address translation) is still 78 | underused technology among reverse engineers due to lack of information on how 79 | it works and how to control it through programming. 80 | 81 | MoRE[6] by Jacob Torrey is a one of very few open source projects demonstrating 82 | use of EPT with small amount of code. While we recommend to look into the 83 | project for basic comprehension of how EPT can be initialized and used to set up 84 | more than 1:1 guest to machine physical memory mapping, MoRE lacks flexibility 85 | to extend its code for supporting broader platforms and implementing your own 86 | analysis tools. 87 | 88 | DdiMon provides a similar sample use of EPT as what MoRE does with a greater 89 | range of platform support such as x64 and/or Windows 10. DdiMon, also, can be 90 | seen as example extension of HyperPlatform for memory virtualization. 91 | 92 | - [1] SecVisor: A Tiny Hypervisor to Provide Lifetime Kernel Code Integrity for 93 | Commodity OSes 94 | - https://www.cs.cmu.edu/~arvinds/pubs/secvisor.pdf 95 | - [2] SPIDER: Stealthy Binary Program Instrumentation and Debugging via Hardware 96 | Virtualization 97 | - https://www.cerias.purdue.edu/assets/pdf/bibtex_archive/2013-5.pdf 98 | - [3] Dynamic VM Dependability Monitoring Using Hypervisor Probes 99 | - http://assured-cloud-computing.illinois.edu/files/2014/03/Dynamic-VM-Dependability-Monitoring-Using-Hypervisor-Probes.pdf 100 | - [4] Windows 10 Virtualization-based Security (Device Guard) 101 | - https://technet.microsoft.com/en-us/library/mt463091(v=vs.85).aspx 102 | - [5] VMRay 103 | - https://www.vmray.com/features/ 104 | - [6] MoRE 105 | - https://github.com/ainfosec/MoRE 106 | 107 | 108 | Design 109 | ------- 110 | In order to install a shadow hook, DdiMon creates a couple of copies of a page 111 | where the address to install a hook belongs to. After DdiMon is initialized, 112 | those two pages are accessed when a guest, namely all but ones by the hypervisor 113 | (ie, DdiMon), attempts to access to the original page instead. For example, when 114 | DdiMon installs a hook onto 0x1234, two copied pages are created: 0xa000 for 115 | execution access and 0xb000 for read or write access, and memory access is 116 | performed as below after the hook is activated: 117 | 118 | Requested Accessed 119 | By Hypervisor: 0x1234 -> 0x1234 on all access 120 | By Guest: 0x1234 -> 0xa234 on execution access 121 | -> 0xb234 on read or write access 122 | 123 | The following explains how it is accomplished. 124 | 125 | **Default state** 126 | 127 | DdiMon first configures an EPT entry corresponds to 0x1000-0x1fff to refer to 128 | the contents of 0xa000 and to disallow read and write access to the page. 129 | 130 | **Scenario: Read or Write** 131 | 132 | 1. With this configuration, any read and write access triggers EPT violation 133 | VM-exit. Up on the VM-exit, the EPT entry for 0x1000-0x1fff is modified to refer 134 | to the contents of 0xb000, which is copy of 0x1000, and to allow read and write 135 | to the page. And then, sets the Monitor Trap Flag (MTF), which works like the 136 | Trap Flag of the flag register but not visible to a guest, so that a guest can 137 | perform the read or write operation and then interrupted by the hypervisor with 138 | MTF VM-exit. 139 | 140 | 2. After executing a single instruction, a guest is interrupted by MTF VM-exit. 141 | On this VM-exit, the hypervisor clears the MTF and resets the EPT entry to the 142 | default state so that subsequent execution is done with the contents of 0xa000. 143 | 144 | As a result of this sequence of operations, a guest executed a single 145 | instruction reading from or writing to 0xb234. 146 | 147 | **Scenario: Execute** 148 | 149 | At this time, execution is done against contents of 0xa000 without triggering 150 | any events unless no other settings is made. In order to monitor execution of 151 | 0xa234 (0x1234 from guest's perspective), DdiMon sets a break point (0xcc) to 152 | 0xa234 and handles #BP in the hypervisor. Following steps are how DdiMon 153 | hooks execution of 0xa234. 154 | 155 | 1. On #BP VM-exit, the hypervisor checks if guest's EIP/RIP is 0x1234 first. If 156 | so, the hypervisor changes the contents of the register to point to a 157 | corresponding hook handler for instrumenting the DDI call. 158 | 159 | 2. On VM-enter, a guest executes the hook handler. The hook handler calls an 160 | original function, examines parameters, return values and/or a return address, 161 | and takes action accordingly. 162 | 163 | This is just like a typical inline hooking. Only differences are that it sets 164 | 0xcc and changes EIP/RIP from a hypervisor instead of overwriting original code 165 | with JMP instructions and that installed hooks are not visible from a guest. An 166 | advantage of using 0xcc is that it does not require a target function to have a 167 | length to install JMP instructions. 168 | 169 | 170 | Implementation 171 | --------------- 172 | The following are a call hierarchy with regard to sequences explained above. 173 | 174 | **On DriverEntry** 175 | 176 | DdimonInitialization() 177 | DdimonpEnumExportedSymbolsCallback() // Enumerates exports of ntoskrnl 178 | ShInstallHook() // Installs a stealth hook 179 | ShEnableHooks() // Activates installed hooks 180 | ShEnablePageShadowing() 181 | ShpEnablePageShadowingForExec() // Configures an EPT entry as 182 | // explained in "Default state" 183 | 184 | **On EPT violation VM-exit with read or write** 185 | 186 | VmmpHandleEptViolation() 187 | EptHandleEptViolation() 188 | ShHandleEptViolation() // Performs actions as explained in the 1 of 189 | // "Scenario: Read or Write" 190 | 191 | **On MTF VM-exit** 192 | 193 | VmmpHandleMonitorTrap() 194 | ShHandleMonitorTrapFlag() // Performs actions as explained in the 2 of 195 | // "Scenario: Read or Write" 196 | 197 | **On #BP VM-exit** 198 | 199 | VmmpHandleException() 200 | ShHandleBreakpoint() // Performs actions as explained in the 1 of 201 | // "Scenario: Execute" 202 | 203 | 204 | 205 | Implemented Hook Handlers 206 | -------------------------- 207 | - ExQueueWorkItem 208 | - The hook handler prints out given parameters when a specified work 209 | item routine is not inside any images. 210 | 211 | - ExAllocatePoolWithTag 212 | - The hook handler prints out given parameters and a return value of 213 | ExAllocatePoolWithTag() when it is called from an address where is 214 | not backed by any images. 215 | 216 | - ExFreePool and ExFreePoolWithTag 217 | - The hook handlers print out given parameters when they are called 218 | from addresses where are not backed by any images. 219 | 220 | - NtQuerySystemInformation 221 | - The hook handler takes out an entry for "cmd.exe" from returned 222 | process information so that cmd.exe is not listed by process 223 | enumeration. 224 | 225 | The easiest way to see those logs is installing NoImage.sys. 226 | - https://github.com/tandasat/MemoryMon/tree/master/MemoryMonTest 227 | 228 | Logs for activities of NoImage are look like this: 229 | 230 | 17:59:23.014 INF #0 4 48 System 84660265: ExFreePoolWithTag(P= 84665000, Tag= nigm) 231 | 17:59:23.014 INF #0 4 48 System 84660283: ExAllocatePoolWithTag(POOL_TYPE= 00000000, NumberOfBytes= 00001000, Tag= nigm) => 8517B000 232 | 17:59:23.014 INF #0 4 48 System 8517B1C3: ExQueueWorkItem({Routine= 8517B1D4, Parameter= 8517B000}, 1) 233 | 234 | 235 | Caveats 236 | -------- 237 | DdiMon is meant to be an educational tool and not robust, production quality 238 | software which is able to handle various edge cases. For example, DdiMon 239 | does not handle self-modification code since any memory writes on a shadowed 240 | page is not reflected to a view for execution. For this reason, researchers are 241 | encouraged to use this project as sample code to get familiar with EPT and 242 | develop their own tools as needed. 243 | 244 | 245 | Supported Platforms 246 | ---------------------- 247 | - x86 and x64 Windows 7, 8.1 and 10 248 | - The system must support the Intel VT-x and EPT technology 249 | 250 | 251 | License 252 | -------- 253 | This software is released under the MIT License, see LICENSE. 254 | -------------------------------------------------------------------------------- /clean.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | del *.sdf 3 | del *.VC.db 4 | del /s *.aps 5 | del /a:h *.suo 6 | rmdir /s /q .vs 7 | rmdir /s /q ipch 8 | rmdir /s /q x64 9 | rmdir /s /q Debug 10 | rmdir /s /q Debug_WDK 11 | rmdir /s /q Release 12 | rmdir /s /q Release_WDK 13 | rmdir /s /q DdiMon\x64 14 | rmdir /s /q DdiMon\Debug 15 | rmdir /s /q DdiMon\Release 16 | cd HyperPlatform 17 | clean.bat 18 | --------------------------------------------------------------------------------