├── .gitattributes ├── .gitignore ├── .gitmodules ├── Hypervisor ├── BEA │ ├── BeaEngine.h │ ├── BeaEngine5_x64.lib │ ├── basic_types.h │ └── export.h ├── CPUID.c ├── CPUID.h ├── Debug.h ├── EPT.c ├── EPT.h ├── GDT.c ├── GDT.h ├── GuestShim.c ├── GuestShim.h ├── HandlerShim.asm ├── HandlerShim.h ├── Handlers.c ├── Handlers.h ├── Hypervisor.c ├── Hypervisor.h ├── Hypervisor.vcxproj ├── Hypervisor.vcxproj.filters ├── Intrinsics.asm ├── Intrinsics.h ├── MSR.c ├── MSR.h ├── MTF.c ├── MTF.h ├── MTRR.c ├── MTRR.h ├── MemManage.c ├── MemManage.h ├── PageTable.c ├── PageTable.h ├── ProcessDefines.h ├── VMCALL.c ├── VMCALL.h ├── VMCALL_Common.h ├── VMCALL_Stub.asm ├── VMHook.c ├── VMHook.h ├── VMM.c ├── VMM.h ├── VMShadow.c └── VMShadow.h ├── HypervisorBase.sln └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ia32-doc"] 2 | path = ia32-doc 3 | url = https://github.com/wbenny/ia32-doc.git 4 | -------------------------------------------------------------------------------- /Hypervisor/BEA/BeaEngine.h: -------------------------------------------------------------------------------- 1 | /* Header for BeaEngine 4.x */ 2 | #ifndef _BEA_ENGINE_ 3 | #define _BEA_ENGINE_ 4 | 5 | #include "export.h" 6 | #include "basic_types.h" 7 | 8 | #if !defined(BEA_ENGINE_STATIC) 9 | #if defined(BUILD_BEA_ENGINE_DLL) 10 | #define BEA_API bea__api_export__ 11 | #else 12 | #define BEA_API bea__api_import__ 13 | #endif 14 | #else 15 | #define BEA_API 16 | #endif 17 | 18 | 19 | #define INSTRUCT_LENGTH 64 20 | 21 | #pragma pack(1) 22 | typedef struct { 23 | UInt8 W_; 24 | UInt8 R_; 25 | UInt8 X_; 26 | UInt8 B_; 27 | UInt8 state; 28 | } REX_Struct ; 29 | #pragma pack() 30 | 31 | #pragma pack(1) 32 | typedef struct { 33 | int Number; 34 | int NbUndefined; 35 | UInt8 LockPrefix; 36 | UInt8 OperandSize; 37 | UInt8 AddressSize; 38 | UInt8 RepnePrefix; 39 | UInt8 RepPrefix; 40 | UInt8 FSPrefix; 41 | UInt8 SSPrefix; 42 | UInt8 GSPrefix; 43 | UInt8 ESPrefix; 44 | UInt8 CSPrefix; 45 | UInt8 DSPrefix; 46 | UInt8 BranchTaken; 47 | UInt8 BranchNotTaken; 48 | REX_Struct REX; 49 | char alignment[2]; 50 | } PREFIXINFO ; 51 | #pragma pack() 52 | 53 | #pragma pack(1) 54 | typedef struct { 55 | UInt8 OF_; 56 | UInt8 SF_; 57 | UInt8 ZF_; 58 | UInt8 AF_; 59 | UInt8 PF_; 60 | UInt8 CF_; 61 | UInt8 TF_; 62 | UInt8 IF_; 63 | UInt8 DF_; 64 | UInt8 NT_; 65 | UInt8 RF_; 66 | UInt8 alignment; 67 | } EFLStruct ; 68 | #pragma pack() 69 | 70 | #pragma pack(4) 71 | typedef struct { 72 | Int64 BaseRegister; 73 | Int64 IndexRegister; 74 | Int32 Scale; 75 | Int64 Displacement; 76 | } MEMORYTYPE ; 77 | #pragma pack() 78 | 79 | #pragma pack(4) 80 | typedef struct { 81 | Int64 type; 82 | Int64 gpr; 83 | Int64 mmx; 84 | Int64 xmm; 85 | Int64 ymm; 86 | Int64 zmm; 87 | Int64 special; 88 | Int64 cr; 89 | Int64 dr; 90 | Int64 mem_management; 91 | Int64 mpx; 92 | Int64 opmask; 93 | Int64 segment; 94 | Int64 fpu; 95 | } REGISTERTYPE ; 96 | #pragma pack() 97 | 98 | #pragma pack(1) 99 | typedef struct { 100 | Int32 Category; 101 | Int32 Opcode; 102 | char Mnemonic[24]; 103 | Int32 BranchType; 104 | EFLStruct Flags; 105 | UInt64 AddrValue; 106 | Int64 Immediat; 107 | REGISTERTYPE ImplicitModifiedRegs; 108 | } INSTRTYPE; 109 | #pragma pack() 110 | 111 | #pragma pack(1) 112 | typedef struct { 113 | char OpMnemonic[24]; 114 | UInt64 OpType; 115 | Int32 OpSize; 116 | Int32 OpPosition; 117 | UInt32 AccessMode; 118 | MEMORYTYPE Memory; 119 | REGISTERTYPE Registers; 120 | UInt32 SegmentReg; 121 | } OPTYPE; 122 | #pragma pack() 123 | 124 | 125 | 126 | #pragma pack(1) 127 | typedef struct _Disasm { 128 | UIntPtr EIP; 129 | UInt64 VirtualAddr; 130 | UInt32 SecurityBlock; 131 | char CompleteInstr[INSTRUCT_LENGTH]; 132 | UInt32 Archi; 133 | UInt64 Options; 134 | INSTRTYPE Instruction; 135 | OPTYPE Operand1; 136 | OPTYPE Operand2; 137 | OPTYPE Operand3; 138 | OPTYPE Operand4; 139 | PREFIXINFO Prefix; 140 | Int32 Error; 141 | UInt32 Reserved_[48]; 142 | } DISASM, *PDISASM, *LPDISASM; 143 | #pragma pack() 144 | 145 | #define ESReg 1 146 | #define DSReg 2 147 | #define FSReg 3 148 | #define GSReg 4 149 | #define CSReg 5 150 | #define SSReg 6 151 | 152 | #define InvalidPrefix 4 153 | #define SuperfluousPrefix 2 154 | #define NotUsedPrefix 0 155 | #define MandatoryPrefix 8 156 | #define InUsePrefix 1 157 | 158 | #define LowPosition 0 159 | #define HighPosition 1 160 | 161 | enum INSTRUCTION_TYPE 162 | { 163 | GENERAL_PURPOSE_INSTRUCTION = 0x10000, 164 | FPU_INSTRUCTION = 0x20000, 165 | MMX_INSTRUCTION = 0x30000, 166 | SSE_INSTRUCTION = 0x40000, 167 | SSE2_INSTRUCTION = 0x50000, 168 | SSE3_INSTRUCTION = 0x60000, 169 | SSSE3_INSTRUCTION = 0x70000, 170 | SSE41_INSTRUCTION = 0x80000, 171 | SSE42_INSTRUCTION = 0x90000, 172 | SYSTEM_INSTRUCTION = 0xa0000, 173 | VM_INSTRUCTION = 0xb0000, 174 | UNDOCUMENTED_INSTRUCTION = 0xc0000, 175 | AMD_INSTRUCTION = 0xd0000, 176 | ILLEGAL_INSTRUCTION = 0xe0000, 177 | AES_INSTRUCTION = 0xf0000, 178 | CLMUL_INSTRUCTION = 0x100000, 179 | AVX_INSTRUCTION = 0x110000, 180 | AVX2_INSTRUCTION = 0x120000, 181 | MPX_INSTRUCTION = 0x130000, 182 | AVX512_INSTRUCTION = 0x140000, 183 | SHA_INSTRUCTION = 0x150000, 184 | BMI2_INSTRUCTION = 0x160000, 185 | CET_INSTRUCTION = 0x170000, 186 | BMI1_INSTRUCTION = 0x180000, 187 | XSAVEOPT_INSTRUCTION = 0x190000, 188 | FSGSBASE_INSTRUCTION = 0x1a0000, 189 | CLWB_INSTRUCTION = 0x1b0000, 190 | CLFLUSHOPT_INSTRUCTION = 0x1c0000, 191 | FXSR_INSTRUCTION = 0x1d0000, 192 | XSAVE_INSTRUCTION = 0x1e0000, 193 | SGX_INSTRUCTION = 0x1f0000, 194 | PCONFIG_INSTRUCTION = 0x200000, 195 | 196 | DATA_TRANSFER = 0x1, 197 | ARITHMETIC_INSTRUCTION, 198 | LOGICAL_INSTRUCTION, 199 | SHIFT_ROTATE, 200 | BIT_UInt8, 201 | CONTROL_TRANSFER, 202 | STRING_INSTRUCTION, 203 | InOutINSTRUCTION, 204 | ENTER_LEAVE_INSTRUCTION, 205 | FLAG_CONTROL_INSTRUCTION, 206 | SEGMENT_REGISTER, 207 | MISCELLANEOUS_INSTRUCTION, 208 | COMPARISON_INSTRUCTION, 209 | LOGARITHMIC_INSTRUCTION, 210 | TRIGONOMETRIC_INSTRUCTION, 211 | UNSUPPORTED_INSTRUCTION, 212 | LOAD_CONSTANTS, 213 | FPUCONTROL, 214 | STATE_MANAGEMENT, 215 | CONVERSION_INSTRUCTION, 216 | SHUFFLE_UNPACK, 217 | PACKED_SINGLE_PRECISION, 218 | SIMD128bits, 219 | SIMD64bits, 220 | CACHEABILITY_CONTROL, 221 | FP_INTEGER_CONVERSION, 222 | SPECIALIZED_128bits, 223 | SIMD_FP_PACKED, 224 | SIMD_FP_HORIZONTAL , 225 | AGENT_SYNCHRONISATION, 226 | PACKED_ALIGN_RIGHT , 227 | PACKED_SIGN, 228 | PACKED_BLENDING_INSTRUCTION, 229 | PACKED_TEST, 230 | PACKED_MINMAX, 231 | HORIZONTAL_SEARCH, 232 | PACKED_EQUALITY, 233 | STREAMING_LOAD, 234 | INSERTION_EXTRACTION, 235 | DOT_PRODUCT, 236 | SAD_INSTRUCTION, 237 | ACCELERATOR_INSTRUCTION, /* crc32, popcnt (sse4.2) */ 238 | ROUND_INSTRUCTION 239 | 240 | }; 241 | 242 | enum EFLAGS_STATES 243 | { 244 | TE_ = 1, 245 | MO_ = 2, 246 | RE_ = 4, 247 | SE_ = 8, 248 | UN_ = 0x10, 249 | PR_ = 0x20 250 | }; 251 | 252 | enum BRANCH_TYPE 253 | { 254 | JO = 1, 255 | JC = 2, 256 | JE = 3, 257 | JA = 4, 258 | JS = 5, 259 | JP = 6, 260 | JL = 7, 261 | JG = 8, 262 | JB = 2, /* JC == JB */ 263 | JECXZ = 10, 264 | JmpType = 11, 265 | CallType = 12, 266 | RetType = 13, 267 | JNO = -1, 268 | JNC = -2, 269 | JNE = -3, 270 | JNA = -4, 271 | JNS = -5, 272 | JNP = -6, 273 | JNL = -7, 274 | JNG = -8, 275 | JNB = -2 /* JNC == JNB */ 276 | }; 277 | 278 | enum ARGUMENTS_TYPE 279 | { 280 | NO_ARGUMENT = 0x10000, 281 | REGISTER_TYPE = 0x20000, 282 | MEMORY_TYPE = 0x30000, 283 | CONSTANT_TYPE = 0x40000, 284 | 285 | GENERAL_REG = 0x1, 286 | MMX_REG = 0x2, 287 | SSE_REG = 0x4, 288 | AVX_REG = 0x8, 289 | AVX512_REG = 0x10, 290 | SPECIAL_REG = 0x20, 291 | CR_REG = 0x40, 292 | DR_REG = 0x80, 293 | MEMORY_MANAGEMENT_REG = 0x100, 294 | MPX_REG = 0x200, 295 | OPMASK_REG = 0x400, 296 | SEGMENT_REG = 0x800, 297 | FPU_REG = 0x1000, 298 | 299 | RELATIVE_ = 0x4000000, 300 | ABSOLUTE_ = 0x8000000, 301 | 302 | READ = 0x1, 303 | WRITE = 0x2, 304 | 305 | REG0 = 0x1, 306 | REG1 = 0x2, 307 | REG2 = 0x4, 308 | REG3 = 0x8, 309 | REG4 = 0x10, 310 | REG5 = 0x20, 311 | REG6 = 0x40, 312 | REG7 = 0x80, 313 | REG8 = 0x100, 314 | REG9 = 0x200, 315 | REG10 = 0x400, 316 | REG11 = 0x800, 317 | REG12 = 0x1000, 318 | REG13 = 0x2000, 319 | REG14 = 0x4000, 320 | REG15 = 0x8000, 321 | REG16 = 0x10000, 322 | REG17 = 0x20000, 323 | REG18 = 0x40000, 324 | REG19 = 0x80000, 325 | REG20 = 0x100000, 326 | REG21 = 0x200000, 327 | REG22 = 0x400000, 328 | REG23 = 0x800000, 329 | REG24 = 0x1000000, 330 | REG25 = 0x2000000, 331 | REG26 = 0x4000000, 332 | REG27 = 0x8000000, 333 | REG28 = 0x10000000, 334 | REG29 = 0x20000000, 335 | REG30 = 0x40000000, 336 | REG31 = 0x80000000 337 | 338 | }; 339 | 340 | enum SPECIAL_INFO 341 | { 342 | UNKNOWN_OPCODE = -1, 343 | OUT_OF_BLOCK = -2, 344 | 345 | /* === mask = 0xff */ 346 | NoTabulation = 0x00000000, 347 | Tabulation = 0x00000001, 348 | 349 | /* === mask = 0xff00 */ 350 | MasmSyntax = 0x00000000, 351 | GoAsmSyntax = 0x00000100, 352 | NasmSyntax = 0x00000200, 353 | ATSyntax = 0x00000400, 354 | IntrinsicMemSyntax= 0x00000800, 355 | 356 | /* === mask = 0xff0000 */ 357 | PrefixedNumeral = 0x00010000, 358 | SuffixedNumeral = 0x00000000, 359 | 360 | /* === mask = 0xff000000 */ 361 | ShowSegmentRegs = 0x01000000, 362 | ShowEVEXMasking = 0x02000000 363 | }; 364 | 365 | #define UD_ 2 366 | #define DE__ 3 367 | 368 | #ifdef __cplusplus 369 | extern "C" { 370 | #endif 371 | 372 | BEA_API int __bea_callspec__ Disasm (LPDISASM pDisAsm); 373 | BEA_API const__ char* __bea_callspec__ BeaEngineVersion (void); 374 | BEA_API const__ char* __bea_callspec__ BeaEngineRevision (void); 375 | 376 | #ifdef __cplusplus 377 | } 378 | #endif 379 | 380 | #endif 381 | -------------------------------------------------------------------------------- /Hypervisor/BEA/BeaEngine5_x64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/POPFD/HypervisorBase/f12ca3493882610e88cf9057a36f9c404d7131c2/Hypervisor/BEA/BeaEngine5_x64.lib -------------------------------------------------------------------------------- /Hypervisor/BEA/basic_types.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file basic_types.h 3 | * @author 4 | * @date Thu Dec 24 19:31:22 2009 5 | * 6 | * @brief Definitions of fixed-size integer types for various platforms 7 | * 8 | * This file is part of BeaEngine. 9 | * 10 | * BeaEngine is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Lesser General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * BeaEngine is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Lesser General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Lesser General Public License 21 | * along with BeaEngine. If not, see . */ 22 | 23 | #ifndef __BEA_BASIC_TYPES_HPP__ 24 | #define __BEA_BASIC_TYPES_HPP__ 25 | 26 | #include 27 | 28 | #if defined(__GNUC__) || defined (__INTEL_COMPILER) || defined(__LCC__) 29 | #include 30 | #endif 31 | 32 | #if defined(_MSC_VER) 33 | /* 34 | * Windows/Visual C++ 35 | */ 36 | typedef signed char Int8; 37 | typedef unsigned char UInt8; 38 | typedef signed short Int16; 39 | typedef unsigned short UInt16; 40 | typedef signed int Int32; 41 | typedef unsigned int UInt32; 42 | typedef signed __int64 Int64; 43 | typedef unsigned __int64 UInt64; 44 | #if defined(_WIN64) 45 | #define BEA_PTR_IS_64_BIT 1 46 | typedef signed __int64 IntPtr; 47 | typedef unsigned __int64 UIntPtr; 48 | #else 49 | typedef signed long IntPtr; 50 | typedef size_t UIntPtr; 51 | #endif 52 | #define BEA_HAVE_INT64 1 53 | #elif defined(__GNUC__) || defined(__LCC__) 54 | /* 55 | * Unix/GCC 56 | */ 57 | typedef signed char Int8; 58 | typedef unsigned char UInt8; 59 | typedef signed short Int16; 60 | typedef unsigned short UInt16; 61 | typedef signed int Int32; 62 | typedef unsigned int UInt32; 63 | typedef intptr_t IntPtr; 64 | typedef uintptr_t UIntPtr; 65 | #if defined(__LP64__) 66 | #define BEA_PTR_IS_64_BIT 1 67 | #define BEA_LONG_IS_64_BIT 1 68 | typedef signed long Int64; 69 | typedef unsigned long UInt64; 70 | #else 71 | #if defined (__INTEL_COMPILER) || defined (__ICC) || defined (_ICC) 72 | typedef __int64 Int64; 73 | typedef unsigned __int64 UInt64; 74 | #else 75 | typedef signed long long Int64; 76 | typedef unsigned long long UInt64; 77 | #endif 78 | #endif 79 | #define BEA_HAVE_INT64 1 80 | #elif defined(__DECCXX) 81 | /* 82 | * Compaq C++ 83 | */ 84 | typedef signed char Int8; 85 | typedef unsigned char UInt8; 86 | typedef signed short Int16; 87 | typedef unsigned short UInt16; 88 | typedef signed int Int32; 89 | typedef unsigned int UInt32; 90 | typedef signed __int64 Int64; 91 | typedef unsigned __int64 UInt64; 92 | #if defined(__VMS) 93 | #if defined(__32BITS) 94 | typedef signed long IntPtr; 95 | typedef unsigned long UIntPtr; 96 | #else 97 | typedef Int64 IntPtr; 98 | typedef UInt64 UIntPtr; 99 | #define BEA_PTR_IS_64_BIT 1 100 | #endif 101 | #else 102 | typedef signed long IntPtr; 103 | typedef unsigned long UIntPtr; 104 | #define BEA_PTR_IS_64_BIT 1 105 | #define BEA_LONG_IS_64_BIT 1 106 | #endif 107 | #define BEA_HAVE_INT64 1 108 | #elif defined(__HP_aCC) 109 | /* 110 | * HP Ansi C++ 111 | */ 112 | typedef signed char Int8; 113 | typedef unsigned char UInt8; 114 | typedef signed short Int16; 115 | typedef unsigned short UInt16; 116 | typedef signed int Int32; 117 | typedef unsigned int UInt32; 118 | typedef signed long IntPtr; 119 | typedef unsigned long UIntPtr; 120 | #if defined(__LP64__) 121 | #define BEA_PTR_IS_64_BIT 1 122 | #define BEA_LONG_IS_64_BIT 1 123 | typedef signed long Int64; 124 | typedef unsigned long UInt64; 125 | #else 126 | typedef signed long long Int64; 127 | typedef unsigned long long UInt64; 128 | #endif 129 | #define BEA_HAVE_INT64 1 130 | #elif defined(__SUNPRO_CC) || defined(__SUNPRO_C) 131 | /* 132 | * SUN Forte C++ 133 | */ 134 | typedef signed char Int8; 135 | typedef unsigned char UInt8; 136 | typedef signed short Int16; 137 | typedef unsigned short UInt16; 138 | typedef signed int Int32; 139 | typedef unsigned int UInt32; 140 | typedef signed long IntPtr; 141 | typedef unsigned long UIntPtr; 142 | #if defined(__sparcv9) 143 | #define BEA_PTR_IS_64_BIT 1 144 | #define BEA_LONG_IS_64_BIT 1 145 | typedef signed long Int64; 146 | typedef unsigned long UInt64; 147 | #else 148 | typedef signed long long Int64; 149 | typedef unsigned long long UInt64; 150 | #endif 151 | #define BEA_HAVE_INT64 1 152 | #elif defined(__IBMCPP__) 153 | /* 154 | * IBM XL C++ 155 | */ 156 | typedef signed char Int8; 157 | typedef unsigned char UInt8; 158 | typedef signed short Int16; 159 | typedef unsigned short UInt16; 160 | typedef signed int Int32; 161 | typedef unsigned int UInt32; 162 | typedef signed long IntPtr; 163 | typedef unsigned long UIntPtr; 164 | #if defined(__64BIT__) 165 | #define BEA_PTR_IS_64_BIT 1 166 | #define BEA_LONG_IS_64_BIT 1 167 | typedef signed long Int64; 168 | typedef unsigned long UInt64; 169 | #else 170 | typedef signed long long Int64; 171 | typedef unsigned long long UInt64; 172 | #endif 173 | #define BEA_HAVE_INT64 1 174 | #elif defined(__BORLANDC__) 175 | /* 176 | * Borland C/C++ 177 | */ 178 | typedef signed char Int8; 179 | typedef unsigned char UInt8; 180 | typedef signed short Int16; 181 | typedef unsigned short UInt16; 182 | typedef signed int Int32; 183 | typedef unsigned int UInt32; 184 | typedef unsigned __int64 Int64; 185 | typedef signed __int64 UInt64; 186 | typedef unsigned long UIntPtr; 187 | #define BEA_HAVE_INT64 1 188 | #elif defined(__WATCOMC__) 189 | /* 190 | * Watcom C/C++ 191 | */ 192 | typedef signed char Int8; 193 | typedef unsigned char UInt8; 194 | typedef signed short Int16; 195 | typedef unsigned short UInt16; 196 | typedef signed int Int32; 197 | typedef unsigned int UInt32; 198 | typedef unsigned __int64 Int64; 199 | typedef signed __int64 UInt64; 200 | #define BEA_HAVE_INT64 1 201 | typedef size_t UIntPtr; 202 | #elif defined(__sgi) 203 | /* 204 | * MIPSpro C++ 205 | */ 206 | typedef signed char Int8; 207 | typedef unsigned char UInt8; 208 | typedef signed short Int16; 209 | typedef unsigned short UInt16; 210 | typedef signed int Int32; 211 | typedef unsigned int UInt32; 212 | typedef signed long IntPtr; 213 | typedef unsigned long UIntPtr; 214 | #if _MIPS_SZLONG == 64 215 | #define BEA_PTR_IS_64_BIT 1 216 | #define BEA_LONG_IS_64_BIT 1 217 | typedef signed long Int64; 218 | typedef unsigned long UInt64; 219 | #else 220 | typedef signed long long Int64; 221 | typedef unsigned long long UInt64; 222 | #endif 223 | #define BEA_HAVE_INT64 1 224 | #endif 225 | 226 | #if defined(_MSC_VER) || defined(__BORLANDC__) 227 | #define W64LIT(x) x##ui64 228 | #else 229 | #define W64LIT(x) x##ULL 230 | #endif 231 | 232 | 233 | #ifndef C_STATIC_ASSERT 234 | #define C_STATIC_ASSERT(tag_name, x) \ 235 | typedef int cache_static_assert_ ## tag_name[(x) * 2-1] 236 | #endif 237 | 238 | C_STATIC_ASSERT(sizeof_Int8 , (sizeof(Int8) == 1)); 239 | C_STATIC_ASSERT(sizeof_UInt8, (sizeof(UInt8) == 1)); 240 | 241 | C_STATIC_ASSERT(sizeof_Int16 , (sizeof(Int16) == 2)); 242 | C_STATIC_ASSERT(sizeof_UInt16, (sizeof(UInt16) == 2)); 243 | 244 | C_STATIC_ASSERT(sizeof_Int32 , (sizeof(Int32) == 4)); 245 | C_STATIC_ASSERT(sizeof_UInt32, (sizeof(UInt32) == 4)); 246 | 247 | C_STATIC_ASSERT(sizeof_Int64 , (sizeof(Int64) == 8)); 248 | C_STATIC_ASSERT(sizeof_UInt64, (sizeof(UInt64) == 8)); 249 | 250 | #endif 251 | -------------------------------------------------------------------------------- /Hypervisor/BEA/export.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file export.h 3 | * @author igor.gutnik@gmail.com 4 | * @date Mon Sep 22 09:28:54 2008 5 | * 6 | * @brief This file sets things up for C dynamic library function definitions and 7 | * static inlined functions 8 | * 9 | * This file is part of BeaEngine. 10 | * 11 | * BeaEngine is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * BeaEngine is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public License 22 | * along with BeaEngine. If not, see . */ 23 | 24 | #ifndef __BEA_EXPORT_H__ 25 | #define __BEA_EXPORT_H__ 26 | 27 | 28 | /* Set up for C function definitions, even when using C++ */ 29 | 30 | #ifdef __cplusplus 31 | #define CPP_VISIBLE_BEGIN extern "C" { 32 | #define CPP_VISIBLE_END } 33 | #else 34 | #define CPP_VISIBLE_BEGIN 35 | #define CPP_VISIBLE_END 36 | #endif 37 | 38 | #if defined(_MSC_VER) 39 | #pragma warning( disable: 4251 ) 40 | #endif 41 | 42 | /* Some compilers use a special export keyword */ 43 | #ifndef bea__api_export__ 44 | # if defined(__BEOS__) 45 | # if defined(__GNUC__) 46 | # define bea__api_export__ __declspec(dllexport) 47 | # else 48 | # define bea__api_export__ __declspec(export) 49 | # endif 50 | # elif defined(_WIN32) || defined(_WIN64) 51 | # ifdef __BORLANDC__ 52 | # define bea__api_export__ __declspec(dllexport) 53 | # define bea__api_import__ __declspec(dllimport) 54 | # elif defined(__WATCOMC__) 55 | # define bea__api_export__ __declspec(dllexport) 56 | # define bea__api_import__ 57 | # else 58 | # define bea__api_export__ __declspec(dllexport) 59 | # define bea__api_import__ __declspec(dllimport) 60 | # endif 61 | # elif defined(__OS2__) 62 | # ifdef __WATCOMC__ 63 | # define bea__api_export__ __declspec(dllexport) 64 | # define bea__api_import__ 65 | # else 66 | # define bea__api_export__ 67 | # define bea__api_import__ 68 | # endif 69 | # else 70 | # if defined(_WIN32) && defined(__GNUC__) && __GNUC__ >= 4 71 | # define bea__api_export__ __attribubea__ ((visibility("default"))) 72 | # define bea__api_import__ __attribubea__ ((visibility("default"))) 73 | # else 74 | # define bea__api_export__ 75 | # define bea__api_import__ 76 | # endif 77 | # endif 78 | #endif 79 | 80 | /* Use C calling convention by default*/ 81 | 82 | #ifndef __bea_callspec__ 83 | #if defined(BEA_USE_STDCALL) 84 | #if defined(__WIN32__) || defined(WIN32) || defined(_WIN32) || defined(_WIN64) 85 | #if defined(__BORLANDC__) || defined(__WATCOMC__) || defined(_MSC_VER) || defined(__MINGW32__) || defined(__POCC__) 86 | #define __bea_callspec__ __stdcall 87 | #else 88 | #define __bea_callspec__ 89 | #endif 90 | #else 91 | #ifdef __OS2__ 92 | #define __bea_callspec__ _System 93 | #else 94 | #define __bea_callspec__ 95 | #endif 96 | #endif 97 | #else 98 | #define __bea_callspec__ 99 | #endif 100 | #endif 101 | 102 | #ifdef __SYMBIAN32__ 103 | # ifndef EKA2 104 | # undef bea__api_export__ 105 | # undef bea__api_import__ 106 | # define bea__api_export__ 107 | # define bea__api_import__ 108 | # elif !defined(__WINS__) 109 | # undef bea__api_export__ 110 | # undef bea__api_import__ 111 | # define bea__api_export__ __declspec(dllexport) 112 | # define bea__api_import__ __declspec(dllexport) 113 | # endif /* !EKA2 */ 114 | #endif /* __SYMBIAN32__ */ 115 | 116 | 117 | #if defined(__GNUC__) && (__GNUC__ > 2) 118 | #define BEA_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) 119 | #define BEA_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) 120 | #else 121 | #define BEA_EXPECT_CONDITIONAL(c) (c) 122 | #define BEA_UNEXPECT_CONDITIONAL(c) (c) 123 | #endif 124 | 125 | 126 | /* Set up compiler-specific options for inlining functions */ 127 | #ifndef BEA_HAS_INLINE 128 | #if defined(__GNUC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) 129 | #define BEA_HAS_INLINE 130 | #else 131 | /* Add any special compiler-specific cases here */ 132 | #if defined(_MSC_VER) || defined(__BORLANDC__) || \ 133 | defined(__DMC__) || defined(__SC__) || \ 134 | defined(__WATCOMC__) || defined(__LCC__) || \ 135 | defined(__DECC) || defined(__EABI__) 136 | #ifndef __inline__ 137 | #define __inline__ __inline 138 | #endif 139 | #define BEA_HAS_INLINE 140 | #else 141 | #if !defined(__MRC__) && !defined(_SGI_SOURCE) 142 | #ifndef __inline__ 143 | #define __inline__ inline 144 | #endif 145 | #define BEA_HAS_INLINE 146 | #endif /* Not a funky compiler */ 147 | #endif /* Visual C++ */ 148 | #endif /* GNU C */ 149 | #endif /* CACHE_HAS_INLINE */ 150 | 151 | /* If inlining isn't supported, remove "__inline__", turning static 152 | inlined functions into static functions (resulting in code bloat 153 | in all files which include the offending header files) 154 | */ 155 | #ifndef BEA_HAS_INLINE 156 | #define __inline__ 157 | #endif 158 | 159 | /* fix a bug with gcc under windows */ 160 | 161 | #if defined(__WIN32__) || defined(WIN32) || defined(_WIN32) || defined(_WIN64) 162 | #if defined(__MINGW32__) 163 | #define const__ 164 | #else 165 | #define const__ const 166 | #endif 167 | #else 168 | #define const__ const 169 | #endif 170 | 171 | 172 | 173 | #endif 174 | -------------------------------------------------------------------------------- /Hypervisor/CPUID.c: -------------------------------------------------------------------------------- 1 | #include "CPUID.h" 2 | 3 | /******************** External API ********************/ 4 | 5 | 6 | /******************** Module Typedefs ********************/ 7 | 8 | /* CPUID register indexes */ 9 | typedef enum 10 | { 11 | CPUID_REGISTER_EAX = 0x00, 12 | CPUID_REGISTER_EBX, 13 | CPUID_REGISTER_ECX, 14 | CPUID_REGISTER_EDX, 15 | CPUID_REGISTER_COUNT /* Used for calculating number of ID registers*/ 16 | } CPUID_REGISTER_INDEX; 17 | 18 | /******************** Module Constants ********************/ 19 | 20 | /* CPUID processor info bits */ 21 | #define CPUID_VI_BIT_VMX_EXTENSION 0x20 22 | #define CPUID_VI_BIT_HYPERVISOR_PRESENT 0x80000000 23 | 24 | /******************** Module Variables ********************/ 25 | 26 | 27 | /******************** Module Prototypes ********************/ 28 | 29 | 30 | /******************** Public Code ********************/ 31 | 32 | BOOLEAN CPUID_handle(PVMM_DATA lpData) 33 | { 34 | UNREFERENCED_PARAMETER(lpData); 35 | 36 | /* Call CPUID instruction based on the indexes in the logical processors RAX and RCX registers.*/ 37 | INT32 cpuInfo[CPUID_REGISTER_COUNT]; 38 | __cpuidex(cpuInfo, (INT32)lpData->guestContext.Rax, (INT32)lpData->guestContext.Rcx); 39 | 40 | /* Override certain conditions. */ 41 | switch (lpData->guestContext.Rax) 42 | { 43 | case CPUID_SIGNATURE: 44 | { 45 | /* Replace the vendor string with ours. */ 46 | //cpuInfo[CPUID_REGISTER_EBX] = 'ekaF'; 47 | //cpuInfo[CPUID_REGISTER_EDX] = 'etnI'; 48 | //cpuInfo[CPUID_REGISTER_ECX] = '!!!l'; 49 | break; 50 | } 51 | 52 | case CPUID_VERSION_INFORMATION: 53 | { 54 | /* Hide the presence of the hypervisor and support for it. */ 55 | //cpuInfo[CPUID_REGISTER_ECX] = (cpuInfo[CPUID_REGISTER_ECX] & ~CPUID_VI_BIT_VMX_EXTENSION); 56 | cpuInfo[CPUID_REGISTER_ECX] = (cpuInfo[CPUID_REGISTER_ECX] & ~CPUID_VI_BIT_HYPERVISOR_PRESENT); 57 | break; 58 | } 59 | } 60 | 61 | /* Copy the modified CPU info into the guests registers. */ 62 | lpData->guestContext.Rax = cpuInfo[CPUID_REGISTER_EAX]; 63 | lpData->guestContext.Rbx = cpuInfo[CPUID_REGISTER_EBX]; 64 | lpData->guestContext.Rcx = cpuInfo[CPUID_REGISTER_ECX]; 65 | lpData->guestContext.Rdx = cpuInfo[CPUID_REGISTER_EDX]; 66 | 67 | return TRUE; 68 | } 69 | 70 | /******************** Module Code ********************/ -------------------------------------------------------------------------------- /Hypervisor/CPUID.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "VMM.h" 4 | 5 | /******************** Public Defines ********************/ 6 | 7 | /******************** Public Typedefs ********************/ 8 | 9 | /******************** Public Constants ********************/ 10 | 11 | /******************** Public Variables ********************/ 12 | 13 | /******************** Public Prototypes ********************/ 14 | 15 | BOOLEAN CPUID_handle(PVMM_DATA lpData); 16 | -------------------------------------------------------------------------------- /Hypervisor/Debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | /******************** Public Typedefs ********************/ 5 | 6 | /******************** Public Constants ********************/ 7 | 8 | #ifdef _DEBUG 9 | #define DEBUG_PRINT(format, ...) DbgPrint(format, ##__VA_ARGS__) 10 | #else 11 | #define DEBUG_PRINT(format, ...) 12 | #endif 13 | 14 | /******************** Public Variables ********************/ 15 | 16 | /******************** Public Prototypes ********************/ 17 | -------------------------------------------------------------------------------- /Hypervisor/EPT.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "EPT.h" 4 | #include "Intrinsics.h" 5 | #include "Debug.h" 6 | 7 | /******************** External API ********************/ 8 | 9 | 10 | /******************** Module Typedefs ********************/ 11 | 12 | 13 | /******************** Module Constants ********************/ 14 | 15 | 16 | /******************** Module Variables ********************/ 17 | 18 | 19 | /******************** Module Prototypes ********************/ 20 | static UINT32 adjustEffectiveMemoryType(const PMTRR_RANGE mtrrTable, UINT64 pageAddress, UINT32 desiredType); 21 | 22 | /******************** Public Code ********************/ 23 | 24 | void EPT_initialise(PEPT_CONFIG eptConfig, const PMTRR_RANGE mtrrTable) 25 | { 26 | DEBUG_PRINT("Initialising the EPT for the virtual machine.\r\n"); 27 | 28 | /* Initialise the linked list used for holding violation handlers. */ 29 | InitializeListHead(&eptConfig->handlerList); 30 | 31 | /* Initialise the linked list used for holding split pages. */ 32 | InitializeListHead(&eptConfig->dynamicSplitList); 33 | 34 | /* Create the EPT pointer for the structure. */ 35 | eptConfig->eptPointer.PageWalkLength = 3; 36 | eptConfig->eptPointer.MemoryType = MEMORY_TYPE_WRITE_BACK; 37 | eptConfig->eptPointer.PageFrameNumber = MmGetPhysicalAddress(&eptConfig->PML4).QuadPart / PAGE_SIZE; 38 | 39 | /* Fill out the PML4 which covers the first 512GB of RAM. */ 40 | eptConfig->PML4[0].ReadAccess = 1; 41 | eptConfig->PML4[0].WriteAccess = 1; 42 | eptConfig->PML4[0].ExecuteAccess = 1; 43 | 44 | /* Set the page frame number to the page index of the PDPT. */ 45 | eptConfig->PML4[0].PageFrameNumber = MmGetPhysicalAddress(&eptConfig->PML3).QuadPart / PAGE_SIZE; 46 | 47 | /* Create a RWX PDPTE. */ 48 | EPT_PML3_POINTER tempPML3P = { 0 }; 49 | tempPML3P.ReadAccess = 1; 50 | tempPML3P.WriteAccess = 1; 51 | tempPML3P.ExecuteAccess = 1; 52 | 53 | /* Store the temporarily created PDPTE to each of the entries in the page directory pointer table. */ 54 | __stosq((UINT64*)eptConfig->PML3, tempPML3P.Flags, EPT_PML3E_COUNT); 55 | 56 | /* Set the page frame numbers for each of the created PDPTEs so that they 57 | * link to their root respective page data entry. */ 58 | for (UINT32 i = 0; i < EPT_PML3E_COUNT; i++) 59 | { 60 | eptConfig->PML3[i].PageFrameNumber = MmGetPhysicalAddress(&eptConfig->PML2[i][0]).QuadPart / PAGE_SIZE; 61 | } 62 | 63 | /* Create a large PDE. */ 64 | EPT_PML2_2MB tempLargePML2E = { 0 }; 65 | tempLargePML2E.ReadAccess = 1; 66 | tempLargePML2E.WriteAccess = 1; 67 | tempLargePML2E.ExecuteAccess = 1; 68 | tempLargePML2E.LargePage = 1; 69 | 70 | /* Store the temporarily create LARGE_PDE to each of the entries in the page directory table. */ 71 | __stosq((UINT64*)eptConfig->PML2, tempLargePML2E.Flags, EPT_PML3E_COUNT * EPT_PML2E_COUNT); 72 | 73 | /* Loop every 1GB of RAM (described by the PDPTE). */ 74 | for (UINT32 i = 0; i < EPT_PML3E_COUNT; i++) 75 | { 76 | /* Construct the EPT identity map for every 2MB of RAM. */ 77 | for (UINT32 j = 0; j < EPT_PML2E_COUNT; j++) 78 | { 79 | eptConfig->PML2[i][j].PageFrameNumber = ((UINT64)i * EPT_PML3E_COUNT) + j; 80 | 81 | /* Calculate the page physical address, by using the page number and multiplying it 82 | * by the size of a LARGE_PAGE. */ 83 | UINT64 largePageAddress = eptConfig->PML2[i][j].PageFrameNumber * SIZE_2MB; 84 | 85 | /* Adjust the type for each page entry based on the MTRR table. 86 | * We want to use writeback, unless the page address falls within a MTRR entry. */ 87 | UINT32 adjustedType = adjustEffectiveMemoryType(mtrrTable, largePageAddress, MEMORY_TYPE_WRITE_BACK); 88 | 89 | eptConfig->PML2[i][j].MemoryType = adjustedType; 90 | } 91 | } 92 | } 93 | 94 | BOOLEAN EPT_handleViolation(PEPT_CONFIG eptConfig, PCONTEXT guestContext) 95 | { 96 | /* Result indicates handled successfully. */ 97 | BOOLEAN result = FALSE; 98 | 99 | /* Get the physical address of the page that caused the violation. */ 100 | PHYSICAL_ADDRESS violationGuestPA; 101 | __vmx_vmread(VMCS_GUEST_PHYSICAL_ADDRESS, (SIZE_T*)&violationGuestPA.QuadPart); 102 | 103 | /* Search the list of EPT handlers and determine which one to call. */ 104 | for (PLIST_ENTRY currentEntry = eptConfig->handlerList.Flink; 105 | currentEntry != &eptConfig->handlerList; 106 | currentEntry = currentEntry->Flink) 107 | { 108 | /* Use the CONTAINING_RECORD macro to get the actual record 109 | * the linked list is holding. */ 110 | PEPT_HANDLER eptHandler = CONTAINING_RECORD(currentEntry, EPT_HANDLER, listEntry); 111 | 112 | /* Check to see if the physical address associated with the handler matches. */ 113 | if ((violationGuestPA.QuadPart >= eptHandler->physRange.start.QuadPart) && 114 | (violationGuestPA.QuadPart <= eptHandler->physRange.end.QuadPart)) 115 | { 116 | result = eptHandler->callback(eptConfig, guestContext, eptHandler->userParameter); 117 | break; 118 | } 119 | } 120 | 121 | if (FALSE == result) 122 | { 123 | /*** DEBUG ***/ 124 | 125 | /* Print the information regarding where the violation took place. */ 126 | PVOID virtPA = MmGetVirtualForPhysical(violationGuestPA); 127 | 128 | DEBUG_PRINT("Unhandled EPT violation: PhysAlign %p\tPhysReal %p\tVirtReal %p\n", 129 | PAGE_ALIGN(violationGuestPA.QuadPart), 130 | (PVOID)violationGuestPA.QuadPart, 131 | virtPA); 132 | 133 | /* Print the guest RIP. */ 134 | SIZE_T guestRIP; 135 | __vmx_vmread(VMCS_GUEST_RIP, &guestRIP); 136 | DEBUG_PRINT("\tGuest RIP: %p\n\n", (PVOID)guestRIP); 137 | 138 | /* Print the violation qualification information. */ 139 | VMX_EXIT_QUALIFICATION_EPT_VIOLATION qualification; 140 | __vmx_vmread(VMCS_EXIT_QUALIFICATION, &qualification.Flags); 141 | 142 | DEBUG_PRINT("\tQualification.ReadAccess: 0x%I64X\n", qualification.ReadAccess); 143 | DEBUG_PRINT("\tQualification.WriteAccess: 0x%I64X\n", qualification.WriteAccess); 144 | DEBUG_PRINT("\tQualification.ExecuteAccess: 0x%I64X\n", qualification.ExecuteAccess); 145 | DEBUG_PRINT("\tQualification.EptReadable: 0x%I64X\n", qualification.EptReadable); 146 | DEBUG_PRINT("\tQualification.EptWriteable: 0x%I64X\n", qualification.EptWriteable); 147 | DEBUG_PRINT("\tQualification.EptExecutable: 0x%I64X\n", qualification.EptExecutable); 148 | DEBUG_PRINT("\tQualification.EptExecutableForUserMode: 0x%I64X\n", qualification.EptExecutableForUserMode); 149 | DEBUG_PRINT("\tQualification.ValidGuestLinearAddress: 0x%I64X\n", qualification.ValidGuestLinearAddress); 150 | DEBUG_PRINT("\tQualification.CausedByTranslation: 0x%I64X\n", qualification.CausedByTranslation); 151 | DEBUG_PRINT("\tQualification.UserModeLinearAddress: 0x%I64X\n", qualification.UserModeLinearAddress); 152 | DEBUG_PRINT("\tQualification.ReadableWritablePage: 0x%I64X\n", qualification.ReadableWritablePage); 153 | DEBUG_PRINT("\tQualification.ExecuteDisablePage: 0x%I64X\n", qualification.ExecuteDisablePage); 154 | DEBUG_PRINT("\tQualification.NmiUnblocking: 0x%I64X\n", qualification.NmiUnblocking); 155 | DEBUG_PRINT("\tQualification.Reserved1: 0x%I64X\n", qualification.Reserved1); 156 | 157 | DbgBreakPoint(); 158 | } 159 | 160 | return result; 161 | } 162 | 163 | NTSTATUS EPT_addViolationHandler(PEPT_CONFIG eptConfig, PHYSICAL_RANGE physicalRange, fnEPTHandlerCallback callback, PVOID userParameter) 164 | { 165 | NTSTATUS status; 166 | 167 | if (NULL != callback) 168 | { 169 | /* Allocate a new handler structure that will be used for traversal later. */ 170 | PEPT_HANDLER newHandler = (PEPT_HANDLER)ExAllocatePool(NonPagedPoolNx, sizeof(EPT_HANDLER)); 171 | if (NULL != newHandler) 172 | { 173 | newHandler->physRange = physicalRange; 174 | newHandler->callback = callback; 175 | newHandler->userParameter = userParameter; 176 | 177 | /* Add this structure to the linked list of already existing handlers. */ 178 | InsertHeadList(&eptConfig->handlerList, &newHandler->listEntry); 179 | status = STATUS_SUCCESS; 180 | } 181 | else 182 | { 183 | status = STATUS_NO_MEMORY; 184 | } 185 | } 186 | else 187 | { 188 | status = STATUS_INVALID_PARAMETER; 189 | } 190 | 191 | return status; 192 | } 193 | 194 | NTSTATUS EPT_splitLargePage(PEPT_CONFIG eptConfig, PHYSICAL_ADDRESS physicalAddress) 195 | { 196 | NTSTATUS status; 197 | 198 | /* Find the PML2E that relates to the physical address. */ 199 | PEPT_PML2_2MB targetPML2E = EPT_getPML2EFromAddress(eptConfig, physicalAddress); 200 | 201 | if (NULL != targetPML2E) 202 | { 203 | /* Check to see if the PDE is marked as a large page, if it isn't 204 | * then we don't have to split it as it is already done. */ 205 | if (FALSE != targetPML2E->LargePage) 206 | { 207 | PEPT_DYNAMIC_SPLIT newSplit = (PEPT_DYNAMIC_SPLIT)ExAllocatePool(NonPagedPoolNx, sizeof(EPT_DYNAMIC_SPLIT)); 208 | 209 | if (NULL != newSplit) 210 | { 211 | newSplit->pml2Entry = targetPML2E; 212 | 213 | /* Make a template for RWX. */ 214 | EPT_PML1_ENTRY tempPML1 = { 0 }; 215 | tempPML1.ReadAccess = 1; 216 | tempPML1.WriteAccess = 1; 217 | tempPML1.ExecuteAccess = 1; 218 | tempPML1.MemoryType = targetPML2E->MemoryType; 219 | tempPML1.IgnorePat = targetPML2E->IgnorePat; 220 | tempPML1.SuppressVe = targetPML2E->SuppressVe; 221 | 222 | /* Copy the template into all of the PML1 entries. */ 223 | __stosq((PULONG64)&newSplit->PML1[0], tempPML1.Flags, EPT_PML1E_COUNT); 224 | 225 | /* Calculate the physical address of the PML2 entry. */ 226 | UINT64 addressPML2E = targetPML2E->PageFrameNumber * SIZE_2MB; 227 | 228 | /* Calculate the page frame number of the first page in the table. */ 229 | UINT64 basePageNumber = addressPML2E / PAGE_SIZE; 230 | 231 | /* Set page frame numbres for each of the entries within the PML1 table. */ 232 | for (UINT32 i = 0; i < EPT_PML1E_COUNT; i++) 233 | { 234 | /* Convert the 2MB page frame number to the 4096 page entry number, plus the offset into the frame. */ 235 | newSplit->PML1[i].PageFrameNumber = basePageNumber + i; 236 | } 237 | 238 | /* Create a new PML2 pointer that will replace the 2MB entry with a pointer to the newly 239 | * created PML1 table. */ 240 | EPT_PML2_POINTER tempPML2 = { 0 }; 241 | tempPML2.ReadAccess = 1; 242 | tempPML2.WriteAccess = 1; 243 | tempPML2.ExecuteAccess = 1; 244 | tempPML2.PageFrameNumber = MmGetPhysicalAddress(&newSplit->PML1[0]).QuadPart / PAGE_SIZE; 245 | 246 | /* Replace the old entry with the new split pointer. */ 247 | targetPML2E->Flags = tempPML2.Flags; 248 | 249 | /* Add the split entry to the list of split pages, so we can de-allocate them later. */ 250 | InsertHeadList(&eptConfig->dynamicSplitList, &newSplit->listEntry); 251 | 252 | status = STATUS_SUCCESS; 253 | } 254 | else 255 | { 256 | status = STATUS_NO_MEMORY; 257 | } 258 | } 259 | else 260 | { 261 | /* Page is already split, do nothing. */ 262 | status = STATUS_ALREADY_COMPLETE; 263 | } 264 | } 265 | else 266 | { 267 | status = STATUS_INVALID_ADDRESS; 268 | } 269 | 270 | return status; 271 | } 272 | 273 | PEPT_PML2_2MB EPT_getPML2EFromAddress(PEPT_CONFIG eptConfig, PHYSICAL_ADDRESS physicalAddress) 274 | { 275 | PEPT_PML2_2MB result; 276 | 277 | UINT64 unsignedAddr = physicalAddress.QuadPart; 278 | 279 | UINT64 indexPML4 = ADDRMASK_EPT_PML4_INDEX(unsignedAddr); 280 | 281 | if (indexPML4 == 0) 282 | { 283 | UINT64 indexPML3 = ADDRMASK_EPT_PML3_INDEX(unsignedAddr); 284 | UINT64 indexPML2 = ADDRMASK_EPT_PML2_INDEX(unsignedAddr); 285 | 286 | result = &(eptConfig->PML2[indexPML3][indexPML2]); 287 | } 288 | else 289 | { 290 | /* Cannot support an entry above 512GB. */ 291 | result = NULL; 292 | } 293 | 294 | return result; 295 | } 296 | 297 | PEPT_PML1_ENTRY EPT_getPML1EFromAddress(PEPT_CONFIG eptConfig, PHYSICAL_ADDRESS physicalAddress) 298 | { 299 | PEPT_PML1_ENTRY result = NULL; 300 | 301 | UINT64 unsignedAddr = physicalAddress.QuadPart; 302 | 303 | UINT64 indexPML4 = ADDRMASK_EPT_PML4_INDEX(unsignedAddr); 304 | 305 | /* Ensure the entry is within the first 512GB. */ 306 | if (indexPML4 == 0) 307 | { 308 | UINT64 indexPML3 = ADDRMASK_EPT_PML3_INDEX(unsignedAddr); 309 | UINT64 indexPML2 = ADDRMASK_EPT_PML2_INDEX(unsignedAddr); 310 | 311 | PEPT_PML2_2MB entryPML2 = &eptConfig->PML2[indexPML3][indexPML2]; 312 | 313 | /* Ensure that PML2 entry is not a large page, if it is this means we are at the lowest level already 314 | * so it is impossible to get PML1E as it doesn't exist. */ 315 | if (FALSE == entryPML2->LargePage) 316 | { 317 | /* We have determined it is not the lowest level, so cast the entry to a pointer to the level 1 table. */ 318 | PEPT_PML2_POINTER pointerPML2 = (PEPT_PML2_POINTER)entryPML2; 319 | 320 | /* Convert the physical address to a virtual address. */ 321 | PHYSICAL_ADDRESS physicalPML1; 322 | physicalPML1.QuadPart = pointerPML2->PageFrameNumber * PAGE_SIZE; 323 | 324 | PEPT_PML1_ENTRY entryPML1 = (PEPT_PML1_ENTRY)MmGetVirtualForPhysical(physicalPML1); 325 | 326 | if (NULL != entryPML1) 327 | { 328 | UINT64 indexPML1 = ADDRMASK_EPT_PML1_INDEX(unsignedAddr); 329 | 330 | result = &entryPML1[indexPML1]; 331 | } 332 | } 333 | } 334 | 335 | return result; 336 | } 337 | 338 | void EPT_invalidateAndFlush(PEPT_CONFIG eptConfig) 339 | { 340 | INVEPT_DESCRIPTOR eptDescriptor; 341 | 342 | eptDescriptor.EptPointer = eptConfig->eptPointer.Flags; 343 | eptDescriptor.Reserved = 0; 344 | __invept(InveptSingleContext, &eptDescriptor); 345 | } 346 | 347 | /******************** Module Code ********************/ 348 | 349 | static UINT32 adjustEffectiveMemoryType(const PMTRR_RANGE mtrrTable, UINT64 pageAddress, UINT32 desiredType) 350 | { 351 | for (UINT32 i = 0; i < IA32_MTRR_VARIABLE_COUNT; i++) 352 | { 353 | /* Check to see if the MTRR is active. */ 354 | if (mtrrTable[i].Valid != FALSE) 355 | { 356 | /* Check if the page address falls within the boundary of the MTRR. 357 | * If a single MTRR touches it, we need to override the desired PDE's type. */ 358 | if (((pageAddress + (SIZE_2MB - 1) >= mtrrTable[i].PhysicalAddressMin) && 359 | (pageAddress <= mtrrTable[i].PhysicalAddressMax))) 360 | { 361 | desiredType = mtrrTable[i].Type; 362 | } 363 | } 364 | } 365 | 366 | return desiredType; 367 | } 368 | -------------------------------------------------------------------------------- /Hypervisor/EPT.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ia32.h" 4 | #include "MTRR.h" 5 | 6 | /******************** Public Defines ********************/ 7 | 8 | #define EPT_PML4E_COUNT 512 9 | #define EPT_PML3E_COUNT 512 10 | #define EPT_PML2E_COUNT 512 11 | #define EPT_PML1E_COUNT 512 12 | 13 | #define SIZE_1GB (1 * 1024 * 1024 * 1024) 14 | #define SIZE_2MB (2 * 1024 * 1024) 15 | 16 | /* Calculates the offset into the PDE (PML1) structure. */ 17 | #define ADDRMASK_EPT_PML1_OFFSET(_VAR_) ((SIZE_T)_VAR_ & 0xFFFULL) 18 | 19 | /* Calculates the index of the PDE (PML1) within the PDT structure. */ 20 | #define ADDRMASK_EPT_PML1_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x1FF000ULL) >> 12) 21 | 22 | /* Calculates the index of the PDT (PML2) within the PDPT*/ 23 | #define ADDRMASK_EPT_PML2_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x3FE00000ULL) >> 21) 24 | 25 | /* Calculates the index of the PDPT (PML3) within the PML4 */ 26 | #define ADDRMASK_EPT_PML3_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x7FC0000000ULL) >> 30) 27 | 28 | /* Calculates the index of PML4. */ 29 | #define ADDRMASK_EPT_PML4_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0xFF8000000000ULL) >> 39) 30 | 31 | /******************** Public Typedefs ********************/ 32 | 33 | typedef EPT_PML4 EPT_PML4_POINTER, *PEPT_PML4_POINTER; 34 | typedef EPDPTE EPT_PML3_POINTER, *PEPT_PML3_POINTER; 35 | typedef EPDE_2MB EPT_PML2_2MB, *PEPT_PML2_2MB; 36 | typedef EPDE EPT_PML2_POINTER, *PEPT_PML2_POINTER; 37 | typedef EPTE EPT_PML1_ENTRY, *PEPT_PML1_ENTRY; 38 | 39 | typedef struct _PHYSICAL_RANGE 40 | { 41 | PHYSICAL_ADDRESS start; 42 | PHYSICAL_ADDRESS end; 43 | } PHYSICAL_RANGE, *PPHYSICAL_RANGE; 44 | 45 | /* Structure that will hold the PML1 data for a dynamically split PML2 entry. */ 46 | typedef struct _EPT_DYNAMIC_SPLIT 47 | { 48 | /* The 4096 byte page table entries that correspond to the split 2MB table entry. */ 49 | DECLSPEC_ALIGN(PAGE_SIZE) EPT_PML1_ENTRY PML1[EPT_PML1E_COUNT]; 50 | 51 | /* A pointer to the 2MB entry in the page table which this split was created for. */ 52 | PEPT_PML2_2MB pml2Entry; 53 | 54 | /* List entry for the dynamic split, will be used to keep track of all split entries. */ 55 | LIST_ENTRY listEntry; 56 | 57 | } EPT_DYNAMIC_SPLIT, *PEPT_DYNAMIC_SPLIT; 58 | 59 | typedef struct _EPT_CONFIG 60 | { 61 | /* Describes 512 contiguous 512GB memory regions each with 512 512GB regions. */ 62 | DECLSPEC_ALIGN(PAGE_SIZE) EPT_PML4_POINTER PML4[EPT_PML4E_COUNT]; 63 | 64 | /* Describes exactly 512 contiguous 1GB memory regions within a singular PML4 region. */ 65 | DECLSPEC_ALIGN(PAGE_SIZE) EPT_PML3_POINTER PML3[EPT_PML3E_COUNT]; 66 | 67 | /* For each 1GB PML3 entry, create 512 2MB regions. We are using 2MB pages as the smallest paging size in 68 | * the map so that we do not need to allocate individual 4096 PML1 paging structures. */ 69 | DECLSPEC_ALIGN(PAGE_SIZE) EPT_PML2_2MB PML2[EPT_PML3E_COUNT][EPT_PML2E_COUNT]; 70 | 71 | /* List all of the EPT handlers that are used. */ 72 | LIST_ENTRY handlerList; 73 | 74 | /* List of all dynamically split pages (from 2MB to 4KB). This will be used for 75 | * when they need to be freed during uninitialisation. 76 | * TODO: Actually implement uninit. */ 77 | LIST_ENTRY dynamicSplitList; 78 | 79 | /* EPT pointer that will be used for the VMCS. */ 80 | EPT_POINTER eptPointer; 81 | } EPT_CONFIG, *PEPT_CONFIG; 82 | 83 | /* Callback function for the EPT violation handler. */ 84 | typedef BOOLEAN(*fnEPTHandlerCallback)(PEPT_CONFIG eptConfig, PCONTEXT guestContext, PVOID userBuffer); 85 | 86 | /* Structure that holds the information of each handler that 87 | * are used for parsing violations. */ 88 | typedef struct _EPT_HANDLER 89 | { 90 | /* Range of physical memory that the handler 91 | * is registered to. */ 92 | PHYSICAL_RANGE physRange; 93 | 94 | /* Callback of the handler, that will be called for processing the violation. */ 95 | fnEPTHandlerCallback callback; 96 | 97 | /* Buffer that can be used for user-supplied configs. */ 98 | PVOID userParameter; 99 | 100 | /* Linked list entry, used for traversal. */ 101 | LIST_ENTRY listEntry; 102 | } EPT_HANDLER, *PEPT_HANDLER; 103 | 104 | /******************** Public Constants ********************/ 105 | 106 | /******************** Public Variables ********************/ 107 | 108 | /******************** Public Prototypes ********************/ 109 | 110 | void EPT_initialise(PEPT_CONFIG eptTable, const PMTRR_RANGE mtrrTable); 111 | BOOLEAN EPT_handleViolation(PEPT_CONFIG eptConfig, PCONTEXT guestContext); 112 | NTSTATUS EPT_addViolationHandler(PEPT_CONFIG eptConfig, PHYSICAL_RANGE physicalRange, fnEPTHandlerCallback callback, PVOID userParameter); 113 | NTSTATUS EPT_splitLargePage(PEPT_CONFIG eptConfig, PHYSICAL_ADDRESS physicalAddress); 114 | PEPT_PML2_2MB EPT_getPML2EFromAddress(PEPT_CONFIG eptConfig, PHYSICAL_ADDRESS physicalAddress); 115 | PEPT_PML1_ENTRY EPT_getPML1EFromAddress(PEPT_CONFIG eptConfig, PHYSICAL_ADDRESS physicalAddress); 116 | void EPT_invalidateAndFlush(PEPT_CONFIG eptConfig); -------------------------------------------------------------------------------- /Hypervisor/GDT.c: -------------------------------------------------------------------------------- 1 | #include "GDT.h" 2 | 3 | /******************** External API ********************/ 4 | 5 | 6 | /******************** Module Typedefs ********************/ 7 | 8 | 9 | /******************** Module Constants ********************/ 10 | 11 | #define SELECTOR_TABLE_INDEX 0x04 12 | 13 | 14 | /******************** Module Variables ********************/ 15 | 16 | 17 | /******************** Module Prototypes ********************/ 18 | 19 | 20 | /******************** Public Code ********************/ 21 | 22 | VOID GDT_convertGdtEntry(VOID* GdtBase, UINT16 Selector, PVMX_GDTENTRY64 VmxGdtEntry) 23 | { 24 | PKGDTENTRY64 gdtEntry; 25 | 26 | /* Reject LDT or NULL entries */ 27 | if ((Selector == 0) || 28 | (Selector & SELECTOR_TABLE_INDEX) != 0) 29 | { 30 | VmxGdtEntry->Limit = VmxGdtEntry->AccessRights = 0; 31 | VmxGdtEntry->Base = 0; 32 | VmxGdtEntry->Selector = 0; 33 | VmxGdtEntry->Bits.Unusable = TRUE; 34 | return; 35 | } 36 | 37 | /* Read the GDT entry at the given selector, masking out the RPL bits. */ 38 | gdtEntry = (PKGDTENTRY64)((uintptr_t)GdtBase + (Selector & ~RPL_MASK)); 39 | 40 | /* Write the selector directly */ 41 | VmxGdtEntry->Selector = Selector; 42 | 43 | /* Use the LSL intrinsic to read the segment limit */ 44 | VmxGdtEntry->Limit = __segmentlimit(Selector); 45 | 46 | /* 47 | * Build the full 64-bit effective address, keeping in mind that only when 48 | * the System bit is unset, should this be done. 49 | * 50 | * NOTE: The Windows definition of KGDTENTRY64 is WRONG. The "System" field 51 | * is incorrectly defined at the position of where the AVL bit should be. 52 | * The actual location of the SYSTEM bit is encoded as the highest bit in 53 | * the "Type" field. 54 | */ 55 | VmxGdtEntry->Base = ((gdtEntry->Bytes.BaseHigh << 24) | 56 | (gdtEntry->Bytes.BaseMiddle << 16) | 57 | (gdtEntry->BaseLow)) & 0xFFFFFFFF; 58 | VmxGdtEntry->Base |= ((gdtEntry->Bits.Type & 0x10) == 0) ? 59 | ((uintptr_t)gdtEntry->BaseUpper << 32) : 0; 60 | 61 | /* Load the access rights */ 62 | VmxGdtEntry->AccessRights = 0; 63 | VmxGdtEntry->Bytes.Flags1 = gdtEntry->Bytes.Flags1; 64 | VmxGdtEntry->Bytes.Flags2 = gdtEntry->Bytes.Flags2; 65 | 66 | /* Finally, handle the VMX-specific bits */ 67 | VmxGdtEntry->Bits.Reserved = 0; 68 | VmxGdtEntry->Bits.Unusable = !gdtEntry->Bits.Present; 69 | } 70 | 71 | /******************** Module Code ********************/ -------------------------------------------------------------------------------- /Hypervisor/GDT.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #pragma warning(disable:4201) 5 | #pragma warning(disable:4214) 6 | 7 | /******************** Public Defines ********************/ 8 | 9 | #define RPL_MASK 3 10 | 11 | /******************** Public Typedefs ********************/ 12 | 13 | typedef struct _VMX_GDTENTRY64 14 | { 15 | UINT64 Base; 16 | UINT32 Limit; 17 | union 18 | { 19 | struct 20 | { 21 | UINT8 Flags1; 22 | UINT8 Flags2; 23 | UINT8 Flags3; 24 | UINT8 Flags4; 25 | } Bytes; 26 | struct 27 | { 28 | UINT16 SegmentType : 4; 29 | UINT16 DescriptorType : 1; 30 | UINT16 Dpl : 2; 31 | UINT16 Present : 1; 32 | 33 | UINT16 Reserved : 4; 34 | UINT16 System : 1; 35 | UINT16 LongMode : 1; 36 | UINT16 DefaultBig : 1; 37 | UINT16 Granularity : 1; 38 | 39 | UINT16 Unusable : 1; 40 | UINT16 Reserved2 : 15; 41 | } Bits; 42 | UINT32 AccessRights; 43 | }; 44 | UINT16 Selector; 45 | } VMX_GDTENTRY64, *PVMX_GDTENTRY64; 46 | 47 | typedef union _KGDTENTRY64 48 | { 49 | struct 50 | { 51 | UINT16 LimitLow; 52 | UINT16 BaseLow; 53 | union 54 | { 55 | struct 56 | { 57 | UINT8 BaseMiddle; 58 | UINT8 Flags1; 59 | UINT8 Flags2; 60 | UINT8 BaseHigh; 61 | } Bytes; 62 | struct 63 | { 64 | UINT32 BaseMiddle : 8; 65 | UINT32 Type : 5; 66 | UINT32 Dpl : 2; 67 | UINT32 Present : 1; 68 | UINT32 LimitHigh : 4; 69 | UINT32 System : 1; 70 | UINT32 LongMode : 1; 71 | UINT32 DefaultBig : 1; 72 | UINT32 Granularity : 1; 73 | UINT32 BaseHigh : 8; 74 | } Bits; 75 | }; 76 | UINT32 BaseUpper; 77 | UINT32 MustBeZero; 78 | }; 79 | struct 80 | { 81 | INT64 DataLow; 82 | INT64 DataHigh; 83 | }; 84 | } KGDTENTRY64, *PKGDTENTRY64; 85 | 86 | /******************** Public Constants ********************/ 87 | 88 | /******************** Public Variables ********************/ 89 | 90 | /******************** Public Prototypes ********************/ 91 | 92 | VOID GDT_convertGdtEntry(VOID* GdtBase, UINT16 Selector, PVMX_GDTENTRY64 VmxGdtEntry); 93 | -------------------------------------------------------------------------------- /Hypervisor/GuestShim.c: -------------------------------------------------------------------------------- 1 | #include "GuestShim.h" 2 | #include "Debug.h" 3 | 4 | /******************** External API ********************/ 5 | 6 | 7 | /******************** Module Typedefs ********************/ 8 | 9 | typedef enum 10 | { 11 | PT_LEVEL_PTE = 1, 12 | PT_LEVEL_PDE = 2, 13 | PT_LEVEL_PDPTE = 3, 14 | PT_LEVEL_PML4E = 4 15 | } PT_LEVEL; 16 | 17 | /******************** Module Constants ********************/ 18 | 19 | #define SIZE_1GB 0x40000000 20 | #define SIZE_2MB 0x00200000 21 | 22 | /* Calculates the offset into the PT (PML1) table. */ 23 | #define ADDRMASK_PML1_OFFSET(_VAR_) ((SIZE_T)_VAR_ & 0xFFFULL) 24 | 25 | /* Calculates the offset into the PD (PML2) table. */ 26 | #define ADDRMASK_PML2_OFFSET(_VAR_) ((SIZE_T)_VAR_ & 0x1FFFFFULL) 27 | 28 | /* Calculates the offset into the PDPT (PML3) table. */ 29 | #define ADDRMASK_PML3_OFFSET(_VAR_) ((SIZE_T)_VAR_ & 0x3FFFFFFFULL) 30 | 31 | /* Calculates the index of the PDE (PML1) within the PDT structure. */ 32 | #define ADDRMASK_PML1_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x1FF000ULL) >> 12) 33 | 34 | /* Calculates the index of the PDT (PML2) within the PDPT*/ 35 | #define ADDRMASK_PML2_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x3FE00000ULL) >> 21) 36 | 37 | /* Calculates the index of the PDPT (PML3) within the PML4 */ 38 | #define ADDRMASK_PML3_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x7FC0000000ULL) >> 30) 39 | 40 | /* Calculates the index of PML4. */ 41 | #define ADDRMASK_PML4_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0xFF8000000000ULL) >> 39) 42 | 43 | /******************** Module Variables ********************/ 44 | 45 | 46 | /******************** Module Prototypes ********************/ 47 | static HOST_PHYS_ADDRESS getHostPAFromGuestVA(PMM_CONTEXT mmContext, CR3 guestCR3, PVOID guestVA); 48 | static PT_ENTRY_64 getGuestPTEFromVA(PMM_CONTEXT mmContext, CR3 guestCR3, PVOID guestVA, PT_LEVEL* level); 49 | 50 | /******************** Public Code ********************/ 51 | HOST_PHYS_ADDRESS GuestShim_GuestUVAToHPA(PMM_CONTEXT mmContext, CR3 userCR3, GUEST_VIRTUAL_ADDRESS guestAddress) 52 | { 53 | /* Converts a guest user virtual address 54 | * to a host physical address. 55 | * 56 | * Same as above, guestPA = hostPA. */ 57 | return getHostPAFromGuestVA(mmContext, userCR3, (PVOID)guestAddress); 58 | } 59 | 60 | /******************** Module Code ********************/ 61 | 62 | static HOST_PHYS_ADDRESS getHostPAFromGuestVA(PMM_CONTEXT mmContext, CR3 guestCR3, PVOID guestVA) 63 | { 64 | HOST_PHYS_ADDRESS result = 0; 65 | 66 | /* Attempt to get the page table entry and level from the guest, 67 | * from that we can calculate where in host physical memory it is. */ 68 | PT_LEVEL tableLevel; 69 | PT_ENTRY_64 guestEntry = getGuestPTEFromVA(mmContext, guestCR3, guestVA, &tableLevel); 70 | if (0 != guestEntry.Flags) 71 | { 72 | 73 | /* Check to see which level we retrieved from the guest mapping table. */ 74 | switch (tableLevel) 75 | { 76 | case PT_LEVEL_PML4E: 77 | { 78 | /* Something went wrong, there should never be a PML4E 79 | * and no entries below. */ 80 | DbgBreakPoint(); 81 | break; 82 | } 83 | 84 | case PT_LEVEL_PDPTE: 85 | { 86 | /* If it is present it must mean below is a 1GB large page 87 | * as such we calculate where our address would be. */ 88 | PDPTE_1GB_64 guestPDPTE; 89 | guestPDPTE.Flags = guestEntry.Flags; 90 | if (TRUE == guestPDPTE.Present) 91 | { 92 | /* Calculate the base of where the page begins. */ 93 | result = (guestPDPTE.PageFrameNumber * SIZE_1GB); 94 | 95 | /* Add the offset from the VA. */ 96 | /* TODO: Verify this is how offset calc should work on large pages. */ 97 | result += ADDRMASK_PML3_OFFSET(guestVA); 98 | } 99 | break; 100 | } 101 | 102 | case PT_LEVEL_PDE: 103 | { 104 | /* If it is present it must mean below is a 2MB large page 105 | * as such we calculate where our address would be. */ 106 | PDE_2MB_64 guestPDE; 107 | guestPDE.Flags = guestEntry.Flags; 108 | if (TRUE == guestPDE.Present) 109 | { 110 | /* Calculate the base of where the page is. */ 111 | result = (guestPDE.PageFrameNumber * SIZE_2MB); 112 | 113 | /* Add the offset from the VA. */ 114 | result += ADDRMASK_PML2_OFFSET(guestVA); 115 | } 116 | break; 117 | } 118 | 119 | case PT_LEVEL_PTE: 120 | { 121 | /* If it is present, this is a 4KB page. */ 122 | PTE_64 guestPTE; 123 | guestPTE.Flags = guestEntry.Flags; 124 | if (TRUE == guestPTE.Present) 125 | { 126 | /* Calculate the base of where the page is. */ 127 | result = (guestPTE.PageFrameNumber * PAGE_SIZE); 128 | 129 | /* Add the offset from the VA. */ 130 | result += ADDRMASK_PML1_OFFSET(guestVA); 131 | } 132 | break; 133 | } 134 | 135 | default: 136 | { 137 | /* Something went wrong. */ 138 | DbgBreakPoint(); 139 | break; 140 | } 141 | } 142 | 143 | } 144 | 145 | return result; 146 | } 147 | 148 | static PT_ENTRY_64 getGuestPTEFromVA(PMM_CONTEXT mmContext, CR3 guestCR3, PVOID guestVA, PT_LEVEL* level) 149 | { 150 | PT_ENTRY_64 result = { 0 }; 151 | 152 | /* Calculated the indexes for each of the tables in the paging structure. */ 153 | SIZE_T indexPML4 = ADDRMASK_PML4_INDEX(guestVA); 154 | SIZE_T indexPML3 = ADDRMASK_PML3_INDEX(guestVA); 155 | SIZE_T indexPML2 = ADDRMASK_PML2_INDEX(guestVA); 156 | SIZE_T indexPML1 = ADDRMASK_PML1_INDEX(guestVA); 157 | 158 | /* Read the PML4 address of our paging table. */ 159 | PML4E_64* pml4 = (PML4E_64*)(guestCR3.AddressOfPageDirectory * PAGE_SIZE); 160 | HOST_PHYS_ADDRESS physPML4E = (HOST_PHYS_ADDRESS)&pml4[indexPML4]; 161 | 162 | PML4E_64 readPML4E; 163 | NTSTATUS status = MemManage_readPhysicalAddress(mmContext, physPML4E, &readPML4E, sizeof(readPML4E)); 164 | 165 | /* Set the initial level we were able to traverse down to as PML4, 166 | * We then check if it's present. If so we can traverse the next table. */ 167 | if (NT_SUCCESS(status) && (readPML4E.Present)) 168 | { 169 | result.Flags = readPML4E.Flags; 170 | *level = PT_LEVEL_PML4E; 171 | 172 | /* Read PML3 */ 173 | PDPTE_64* pdpt = (PDPTE_64*)(readPML4E.PageFrameNumber * PAGE_SIZE); 174 | HOST_PHYS_ADDRESS physPDPTE = (HOST_PHYS_ADDRESS)&pdpt[indexPML3]; 175 | 176 | PDPTE_64 readPDPTE; 177 | status = MemManage_readPhysicalAddress(mmContext, physPDPTE, &readPDPTE, sizeof(readPDPTE)); 178 | 179 | /* Only attempt to get lower level if present. */ 180 | if (NT_SUCCESS(status) && (TRUE == readPDPTE.Present)) 181 | { 182 | result.Flags = readPDPTE.Flags; 183 | *level = PT_LEVEL_PDPTE; 184 | 185 | /* If not a large page that means we can traverse lower. */ 186 | if (FALSE == readPDPTE.LargePage) 187 | { 188 | /* Read PML2 */ 189 | PDE_64* pd = (PDE_64*)(readPDPTE.PageFrameNumber * PAGE_SIZE); 190 | HOST_PHYS_ADDRESS physPDE = (HOST_PHYS_ADDRESS)&pd[indexPML2]; 191 | 192 | PDE_64 readPDE; 193 | status = MemManage_readPhysicalAddress(mmContext, physPDE, &readPDE, sizeof(readPDE)); 194 | 195 | /* Only attempt to go lower if present. */ 196 | if (NT_SUCCESS(status) && (TRUE == readPDE.Present)) 197 | { 198 | result.Flags = readPDE.Flags; 199 | *level = PT_LEVEL_PDE; 200 | 201 | /* If not a large page, that means we can traverse lower. */ 202 | if (FALSE == readPDE.LargePage) 203 | { 204 | /* Read PML1 */ 205 | PTE_64* pt = (PTE_64*)(readPDE.PageFrameNumber * PAGE_SIZE); 206 | HOST_PHYS_ADDRESS physPTE = (HOST_PHYS_ADDRESS)&pt[indexPML1]; 207 | 208 | PTE_64 readPTE; 209 | status = MemManage_readPhysicalAddress(mmContext, physPTE, &readPTE, sizeof(readPTE)); 210 | 211 | if (NT_SUCCESS(status)) 212 | { 213 | result.Flags = readPTE.Flags; 214 | *level = PT_LEVEL_PTE; 215 | } 216 | } 217 | } 218 | else 219 | { 220 | result.Flags = 0; 221 | } 222 | } 223 | } 224 | else 225 | { 226 | result.Flags = 0; 227 | } 228 | } 229 | else 230 | { 231 | result.Flags = 0; 232 | } 233 | 234 | return result; 235 | } 236 | 237 | -------------------------------------------------------------------------------- /Hypervisor/GuestShim.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "MemManage.h" 4 | #include "ia32.h" 5 | 6 | /******************** Public Typedefs ********************/ 7 | 8 | /******************** Public Constants ********************/ 9 | 10 | /******************** Public Variables ********************/ 11 | 12 | /******************** Public Prototypes ********************/ 13 | HOST_PHYS_ADDRESS GuestShim_GuestUVAToHPA(PMM_CONTEXT mmContext, CR3 userCR3, GUEST_VIRTUAL_ADDRESS guestAddress); 14 | -------------------------------------------------------------------------------- /Hypervisor/HandlerShim.asm: -------------------------------------------------------------------------------- 1 | .code 2 | 3 | extern Handlers_hostToGuest:proc 4 | extern Handlers_guestToHost:proc 5 | extern RtlCaptureContext:proc 6 | 7 | ; Called when the transition to GUEST takes place. Assembly used for easy breakpointing. 8 | HandlerShim_hostToGuest PROC 9 | 10 | ;int 3 11 | 12 | jmp Handlers_hostToGuest 13 | 14 | HandlerShim_hostToGuest ENDP 15 | 16 | ; Called when a GUEST transition to HOST takes place. 17 | HandlerShim_guestToHost PROC 18 | 19 | ;int 3 20 | 21 | push rcx ; save the RCX register, which we spill below 22 | lea rcx, [rsp+8h] ; store the context in the stack, bias for 23 | ; the return address and the push we just did. 24 | call RtlCaptureContext ; save the current register state. 25 | ; note that this is a specially written function 26 | ; which has the following key characteristics: 27 | ; 1) it does not taint the value of RCX 28 | ; 2) it does not spill any registers, nor 29 | ; expect home space to be allocated for it 30 | jmp Handlers_guestToHost ; jump to the C code handler. we assume that it 31 | ; compiled with optimizations and does not use 32 | ; home space, which is true of release builds. 33 | HandlerShim_guestToHost ENDP 34 | 35 | HandlerShim_VMCALL PROC 36 | 37 | ; RCX should hold the key (hopefully convention not broken) 38 | ; RDX should hold the context/parameters. 39 | ; Need to fix it so they are ensured. 40 | vmcall 41 | 42 | ; RAX will contain the result NTSTATUS in the end, set by the host. 43 | 44 | ret 45 | 46 | HandlerShim_VMCALL ENDP 47 | 48 | end -------------------------------------------------------------------------------- /Hypervisor/HandlerShim.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /******************** Public Defines ********************/ 5 | 6 | 7 | /******************** Public Typedefs ********************/ 8 | 9 | 10 | /******************** Public Constants ********************/ 11 | 12 | /******************** Public Variables ********************/ 13 | 14 | /******************** Public Prototypes ********************/ 15 | 16 | void HandlerShim_hostToGuest(void); 17 | void HandlerShim_guestToHost(void); 18 | NTSTATUS HandlerShim_VMCALL(UINT64 key, void* params); -------------------------------------------------------------------------------- /Hypervisor/Handlers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "VMM.h" 4 | #include "Handlers.h" 5 | #include "HandlerShim.h" 6 | #include "Intrinsics.h" 7 | #include "CPUID.h" 8 | #include "VMCALL.h" 9 | #include "VMShadow.h" 10 | #include "Debug.h" 11 | 12 | /******************** External API ********************/ 13 | 14 | /* DEBUG: Just some fields for monitoring VMExits. */ 15 | SIZE_T monitoredRangeStart = 0; 16 | SIZE_T monitoredRangeEnd = 0; 17 | 18 | /******************** Module Typedefs ********************/ 19 | 20 | 21 | /******************** Module Constants ********************/ 22 | 23 | 24 | /******************** Module Variables ********************/ 25 | 26 | 27 | /******************** Module Prototypes ********************/ 28 | static void handleExitReason(PVMM_DATA lpData); 29 | static void incrementRIP(void); 30 | static void indicateVMXFail(void); 31 | 32 | /******************** Public Code ********************/ 33 | 34 | DECLSPEC_NORETURN VOID Handlers_hostToGuest(void) 35 | { 36 | PVMM_DATA lpData; 37 | 38 | /* We are currently executing in the state of the guest, 39 | * we need to find the original lpData structure, the stack pointer should be within 40 | * the LP data structure, we use this to find the start of the structure. */ 41 | lpData = (PVMM_DATA)((uintptr_t)_AddressOfReturnAddress() + 42 | sizeof(CONTEXT) - 43 | KERNEL_STACK_SIZE); 44 | 45 | /* Record that we are running under the context of the hypervisor as a guest, 46 | * we do this by setting the align check bit in EFLAGS. 47 | * 48 | * This is checked within initialiseVirtualProcessor to continue execution when a guest. */ 49 | lpData->hostContext.EFlags |= EFLAGS_ALIGNMENT_CHECK_FLAG_FLAG; 50 | 51 | _RestoreContext(&lpData->hostContext, NULL); 52 | } 53 | 54 | 55 | DECLSPEC_NORETURN VOID Handlers_guestToHost(PCONTEXT guestContext) 56 | { 57 | /* Because we had to use RCX when calling RtlCaptureContext, its value 58 | * was actually pushed on the stack right before the call. 59 | * We find the bogus value we set to RCX to and overwrite it with what was originally there. */ 60 | guestContext->Rcx = *(UINT64*)((uintptr_t)guestContext - sizeof(guestContext->Rcx)); 61 | 62 | /* Find the LP_DATA structure by using the context variable that was passed in (technically the stack). */ 63 | PVMM_DATA lpData = (VOID*)((uintptr_t)(guestContext + 1) - KERNEL_STACK_SIZE); 64 | 65 | /* Copy the guest context into our LP data structure. */ 66 | RtlCopyMemory(&lpData->guestContext, guestContext, sizeof(CONTEXT)); 67 | 68 | /* Handle the exit reason. */ 69 | handleExitReason(lpData); 70 | 71 | /* Now to restore back to the guest. */ 72 | 73 | /* In the assembly stub code there was a PUSH RCX instruction we used, 74 | * we need to account for that here and negate the effect of it. This is easily 75 | * done by adjusting the stack pointer by the size of the register */ 76 | lpData->guestContext.Rsp += sizeof(lpData->guestContext.Rcx); 77 | 78 | /* We now set our desired instruction pointer at the VMXResume handler, 79 | * We use the restore context function to do this rather than a call to ensure 80 | * that we are in the EXACT same state as we were prior to this function. */ 81 | lpData->guestContext.Rip = (UINT64)Handlers_VMResume; 82 | 83 | /* Restore the context. */ 84 | _RestoreContext(&lpData->guestContext, NULL); 85 | } 86 | 87 | DECLSPEC_NORETURN void Handlers_VMResume(void) 88 | { 89 | /* Issue a VMXRESUME. The reason that we've defined an entire function for 90 | * this sole instruction is both so that we can use it as the target of the 91 | * VMCS when re-entering the VM After a VM-Exit, as well as so that we can 92 | * decorate it with the DECLSPEC_NORETURN marker, which is not set on the 93 | * intrinsic (as it can fail in case of an error). */ 94 | __vmx_vmresume(); 95 | } 96 | 97 | /******************** Module Code ********************/ 98 | 99 | static void handleExitReason(PVMM_DATA lpData) 100 | { 101 | BOOLEAN moveToNextInstruction = FALSE; 102 | 103 | /* We need to determine what the exit reason was and take appropriate action. */ 104 | size_t exitReason; 105 | __vmx_vmread(VMCS_EXIT_REASON, &exitReason); 106 | exitReason &= 0xFFFF; 107 | 108 | ///* Check to see if we are actively monitoring a range. */ 109 | //if ((0 != monitoredRangeStart) && (0 != monitoredRangeEnd)) 110 | //{ 111 | // /* Check to see if guest RIP is within this range. */ 112 | // /* If so, we log the VMM exit. */ 113 | // size_t guestRIP; 114 | // __vmx_vmread(VMCS_GUEST_RIP, &guestRIP); 115 | 116 | // if ((guestRIP >= monitoredRangeStart) && (guestRIP <= monitoredRangeEnd)) 117 | // { 118 | // DEBUG_PRINT("VMM exit in monitored range.\n"); 119 | // DEBUG_PRINT("\tExit Reason: 0x%I64X\n", exitReason); 120 | // DEBUG_PRINT("\tGuest RIP: 0x%I64X\n", guestRIP); 121 | 122 | // static SIZE_T lastExitRIP = 0; 123 | 124 | // /* Some debug code to prevent multiple breaks on same instruction due to loops */ 125 | // if (guestRIP != lastExitRIP) 126 | // { 127 | // //DbgBreakPoint(); 128 | // lastExitRIP = guestRIP; 129 | // } 130 | // } 131 | //} 132 | 133 | switch (exitReason) 134 | { 135 | case VMX_EXIT_REASON_MONITOR_TRAP_FLAG: 136 | { 137 | if (TRUE == MTF_handleTrap(&lpData->mtfConfig)) 138 | { 139 | /* Do nothing. */ 140 | } 141 | else 142 | { 143 | DbgBreakPoint(); 144 | } 145 | 146 | break; 147 | } 148 | 149 | case VMX_EXIT_REASON_EPT_VIOLATION: 150 | { 151 | if (TRUE == EPT_handleViolation(&lpData->eptConfig, &lpData->guestContext)) 152 | { 153 | /* If we have handled the violation properly, we don't want to move to the next instruction, 154 | * We want to try process the instruction again, now that the page has been switched. */ 155 | moveToNextInstruction = FALSE; 156 | } 157 | else 158 | { 159 | DbgBreakPoint(); 160 | } 161 | 162 | break; 163 | } 164 | 165 | case VMX_EXIT_REASON_MOV_CR: 166 | { 167 | /* If we have handled the MOV to/from CR correctly, 168 | * we go to the next instruction. */ 169 | moveToNextInstruction = VMShadow_handleMovCR(lpData); 170 | break; 171 | } 172 | 173 | case VMX_EXIT_REASON_EXECUTE_INVD: 174 | { 175 | __wbinvd(); 176 | moveToNextInstruction = TRUE; 177 | break; 178 | } 179 | 180 | case VMX_EXIT_REASON_EXECUTE_XSETBV: 181 | { 182 | _xsetbv((UINT32)lpData->guestContext.Rcx, lpData->guestContext.Rdx << 32 | lpData->guestContext.Rax); 183 | moveToNextInstruction = TRUE; 184 | break; 185 | } 186 | 187 | case VMX_EXIT_REASON_EXECUTE_RDMSR: 188 | { 189 | UINT64 msrResult = __readmsr((UINT32)lpData->guestContext.Rcx); 190 | 191 | lpData->guestContext.Rdx = msrResult >> 32; 192 | lpData->guestContext.Rax = msrResult & 0xFFFFFFFF; 193 | moveToNextInstruction = TRUE; 194 | break; 195 | } 196 | 197 | case VMX_EXIT_REASON_EXECUTE_WRMSR: 198 | { 199 | /* Only take 32 bits from each register. */ 200 | UINT32 highBits = (UINT32)lpData->guestContext.Rdx; 201 | UINT32 lowBits = (UINT32)lpData->guestContext.Rax; 202 | 203 | UINT64 value = ((UINT64)highBits << 32) | lowBits; 204 | 205 | __writemsr((UINT32)lpData->guestContext.Rcx, value); 206 | 207 | moveToNextInstruction = TRUE; 208 | break; 209 | } 210 | 211 | case VMX_EXIT_REASON_EXECUTE_CPUID: 212 | { 213 | if (TRUE == CPUID_handle(lpData)) 214 | { 215 | moveToNextInstruction = TRUE; 216 | } 217 | break; 218 | } 219 | 220 | case VMX_EXIT_REASON_EXECUTE_VMCALL: 221 | { 222 | if (TRUE == VMCALL_handle(lpData)) 223 | { 224 | moveToNextInstruction = TRUE; 225 | } 226 | else 227 | { 228 | indicateVMXFail(); 229 | moveToNextInstruction = FALSE; 230 | } 231 | break; 232 | } 233 | 234 | case VMX_EXIT_REASON_EXECUTE_VMCLEAR: 235 | case VMX_EXIT_REASON_EXECUTE_VMLAUNCH: 236 | case VMX_EXIT_REASON_EXECUTE_VMPTRLD: 237 | case VMX_EXIT_REASON_EXECUTE_VMPTRST: 238 | case VMX_EXIT_REASON_EXECUTE_VMREAD: 239 | case VMX_EXIT_REASON_EXECUTE_VMRESUME: 240 | case VMX_EXIT_REASON_EXECUTE_VMWRITE: 241 | case VMX_EXIT_REASON_EXECUTE_VMXOFF: 242 | case VMX_EXIT_REASON_EXECUTE_VMXON: 243 | case VMX_EXIT_REASON_EXECUTE_INVEPT: 244 | { 245 | indicateVMXFail(); 246 | moveToNextInstruction = FALSE; 247 | break; 248 | } 249 | 250 | default: 251 | { 252 | if (FALSE == KD_DEBUGGER_NOT_PRESENT) 253 | { 254 | DbgBreakPoint(); 255 | } 256 | 257 | DEBUG_PRINT("Unhandled VMExit with reason: 0x%I64X\r\n", exitReason); 258 | break; 259 | } 260 | } 261 | 262 | if (TRUE == moveToNextInstruction) 263 | { 264 | incrementRIP(); 265 | } 266 | } 267 | 268 | static void incrementRIP(void) 269 | { 270 | /* Move the instruction pointer to the next instruction after the one that 271 | * caused the exit. */ 272 | size_t guestRIP; 273 | __vmx_vmread(VMCS_GUEST_RIP, &guestRIP); 274 | 275 | size_t instructionLength; 276 | __vmx_vmread(VMCS_VMEXIT_INSTRUCTION_LENGTH, &instructionLength); 277 | guestRIP += instructionLength; 278 | 279 | __vmx_vmwrite(VMCS_GUEST_RIP, guestRIP); 280 | 281 | RFLAGS guestRFLAGS; 282 | __vmx_vmread(VMCS_GUEST_RFLAGS, &guestRFLAGS.Flags); 283 | 284 | /* Check to see if trap flag set. */ 285 | if (TRUE == guestRFLAGS.TrapFlag) 286 | { 287 | /* Check to see if bit BTF is clear in DEBUGCTL, if so 288 | * single step on instructions. */ 289 | IA32_DEBUGCTL_REGISTER debugCtrl = { 0 }; 290 | __vmx_vmread(VMCS_GUEST_DEBUGCTL, &debugCtrl.Flags); 291 | 292 | if (FALSE == debugCtrl.Btf) 293 | { 294 | /* Clear the trap flag, and write to guest. */ 295 | guestRFLAGS.TrapFlag = FALSE; 296 | __vmx_vmwrite(VMCS_GUEST_RFLAGS, guestRFLAGS.Flags); 297 | 298 | /* Clear the blocking interruptibility state fields (apart from NMI) 299 | * So bits [2:0]. */ 300 | UINT64 guestIS = 0; 301 | __vmx_vmread(VMCS_GUEST_INTERRUPTIBILITY_STATE, &guestIS); 302 | 303 | guestIS &= ~7; 304 | __vmx_vmwrite(VMCS_GUEST_INTERRUPTIBILITY_STATE, guestIS); 305 | 306 | /* Inject the interrupt for TF. */ 307 | VMENTRY_INTERRUPT_INFORMATION interruptInfo = { 0 }; 308 | interruptInfo.Vector = Debug; 309 | interruptInfo.InterruptionType = HardwareException; 310 | interruptInfo.DeliverErrorCode = FALSE; 311 | interruptInfo.Valid = TRUE; 312 | __vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interruptInfo.Flags); 313 | } 314 | } 315 | } 316 | 317 | static void indicateVMXFail(void) 318 | { 319 | VMENTRY_INTERRUPT_INFORMATION interruptInfo; 320 | 321 | /* Zero the vector*/ 322 | interruptInfo.Flags = 0; /* Zero the field first. */ 323 | interruptInfo.Vector = InvalidOpcode; 324 | interruptInfo.InterruptionType = HardwareException; 325 | interruptInfo.DeliverErrorCode = 0; 326 | interruptInfo.Valid = 1; 327 | 328 | /* Set the exception. */ 329 | __vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interruptInfo.Flags); 330 | 331 | /* Indicate the instruction shouldn't move to next. */ 332 | __vmx_vmwrite(VMCS_CTRL_VMENTRY_INSTRUCTION_LENGTH, 0); 333 | 334 | /* Set the CF flag, this is how VMX instructions indicate a failure. */ 335 | UINT64 guestFlags; 336 | __vmx_vmread(VMCS_GUEST_RFLAGS, &guestFlags); 337 | guestFlags |= EFLAGS_CARRY_FLAG_FLAG; 338 | 339 | /* Set the EFLAGS in the VMCS with the updated field. */ 340 | //__vmx_vmwrite(VMCS_GUEST_RFLAGS, guestFlags); 341 | } 342 | -------------------------------------------------------------------------------- /Hypervisor/Handlers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /******************** Public Defines ********************/ 5 | 6 | /******************** Public Typedefs ********************/ 7 | 8 | /******************** Public Constants ********************/ 9 | 10 | /******************** Public Variables ********************/ 11 | 12 | /******************** Public Prototypes ********************/ 13 | 14 | DECLSPEC_NORETURN VOID Handlers_hostToGuest(void); 15 | DECLSPEC_NORETURN VOID Handlers_guestToHost(PCONTEXT guestContext); 16 | DECLSPEC_NORETURN void Handlers_VMResume(void); 17 | -------------------------------------------------------------------------------- /Hypervisor/Hypervisor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Hypervisor.h" 4 | #include "PageTable.h" 5 | #include "VMM.h" 6 | #include "Debug.h" 7 | #include "ia32.h" 8 | 9 | /******************** External API ********************/ 10 | 11 | 12 | /******************** Module Typedefs ********************/ 13 | 14 | 15 | /******************** Module Constants ********************/ 16 | #define MAX_LOGICAL_PROCESSORS 64 17 | 18 | /******************** Module Variables ********************/ 19 | 20 | /* Holds the runtime data for each logical processor. */ 21 | static VMM_DATA vmmData[MAX_LOGICAL_PROCESSORS] = { 0 }; 22 | 23 | /******************** Module Prototypes ********************/ 24 | static ULONG_PTR logicalProcessorInit(ULONG_PTR argument); 25 | static NTSTATUS isHVSupported(void); 26 | 27 | /******************** Public Code ********************/ 28 | 29 | NTSTATUS Hypervisor_init(void) 30 | { 31 | /* Initialises the virtualisation of the host environment so that 32 | * instrospection of it (as a guest) can take place. */ 33 | NTSTATUS status; 34 | 35 | //if (FALSE == KD_DEBUGGER_NOT_PRESENT) 36 | //{ 37 | // DbgBreakPoint(); 38 | //} 39 | 40 | status = isHVSupported(); 41 | if (NT_SUCCESS(status)) 42 | { 43 | /* Holds the CR3/PML4 entry that our HOST (when we are VMX root) will use. */ 44 | CR3 originalCR3; 45 | CR3 vmCR3; 46 | originalCR3.Flags = __readcr3(); 47 | 48 | status = PageTable_init(originalCR3, &vmCR3); 49 | if (NT_SUCCESS(status)) 50 | { 51 | 52 | /* We need to notify each logical processor to start the hypervisor. 53 | * This is done using using a IPI. 54 | * 55 | * TODO: IPI result only returns callee processors status 56 | * We are discarding other X logical processors results, need to fix this. */ 57 | status = (NTSTATUS)KeIpiGenericCall(logicalProcessorInit, (ULONG_PTR)vmCR3.Flags); 58 | } 59 | } 60 | 61 | return status; 62 | } 63 | 64 | /******************** Module Code ********************/ 65 | 66 | static ULONG_PTR logicalProcessorInit(ULONG_PTR argument) 67 | { 68 | /* Re-cast the argument back to the correct type so we 69 | * can use it. */ 70 | CR3 hostCR3; 71 | hostCR3.Flags = argument; 72 | 73 | NTSTATUS status = STATUS_UNSUCCESSFUL; 74 | 75 | /* Determine which logical processor we are running on, so we can 76 | * get a pointer to the config/data space we will be using. */ 77 | ULONG procIndex = KeGetCurrentProcessorIndex(); 78 | PVMM_DATA lpData = &vmmData[procIndex]; 79 | 80 | /* We need to ensure that the logical processor this is executing on uses the correct 81 | * PML4/CR3 when we are VM ROOT / HOST so we store it in the config. */ 82 | lpData->processorIndex = procIndex; 83 | lpData->hostCR3 = hostCR3; 84 | 85 | /* Initialise the VMM here. */ 86 | status = VMM_init(lpData); 87 | 88 | /* Explicitly cast to desired format for IPI broadcast. */ 89 | return (ULONG_PTR)status; 90 | } 91 | 92 | static NTSTATUS isHVSupported(void) 93 | { 94 | NTSTATUS status; 95 | 96 | DEBUG_PRINT("Checking to see if a hypervisor is supported.\r\n"); 97 | 98 | /* Check to see if VMX extensions are supported on the processor. */ 99 | CPUID_EAX_01 versionInfo; 100 | 101 | __cpuid((int*)&versionInfo, CPUID_VERSION_INFORMATION); 102 | 103 | if (TRUE == versionInfo.CpuidFeatureInformationEcx.VirtualMachineExtensions) 104 | { 105 | /* Check to see if the feature control MSR is locked in. 106 | * If it is not locked in, this means BIOS/UEFI has messed up and we cannot 107 | * use the MSR properly. */ 108 | IA32_FEATURE_CONTROL_REGISTER featureControl; 109 | featureControl.Flags = __readmsr(IA32_FEATURE_CONTROL); 110 | 111 | if (TRUE == featureControl.LockBit) 112 | { 113 | /* Check to see if VMX is enabled in normal operation. */ 114 | if (TRUE == featureControl.EnableVmxOutsideSmx) 115 | { 116 | status = STATUS_SUCCESS; 117 | } 118 | else 119 | { 120 | DEBUG_PRINT("VMX is not enabled outside of SMX.\r\n"); 121 | status = STATUS_INVALID_PARAMETER; 122 | } 123 | } 124 | else 125 | { 126 | DEBUG_PRINT("Feature control bit os not locked.\r\n"); 127 | status = STATUS_INVALID_PARAMETER; 128 | } 129 | } 130 | else 131 | { 132 | DEBUG_PRINT("VMX Extensions are not supported.\r\n"); 133 | status = STATUS_NOT_SUPPORTED; 134 | } 135 | 136 | return status; 137 | } -------------------------------------------------------------------------------- /Hypervisor/Hypervisor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /******************** Public Typedefs ********************/ 5 | 6 | /******************** Public Constants ********************/ 7 | 8 | /******************** Public Variables ********************/ 9 | 10 | /******************** Public Prototypes ********************/ 11 | 12 | NTSTATUS Hypervisor_init(void); -------------------------------------------------------------------------------- /Hypervisor/Hypervisor.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 | Debug 22 | ARM 23 | 24 | 25 | Release 26 | ARM 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Release 34 | ARM64 35 | 36 | 37 | 38 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27} 39 | {dd38f7fc-d7bd-488b-9242-7d8754cde80d} 40 | v4.5 41 | 12.0 42 | Debug 43 | Win32 44 | Hypervisor 45 | 10.0.14393.0 46 | 47 | 48 | 49 | Windows10 50 | true 51 | WindowsKernelModeDriver10.0 52 | Driver 53 | WDM 54 | 55 | 56 | Windows10 57 | false 58 | WindowsKernelModeDriver10.0 59 | Driver 60 | WDM 61 | 62 | 63 | Windows10 64 | true 65 | WindowsKernelModeDriver10.0 66 | StaticLibrary 67 | WDM 68 | false 69 | 70 | 71 | Windows10 72 | false 73 | WindowsKernelModeDriver10.0 74 | StaticLibrary 75 | WDM 76 | false 77 | 78 | 79 | Windows10 80 | true 81 | WindowsKernelModeDriver10.0 82 | Driver 83 | WDM 84 | 85 | 86 | Windows10 87 | false 88 | WindowsKernelModeDriver10.0 89 | Driver 90 | WDM 91 | 92 | 93 | Windows10 94 | true 95 | WindowsKernelModeDriver10.0 96 | Driver 97 | WDM 98 | 99 | 100 | Windows10 101 | false 102 | WindowsKernelModeDriver10.0 103 | Driver 104 | WDM 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | DbgengKernelDebugger 115 | 116 | 117 | DbgengKernelDebugger 118 | 119 | 120 | DbgengKernelDebugger 121 | $(ProjectDir)..\ia32-doc\out\;$(IncludePath) 122 | 123 | 124 | DbgengKernelDebugger 125 | $(ProjectDir)..\ia32-doc\out\;$(IncludePath) 126 | 127 | 128 | DbgengKernelDebugger 129 | 130 | 131 | DbgengKernelDebugger 132 | 133 | 134 | DbgengKernelDebugger 135 | 136 | 137 | DbgengKernelDebugger 138 | 139 | 140 | 141 | false 142 | 143 | 144 | 145 | 146 | false 147 | _DEBUG;_WIN32_WINNT=0x0A00;BEA_ENGINE_STATIC;%(PreprocessorDefinitions) 148 | 4214;4201;4996;%(DisableSpecificWarnings) 149 | 150 | 151 | 152 | 153 | false 154 | 155 | 156 | 157 | 158 | false 159 | _WIN32_WINNT=0x0A00;BEA_ENGINE_STATIC;%(PreprocessorDefinitions) 160 | 4214;4201;4996;%(DisableSpecificWarnings) 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /Hypervisor/Hypervisor.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | {fc0548fe-9521-4694-89cc-40a9799aa883} 22 | 23 | 24 | {892b3367-7a0f-40f2-ba6d-e6717e715819} 25 | 26 | 27 | {237c3c4a-885c-4770-9cae-e31ffd905d58} 28 | 29 | 30 | 31 | 32 | Source Files\ASM 33 | 34 | 35 | Source Files\ASM 36 | 37 | 38 | Source Files\ASM 39 | 40 | 41 | 42 | 43 | Header Files\ASM 44 | 45 | 46 | Header Files\ASM 47 | 48 | 49 | Header Files\BEA 50 | 51 | 52 | Header Files\BEA 53 | 54 | 55 | Header Files\BEA 56 | 57 | 58 | Header Files 59 | 60 | 61 | Header Files 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | Header Files 71 | 72 | 73 | Header Files 74 | 75 | 76 | Header Files 77 | 78 | 79 | Header Files 80 | 81 | 82 | Header Files 83 | 84 | 85 | Header Files 86 | 87 | 88 | Header Files 89 | 90 | 91 | Header Files 92 | 93 | 94 | Header Files 95 | 96 | 97 | Header Files 98 | 99 | 100 | Header Files 101 | 102 | 103 | Header Files 104 | 105 | 106 | Header Files 107 | 108 | 109 | 110 | 111 | Source Files 112 | 113 | 114 | Source Files 115 | 116 | 117 | Source Files 118 | 119 | 120 | Source Files 121 | 122 | 123 | Source Files 124 | 125 | 126 | Source Files 127 | 128 | 129 | Source Files 130 | 131 | 132 | Source Files 133 | 134 | 135 | Source Files 136 | 137 | 138 | Source Files 139 | 140 | 141 | Source Files 142 | 143 | 144 | Source Files 145 | 146 | 147 | Source Files 148 | 149 | 150 | Source Files 151 | 152 | 153 | Source Files 154 | 155 | 156 | -------------------------------------------------------------------------------- /Hypervisor/Intrinsics.asm: -------------------------------------------------------------------------------- 1 | ; Contains code that cannot be directly written in C, thus assembly is used. 2 | 3 | include ksamd64.inc 4 | 5 | LEAF_ENTRY _str, _TEXT$00 6 | str word ptr [rcx] ; Store TR value 7 | ret 8 | LEAF_END _str, _TEXT$00 9 | 10 | LEAF_ENTRY _sldt, _TEXT$00 11 | sldt word ptr [rcx] ; Store LDTR value 12 | ret 13 | LEAF_END _sldt, _TEXT$00 14 | 15 | LEAF_ENTRY __invept, _TEXT$00 16 | invept rcx, OWORD PTR[rdx] 17 | ret 18 | LEAF_END __invept, _TEXT$00 19 | 20 | LEAF_ENTRY __invvpid, _TEXT$00 21 | invvpid rcx, OWORD PTR[rdx] 22 | ret 23 | LEAF_END __invvpid, _TEXT$00 24 | 25 | LEAF_ENTRY _RestoreContext, _TEXT$00 26 | movaps xmm0, CxXmm0[rcx] ; 27 | movaps xmm1, CxXmm1[rcx] ; 28 | movaps xmm2, CxXmm2[rcx] ; 29 | movaps xmm3, CxXmm3[rcx] ; 30 | movaps xmm4, CxXmm4[rcx] ; 31 | movaps xmm5, CxXmm5[rcx] ; 32 | movaps xmm6, CxXmm6[rcx] ; Restore all XMM registers 33 | movaps xmm7, CxXmm7[rcx] ; 34 | movaps xmm8, CxXmm8[rcx] ; 35 | movaps xmm9, CxXmm9[rcx] ; 36 | movaps xmm10, CxXmm10[rcx] ; 37 | movaps xmm11, CxXmm11[rcx] ; 38 | movaps xmm12, CxXmm12[rcx] ; 39 | movaps xmm13, CxXmm13[rcx] ; 40 | movaps xmm14, CxXmm14[rcx] ; 41 | movaps xmm15, CxXmm15[rcx] ; 42 | ldmxcsr CxMxCsr[rcx] ; 43 | 44 | mov rax, CxRax[rcx] ; 45 | mov rdx, CxRdx[rcx] ; 46 | mov r8, CxR8[rcx] ; Restore volatile registers 47 | mov r9, CxR9[rcx] ; 48 | mov r10, CxR10[rcx] ; 49 | mov r11, CxR11[rcx] ; 50 | 51 | mov rbx, CxRbx[rcx] ; 52 | mov rsi, CxRsi[rcx] ; 53 | mov rdi, CxRdi[rcx] ; 54 | mov rbp, CxRbp[rcx] ; Restore non volatile regsiters 55 | mov r12, CxR12[rcx] ; 56 | mov r13, CxR13[rcx] ; 57 | mov r14, CxR14[rcx] ; 58 | mov r15, CxR15[rcx] ; 59 | 60 | cli ; Disable interrupts 61 | push CxEFlags[rcx] ; Push RFLAGS on stack 62 | popfq ; Restore RFLAGS 63 | mov rsp, CxRsp[rcx] ; Restore old stack 64 | push CxRip[rcx] ; Push RIP on old stack 65 | mov rcx, CxRcx[rcx] ; Restore RCX since we spilled it 66 | ret ; Restore RIP 67 | LEAF_END _RestoreContext, _TEXT$00 68 | 69 | END -------------------------------------------------------------------------------- /Hypervisor/Intrinsics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ia32.h" 4 | 5 | /******************** Public Typedefs ********************/ 6 | 7 | /******************** Public Constants ********************/ 8 | 9 | /******************** Public Variables ********************/ 10 | 11 | /******************** Public Prototypes ********************/ 12 | 13 | VOID _str(_In_ UINT16* Tr); 14 | VOID _sldt(_In_ UINT16* Ldtr); 15 | VOID __invept(INVEPT_TYPE Type, INVEPT_DESCRIPTOR* Descriptor); 16 | VOID __invvpid(INVVPID_TYPE Type, INVVPID_DESCRIPTOR* Descriptor); 17 | 18 | DECLSPEC_NORETURN 19 | VOID 20 | __cdecl 21 | _RestoreContext( 22 | _In_ PCONTEXT ContextRecord, 23 | _In_opt_ struct _EXCEPTION_RECORD * ExceptionRecord 24 | ); -------------------------------------------------------------------------------- /Hypervisor/MSR.c: -------------------------------------------------------------------------------- 1 | #include "MSR.h" 2 | 3 | /******************** External API ********************/ 4 | 5 | 6 | /******************** Module Typedefs ********************/ 7 | 8 | 9 | /******************** Module Constants ********************/ 10 | 11 | 12 | /******************** Module Variables ********************/ 13 | 14 | 15 | /******************** Module Prototypes ********************/ 16 | 17 | 18 | /******************** Public Code ********************/ 19 | 20 | void MSR_readXMSR(PLARGE_INTEGER msrData, SIZE_T count, UINT32 msrBase) 21 | { 22 | /* Read X MSR's starting from the base, into the MSR array. */ 23 | for (UINT32 i = 0; i < count; i++) 24 | { 25 | msrData[i].QuadPart = __readmsr(msrBase + i); 26 | } 27 | } 28 | 29 | UINT32 MSR_adjustMSR(LARGE_INTEGER ControlValue, UINT32 DesiredValue) 30 | { 31 | /* 32 | * VMX feature/capability MSRs encode the "must be 0" bits in the high word 33 | * of their value, and the "must be 1" bits in the low word of their value. 34 | * Adjust any requested capability/feature based on these requirements. 35 | */ 36 | DesiredValue &= ControlValue.HighPart; 37 | DesiredValue |= ControlValue.LowPart; 38 | return DesiredValue; 39 | } 40 | 41 | /******************** Module Code ********************/ -------------------------------------------------------------------------------- /Hypervisor/MSR.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /******************** Public Defines ********************/ 5 | 6 | /******************** Public Typedefs ********************/ 7 | 8 | /******************** Public Constants ********************/ 9 | 10 | /******************** Public Variables ********************/ 11 | 12 | /******************** Public Prototypes ********************/ 13 | 14 | void MSR_readXMSR(PLARGE_INTEGER msrData, SIZE_T count, UINT32 msrBase); 15 | UINT32 MSR_adjustMSR(LARGE_INTEGER ControlValue, UINT32 DesiredValue); 16 | -------------------------------------------------------------------------------- /Hypervisor/MTF.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "MTF.h" 4 | #include "Debug.h" 5 | #include "ia32.h" 6 | 7 | /******************** External API ********************/ 8 | 9 | 10 | /******************** Module Typedefs ********************/ 11 | 12 | typedef struct _MTF_HANDLER 13 | { 14 | /* Start/End addresses of the range monitored. */ 15 | PUINT8 rangeStart; 16 | PUINT8 rangeEnd; 17 | 18 | /* Callback of the handler, that will be called for processing the trap. */ 19 | fnMTFHandlerCallback callback; 20 | 21 | /* Buffer that can be used for user-supplied configs. */ 22 | PVOID userParameter; 23 | 24 | /* Linked list entry, used for traversal. */ 25 | LIST_ENTRY listEntry; 26 | } MTF_HANDLER, *PMTF_HANDLER; 27 | 28 | /******************** Module Constants ********************/ 29 | 30 | 31 | /******************** Module Variables ********************/ 32 | 33 | 34 | /******************** Module Prototypes ********************/ 35 | 36 | 37 | /******************** Public Code ********************/ 38 | 39 | void MTF_initialise(PMTF_CONFIG mtfConfig) 40 | { 41 | /* By default MTF tracing will be disabled, however we still 42 | * need to initialise it when the VMM starts up. 43 | * 44 | * This is so we can keep track of any MTF handlers are ran at runtime 45 | * before MTF tracing is enabled. */ 46 | InitializeListHead(&mtfConfig->handlerList); 47 | } 48 | 49 | BOOLEAN MTF_handleTrap(PMTF_CONFIG mtfConfig) 50 | { 51 | /* Result indicates handled successfully. */ 52 | BOOLEAN result = FALSE; 53 | 54 | /* Get the value of the guest RIP. */ 55 | SIZE_T guestRIP; 56 | __vmx_vmread(VMCS_GUEST_RIP, &guestRIP); 57 | 58 | /* Search the list of MTF handler and determine which one to call. */ 59 | for (PLIST_ENTRY currentEntry = mtfConfig->handlerList.Flink; 60 | currentEntry != &mtfConfig->handlerList; 61 | currentEntry = currentEntry->Flink) 62 | { 63 | /* Get the handler structure. */ 64 | PMTF_HANDLER mtfHandler = CONTAINING_RECORD(currentEntry, MTF_HANDLER, listEntry); 65 | 66 | /* Check to see if the guest RIP is within these bounds. */ 67 | if ((guestRIP >= (SIZE_T)mtfHandler->rangeStart) && (guestRIP <= (SIZE_T)mtfHandler->rangeEnd)) 68 | { 69 | result = mtfHandler->callback(mtfConfig, mtfHandler->userParameter); 70 | break; 71 | } 72 | } 73 | 74 | return result; 75 | } 76 | 77 | NTSTATUS MTF_addHandler(PMTF_CONFIG mtfConfig, PUINT8 rangeStart, PUINT8 rangeEnd, fnMTFHandlerCallback callback, PVOID userParameter) 78 | { 79 | NTSTATUS status; 80 | 81 | if (NULL != callback) 82 | { 83 | PMTF_HANDLER newHandler = (PMTF_HANDLER)ExAllocatePool(NonPagedPoolNx, sizeof(MTF_HANDLER)); 84 | if (NULL != newHandler) 85 | { 86 | newHandler->rangeStart = rangeStart; 87 | newHandler->rangeEnd = rangeEnd; 88 | newHandler->callback = callback; 89 | newHandler->userParameter = userParameter; 90 | 91 | /* Add this structure to the linked list of already existing handlers. */ 92 | InsertHeadList(&mtfConfig->handlerList, &newHandler->listEntry); 93 | status = STATUS_SUCCESS; 94 | } 95 | else 96 | { 97 | status = STATUS_NO_MEMORY; 98 | } 99 | } 100 | else 101 | { 102 | status = STATUS_INVALID_PARAMETER; 103 | } 104 | 105 | return status; 106 | } 107 | 108 | NTSTATUS MTF_removeHandler(PMTF_CONFIG mtfConfig, fnMTFHandlerCallback callback) 109 | { 110 | NTSTATUS status; 111 | 112 | if (NULL != mtfConfig) 113 | { 114 | status = STATUS_UNSUCCESSFUL; 115 | 116 | /* Search the list of MTF handler and determine which one to remove. */ 117 | for (PLIST_ENTRY currentEntry = mtfConfig->handlerList.Flink; 118 | currentEntry != &mtfConfig->handlerList; 119 | currentEntry = currentEntry->Flink) 120 | { 121 | /* Get the handler structure. */ 122 | PMTF_HANDLER mtfHandler = CONTAINING_RECORD(currentEntry, MTF_HANDLER, listEntry); 123 | 124 | /* Check to see if the guest RIP is within these bounds. */ 125 | if (callback == mtfHandler->callback) 126 | { 127 | RemoveEntryList(currentEntry); 128 | status = STATUS_SUCCESS; 129 | break; 130 | } 131 | } 132 | } 133 | else 134 | { 135 | status = STATUS_INVALID_PARAMETER; 136 | } 137 | 138 | return status; 139 | } 140 | 141 | void MTF_setTracingEnabled(BOOLEAN enabled) 142 | { 143 | IA32_VMX_PROCBASED_CTLS_REGISTER procCtls; 144 | __vmx_vmread(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, &procCtls.Flags); 145 | 146 | procCtls.MonitorTrapFlag = enabled; 147 | __vmx_vmwrite(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, procCtls.Flags); 148 | } 149 | 150 | /******************** Module Code ********************/ -------------------------------------------------------------------------------- /Hypervisor/MTF.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /******************** Public Typedefs ********************/ 5 | 6 | typedef struct _MTF_CONFIG 7 | { 8 | /* List of all MTF handlers that are used. */ 9 | LIST_ENTRY handlerList; 10 | 11 | } MTF_CONFIG, *PMTF_CONFIG; 12 | 13 | /* Callback function for the MTF trap handler. */ 14 | typedef BOOLEAN(*fnMTFHandlerCallback)(PMTF_CONFIG, PVOID userBuffer); 15 | 16 | /******************** Public Constants ********************/ 17 | 18 | /******************** Public Variables ********************/ 19 | 20 | /******************** Public Prototypes ********************/ 21 | void MTF_initialise(PMTF_CONFIG mtfConfig); 22 | BOOLEAN MTF_handleTrap(PMTF_CONFIG mtfConfig); 23 | NTSTATUS MTF_addHandler(PMTF_CONFIG mtfConfig, PUINT8 rangeStart, PUINT8 rangeEnd, fnMTFHandlerCallback callback, PVOID userParameter); 24 | NTSTATUS MTF_removeHandler(PMTF_CONFIG mtfConfig, fnMTFHandlerCallback callback); 25 | void MTF_setTracingEnabled(BOOLEAN enabled); 26 | -------------------------------------------------------------------------------- /Hypervisor/MTRR.c: -------------------------------------------------------------------------------- 1 | #include "MTRR.h" 2 | #include "Debug.h" 3 | 4 | /******************** External API ********************/ 5 | 6 | 7 | /******************** Module Typedefs ********************/ 8 | 9 | 10 | /******************** Module Constants ********************/ 11 | 12 | 13 | /******************** Module Variables ********************/ 14 | 15 | 16 | /******************** Module Prototypes ********************/ 17 | 18 | 19 | /******************** Public Code ********************/ 20 | 21 | void MTRR_readAll(MTRR_RANGE mtrrTable[IA32_MTRR_VARIABLE_COUNT]) 22 | { 23 | IA32_MTRR_CAPABILITIES_REGISTER mtrrCapabilities; 24 | IA32_MTRR_PHYSBASE_REGISTER mtrrBase; 25 | IA32_MTRR_PHYSMASK_REGISTER mtrrMask; 26 | 27 | /* Read the capabilities mask. */ 28 | mtrrCapabilities.Flags = __readmsr(IA32_MTRR_CAPABILITIES); 29 | 30 | DEBUG_PRINT("Storing 0x%I64X MTRR register variables.\r\n", mtrrCapabilities.VariableRangeCount); 31 | 32 | for (UINT32 i = 0; i < mtrrCapabilities.VariableRangeCount; i++) 33 | { 34 | /* Capture the value MTRR value. */ 35 | mtrrBase.Flags = __readmsr(IA32_MTRR_PHYSBASE0 + i * 2); 36 | mtrrMask.Flags = __readmsr((IA32_MTRR_PHYSBASE0 + 1) + i * 2); 37 | 38 | /* Check to see if the specific MTRR is enabled. */ 39 | mtrrTable[i].Type = (UINT32)mtrrBase.Type; 40 | mtrrTable[i].Valid = (UINT32)mtrrMask.Valid; 41 | 42 | if (mtrrTable[i].Valid != FALSE) 43 | { 44 | /* Store the minimum physical address. */ 45 | mtrrTable[i].PhysicalAddressMin = mtrrBase.PageFrameNumber * PAGE_SIZE; 46 | 47 | /* Compute the length and store the maximum physical address. */ 48 | unsigned long bit; 49 | 50 | _BitScanForward64(&bit, mtrrMask.PageFrameNumber * PAGE_SIZE); 51 | mtrrTable[i].PhysicalAddressMax = mtrrTable[i].PhysicalAddressMin + ((1ULL << bit) - 1); 52 | } 53 | 54 | } 55 | } 56 | 57 | /******************** Module Code ********************/ -------------------------------------------------------------------------------- /Hypervisor/MTRR.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ia32.h" 4 | 5 | /******************** Public Defines ********************/ 6 | 7 | 8 | /******************** Public Typedefs ********************/ 9 | 10 | typedef struct _MTRR_RANGE 11 | { 12 | UINT32 Valid; 13 | UINT32 Type; 14 | UINT64 PhysicalAddressMin; 15 | UINT64 PhysicalAddressMax; 16 | } MTRR_RANGE, *PMTRR_RANGE; 17 | 18 | /******************** Public Constants ********************/ 19 | 20 | /******************** Public Variables ********************/ 21 | 22 | /******************** Public Prototypes ********************/ 23 | 24 | void MTRR_readAll(MTRR_RANGE mtrrTable[IA32_MTRR_VARIABLE_COUNT]); 25 | 26 | 27 | -------------------------------------------------------------------------------- /Hypervisor/MemManage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "MemManage.h" 4 | #include "GuestShim.h" 5 | #include "ia32.h" 6 | #include "Debug.h" 7 | 8 | /******************** External API ********************/ 9 | 10 | 11 | /******************** Module Typedefs ********************/ 12 | 13 | /* Each level of a paging structure. */ 14 | typedef enum 15 | { 16 | MM_LEVEL_PTE = 1, 17 | MM_LEVEL_PDE = 2, 18 | MM_LEVEL_PDPTE = 3, 19 | MM_LEVEL_PML4E = 4 20 | } MM_LEVEL; 21 | 22 | /******************** Module Constants ********************/ 23 | #define POOL_TAG '1TST' 24 | 25 | #define OFFSET_DIRECTORY_TABLE_BASE 0x028 26 | #define OFFSET_USER_DIR_TABLE 0x280 27 | 28 | #define SIZE_2MB (2 * 1024 * 1024) 29 | 30 | /* Counts of how many entries are in a table. */ 31 | #define PAGING_TABLE_ENTRY_COUNT 512 32 | #define PAGING_PML4E_COUNT 512 33 | #define PAGING_PML3E_COUNT 512 34 | #define PAGING_PML2E_COUNT 512 35 | #define PAGING_PML1E_COUNT 512 36 | 37 | /* Calculates the offset into the PDE (PML1) structure. */ 38 | #define ADDRMASK_PML1_OFFSET(_VAR_) ((SIZE_T)_VAR_ & 0xFFFULL) 39 | 40 | /* Calculates the index of the PDE (PML1) within the PDT structure. */ 41 | #define ADDRMASK_PML1_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x1FF000ULL) >> 12) 42 | 43 | /* Calculates the index of the PDT (PML2) within the PDPT*/ 44 | #define ADDRMASK_PML2_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x3FE00000ULL) >> 21) 45 | 46 | /* Calculates the index of the PDPT (PML3) within the PML4 */ 47 | #define ADDRMASK_PML3_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0x7FC0000000ULL) >> 30) 48 | 49 | /* Calculates the index of PML4. */ 50 | #define ADDRMASK_PML4_INDEX(_VAR_) (((SIZE_T)_VAR_ & 0xFF8000000000ULL) >> 39) 51 | 52 | /******************** Module Variables ********************/ 53 | 54 | 55 | /******************** Module Prototypes ********************/ 56 | static PT_ENTRY_64* getSystemPTEFromVA(CR3 tableBase, PVOID virtualAddress, MM_LEVEL* level); 57 | static VOID* mapPhysicalAddress(PMM_CONTEXT context, HOST_PHYS_ADDRESS physicalAddress); 58 | static void unmapPhysicalAddress(PMM_CONTEXT context); 59 | static NTSTATUS split2MbPage(PDE_2MB_64* pdeLarge); 60 | static UINT64 physicalFromVirtual(VOID* virtualAddress); 61 | static VOID* virtualFromPhysical(UINT64 physicalAddress); 62 | 63 | /******************** Public Code ********************/ 64 | 65 | NTSTATUS MemManage_init(PMM_CONTEXT context, CR3 hostCR3) 66 | { 67 | NTSTATUS status = STATUS_SUCCESS; 68 | 69 | /* Reserve a single page, this will be used for mapping in the guest 70 | * page data into. */ 71 | PVOID reservedPage = MmAllocateMappingAddress(PAGE_SIZE, POOL_TAG); 72 | if (NULL != reservedPage) 73 | { 74 | /* Attempt to get the page table entry of the reserved page, 75 | * we need to ensure this is not a 2MB large page, if so we must split it. */ 76 | MM_LEVEL tableLevel; 77 | PT_ENTRY_64* reservedPagePTE = getSystemPTEFromVA(hostCR3, reservedPage, &tableLevel); 78 | if (MM_LEVEL_PDE == tableLevel) 79 | { 80 | /* A split must take place. */ 81 | status = split2MbPage((PDE_2MB_64*)reservedPagePTE); 82 | 83 | /* Get the new PTE. */ 84 | reservedPagePTE = getSystemPTEFromVA(hostCR3, reservedPage, &tableLevel); 85 | } 86 | 87 | /* Ensure we are still in success state, splitting could have failed. */ 88 | if (NT_SUCCESS(status)) 89 | { 90 | /* Drop the translation of the virtual address, 91 | * If at any point we observe it at 0, we know nothing is "mapped". */ 92 | reservedPagePTE->Flags = 0; 93 | 94 | context->reservedPage = reservedPage; 95 | context->reservedPagePte = (PTE_64*)reservedPagePTE; 96 | } 97 | } 98 | else 99 | { 100 | status = STATUS_INSUFFICIENT_RESOURCES; 101 | } 102 | 103 | /* If an error took place during initialisation, free 104 | * all allocated memory to prevent leaks. */ 105 | if (NT_ERROR(status)) 106 | { 107 | if (NULL != reservedPage) 108 | { 109 | MmFreeMappingAddress(reservedPage, POOL_TAG); 110 | } 111 | } 112 | 113 | return status; 114 | } 115 | 116 | void MemManage_uninit(PMM_CONTEXT context) 117 | { 118 | MmFreeMappingAddress(context->reservedPage, POOL_TAG); 119 | } 120 | 121 | NTSTATUS MemManage_readVirtualAddress(PMM_CONTEXT context, CR3 tableBase, GUEST_VIRTUAL_ADDRESS guestVA, PVOID buffer, SIZE_T size) 122 | { 123 | NTSTATUS status = STATUS_UNSUCCESSFUL; 124 | 125 | GUEST_VIRTUAL_ADDRESS currentVA = guestVA; 126 | PUINT8 currentBuffer = buffer; 127 | 128 | while (0 != size) 129 | { 130 | /* We can only read one page at a time, therfore we need 131 | * to split our actions into a per page business. */ 132 | SIZE_T bytesThisPage = size; 133 | 134 | /* Now trim so the first time in the loop we only do to a page boundary. */ 135 | if (currentVA & (PAGE_SIZE - 1)) 136 | { 137 | SIZE_T misalignedBy = PAGE_SIZE - (currentVA & (PAGE_SIZE - 1)); 138 | 139 | if (bytesThisPage > misalignedBy) 140 | { 141 | bytesThisPage = misalignedBy; 142 | } 143 | } 144 | 145 | /* If we still have more than a page left, only read a page this time. */ 146 | if (bytesThisPage > PAGE_SIZE) 147 | { 148 | bytesThisPage = PAGE_SIZE; 149 | } 150 | 151 | /* Get the physical address of the guest memory. */ 152 | HOST_PHYS_ADDRESS physHost = GuestShim_GuestUVAToHPA(context, tableBase, currentVA); 153 | if (0 != physHost) 154 | { 155 | /* Read the memory. */ 156 | status = MemManage_readPhysicalAddress(context, physHost, currentBuffer, bytesThisPage); 157 | 158 | if (NT_SUCCESS(status)) 159 | { 160 | /* Increment pointer and subtract size remaining. */ 161 | currentVA += bytesThisPage; 162 | currentBuffer += bytesThisPage; 163 | size -= bytesThisPage; 164 | } 165 | } 166 | } 167 | 168 | return status; 169 | } 170 | 171 | NTSTATUS MemManage_writeVirtualAddress(PMM_CONTEXT context, CR3 tableBase, GUEST_VIRTUAL_ADDRESS guestVA, PVOID buffer, SIZE_T size) 172 | { 173 | NTSTATUS status = STATUS_UNSUCCESSFUL; 174 | 175 | GUEST_VIRTUAL_ADDRESS currentVA = guestVA; 176 | PUINT8 currentBuffer = buffer; 177 | 178 | while (0 != size) 179 | { 180 | /* We can only read one page at a time, therfore we need 181 | * to split our actions into a per page business. */ 182 | SIZE_T bytesThisPage = size; 183 | 184 | /* Now trim so the first time in the loop we only do to a page boundary. */ 185 | if (currentVA & (PAGE_SIZE - 1)) 186 | { 187 | SIZE_T misalignedBy = PAGE_SIZE - (currentVA & (PAGE_SIZE - 1)); 188 | 189 | if (bytesThisPage > misalignedBy) 190 | { 191 | bytesThisPage = misalignedBy; 192 | } 193 | } 194 | 195 | /* If we still have more than a page left, only read a page this time. */ 196 | if (bytesThisPage > PAGE_SIZE) 197 | { 198 | bytesThisPage = PAGE_SIZE; 199 | } 200 | 201 | /* Get the physical address of the guest memory. */ 202 | HOST_PHYS_ADDRESS physHost = GuestShim_GuestUVAToHPA(context, tableBase, currentVA); 203 | if (0 != physHost) 204 | { 205 | /* Write the memory. */ 206 | status = MemManage_writePhysicalAddress(context, physHost, currentBuffer, bytesThisPage); 207 | 208 | if (NT_SUCCESS(status)) 209 | { 210 | /* Increment pointer and subtract size remaining. */ 211 | currentVA += bytesThisPage; 212 | currentBuffer += bytesThisPage; 213 | size -= bytesThisPage; 214 | } 215 | } 216 | } 217 | 218 | return status; 219 | } 220 | 221 | NTSTATUS MemManage_readPhysicalAddress(PMM_CONTEXT context, HOST_PHYS_ADDRESS physicalAddress, VOID* buffer, SIZE_T bytesToCopy) 222 | { 223 | NTSTATUS status; 224 | 225 | /* Map the physical memory. */ 226 | VOID* mappedVA = mapPhysicalAddress(context, physicalAddress); 227 | if (NULL != mappedVA) 228 | { 229 | /* Do the copy. */ 230 | RtlCopyMemory(buffer, mappedVA, bytesToCopy); 231 | 232 | /* Unmap the physical memory. */ 233 | unmapPhysicalAddress(context); 234 | status = STATUS_SUCCESS; 235 | } 236 | else 237 | { 238 | status = STATUS_NO_MEMORY; 239 | } 240 | 241 | return status; 242 | } 243 | 244 | NTSTATUS MemManage_writePhysicalAddress(PMM_CONTEXT context, HOST_PHYS_ADDRESS physicalAddress, VOID* buffer, SIZE_T bytesToCopy) 245 | { 246 | NTSTATUS status; 247 | 248 | /* Map the physical memory. */ 249 | VOID* mappedVA = mapPhysicalAddress(context, physicalAddress); 250 | if (NULL != mappedVA) 251 | { 252 | /* Do the copy. */ 253 | RtlCopyMemory(mappedVA, buffer, bytesToCopy); 254 | 255 | /* Unmap the physical memory. */ 256 | unmapPhysicalAddress(context); 257 | status = STATUS_SUCCESS; 258 | } 259 | else 260 | { 261 | status = STATUS_NO_MEMORY; 262 | } 263 | 264 | return status; 265 | } 266 | CR3 MemManage_getPageTableBase(PEPROCESS process) 267 | { 268 | /* As KVA shadowing is used for CR3 as a mitigation for spectre/meltdown 269 | * we cannot use the CR3 as it is a shadowed table instead. Directly 270 | * access to DirectoryTableBase of the process that caused 271 | * the VMExit. 272 | * 273 | * UserDirectoryTableBase at a fixed offset, EPROCESS->Pcb.UserDirectoryTableBase 274 | * This was found using the following windbg commands: 275 | * dt nt!_EPROCESS 276 | * dt nt!_KPROCESS 277 | * 278 | * This is subject to change between Windows versions. */ 279 | 280 | /* If running as admin, UserDirectoryTableBase will be set as 1 and KVA shadowing will not be present, 281 | * therefor we should use the directory table base (kernel). Again due to KVA shadowing... */ 282 | CR3 tableBase; 283 | 284 | tableBase = *((CR3*)((UINT64)process + OFFSET_USER_DIR_TABLE)); 285 | tableBase.Flags &= ~0xFFF; 286 | if (0 == tableBase.Flags) 287 | { 288 | tableBase = *((CR3*)((UINT64)process + OFFSET_DIRECTORY_TABLE_BASE)); 289 | tableBase.Flags &= ~0xFFF; 290 | } 291 | 292 | return tableBase; 293 | } 294 | 295 | /******************** Module Code ********************/ 296 | 297 | static PT_ENTRY_64* getSystemPTEFromVA(CR3 tableBase, PVOID virtualAddress, MM_LEVEL* level) 298 | { 299 | PT_ENTRY_64* result = NULL; 300 | 301 | /* Gather the indexes for the page tables from the VA. */ 302 | UINT64 indexPML4 = ADDRMASK_PML4_INDEX(virtualAddress); 303 | UINT64 indexPML3 = ADDRMASK_PML3_INDEX(virtualAddress); 304 | UINT64 indexPML2 = ADDRMASK_PML2_INDEX(virtualAddress); 305 | UINT64 indexPML1 = ADDRMASK_PML1_INDEX(virtualAddress); 306 | 307 | /* Read the PML4 from the target. */ 308 | PML4E_64* pml4 = virtualFromPhysical(tableBase.AddressOfPageDirectory * PAGE_SIZE); 309 | PML4E_64* pml4e = &pml4[indexPML4]; 310 | 311 | result = (PT_ENTRY_64*)pml4e; 312 | *level = MM_LEVEL_PML4E; 313 | if (TRUE == pml4e->Present) 314 | { 315 | /* Read PML3 from the guest. */ 316 | PDPTE_64* pdpt = virtualFromPhysical(pml4e->PageFrameNumber * PAGE_SIZE); 317 | PDPTE_64* pdpte = &pdpt[indexPML3]; 318 | 319 | result = (PT_ENTRY_64*)pdpte; 320 | *level = MM_LEVEL_PDPTE; 321 | 322 | /* Only attempt to get lower level if present and not a large page. */ 323 | if ((TRUE == pdpte->Present) && (FALSE == pdpte->LargePage)) 324 | { 325 | /* Read PML2 from the guest. */ 326 | PDE_64* pd = virtualFromPhysical(pdpte->PageFrameNumber * PAGE_SIZE); 327 | PDE_64* pde = &pd[indexPML2]; 328 | 329 | result = (PT_ENTRY_64*)pde; 330 | *level = MM_LEVEL_PDE; 331 | if ((TRUE == pde->Present) && (FALSE == pde->LargePage)) 332 | { 333 | /* Read PML1 from the guest. */ 334 | PTE_64* pt = virtualFromPhysical(pde->PageFrameNumber * PAGE_SIZE); 335 | PTE_64* pte = &pt[indexPML1]; 336 | 337 | result = (PT_ENTRY_64*)pte; 338 | *level = MM_LEVEL_PTE; 339 | } 340 | } 341 | } 342 | 343 | return result; 344 | } 345 | 346 | static VOID* mapPhysicalAddress(PMM_CONTEXT context, HOST_PHYS_ADDRESS physicalAddress) 347 | { 348 | /* Map the requested physical address to our reserved page. */ 349 | context->reservedPagePte->Present = TRUE; 350 | context->reservedPagePte->Write = TRUE; 351 | context->reservedPagePte->PageFrameNumber = physicalAddress / PAGE_SIZE; 352 | 353 | /* Invalidate the TLB entries so we don't get cached old data. */ 354 | __invlpg(context->reservedPage); 355 | 356 | return (VOID*)(((PUINT8)context->reservedPage) + ADDRMASK_PML1_OFFSET(physicalAddress)); 357 | } 358 | 359 | static void unmapPhysicalAddress(PMM_CONTEXT context) 360 | { 361 | /* Clear the page entry and flush the cache (TLB). */ 362 | context->reservedPagePte->Flags = 0; 363 | __invlpg(context->reservedPage); 364 | } 365 | 366 | static NTSTATUS split2MbPage(PDE_2MB_64* pdeLarge) 367 | { 368 | NTSTATUS status; 369 | 370 | /* Allocate a new page table, this will be used for splitting the 2MB 371 | * entry into 512 4kb pages, 512 8byte entries = one page. */ 372 | PTE_64* pt = ExAllocatePool(NonPagedPoolNx, PAGE_SIZE); 373 | if (NULL != pt) 374 | { 375 | /* Close the large page bit and then propagate the current permissions to 376 | * all the entries in our newly allocated PT. */ 377 | pdeLarge->LargePage = FALSE; 378 | __stosq((UINT64*)pt, pdeLarge->Flags, PAGING_TABLE_ENTRY_COUNT); 379 | 380 | /* Calculate the physical address of where the PDPTE will be. */ 381 | UINT64 base = pdeLarge->PageFrameNumber * SIZE_2MB; 382 | 383 | /* Update the page frame mapping for each PTE. */ 384 | for (SIZE_T i = 0; i < PAGING_TABLE_ENTRY_COUNT; i++) 385 | { 386 | UINT64 physAddrToMap = base + (i * PAGE_SIZE); 387 | pt[i].PageFrameNumber = physAddrToMap / PAGE_SIZE; 388 | } 389 | 390 | /* Now update the convert the largePDE to a standard entry. */ 391 | PDE_64* pde = (PDE_64*)pdeLarge; 392 | pde->Reserved1 = 0; 393 | pde->Reserved2 = 0; 394 | pde->PageFrameNumber = physicalFromVirtual(pt) / PAGE_SIZE; 395 | status = STATUS_SUCCESS; 396 | } 397 | else 398 | { 399 | status = STATUS_INSUFFICIENT_RESOURCES; 400 | } 401 | 402 | return status; 403 | } 404 | 405 | static UINT64 physicalFromVirtual(VOID* virtualAddress) 406 | { 407 | return MmGetPhysicalAddress(virtualAddress).QuadPart; 408 | } 409 | 410 | static VOID* virtualFromPhysical(UINT64 physicalAddress) 411 | { 412 | PHYSICAL_ADDRESS pa; 413 | pa.QuadPart = physicalAddress; 414 | 415 | return MmGetVirtualForPhysical(pa); 416 | } 417 | -------------------------------------------------------------------------------- /Hypervisor/MemManage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ia32.h" 4 | 5 | /******************** Public Typedefs ********************/ 6 | typedef struct _MM_CONTEXT 7 | { 8 | /* The reserved page that will be used for storing data in. */ 9 | PVOID reservedPage; 10 | 11 | /* PTE that belongs to the reserved page. */ 12 | PTE_64* reservedPagePte; 13 | } MM_CONTEXT, *PMM_CONTEXT; 14 | 15 | typedef SIZE_T HOST_PHYS_ADDRESS; 16 | typedef SIZE_T GUEST_VIRTUAL_ADDRESS; 17 | 18 | /******************** Public Constants ********************/ 19 | 20 | /******************** Public Variables ********************/ 21 | 22 | /******************** Public Prototypes ********************/ 23 | NTSTATUS MemManage_init(PMM_CONTEXT context, CR3 hostCR3); 24 | void MemManage_uninit(PMM_CONTEXT context); 25 | NTSTATUS MemManage_readVirtualAddress(PMM_CONTEXT context, CR3 tableBase, GUEST_VIRTUAL_ADDRESS guestVA, PVOID buffer, SIZE_T size); 26 | NTSTATUS MemManage_writeVirtualAddress(PMM_CONTEXT context, CR3 tableBase, GUEST_VIRTUAL_ADDRESS guestVA, PVOID buffer, SIZE_T size); 27 | NTSTATUS MemManage_readPhysicalAddress(PMM_CONTEXT context, HOST_PHYS_ADDRESS physicalAddress, VOID* buffer, SIZE_T bytesToCopy); 28 | NTSTATUS MemManage_writePhysicalAddress(PMM_CONTEXT context, HOST_PHYS_ADDRESS physicalAddress, VOID* buffer, SIZE_T bytesToCopy); 29 | CR3 MemManage_getPageTableBase(PEPROCESS process); -------------------------------------------------------------------------------- /Hypervisor/PageTable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "PageTable.h" 3 | 4 | /******************** External API ********************/ 5 | 6 | 7 | /******************** Module Typedefs ********************/ 8 | 9 | 10 | /******************** Module Constants ********************/ 11 | #define PML4E_COUNT 512 12 | 13 | /******************** Module Variables ********************/ 14 | 15 | static DECLSPEC_ALIGN(PAGE_SIZE) PML4E_64 vmPML4[PML4E_COUNT] = { 0 }; 16 | 17 | /******************** Module Prototypes ********************/ 18 | 19 | 20 | /******************** Public Code ********************/ 21 | 22 | NTSTATUS PageTable_init(CR3 originalCR3, CR3* newCR3) 23 | { 24 | /* We emulate providing our own page table, 25 | * to do this, we copy the original hosts PML4E 26 | * and provide our own CR3, this should be enough. */ 27 | NTSTATUS status; 28 | 29 | /* Get the virtual address of the original hosts PML4. */ 30 | PHYSICAL_ADDRESS physOrigPML4; 31 | physOrigPML4.QuadPart = originalCR3.AddressOfPageDirectory * PAGE_SIZE; 32 | 33 | PML4E_64* originalPML4 = (PML4E_64*)MmGetVirtualForPhysical(physOrigPML4); 34 | if (NULL != originalPML4) 35 | { 36 | /* Copy the PML4E entries from the host/original CR3. */ 37 | for (SIZE_T i = 0; i < PML4E_COUNT; i++) 38 | { 39 | vmPML4[i] = originalPML4[i]; 40 | } 41 | 42 | /* Calculate the physical address of our PML4 table. */ 43 | PHYSICAL_ADDRESS physNewPML4; 44 | physNewPML4 = MmGetPhysicalAddress(vmPML4); 45 | 46 | /* Create the new CR3 that will indicate to our PML4 base. */ 47 | newCR3->Flags = 0; 48 | newCR3->AddressOfPageDirectory = (physNewPML4.QuadPart) / PAGE_SIZE; 49 | 50 | status = STATUS_SUCCESS; 51 | } 52 | else 53 | { 54 | status = STATUS_INVALID_ADDRESS; 55 | } 56 | 57 | return status; 58 | } 59 | 60 | 61 | /******************** Module Code ********************/ -------------------------------------------------------------------------------- /Hypervisor/PageTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ia32.h" 4 | 5 | /******************** Public Typedefs ********************/ 6 | 7 | /******************** Public Constants ********************/ 8 | 9 | /******************** Public Variables ********************/ 10 | 11 | /******************** Public Prototypes ********************/ 12 | 13 | NTSTATUS PageTable_init(CR3 originalCR3, CR3* newCR3); 14 | -------------------------------------------------------------------------------- /Hypervisor/ProcessDefines.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /******************** Public Defines *********************/ 5 | #define IMAGE_DOS_SIGNATURE 0x5A4D 6 | #define IMAGE_NT_SIGNATURE 0x00004550 7 | #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20B 8 | #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 9 | #define IMAGE_SIZEOF_SHORT_NAME 8 10 | #define IMAGE_SIZEOF_SECTION_HEADER 40 11 | 12 | #define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable. 13 | #define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable. 14 | #define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable. 15 | 16 | #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory 17 | #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory 18 | #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory 19 | #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory 20 | #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory 21 | #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table 22 | #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory 23 | // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) 24 | #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data 25 | #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP 26 | #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory 27 | #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory 28 | #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers 29 | #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table 30 | #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors 31 | #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor 32 | 33 | /******************** Public Typedefs ********************/ 34 | 35 | typedef PVOID* PPVOID; 36 | 37 | typedef struct _RTL_PROCESS_MODULE_INFORMATION 38 | { 39 | HANDLE Section; 40 | PVOID MappedBase; 41 | PVOID ImageBase; 42 | ULONG ImageSize; 43 | ULONG Flags; 44 | USHORT LoadOrderIndex; 45 | USHORT InitOrderIndex; 46 | USHORT LoadCount; 47 | USHORT OffsetToFileName; 48 | UCHAR FullPathName[256]; 49 | } RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; 50 | 51 | typedef struct _RTL_PROCESS_MODULES 52 | { 53 | ULONG NumberOfModules; 54 | RTL_PROCESS_MODULE_INFORMATION Modules[1]; 55 | } RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; 56 | 57 | typedef struct _RTL_USER_PROCESS_PARAMETERS { 58 | UINT8 Reserved1[16]; 59 | PVOID Reserved2[10]; 60 | UNICODE_STRING ImagePathName; 61 | UNICODE_STRING CommandLine; 62 | } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; 63 | 64 | typedef struct _LDR_DATA_TABLE_ENTRY 65 | { 66 | LIST_ENTRY InLoadOrderLinks; 67 | LIST_ENTRY InMemoryOrderLinks; 68 | LIST_ENTRY InInitializationOrderLinks; 69 | PVOID DllBase; 70 | PVOID EntryPoint; 71 | ULONG SizeOfImage; 72 | UNICODE_STRING FullDllName; 73 | UNICODE_STRING BaseDllName; 74 | ULONG Flags; 75 | UINT16 LoadCount; 76 | UINT16 TlsIndex; 77 | union 78 | { 79 | LIST_ENTRY HashLinks; 80 | struct 81 | { 82 | PVOID SectionPointer; 83 | ULONG CheckSum; 84 | }; 85 | }; 86 | union 87 | { 88 | ULONG TimeDateStamp; 89 | PVOID LoadedImports; 90 | }; 91 | PVOID EntryPointActivationContext; 92 | PVOID PatchInformation; 93 | LIST_ENTRY ForwarderLinks; 94 | LIST_ENTRY ServiceTagLinks; 95 | LIST_ENTRY StaticLinks; 96 | } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; 97 | 98 | typedef struct _PEB_LDR_DATA 99 | { 100 | ULONG Length; 101 | UCHAR Initialized; 102 | PVOID SsHandle; 103 | LIST_ENTRY InLoadOrderModuleList; 104 | LIST_ENTRY InMemoryOrderModuleList; 105 | LIST_ENTRY InInitializationOrderModuleList; 106 | PVOID EntryInProgress; 107 | } PEB_LDR_DATA, *PPEB_LDR_DATA; 108 | 109 | typedef struct _PEB { 110 | BOOLEAN InheritedAddressSpace; 111 | BOOLEAN ReadImageFileExecOptions; 112 | BOOLEAN BeingDebugged; 113 | BOOLEAN Spare; 114 | HANDLE Mutant; 115 | PVOID ImageBaseAddress; 116 | PPEB_LDR_DATA Ldr; 117 | PRTL_USER_PROCESS_PARAMETERS ProcessParameters; 118 | PVOID SubSystemData; 119 | PVOID ProcessHeap; 120 | PVOID FastPebLock; 121 | PVOID /*PPEBLOCKROUTINE*/FastPebLockRoutine; 122 | PVOID /*PPEBLOCKROUTINE*/FastPebUnlockRoutine; 123 | ULONG EnvironmentUpdateCount; 124 | PPVOID KernelCallbackTable; 125 | PVOID EventLogSection; 126 | PVOID EventLog; 127 | PVOID FreeList; 128 | ULONG TlsExpansionCounter; 129 | PVOID TlsBitmap; 130 | ULONG TlsBitmapBits[0x2]; 131 | PVOID ReadOnlySharedMemoryBase; 132 | PVOID ReadOnlySharedMemoryHeap; 133 | PPVOID ReadOnlyStaticServerData; 134 | PVOID AnsiCodePageData; 135 | PVOID OemCodePageData; 136 | PVOID UnicodeCaseTableData; 137 | ULONG NumberOfProcessors; 138 | ULONG NtGlobalFlag; 139 | UINT8 Spare2[0x4]; 140 | LARGE_INTEGER CriticalSectionTimeout; 141 | ULONG HeapSegmentReserve; 142 | ULONG HeapSegmentCommit; 143 | ULONG HeapDeCommitTotalFreeThreshold; 144 | ULONG HeapDeCommitFreeBlockThreshold; 145 | ULONG NumberOfHeaps; 146 | ULONG MaximumNumberOfHeaps; 147 | PPVOID* ProcessHeaps; 148 | PVOID GdiSharedHandleTable; 149 | PVOID ProcessStarterHelper; 150 | PVOID GdiDCAttributeList; 151 | PVOID LoaderLock; 152 | ULONG OSMajorVersion; 153 | ULONG OSMinorVersion; 154 | ULONG OSBuildNumber; 155 | ULONG OSPlatformId; 156 | ULONG ImageSubSystem; 157 | ULONG ImageSubSystemMajorVersion; 158 | ULONG ImageSubSystemMinorVersion; 159 | ULONG GdiHandleBuffer[0x22]; 160 | ULONG PostProcessInitRoutine; 161 | ULONG TlsExpansionBitmap; 162 | UINT8 TlsExpansionBitmapBits[0x80]; 163 | ULONG SessionId; 164 | 165 | } PEB, *PPEB; 166 | 167 | typedef enum _SYSTEM_INFORMATION_CLASS 168 | { 169 | SystemBasicInformation, 170 | SystemProcessorInformation, 171 | SystemPerformanceInformation, 172 | SystemTimeOfDayInformation, 173 | SystemPathInformation, 174 | SystemProcessInformation, 175 | SystemCallCountInformation, 176 | SystemDeviceInformation, 177 | SystemProcessorPerformanceInformation, 178 | SystemFlagsInformation, 179 | SystemCallTimeInformation, 180 | SystemModuleInformation, 181 | SystemBigPoolInformation = 0x42 182 | } SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS; 183 | 184 | typedef struct _IMAGE_DOS_HEADER 185 | { 186 | WORD e_magic; 187 | WORD e_cblp; 188 | WORD e_cp; 189 | WORD e_crlc; 190 | WORD e_cparhdr; 191 | WORD e_minalloc; 192 | WORD e_maxalloc; 193 | WORD e_ss; 194 | WORD e_sp; 195 | WORD e_csum; 196 | WORD e_ip; 197 | WORD e_cs; 198 | WORD e_lfarlc; 199 | WORD e_ovno; 200 | WORD e_res[4]; 201 | WORD e_oemid; 202 | WORD e_oeminfo; 203 | WORD e_res2[10]; 204 | LONG e_lfanew; 205 | } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 206 | 207 | typedef struct _IMAGE_FILE_HEADER 208 | { 209 | WORD Machine; 210 | WORD NumberOfSections; 211 | DWORD TimeDateStamp; 212 | DWORD PointerToSymbolTable; 213 | DWORD NumberOfSymbols; 214 | WORD SizeOfOptionalHeader; 215 | WORD Characteristics; 216 | } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 217 | 218 | typedef struct _IMAGE_DATA_DIRECTORY 219 | { 220 | DWORD VirtualAddress; 221 | DWORD Size; 222 | } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 223 | 224 | typedef struct _IMAGE_OPTIONAL_HEADER64 225 | { 226 | WORD Magic; 227 | BYTE MajorLinkerVersion; 228 | BYTE MinorLinkerVersion; 229 | DWORD SizeOfCode; 230 | DWORD SizeOfInitializedData; 231 | DWORD SizeOfUninitializedData; 232 | DWORD AddressOfEntryPoint; 233 | DWORD BaseOfCode; 234 | ULONGLONG ImageBase; 235 | DWORD SectionAlignment; 236 | DWORD FileAlignment; 237 | WORD MajorOperatingSystemVersion; 238 | WORD MinorOperatingSystemVersion; 239 | WORD MajorImageVersion; 240 | WORD MinorImageVersion; 241 | WORD MajorSubsystemVersion; 242 | WORD MinorSubsystemVersion; 243 | DWORD Win32VersionValue; 244 | DWORD SizeOfImage; 245 | DWORD SizeOfHeaders; 246 | DWORD CheckSum; 247 | WORD Subsystem; 248 | WORD DllCharacteristics; 249 | ULONGLONG SizeOfStackReserve; 250 | ULONGLONG SizeOfStackCommit; 251 | ULONGLONG SizeOfHeapReserve; 252 | ULONGLONG SizeOfHeapCommit; 253 | DWORD LoaderFlags; 254 | DWORD NumberOfRvaAndSizes; 255 | IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 256 | } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; 257 | 258 | typedef struct _IMAGE_NT_HEADERS64 259 | { 260 | DWORD Signature; 261 | IMAGE_FILE_HEADER FileHeader; 262 | IMAGE_OPTIONAL_HEADER64 OptionalHeader; 263 | } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; 264 | 265 | typedef struct _IMAGE_SECTION_HEADER 266 | { 267 | BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; 268 | union { 269 | DWORD PhysicalAddress; 270 | DWORD VirtualSize; 271 | } Misc; 272 | DWORD VirtualAddress; 273 | DWORD SizeOfRawData; 274 | DWORD PointerToRawData; 275 | DWORD PointerToRelocations; 276 | DWORD PointerToLinenumbers; 277 | WORD NumberOfRelocations; 278 | WORD NumberOfLinenumbers; 279 | DWORD Characteristics; 280 | } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 281 | 282 | typedef struct _IMAGE_EXPORT_DIRECTORY { 283 | DWORD Characteristics; 284 | DWORD TimeDateStamp; 285 | WORD MajorVersion; 286 | WORD MinorVersion; 287 | DWORD Name; 288 | DWORD Base; 289 | DWORD NumberOfFunctions; 290 | DWORD NumberOfNames; 291 | DWORD AddressOfFunctions; // RVA from base of image 292 | DWORD AddressOfNames; // RVA from base of image 293 | DWORD AddressOfNameOrdinals; // RVA from base of image 294 | } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; 295 | 296 | #define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER) \ 297 | ((ULONG_PTR)(ntheader) + \ 298 | FIELD_OFFSET( IMAGE_NT_HEADERS64, OptionalHeader ) + \ 299 | ((ntheader))->FileHeader.SizeOfOptionalHeader \ 300 | )) 301 | 302 | /******************** Public Constants ********************/ 303 | 304 | #define MIN_USER_MODE_ADDR ((PVOID)0x10000LL) 305 | #define MAX_USER_MODE_ADDR ((PVOID)0x7FFFFFFFFFFFULL) 306 | 307 | /******************** Public Variables ********************/ 308 | 309 | /******************** Public Prototypes ********************/ 310 | 311 | NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation( 312 | IN SYSTEM_INFORMATION_CLASS SystemInformationClass, 313 | OUT PVOID SystemInformation, 314 | IN ULONG SystemInformationLength, 315 | OUT PULONG ReturnLength OPTIONAL 316 | ); 317 | 318 | NTSYSAPI ULONG NTAPI RtlRandomEx( 319 | _Inout_ PULONG Seed 320 | ); 321 | 322 | NTSTATUS NTAPI MmCopyVirtualMemory 323 | ( 324 | PEPROCESS SourceProcess, 325 | PVOID SourceAddress, 326 | PEPROCESS TargetProcess, 327 | PVOID TargetAddress, 328 | SIZE_T BufferSize, 329 | KPROCESSOR_MODE PreviousMode, 330 | PSIZE_T ReturnSize 331 | ); 332 | 333 | NTKERNELAPI PPEB NTAPI PsGetProcessPeb( 334 | IN PEPROCESS Process 335 | ); 336 | -------------------------------------------------------------------------------- /Hypervisor/VMCALL.c: -------------------------------------------------------------------------------- 1 | #include "VMCALL.h" 2 | #include "VMCALL_Common.h" 3 | #include "MemManage.h" 4 | #include "VMShadow.h" 5 | #include "Process.h" 6 | 7 | /******************** External API ********************/ 8 | 9 | 10 | /******************** Module Typedefs ********************/ 11 | 12 | typedef NTSTATUS(*fnActionHandler)(PVMM_DATA lpData, CR3 guestCR3, GUEST_VIRTUAL_ADDRESS buffer, SIZE_T bufferSize); 13 | 14 | /******************** Module Constants ********************/ 15 | 16 | 17 | /******************** Module Variables ********************/ 18 | 19 | 20 | /******************** Module Prototypes ********************/ 21 | static NTSTATUS actionCheckPresence(PVMM_DATA lpData, CR3 guestCR3, GUEST_VIRTUAL_ADDRESS buffer, SIZE_T bufferSize); 22 | static NTSTATUS actionRunAsRoot(PVMM_DATA lpData, CR3 guestCR3, GUEST_VIRTUAL_ADDRESS buffer, SIZE_T bufferSize); 23 | static NTSTATUS actionShadowInProcess(PVMM_DATA lpData, CR3 guestCR3, GUEST_VIRTUAL_ADDRESS buffer, SIZE_T bufferSize); 24 | 25 | /******************** Action Handlers ********************/ 26 | 27 | static const fnActionHandler ACTION_HANDLERS[VMCALL_ACTION_COUNT] = 28 | { 29 | [VMCALL_ACTION_CHECK_PRESENCE] = actionCheckPresence, 30 | [VMCALL_ACTION_RUN_AS_ROOT] = actionRunAsRoot, 31 | [VMCALL_ACTION_SHADOW_IN_PROCESS] = actionShadowInProcess, 32 | }; 33 | 34 | /******************** Public Code ********************/ 35 | 36 | BOOLEAN VMCALL_handle(PVMM_DATA lpData) 37 | { 38 | UNREFERENCED_PARAMETER(lpData); 39 | 40 | BOOLEAN result = FALSE; 41 | 42 | /* This is used when the guest wants to send something to the host. 43 | * A secret key is used to protect malicious (or unknown) actors from 44 | * trying to do a hyper call. 45 | * 46 | * RCX = Secret Key 47 | * RDX = VMCALL Command Buffer 48 | */ 49 | if (VMCALL_KEY == lpData->guestContext.Rcx) 50 | { 51 | /* Attempt to read the guest command buffer. */ 52 | CR3 guestCR3; 53 | __vmx_vmread(VMCS_GUEST_CR3, &guestCR3.Flags); 54 | 55 | /* Treat RDX of the guest as the pointer for the command. */ 56 | VMCALL_COMMAND readCommand = { 0 }; 57 | NTSTATUS status = MemManage_readVirtualAddress(&lpData->mmContext, guestCR3, lpData->guestContext.Rdx, 58 | &readCommand, sizeof(readCommand)); 59 | 60 | if (NT_SUCCESS(status)) 61 | { 62 | /* Call the specific action handler for the command and put the result NTSTATUS into RAX. */ 63 | if (readCommand.action < VMCALL_ACTION_COUNT) 64 | { 65 | lpData->guestContext.Rax = ACTION_HANDLERS[readCommand.action](lpData, guestCR3, 66 | (GUEST_VIRTUAL_ADDRESS)readCommand.buffer, 67 | readCommand.bufferSize); 68 | } 69 | else 70 | { 71 | lpData->guestContext.Rax = (ULONG64)STATUS_INVALID_PARAMETER; 72 | } 73 | 74 | result = TRUE; 75 | } 76 | } 77 | 78 | return result; 79 | } 80 | 81 | /******************** Module Code ********************/ 82 | 83 | static NTSTATUS actionCheckPresence(PVMM_DATA lpData, CR3 guestCR3, GUEST_VIRTUAL_ADDRESS buffer, SIZE_T bufferSize) 84 | { 85 | UNREFERENCED_PARAMETER(lpData); 86 | UNREFERENCED_PARAMETER(guestCR3); 87 | UNREFERENCED_PARAMETER(buffer); 88 | UNREFERENCED_PARAMETER(bufferSize); 89 | 90 | return STATUS_SUCCESS; 91 | } 92 | 93 | static NTSTATUS actionRunAsRoot(PVMM_DATA lpData, CR3 guestCR3, GUEST_VIRTUAL_ADDRESS buffer, SIZE_T bufferSize) 94 | { 95 | NTSTATUS status; 96 | 97 | if ((0 != buffer) && (sizeof(VM_PARAM_RUN_AS_ROOT) == bufferSize)) 98 | { 99 | VM_PARAM_RUN_AS_ROOT params = { 0 }; 100 | 101 | status = MemManage_readVirtualAddress(&lpData->mmContext, guestCR3, buffer, ¶ms, sizeof(params)); 102 | if (NT_SUCCESS(status)) 103 | { 104 | /* Call the specified function (we will currently be in VMX ROOT). */ 105 | if (NULL != params.callback) 106 | { 107 | status = params.callback(lpData, params.parameter); 108 | } 109 | else 110 | { 111 | status = STATUS_INVALID_PARAMETER; 112 | } 113 | } 114 | } 115 | else 116 | { 117 | status = STATUS_INVALID_PARAMETER; 118 | } 119 | 120 | return status; 121 | } 122 | 123 | static NTSTATUS actionShadowInProcess(PVMM_DATA lpData, CR3 guestCR3, GUEST_VIRTUAL_ADDRESS buffer, SIZE_T bufferSize) 124 | { 125 | NTSTATUS status; 126 | 127 | if ((0 != buffer) && (sizeof(VM_PARAM_SHADOW_PROC) == bufferSize)) 128 | { 129 | VM_PARAM_SHADOW_PROC params = { 0 }; 130 | 131 | status = MemManage_readVirtualAddress(&lpData->mmContext, guestCR3, buffer, ¶ms, sizeof(params)); 132 | if (NT_SUCCESS(status)) 133 | { 134 | 135 | /* Get the PEPROCESS of the target process. */ 136 | PEPROCESS targetProcess; 137 | if (0 != params.procID) 138 | { 139 | status = PsLookupProcessByProcessId((HANDLE)params.procID, &targetProcess); 140 | if (NT_SUCCESS(status)) 141 | { 142 | /* Tell the VMShadow module to hide the executable page at the specified 143 | * address, for the target process only. */ 144 | status = VMShadow_hideExecInProcess(lpData, 145 | targetProcess, 146 | params.userTargetVA, 147 | params.kernelExecPageVA); 148 | 149 | ObDereferenceObject(targetProcess); 150 | } 151 | } 152 | else 153 | { 154 | status = STATUS_INVALID_PARAMETER; 155 | } 156 | } 157 | } 158 | else 159 | { 160 | status = STATUS_INVALID_PARAMETER; 161 | } 162 | 163 | return status; 164 | } 165 | -------------------------------------------------------------------------------- /Hypervisor/VMCALL.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "VMM.h" 4 | 5 | /******************** Public Typedefs ********************/ 6 | 7 | /******************** Public Constants ********************/ 8 | 9 | /******************** Public Variables ********************/ 10 | 11 | /******************** Public Prototypes ********************/ 12 | 13 | BOOLEAN VMCALL_handle(PVMM_DATA lpData); -------------------------------------------------------------------------------- /Hypervisor/VMCALL_Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" 5 | { 6 | #endif 7 | 8 | /******************** Public Typedefs ********************/ 9 | typedef NTSTATUS (*fnRootCallback)(PVOID hvParameter, PVOID userParameter); 10 | 11 | typedef enum 12 | { 13 | VMCALL_ACTION_CHECK_PRESENCE = 0, 14 | VMCALL_ACTION_RUN_AS_ROOT, 15 | VMCALL_ACTION_SHADOW_IN_PROCESS, 16 | VMCALL_ACTION_COUNT 17 | } VMCALL_ACTION; 18 | 19 | typedef struct _VMCALL_COMMAND 20 | { 21 | VMCALL_ACTION action; 22 | PVOID buffer; 23 | SIZE_T bufferSize; 24 | } VMCALL_COMMAND, *PVMCALL_COMMAND; 25 | 26 | typedef struct _VM_PARAM_RUN_AS_ROOT 27 | { 28 | fnRootCallback callback; 29 | PVOID parameter; 30 | } VM_PARAM_RUN_AS_ROOT, *PVM_PARAM_RUN_AS_ROOT; 31 | 32 | typedef struct _VM_PARAM_SHADOW_PROC 33 | { 34 | DWORD32 procID; /* IN */ 35 | PUINT8 userTargetVA; /* IN */ 36 | PUINT8 kernelExecPageVA; /* IN */ 37 | } VM_PARAM_SHADOW_PROC, *PVM_PARAM_SHADOW_PROC; 38 | 39 | /******************** Public Constants ********************/ 40 | 41 | #define VMCALL_KEY ((UINT64)0xDEADDEAD) 42 | 43 | /******************** Public Variables ********************/ 44 | 45 | /******************** Public Prototypes ********************/ 46 | 47 | /* Calling convention of the function for calling the host. */ 48 | NTSTATUS VMCALL_actionHost(UINT64 key, PVMCALL_COMMAND command); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | -------------------------------------------------------------------------------- /Hypervisor/VMCALL_Stub.asm: -------------------------------------------------------------------------------- 1 | .code 2 | 3 | VMCALL_actionHost PROC 4 | 5 | ; RCX should hold the key (hopefully convention not broken) 6 | ; RDX should hold a pointer to VMCALL_COMMAND parameters (same as above) 7 | ; Need to fix it so they are ensured. 8 | vmcall 9 | 10 | ; RAX will contain the result NTSTATUS in the end, set by the host. 11 | 12 | ret 13 | 14 | VMCALL_actionHost ENDP 15 | 16 | end -------------------------------------------------------------------------------- /Hypervisor/VMHook.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "VMHook.h" 4 | #include "VMShadow.h" 5 | #include "BEA\BeaEngine.h" 6 | #include "Debug.h" 7 | 8 | /******************** External API ********************/ 9 | 10 | 11 | /******************** Module Typedefs ********************/ 12 | 13 | typedef struct _PENDING_HOOK 14 | { 15 | PVOID target; 16 | PVOID hook; 17 | PVOID* original; 18 | } PENDING_HOOK, *PPENDING_HOOK; 19 | 20 | /******************** Module Constants ********************/ 21 | #define MAX_HOOKS 50 22 | 23 | /******************** Module Variables ********************/ 24 | 25 | static SIZE_T pendingHookCount = 0; 26 | static PENDING_HOOK pendingHooks[MAX_HOOKS] = { 0 }; 27 | 28 | /******************** Module Prototypes ********************/ 29 | static NTSTATUS hookAFunction(PEPT_CONFIG eptConfig, PVOID targetFunction, PVOID hookFunction, PVOID* origFunction); 30 | static NTSTATUS createTrampoline(PUINT8 hookPage, PVOID targetFunction, PVOID hookFunction, PVOID* origFunction); 31 | static void generateAbsoluteJump(PUINT8 targetBuffer, SIZE_T targetAddress); 32 | 33 | /******************** Public Code ********************/ 34 | 35 | NTSTATUS VMHook_init(PEPT_CONFIG eptConfig) 36 | { 37 | /* This is called when the hypervisor IS initialised. Hooks can be pending before. 38 | * This is called once per logical-processor. */ 39 | 40 | /* Assume successful until failure. */ 41 | NTSTATUS status = STATUS_SUCCESS; 42 | 43 | /* Iterate through all the pending hooks and initialise them. */ 44 | for (SIZE_T i = 0; i < pendingHookCount; i++) 45 | { 46 | PPENDING_HOOK current = &pendingHooks[i]; 47 | 48 | status = hookAFunction(eptConfig, current->target, current->hook, current->original); 49 | 50 | if (FALSE == NT_SUCCESS(status)) 51 | { 52 | /* Don't continue with hooking, just fail gracefully. */ 53 | break; 54 | } 55 | } 56 | 57 | return status; 58 | } 59 | 60 | void VMHook_queueHook(PVOID targetFunction, PVOID hookFunction, PVOID* origFunction) 61 | { 62 | /* As some hooks are may be called before the hypervisor is initialised, we provide the 63 | * ability to create a queue of hooks, this means we can do the hooks without needing to be 64 | * running at the hypervisor's DPC IRQL. 65 | * 66 | * All pending hooks are hooked at hypervisor initialisation. */ 67 | 68 | /* NOTE: At the moment this only works with virtual addresses in the kernel as they are mapped 69 | * to every logical processor. We will need to use IoAllocateMdl if we want to hook usermode 70 | * addresses in the future. */ 71 | 72 | PPENDING_HOOK newHook = &pendingHooks[pendingHookCount]; 73 | 74 | newHook->target = targetFunction; 75 | newHook->hook = hookFunction; 76 | newHook->original = origFunction; 77 | 78 | pendingHookCount++; 79 | } 80 | 81 | /******************** Module Code ********************/ 82 | 83 | static NTSTATUS hookAFunction(PEPT_CONFIG eptConfig, PVOID targetFunction, PVOID hookFunction, PVOID* origFunction) 84 | { 85 | NTSTATUS status; 86 | 87 | /* Create the page that will be used as the shadow. */ 88 | PVOID alignedTarget = PAGE_ALIGN(targetFunction); 89 | 90 | /* Create a buffer that will contain the hook 91 | * First copy the original bytes of the page into it. */ 92 | PUINT8 hookPage = (PUINT8)ExAllocatePool(NonPagedPoolNx, PAGE_SIZE); 93 | 94 | if (NULL != hookPage) 95 | { 96 | /* Copy the original bytes into the hook page. */ 97 | RtlCopyMemory(hookPage, alignedTarget, PAGE_SIZE); 98 | 99 | /* Create the trampoline in the hook page. */ 100 | status = createTrampoline(hookPage, targetFunction, hookFunction, origFunction); 101 | 102 | if (NT_SUCCESS(status)) 103 | { 104 | PHYSICAL_ADDRESS targetPA = MmGetPhysicalAddress(alignedTarget); 105 | status = VMShadow_hidePageGlobally(eptConfig, targetPA, hookPage, FALSE); 106 | } 107 | 108 | /* Free the memory as no longer needed as VMShadow copies the hooked page. */ 109 | ExFreePool(hookPage); 110 | } 111 | else 112 | { 113 | status = STATUS_NO_MEMORY; 114 | } 115 | 116 | 117 | return status; 118 | } 119 | 120 | static NTSTATUS createTrampoline(PUINT8 hookPage, PVOID targetFunction, PVOID hookFunction, PVOID* origFunction) 121 | { 122 | static const Int32 GP_CONTROL_TRANSFER = (GENERAL_PURPOSE_INSTRUCTION | CONTROL_TRANSFER); 123 | static const SIZE_T BYTES_FOR_ABSOLUTE_JUMP = 16; 124 | 125 | NTSTATUS status = STATUS_SUCCESS; 126 | 127 | /* Calculate the function's offset into the page. */ 128 | SIZE_T offsetIntoPage = ADDRMASK_EPT_PML1_OFFSET((UINT64)targetFunction); 129 | 130 | /* Allocate some memory for the trampoline. */ 131 | PUINT8 trampoline = ExAllocatePool(NonPagedPoolExecute, PAGE_SIZE); 132 | 133 | if (NULL != trampoline) 134 | { 135 | /* Zero the trampoline buffer. */ 136 | RtlZeroMemory(trampoline, PAGE_SIZE); 137 | 138 | /* Determine the number of instructions necessary to overwrite to fit the hook using a disassembler. */ 139 | SIZE_T sizeOfTrampoline = 0; 140 | SIZE_T sizeOfDisassembled = 0; 141 | 142 | /* Get the size of instructions we will overwrite. */ 143 | DISASM disInfo = { 0 }; 144 | disInfo.EIP = (UIntPtr)targetFunction; 145 | 146 | while (sizeOfTrampoline < BYTES_FOR_ABSOLUTE_JUMP) 147 | { 148 | SIZE_T instrLength = Disasm(&disInfo); 149 | 150 | /* Check to see if the disassembled instruction uses relative control transfers 151 | * CALL, JXX etc. If it does we need to patch the address. */ 152 | if (disInfo.Error != UNKNOWN_OPCODE) 153 | { 154 | if (((disInfo.Instruction.Category & GP_CONTROL_TRANSFER) == GP_CONTROL_TRANSFER) && 155 | (CallType == disInfo.Instruction.BranchType)) 156 | { 157 | /* At the moment we will only fix CALL's, we trash the R11 register doing so too. 158 | * hopefully as it's rare R11 is used in first few bytes this is acceptable. */ 159 | 160 | /* MOVABS R11, 0x0000000000000000 161 | * CALL R11 */ 162 | UINT8 shimAbsCall[12] = { 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xD0 }; 163 | *((UINT64*)&shimAbsCall[2]) = disInfo.Instruction.AddrValue; 164 | 165 | /* Copy the shim. */ 166 | RtlCopyMemory(trampoline + sizeOfTrampoline, 167 | shimAbsCall, 168 | sizeof(shimAbsCall)); 169 | 170 | /* Adjust the size of hooked instruction + EIP. */ 171 | sizeOfTrampoline += sizeof(shimAbsCall); 172 | sizeOfDisassembled += instrLength; /* Not the same size as the shim. */ 173 | disInfo.EIP += instrLength; 174 | } 175 | else 176 | { 177 | /* We don't need to patch, just copy. */ 178 | RtlCopyMemory(trampoline + sizeOfTrampoline, 179 | ((PUINT8)targetFunction) + sizeOfTrampoline, 180 | instrLength); 181 | 182 | sizeOfTrampoline += instrLength; 183 | sizeOfDisassembled += instrLength; 184 | disInfo.EIP += instrLength; 185 | } 186 | } 187 | else 188 | { 189 | /* No tidy way of returning here really. */ 190 | status = STATUS_UNSUCCESSFUL; 191 | break; 192 | } 193 | } 194 | 195 | /* Ensure the hook isn't over two pages. */ 196 | if ((offsetIntoPage + sizeOfTrampoline) < (PAGE_SIZE - 1)) 197 | { 198 | /* Add the absolute jump to the trampoline to return back to actual code. */ 199 | generateAbsoluteJump(&trampoline[sizeOfTrampoline], (SIZE_T)targetFunction + sizeOfDisassembled); 200 | 201 | /* Add the absolute jump to detour the target to the hook code. */ 202 | generateAbsoluteJump(&hookPage[offsetIntoPage], (SIZE_T)hookFunction); 203 | 204 | /* Store the hook function, so it can be used. */ 205 | *origFunction = trampoline; 206 | 207 | status = STATUS_SUCCESS; 208 | } 209 | else 210 | { 211 | status = STATUS_NOT_CAPABLE; 212 | } 213 | } 214 | else 215 | { 216 | status = STATUS_NO_MEMORY; 217 | } 218 | 219 | if (FALSE == NT_SUCCESS(status)) 220 | { 221 | ExFreePool(trampoline); 222 | } 223 | 224 | return status; 225 | } 226 | 227 | static void generateAbsoluteJump(PUINT8 targetBuffer, SIZE_T targetAddress) 228 | { 229 | /* PUSH RAX */ 230 | targetBuffer[0] = 0x50; 231 | 232 | /* MOV RAX, targetAddress */ 233 | targetBuffer[1] = 0x48; 234 | targetBuffer[2] = 0xB8; 235 | 236 | *((PSIZE_T)&targetBuffer[3]) = targetAddress; 237 | 238 | /* XCHG [RSP], RAX */ 239 | targetBuffer[11] = 0x48; 240 | targetBuffer[12] = 0x87; 241 | targetBuffer[13] = 0x04; 242 | targetBuffer[14] = 0x24; 243 | 244 | /* RET */ 245 | targetBuffer[15] = 0xC3; 246 | } 247 | -------------------------------------------------------------------------------- /Hypervisor/VMHook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "EPT.h" 4 | 5 | /******************** Public Defines ********************/ 6 | 7 | /******************** Public Typedefs ********************/ 8 | 9 | /******************** Public Constants ********************/ 10 | 11 | /******************** Public Variables ********************/ 12 | 13 | /******************** Public Prototypes ********************/ 14 | NTSTATUS VMHook_init(PEPT_CONFIG eptConfig); 15 | void VMHook_queueHook(PVOID targetFunction, PVOID hookFunction, PVOID* origFunction); 16 | -------------------------------------------------------------------------------- /Hypervisor/VMM.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "VMM.h" 3 | #include "VMHook.h" 4 | #include "Intrinsics.h" 5 | #include "MSR.h" 6 | #include "GDT.h" 7 | #include "MemManage.h" 8 | #include "HandlerShim.h" 9 | #include "Debug.h" 10 | #include "ia32.h" 11 | 12 | /******************** External API ********************/ 13 | 14 | 15 | /******************** Module Typedefs ********************/ 16 | 17 | 18 | /******************** Module Constants ********************/ 19 | 20 | 21 | /******************** Module Variables ********************/ 22 | 23 | 24 | /******************** Module Prototypes ********************/ 25 | static NTSTATUS launchVMMOnProcessor(PVMM_DATA lpData); 26 | static NTSTATUS enterRootMode(PVMM_DATA lpData); 27 | static void setupVMCS(PVMM_DATA lpData); 28 | static NTSTATUS launchVMX(void); 29 | static void captureControlRegisters(PCONTROL_REGISTERS registers); 30 | 31 | /******************** Public Code ********************/ 32 | 33 | NTSTATUS VMM_init(PVMM_DATA lpData) 34 | { 35 | NTSTATUS status; 36 | 37 | /* Capture the control registers for the processor. */ 38 | captureControlRegisters(&lpData->controlRegisters); 39 | DEBUG_PRINT("VMM %d Control Registers:\r\n" 40 | "\tCR0: %I64X\r\n" 41 | "\tCR3: %I64X\r\n" 42 | "\tCR4: %I64X\r\n", 43 | lpData->processorIndex, lpData->controlRegisters.Cr0, 44 | lpData->controlRegisters.Cr3, lpData->controlRegisters.Cr4); 45 | 46 | /* Ensure the align check flag isn't set when here, as we are going to use this 47 | * to verify we enter VM guest successfully. */ 48 | __writeeflags(__readeflags() & ~EFLAGS_ALIGNMENT_CHECK_FLAG_FLAG); 49 | 50 | /* Capture the entire register state for the processor. 51 | * This is needed as once the VM is launched it will begin execution at 52 | * the defined instruction. This will be the on launch function, within here 53 | * we need to restore everything back to what it originally was (as we are hijacking and containerising the host). */ 54 | DEBUG_PRINT("Capturing host context.\r\n"); 55 | RtlCaptureContext(&lpData->hostContext); 56 | 57 | /* This is where the hypervisor will re-enter once launched, 58 | * We will use the AC flag within EFLAGS to indicate whether we are hypervised. 59 | * The first time it gets to this point in code we haven't ran the launch function thus the flag won't be set. 60 | * We check to ensure that this flag has been set to ensure we don't try launch it twice on the processor. */ 61 | if ((__readeflags() & EFLAGS_ALIGNMENT_CHECK_FLAG_FLAG) == 0) 62 | { 63 | status = launchVMMOnProcessor(lpData); 64 | } 65 | else 66 | { 67 | /* If we are here, we are hypervised, so return success. */ 68 | status = STATUS_SUCCESS; 69 | } 70 | 71 | return status; 72 | } 73 | 74 | /******************** Module Code ********************/ 75 | 76 | static NTSTATUS launchVMMOnProcessor(PVMM_DATA lpData) 77 | { 78 | NTSTATUS status; 79 | 80 | /* Read all of the MSRs that are related to VMX. */ 81 | MSR_readXMSR(lpData->msrData, sizeof(lpData->msrData) / sizeof(lpData->msrData[0]), IA32_VMX_BASIC); 82 | 83 | /* Store all of the MTRR-related MSRs. */ 84 | MTRR_readAll(lpData->mtrrTable); 85 | 86 | /* Initialise the memory manager module. */ 87 | status = MemManage_init(&lpData->mmContext, lpData->hostCR3); 88 | if (NT_SUCCESS(status)) 89 | { 90 | /* Initialise the MTF structure. */ 91 | MTF_initialise(&lpData->mtfConfig); 92 | 93 | /* Initialise EPT structure. */ 94 | EPT_initialise(&lpData->eptConfig, (const PMTRR_RANGE)&lpData->mtrrTable); 95 | 96 | /* Initialise all of the pending hooks. */ 97 | VMHook_init(&lpData->eptConfig); 98 | 99 | /* Attempt to enter VMX root. */ 100 | status = enterRootMode(lpData); 101 | 102 | if (NT_SUCCESS(status)) 103 | { 104 | /* Initialise VMCS for both the guest and host. */ 105 | setupVMCS(lpData); 106 | 107 | /* Launch hypervisor using VMX. */ 108 | status = launchVMX(); 109 | } 110 | } 111 | 112 | return status; 113 | } 114 | 115 | static NTSTATUS enterRootMode(PVMM_DATA lpData) 116 | { 117 | /* Instead of using magic numbers, calulate the indexes of the ID's we need. */ 118 | static const UINT32 INDEX_VMX_BASIC = IA32_VMX_BASIC - IA32_VMX_BASIC; 119 | static const UINT32 INDEX_VMX_EPT_VPID = IA32_VMX_EPT_VPID_CAP - IA32_VMX_BASIC; 120 | static const UINT32 INDEX_VMX_CR0_FIXED0 = IA32_VMX_CR0_FIXED0 - IA32_VMX_BASIC; 121 | static const UINT32 INDEX_VMX_CR0_FIXED1 = IA32_VMX_CR0_FIXED1 - IA32_VMX_BASIC; 122 | static const UINT32 INDEX_VMX_CR4_FIXED0 = IA32_VMX_CR4_FIXED0 - IA32_VMX_BASIC; 123 | static const UINT32 INDEX_VMX_CR4_FIXED1 = IA32_VMX_CR4_FIXED1 - IA32_VMX_BASIC; 124 | 125 | NTSTATUS status = STATUS_INTERNAL_ERROR; 126 | 127 | /* Ensure the VMCS can fit into a single page. */ 128 | if (IA32_VMX_BASIC_VMCS_SIZE_IN_BYTES(lpData->msrData[INDEX_VMX_BASIC].QuadPart) > PAGE_SIZE) 129 | { 130 | DEBUG_PRINT("VMCS does not fit inside a single page.\r\n"); 131 | return status; 132 | } 133 | 134 | /* Ensure the VMCS is supported in writeback memory. */ 135 | if (IA32_VMX_BASIC_MEMORY_TYPE(lpData->msrData[INDEX_VMX_BASIC].QuadPart) != MEMORY_TYPE_WRITE_BACK) 136 | { 137 | DEBUG_PRINT("VMCS is not supported in writeback memory.\r\n"); 138 | return status; 139 | } 140 | 141 | /* Ensure that true MSRs can be used for capabilities. */ 142 | if (IA32_VMX_BASIC_VMX_CONTROLS(lpData->msrData[INDEX_VMX_BASIC].QuadPart) == 0) 143 | { 144 | DEBUG_PRINT("MSRs cannot be used for capabilities.\r\n"); 145 | return status; 146 | } 147 | 148 | /* Ensure that EPT is available with the features needed. */ 149 | if ((lpData->msrData[INDEX_VMX_EPT_VPID].QuadPart & IA32_VMX_EPT_VPID_CAP_PAGE_WALK_LENGTH_4_FLAG) && 150 | (lpData->msrData[INDEX_VMX_EPT_VPID].QuadPart & IA32_VMX_EPT_VPID_CAP_MEMORY_TYPE_WRITE_BACK_FLAG) && 151 | (lpData->msrData[INDEX_VMX_EPT_VPID].QuadPart & IA32_VMX_EPT_VPID_CAP_PDE_2MB_PAGES_FLAG)) 152 | { 153 | lpData->eptControls = IA32_VMX_PROCBASED_CTLS2_ENABLE_EPT_FLAG | IA32_VMX_PROCBASED_CTLS2_ENABLE_VPID_FLAG; 154 | } 155 | 156 | /* Set the revision ID for the VMXON and VMCS regions to what was received in the MSR. */ 157 | lpData->vmxOn.RevisionId = lpData->msrData[INDEX_VMX_BASIC].LowPart; 158 | lpData->vmcs.RevisionId = lpData->msrData[INDEX_VMX_BASIC].LowPart; 159 | 160 | /* Update CR0 with the fixed 0 and fixed 1 requirments. */ 161 | lpData->controlRegisters.Cr0 &= lpData->msrData[INDEX_VMX_CR0_FIXED1].LowPart; 162 | lpData->controlRegisters.Cr0 |= lpData->msrData[INDEX_VMX_CR0_FIXED0].LowPart; 163 | 164 | /* Do the same with CR1. */ 165 | lpData->controlRegisters.Cr4 &= lpData->msrData[INDEX_VMX_CR4_FIXED1].LowPart; 166 | lpData->controlRegisters.Cr4 |= lpData->msrData[INDEX_VMX_CR4_FIXED0].LowPart; 167 | 168 | /* Update the hosts CR0 and CR4 with the new requirements. */ 169 | __writecr0(lpData->controlRegisters.Cr0); 170 | __writecr4(lpData->controlRegisters.Cr4); 171 | 172 | UINT64 vmxOnPhysicalAddress = MmGetPhysicalAddress(&lpData->vmxOn).QuadPart; 173 | 174 | /* Enable VMX root mode. */ 175 | if (__vmx_on(&vmxOnPhysicalAddress)) 176 | { 177 | DEBUG_PRINT("Unable to enter VMX root mode.\r\n"); 178 | return status; 179 | } 180 | 181 | UINT64 vmcsPhysicalAddress = MmGetPhysicalAddress(&lpData->vmcs).QuadPart; 182 | 183 | /* Clear the state of the VMCS, setting it to inactive. */ 184 | if (__vmx_vmclear(&vmcsPhysicalAddress)) 185 | { 186 | DEBUG_PRINT("Unable to clear the VMCS.\r\n"); 187 | __vmx_off(); 188 | return status; 189 | } 190 | 191 | /* Load the VMCS, setting its state to active. */ 192 | if (__vmx_vmptrld(&vmcsPhysicalAddress)) 193 | { 194 | DEBUG_PRINT("Unable to load the VMCS.\r\n"); 195 | __vmx_off(); 196 | return status; 197 | } 198 | 199 | /* If we have reached this point, we have successfully entered root mode. */ 200 | return STATUS_SUCCESS; 201 | } 202 | 203 | static void setupVMCS(PVMM_DATA lpData) 204 | { 205 | PCONTROL_REGISTERS controlRegisters = &lpData->controlRegisters; 206 | PCONTEXT context = &lpData->hostContext; 207 | 208 | DEBUG_PRINT("Initialising the VMCS for the logical processor.\r\n"); 209 | 210 | /* Set the link pointer to the required value for the 4KB VMCS. */ 211 | __vmx_vmwrite(VMCS_GUEST_VMCS_LINK_POINTER, ~0ULL); 212 | 213 | /* Enable EPT if it is supported. */ 214 | if (0 != lpData->eptControls) 215 | { 216 | /* Load the EPT root pointer. */ 217 | __vmx_vmwrite(VMCS_CTRL_EPT_POINTER, lpData->eptConfig.eptPointer.Flags); 218 | 219 | /* Set the VPID to one. */ 220 | __vmx_vmwrite(VMCS_CTRL_VIRTUAL_PROCESSOR_IDENTIFIER, 1); 221 | } 222 | 223 | /* Load the MSR bitmap. Unlike other bitmaps, not having a MSR bitmap will trap all of the MSRs, 224 | * So we allocate an empty MSR bitmap. */ 225 | UINT64 msrBitmapPhysicalAddress = MmGetPhysicalAddress(&lpData->msrBitmap).QuadPart; 226 | __vmx_vmwrite(VMCS_CTRL_MSR_BITMAP_ADDRESS, msrBitmapPhysicalAddress); 227 | 228 | /* 229 | * Enable support for RDTSCP and XSAVES/XRESTORES in the guest. Windows 10 230 | * makes use of both of these instructions if the CPU supports it. By using 231 | * MSR_adjustMSR, these options will be ignored if this processor does 232 | * not actually support the instructions to begin with. 233 | * 234 | * Also enable EPT support, for additional performance and ability to trap 235 | * memory access efficiently. 236 | */ 237 | UINT32 adjustedMSR = MSR_adjustMSR(lpData->msrData[11], 238 | IA32_VMX_PROCBASED_CTLS2_ENABLE_RDTSCP_FLAG | 239 | IA32_VMX_PROCBASED_CTLS2_ENABLE_INVPCID_FLAG | 240 | IA32_VMX_PROCBASED_CTLS2_ENABLE_XSAVES_FLAG | 241 | lpData->eptControls); 242 | 243 | __vmx_vmwrite(VMCS_CTRL_SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, adjustedMSR); 244 | 245 | /* 246 | * Enable no pin-based options ourselves, but there may be some required by 247 | * the processor. Use ShvUtilAdjustMsr to add those in. 248 | */ 249 | adjustedMSR = MSR_adjustMSR(lpData->msrData[13], 0); 250 | __vmx_vmwrite(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, adjustedMSR); 251 | 252 | /* 253 | * In order for our choice of supporting RDTSCP and XSAVE/RESTORES above to 254 | * actually mean something, we have to request secondary controls. We also 255 | * want to activate the MSR bitmap in order to keep them from being caught. 256 | */ 257 | adjustedMSR = MSR_adjustMSR(lpData->msrData[14], 258 | IA32_VMX_PROCBASED_CTLS_USE_MSR_BITMAPS_FLAG | 259 | IA32_VMX_PROCBASED_CTLS_ACTIVATE_SECONDARY_CONTROLS_FLAG | 260 | IA32_VMX_PROCBASED_CTLS_CR3_LOAD_EXITING_FLAG); 261 | 262 | __vmx_vmwrite(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, adjustedMSR); 263 | 264 | /* Make sure to enter us in x64 mode at all times.*/ 265 | adjustedMSR = MSR_adjustMSR(lpData->msrData[15], IA32_VMX_EXIT_CTLS_HOST_ADDRESS_SPACE_SIZE_FLAG); 266 | __vmx_vmwrite(VMCS_CTRL_VMEXIT_CONTROLS, adjustedMSR); 267 | 268 | /* As we exit back into the guest, make sure to exist in x64 mode as well. */ 269 | adjustedMSR = MSR_adjustMSR(lpData->msrData[16], IA32_VMX_ENTRY_CTLS_IA32E_MODE_GUEST_FLAG); 270 | __vmx_vmwrite(VMCS_CTRL_VMENTRY_CONTROLS, adjustedMSR); 271 | 272 | /* Load the CS Segment (Ring 0 Code) */ 273 | VMX_GDTENTRY64 vmxGdtEntry; 274 | 275 | GDT_convertGdtEntry(controlRegisters->Gdtr.Base, context->SegCs, &vmxGdtEntry); 276 | __vmx_vmwrite(VMCS_GUEST_CS_SELECTOR, vmxGdtEntry.Selector); 277 | __vmx_vmwrite(VMCS_GUEST_CS_LIMIT, vmxGdtEntry.Limit); 278 | __vmx_vmwrite(VMCS_GUEST_CS_ACCESS_RIGHTS, vmxGdtEntry.AccessRights); 279 | __vmx_vmwrite(VMCS_GUEST_CS_BASE, vmxGdtEntry.Base); 280 | __vmx_vmwrite(VMCS_HOST_CS_SELECTOR, context->SegCs & ~RPL_MASK); 281 | 282 | /* Load the SS Segment (Ring 0 Data) */ 283 | GDT_convertGdtEntry(controlRegisters->Gdtr.Base, context->SegSs, &vmxGdtEntry); 284 | __vmx_vmwrite(VMCS_GUEST_SS_SELECTOR, vmxGdtEntry.Selector); 285 | __vmx_vmwrite(VMCS_GUEST_SS_LIMIT, vmxGdtEntry.Limit); 286 | __vmx_vmwrite(VMCS_GUEST_SS_ACCESS_RIGHTS, vmxGdtEntry.AccessRights); 287 | __vmx_vmwrite(VMCS_GUEST_SS_BASE, vmxGdtEntry.Base); 288 | __vmx_vmwrite(VMCS_HOST_SS_SELECTOR, context->SegSs & ~RPL_MASK); 289 | 290 | /* Load the DS Segment (Ring 3 Data) */ 291 | GDT_convertGdtEntry(controlRegisters->Gdtr.Base, context->SegDs, &vmxGdtEntry); 292 | __vmx_vmwrite(VMCS_GUEST_DS_SELECTOR, vmxGdtEntry.Selector); 293 | __vmx_vmwrite(VMCS_GUEST_DS_LIMIT, vmxGdtEntry.Limit); 294 | __vmx_vmwrite(VMCS_GUEST_DS_ACCESS_RIGHTS, vmxGdtEntry.AccessRights); 295 | __vmx_vmwrite(VMCS_GUEST_DS_BASE, vmxGdtEntry.Base); 296 | __vmx_vmwrite(VMCS_HOST_DS_SELECTOR, context->SegDs & ~RPL_MASK); 297 | 298 | /* Load the ES Segment (Ring 3 Data) */ 299 | GDT_convertGdtEntry(controlRegisters->Gdtr.Base, context->SegEs, &vmxGdtEntry); 300 | __vmx_vmwrite(VMCS_GUEST_ES_SELECTOR, vmxGdtEntry.Selector); 301 | __vmx_vmwrite(VMCS_GUEST_ES_LIMIT, vmxGdtEntry.Limit); 302 | __vmx_vmwrite(VMCS_GUEST_ES_ACCESS_RIGHTS, vmxGdtEntry.AccessRights); 303 | __vmx_vmwrite(VMCS_GUEST_ES_BASE, vmxGdtEntry.Base); 304 | __vmx_vmwrite(VMCS_HOST_ES_SELECTOR, context->SegEs & ~RPL_MASK); 305 | 306 | /* Load the FS Segment (Ring 3 Compatibility-Mode TEB) */ 307 | GDT_convertGdtEntry(controlRegisters->Gdtr.Base, context->SegFs, &vmxGdtEntry); 308 | __vmx_vmwrite(VMCS_GUEST_FS_SELECTOR, vmxGdtEntry.Selector); 309 | __vmx_vmwrite(VMCS_GUEST_FS_LIMIT, vmxGdtEntry.Limit); 310 | __vmx_vmwrite(VMCS_GUEST_FS_ACCESS_RIGHTS, vmxGdtEntry.AccessRights); 311 | __vmx_vmwrite(VMCS_GUEST_FS_BASE, vmxGdtEntry.Base); 312 | __vmx_vmwrite(VMCS_HOST_FS_BASE, vmxGdtEntry.Base); 313 | __vmx_vmwrite(VMCS_HOST_FS_SELECTOR, context->SegFs & ~RPL_MASK); 314 | 315 | /* Load the GS Segment (Ring 3 Data if in Compatibility-Mode, MSR-based in Long Mode) */ 316 | GDT_convertGdtEntry(controlRegisters->Gdtr.Base, context->SegGs, &vmxGdtEntry); 317 | __vmx_vmwrite(VMCS_GUEST_GS_SELECTOR, vmxGdtEntry.Selector); 318 | __vmx_vmwrite(VMCS_GUEST_GS_LIMIT, vmxGdtEntry.Limit); 319 | __vmx_vmwrite(VMCS_GUEST_GS_ACCESS_RIGHTS, vmxGdtEntry.AccessRights); 320 | __vmx_vmwrite(VMCS_GUEST_GS_BASE, controlRegisters->MsrGsBase); 321 | __vmx_vmwrite(VMCS_HOST_GS_BASE, controlRegisters->MsrGsBase); 322 | __vmx_vmwrite(VMCS_HOST_GS_SELECTOR, context->SegGs & ~RPL_MASK); 323 | 324 | /* Load the Task Register (Ring 0 TSS) */ 325 | GDT_convertGdtEntry(controlRegisters->Gdtr.Base, controlRegisters->Tr, &vmxGdtEntry); 326 | __vmx_vmwrite(VMCS_GUEST_TR_SELECTOR, vmxGdtEntry.Selector); 327 | __vmx_vmwrite(VMCS_GUEST_TR_LIMIT, vmxGdtEntry.Limit); 328 | __vmx_vmwrite(VMCS_GUEST_TR_ACCESS_RIGHTS, vmxGdtEntry.AccessRights); 329 | __vmx_vmwrite(VMCS_GUEST_TR_BASE, vmxGdtEntry.Base); 330 | __vmx_vmwrite(VMCS_HOST_TR_BASE, vmxGdtEntry.Base); 331 | __vmx_vmwrite(VMCS_HOST_TR_SELECTOR, controlRegisters->Tr & ~RPL_MASK); 332 | 333 | /* Load the Local Descriptor Table (Ring 0 LDT on Redstone) */ 334 | GDT_convertGdtEntry(controlRegisters->Gdtr.Base, controlRegisters->Ldtr, &vmxGdtEntry); 335 | __vmx_vmwrite(VMCS_GUEST_LDTR_SELECTOR, vmxGdtEntry.Selector); 336 | __vmx_vmwrite(VMCS_GUEST_LDTR_LIMIT, vmxGdtEntry.Limit); 337 | __vmx_vmwrite(VMCS_GUEST_LDTR_ACCESS_RIGHTS, vmxGdtEntry.AccessRights); 338 | __vmx_vmwrite(VMCS_GUEST_LDTR_BASE, vmxGdtEntry.Base); 339 | 340 | /* Now load the GDT itself */ 341 | __vmx_vmwrite(VMCS_GUEST_GDTR_BASE, (uintptr_t)controlRegisters->Gdtr.Base); 342 | __vmx_vmwrite(VMCS_GUEST_GDTR_LIMIT, controlRegisters->Gdtr.Limit); 343 | __vmx_vmwrite(VMCS_HOST_GDTR_BASE, (uintptr_t)controlRegisters->Gdtr.Base); 344 | 345 | /* And then the IDT */ 346 | __vmx_vmwrite(VMCS_GUEST_IDTR_BASE, (uintptr_t)controlRegisters->Idtr.Base); 347 | __vmx_vmwrite(VMCS_GUEST_IDTR_LIMIT, controlRegisters->Idtr.Limit); 348 | __vmx_vmwrite(VMCS_HOST_IDTR_BASE, (uintptr_t)controlRegisters->Idtr.Base); 349 | 350 | /* Load CR0 */ 351 | __vmx_vmwrite(VMCS_CTRL_CR0_READ_SHADOW, controlRegisters->Cr0); 352 | __vmx_vmwrite(VMCS_HOST_CR0, controlRegisters->Cr0); 353 | __vmx_vmwrite(VMCS_GUEST_CR0, controlRegisters->Cr0); 354 | 355 | /* 356 | * Load CR3 -- do not use the current process' address space for the host, 357 | * because we may be executing in an arbitrary user-mode process right now 358 | * as part of the DPC interrupt we execute in. 359 | */ 360 | __vmx_vmwrite(VMCS_HOST_CR3, lpData->hostCR3.Flags); 361 | __vmx_vmwrite(VMCS_GUEST_CR3, controlRegisters->Cr3); 362 | __vmx_vmwrite(VMCS_CTRL_CR3_TARGET_COUNT, 0); 363 | 364 | /* Load CR4 */ 365 | __vmx_vmwrite(VMCS_HOST_CR4, controlRegisters->Cr4); 366 | __vmx_vmwrite(VMCS_GUEST_CR4, controlRegisters->Cr4); 367 | 368 | /* Set the guest/host mask and a shadow, this is used to hide 369 | * the fact that VMXE bit in CR4 is set. 370 | * 371 | * Setting a bit to 1 ensures that the bit is host owned, 372 | * meaning that the value will be read from the shadow register. 373 | * 374 | * Setting a bit to 0 ensures that the bit is guest owned, 375 | * meaning that the actual value will be read. 376 | */ 377 | static const UINT64 POSITION_VMXE_BIT = (1 << 13); 378 | static const UINT64 DISABLE_VMXE_BIT = ~((UINT64)(1 << 13)); 379 | 380 | __vmx_vmwrite(VMCS_CTRL_CR4_GUEST_HOST_MASK, POSITION_VMXE_BIT); 381 | __vmx_vmwrite(VMCS_CTRL_CR4_READ_SHADOW, controlRegisters->Cr4 & DISABLE_VMXE_BIT); 382 | 383 | /* Load debug MSR and register (DR7) */ 384 | __vmx_vmwrite(VMCS_GUEST_DEBUGCTL, controlRegisters->DebugControl); 385 | __vmx_vmwrite(VMCS_GUEST_DR7, controlRegisters->KernelDr7); 386 | 387 | /* 388 | * Finally, load the guest stack, instruction pointer, and rflags, which 389 | * corresponds exactly to the location where RtlCaptureContext will return 390 | * to inside of initialiseVirtualProcessor. 391 | */ 392 | __vmx_vmwrite(VMCS_GUEST_RSP, (uintptr_t)lpData->hypervisorStack + KERNEL_STACK_SIZE - sizeof(CONTEXT)); 393 | __vmx_vmwrite(VMCS_GUEST_RIP, (uintptr_t)HandlerShim_hostToGuest); 394 | __vmx_vmwrite(VMCS_GUEST_RFLAGS, context->EFlags); 395 | 396 | /* 397 | * Load the hypervisor entrypoint and stack. We give ourselves a standard 398 | * size kernel stack (24KB) and bias for the context structure that the 399 | * hypervisor entrypoint will push on the stack, avoiding the need for RSP 400 | * modifying instructions in the entrypoint. Note that the CONTEXT pointer 401 | * and thus the stack itself, must be 16-byte aligned for ABI compatibility 402 | * with AMD64 -- specifically, XMM operations will fail otherwise, such as 403 | * the ones that RtlCaptureContext will perform. 404 | */ 405 | C_ASSERT((KERNEL_STACK_SIZE - sizeof(CONTEXT)) % 16 == 0); 406 | __vmx_vmwrite(VMCS_HOST_RSP, (uintptr_t)lpData->hypervisorStack + KERNEL_STACK_SIZE - sizeof(CONTEXT)); 407 | __vmx_vmwrite(VMCS_HOST_RIP, (uintptr_t)HandlerShim_guestToHost); 408 | } 409 | 410 | static NTSTATUS launchVMX(void) 411 | { 412 | /* Launch the VMX, Good luck!!! */ 413 | __vmx_vmlaunch(); 414 | 415 | /* If we got here, the VMCS failed in some way, or the launch did not proceed as planned. 416 | * This should never get here in a normal situation. */ 417 | 418 | /* Breakpoint for debugging. */ 419 | #ifdef _DEBUG 420 | if (FALSE == KD_DEBUGGER_NOT_PRESENT) 421 | { 422 | DbgBreakPoint(); 423 | } 424 | #endif 425 | 426 | NTSTATUS result = FALSE; 427 | __vmx_vmread(VMCS_VM_INSTRUCTION_ERROR, &result); 428 | 429 | /* Exit VMX root mode. */ 430 | __vmx_off(); 431 | 432 | return result; 433 | } 434 | 435 | static void captureControlRegisters(PCONTROL_REGISTERS registers) 436 | { 437 | registers->Cr0 = __readcr0(); 438 | registers->Cr3 = __readcr3(); 439 | registers->Cr4 = __readcr4(); 440 | registers->DebugControl = __readmsr(IA32_DEBUGCTL); 441 | registers->MsrGsBase = __readmsr(IA32_GS_BASE); 442 | registers->KernelDr7 = __readdr(7); 443 | 444 | _sgdt(®isters->Gdtr.Limit); 445 | __sidt(®isters->Idtr.Limit); 446 | _str(®isters->Tr); 447 | _sldt(®isters->Ldtr); 448 | } -------------------------------------------------------------------------------- /Hypervisor/VMM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ia32.h" 3 | #include "MTRR.h" 4 | #include "EPT.h" 5 | #include "MTF.h" 6 | #include "MemManage.h" 7 | 8 | /******************** Public Typedefs ********************/ 9 | 10 | typedef struct _KDESCRIPTOR 11 | { 12 | UINT16 Pad[3]; 13 | UINT16 Limit; 14 | void* Base; 15 | } KDESCRIPTOR, *PKDESCRIPTOR; 16 | 17 | typedef struct _CONTROL_REGISTERS 18 | { 19 | UINT64 Cr0; 20 | UINT64 Cr3; 21 | UINT64 Cr4; 22 | UINT64 MsrGsBase; 23 | UINT16 Tr; 24 | UINT16 Ldtr; 25 | UINT64 DebugControl; 26 | UINT64 KernelDr7; 27 | KDESCRIPTOR Idtr; 28 | KDESCRIPTOR Gdtr; 29 | } CONTROL_REGISTERS, *PCONTROL_REGISTERS; 30 | 31 | /* Structure for holding information for a logical processor. */ 32 | typedef struct _VMM_DATA 33 | { 34 | /* Hypervisor stack must be at the beginning, this is as when we are hypervised, 35 | * we use the stack pointer to find the location of the LP_DATA structure. */ 36 | DECLSPEC_ALIGN(PAGE_SIZE) UINT8 hypervisorStack[KERNEL_STACK_SIZE]; 37 | 38 | DECLSPEC_ALIGN(PAGE_SIZE) EPT_CONFIG eptConfig; 39 | DECLSPEC_ALIGN(PAGE_SIZE) MTF_CONFIG mtfConfig; 40 | DECLSPEC_ALIGN(PAGE_SIZE) UINT8 msrBitmap[PAGE_SIZE]; 41 | DECLSPEC_ALIGN(PAGE_SIZE) VMCS vmxOn; 42 | DECLSPEC_ALIGN(PAGE_SIZE) VMCS vmcs; 43 | 44 | MM_CONTEXT mmContext; 45 | ULONG processorIndex; 46 | CR3 hostCR3; 47 | CONTROL_REGISTERS controlRegisters; 48 | CONTEXT hostContext; 49 | CONTEXT guestContext; 50 | LARGE_INTEGER msrData[17]; 51 | MTRR_RANGE mtrrTable[IA32_MTRR_VARIABLE_COUNT]; 52 | UINT32 eptControls; 53 | } VMM_DATA, *PVMM_DATA; 54 | 55 | /******************** Public Constants ********************/ 56 | 57 | /******************** Public Variables ********************/ 58 | 59 | /******************** Public Prototypes ********************/ 60 | 61 | NTSTATUS VMM_init(PVMM_DATA lpData); 62 | -------------------------------------------------------------------------------- /Hypervisor/VMShadow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "VMShadow.h" 4 | #include "MemManage.h" 5 | #include "GuestShim.h" 6 | #include "Intrinsics.h" 7 | #include "Debug.h" 8 | 9 | /******************** External API ********************/ 10 | 11 | 12 | /******************** Module Typedefs ********************/ 13 | 14 | /* Structure that will hold the shadow configuration for hiding executable pages. */ 15 | typedef struct _SHADOW_PAGE 16 | { 17 | DECLSPEC_ALIGN(PAGE_SIZE) UINT8 executePage[PAGE_SIZE]; 18 | 19 | /* Target process that will be hooked, NULL if global. */ 20 | CR3 targetCR3; 21 | 22 | /* Pointer to the PML1 entry that will be modified between RW and E. */ 23 | PEPT_PML1_ENTRY targetPML1E; 24 | 25 | /* Will store the flags of the specific PML1E's that will be 26 | * used for targetting shadowing. */ 27 | EPT_PML1_ENTRY originalPML1E; 28 | EPT_PML1_ENTRY activeExecTargetPML1E; 29 | EPT_PML1_ENTRY activeExecNotTargetPML1E; 30 | EPT_PML1_ENTRY activeRWPML1E; 31 | 32 | } SHADOW_PAGE, *PSHADOW_PAGE; 33 | 34 | /******************** Module Constants ********************/ 35 | 36 | 37 | /******************** Module Variables ********************/ 38 | 39 | 40 | /******************** Module Prototypes ********************/ 41 | static BOOLEAN handleShadowExec(PEPT_CONFIG eptConfig, PCONTEXT guestContext, PVOID userBuffer); 42 | static NTSTATUS hidePage(PEPT_CONFIG eptConfig, CR3 targetCR3, PHYSICAL_ADDRESS targetPA, PVOID executePage); 43 | static void setAllShadowsToReadWrite(PEPT_CONFIG eptConfig); 44 | 45 | /******************** Public Code ********************/ 46 | 47 | BOOLEAN VMShadow_handleMovCR(PVMM_DATA lpData) 48 | { 49 | /* Cast the exit qualification to its proper type. */ 50 | VMX_EXIT_QUALIFICATION_MOV_CR exitQualification; 51 | __vmx_vmread(VMCS_EXIT_QUALIFICATION, &exitQualification.Flags); 52 | 53 | /* Check if caused by a MOV CR3, REG */ 54 | if (VMX_EXIT_QUALIFICATION_REGISTER_CR3 == exitQualification.ControlRegister) 55 | { 56 | if (VMX_EXIT_QUALIFICATION_ACCESS_MOV_TO_CR == exitQualification.AccessType) 57 | { 58 | /* TODO: Make it so we only switch shadows to execute if new CR3 is 59 | * in one of the target pages. Will be quicker. */ 60 | 61 | /* MOV CR3, XXX has taken place, this indicates a new page table has been loaded. 62 | * We should iterate through all of the shadow pages and ensure RW pages are all 63 | * set instead of execute. That way if an execute happens on one, the target 64 | * will flip to the right execute entry later depending if it is a targetted process or not. */ 65 | setAllShadowsToReadWrite(&lpData->eptConfig); 66 | EPT_invalidateAndFlush(&lpData->eptConfig); 67 | 68 | /* Set the guest CR3 register, to the value of the general purpose register. */ 69 | ULONG64* registerList = &lpData->guestContext.Rax; 70 | 71 | ULONG64 registerValue; 72 | if (VMX_EXIT_QUALIFICATION_GENREG_RSP == exitQualification.GeneralPurposeRegister) 73 | { 74 | __vmx_vmread(VMCS_GUEST_RSP, ®isterValue); 75 | } 76 | else 77 | { 78 | registerValue = registerList[exitQualification.GeneralPurposeRegister]; 79 | } 80 | registerValue &= ~(1ULL << 63); 81 | 82 | __vmx_vmwrite(VMCS_GUEST_CR3, registerValue); 83 | 84 | /* Flush the TLB for the current logical processor. */ 85 | INVVPID_DESCRIPTOR descriptor = { 0 }; 86 | descriptor.Vpid = 1; 87 | __invvpid(InvvpidSingleContextRetainingGlobals, &descriptor); 88 | } 89 | } 90 | 91 | return TRUE; 92 | } 93 | 94 | NTSTATUS VMShadow_hidePageGlobally( 95 | PEPT_CONFIG eptConfig, 96 | PHYSICAL_ADDRESS targetPA, 97 | PUINT8 payloadPage, 98 | BOOLEAN hypervisorRunning 99 | ) 100 | { 101 | NTSTATUS status = STATUS_INVALID_PARAMETER; 102 | 103 | CR3 nullCR3 = { .Flags = 0 }; 104 | status = hidePage(eptConfig, nullCR3, targetPA, payloadPage); 105 | 106 | if (NT_SUCCESS(status) && (TRUE == hypervisorRunning)) 107 | { 108 | /* We have modified EPT layout, therefore flush and reload. */ 109 | EPT_invalidateAndFlush(eptConfig); 110 | } 111 | 112 | return status; 113 | } 114 | 115 | NTSTATUS VMShadow_hideExecInProcess( 116 | PVMM_DATA lpData, 117 | PEPROCESS targetProcess, 118 | PUINT8 targetVA, 119 | PUINT8 execVA 120 | ) 121 | { 122 | NTSTATUS status = STATUS_UNSUCCESSFUL; 123 | 124 | /* Get the process/page table we want to shadow the memory from. */ 125 | CR3 tableBase = MemManage_getPageTableBase(targetProcess); 126 | if (0 != tableBase.Flags) 127 | { 128 | /* Calculate the physical address of the target VA, 129 | * I know we could calculate this by reading the PTE here and 130 | * calculating, however we have a function for this already (at the expense of reading PTE again.. */ 131 | PHYSICAL_ADDRESS physTargetVA = { 0 }; 132 | physTargetVA.QuadPart = GuestShim_GuestUVAToHPA(&lpData->mmContext, tableBase, (GUEST_VIRTUAL_ADDRESS)targetVA); 133 | if (0 != physTargetVA.QuadPart) 134 | { 135 | /* Hide the executable page, for that page only. */ 136 | status = hidePage(&lpData->eptConfig, tableBase, physTargetVA, execVA); 137 | if (NT_SUCCESS(status)) 138 | { 139 | /* As we are attempting to hide exec memory in a process, 140 | * it's safe to say the hypervisor & EPT is already running. 141 | * Therefore we should invalidate the already existing EPT to flush 142 | * in the new config. */ 143 | EPT_invalidateAndFlush(&lpData->eptConfig); 144 | } 145 | } 146 | } 147 | else 148 | { 149 | /* Unable to get the table base. */ 150 | status = STATUS_INVALID_MEMBER; 151 | } 152 | 153 | return status; 154 | } 155 | 156 | /******************** Module Code ********************/ 157 | 158 | static BOOLEAN handleShadowExec(PEPT_CONFIG eptConfig, PCONTEXT guestContext, PVOID userBuffer) 159 | { 160 | UNREFERENCED_PARAMETER(eptConfig); 161 | UNREFERENCED_PARAMETER(guestContext); 162 | BOOLEAN result = FALSE; 163 | 164 | /* Cast the exit qualification to it's proper type, as an EPT violation. */ 165 | VMX_EXIT_QUALIFICATION_EPT_VIOLATION violationQual; 166 | __vmx_vmread(VMCS_EXIT_QUALIFICATION, &violationQual.Flags); 167 | 168 | /* We should only deal with shadow pages caused by translation. */ 169 | if (TRUE == violationQual.CausedByTranslation) 170 | { 171 | /* The user supplied parameter when the handler was registered supplies 172 | * the shadow page config, so we re-cast it to the desired type. */ 173 | PSHADOW_PAGE shadowPage = (PSHADOW_PAGE)userBuffer; 174 | if (NULL != shadowPage) 175 | { 176 | /* Check to see if the violation was from trying to execute a non-executable page. */ 177 | if ((FALSE == violationQual.EptExecutable) && (TRUE == violationQual.ExecuteAccess)) 178 | { 179 | /* Check to see if target process matches. */ 180 | CR3 guestCR3; 181 | __vmx_vmread(VMCS_GUEST_CR3, &guestCR3.Flags); 182 | 183 | if ((0 == shadowPage->targetCR3.Flags) || (guestCR3.AddressOfPageDirectory == shadowPage->targetCR3.AddressOfPageDirectory)) 184 | { 185 | /* Switch to the target execute page, this is if there it is a global shadow (no target CR3) 186 | * or the CR3 matches the target. */ 187 | shadowPage->targetPML1E->Flags = shadowPage->activeExecTargetPML1E.Flags; 188 | } 189 | else 190 | { 191 | /* Switch to the original execute page */ 192 | shadowPage->targetPML1E->Flags = shadowPage->activeExecNotTargetPML1E.Flags; 193 | } 194 | 195 | result = TRUE; 196 | } 197 | else if ((TRUE == violationQual.EptExecutable) && 198 | (violationQual.ReadAccess || violationQual.WriteAccess)) 199 | { 200 | /* If so, we update the PML1E so that the read/write page is visible to the guest. */ 201 | shadowPage->targetPML1E->Flags = shadowPage->activeRWPML1E.Flags; 202 | result = TRUE; 203 | } 204 | } 205 | } 206 | 207 | return result; 208 | } 209 | 210 | static NTSTATUS hidePage(PEPT_CONFIG eptConfig, CR3 targetCR3, PHYSICAL_ADDRESS targetPA, PVOID executePage) 211 | { 212 | NTSTATUS status; 213 | 214 | if (0ULL != targetPA.QuadPart) 215 | { 216 | PSHADOW_PAGE shadowConfig = (PSHADOW_PAGE)ExAllocatePool(NonPagedPoolNx, sizeof(SHADOW_PAGE)); 217 | 218 | if (NULL != shadowConfig) 219 | { 220 | /* As we have set up PDT to 2MB large pages we need to split this for performance. 221 | * The lowest we can split it to is the size of a page, 2MB = 512 * 4096 blocks. */ 222 | status = EPT_splitLargePage(eptConfig, targetPA); 223 | 224 | /* If the page split was successful or was already split, continue. */ 225 | if (NT_SUCCESS(status) || (STATUS_ALREADY_COMPLETE == status)) 226 | { 227 | /* Zero the newly allocated page config. */ 228 | RtlZeroMemory(shadowConfig, sizeof(SHADOW_PAGE)); 229 | 230 | /* Calculate the start and end of the physical address page we are hooking. */ 231 | PHYSICAL_ADDRESS physStart; 232 | PHYSICAL_ADDRESS physEnd; 233 | 234 | physStart.QuadPart = (LONGLONG)PAGE_ALIGN(targetPA.QuadPart); 235 | physEnd.QuadPart = physStart.QuadPart + PAGE_SIZE; 236 | 237 | /* Store the target process. */ 238 | shadowConfig->targetCR3 = targetCR3; 239 | 240 | /* Store a pointer to the PML1E we will be modifying. */ 241 | shadowConfig->targetPML1E = EPT_getPML1EFromAddress(eptConfig, targetPA); 242 | 243 | if (NULL != shadowConfig->targetPML1E) 244 | { 245 | /* Store a copy of the original */ 246 | shadowConfig->originalPML1E.Flags = shadowConfig->targetPML1E->Flags; 247 | 248 | /* Create the executable PML1E when it IS the target process. */ 249 | shadowConfig->activeExecTargetPML1E.Flags = shadowConfig->targetPML1E->Flags; 250 | shadowConfig->activeExecTargetPML1E.ReadAccess = 0; 251 | shadowConfig->activeExecTargetPML1E.WriteAccess = 0; 252 | shadowConfig->activeExecTargetPML1E.ExecuteAccess = 1; 253 | shadowConfig->activeExecTargetPML1E.PageFrameNumber = MmGetPhysicalAddress(&shadowConfig->executePage).QuadPart / PAGE_SIZE; 254 | 255 | /* Create the executable PML1E when the it is NOT the target process. 256 | * Here we want to keep original flags and guest physical address, but just disable read/write. */ 257 | shadowConfig->activeExecNotTargetPML1E.Flags = shadowConfig->targetPML1E->Flags; 258 | shadowConfig->activeExecNotTargetPML1E.ReadAccess = 0; 259 | shadowConfig->activeExecNotTargetPML1E.WriteAccess = 0; 260 | shadowConfig->activeExecNotTargetPML1E.ExecuteAccess = 1; 261 | 262 | /* Create the readwrite PML1E when ANY read write to the page takes place. 263 | * Here we want to keep original flags, however disable execute access. */ 264 | shadowConfig->activeRWPML1E.Flags = shadowConfig->targetPML1E->Flags; 265 | shadowConfig->activeRWPML1E.ReadAccess = 1; 266 | shadowConfig->activeRWPML1E.WriteAccess = 1; 267 | shadowConfig->activeRWPML1E.ExecuteAccess = 0; 268 | 269 | /* Set the actual PML1E to the value of the readWrite. */ 270 | shadowConfig->targetPML1E->Flags = shadowConfig->activeRWPML1E.Flags; 271 | 272 | /* Copy the fake bytes */ 273 | RtlCopyMemory(&shadowConfig->executePage[0], executePage, PAGE_SIZE); 274 | 275 | /* Calculate the range, that this handler will be for. */ 276 | 277 | 278 | PHYSICAL_RANGE handlerRange; 279 | handlerRange.start = physStart; 280 | handlerRange.end = physEnd; 281 | 282 | /* Add this shadow hook to the EPT shadow list. */ 283 | status = EPT_addViolationHandler(eptConfig, handlerRange, handleShadowExec, (PVOID)shadowConfig); 284 | } 285 | else 286 | { 287 | /* Unable to find the PML1E for the target page. */ 288 | ExFreePool(shadowConfig); 289 | status = STATUS_NO_SUCH_MEMBER; 290 | } 291 | } 292 | } 293 | else 294 | { 295 | status = STATUS_NO_MEMORY; 296 | } 297 | } 298 | else 299 | { 300 | status = STATUS_INVALID_ADDRESS; 301 | } 302 | 303 | return status; 304 | } 305 | 306 | static void setAllShadowsToReadWrite(PEPT_CONFIG eptConfig) 307 | { 308 | /* Go through the whole linked list of the EPT handlers for each of the 309 | * user addresses. If the handler matches the one we use for shadow exec, set it to read/write. */ 310 | for (PLIST_ENTRY currentEntry = eptConfig->handlerList.Flink; 311 | currentEntry != &eptConfig->handlerList; 312 | currentEntry = currentEntry->Flink) 313 | { 314 | PEPT_HANDLER eptHandler = CONTAINING_RECORD(currentEntry, EPT_HANDLER, listEntry); 315 | 316 | /* TODO: Not a great solution, maybe think of something more elegant, 317 | * VMShadow is currently coupled to EPT internal configs (which is shouldn't really have access to). */ 318 | if (eptHandler->callback == handleShadowExec) 319 | { 320 | PSHADOW_PAGE shadowPage = (PSHADOW_PAGE)eptHandler->userParameter; 321 | 322 | /* Only set hooks that are currently enabled. */ 323 | if (shadowPage->targetPML1E->Flags != shadowPage->originalPML1E.Flags) 324 | { 325 | shadowPage->targetPML1E->Flags = shadowPage->activeRWPML1E.Flags; 326 | } 327 | } 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /Hypervisor/VMShadow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "VMM.h" 4 | #include "EPT.h" 5 | 6 | /******************** Public Defines ********************/ 7 | 8 | /******************** Public Typedefs ********************/ 9 | 10 | /******************** Public Constants ********************/ 11 | 12 | /******************** Public Variables ********************/ 13 | 14 | /******************** Public Prototypes ********************/ 15 | 16 | BOOLEAN VMShadow_handleMovCR(PVMM_DATA lpData); 17 | 18 | NTSTATUS VMShadow_hidePageGlobally( 19 | PEPT_CONFIG eptConfig, 20 | PHYSICAL_ADDRESS targetPA, 21 | PUINT8 payloadPage, 22 | BOOLEAN hypervisorRunning 23 | ); 24 | 25 | NTSTATUS VMShadow_hideExecInProcess( 26 | PVMM_DATA lpData, 27 | PEPROCESS targetProcess, 28 | PUINT8 targetVA, 29 | PUINT8 execVA 30 | ); -------------------------------------------------------------------------------- /HypervisorBase.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Hypervisor", "Hypervisor\Hypervisor.vcxproj", "{19377FFF-F4FD-4142-A208-0E37BB0D8A27}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|ARM.ActiveCfg = Debug|ARM 21 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|ARM.Build.0 = Debug|ARM 22 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|ARM.Deploy.0 = Debug|ARM 23 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|ARM64.ActiveCfg = Debug|ARM64 24 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|ARM64.Build.0 = Debug|ARM64 25 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|ARM64.Deploy.0 = Debug|ARM64 26 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|x64.ActiveCfg = Debug|x64 27 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|x64.Build.0 = Debug|x64 28 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|x64.Deploy.0 = Debug|x64 29 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|x86.ActiveCfg = Debug|Win32 30 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|x86.Build.0 = Debug|Win32 31 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Debug|x86.Deploy.0 = Debug|Win32 32 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|ARM.ActiveCfg = Release|ARM 33 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|ARM.Build.0 = Release|ARM 34 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|ARM.Deploy.0 = Release|ARM 35 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|ARM64.ActiveCfg = Release|ARM64 36 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|ARM64.Build.0 = Release|ARM64 37 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|ARM64.Deploy.0 = Release|ARM64 38 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|x64.ActiveCfg = Release|x64 39 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|x64.Build.0 = Release|x64 40 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|x64.Deploy.0 = Release|x64 41 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|x86.ActiveCfg = Release|Win32 42 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|x86.Build.0 = Release|Win32 43 | {19377FFF-F4FD-4142-A208-0E37BB0D8A27}.Release|x86.Deploy.0 = Release|Win32 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HypervisorBase 2 | A library for intel VT-x hypervisor functionality supporting EPT shadowing. 3 | --------------------------------------------------------------------------------