├── .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 | ![screenshot](assets/screenshot.png) 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 | --------------------------------------------------------------------------------