├── .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 |
--------------------------------------------------------------------------------