├── .gitignore
├── LICENSE.txt
├── README.md
├── assets
├── chip-icon.png
└── screenshot.png
├── client
├── client.sln
└── client
│ ├── Library
│ └── memhv.h
│ ├── Source
│ └── Entry.cpp
│ ├── client.vcxproj
│ └── client.vcxproj.filters
└── memhv
├── memhv.sln
└── memhv
├── Source
├── Entry.cpp
├── Global.h
├── Memory
│ ├── Physical.cpp
│ └── Physical.h
├── SVM
│ ├── Defines
│ │ ├── SVM_ControlArea.h
│ │ ├── SVM_NestedPaging.h
│ │ ├── SVM_Platform.h
│ │ └── SVM_ProcessorData.h
│ ├── Handlers
│ │ ├── SVM_HandleGenericVM.cpp
│ │ ├── SVM_HandleMSRAccess.cpp
│ │ ├── SVM_HandleVMCall.cpp
│ │ ├── SVM_VMExit.cpp
│ │ └── SVM_VMExit.h
│ ├── SVM.cpp
│ ├── SVM.h
│ └── SVM_x64.asm
├── Shared.h
├── Utils.cpp
└── Utils.h
├── memhv.vcxproj
└── memhv.vcxproj.filters
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.tlog
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
298 | *.vbp
299 |
300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
301 | *.dsw
302 | *.dsp
303 |
304 | # Visual Studio 6 technical files
305 | *.ncb
306 | *.aps
307 |
308 | # Visual Studio LightSwitch build output
309 | **/*.HTMLClient/GeneratedArtifacts
310 | **/*.DesktopClient/GeneratedArtifacts
311 | **/*.DesktopClient/ModelManifest.xml
312 | **/*.Server/GeneratedArtifacts
313 | **/*.Server/ModelManifest.xml
314 | _Pvt_Extensions
315 |
316 | # Paket dependency manager
317 | .paket/paket.exe
318 | paket-files/
319 |
320 | # FAKE - F# Make
321 | .fake/
322 |
323 | # CodeRush personal settings
324 | .cr/personal
325 |
326 | # Python Tools for Visual Studio (PTVS)
327 | __pycache__/
328 | *.pyc
329 |
330 | # Cake - Uncomment if you are using it
331 | # tools/**
332 | # !tools/packages.config
333 |
334 | # Tabs Studio
335 | *.tss
336 |
337 | # Telerik's JustMock configuration file
338 | *.jmconfig
339 |
340 | # BizTalk build output
341 | *.btp.cs
342 | *.btm.cs
343 | *.odx.cs
344 | *.xsd.cs
345 |
346 | # OpenCover UI analysis results
347 | OpenCover/
348 |
349 | # Azure Stream Analytics local run output
350 | ASALocalRun/
351 |
352 | # MSBuild Binary and Structured Log
353 | *.binlog
354 |
355 | # NVidia Nsight GPU debugger configuration file
356 | *.nvuser
357 |
358 | # MFractors (Xamarin productivity tool) working folder
359 | .mfractor/
360 |
361 | # Local History for Visual Studio
362 | .localhistory/
363 |
364 | # Visual Studio History (VSHistory) files
365 | .vshistory/
366 |
367 | # BeatPulse healthcheck temp database
368 | healthchecksdb
369 |
370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
371 | MigrationBackup/
372 |
373 | # Ionide (cross platform F# VS Code tools) working folder
374 | .ionide/
375 |
376 | # Fody - auto-generated XML schema
377 | FodyWeavers.xsd
378 |
379 | # VS Code files for those working on multiple tools
380 | .vscode/*
381 | !.vscode/settings.json
382 | !.vscode/tasks.json
383 | !.vscode/launch.json
384 | !.vscode/extensions.json
385 | *.code-workspace
386 |
387 | # Local History for Visual Studio Code
388 | .history/
389 |
390 | # Windows Installer files from build outputs
391 | *.cab
392 | *.msi
393 | *.msix
394 | *.msm
395 | *.msp
396 |
397 | # JetBrains Rider
398 | *.sln.iml
399 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Samuel Tulach
4 | Copyright (c) 2017-2018 Satoshi Tanda
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
memhv
4 | Minimalistic hypervisor with memory introspection capabilities
5 |
6 |
7 | ## About
8 | This project has a single goal: to be as minimal as possible while providing a hypercall API for reading/writing an address space of any (protected) process. It is a standalone Microsoft Windows kernel-mode driver that can be loaded either normally or through manual mapping.
9 |
10 | 
11 |
12 | ## Support
13 | - Windows 10 or Windows 11 (both 64-bit, tested on 22H2 and 24H2)
14 | - AMD processor with SVM and NPT support
15 |
16 | ## Usage
17 | 1. Ensure that you have SVM enabled in UEFI firmware options (BIOS)
18 | 2. Make sure Microsoft Hyper-V is fully disabled
19 | 3. Sign and load the driver or use other means to load it ([kdmapper](https://github.com/TheCruZ/kdmapper), [KDU](https://github.com/hfiref0x/KDU), **make sure PE headers are not erased** if you want the hypervisor to use NPT to hide its memory from guest)
20 | 4. Enjoy hypercall API (see client folder)
21 |
22 | ## Detection vectors
23 | Common timing attacks are ineffective against this hypervisor, as it does not exit on CPUID or similar instructions typically used in such attacks. Memory of the hypervisor is hidden from the guest using NPT.
24 |
25 | ## Credits
26 | - [SimpleSvm](https://github.com/tandasat/SimpleSvm) by @tandasat
27 |
--------------------------------------------------------------------------------
/assets/chip-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamuelTulach/memhv/32655cb21ae757dbbc03872fa1fc3f79ca505e5d/assets/chip-icon.png
--------------------------------------------------------------------------------
/assets/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamuelTulach/memhv/32655cb21ae757dbbc03872fa1fc3f79ca505e5d/assets/screenshot.png
--------------------------------------------------------------------------------
/client/client.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.6.33815.320
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "client", "client\client.vcxproj", "{C9531A1C-C0F5-4DB3-AA94-7286C2341A48}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Release|x64 = Release|x64
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {C9531A1C-C0F5-4DB3-AA94-7286C2341A48}.Release|x64.ActiveCfg = Release|x64
14 | {C9531A1C-C0F5-4DB3-AA94-7286C2341A48}.Release|x64.Build.0 = Release|x64
15 | EndGlobalSection
16 | GlobalSection(SolutionProperties) = preSolution
17 | HideSolutionNode = FALSE
18 | EndGlobalSection
19 | GlobalSection(ExtensibilityGlobals) = postSolution
20 | SolutionGuid = {A28829FF-2C1D-465B-A4B0-507C500A6188}
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/client/client/Library/memhv.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | /*
4 | * memhv
5 | * https://github.com/SamuelTulach | https://tulach.cc
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | namespace HV
14 | {
15 | namespace Shared
16 | {
17 | constexpr ULONG64 MAGIC = 0xfeed3;
18 | constexpr ULONG64 COMM_CHECK = 0xdead;
19 |
20 | constexpr ULONG64 MAX_RW_SIZE = 0x10000;
21 |
22 | enum CommandId
23 | {
24 | Invalid,
25 | CheckPresence,
26 | GetProcess,
27 | GetDirectoryBase,
28 | CopyProcessMemory,
29 | ProtectSelf,
30 | };
31 |
32 | enum ErrorCodes
33 | {
34 | Success,
35 | ControlBlockReadFail,
36 | MemoryCopyTooLarge,
37 | MemoryCopyFailSource,
38 | MemoryCopyFailTarget,
39 | };
40 |
41 | typedef struct _COPY_MEMORY_DATA
42 | {
43 | ULONG64 SourceDirectoryBase;
44 | ULONG64 SourceAddress;
45 | ULONG64 DestinationDirectoryBase;
46 | ULONG64 DestinationAddress;
47 | SIZE_T NumberOfBytes;
48 | } COPY_MEMORY_DATA;
49 | }
50 |
51 | namespace Data
52 | {
53 | inline bool Attached = false; // dont be too attached to females, they bite
54 | inline PVOID Shellcode = nullptr;
55 |
56 | inline ULONG64 CurrentEPROCESS = 0;
57 | inline ULONG64 CurrentDirectoryBase = 0;
58 |
59 | inline HANDLE TargetProcessHandle = nullptr;
60 | inline ULONG64 TargetEPROCESS = 0;
61 | inline ULONG64 TargetDirectoryBase = 0;
62 | }
63 |
64 | inline ULONG64 CallVM(ULONG64 arg1 = 0, ULONG64 arg2 = 0, ULONG64 arg3 = 0, ULONG64 arg4 = 0)
65 | {
66 | if (!Data::Shellcode)
67 | {
68 | // vmmcall
69 | // ret
70 | const BYTE code[] = { 0x0F, 0x01, 0xD9, 0xC3 };
71 |
72 | Data::Shellcode = VirtualAlloc(nullptr, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
73 | if (!Data::Shellcode)
74 | throw std::exception("Out of memory");
75 |
76 | memcpy(Data::Shellcode, code, sizeof(code));
77 | }
78 |
79 | typedef ULONG64(__stdcall* CallFunc)(ULONG64 arg1, ULONG64 arg2, ULONG64 arg3, ULONG64 arg4);
80 | const CallFunc func = static_cast(Data::Shellcode);
81 |
82 | ULONG64 output;
83 | __try
84 | {
85 | output = func(arg1, arg2, arg3, arg4);
86 | }
87 | __except (EXCEPTION_EXECUTE_HANDLER)
88 | {
89 | printf("[!] SEH handler called\n");
90 | output = 0xAABB;
91 | }
92 |
93 | return output;
94 | }
95 |
96 | inline bool Error(ULONG64 hvOutput)
97 | {
98 | if (hvOutput == 0xAABB)
99 | return true; // HV not loaded
100 |
101 | if (hvOutput == 0xFFFF)
102 | return true; // command not implemented
103 |
104 | if (!hvOutput)
105 | return true; // empty output
106 |
107 | return false;
108 | }
109 |
110 | inline bool CheckPresence()
111 | {
112 | const ULONG64 output = CallVM(Shared::MAGIC, Shared::CommandId::CheckPresence);
113 | return output == Shared::COMM_CHECK;
114 | }
115 |
116 | inline bool Protect()
117 | {
118 | const ULONG64 output = CallVM(Shared::MAGIC, Shared::CommandId::ProtectSelf);
119 | return output == Shared::ErrorCodes::Success;
120 | }
121 |
122 | inline ULONG64 GetEPROCESS(UINT32 processId)
123 | {
124 | return CallVM(Shared::MAGIC, Shared::CommandId::GetProcess, processId);
125 | }
126 |
127 | inline ULONG64 GetDirbase(ULONG64 targetProcess)
128 | {
129 | return CallVM(Shared::MAGIC, Shared::CommandId::GetDirectoryBase, targetProcess);
130 | }
131 |
132 | inline bool AttachToProcess(UINT32 processId)
133 | {
134 | Data::CurrentEPROCESS = GetEPROCESS(GetCurrentProcessId());
135 | if (Error(Data::CurrentEPROCESS))
136 | return false;
137 |
138 | Data::TargetEPROCESS = GetEPROCESS(processId);
139 | if (Error(Data::TargetEPROCESS))
140 | return false;
141 |
142 | Data::CurrentDirectoryBase = GetDirbase(Data::CurrentEPROCESS);
143 | if (Error(Data::CurrentDirectoryBase))
144 | return false;
145 |
146 | Data::TargetDirectoryBase = GetDirbase(Data::TargetEPROCESS);
147 | if (Error(Data::TargetDirectoryBase))
148 | return false;
149 |
150 | Data::TargetProcessHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, processId);
151 | if (!Data::TargetProcessHandle || Data::TargetProcessHandle == INVALID_HANDLE_VALUE)
152 | return false;
153 |
154 | Data::Attached = true;
155 | return true;
156 | }
157 |
158 | inline bool CopyProcessMemory(ULONG64 sourceDirbase, ULONG64 sourceAddress, ULONG64 targetDirbase, ULONG64 targetAddress, SIZE_T bytes)
159 | {
160 | Shared::COPY_MEMORY_DATA data = { 0 };
161 | data.SourceDirectoryBase = sourceDirbase;
162 | data.SourceAddress = sourceAddress;
163 | data.DestinationDirectoryBase = targetDirbase;
164 | data.DestinationAddress = targetAddress;
165 | data.NumberOfBytes = bytes;
166 |
167 | const ULONG64 value = CallVM(Shared::MAGIC, Shared::CommandId::CopyProcessMemory, reinterpret_cast(&data), Data::CurrentDirectoryBase);
168 | return value == Shared::ErrorCodes::Success;
169 | }
170 |
171 | inline bool ReadMemory(ULONG64 source, ULONG64 buffer, SIZE_T size)
172 | {
173 | memset(reinterpret_cast(buffer), 0, size);
174 | return CopyProcessMemory(Data::TargetDirectoryBase, source, Data::CurrentDirectoryBase, buffer, size);
175 | }
176 |
177 | inline bool WriteMemory(ULONG64 buffer, ULONG64 destination, SIZE_T size)
178 | {
179 | return CopyProcessMemory(Data::CurrentDirectoryBase, buffer, Data::TargetDirectoryBase, destination, size);
180 | }
181 |
182 | template
183 | T Read(ULONG64 address)
184 | {
185 | T value = T();
186 | ReadMemory(address, (ULONG64)&value, sizeof(T));
187 | return value;
188 | }
189 |
190 | template
191 | void Write(ULONG64 address, T value)
192 | {
193 | WriteMemory((ULONG64)&value, address, sizeof(T));
194 | }
195 | }
--------------------------------------------------------------------------------
/client/client/Source/Entry.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "../Library/memhv.h"
9 |
10 | typedef enum _MEMORY_INFORMATION_CLASS
11 | {
12 | MemoryBasicInformation, // MEMORY_BASIC_INFORMATION
13 | MemoryWorkingSetInformation, // MEMORY_WORKING_SET_INFORMATION
14 | MemoryMappedFilenameInformation, // UNICODE_STRING
15 | MemoryRegionInformation, // MEMORY_REGION_INFORMATION
16 | MemoryWorkingSetExInformation, // MEMORY_WORKING_SET_EX_INFORMATION
17 | MemorySharedCommitInformation, // MEMORY_SHARED_COMMIT_INFORMATION
18 | MemoryImageInformation,
19 | MemoryRegionInformationEx,
20 | MemoryPrivilegedBasicInformation,
21 | MemoryEnclaveImageInformation,
22 | MemoryBasicInformationCapped
23 | } MEMORY_INFORMATION_CLASS;
24 |
25 | EXTERN_C NTSYSAPI NTSTATUS NTAPI NtQueryVirtualMemory(HANDLE processHandle, void* baseAddress, MEMORY_INFORMATION_CLASS memoryInformationClass, void* memoryInformation, size_t memoryInformationLength, size_t* returnLength);
26 |
27 | ULONG64 GetModule(UINT32 pid, const wchar_t* moduleName)
28 | {
29 | HANDLE targetProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid);
30 | if (!targetProcessHandle || targetProcessHandle == INVALID_HANDLE_VALUE)
31 | return 0;
32 |
33 | ULONG64 currentAddress = 0;
34 | MEMORY_BASIC_INFORMATION memoryInformation;
35 | while (VirtualQueryEx(targetProcessHandle, reinterpret_cast(currentAddress), &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION64)))
36 | {
37 | if (memoryInformation.Type == MEM_MAPPED || memoryInformation.Type == MEM_IMAGE)
38 | {
39 | constexpr size_t bufferSize = 1024;
40 | void* buffer = malloc(bufferSize);
41 |
42 | size_t bytesOut;
43 | NTSTATUS status = NtQueryVirtualMemory(targetProcessHandle, memoryInformation.BaseAddress, MemoryMappedFilenameInformation, buffer, bufferSize, &bytesOut);
44 | if (status == 0)
45 | {
46 | UNICODE_STRING* stringBuffer = static_cast(buffer);
47 | if (wcsstr(stringBuffer->Buffer, moduleName) && !wcsstr(stringBuffer->Buffer, L".mui"))
48 | {
49 | free(buffer);
50 | CloseHandle(targetProcessHandle);
51 | return reinterpret_cast(memoryInformation.BaseAddress);
52 | }
53 | }
54 |
55 | free(buffer);
56 | }
57 |
58 | currentAddress = reinterpret_cast(memoryInformation.BaseAddress) + memoryInformation.RegionSize;
59 | }
60 |
61 | CloseHandle(targetProcessHandle);
62 | return 0;
63 | }
64 |
65 | UINT32 LookupProcessId(const wchar_t* processName)
66 | {
67 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
68 | if (snapshot)
69 | {
70 | PROCESSENTRY32 entry = { 0 };
71 | entry.dwSize = sizeof(entry);
72 | if (Process32First(snapshot, &entry))
73 | {
74 | do
75 | {
76 | if (0 == _wcsicmp(entry.szExeFile, processName))
77 | {
78 | CloseHandle(snapshot);
79 | return entry.th32ProcessID;
80 | }
81 | } while (Process32Next(snapshot, &entry));
82 | }
83 |
84 | CloseHandle(snapshot);
85 | }
86 |
87 | return 0;
88 | }
89 |
90 | std::mutex m;
91 | ULONG64 MainModule = 0;
92 | void ThreadBench(int id)
93 | {
94 | while (true)
95 | {
96 | UINT64 totalOk = 0;
97 | UINT64 totalFail = 0;
98 |
99 | auto t1 = std::chrono::high_resolution_clock::now();
100 | for (int i = 0; i < 100000; i++)
101 | {
102 | int offset = rand() % 20 + 1;
103 | offset += 0x48;
104 | volatile int readValue = HV::Read(MainModule + offset);
105 | volatile int readConfirm = HV::Read(MainModule + offset);
106 | if (readValue == readConfirm && readValue != 0)
107 | totalOk++;
108 | else
109 | {
110 | totalFail++;
111 | m.lock();
112 | printf("[!] Invalid read: %x %x\n", readValue, readConfirm);
113 | m.unlock();
114 | }
115 | }
116 | auto t2 = std::chrono::high_resolution_clock::now();
117 | auto duration = std::chrono::duration_cast(t2 - t1).count();
118 | m.lock();
119 | printf("[+] Ok: %llu Fail: %llu In: %llu\n", totalOk, totalFail, duration);
120 | m.unlock();
121 | }
122 | }
123 |
124 | int main()
125 | {
126 | printf("[>] Checking presence...\n");
127 | bool status = HV::CheckPresence();
128 | if (!status)
129 | {
130 | printf("[!] Hypervisor not running\n");
131 | getchar();
132 | return EXIT_FAILURE;
133 | }
134 |
135 | printf("[>] Instructing hypervisor to protect itself...\n");
136 | status = HV::Protect();
137 | if (!status)
138 | {
139 | printf("[!] Hypervisor self-protection failed\n");
140 | getchar();
141 | return EXIT_FAILURE;
142 | }
143 |
144 | printf("[>] Searching for target process...\n");
145 | UINT32 targetProcessId = LookupProcessId(L"SystemInformer.exe");
146 | if (!targetProcessId)
147 | {
148 | printf("[!] Process not found\n");
149 | getchar();
150 | return EXIT_FAILURE;
151 | }
152 |
153 | printf("[+] Process has PID of %u\n", targetProcessId);
154 |
155 | printf("[>] Attaching to process...\n");
156 | status = HV::AttachToProcess(targetProcessId);
157 | if (!status)
158 | {
159 | printf("[!] Failed to attach\n");
160 | getchar();
161 | return EXIT_FAILURE;
162 | }
163 |
164 | printf("[+] Current process: EPROCESS -> 0x%llx CR3 -> 0x%llx\n", HV::Data::CurrentEPROCESS, HV::Data::CurrentDirectoryBase);
165 | printf("[+] Target process: EPROCESS -> 0x%llx CR3 -> 0x%llx\n", HV::Data::TargetEPROCESS, HV::Data::TargetDirectoryBase);
166 |
167 | printf("[>] Getting module base address...\n");
168 | MainModule = GetModule(targetProcessId, L"SystemInformer.exe");
169 | if (!MainModule)
170 | {
171 | printf("[!] Failed to get module base address\n");
172 | getchar();
173 | return EXIT_FAILURE;
174 | }
175 |
176 | printf("[+] Module is at 0x%llx\n", MainModule);
177 |
178 | printf("[>] Reading module header...\n");
179 | UINT64 header = HV::Read(MainModule);
180 | if (!header)
181 | {
182 | printf("[!] Failed to read header\n");
183 | getchar();
184 | return EXIT_FAILURE;
185 | }
186 |
187 | printf("[+] Header data: 0x%p\n", reinterpret_cast(header));
188 |
189 | printf("[>] Press any key to start multi-threaded test...\n");
190 | getchar();
191 |
192 | printf("[>] Multi-thread test...\n");
193 | for (int i = 0; i < 5; i++)
194 | {
195 | printf("[+] Starting ID %i...\n", i);
196 | std::thread thread(ThreadBench, i);
197 | thread.detach();
198 | Sleep(10);
199 | }
200 |
201 | getchar();
202 | return EXIT_SUCCESS;
203 | }
--------------------------------------------------------------------------------
/client/client/client.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 16.0
29 | Win32Proj
30 | {c9531a1c-c0f5-4db3-aa94-7286c2341a48}
31 | client
32 | 10.0
33 |
34 |
35 |
36 | Application
37 | true
38 | v143
39 | Unicode
40 |
41 |
42 | Application
43 | false
44 | v143
45 | true
46 | Unicode
47 |
48 |
49 | Application
50 | true
51 | v143
52 | Unicode
53 |
54 |
55 | Application
56 | false
57 | v143
58 | true
59 | Unicode
60 | Static
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | Level3
83 | true
84 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
85 | true
86 |
87 |
88 | Console
89 | true
90 |
91 |
92 |
93 |
94 | Level3
95 | true
96 | true
97 | true
98 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
99 | true
100 |
101 |
102 | Console
103 | true
104 | true
105 | true
106 |
107 |
108 |
109 |
110 | Level3
111 | true
112 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
113 | true
114 |
115 |
116 | Console
117 | true
118 |
119 |
120 |
121 |
122 | Level3
123 | true
124 | true
125 | true
126 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
127 | true
128 | stdcpp20
129 |
130 |
131 | Console
132 | true
133 | true
134 | true
135 | ntdll.lib;%(AdditionalDependencies)
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/client/client/client.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {558ccba3-1e48-49d6-a656-a8e252bda54b}
10 |
11 |
12 |
13 |
14 | Source
15 |
16 |
17 |
18 |
19 | Library
20 |
21 |
22 |
--------------------------------------------------------------------------------
/memhv/memhv.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.6.33815.320
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memhv", "memhv\memhv.vcxproj", "{6917B525-F951-4618-8F29-07896190CA75}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Release|x64 = Release|x64
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {6917B525-F951-4618-8F29-07896190CA75}.Release|x64.ActiveCfg = Release|x64
14 | {6917B525-F951-4618-8F29-07896190CA75}.Release|x64.Build.0 = Release|x64
15 | {6917B525-F951-4618-8F29-07896190CA75}.Release|x64.Deploy.0 = Release|x64
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | GlobalSection(ExtensibilityGlobals) = postSolution
21 | SolutionGuid = {67674E49-7964-4618-AD72-A89B758DB5D3}
22 | EndGlobalSection
23 | EndGlobal
24 |
--------------------------------------------------------------------------------
/memhv/memhv/Source/Entry.cpp:
--------------------------------------------------------------------------------
1 | #include "Global.h"
2 |
3 | NTSTATUS GetOffsets()
4 | {
5 | UNICODE_STRING stringPsGetProcessId;
6 | RtlInitUnicodeString(&stringPsGetProcessId, L"PsGetProcessId");
7 |
8 | BYTE* routinePsGetProcessId = static_cast(MmGetSystemRoutineAddress(&stringPsGetProcessId));
9 | if (!routinePsGetProcessId)
10 | return STATUS_NOT_FOUND;
11 |
12 | // PsGetProcessId proc near
13 | // mov rax, [rcx + 1D0h]
14 | // 48 8B 81 (D0 01 00 00)
15 | // VOID* UniqueProcessId;
16 | // struct _LIST_ENTRY ActiveProcessLinks;
17 | Global::Offsets::ActiveProcessLinks = *reinterpret_cast(routinePsGetProcessId + 3) + 8;
18 |
19 | return STATUS_SUCCESS;
20 | }
21 |
22 | NTSTATUS PreallocatePools()
23 | {
24 | for (UINT32 i = 0; i < ARRAYSIZE(Global::PreallocatedPools); i++)
25 | {
26 | Global::PreallocatedPools[i] = Utils::AllocatePageAligned(Utils::PreallocatedPoolSize);
27 | if (!Global::PreallocatedPools[i])
28 | return STATUS_INSUFFICIENT_RESOURCES;
29 | }
30 |
31 | return STATUS_SUCCESS;
32 | }
33 |
34 | EXTERN_C NTSTATUS Entry()
35 | {
36 | if (!Memory::PreparePages())
37 | return STATUS_INSUFFICIENT_RESOURCES;
38 |
39 | NTSTATUS status = SVM::CheckSupport();
40 | if (!NT_SUCCESS(status))
41 | return status;
42 |
43 | status = GetOffsets();
44 | if (!NT_SUCCESS(status))
45 | return status;
46 |
47 | status = PreallocatePools();
48 | if (!NT_SUCCESS(status))
49 | return status;
50 |
51 | Global::BlankPage = Utils::AllocatePageAligned(PAGE_SIZE);
52 | if (!Global::BlankPage)
53 | return STATUS_INSUFFICIENT_RESOURCES;
54 |
55 | HANDLE threadHandle;
56 | status = PsCreateSystemThread(&threadHandle, THREAD_ALL_ACCESS, nullptr, nullptr, nullptr, SVM::VirtualizeAllProcessors, nullptr);
57 | if (!NT_SUCCESS(status))
58 | return status;
59 |
60 | return status;
61 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/Global.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define NESTED_MODE true
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "Utils.h"
13 | #include "Shared.h"
14 |
15 | #include "Memory/Physical.h"
16 |
17 | #include "SVM/Defines/SVM_Platform.h"
18 | #include "SVM/Defines/SVM_NestedPaging.h"
19 | #include "SVM/Defines/SVM_ControlArea.h"
20 | #include "SVM/Defines/SVM_ProcessorData.h"
21 | #include "SVM/SVM.h"
22 | #include "SVM/Handlers/SVM_VMExit.h"
23 |
24 | namespace Global
25 | {
26 | inline PVOID BlankPage = nullptr;
27 | inline PVOID PreallocatedPools[32];
28 |
29 | namespace Offsets
30 | {
31 | inline ULONG64 ActiveProcessLinks = 0x448;
32 | }
33 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/Memory/Physical.cpp:
--------------------------------------------------------------------------------
1 | #include "../Global.h"
2 |
3 | constexpr UINT32 PageCount = 64;
4 | static Memory::PAGE_INFO PageList[PageCount];
5 |
6 | ULONG64 Memory::GetDirectoryBase(const PEPROCESS process)
7 | {
8 | return *reinterpret_cast(reinterpret_cast(process) + 0x28);
9 | }
10 |
11 | ULONG64 Memory::VirtualToPhysical(const ULONG64 virtualAddress)
12 | {
13 | return MmGetPhysicalAddress(reinterpret_cast(virtualAddress)).QuadPart;
14 | }
15 |
16 | ULONG64 Memory::PhysicalToVirtual(const ULONG64 physicalAddress)
17 | {
18 | PHYSICAL_ADDRESS physical;
19 | physical.QuadPart = physicalAddress;
20 | return reinterpret_cast(MmGetVirtualForPhysical(physical));
21 | }
22 |
23 | ULONG64 Memory::ResolveProcessPhysicalAddress(const UINT32 pageIndex, const ULONG64 directoryBase, const ULONG64 virtualAddress)
24 | {
25 | UNREFERENCED_PARAMETER(pageIndex);
26 |
27 | const ULONG64 original = __readcr3();
28 | __writecr3(directoryBase);
29 |
30 | ULONG64 result = VirtualToPhysical(virtualAddress);
31 |
32 | __writecr3(original);
33 |
34 | return result;
35 | }
36 |
37 | Memory::PTE* Memory::GetPte(const ULONG64 address)
38 | {
39 | VIRTUAL_ADDRESS virtualAddress;
40 | virtualAddress.Value = address;
41 |
42 | PTE_CR3 cr3;
43 | cr3.Value = __readcr3();
44 |
45 | PML4E* pml4 = reinterpret_cast(PhysicalToVirtual(PFN_TO_PAGE(cr3.Pml4)));
46 | const PML4E* pml4e = (pml4 + virtualAddress.Pml4Index);
47 | if (!pml4e->Present)
48 | return nullptr;
49 |
50 | PDPTE* pdpt = reinterpret_cast(PhysicalToVirtual(PFN_TO_PAGE(pml4e->Pdpt)));
51 | const PDPTE* pdpte = pdpte = (pdpt + virtualAddress.PdptIndex);
52 | if (!pdpte->Present)
53 | return nullptr;
54 |
55 | // sanity check 1GB page
56 | if (pdpte->PageSize)
57 | return nullptr;
58 |
59 | PDE* pd = reinterpret_cast(PhysicalToVirtual(PFN_TO_PAGE(pdpte->Pd)));
60 | const PDE* pde = pde = (pd + virtualAddress.PdIndex);
61 | if (!pde->Present)
62 | return nullptr;
63 |
64 | // sanity check 2MB page
65 | if (pde->PageSize)
66 | return nullptr;
67 |
68 | PTE* pt = reinterpret_cast(PhysicalToVirtual(PFN_TO_PAGE(pde->Pt)));
69 | PTE* pte = (pt + virtualAddress.PtIndex);
70 | if (!pte->Present)
71 | return nullptr;
72 |
73 | return pte;
74 | }
75 |
76 | bool Memory::PreparePage(PAGE_INFO* targetPage)
77 | {
78 | PHYSICAL_ADDRESS maxAddress;
79 | maxAddress.QuadPart = MAXULONG64;
80 |
81 | targetPage->VirtualAddress = MmAllocateContiguousMemory(PAGE_SIZE, maxAddress);
82 | if (!targetPage->VirtualAddress)
83 | return false;
84 |
85 | targetPage->CopyBuffer = ExAllocatePool(NonPagedPool, Shared::MAX_RW_SIZE);
86 | if (!targetPage->CopyBuffer)
87 | return false;
88 |
89 | targetPage->PageEntry = GetPte(reinterpret_cast(targetPage->VirtualAddress));
90 | if (!targetPage->PageEntry)
91 | return false;
92 |
93 | return true;
94 | }
95 |
96 | bool Memory::PreparePages()
97 | {
98 | for (UINT32 i = 0; i < PageCount; i++)
99 | {
100 | if (!PreparePage(&PageList[i]))
101 | return false;
102 | }
103 |
104 | return true;
105 | }
106 |
107 | PVOID Memory::OverwritePage(const UINT32 pageIndex, const ULONG64 physicalAddress)
108 | {
109 | const ULONG pageOffset = physicalAddress % PAGE_SIZE;
110 | const ULONG64 pageStartPhysical = physicalAddress - pageOffset;
111 |
112 | PAGE_INFO* pageInfo = &PageList[pageIndex];
113 | pageInfo->PreviousPageFrame = pageInfo->PageEntry->PageFrame;
114 | pageInfo->PageEntry->PageFrame = PAGE_TO_PFN(pageStartPhysical);
115 | __invlpg(pageInfo->VirtualAddress);
116 |
117 | return reinterpret_cast(reinterpret_cast(pageInfo->VirtualAddress) + pageOffset);
118 | }
119 |
120 | void Memory::RestorePage(const UINT32 pageIndex)
121 | {
122 | const PAGE_INFO* pageInfo = &PageList[pageIndex];
123 | pageInfo->PageEntry->PageFrame = pageInfo->PreviousPageFrame;
124 | __invlpg(pageInfo->VirtualAddress);
125 | }
126 |
127 | void Memory::ReadPhysicalAddress(const UINT32 pageIndex, const ULONG64 targetAddress, const PVOID buffer, const SIZE_T size)
128 | {
129 | const PVOID virtualAddress = OverwritePage(pageIndex, targetAddress);
130 | memcpy(buffer, virtualAddress, size);
131 | RestorePage(pageIndex);
132 | }
133 |
134 | void Memory::WritePhysicalAddress(const UINT32 pageIndex, const ULONG64 targetAddress, const PVOID buffer, const SIZE_T size)
135 | {
136 | const PVOID virtualAddress = OverwritePage(pageIndex, targetAddress);
137 | memcpy(virtualAddress, buffer, size);
138 | RestorePage(pageIndex);
139 | }
140 |
141 | NTSTATUS Memory::ReadProcessMemory(const UINT32 pageIndex, const ULONG64 directoryBase, const ULONG64 address, PVOID buffer, const SIZE_T size, SIZE_T* bytesRead)
142 | {
143 | SIZE_T currentOffset = 0;
144 | SIZE_T totalSize = size;
145 | while (totalSize)
146 | {
147 | const ULONG64 currentPhysicalAddress = ResolveProcessPhysicalAddress(pageIndex, directoryBase, address + currentOffset);
148 | if (!currentPhysicalAddress)
149 | return STATUS_NOT_FOUND;
150 |
151 | const ULONG64 readSize = min(PAGE_SIZE - (currentPhysicalAddress & 0xFFF), totalSize);
152 |
153 | ReadPhysicalAddress(pageIndex, currentPhysicalAddress, reinterpret_cast(reinterpret_cast(buffer) + currentOffset), readSize);
154 |
155 | totalSize -= readSize;
156 | currentOffset += readSize;
157 | }
158 |
159 | *bytesRead = currentOffset;
160 |
161 | return STATUS_SUCCESS;
162 | }
163 |
164 | NTSTATUS Memory::WriteProcessMemory(const UINT32 pageIndex, const ULONG64 directoryBase, const ULONG64 address, PVOID buffer, const SIZE_T size, SIZE_T* bytesWritten)
165 | {
166 | SIZE_T currentOffset = 0;
167 | SIZE_T totalSize = size;
168 | while (totalSize)
169 | {
170 | const ULONG64 currentPhysicalAddress = ResolveProcessPhysicalAddress(pageIndex, directoryBase, address + currentOffset);
171 | if (!currentPhysicalAddress)
172 | return STATUS_NOT_FOUND;
173 |
174 | const ULONG64 writeSize = min(PAGE_SIZE - (currentPhysicalAddress & 0xFFF), totalSize);
175 |
176 | WritePhysicalAddress(pageIndex, currentPhysicalAddress, reinterpret_cast(reinterpret_cast(buffer) + currentOffset), writeSize);
177 |
178 | totalSize -= writeSize;
179 | currentOffset += writeSize;
180 | }
181 |
182 | *bytesWritten = currentOffset;
183 |
184 | return STATUS_SUCCESS;
185 | }
186 |
187 | NTSTATUS Memory::CopyProcessMemory(const UINT32 pageIndex, const ULONG64 sourceDirectoryBase, const ULONG64 sourceAddress, const ULONG64 targetDirectoryBase, const ULONG64 targetAddress, const SIZE_T bufferSize)
188 | {
189 | if (!Utils::ValidUsermodeAddress(sourceAddress))
190 | return STATUS_INVALID_ADDRESS;
191 |
192 | if (!Utils::ValidUsermodeAddress(targetAddress))
193 | return STATUS_INVALID_ADDRESS;
194 |
195 | const PAGE_INFO* pageInfo = &PageList[pageIndex];
196 |
197 | SIZE_T bytes = 0;
198 | NTSTATUS status = ReadProcessMemory(pageIndex, sourceDirectoryBase, sourceAddress, pageInfo->CopyBuffer, bufferSize, &bytes);
199 | if (!NT_SUCCESS(status))
200 | return STATUS_ABANDONED;
201 |
202 | return WriteProcessMemory(pageIndex, targetDirectoryBase, targetAddress, pageInfo->CopyBuffer, bufferSize, &bytes);
203 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/Memory/Physical.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace Memory
4 | {
5 | #define PFN_TO_PAGE(pfn) (pfn << PAGE_SHIFT)
6 | #define PAGE_TO_PFN(pfn) (pfn >> PAGE_SHIFT)
7 |
8 | #pragma warning(push)
9 | #pragma warning(disable : 4201) // nonstandard extension used: nameless struct/union
10 |
11 | #pragma pack(push, 1)
12 | typedef union CR3_
13 | {
14 | ULONG64 Value;
15 | struct
16 | {
17 | ULONG64 Ignored1 : 3;
18 | ULONG64 WriteThrough : 1;
19 | ULONG64 CacheDisable : 1;
20 | ULONG64 Ignored2 : 7;
21 | ULONG64 Pml4 : 40;
22 | ULONG64 Reserved : 12;
23 | };
24 | } PTE_CR3;
25 |
26 | typedef union VIRT_ADDR_
27 | {
28 | ULONG64 Value;
29 | void* Pointer;
30 | struct
31 | {
32 | ULONG64 Offset : 12;
33 | ULONG64 PtIndex : 9;
34 | ULONG64 PdIndex : 9;
35 | ULONG64 PdptIndex : 9;
36 | ULONG64 Pml4Index : 9;
37 | ULONG64 Reserved : 16;
38 | };
39 | } VIRTUAL_ADDRESS;
40 |
41 | typedef union PML4E_
42 | {
43 | ULONG64 Value;
44 | struct
45 | {
46 | ULONG64 Present : 1;
47 | ULONG64 Rw : 1;
48 | ULONG64 User : 1;
49 | ULONG64 WriteThrough : 1;
50 | ULONG64 CacheDisable : 1;
51 | ULONG64 Accessed : 1;
52 | ULONG64 Ignored1 : 1;
53 | ULONG64 Reserved1 : 1;
54 | ULONG64 Ignored2 : 4;
55 | ULONG64 Pdpt : 40;
56 | ULONG64 Ignored3 : 11;
57 | ULONG64 Xd : 1;
58 | };
59 | } PML4E;
60 |
61 | typedef union PDPTE_
62 | {
63 | ULONG64 Value;
64 | struct
65 | {
66 | ULONG64 Present : 1;
67 | ULONG64 Rw : 1;
68 | ULONG64 User : 1;
69 | ULONG64 WriteThrough : 1;
70 | ULONG64 CacheDisable : 1;
71 | ULONG64 Accessed : 1;
72 | ULONG64 Dirty : 1;
73 | ULONG64 PageSize : 1;
74 | ULONG64 Ignored2 : 4;
75 | ULONG64 Pd : 40;
76 | ULONG64 Ignored3 : 11;
77 | ULONG64 Xd : 1;
78 | };
79 | } PDPTE;
80 |
81 | typedef union PDE_
82 | {
83 | ULONG64 Value;
84 | struct
85 | {
86 | ULONG64 Present : 1;
87 | ULONG64 Rw : 1;
88 | ULONG64 User : 1;
89 | ULONG64 WriteThrough : 1;
90 | ULONG64 CacheDisable : 1;
91 | ULONG64 Accessed : 1;
92 | ULONG64 Dirty : 1;
93 | ULONG64 PageSize : 1;
94 | ULONG64 Ignored2 : 4;
95 | ULONG64 Pt : 40;
96 | ULONG64 Ignored3 : 11;
97 | ULONG64 Xd : 1;
98 | };
99 | } PDE;
100 |
101 | typedef union PTE_
102 | {
103 | ULONG64 Value;
104 | VIRTUAL_ADDRESS VirtualAddress;
105 | struct
106 | {
107 | ULONG64 Present : 1;
108 | ULONG64 Rw : 1;
109 | ULONG64 User : 1;
110 | ULONG64 WriteThrough : 1;
111 | ULONG64 CacheDisable : 1;
112 | ULONG64 Accessed : 1;
113 | ULONG64 Dirty : 1;
114 | ULONG64 Pat : 1;
115 | ULONG64 Global : 1;
116 | ULONG64 Ignored1 : 3;
117 | ULONG64 PageFrame : 40;
118 | ULONG64 Ignored3 : 11;
119 | ULONG64 Xd : 1;
120 | };
121 | } PTE;
122 |
123 | typedef struct _MMPTE_HARDWARE
124 | {
125 | struct /* bitfield */
126 | {
127 | /* 0x0000 */ unsigned __int64 Valid : 1; /* bit position: 0 */
128 | /* 0x0000 */ unsigned __int64 Dirty1 : 1; /* bit position: 1 */
129 | /* 0x0000 */ unsigned __int64 Owner : 1; /* bit position: 2 */
130 | /* 0x0000 */ unsigned __int64 WriteThrough : 1; /* bit position: 3 */
131 | /* 0x0000 */ unsigned __int64 CacheDisable : 1; /* bit position: 4 */
132 | /* 0x0000 */ unsigned __int64 Accessed : 1; /* bit position: 5 */
133 | /* 0x0000 */ unsigned __int64 Dirty : 1; /* bit position: 6 */
134 | /* 0x0000 */ unsigned __int64 LargePage : 1; /* bit position: 7 */
135 | /* 0x0000 */ unsigned __int64 Global : 1; /* bit position: 8 */
136 | /* 0x0000 */ unsigned __int64 CopyOnWrite : 1; /* bit position: 9 */
137 | /* 0x0000 */ unsigned __int64 Unused : 1; /* bit position: 10 */
138 | /* 0x0000 */ unsigned __int64 Write : 1; /* bit position: 11 */
139 | /* 0x0000 */ unsigned __int64 PageFrameNumber : 36; /* bit position: 12 */
140 | /* 0x0000 */ unsigned __int64 ReservedForHardware : 4; /* bit position: 48 */
141 | /* 0x0000 */ unsigned __int64 ReservedForSoftware : 4; /* bit position: 52 */
142 | /* 0x0000 */ unsigned __int64 WsleAge : 4; /* bit position: 56 */
143 | /* 0x0000 */ unsigned __int64 WsleProtection : 3; /* bit position: 60 */
144 | /* 0x0000 */ unsigned __int64 NoExecute : 1; /* bit position: 63 */
145 | }; /* bitfield */
146 | } MMPTE_HARDWARE, * PMMPTE_HARDWARE; /* size: 0x0008 */
147 |
148 | typedef struct _MMPTE_PROTOTYPE
149 | {
150 | struct /* bitfield */
151 | {
152 | /* 0x0000 */ unsigned __int64 Valid : 1; /* bit position: 0 */
153 | /* 0x0000 */ unsigned __int64 DemandFillProto : 1; /* bit position: 1 */
154 | /* 0x0000 */ unsigned __int64 HiberVerifyConverted : 1; /* bit position: 2 */
155 | /* 0x0000 */ unsigned __int64 ReadOnly : 1; /* bit position: 3 */
156 | /* 0x0000 */ unsigned __int64 SwizzleBit : 1; /* bit position: 4 */
157 | /* 0x0000 */ unsigned __int64 Protection : 5; /* bit position: 5 */
158 | /* 0x0000 */ unsigned __int64 Prototype : 1; /* bit position: 10 */
159 | /* 0x0000 */ unsigned __int64 Combined : 1; /* bit position: 11 */
160 | /* 0x0000 */ unsigned __int64 Unused1 : 4; /* bit position: 12 */
161 | /* 0x0000 */ __int64 ProtoAddress : 48; /* bit position: 16 */
162 | }; /* bitfield */
163 | } MMPTE_PROTOTYPE, * PMMPTE_PROTOTYPE; /* size: 0x0008 */
164 |
165 | typedef struct _MMPTE_SOFTWARE
166 | {
167 | struct /* bitfield */
168 | {
169 | /* 0x0000 */ unsigned __int64 Valid : 1; /* bit position: 0 */
170 | /* 0x0000 */ unsigned __int64 PageFileReserved : 1; /* bit position: 1 */
171 | /* 0x0000 */ unsigned __int64 PageFileAllocated : 1; /* bit position: 2 */
172 | /* 0x0000 */ unsigned __int64 ColdPage : 1; /* bit position: 3 */
173 | /* 0x0000 */ unsigned __int64 SwizzleBit : 1; /* bit position: 4 */
174 | /* 0x0000 */ unsigned __int64 Protection : 5; /* bit position: 5 */
175 | /* 0x0000 */ unsigned __int64 Prototype : 1; /* bit position: 10 */
176 | /* 0x0000 */ unsigned __int64 Transition : 1; /* bit position: 11 */
177 | /* 0x0000 */ unsigned __int64 PageFileLow : 4; /* bit position: 12 */
178 | /* 0x0000 */ unsigned __int64 UsedPageTableEntries : 10; /* bit position: 16 */
179 | /* 0x0000 */ unsigned __int64 ShadowStack : 1; /* bit position: 26 */
180 | /* 0x0000 */ unsigned __int64 Unused : 5; /* bit position: 27 */
181 | /* 0x0000 */ unsigned __int64 PageFileHigh : 32; /* bit position: 32 */
182 | }; /* bitfield */
183 | } MMPTE_SOFTWARE, * PMMPTE_SOFTWARE; /* size: 0x0008 */
184 |
185 | typedef struct _MMPTE_TIMESTAMP
186 | {
187 | struct /* bitfield */
188 | {
189 | /* 0x0000 */ unsigned __int64 MustBeZero : 1; /* bit position: 0 */
190 | /* 0x0000 */ unsigned __int64 Unused : 3; /* bit position: 1 */
191 | /* 0x0000 */ unsigned __int64 SwizzleBit : 1; /* bit position: 4 */
192 | /* 0x0000 */ unsigned __int64 Protection : 5; /* bit position: 5 */
193 | /* 0x0000 */ unsigned __int64 Prototype : 1; /* bit position: 10 */
194 | /* 0x0000 */ unsigned __int64 Transition : 1; /* bit position: 11 */
195 | /* 0x0000 */ unsigned __int64 PageFileLow : 4; /* bit position: 12 */
196 | /* 0x0000 */ unsigned __int64 Reserved : 16; /* bit position: 16 */
197 | /* 0x0000 */ unsigned __int64 GlobalTimeStamp : 32; /* bit position: 32 */
198 | }; /* bitfield */
199 | } MMPTE_TIMESTAMP, * PMMPTE_TIMESTAMP; /* size: 0x0008 */
200 |
201 | typedef struct _MMPTE_TRANSITION
202 | {
203 | struct /* bitfield */
204 | {
205 | /* 0x0000 */ unsigned __int64 Valid : 1; /* bit position: 0 */
206 | /* 0x0000 */ unsigned __int64 Write : 1; /* bit position: 1 */
207 | /* 0x0000 */ unsigned __int64 Spare : 1; /* bit position: 2 */
208 | /* 0x0000 */ unsigned __int64 IoTracker : 1; /* bit position: 3 */
209 | /* 0x0000 */ unsigned __int64 SwizzleBit : 1; /* bit position: 4 */
210 | /* 0x0000 */ unsigned __int64 Protection : 5; /* bit position: 5 */
211 | /* 0x0000 */ unsigned __int64 Prototype : 1; /* bit position: 10 */
212 | /* 0x0000 */ unsigned __int64 Transition : 1; /* bit position: 11 */
213 | /* 0x0000 */ unsigned __int64 PageFrameNumber : 36; /* bit position: 12 */
214 | /* 0x0000 */ unsigned __int64 Unused : 16; /* bit position: 48 */
215 | }; /* bitfield */
216 | } MMPTE_TRANSITION, * PMMPTE_TRANSITION; /* size: 0x0008 */
217 |
218 | typedef struct _MMPTE_SUBSECTION
219 | {
220 | struct /* bitfield */
221 | {
222 | /* 0x0000 */ unsigned __int64 Valid : 1; /* bit position: 0 */
223 | /* 0x0000 */ unsigned __int64 Unused0 : 3; /* bit position: 1 */
224 | /* 0x0000 */ unsigned __int64 SwizzleBit : 1; /* bit position: 4 */
225 | /* 0x0000 */ unsigned __int64 Protection : 5; /* bit position: 5 */
226 | /* 0x0000 */ unsigned __int64 Prototype : 1; /* bit position: 10 */
227 | /* 0x0000 */ unsigned __int64 ColdPage : 1; /* bit position: 11 */
228 | /* 0x0000 */ unsigned __int64 Unused1 : 3; /* bit position: 12 */
229 | /* 0x0000 */ unsigned __int64 ExecutePrivilege : 1; /* bit position: 15 */
230 | /* 0x0000 */ __int64 SubsectionAddress : 48; /* bit position: 16 */
231 | }; /* bitfield */
232 | } MMPTE_SUBSECTION, * PMMPTE_SUBSECTION; /* size: 0x0008 */
233 |
234 | typedef struct _MMPTE_LIST
235 | {
236 | struct /* bitfield */
237 | {
238 | /* 0x0000 */ unsigned __int64 Valid : 1; /* bit position: 0 */
239 | /* 0x0000 */ unsigned __int64 OneEntry : 1; /* bit position: 1 */
240 | /* 0x0000 */ unsigned __int64 filler0 : 2; /* bit position: 2 */
241 | /* 0x0000 */ unsigned __int64 SwizzleBit : 1; /* bit position: 4 */
242 | /* 0x0000 */ unsigned __int64 Protection : 5; /* bit position: 5 */
243 | /* 0x0000 */ unsigned __int64 Prototype : 1; /* bit position: 10 */
244 | /* 0x0000 */ unsigned __int64 Transition : 1; /* bit position: 11 */
245 | /* 0x0000 */ unsigned __int64 filler1 : 16; /* bit position: 12 */
246 | /* 0x0000 */ unsigned __int64 NextEntry : 36; /* bit position: 28 */
247 | }; /* bitfield */
248 | } MMPTE_LIST, * PMMPTE_LIST; /* size: 0x0008 */
249 |
250 | typedef struct _MMPTE
251 | {
252 | union
253 | {
254 | union
255 | {
256 | /* 0x0000 */ unsigned __int64 Long;
257 | /* 0x0000 */ volatile unsigned __int64 VolatileLong;
258 | /* 0x0000 */ struct _MMPTE_HARDWARE Hard;
259 | /* 0x0000 */ struct _MMPTE_PROTOTYPE Proto;
260 | /* 0x0000 */ struct _MMPTE_SOFTWARE Soft;
261 | /* 0x0000 */ struct _MMPTE_TIMESTAMP TimeStamp;
262 | /* 0x0000 */ struct _MMPTE_TRANSITION Trans;
263 | /* 0x0000 */ struct _MMPTE_SUBSECTION Subsect;
264 | /* 0x0000 */ struct _MMPTE_LIST List;
265 | }; /* size: 0x0008 */
266 | } /* size: 0x0008 */ u;
267 | } MMPTE, * PMMPTE; /* size: 0x0008 */
268 | #pragma pack(pop)
269 |
270 | typedef struct _PAGE_INFO
271 | {
272 | PVOID VirtualAddress;
273 | PTE* PageEntry;
274 | ULONG64 PreviousPageFrame;
275 | PVOID CopyBuffer;
276 | } PAGE_INFO;
277 |
278 | ULONG64 GetDirectoryBase(PEPROCESS process);
279 | ULONG64 VirtualToPhysical(ULONG64 virtualAddress);
280 | ULONG64 PhysicalToVirtual(ULONG64 physicalAddress);
281 | ULONG64 ResolveProcessPhysicalAddress(UINT32 pageIndex, ULONG64 directoryBase, ULONG64 virtualAddress);
282 | PTE* GetPte(ULONG64 address);
283 | bool PreparePage(PAGE_INFO* targetPage);
284 | bool PreparePages();
285 | PVOID OverwritePage(UINT32 pageIndex, ULONG64 physicalAddress);
286 | void RestorePage(UINT32 pageIndex);
287 | void ReadPhysicalAddress(UINT32 pageIndex, ULONG64 targetAddress, PVOID buffer, SIZE_T size);
288 | void WritePhysicalAddress(UINT32 pageIndex, ULONG64 targetAddress, PVOID buffer, SIZE_T size);
289 | NTSTATUS ReadProcessMemory(UINT32 pageIndex, ULONG64 directoryBase, ULONG64 address, PVOID buffer, SIZE_T size, SIZE_T* bytesRead);
290 | NTSTATUS WriteProcessMemory(UINT32 pageIndex, ULONG64 directoryBase, ULONG64 address, PVOID buffer, SIZE_T size, SIZE_T* bytesWritten);
291 | NTSTATUS CopyProcessMemory(UINT32 pageIndex, ULONG64 sourceDirectoryBase, ULONG64 sourceAddress, ULONG64 targetDirectoryBase, ULONG64 targetAddress, SIZE_T bufferSize);
292 | }
293 |
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Defines/SVM_ControlArea.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace SVM
4 | {
5 | typedef struct _VMCB_CONTROL_AREA
6 | {
7 | UINT16 InterceptCrRead; // +0x000
8 | UINT16 InterceptCrWrite; // +0x002
9 | UINT16 InterceptDrRead; // +0x004
10 | UINT16 InterceptDrWrite; // +0x006
11 | UINT32 InterceptException; // +0x008
12 | UINT32 InterceptMisc1; // +0x00c
13 | UINT32 InterceptMisc2; // +0x010
14 | UINT8 Reserved1[0x03c - 0x014]; // +0x014
15 | UINT16 PauseFilterThreshold; // +0x03c
16 | UINT16 PauseFilterCount; // +0x03e
17 | UINT64 IopmBasePa; // +0x040
18 | UINT64 MsrpmBasePa; // +0x048
19 | UINT64 TscOffset; // +0x050
20 | UINT32 GuestAsid; // +0x058
21 | UINT32 TlbControl; // +0x05c
22 | UINT64 VIntr; // +0x060
23 | UINT64 InterruptShadow; // +0x068
24 | UINT64 ExitCode; // +0x070
25 | UINT64 ExitInfo1; // +0x078
26 | UINT64 ExitInfo2; // +0x080
27 | UINT64 ExitIntInfo; // +0x088
28 | UINT64 NpEnable; // +0x090
29 | UINT64 AvicApicBar; // +0x098
30 | UINT64 GuestPaOfGhcb; // +0x0a0
31 | UINT64 EventInj; // +0x0a8
32 | UINT64 NCr3; // +0x0b0
33 | UINT64 LbrVirtualizationEnable; // +0x0b8
34 | UINT64 VmcbClean; // +0x0c0
35 | UINT64 NRip; // +0x0c8
36 | UINT8 NumOfBytesFetched; // +0x0d0
37 | UINT8 GuestInstructionBytes[15]; // +0x0d1
38 | UINT64 AvicApicBackingPagePointer; // +0x0e0
39 | UINT64 Reserved2; // +0x0e8
40 | UINT64 AvicLogicalTablePointer; // +0x0f0
41 | UINT64 AvicPhysicalTablePointer; // +0x0f8
42 | UINT64 Reserved3; // +0x100
43 | UINT64 VmcbSaveStatePointer; // +0x108
44 | UINT8 Reserved4[0x400 - 0x110]; // +0x110
45 | } VMCB_CONTROL_AREA, * PVMCB_CONTROL_AREA;
46 | static_assert(sizeof(VMCB_CONTROL_AREA) == 0x400, "VMCB_CONTROL_AREA size mismatch");
47 |
48 | typedef struct _VMCB_STATE_SAVE_AREA
49 | {
50 | UINT16 EsSelector; // +0x000
51 | UINT16 EsAttrib; // +0x002
52 | UINT32 EsLimit; // +0x004
53 | UINT64 EsBase; // +0x008
54 | UINT16 CsSelector; // +0x010
55 | UINT16 CsAttrib; // +0x012
56 | UINT32 CsLimit; // +0x014
57 | UINT64 CsBase; // +0x018
58 | UINT16 SsSelector; // +0x020
59 | UINT16 SsAttrib; // +0x022
60 | UINT32 SsLimit; // +0x024
61 | UINT64 SsBase; // +0x028
62 | UINT16 DsSelector; // +0x030
63 | UINT16 DsAttrib; // +0x032
64 | UINT32 DsLimit; // +0x034
65 | UINT64 DsBase; // +0x038
66 | UINT16 FsSelector; // +0x040
67 | UINT16 FsAttrib; // +0x042
68 | UINT32 FsLimit; // +0x044
69 | UINT64 FsBase; // +0x048
70 | UINT16 GsSelector; // +0x050
71 | UINT16 GsAttrib; // +0x052
72 | UINT32 GsLimit; // +0x054
73 | UINT64 GsBase; // +0x058
74 | UINT16 GdtrSelector; // +0x060
75 | UINT16 GdtrAttrib; // +0x062
76 | UINT32 GdtrLimit; // +0x064
77 | UINT64 GdtrBase; // +0x068
78 | UINT16 LdtrSelector; // +0x070
79 | UINT16 LdtrAttrib; // +0x072
80 | UINT32 LdtrLimit; // +0x074
81 | UINT64 LdtrBase; // +0x078
82 | UINT16 IdtrSelector; // +0x080
83 | UINT16 IdtrAttrib; // +0x082
84 | UINT32 IdtrLimit; // +0x084
85 | UINT64 IdtrBase; // +0x088
86 | UINT16 TrSelector; // +0x090
87 | UINT16 TrAttrib; // +0x092
88 | UINT32 TrLimit; // +0x094
89 | UINT64 TrBase; // +0x098
90 | UINT8 Reserved1[0x0cb - 0x0a0]; // +0x0a0
91 | UINT8 Cpl; // +0x0cb
92 | UINT32 Reserved2; // +0x0cc
93 | UINT64 Efer; // +0x0d0
94 | UINT8 Reserved3[0x148 - 0x0d8]; // +0x0d8
95 | UINT64 Cr4; // +0x148
96 | UINT64 Cr3; // +0x150
97 | UINT64 Cr0; // +0x158
98 | UINT64 Dr7; // +0x160
99 | UINT64 Dr6; // +0x168
100 | UINT64 Rflags; // +0x170
101 | UINT64 Rip; // +0x178
102 | UINT8 Reserved4[0x1d8 - 0x180]; // +0x180
103 | UINT64 Rsp; // +0x1d8
104 | UINT8 Reserved5[0x1f8 - 0x1e0]; // +0x1e0
105 | UINT64 Rax; // +0x1f8
106 | UINT64 Star; // +0x200
107 | UINT64 LStar; // +0x208
108 | UINT64 CStar; // +0x210
109 | UINT64 SfMask; // +0x218
110 | UINT64 KernelGsBase; // +0x220
111 | UINT64 SysenterCs; // +0x228
112 | UINT64 SysenterEsp; // +0x230
113 | UINT64 SysenterEip; // +0x238
114 | UINT64 Cr2; // +0x240
115 | UINT8 Reserved6[0x268 - 0x248]; // +0x248
116 | UINT64 GPat; // +0x268
117 | UINT64 DbgCtl; // +0x270
118 | UINT64 BrFrom; // +0x278
119 | UINT64 BrTo; // +0x280
120 | UINT64 LastExcepFrom; // +0x288
121 | UINT64 LastExcepTo; // +0x290
122 | } VMCB_STATE_SAVE_AREA, * PVMCB_STATE_SAVE_AREA;
123 | static_assert(sizeof(VMCB_STATE_SAVE_AREA) == 0x298, "VMCB_STATE_SAVE_AREA size mismatch");
124 |
125 | typedef struct _VMCB
126 | {
127 | VMCB_CONTROL_AREA ControlArea;
128 | VMCB_STATE_SAVE_AREA StateSaveArea;
129 | UINT8 Reserved1[0x1000 - sizeof(VMCB_CONTROL_AREA) - sizeof(VMCB_STATE_SAVE_AREA)];
130 | } VMCB, * PVMCB;
131 | static_assert(sizeof(VMCB) == 0x1000, "VMCB size mismatch");
132 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Defines/SVM_NestedPaging.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace SVM
4 | {
5 | typedef struct _APIC_BASE
6 | {
7 | union
8 | {
9 | UINT64 AsUInt64;
10 | struct
11 | {
12 | UINT64 Reserved1 : 8; // [0:7]
13 | UINT64 BootstrapProcessor : 1; // [8]
14 | UINT64 Reserved2 : 1; // [9]
15 | UINT64 EnableX2ApicMode : 1; // [10]
16 | UINT64 EnableXApicGlobal : 1; // [11]
17 | UINT64 ApicBase : 24; // [12:35]
18 | };
19 | };
20 | } APIC_BASE, * PAPIC_BASE;
21 |
22 | typedef struct _PML4_ENTRY_4KB
23 | {
24 | union
25 | {
26 | UINT64 AsUInt64;
27 | struct
28 | {
29 | UINT64 Present : 1;
30 | UINT64 Write : 1;
31 | UINT64 Supervisor : 1;
32 | UINT64 WriteThrough : 1;
33 | UINT64 CacheDisable : 1;
34 | UINT64 Accessed : 1;
35 | UINT64 Reserved1 : 3;
36 | UINT64 Avl : 3;
37 | UINT64 PageFrameNumber : 36;
38 | UINT64 Reserved2 : 15;
39 | UINT64 NoExecute : 1;
40 | };
41 | };
42 | } PML4_ENTRY_4KB, * PPML4_ENTRY_4KB,
43 | PDP_ENTRY_4KB, * PPDP_ENTRY_4KB,
44 | PT_ENTRY_4KB, * PPT_ENTRY_4KB;
45 | static_assert(sizeof(PML4_ENTRY_4KB) == 8, "size mismatch");
46 |
47 | typedef struct _PD_ENTRY_2MB
48 | {
49 | union
50 | {
51 | UINT64 AsUInt64;
52 | struct
53 | {
54 | UINT64 Present : 1;
55 | UINT64 Write : 1;
56 | UINT64 Supervisor : 1;
57 | UINT64 WriteThrough : 1;
58 | UINT64 CacheDisable : 1;
59 | UINT64 Accessed : 1;
60 | UINT64 Dirty : 1;
61 | UINT64 LargePage : 1;
62 | UINT64 Global : 1;
63 | UINT64 Avl : 3;
64 | UINT64 Pat : 1;
65 | UINT64 Reserved1 : 8;
66 | UINT64 PageFrameNumber : 27;
67 | UINT64 Reserved2 : 15;
68 | UINT64 NoExecute : 1;
69 | };
70 | };
71 | } PD_ENTRY_2MB, * PPD_ENTRY_2MB;
72 | static_assert(sizeof(PD_ENTRY_2MB) == 8, "PD_ENTRY_2MB size mismatch");
73 |
74 | typedef union
75 | {
76 | struct
77 | {
78 | UINT64 Present : 1;
79 | UINT64 Write : 1;
80 | UINT64 Supervisor : 1;
81 | UINT64 PageLevelWriteThrough : 1;
82 | UINT64 PageLevelCacheDisable : 1;
83 | UINT64 Accessed : 1;
84 | UINT64 Reserved1 : 1;
85 | UINT64 LargePage : 1;
86 | UINT64 Ignored1 : 3;
87 | UINT64 Restart : 1;
88 | UINT64 PageFrameNumber : 36;
89 | UINT64 Reserved2 : 4;
90 | UINT64 Ignored2 : 11;
91 | UINT64 ExecuteDisable : 1;
92 | };
93 |
94 | UINT64 AsUInt;
95 | } PDE_4KB;
96 | static_assert(sizeof(PDE_4KB) == 8, "PDE_4KB size mismatch");
97 |
98 | typedef union _PDPTE_1GB
99 | {
100 | struct
101 | {
102 | UINT64 Present : 1;
103 | UINT64 Write : 1;
104 | UINT64 Supervisor : 1;
105 | UINT64 PageLevelWriteThrough : 1;
106 | UINT64 PageLevelCacheDisable : 1;
107 | UINT64 Accessed : 1;
108 | UINT64 Dirty : 1;
109 | UINT64 LargePage : 1;
110 | UINT64 Global : 1;
111 | UINT64 Ignored1 : 2;
112 | UINT64 Restart : 1;
113 | UINT64 Pat : 1;
114 | UINT64 Reserved1 : 17;
115 | UINT64 PageFrameNumber : 18;
116 | UINT64 Reserved2 : 4;
117 | UINT64 Ignored2 : 7;
118 | UINT64 ProtectionKey : 4;
119 | UINT64 ExecuteDisable : 1;
120 | };
121 | UINT64 AsUInt;
122 | } PDPTE_1GB;
123 | static_assert(sizeof(PDPTE_1GB) == 8, "PDPTE_1GB size mismatch");
124 |
125 | typedef union
126 | {
127 | struct
128 | {
129 | UINT64 Present : 1;
130 | UINT64 Write : 1;
131 | UINT64 Supervisor : 1;
132 | UINT64 PageLevelWriteThrough : 1;
133 | UINT64 PageLevelCacheDisable : 1;
134 | UINT64 Accessed : 1;
135 | UINT64 Reserved1 : 1;
136 | UINT64 LargePage : 1;
137 | UINT64 Ignored1 : 3;
138 | UINT64 Restart : 1;
139 | UINT64 PageFrameNumber : 36;
140 | UINT64 Reserved2 : 4;
141 | UINT64 Ignored2 : 11;
142 | UINT64 ExecuteDisable : 1;
143 | };
144 |
145 | UINT64 AsUInt;
146 | } PDPTE_2MB;
147 | static_assert(sizeof(PDPTE_2MB) == 8, "PDPTE_2MB size mismatch");
148 |
149 | typedef union _PML4E_64
150 | {
151 | struct
152 | {
153 | UINT64 Present : 1;
154 | UINT64 Write : 1;
155 | UINT64 Supervisor : 1;
156 | UINT64 PageLevelWriteThrough : 1;
157 | UINT64 PageLevelCacheDisable : 1;
158 | UINT64 Accessed : 1;
159 | UINT64 Reserved1 : 1;
160 | UINT64 MustBeZero : 1;
161 | UINT64 Ignored1 : 3;
162 | UINT64 Restart : 1;
163 | UINT64 PageFrameNumber : 36;
164 | UINT64 Reserved2 : 4;
165 | UINT64 Ignored2 : 11;
166 | UINT64 ExecuteDisable : 1;
167 | };
168 | UINT64 AsUInt;
169 | } PML4E_64;
170 | static_assert(sizeof(PML4E_64) == 8, "PML4E_64 size mismatch");
171 |
172 | #include
173 | typedef struct _DESCRIPTOR_TABLE_REGISTER
174 | {
175 | UINT16 Limit;
176 | ULONG_PTR Base;
177 | } DESCRIPTOR_TABLE_REGISTER, * PDESCRIPTOR_TABLE_REGISTER;
178 | static_assert(sizeof(DESCRIPTOR_TABLE_REGISTER) == 10,
179 | "DESCRIPTOR_TABLE_REGISTER size mismatch");
180 | #include
181 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Defines/SVM_Platform.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define INDEX_MASK 0x1FF
4 | #define PD_PAGE_SHIFT 21
5 | #define PML4_PAGE_SHIFT 39
6 | #define PDP_PAGE_SHIFT 30
7 |
8 | #define PAGE_SIZE_4KB 0x1000
9 | #define PAGE_SIZE_2MB 0x200000
10 | #define PAGE_SIZE_1GB 0x40000000
11 |
12 | #define IA32_APIC_BASE 0x0000001b
13 | #define IA32_MSR_PAT 0x00000277
14 | #define IA32_MSR_EFER 0xc0000080
15 |
16 | #define EFER_SVME (1UL << 12)
17 |
18 | #define RPL_MASK 3
19 | #define DPL_SYSTEM 0
20 |
21 | #define CPUID_FN8000_0001_ECX_SVM (1UL << 2)
22 | #define CPUID_FN0000_0001_ECX_HYPERVISOR_PRESENT (1UL << 31)
23 | #define CPUID_FN8000_000A_EDX_NP (1UL << 0)
24 |
25 | #define CPUID_MAX_STANDARD_FN_NUMBER_AND_VENDOR_STRING 0x00000000
26 | #define CPUID_PROCESSOR_AND_PROCESSOR_FEATURE_IDENTIFIERS 0x00000001
27 | #define CPUID_PROCESSOR_AND_PROCESSOR_FEATURE_IDENTIFIERS_EX 0x80000001
28 | #define CPUID_SVM_FEATURES 0x8000000a
29 | #define CPUID_SVM_FEATURES_EX 0x80000001
30 |
31 | #define SVM_MSR_VM_CR 0xc0010114
32 | #define SVM_MSR_VM_HSAVE_PA 0xc0010117
33 |
34 | #define SVM_VM_CR_SVMDIS (1UL << 4)
35 |
36 | #define SVM_MSR_PERMISSIONS_MAP_SIZE (PAGE_SIZE * 2)
37 |
38 | // Page 120
39 | // http://www.0x04.net/doc/amd/33047.pdf
40 | #define SVM_INTERCEPT_MISC1_CPUID (1UL << 18)
41 | #define SVM_INTERCEPT_MISC1_RDTSC (1UL << 14)
42 | #define SVM_INTERCEPT_MISC1_MSR_PROT (1UL << 28)
43 | #define SVM_INTERCEPT_MISC1_IOIO_PROT (1UL << 27)
44 | #define SVM_INTERCEPT_MISC2_VMRUN (1UL << 0)
45 | #define SVM_INTERCEPT_MISC2_VMCALL (1UL << 1)
46 | #define SVM_INTERCEPT_MISC2_VMLOAD (1UL << 2)
47 | #define SVM_INTERCEPT_MISC2_VMSAVE (1UL << 3)
48 | #define SVM_NP_ENABLE_NP_ENABLE (1UL << 0)
49 |
50 | #define SVM_INTERCEPT_CR_WRITES_CR3 (1UL << 3)
51 |
52 | #define CPUID_HV_VENDOR_AND_MAX_FUNCTIONS 0x40000000
53 | #define CPUID_HV_INTERFACE 0x40000001
54 |
55 | #define CPUID_HV_MAX CPUID_HV_INTERFACE
56 |
57 | #define SVM_IOIO_STR_SHIFT 2
58 | #define SVM_IOIO_REP_SHIFT 3
59 | #define SVM_IOIO_SIZE_SHIFT 4
60 | #define SVM_IOIO_ASIZE_SHIFT 7
61 |
62 | #define SVM_IOIO_TYPE_MASK 1
63 | #define SVM_IOIO_STR_MASK (1 << SVM_IOIO_STR_SHIFT)
64 | #define SVM_IOIO_REP_MASK (1 << SVM_IOIO_REP_SHIFT)
65 | #define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT)
66 | #define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT)
67 |
68 | #define VMEXIT_CR0_READ 0x0000
69 | #define VMEXIT_CR1_READ 0x0001
70 | #define VMEXIT_CR2_READ 0x0002
71 | #define VMEXIT_CR3_READ 0x0003
72 | #define VMEXIT_CR4_READ 0x0004
73 | #define VMEXIT_CR5_READ 0x0005
74 | #define VMEXIT_CR6_READ 0x0006
75 | #define VMEXIT_CR7_READ 0x0007
76 | #define VMEXIT_CR8_READ 0x0008
77 | #define VMEXIT_CR9_READ 0x0009
78 | #define VMEXIT_CR10_READ 0x000a
79 | #define VMEXIT_CR11_READ 0x000b
80 | #define VMEXIT_CR12_READ 0x000c
81 | #define VMEXIT_CR13_READ 0x000d
82 | #define VMEXIT_CR14_READ 0x000e
83 | #define VMEXIT_CR15_READ 0x000f
84 | #define VMEXIT_CR0_WRITE 0x0010
85 | #define VMEXIT_CR1_WRITE 0x0011
86 | #define VMEXIT_CR2_WRITE 0x0012
87 | #define VMEXIT_CR3_WRITE 0x0013
88 | #define VMEXIT_CR4_WRITE 0x0014
89 | #define VMEXIT_CR5_WRITE 0x0015
90 | #define VMEXIT_CR6_WRITE 0x0016
91 | #define VMEXIT_CR7_WRITE 0x0017
92 | #define VMEXIT_CR8_WRITE 0x0018
93 | #define VMEXIT_CR9_WRITE 0x0019
94 | #define VMEXIT_CR10_WRITE 0x001a
95 | #define VMEXIT_CR11_WRITE 0x001b
96 | #define VMEXIT_CR12_WRITE 0x001c
97 | #define VMEXIT_CR13_WRITE 0x001d
98 | #define VMEXIT_CR14_WRITE 0x001e
99 | #define VMEXIT_CR15_WRITE 0x001f
100 | #define VMEXIT_DR0_READ 0x0020
101 | #define VMEXIT_DR1_READ 0x0021
102 | #define VMEXIT_DR2_READ 0x0022
103 | #define VMEXIT_DR3_READ 0x0023
104 | #define VMEXIT_DR4_READ 0x0024
105 | #define VMEXIT_DR5_READ 0x0025
106 | #define VMEXIT_DR6_READ 0x0026
107 | #define VMEXIT_DR7_READ 0x0027
108 | #define VMEXIT_DR8_READ 0x0028
109 | #define VMEXIT_DR9_READ 0x0029
110 | #define VMEXIT_DR10_READ 0x002a
111 | #define VMEXIT_DR11_READ 0x002b
112 | #define VMEXIT_DR12_READ 0x002c
113 | #define VMEXIT_DR13_READ 0x002d
114 | #define VMEXIT_DR14_READ 0x002e
115 | #define VMEXIT_DR15_READ 0x002f
116 | #define VMEXIT_DR0_WRITE 0x0030
117 | #define VMEXIT_DR1_WRITE 0x0031
118 | #define VMEXIT_DR2_WRITE 0x0032
119 | #define VMEXIT_DR3_WRITE 0x0033
120 | #define VMEXIT_DR4_WRITE 0x0034
121 | #define VMEXIT_DR5_WRITE 0x0035
122 | #define VMEXIT_DR6_WRITE 0x0036
123 | #define VMEXIT_DR7_WRITE 0x0037
124 | #define VMEXIT_DR8_WRITE 0x0038
125 | #define VMEXIT_DR9_WRITE 0x0039
126 | #define VMEXIT_DR10_WRITE 0x003a
127 | #define VMEXIT_DR11_WRITE 0x003b
128 | #define VMEXIT_DR12_WRITE 0x003c
129 | #define VMEXIT_DR13_WRITE 0x003d
130 | #define VMEXIT_DR14_WRITE 0x003e
131 | #define VMEXIT_DR15_WRITE 0x003f
132 | #define VMEXIT_EXCEPTION_DE 0x0040
133 | #define VMEXIT_EXCEPTION_DB 0x0041
134 | #define VMEXIT_EXCEPTION_NMI 0x0042
135 | #define VMEXIT_EXCEPTION_BP 0x0043
136 | #define VMEXIT_EXCEPTION_OF 0x0044
137 | #define VMEXIT_EXCEPTION_BR 0x0045
138 | #define VMEXIT_EXCEPTION_UD 0x0046
139 | #define VMEXIT_EXCEPTION_NM 0x0047
140 | #define VMEXIT_EXCEPTION_DF 0x0048
141 | #define VMEXIT_EXCEPTION_09 0x0049
142 | #define VMEXIT_EXCEPTION_TS 0x004a
143 | #define VMEXIT_EXCEPTION_NP 0x004b
144 | #define VMEXIT_EXCEPTION_SS 0x004c
145 | #define VMEXIT_EXCEPTION_GP 0x004d
146 | #define VMEXIT_EXCEPTION_PF 0x004e
147 | #define VMEXIT_EXCEPTION_15 0x004f
148 | #define VMEXIT_EXCEPTION_MF 0x0050
149 | #define VMEXIT_EXCEPTION_AC 0x0051
150 | #define VMEXIT_EXCEPTION_MC 0x0052
151 | #define VMEXIT_EXCEPTION_XF 0x0053
152 | #define VMEXIT_EXCEPTION_20 0x0054
153 | #define VMEXIT_EXCEPTION_21 0x0055
154 | #define VMEXIT_EXCEPTION_22 0x0056
155 | #define VMEXIT_EXCEPTION_23 0x0057
156 | #define VMEXIT_EXCEPTION_24 0x0058
157 | #define VMEXIT_EXCEPTION_25 0x0059
158 | #define VMEXIT_EXCEPTION_26 0x005a
159 | #define VMEXIT_EXCEPTION_27 0x005b
160 | #define VMEXIT_EXCEPTION_28 0x005c
161 | #define VMEXIT_EXCEPTION_VC 0x005d
162 | #define VMEXIT_EXCEPTION_SX 0x005e
163 | #define VMEXIT_EXCEPTION_31 0x005f
164 | #define VMEXIT_INTR 0x0060
165 | #define VMEXIT_NMI 0x0061
166 | #define VMEXIT_SMI 0x0062
167 | #define VMEXIT_INIT 0x0063
168 | #define VMEXIT_VINTR 0x0064
169 | #define VMEXIT_CR0_SEL_WRITE 0x0065
170 | #define VMEXIT_IDTR_READ 0x0066
171 | #define VMEXIT_GDTR_READ 0x0067
172 | #define VMEXIT_LDTR_READ 0x0068
173 | #define VMEXIT_TR_READ 0x0069
174 | #define VMEXIT_IDTR_WRITE 0x006a
175 | #define VMEXIT_GDTR_WRITE 0x006b
176 | #define VMEXIT_LDTR_WRITE 0x006c
177 | #define VMEXIT_TR_WRITE 0x006d
178 | #define VMEXIT_RDTSC 0x006e
179 | #define VMEXIT_RDPMC 0x006f
180 | #define VMEXIT_PUSHF 0x0070
181 | #define VMEXIT_POPF 0x0071
182 | #define VMEXIT_CPUID 0x0072
183 | #define VMEXIT_RSM 0x0073
184 | #define VMEXIT_IRET 0x0074
185 | #define VMEXIT_SWINT 0x0075
186 | #define VMEXIT_INVD 0x0076
187 | #define VMEXIT_PAUSE 0x0077
188 | #define VMEXIT_HLT 0x0078
189 | #define VMEXIT_INVLPG 0x0079
190 | #define VMEXIT_INVLPGA 0x007a
191 | #define VMEXIT_IOIO 0x007b
192 | #define VMEXIT_MSR 0x007c
193 | #define VMEXIT_TASK_SWITCH 0x007d
194 | #define VMEXIT_FERR_FREEZE 0x007e
195 | #define VMEXIT_SHUTDOWN 0x007f
196 | #define VMEXIT_VMRUN 0x0080
197 | #define VMEXIT_VMMCALL 0x0081
198 | #define VMEXIT_VMLOAD 0x0082
199 | #define VMEXIT_VMSAVE 0x0083
200 | #define VMEXIT_STGI 0x0084
201 | #define VMEXIT_CLGI 0x0085
202 | #define VMEXIT_SKINIT 0x0086
203 | #define VMEXIT_RDTSCP 0x0087
204 | #define VMEXIT_ICEBP 0x0088
205 | #define VMEXIT_WBINVD 0x0089
206 | #define VMEXIT_MONITOR 0x008a
207 | #define VMEXIT_MWAIT 0x008b
208 | #define VMEXIT_MWAIT_CONDITIONAL 0x008c
209 | #define VMEXIT_XSETBV 0x008d
210 | #define VMEXIT_EFER_WRITE_TRAP 0x008f
211 | #define VMEXIT_CR0_WRITE_TRAP 0x0090
212 | #define VMEXIT_CR1_WRITE_TRAP 0x0091
213 | #define VMEXIT_CR2_WRITE_TRAP 0x0092
214 | #define VMEXIT_CR3_WRITE_TRAP 0x0093
215 | #define VMEXIT_CR4_WRITE_TRAP 0x0094
216 | #define VMEXIT_CR5_WRITE_TRAP 0x0095
217 | #define VMEXIT_CR6_WRITE_TRAP 0x0096
218 | #define VMEXIT_CR7_WRITE_TRAP 0x0097
219 | #define VMEXIT_CR8_WRITE_TRAP 0x0098
220 | #define VMEXIT_CR9_WRITE_TRAP 0x0099
221 | #define VMEXIT_CR10_WRITE_TRAP 0x009a
222 | #define VMEXIT_CR11_WRITE_TRAP 0x009b
223 | #define VMEXIT_CR12_WRITE_TRAP 0x009c
224 | #define VMEXIT_CR13_WRITE_TRAP 0x009d
225 | #define VMEXIT_CR14_WRITE_TRAP 0x009e
226 | #define VMEXIT_CR15_WRITE_TRAP 0x009f
227 | #define VMEXIT_NPF 0x0400
228 | #define AVIC_INCOMPLETE_IPI 0x0401
229 | #define AVIC_NOACCEL 0x0402
230 | #define VMEXIT_VMGEXIT 0x0403
231 | #define VMEXIT_INVALID -1
232 |
233 | #define ROUND_TO_SIZE(_length, _alignment) (((_length)+((_alignment)-1)) & ~((_alignment)-1))
234 |
235 | typedef enum _INTERRUPT_TYPE
236 | {
237 | INTERRUPT_TYPE_EXTERNAL_INTERRUPT = 0,
238 | INTERRUPT_TYPE_RESERVED = 1,
239 | INTERRUPT_TYPE_NMI = 2,
240 | INTERRUPT_TYPE_HARDWARE_EXCEPTION = 3,
241 | INTERRUPT_TYPE_SOFTWARE_INTERRUPT = 4,
242 | INTERRUPT_TYPE_PRIVILEGED_SOFTWARE_INTERRUPT = 5,
243 | INTERRUPT_TYPE_SOFTWARE_EXCEPTION = 6,
244 | INTERRUPT_TYPE_OTHER_EVENT = 7
245 | } INTERRUPT_TYPE;
246 |
247 | typedef enum _EXCEPTION_VECTORS
248 | {
249 | EXCEPTION_VECTOR_DIVIDE_ERROR,
250 | EXCEPTION_VECTOR_DEBUG_BREAKPOINT,
251 | EXCEPTION_VECTOR_NMI,
252 | EXCEPTION_VECTOR_BREAKPOINT,
253 | EXCEPTION_VECTOR_OVERFLOW,
254 | EXCEPTION_VECTOR_BOUND_RANGE_EXCEEDED,
255 | EXCEPTION_VECTOR_UNDEFINED_OPCODE,
256 | EXCEPTION_VECTOR_NO_MATH_COPROCESSOR,
257 | EXCEPTION_VECTOR_DOUBLE_FAULT,
258 | EXCEPTION_VECTOR_RESERVED0,
259 | EXCEPTION_VECTOR_INVALID_TASK_SEGMENT_SELECTOR,
260 | EXCEPTION_VECTOR_SEGMENT_NOT_PRESENT,
261 | EXCEPTION_VECTOR_STACK_SEGMENT_FAULT,
262 | EXCEPTION_VECTOR_GENERAL_PROTECTION_FAULT,
263 | EXCEPTION_VECTOR_PAGE_FAULT,
264 | EXCEPTION_VECTOR_RESERVED1,
265 | EXCEPTION_VECTOR_MATH_FAULT,
266 | EXCEPTION_VECTOR_ALIGNMENT_CHECK,
267 | EXCEPTION_VECTOR_MACHINE_CHECK,
268 | EXCEPTION_VECTOR_SIMD_FLOATING_POINT_NUMERIC_ERROR,
269 | EXCEPTION_VECTOR_VIRTUAL_EXCEPTION,
270 | EXCEPTION_VECTOR_RESERVED2,
271 | EXCEPTION_VECTOR_RESERVED3,
272 | EXCEPTION_VECTOR_RESERVED4,
273 | EXCEPTION_VECTOR_RESERVED5,
274 | EXCEPTION_VECTOR_RESERVED6,
275 | EXCEPTION_VECTOR_RESERVED7,
276 | EXCEPTION_VECTOR_RESERVED8,
277 | EXCEPTION_VECTOR_RESERVED9,
278 | EXCEPTION_VECTOR_RESERVED10,
279 | EXCEPTION_VECTOR_RESERVED11,
280 | EXCEPTION_VECTOR_RESERVED12,
281 |
282 | //
283 | // NT (Windows) specific exception vectors.
284 | //
285 | APC_INTERRUPT = 31,
286 | DPC_INTERRUPT = 47,
287 | CLOCK_INTERRUPT = 209,
288 | IPI_INTERRUPT = 225,
289 | PMI_INTERRUPT = 254,
290 | } EXCEPTION_VECTORS;
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Defines/SVM_ProcessorData.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace SVM
4 | {
5 | typedef struct _SEGMENT_DESCRIPTOR
6 | {
7 | union
8 | {
9 | UINT64 AsUInt64;
10 | struct
11 | {
12 | UINT16 LimitLow; // [0:15]
13 | UINT16 BaseLow; // [16:31]
14 | UINT32 BaseMiddle : 8; // [32:39]
15 | UINT32 Type : 4; // [40:43]
16 | UINT32 System : 1; // [44]
17 | UINT32 Dpl : 2; // [45:46]
18 | UINT32 Present : 1; // [47]
19 | UINT32 LimitHigh : 4; // [48:51]
20 | UINT32 Avl : 1; // [52]
21 | UINT32 LongMode : 1; // [53]
22 | UINT32 DefaultBit : 1; // [54]
23 | UINT32 Granularity : 1; // [55]
24 | UINT32 BaseHigh : 8; // [56:63]
25 | } Fields;
26 | };
27 | } SEGMENT_DESCRIPTOR, * PSEGMENT_DESCRIPTOR;
28 | static_assert(sizeof(SEGMENT_DESCRIPTOR) == 8, "SEGMENT_DESCRIPTOR size mismatch");
29 |
30 | typedef struct _SEGMENT_ATTRIBUTE
31 | {
32 | union
33 | {
34 | UINT16 AsUInt16;
35 | struct
36 | {
37 | UINT16 Type : 4; // [0:3]
38 | UINT16 System : 1; // [4]
39 | UINT16 Dpl : 2; // [5:6]
40 | UINT16 Present : 1; // [7]
41 | UINT16 Avl : 1; // [8]
42 | UINT16 LongMode : 1; // [9]
43 | UINT16 DefaultBit : 1; // [10]
44 | UINT16 Granularity : 1; // [11]
45 | UINT16 Reserved1 : 4; // [12:15]
46 | } Fields;
47 | };
48 | } SEGMENT_ATTRIBUTE, * PSEGMENT_ATTRIBUTE;
49 | static_assert(sizeof(SEGMENT_ATTRIBUTE) == 2, "SEGMENT_ATTRIBUTE size mismatch");
50 |
51 | typedef struct _SHARED_VIRTUAL_PROCESSOR_DATA
52 | {
53 | PVOID MsrPermissionsMap;
54 | DECLSPEC_ALIGN(PAGE_SIZE) PML4E_64* Pml4Entries;
55 | DECLSPEC_ALIGN(PAGE_SIZE) PDPTE_1GB(*PdpEntries)[512];
56 | } SHARED_VIRTUAL_PROCESSOR_DATA, * PSHARED_VIRTUAL_PROCESSOR_DATA;
57 |
58 | typedef struct _VIRTUAL_PROCESSOR_DATA
59 | {
60 | union
61 | {
62 | DECLSPEC_ALIGN(PAGE_SIZE) UINT8 HostStackLimit[KERNEL_STACK_SIZE];
63 | struct
64 | {
65 | UINT8 StackContents[KERNEL_STACK_SIZE - (sizeof(PVOID) * 6) - sizeof(KTRAP_FRAME)];
66 | KTRAP_FRAME TrapFrame;
67 | UINT64 GuestVmcbPa;
68 | UINT64 HostVmcbPa;
69 | struct _VIRTUAL_PROCESSOR_DATA* Self;
70 | PSHARED_VIRTUAL_PROCESSOR_DATA SharedVpData;
71 | UINT64 ProcessorIndex;
72 | UINT64 Reserved1;
73 | } HostStackLayout;
74 | };
75 |
76 | DECLSPEC_ALIGN(PAGE_SIZE) VMCB GuestVmcb;
77 | DECLSPEC_ALIGN(PAGE_SIZE) VMCB HostVmcb;
78 | DECLSPEC_ALIGN(PAGE_SIZE) UINT8 HostStateArea[PAGE_SIZE];
79 | } VIRTUAL_PROCESSOR_DATA, * PVIRTUAL_PROCESSOR_DATA;
80 | static_assert(sizeof(VIRTUAL_PROCESSOR_DATA) == KERNEL_STACK_SIZE + PAGE_SIZE * 3, "VIRTUAL_PROCESSOR_DATA size mismatch");
81 |
82 | typedef struct _GUEST_REGISTERS
83 | {
84 | UINT64 R15;
85 | UINT64 R14;
86 | UINT64 R13;
87 | UINT64 R12;
88 | UINT64 R11;
89 | UINT64 R10;
90 | UINT64 R9;
91 | UINT64 R8;
92 | UINT64 Rdi;
93 | UINT64 Rsi;
94 | UINT64 Rbp;
95 | UINT64 Rsp;
96 | UINT64 Rbx;
97 | UINT64 Rdx;
98 | UINT64 Rcx;
99 | UINT64 Rax;
100 | } GUEST_REGISTERS, * PGUEST_REGISTERS;
101 |
102 | typedef struct _GUEST_CONTEXT
103 | {
104 | PGUEST_REGISTERS VpRegs;
105 | BOOLEAN ExitVm;
106 | } GUEST_CONTEXT, * PGUEST_CONTEXT;
107 |
108 | typedef struct _EVENTINJ
109 | {
110 | union
111 | {
112 | UINT64 AsUInt64;
113 | struct
114 | {
115 | UINT64 Vector : 8; // [0:7]
116 | UINT64 Type : 3; // [8:10]
117 | UINT64 ErrorCodeValid : 1; // [11]
118 | UINT64 Reserved1 : 19; // [12:30]
119 | UINT64 Valid : 1; // [31]
120 | UINT64 ErrorCode : 32; // [32:63]
121 | } Fields;
122 | };
123 | } EVENTINJ, * PEVENTINJ;
124 | static_assert(sizeof(EVENTINJ) == 8, "EVENTINJ size mismatch");
125 |
126 | typedef struct _NPF_EXITINFO1
127 | {
128 | union
129 | {
130 | UINT64 AsUInt64;
131 | struct
132 | {
133 | UINT64 Valid : 1; // [0]
134 | UINT64 Write : 1; // [1]
135 | UINT64 User : 1; // [2]
136 | UINT64 Reserved : 1; // [3]
137 | UINT64 Execute : 1; // [4]
138 | UINT64 Reserved2 : 27; // [5:31]
139 | UINT64 GuestPhysicalAddress : 1; // [32]
140 | UINT64 GuestPageTables : 1; // [33]
141 | } Fields;
142 | };
143 | } NPF_EXITINFO1, * PNPF_EXITINFO1;
144 | static_assert(sizeof(NPF_EXITINFO1) == 8, "NPF_EXITINFO1 size mismatch");
145 | }
146 |
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Handlers/SVM_HandleGenericVM.cpp:
--------------------------------------------------------------------------------
1 | #include "../../Global.h"
2 |
3 | void SVM::HandleGenericSVM(const PVIRTUAL_PROCESSOR_DATA vpData, const PGUEST_CONTEXT guestContext)
4 | {
5 | UNREFERENCED_PARAMETER(guestContext);
6 |
7 | InjectGeneralProtectionException(vpData);
8 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Handlers/SVM_HandleMSRAccess.cpp:
--------------------------------------------------------------------------------
1 | #include "../../Global.h"
2 |
3 | FORCEINLINE bool CheckRange(const ULONG64 input, const ULONG64 rangeStart, const ULONG64 rangeEnd)
4 | {
5 | if (input >= rangeStart && input <= rangeEnd)
6 | return true;
7 |
8 | return false;
9 | }
10 |
11 | void SVM::HandleMSRAccess(const PVIRTUAL_PROCESSOR_DATA vpData, const PGUEST_CONTEXT guestContext)
12 | {
13 | const UINT32 msr = guestContext->VpRegs->Rcx & MAXUINT32;
14 | const BOOLEAN writeAccess = (vpData->GuestVmcb.ControlArea.ExitInfo1 != 0);
15 |
16 | #if !NESTED_MODE
17 | if (!Utils::CheckMSR(msr))
18 | {
19 | InjectGeneralProtectionException(vpData);
20 | return;
21 | }
22 | #endif
23 |
24 | ULARGE_INTEGER value;
25 | if (msr == IA32_MSR_EFER)
26 | {
27 | if (writeAccess)
28 | {
29 | value.LowPart = guestContext->VpRegs->Rax & MAXUINT32;
30 | value.HighPart = guestContext->VpRegs->Rdx & MAXUINT32;
31 | value.QuadPart |= EFER_SVME;
32 |
33 | vpData->GuestVmcb.StateSaveArea.Efer = value.QuadPart;
34 | }
35 | else
36 | {
37 | value.QuadPart = __readmsr(msr);
38 | value.QuadPart &= ~EFER_SVME;
39 | guestContext->VpRegs->Rax = value.LowPart;
40 | guestContext->VpRegs->Rdx = value.HighPart;
41 | }
42 | }
43 | else
44 | {
45 | if (writeAccess)
46 | {
47 | value.LowPart = guestContext->VpRegs->Rax & MAXUINT32;
48 | value.HighPart = guestContext->VpRegs->Rdx & MAXUINT32;
49 | __writemsr(msr, value.QuadPart);
50 | }
51 | else
52 | {
53 | value.QuadPart = __readmsr(msr);
54 | guestContext->VpRegs->Rax = value.LowPart;
55 | guestContext->VpRegs->Rdx = value.HighPart;
56 | }
57 | }
58 |
59 | vpData->GuestVmcb.StateSaveArea.Rip = vpData->GuestVmcb.ControlArea.NRip;
60 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Handlers/SVM_HandleVMCall.cpp:
--------------------------------------------------------------------------------
1 | #include "../../Global.h"
2 |
3 | __forceinline void HandleInvalid(const SVM::PVIRTUAL_PROCESSOR_DATA vpData, const SVM::PGUEST_CONTEXT guestContext)
4 | {
5 | UNREFERENCED_PARAMETER(vpData);
6 |
7 | guestContext->VpRegs->Rax = 0xFFFF;
8 | }
9 |
10 | __forceinline void HandleCheckPresence(const SVM::PVIRTUAL_PROCESSOR_DATA vpData, const SVM::PGUEST_CONTEXT guestContext)
11 | {
12 | UNREFERENCED_PARAMETER(vpData);
13 |
14 | guestContext->VpRegs->Rax = Shared::COMM_CHECK;
15 | }
16 |
17 | void HandleGetProcess(const SVM::PVIRTUAL_PROCESSOR_DATA vpData, const SVM::PGUEST_CONTEXT guestContext)
18 | {
19 | UNREFERENCED_PARAMETER(vpData);
20 |
21 | const ULONG64 processId = guestContext->VpRegs->R8;
22 | guestContext->VpRegs->Rax = reinterpret_cast(Utils::FindProcess(reinterpret_cast(processId)));
23 | }
24 |
25 | void HandleGetDirectoryBase(const SVM::PVIRTUAL_PROCESSOR_DATA vpData, const SVM::PGUEST_CONTEXT guestContext)
26 | {
27 | UNREFERENCED_PARAMETER(vpData);
28 |
29 | const ULONG64 targetProcess = guestContext->VpRegs->R8;
30 |
31 | /*
32 | * If we don't reference the process object, the address space will
33 | * be trashed when the process starts exiting or crashes, which will
34 | * lead to system crash or freeze due to us overwriting the cr3 value
35 | * when reading the memory.
36 | */
37 | Utils::ReferenceObject(reinterpret_cast(targetProcess));
38 |
39 | guestContext->VpRegs->Rax = Memory::GetDirectoryBase(reinterpret_cast(targetProcess));
40 | }
41 |
42 | void HandleCopyProcessMemory(const SVM::PVIRTUAL_PROCESSOR_DATA vpData, const SVM::PGUEST_CONTEXT guestContext)
43 | {
44 | UNREFERENCED_PARAMETER(vpData);
45 |
46 | const UINT32 processorIndex = static_cast(vpData->HostStackLayout.ProcessorIndex);
47 | const ULONG64 controlData = guestContext->VpRegs->R8;
48 | const ULONG64 currentProcessCr3 = guestContext->VpRegs->R9;
49 |
50 | Shared::COPY_MEMORY_DATA copyData = { 0 };
51 | SIZE_T bytesRead;
52 | NTSTATUS status = Memory::ReadProcessMemory(processorIndex, currentProcessCr3, controlData, ©Data, sizeof(Shared::COPY_MEMORY_DATA), &bytesRead);
53 | if (!NT_SUCCESS(status))
54 | {
55 | guestContext->VpRegs->Rax = Shared::ErrorCodes::ControlBlockReadFail;
56 | return;
57 | }
58 |
59 | if (copyData.NumberOfBytes > Shared::MAX_RW_SIZE)
60 | {
61 | guestContext->VpRegs->Rax = Shared::ErrorCodes::MemoryCopyTooLarge;
62 | return;
63 | }
64 |
65 | status = Memory::CopyProcessMemory(processorIndex, copyData.SourceDirectoryBase, copyData.SourceAddress, copyData.DestinationDirectoryBase, copyData.DestinationAddress, copyData.NumberOfBytes);
66 | if (!NT_SUCCESS(status))
67 | {
68 | guestContext->VpRegs->Rax = status == STATUS_ABANDONED ? Shared::ErrorCodes::MemoryCopyFailSource : Shared::ErrorCodes::MemoryCopyFailTarget;
69 | return;
70 | }
71 |
72 | guestContext->VpRegs->Rax = Shared::ErrorCodes::Success;
73 | }
74 |
75 | void HandleProtectSelf(const SVM::PVIRTUAL_PROCESSOR_DATA vpData, const SVM::PGUEST_CONTEXT guestContext)
76 | {
77 | UNREFERENCED_PARAMETER(vpData);
78 |
79 | SVM::ProtectSelf(vpData->HostStackLayout.SharedVpData);
80 |
81 | vpData->GuestVmcb.ControlArea.VmcbClean &= 0xFFFFFFEF;
82 | vpData->GuestVmcb.ControlArea.TlbControl = 1;
83 |
84 | guestContext->VpRegs->Rax = Shared::ErrorCodes::Success;
85 | }
86 |
87 | void SVM::HandleVMCall(const PVIRTUAL_PROCESSOR_DATA vpData, const PGUEST_CONTEXT guestContext)
88 | {
89 | const ULONG64 magic = guestContext->VpRegs->Rcx;
90 | if (magic == Shared::MAGIC)
91 | {
92 | const ULONG64 command = guestContext->VpRegs->Rdx;
93 | switch (command)
94 | {
95 | case Shared::CheckPresence:
96 | HandleCheckPresence(vpData, guestContext);
97 | break;
98 | case Shared::GetProcess:
99 | HandleGetProcess(vpData, guestContext);
100 | break;
101 | case Shared::GetDirectoryBase:
102 | HandleGetDirectoryBase(vpData, guestContext);
103 | break;
104 | case Shared::CopyProcessMemory:
105 | HandleCopyProcessMemory(vpData, guestContext);
106 | break;
107 | case Shared::ProtectSelf:
108 | HandleProtectSelf(vpData, guestContext);
109 | break;
110 | default:
111 | HandleInvalid(vpData, guestContext);
112 | break;
113 | }
114 | }
115 | else
116 | {
117 | InjectGeneralProtectionException(vpData);
118 | return;
119 | }
120 |
121 | vpData->GuestVmcb.StateSaveArea.Rip = vpData->GuestVmcb.ControlArea.NRip;
122 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Handlers/SVM_VMExit.cpp:
--------------------------------------------------------------------------------
1 | #include "../../Global.h"
2 |
3 | void SVM::InjectGeneralProtectionException(const PVIRTUAL_PROCESSOR_DATA vpData)
4 | {
5 | EVENTINJ event;
6 | event.AsUInt64 = 0;
7 | event.Fields.Vector = EXCEPTION_VECTOR_GENERAL_PROTECTION_FAULT;
8 | event.Fields.Type = INTERRUPT_TYPE_HARDWARE_EXCEPTION;
9 | event.Fields.ErrorCodeValid = 1;
10 | event.Fields.Valid = 1;
11 | vpData->GuestVmcb.ControlArea.EventInj = event.AsUInt64;
12 | }
13 |
14 | /*void SVM::InjectUndefinedOpcodeException(const PVIRTUAL_PROCESSOR_DATA vpData)
15 | {
16 | EVENTINJ event;
17 | event.AsUInt64 = 0;
18 | event.Fields.Vector = EXCEPTION_VECTOR_UNDEFINED_OPCODE;
19 | event.Fields.Type = INTERRUPT_TYPE_HARDWARE_EXCEPTION;
20 | event.Fields.ErrorCodeValid = 0;
21 | event.Fields.Present = 1;
22 | vpData->GuestVmcb.ControlArea.EventInj = event.AsUInt64;
23 | }
24 |
25 | void SVM::InjectPageFaultException(const PVIRTUAL_PROCESSOR_DATA vpData, const ULONG64 address)
26 | {
27 | // https://wiki.osdev.org/Exceptions#Page_Fault
28 | EVENTINJ event;
29 | event.AsUInt64 = 0;
30 | event.Fields.Vector = EXCEPTION_VECTOR_PAGE_FAULT;
31 | event.Fields.Type = INTERRUPT_TYPE_HARDWARE_EXCEPTION;
32 | event.Fields.ErrorCodeValid = 1;
33 | event.Fields.Present = 1;
34 |
35 | PAGE_FAULT_EXCEPTION errorCode;
36 | errorCode.AsUInt = 0;
37 | errorCode.UserModeAccess = 1;
38 |
39 | event.Fields.ErrorCode = errorCode.AsUInt;
40 |
41 | vpData->GuestVmcb.StateSaveArea.Cr2 = address;
42 |
43 | vpData->GuestVmcb.ControlArea.EventInj = event.AsUInt64;
44 | }*/
45 |
46 | bool SVM::IsInUserland(PVIRTUAL_PROCESSOR_DATA vpData)
47 | {
48 | return vpData->GuestVmcb.StateSaveArea.Cpl == 3 && vpData->GuestVmcb.ControlArea.NRip < 0x7FFFFFFEFFFF;
49 | }
50 |
51 | EXTERN_C bool SVM::HandleExit(PVIRTUAL_PROCESSOR_DATA vpData, const PGUEST_REGISTERS guestRegisters)
52 | {
53 | GUEST_CONTEXT guestContext;
54 | guestContext.VpRegs = guestRegisters;
55 | guestContext.ExitVm = false;
56 |
57 | __svm_vmload(vpData->HostStackLayout.HostVmcbPa);
58 |
59 | guestRegisters->Rax = vpData->GuestVmcb.StateSaveArea.Rax;
60 |
61 | vpData->HostStackLayout.TrapFrame.Rsp = vpData->GuestVmcb.StateSaveArea.Rsp;
62 | vpData->HostStackLayout.TrapFrame.Rip = vpData->GuestVmcb.ControlArea.NRip;
63 |
64 | switch (vpData->GuestVmcb.ControlArea.ExitCode)
65 | {
66 | case VMEXIT_MSR:
67 | HandleMSRAccess(vpData, &guestContext);
68 | break;
69 | case VMEXIT_VMRUN:
70 | case VMEXIT_VMLOAD:
71 | case VMEXIT_VMSAVE:
72 | HandleGenericSVM(vpData, &guestContext);
73 | break;
74 | case VMEXIT_VMMCALL:
75 | HandleVMCall(vpData, &guestContext);
76 | break;
77 | default:
78 | KeBugCheckEx(INVALID_DRIVER_HANDLE, IsInUserland(vpData), vpData->GuestVmcb.StateSaveArea.Rip, vpData->GuestVmcb.ControlArea.ExitCode, vpData->GuestVmcb.ControlArea.ExitInfo1);
79 | }
80 |
81 | if (guestContext.ExitVm)
82 | {
83 | guestContext.VpRegs->Rax = reinterpret_cast(vpData) & MAXUINT32;
84 | guestContext.VpRegs->Rbx = vpData->GuestVmcb.ControlArea.NRip;
85 | guestContext.VpRegs->Rcx = vpData->GuestVmcb.StateSaveArea.Rsp;
86 | guestContext.VpRegs->Rdx = reinterpret_cast(vpData) >> 32;
87 |
88 | __svm_vmload(MmGetPhysicalAddress(&vpData->GuestVmcb).QuadPart);
89 |
90 | _disable();
91 | __svm_stgi();
92 |
93 | __writemsr(IA32_MSR_EFER, __readmsr(IA32_MSR_EFER) & ~EFER_SVME);
94 | __writeeflags(vpData->GuestVmcb.StateSaveArea.Rflags);
95 |
96 | return true;
97 | }
98 |
99 | vpData->GuestVmcb.StateSaveArea.Rax = guestContext.VpRegs->Rax;
100 |
101 | return false;
102 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/Handlers/SVM_VMExit.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace SVM
4 | {
5 | void InjectGeneralProtectionException(PVIRTUAL_PROCESSOR_DATA vpData);
6 |
7 | bool IsInUserland(PVIRTUAL_PROCESSOR_DATA vpData);
8 |
9 | void HandleGenericSVM(PVIRTUAL_PROCESSOR_DATA vpData, PGUEST_CONTEXT guestContext);
10 | void HandleVMCall(PVIRTUAL_PROCESSOR_DATA vpData, PGUEST_CONTEXT guestContext);
11 | void HandleMSRAccess(PVIRTUAL_PROCESSOR_DATA vpData, PGUEST_CONTEXT guestContext);
12 |
13 | EXTERN_C bool HandleExit(PVIRTUAL_PROCESSOR_DATA vpData, PGUEST_REGISTERS guestRegisters);
14 | }
15 |
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/SVM.cpp:
--------------------------------------------------------------------------------
1 | #include "../Global.h"
2 |
3 | NTSTATUS SVM::CheckSupport()
4 | {
5 | int registers[4];
6 |
7 | __cpuid(registers, CPUID_MAX_STANDARD_FN_NUMBER_AND_VENDOR_STRING);
8 | if ((registers[1] != 'htuA') ||
9 | (registers[3] != 'itne') ||
10 | (registers[2] != 'DMAc'))
11 | return STATUS_HV_INVALID_DEVICE_ID;
12 |
13 | __cpuid(registers, CPUID_PROCESSOR_AND_PROCESSOR_FEATURE_IDENTIFIERS_EX);
14 | if (!(registers[2] & CPUID_FN8000_0001_ECX_SVM))
15 | return STATUS_HV_CPUID_FEATURE_VALIDATION_ERROR;
16 |
17 | __cpuid(registers, CPUID_SVM_FEATURES);
18 | if (!(registers[3] & CPUID_FN8000_000A_EDX_NP))
19 | return STATUS_HV_CPUID_FEATURE_VALIDATION_ERROR;
20 |
21 | const ULONG64 vmcr = __readmsr(SVM_MSR_VM_CR);
22 | if (vmcr & SVM_VM_CR_SVMDIS)
23 | return STATUS_HV_INVALID_DEVICE_STATE;
24 |
25 | return STATUS_SUCCESS;
26 | }
27 |
28 | void SVM::BuildNestedPageTables(const PSHARED_VIRTUAL_PROCESSOR_DATA sharedData)
29 | {
30 | /*
31 | * On real hardware, physical addresses above 512GB (not only first PML4) are being accessed
32 | * for example due to above 4G encoding (REBAR) there will be accesses from 880GB-1000GB range.
33 | *
34 | * For this reason, we are using 1GB huge pages and map every single possible GPA to HPA 1:1.
35 | */
36 | sharedData->Pml4Entries = static_cast(Utils::AllocatePageAligned(sizeof(PML4E_64) * 512));
37 | sharedData->PdpEntries = static_cast(Utils::AllocatePageAligned(sizeof(PDPTE_1GB) * 512 * 512));
38 |
39 | for (ULONG64 pml4Index = 0; pml4Index < 512; pml4Index++)
40 | {
41 | const ULONG64 pdpBasePa = MmGetPhysicalAddress(&sharedData->PdpEntries[pml4Index]).QuadPart;
42 | sharedData->Pml4Entries[pml4Index].Present = 1;
43 | sharedData->Pml4Entries[pml4Index].Write = 1;
44 | sharedData->Pml4Entries[pml4Index].Supervisor = 1;
45 | sharedData->Pml4Entries[pml4Index].PageFrameNumber = pdpBasePa >> PAGE_SHIFT;
46 |
47 | for (ULONG64 pdpIndex = 0; pdpIndex < 512; pdpIndex++)
48 | {
49 | const ULONG64 translationPa = (pml4Index * 512ULL + pdpIndex);
50 | sharedData->PdpEntries[pml4Index][pdpIndex].Present = 1;
51 | sharedData->PdpEntries[pml4Index][pdpIndex].Write = 1;
52 | sharedData->PdpEntries[pml4Index][pdpIndex].Supervisor = 1;
53 | sharedData->PdpEntries[pml4Index][pdpIndex].LargePage = 1;
54 | sharedData->PdpEntries[pml4Index][pdpIndex].PageFrameNumber = translationPa;
55 | }
56 | }
57 | }
58 |
59 | void SVM::ProtectSelf(PSHARED_VIRTUAL_PROCESSOR_DATA sharedData)
60 | {
61 | static bool alreadyProtected = false;
62 | if (alreadyProtected)
63 | return;
64 |
65 | alreadyProtected = true;
66 |
67 | ULONG64 driverVirtualBase = 0;
68 | SIZE_T driverSize = 0;
69 | const NTSTATUS status = Utils::GetCurrentDriverInfo(&driverVirtualBase, &driverSize);
70 | if (!NT_SUCCESS(status))
71 | KeBugCheck(MEMORY1_INITIALIZATION_FAILED);
72 |
73 | const ULONG64 driverPhysicalBase = MmGetPhysicalAddress(reinterpret_cast(driverVirtualBase)).QuadPart;
74 | const ULONG64 driverPhysicalEnd = driverPhysicalBase + driverSize;
75 | const ULONG64 blankPagePhysical = MmGetPhysicalAddress(Global::BlankPage).QuadPart;
76 |
77 | bool found = false;
78 | for (ULONG64 pml4Index = 0; pml4Index < 512; pml4Index++)
79 | {
80 | for (ULONG64 pdpIndex = 0; pdpIndex < 512; pdpIndex++)
81 | {
82 | const ULONG64 mappingBase = (pml4Index * 512ULL + pdpIndex) * PAGE_SIZE_1GB;
83 | const ULONG64 mappingEnd = mappingBase + PAGE_SIZE_1GB;
84 | if (driverPhysicalBase < mappingEnd && driverPhysicalEnd > mappingBase)
85 | {
86 | PD_ENTRY_2MB* newPdTable = reinterpret_cast(Utils::GetPreallocatedPool(sizeof(PD_ENTRY_2MB) * 512));
87 | for (ULONG64 pdIndex = 0; pdIndex < 512; pdIndex++)
88 | {
89 | const ULONG64 pdMappingBase = mappingBase + pdIndex * PAGE_SIZE_2MB;
90 | const ULONG64 pdMappingEnd = pdMappingBase + PAGE_SIZE_2MB;
91 | if (driverPhysicalBase < pdMappingEnd && driverPhysicalEnd > pdMappingBase)
92 | {
93 | PT_ENTRY_4KB* newPtTable = reinterpret_cast(Utils::GetPreallocatedPool(sizeof(PT_ENTRY_4KB) * 512));
94 | for (ULONG64 ptIndex = 0; ptIndex < 512; ptIndex++)
95 | {
96 | const ULONG64 pageMappingBase = pdMappingBase + ptIndex * PAGE_SIZE_4KB;
97 | newPtTable[ptIndex].Present = 1;
98 | newPtTable[ptIndex].Write = 1;
99 | newPtTable[ptIndex].Supervisor = 1;
100 |
101 | if (pageMappingBase >= driverPhysicalBase && pageMappingBase < driverPhysicalEnd)
102 | {
103 | newPtTable[ptIndex].PageFrameNumber = blankPagePhysical >> PAGE_SHIFT;
104 | found = true;
105 | }
106 | else
107 | newPtTable[ptIndex].PageFrameNumber = pageMappingBase >> PAGE_SHIFT;
108 | }
109 |
110 | PDE_4KB* newPdTableNarrow = reinterpret_cast(newPdTable);
111 | newPdTableNarrow[pdIndex].Present = 1;
112 | newPdTableNarrow[pdIndex].Write = 1;
113 | newPdTableNarrow[pdIndex].Supervisor = 1;
114 | newPdTableNarrow[pdIndex].LargePage = 0;
115 |
116 | const ULONG64 newPtTablePa = MmGetPhysicalAddress(newPtTable).QuadPart;
117 | newPdTableNarrow[pdIndex].PageFrameNumber = newPtTablePa >> PAGE_SHIFT;
118 | }
119 | else
120 | {
121 | newPdTable[pdIndex].Present = 1;
122 | newPdTable[pdIndex].Write = 1;
123 | newPdTable[pdIndex].Supervisor = 1;
124 | newPdTable[pdIndex].LargePage = 1;
125 | newPdTable[pdIndex].PageFrameNumber = pdMappingBase >> PD_PAGE_SHIFT;
126 | }
127 | }
128 |
129 | const ULONG64 newPdTablePa = MmGetPhysicalAddress(newPdTable).QuadPart;
130 |
131 | PDPTE_2MB newEntry;
132 | newEntry.AsUInt = sharedData->PdpEntries[pml4Index][pdpIndex].AsUInt;
133 | newEntry.LargePage = 0;
134 | newEntry.PageFrameNumber = newPdTablePa >> PAGE_SHIFT;
135 |
136 | InterlockedExchange64(
137 | reinterpret_cast(&sharedData->PdpEntries[pml4Index][pdpIndex].AsUInt),
138 | static_cast(newEntry.AsUInt)
139 | );
140 | }
141 | }
142 | }
143 |
144 | if (!found)
145 | KeBugCheck(SECURITY1_INITIALIZATION_FAILED);
146 | }
147 |
148 | void SVM::BuildPermissionMap(const PVOID permissionMap)
149 | {
150 | static const UINT32 BITS_PER_MSR = 2;
151 | static const UINT32 SECOND_MSR_RANGE_BASE = 0xc0000000;
152 | static const UINT32 SECOND_MSRPM_OFFSET = 0x800 * CHAR_BIT;
153 |
154 | RTL_BITMAP bitmapHeader;
155 | RtlInitializeBitMap(&bitmapHeader, static_cast(permissionMap), SVM_MSR_PERMISSIONS_MAP_SIZE * CHAR_BIT);
156 | RtlClearAllBits(&bitmapHeader);
157 |
158 | const ULONG offsetFrom2ndBase = (IA32_MSR_EFER - SECOND_MSR_RANGE_BASE) * BITS_PER_MSR;
159 | const ULONG offset = SECOND_MSRPM_OFFSET + offsetFrom2ndBase;
160 |
161 | RtlSetBits(&bitmapHeader, offset + 1, 1);
162 | }
163 |
164 | static bool VirtualizedProcessors[64] = { 0 };
165 | bool SVM::CheckAndSetInstalled()
166 | {
167 | const ULONG processorIndex = KeGetCurrentProcessorNumber();
168 | if (VirtualizedProcessors[processorIndex])
169 | return true;
170 |
171 | VirtualizedProcessors[processorIndex] = true;
172 | return false;
173 | }
174 |
175 | UINT16 SVM::GetSegmentAccessRights(const UINT16 segmentSelector, const ULONG_PTR gdtBase)
176 | {
177 | const PSEGMENT_DESCRIPTOR descriptor = reinterpret_cast(gdtBase + (segmentSelector & ~RPL_MASK));
178 |
179 | SEGMENT_ATTRIBUTE attribute;
180 | attribute.Fields.Type = descriptor->Fields.Type;
181 | attribute.Fields.System = descriptor->Fields.System;
182 | attribute.Fields.Dpl = descriptor->Fields.Dpl;
183 | attribute.Fields.Present = descriptor->Fields.Present;
184 | attribute.Fields.Avl = descriptor->Fields.Avl;
185 | attribute.Fields.LongMode = descriptor->Fields.LongMode;
186 | attribute.Fields.DefaultBit = descriptor->Fields.DefaultBit;
187 | attribute.Fields.Granularity = descriptor->Fields.Granularity;
188 | attribute.Fields.Reserved1 = 0;
189 |
190 | return attribute.AsUInt16;
191 | }
192 |
193 | void SVM::PrepareForVirtualization(const PVIRTUAL_PROCESSOR_DATA vpData, const PSHARED_VIRTUAL_PROCESSOR_DATA sharedVpData, const PCONTEXT contextRecord)
194 | {
195 | const ULONG processorIndex = KeGetCurrentProcessorNumber();
196 | vpData->HostStackLayout.ProcessorIndex = processorIndex;
197 |
198 | DESCRIPTOR_TABLE_REGISTER gdtr, idtr;
199 | _sgdt(&gdtr);
200 | __sidt(&idtr);
201 |
202 | const PHYSICAL_ADDRESS guestVmcbPa = MmGetPhysicalAddress(&vpData->GuestVmcb);
203 | const PHYSICAL_ADDRESS hostVmcbPa = MmGetPhysicalAddress(&vpData->HostVmcb);
204 | const PHYSICAL_ADDRESS hostStateAreaPa = MmGetPhysicalAddress(&vpData->HostStateArea);
205 | const PHYSICAL_ADDRESS pml4BasePa = MmGetPhysicalAddress(sharedVpData->Pml4Entries);
206 | const PHYSICAL_ADDRESS msrpmPa = MmGetPhysicalAddress(sharedVpData->MsrPermissionsMap);
207 |
208 | vpData->GuestVmcb.ControlArea.InterceptMisc2 |= SVM_INTERCEPT_MISC2_VMRUN;
209 | vpData->GuestVmcb.ControlArea.InterceptMisc2 |= SVM_INTERCEPT_MISC2_VMCALL;
210 |
211 | vpData->GuestVmcb.ControlArea.InterceptMisc1 |= SVM_INTERCEPT_MISC1_MSR_PROT;
212 | vpData->GuestVmcb.ControlArea.MsrpmBasePa = msrpmPa.QuadPart;
213 |
214 | vpData->GuestVmcb.ControlArea.GuestAsid = 1;
215 |
216 | vpData->GuestVmcb.ControlArea.NpEnable |= SVM_NP_ENABLE_NP_ENABLE;
217 | vpData->GuestVmcb.ControlArea.NCr3 = pml4BasePa.QuadPart;
218 |
219 | vpData->GuestVmcb.StateSaveArea.GdtrBase = gdtr.Base;
220 | vpData->GuestVmcb.StateSaveArea.GdtrLimit = gdtr.Limit;
221 | vpData->GuestVmcb.StateSaveArea.IdtrBase = idtr.Base;
222 | vpData->GuestVmcb.StateSaveArea.IdtrLimit = idtr.Limit;
223 |
224 | vpData->GuestVmcb.StateSaveArea.CsLimit = GetSegmentLimit(contextRecord->SegCs);
225 | vpData->GuestVmcb.StateSaveArea.DsLimit = GetSegmentLimit(contextRecord->SegDs);
226 | vpData->GuestVmcb.StateSaveArea.EsLimit = GetSegmentLimit(contextRecord->SegEs);
227 | vpData->GuestVmcb.StateSaveArea.SsLimit = GetSegmentLimit(contextRecord->SegSs);
228 | vpData->GuestVmcb.StateSaveArea.CsSelector = contextRecord->SegCs;
229 | vpData->GuestVmcb.StateSaveArea.DsSelector = contextRecord->SegDs;
230 | vpData->GuestVmcb.StateSaveArea.EsSelector = contextRecord->SegEs;
231 | vpData->GuestVmcb.StateSaveArea.SsSelector = contextRecord->SegSs;
232 | vpData->GuestVmcb.StateSaveArea.CsAttrib = GetSegmentAccessRights(contextRecord->SegCs, gdtr.Base);
233 | vpData->GuestVmcb.StateSaveArea.DsAttrib = GetSegmentAccessRights(contextRecord->SegDs, gdtr.Base);
234 | vpData->GuestVmcb.StateSaveArea.EsAttrib = GetSegmentAccessRights(contextRecord->SegEs, gdtr.Base);
235 | vpData->GuestVmcb.StateSaveArea.SsAttrib = GetSegmentAccessRights(contextRecord->SegSs, gdtr.Base);
236 |
237 | vpData->GuestVmcb.StateSaveArea.Efer = __readmsr(IA32_MSR_EFER);
238 | vpData->GuestVmcb.StateSaveArea.Cr0 = __readcr0();
239 | vpData->GuestVmcb.StateSaveArea.Cr2 = __readcr2();
240 | vpData->GuestVmcb.StateSaveArea.Cr3 = __readcr3();
241 | vpData->GuestVmcb.StateSaveArea.Cr4 = __readcr4();
242 | vpData->GuestVmcb.StateSaveArea.Rflags = contextRecord->EFlags;
243 | vpData->GuestVmcb.StateSaveArea.Rsp = contextRecord->Rsp;
244 | vpData->GuestVmcb.StateSaveArea.Rip = contextRecord->Rip;
245 | vpData->GuestVmcb.StateSaveArea.GPat = __readmsr(IA32_MSR_PAT);
246 |
247 | __svm_vmsave(guestVmcbPa.QuadPart);
248 |
249 | vpData->HostStackLayout.Reserved1 = MAXUINT64;
250 | vpData->HostStackLayout.SharedVpData = sharedVpData;
251 | vpData->HostStackLayout.Self = vpData;
252 | vpData->HostStackLayout.HostVmcbPa = hostVmcbPa.QuadPart;
253 | vpData->HostStackLayout.GuestVmcbPa = guestVmcbPa.QuadPart;
254 |
255 | __writemsr(SVM_MSR_VM_HSAVE_PA, hostStateAreaPa.QuadPart);
256 |
257 | __svm_vmsave(hostVmcbPa.QuadPart);
258 | }
259 |
260 | NTSTATUS SVM::VirtualizeProcessor(const PVOID context)
261 | {
262 | const PCONTEXT contextRecord = static_cast(ExAllocatePool(NonPagedPool, sizeof(CONTEXT)));
263 | if (!contextRecord)
264 | return STATUS_INSUFFICIENT_RESOURCES;
265 |
266 | const PVIRTUAL_PROCESSOR_DATA vpData = static_cast(Utils::AllocatePageAligned(sizeof(VIRTUAL_PROCESSOR_DATA)));
267 | if (!vpData)
268 | return STATUS_INSUFFICIENT_RESOURCES;
269 |
270 | RtlCaptureContext(contextRecord);
271 |
272 | if (!CheckAndSetInstalled())
273 | {
274 | const PSHARED_VIRTUAL_PROCESSOR_DATA sharedVpData = static_cast(context);
275 |
276 | __writemsr(IA32_MSR_EFER, __readmsr(IA32_MSR_EFER) | EFER_SVME);
277 |
278 | PrepareForVirtualization(vpData, sharedVpData, contextRecord);
279 |
280 | LaunchVM(&vpData->HostStackLayout.GuestVmcbPa);
281 |
282 | KeBugCheck(MANUALLY_INITIATED_CRASH);
283 | }
284 |
285 | return STATUS_SUCCESS;
286 | }
287 |
288 | void SVM::VirtualizeAllProcessors(PVOID)
289 | {
290 | const PSHARED_VIRTUAL_PROCESSOR_DATA sharedVpData = static_cast(Utils::AllocatePageAligned(sizeof(SHARED_VIRTUAL_PROCESSOR_DATA)));
291 | if (!sharedVpData)
292 | return;
293 |
294 | sharedVpData->MsrPermissionsMap = Utils::AllocateContiguousMemory(SVM_MSR_PERMISSIONS_MAP_SIZE);
295 | if (!sharedVpData->MsrPermissionsMap)
296 | return;
297 |
298 | BuildNestedPageTables(sharedVpData);
299 | BuildPermissionMap(sharedVpData->MsrPermissionsMap);
300 |
301 | const NTSTATUS status = Utils::ExecuteOnEachProcessor(VirtualizeProcessor, sharedVpData);
302 | if (!NT_SUCCESS(status))
303 | KeBugCheck(MANUALLY_INITIATED_CRASH);
304 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/SVM.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace SVM
4 | {
5 | EXTERN_C void _sgdt(PVOID descriptor);
6 | EXTERN_C void NTAPI LaunchVM(PVOID hostRsp);
7 |
8 | NTSTATUS CheckSupport();
9 | void BuildNestedPageTables(PSHARED_VIRTUAL_PROCESSOR_DATA sharedData);
10 | void ProtectSelf(PSHARED_VIRTUAL_PROCESSOR_DATA sharedData);
11 | void BuildPermissionMap(PVOID permissionMap);
12 | bool CheckAndSetInstalled();
13 | UINT16 GetSegmentAccessRights(UINT16 segmentSelector, ULONG_PTR gdtBase);
14 | void PrepareForVirtualization(PVIRTUAL_PROCESSOR_DATA vpData, PSHARED_VIRTUAL_PROCESSOR_DATA sharedVpData, const PCONTEXT contextRecord);
15 | NTSTATUS VirtualizeProcessor(PVOID context);
16 | void VirtualizeAllProcessors(PVOID);
17 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/SVM/SVM_x64.asm:
--------------------------------------------------------------------------------
1 | .const
2 |
3 | KTRAP_FRAME_SIZE equ 190h
4 | MACHINE_FRAME_SIZE equ 28h
5 |
6 | .code
7 |
8 | extern HandleExit : proc
9 |
10 | PUSHAQ macro
11 | push rax
12 | push rcx
13 | push rdx
14 | push rbx
15 | push -1
16 | push rbp
17 | push rsi
18 | push rdi
19 | push r8
20 | push r9
21 | push r10
22 | push r11
23 | push r12
24 | push r13
25 | push r14
26 | push r15
27 | endm
28 |
29 | POPAQ macro
30 | pop r15
31 | pop r14
32 | pop r13
33 | pop r12
34 | pop r11
35 | pop r10
36 | pop r9
37 | pop r8
38 | pop rdi
39 | pop rsi
40 | pop rbp
41 | pop rbx
42 | pop rbx
43 | pop rdx
44 | pop rcx
45 | pop rax
46 | endm
47 |
48 | LaunchVM proc frame
49 | mov rsp, rcx
50 |
51 | SvLV10:
52 | mov rax, [rsp]
53 | vmload rax
54 |
55 | vmrun rax
56 |
57 | vmsave rax
58 |
59 | .pushframe
60 | sub rsp, KTRAP_FRAME_SIZE
61 | .allocstack KTRAP_FRAME_SIZE - MACHINE_FRAME_SIZE + 100h
62 |
63 | PUSHAQ
64 |
65 | mov rdx, rsp
66 | mov rcx, [rsp + 8 * 18 + KTRAP_FRAME_SIZE]
67 |
68 | sub rsp, 80h
69 | movaps xmmword ptr [rsp + 20h], xmm0
70 | movaps xmmword ptr [rsp + 30h], xmm1
71 | movaps xmmword ptr [rsp + 40h], xmm2
72 | movaps xmmword ptr [rsp + 50h], xmm3
73 | movaps xmmword ptr [rsp + 60h], xmm4
74 | movaps xmmword ptr [rsp + 70h], xmm5
75 | .endprolog
76 |
77 | call HandleExit
78 |
79 | movaps xmm5, xmmword ptr [rsp + 70h]
80 | movaps xmm4, xmmword ptr [rsp + 60h]
81 | movaps xmm3, xmmword ptr [rsp + 50h]
82 | movaps xmm2, xmmword ptr [rsp + 40h]
83 | movaps xmm1, xmmword ptr [rsp + 30h]
84 | movaps xmm0, xmmword ptr [rsp + 20h]
85 | add rsp, 80h
86 |
87 | test al, al
88 | POPAQ
89 |
90 | jnz SvLV20
91 | add rsp, KTRAP_FRAME_SIZE
92 | jmp SvLV10
93 |
94 | SvLV20:
95 | mov rsp, rcx
96 | mov ecx, 'NNNN'
97 | jmp rbx
98 | LaunchVM endp
99 |
100 | end
101 |
--------------------------------------------------------------------------------
/memhv/memhv/Source/Shared.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace Shared
4 | {
5 | constexpr ULONG64 MAGIC = 0xfeed3;
6 | constexpr ULONG64 COMM_CHECK = 0xdead;
7 |
8 | constexpr ULONG64 MAX_RW_SIZE = 0x10000;
9 |
10 | enum CommandId
11 | {
12 | Invalid,
13 | CheckPresence,
14 | GetProcess,
15 | GetDirectoryBase,
16 | CopyProcessMemory,
17 | ProtectSelf,
18 | };
19 |
20 | enum ErrorCodes
21 | {
22 | Success,
23 | ControlBlockReadFail,
24 | MemoryCopyTooLarge,
25 | MemoryCopyFailSource,
26 | MemoryCopyFailTarget,
27 | };
28 |
29 | typedef struct _COPY_MEMORY_DATA
30 | {
31 | ULONG64 SourceDirectoryBase;
32 | ULONG64 SourceAddress;
33 | ULONG64 DestinationDirectoryBase;
34 | ULONG64 DestinationAddress;
35 | SIZE_T NumberOfBytes;
36 | } COPY_MEMORY_DATA;
37 | }
--------------------------------------------------------------------------------
/memhv/memhv/Source/Utils.cpp:
--------------------------------------------------------------------------------
1 | #include "Global.h"
2 |
3 | char* Utils::Compare(const char* haystack, const char* needle)
4 | {
5 | do
6 | {
7 | const char* h = haystack;
8 | const char* n = needle;
9 | while (tolower(static_cast(*h)) == tolower(static_cast(*n)) && *n)
10 | {
11 | h++;
12 | n++;
13 | }
14 |
15 | if (*n == 0)
16 | return const_cast(haystack);
17 | } while (*haystack++);
18 | return nullptr;
19 | }
20 |
21 | PVOID Utils::GetModuleBase(const char* moduleName)
22 | {
23 | PVOID address = nullptr;
24 | ULONG size = 0;
25 |
26 | NTSTATUS status = ZwQuerySystemInformation(SystemModuleInformation, &size, 0, &size);
27 | if (status != STATUS_INFO_LENGTH_MISMATCH)
28 | return nullptr;
29 |
30 | #pragma warning(disable : 4996) // 'ExAllocatePool': ExAllocatePool is deprecated, use ExAllocatePool2
31 | PSYSTEM_MODULE_INFORMATION moduleList = static_cast(ExAllocatePool(NonPagedPool, size));
32 | if (!moduleList)
33 | return nullptr;
34 |
35 | status = ZwQuerySystemInformation(SystemModuleInformation, moduleList, size, nullptr);
36 | if (!NT_SUCCESS(status))
37 | goto end;
38 |
39 | for (ULONG_PTR i = 0; i < moduleList->ulModuleCount; i++)
40 | {
41 | ULONG64 pointer = reinterpret_cast(&moduleList->Modules[i]);
42 | pointer += sizeof(SYSTEM_MODULE);
43 | if (pointer > (reinterpret_cast(moduleList) + size))
44 | break;
45 |
46 | SYSTEM_MODULE module = moduleList->Modules[i];
47 | module.ImageName[255] = '\0';
48 | if (Compare(module.ImageName, moduleName))
49 | {
50 | address = module.Base;
51 | break;
52 | }
53 | }
54 |
55 | end:
56 | ExFreePool(moduleList);
57 | return address;
58 | }
59 |
60 | #define IN_RANGE(x, a, b) (x >= a && x <= b)
61 | #define GET_BITS(x) (IN_RANGE((x&(~0x20)),'A','F')?((x&(~0x20))-'A'+0xA):(IN_RANGE(x,'0','9')?x-'0':0))
62 | #define GET_BYTE(a, b) (GET_BITS(a) << 4 | GET_BITS(b))
63 | ULONG64 Utils::FindPattern(void* baseAddress, const ULONG64 size, const char* pattern)
64 | {
65 | BYTE* firstMatch = nullptr;
66 | const char* currentPattern = pattern;
67 |
68 | BYTE* start = static_cast(baseAddress);
69 | const BYTE* end = start + size;
70 |
71 | for (BYTE* current = start; current < end; current++)
72 | {
73 | const BYTE byte = currentPattern[0]; if (!byte) return reinterpret_cast(firstMatch);
74 | if (byte == '\?' || *static_cast(current) == GET_BYTE(byte, currentPattern[1]))
75 | {
76 | if (!firstMatch) firstMatch = current;
77 | if (!currentPattern[2]) return reinterpret_cast(firstMatch);
78 | ((byte == '\?') ? (currentPattern += 2) : (currentPattern += 3));
79 | }
80 | else
81 | {
82 | currentPattern = pattern;
83 | firstMatch = nullptr;
84 | }
85 | }
86 |
87 | return 0;
88 | }
89 |
90 | ULONG64 Utils::FindPatternImage(void* base, const char* pattern)
91 | {
92 | ULONG64 match = 0;
93 |
94 | PIMAGE_NT_HEADERS64 headers = reinterpret_cast(reinterpret_cast(base) + static_cast(base)->e_lfanew);
95 | const PIMAGE_SECTION_HEADER sections = IMAGE_FIRST_SECTION(headers);
96 | for (USHORT i = 0; i < headers->FileHeader.NumberOfSections; ++i)
97 | {
98 | const PIMAGE_SECTION_HEADER section = §ions[i];
99 | if (memcmp(section->Name, ".text", 5) == 0 || *reinterpret_cast(section->Name) == 'EGAP')
100 | {
101 | match = FindPattern(reinterpret_cast(reinterpret_cast(base) + section->VirtualAddress), section->Misc.VirtualSize, pattern);
102 | if (match)
103 | break;
104 | }
105 | }
106 |
107 | return match;
108 | }
109 |
110 | PVOID Utils::AllocateContiguousMemory(const SIZE_T size)
111 | {
112 | PHYSICAL_ADDRESS boundary, lowest, highest;
113 | boundary.QuadPart = lowest.QuadPart = 0;
114 | highest.QuadPart = -1;
115 |
116 | const PVOID allocated = MmAllocateContiguousMemorySpecifyCacheNode(size, lowest, highest, boundary, MmNonCached, MM_ANY_NODE_OK);
117 | if (!allocated)
118 | KeBugCheck(HAL_MEMORY_ALLOCATION);
119 |
120 | RtlZeroMemory(allocated, size);
121 |
122 | return allocated;
123 | }
124 |
125 | NTSTATUS Utils::ExecuteOnEachProcessor(NTSTATUS(*callback)(PVOID), const PVOID context)
126 | {
127 | const ULONG numOfProcessors = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
128 | for (ULONG i = 0; i < numOfProcessors; i++)
129 | {
130 | PROCESSOR_NUMBER processorNumber;
131 | NTSTATUS status = KeGetProcessorNumberFromIndex(i, &processorNumber);
132 | if (!NT_SUCCESS(status))
133 | return status;
134 |
135 | GROUP_AFFINITY affinity;
136 | affinity.Group = processorNumber.Group;
137 | affinity.Mask = 1ULL << processorNumber.Number;
138 | affinity.Reserved[0] = affinity.Reserved[1] = affinity.Reserved[2] = 0;
139 |
140 | GROUP_AFFINITY oldAffinity;
141 | KeSetSystemGroupAffinityThread(&affinity, &oldAffinity);
142 |
143 | status = callback(context);
144 |
145 | KeRevertToUserGroupAffinityThread(&oldAffinity);
146 |
147 | if (!NT_SUCCESS(status))
148 | return status;
149 | }
150 |
151 | return STATUS_SUCCESS;
152 | }
153 |
154 | extern "C" IMAGE_DOS_HEADER __ImageBase;
155 | NTSTATUS Utils::GetCurrentDriverInfo(ULONG64* baseAddress, SIZE_T* size)
156 | {
157 | if (__ImageBase.e_magic != IMAGE_DOS_SIGNATURE)
158 | return STATUS_INVALID_IMAGE_FORMAT;
159 |
160 | const PIMAGE_NT_HEADERS64 headers = reinterpret_cast(reinterpret_cast(&__ImageBase) + __ImageBase.e_lfanew);
161 | if (headers->Signature != IMAGE_NT_SIGNATURE)
162 | return STATUS_INVALID_IMAGE_FORMAT;
163 |
164 | *size = headers->OptionalHeader.SizeOfImage;
165 | *baseAddress = reinterpret_cast(&__ImageBase);
166 |
167 | return STATUS_SUCCESS;
168 | }
169 |
170 | PVOID Utils::AllocatePageAligned(const SIZE_T size)
171 | {
172 | if (size < PAGE_SIZE)
173 | KeBugCheck(HAL_MEMORY_ALLOCATION);
174 |
175 | const PVOID allocated = AllocateContiguousMemory(size);
176 | if (!allocated)
177 | KeBugCheck(HAL_MEMORY_ALLOCATION);
178 |
179 | RtlZeroMemory(allocated, size);
180 |
181 | return allocated;
182 | }
183 |
184 | PEPROCESS Utils::GetNextProcess(PEPROCESS input)
185 | {
186 | const PLIST_ENTRY currentListEntry = reinterpret_cast(reinterpret_cast(input) + Global::Offsets::ActiveProcessLinks);
187 | PLIST_ENTRY nextListEntry = currentListEntry->Flink;
188 | return reinterpret_cast(reinterpret_cast(nextListEntry) - Global::Offsets::ActiveProcessLinks);
189 | }
190 |
191 | PEPROCESS Utils::FindProcess(const HANDLE processId)
192 | {
193 | for (PEPROCESS current = PsInitialSystemProcess; current != nullptr; current = GetNextProcess(current))
194 | {
195 | const HANDLE currentId = PsGetProcessId(current);
196 | if (currentId == processId)
197 | return current;
198 | }
199 |
200 | return nullptr;
201 | }
202 |
203 | bool Utils::CheckMSR(UINT32 msr)
204 | {
205 | if (((msr > 0) && (msr < 0x00001FFF)) || ((msr > 0xC0000000) && (msr < 0xC0002FFF)) || (msr > 0xC0010000) && (msr < 0xC0011FFF))
206 | return true;
207 |
208 | return false;
209 | }
210 |
211 | bool Utils::ValidUsermodeAddress(const ULONG64 address)
212 | {
213 | if (address < 0x1000)
214 | return false;
215 |
216 | if (address > 0x7FFFFFFFFFFF)
217 | return false;
218 |
219 | return true;
220 | }
221 |
222 | void Utils::ReferenceObject(PVOID object)
223 | {
224 | _InterlockedIncrement64(static_cast(object) - 6);
225 | }
226 |
227 | void Utils::DereferenceObject(PVOID object)
228 | {
229 | _InterlockedExchangeAdd64(static_cast(object) - 6, -1);
230 | }
231 |
232 | PVOID Utils::GetPreallocatedPool(SIZE_T size)
233 | {
234 | if (size > PreallocatedPoolSize)
235 | KeBugCheck(BAD_EXHANDLE);
236 |
237 | static SIZE_T currentPoolIndex = 0;
238 | if (currentPoolIndex >= ARRAYSIZE(Global::PreallocatedPools))
239 | KeBugCheck(BAD_EXHANDLE);
240 |
241 | const PVOID pool = Global::PreallocatedPools[currentPoolIndex];
242 | currentPoolIndex++;
243 |
244 | RtlZeroMemory(pool, size);
245 |
246 | return pool;
247 | }
248 |
--------------------------------------------------------------------------------
/memhv/memhv/Source/Utils.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace Utils
4 | {
5 | typedef enum _SYSTEM_INFORMATION_CLASS
6 | {
7 | SystemInformationClassMin = 0,
8 | SystemBasicInformation = 0,
9 | SystemProcessorInformation = 1,
10 | SystemPerformanceInformation = 2,
11 | SystemTimeOfDayInformation = 3,
12 | SystemPathInformation = 4,
13 | SystemNotImplemented1 = 4,
14 | SystemProcessInformation = 5,
15 | SystemProcessesAndThreadsInformation = 5,
16 | SystemCallCountInfoInformation = 6,
17 | SystemCallCounts = 6,
18 | SystemDeviceInformation = 7,
19 | SystemConfigurationInformation = 7,
20 | SystemProcessorPerformanceInformation = 8,
21 | SystemProcessorTimes = 8,
22 | SystemFlagsInformation = 9,
23 | SystemGlobalFlag = 9,
24 | SystemCallTimeInformation = 10,
25 | SystemNotImplemented2 = 10,
26 | SystemModuleInformation = 11,
27 | SystemLocksInformation = 12,
28 | SystemLockInformation = 12,
29 | SystemStackTraceInformation = 13,
30 | SystemNotImplemented3 = 13,
31 | SystemPagedPoolInformation = 14,
32 | SystemNotImplemented4 = 14,
33 | SystemNonPagedPoolInformation = 15,
34 | SystemNotImplemented5 = 15,
35 | SystemHandleInformation = 16,
36 | SystemObjectInformation = 17,
37 | SystemPageFileInformation = 18,
38 | SystemPagefileInformation = 18,
39 | SystemVdmInstemulInformation = 19,
40 | SystemInstructionEmulationCounts = 19,
41 | SystemVdmBopInformation = 20,
42 | SystemInvalidInfoClass1 = 20,
43 | SystemFileCacheInformation = 21,
44 | SystemCacheInformation = 21,
45 | SystemPoolTagInformation = 22,
46 | SystemInterruptInformation = 23,
47 | SystemProcessorStatistics = 23,
48 | SystemDpcBehaviourInformation = 24,
49 | SystemDpcInformation = 24,
50 | SystemFullMemoryInformation = 25,
51 | SystemNotImplemented6 = 25,
52 | SystemLoadImage = 26,
53 | SystemUnloadImage = 27,
54 | SystemTimeAdjustmentInformation = 28,
55 | SystemTimeAdjustment = 28,
56 | SystemSummaryMemoryInformation = 29,
57 | SystemNotImplemented7 = 29,
58 | SystemNextEventIdInformation = 30,
59 | SystemNotImplemented8 = 30,
60 | SystemEventIdsInformation = 31,
61 | SystemNotImplemented9 = 31,
62 | SystemCrashDumpInformation = 32,
63 | SystemExceptionInformation = 33,
64 | SystemCrashDumpStateInformation = 34,
65 | SystemKernelDebuggerInformation = 35,
66 | SystemContextSwitchInformation = 36,
67 | SystemRegistryQuotaInformation = 37,
68 | SystemLoadAndCallImage = 38,
69 | SystemPrioritySeparation = 39,
70 | SystemPlugPlayBusInformation = 40,
71 | SystemNotImplemented10 = 40,
72 | SystemDockInformation = 41,
73 | SystemNotImplemented11 = 41,
74 | SystemInvalidInfoClass2 = 42,
75 | SystemProcessorSpeedInformation = 43,
76 | SystemInvalidInfoClass3 = 43,
77 | SystemCurrentTimeZoneInformation = 44,
78 | SystemTimeZoneInformation = 44,
79 | SystemLookasideInformation = 45,
80 | SystemSetTimeSlipEvent = 46,
81 | SystemCreateSession = 47,
82 | SystemDeleteSession = 48,
83 | SystemInvalidInfoClass4 = 49,
84 | SystemRangeStartInformation = 50,
85 | SystemVerifierInformation = 51,
86 | SystemAddVerifier = 52,
87 | SystemSessionProcessesInformation = 53,
88 | SystemInformationClassMax
89 | } SYSTEM_INFORMATION_CLASS;
90 |
91 | typedef struct _SYSTEM_MODULE
92 | {
93 | ULONG_PTR Reserved[2];
94 | PVOID Base;
95 | ULONG Size;
96 | ULONG Flags;
97 | USHORT Index;
98 | USHORT Unknown;
99 | USHORT LoadCount;
100 | USHORT ModuleNameOffset;
101 | CHAR ImageName[256];
102 | } SYSTEM_MODULE, * PSYSTEM_MODULE;
103 |
104 | typedef struct _SYSTEM_MODULE_INFORMATION
105 | {
106 | ULONG_PTR ulModuleCount;
107 | SYSTEM_MODULE Modules[1];
108 | } SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
109 |
110 | typedef struct _IOC_REQUEST
111 | {
112 | PVOID Buffer;
113 | ULONG Size;
114 | PVOID OriginalContext;
115 | PIO_COMPLETION_ROUTINE Original;
116 | } IOC_REQUEST, * PIOC_REQUEST;
117 |
118 | EXTERN_C
119 | {
120 | NTSTATUS ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS systemInformationClass, PVOID systemInformation, ULONG systemInformationLength, PULONG returnLength);
121 | NTSTATUS ObReferenceObjectByName(PUNICODE_STRING objectName, ULONG attributes, PACCESS_STATE accessState, ACCESS_MASK desiredAccess, POBJECT_TYPE objectType, KPROCESSOR_MODE accessMode, PVOID parseContext, PVOID* object);
122 | }
123 |
124 | EXTERN_C POBJECT_TYPE* IoDriverObjectType;
125 | EXTERN_C POBJECT_TYPE* IoDeviceObjectType;
126 |
127 | char* Compare(const char* haystack, const char* needle);
128 | PVOID GetModuleBase(const char* moduleName);
129 | ULONG64 FindPattern(void* baseAddress, ULONG64 size, const char* pattern);
130 | ULONG64 FindPatternImage(void* base, const char* pattern);
131 | PVOID AllocateContiguousMemory(SIZE_T size);
132 | NTSTATUS ExecuteOnEachProcessor(NTSTATUS(*callback)(PVOID), PVOID context);
133 | NTSTATUS GetCurrentDriverInfo(ULONG64* baseAddress, SIZE_T* size);
134 | PVOID AllocatePageAligned(SIZE_T size);
135 | PEPROCESS GetNextProcess(PEPROCESS input);
136 | PEPROCESS FindProcess(HANDLE processId);
137 | bool CheckMSR(UINT32 msr);
138 | bool ValidUsermodeAddress(ULONG64 address);
139 | void ReferenceObject(PVOID object);
140 | void DereferenceObject(PVOID object);
141 |
142 | const SIZE_T PreallocatedPoolSize = 0x1000;
143 | PVOID GetPreallocatedPool(SIZE_T poolIndex);
144 | }
145 |
--------------------------------------------------------------------------------
/memhv/memhv/memhv.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x64
7 |
8 |
9 | Release
10 | x64
11 |
12 |
13 | Debug
14 | ARM64
15 |
16 |
17 | Release
18 | ARM64
19 |
20 |
21 |
22 | {6917B525-F951-4618-8F29-07896190CA75}
23 | {1bc93793-694f-48fe-9372-81e2b05556fd}
24 | v4.5
25 | 12.0
26 | Debug
27 | x64
28 | memhv
29 | $(LatestTargetPlatformVersion)
30 |
31 |
32 |
33 | Windows10
34 | true
35 | WindowsKernelModeDriver10.0
36 | Driver
37 | KMDF
38 | Universal
39 |
40 |
41 | Windows10
42 | false
43 | WindowsKernelModeDriver10.0
44 | Driver
45 | KMDF
46 | Desktop
47 | false
48 |
49 |
50 | Windows10
51 | true
52 | WindowsKernelModeDriver10.0
53 | Driver
54 | KMDF
55 | Universal
56 |
57 |
58 | Windows10
59 | false
60 | WindowsKernelModeDriver10.0
61 | Driver
62 | KMDF
63 | Universal
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | DbgengKernelDebugger
74 |
75 |
76 | DbgengKernelDebugger
77 | false
78 |
79 |
80 | DbgengKernelDebugger
81 |
82 |
83 | DbgengKernelDebugger
84 |
85 |
86 |
87 | sha256
88 |
89 |
90 |
91 |
92 | sha256
93 |
94 |
95 | stdcpp20
96 | false
97 | 4996;%(DisableSpecificWarnings)
98 |
99 |
100 | Entry
101 |
102 |
103 |
104 |
105 | sha256
106 |
107 |
108 |
109 |
110 | sha256
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/memhv/memhv/memhv.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 | {c0a5b67b-cb84-4a55-8e96-89fcacc9b998}
10 |
11 |
12 | {b797d344-18c0-4d29-ad2f-5aa7d35b6076}
13 |
14 |
15 | {a1cce910-f87d-422b-9b58-20156ad824bc}
16 |
17 |
18 | {3d6d7aed-724c-4598-954d-e1fe453778eb}
19 |
20 |
21 |
22 |
23 | Source
24 |
25 |
26 | Source\SVM
27 |
28 |
29 | Source
30 |
31 |
32 | Source\SVM\Handlers
33 |
34 |
35 | Source\SVM\Handlers
36 |
37 |
38 | Source\SVM\Handlers
39 |
40 |
41 | Source\SVM\Handlers
42 |
43 |
44 | Source\Memory
45 |
46 |
47 |
48 |
49 | Source
50 |
51 |
52 | Source\SVM
53 |
54 |
55 | Source\SVM\Defines
56 |
57 |
58 | Source\SVM\Defines
59 |
60 |
61 | Source\SVM\Defines
62 |
63 |
64 | Source\SVM\Defines
65 |
66 |
67 | Source
68 |
69 |
70 | Source\SVM\Handlers
71 |
72 |
73 | Source
74 |
75 |
76 | Source\Memory
77 |
78 |
79 |
80 |
81 | Source\SVM
82 |
83 |
84 |
--------------------------------------------------------------------------------