Package rekall :: Package plugins :: Module hypervisors
[frames] | no frames]

Source Code for Module rekall.plugins.hypervisors

  1  """Implements scanners and plugins to find hypervisors in memory.""" 
  2   
  3  from itertools import groupby 
  4  import struct 
  5   
  6  from rekall import plugin 
  7  from rekall import obj 
  8  from rekall import scan 
  9  from rekall import session as session_module 
 10  from rekall.plugins.addrspaces import amd64 
 11  from rekall.plugins.addrspaces import intel 
 12  from rekall.plugins.overlays import basic 
 13  from rekall_lib import utils 
 14   
 15   
 16  KNOWN_REVISION_IDS = { 
 17      # Nested hypervisors 
 18      # VMware Workstation 10.X 
 19      0x01: "VMWARE_NESTED", 
 20      # KVM 
 21      0x11e57ed0: "KVM_NESTED", 
 22      # XEN 
 23      0xda0400: "XEN_NESTED", 
 24      # Intel VT-x microarchitectures. 
 25      0x0d: "PENRYN", 
 26      0x0e: "NEHALEM", 
 27      0x0f: "WESTMERE", 
 28      0x10: "SANDYBRIDGE", 
 29      0x12: "HASWELL", 
 30      } 
 31   
 32   
 33  # TODO: Find more abort codes. 
 34  KNOWN_ABORT_INDICATOR_CODES = { 
 35      '\x00\x00\x00\x00': "NO ABORT", 
 36      '\x05\x00\x00\x00': "MACHINE CHECK DURING VM EXIT", 
 37      '\x0d\x00\x00\x00': "TXT SHUTDOWN", 
 38      } 
 39   
 40   
 41  vmcs_overlay = { 
 42      'NEHALEM_VMCS' : [None, { 
 43          'IS_NESTED': lambda x: False, 
 44          }], 
 45      'SANDYBRIDGE_VMCS' : [None, { 
 46          'IS_NESTED': lambda x: False, 
 47          }], 
 48      'HASWELL_VMCS' : [None, { 
 49          'IS_NESTED': lambda x: False, 
 50          }], 
 51      'WESTMERE_VMCS' : [None, { 
 52          'IS_NESTED': lambda x: False, 
 53          }], 
 54      'PENRYN_VMCS' : [None, { 
 55          'IS_NESTED': lambda x: False, 
 56          }], 
 57      'VMWARE_NESTED_VMCS' : [None, { 
 58          'IS_NESTED': lambda x: True, 
 59          }], 
 60      'KVM_NESTED_VMCS' : [None, { 
 61          'IS_NESTED': lambda x: True, 
 62          }], 
 63      'XEN_NESTED_VMCS' : [None, { 
 64          'IS_NESTED': lambda x: True, 
 65          }], 
 66      } 
67 68 69 -class Error(Exception):
70 """Base exception."""
71
72 73 -class UnrelatedVmcsError(Error):
74 """The provided VMCS is unrelated to the VM."""
75
76 77 -class IncompatibleASError(Error):
78 """An attempt was done at comparing VMCS from different address spaces."""
79
80 81 -class InvalidVM(Error):
82 """The provided VM is invalid."""
83
84 85 -class VMCSProfile(basic.ProfileLP64):
86 """Profile to parse hypervisor control structures. 87 88 We use the basic profile for 64 bit Linux systems to get the expected width 89 for each data type. 90 """ 91 92 @classmethod
93 - def Initialize(cls, profile):
96
97 98 -class VMCSCheck(scan.ScannerCheck):
99 - def check(self, buffer_as, offset):
100 # CHECK 1: Verify that the VMX-Abort indicator has a known value. 101 # 102 # The VMX-Abort indicator field is always at offset 4 in the VMCS 103 # and is a 32-bit field. 104 # This field should be 0 unless the memory image was taken while a 105 # VMX-abort occurred, which is fairly unlikely. Also, if a VMX-abort 106 # occurs, only a set of values are supposed to be set. 107 if buffer_as.read(offset+4, 4) not in KNOWN_ABORT_INDICATOR_CODES: 108 return False 109 110 # Obtain the Revision ID 111 (revision_id,) = struct.unpack_from("<I", buffer_as.read(offset, 4)) 112 revision_id = revision_id & 0x7FFFFFFF 113 114 # Obtain a VMCS object based on the revision_id 115 platform = KNOWN_REVISION_IDS.get(revision_id) 116 if platform is None: 117 return False 118 119 try: 120 vmcs_obj = self.profile.Object("%s_VMCS" % platform, 121 offset=offset, 122 vm=buffer_as) 123 except (AttributeError, TypeError): 124 return False 125 126 # CHECK 2: Verify that the VMCS has the VMX flag enabled. 127 if not vmcs_obj.HOST_CR4 & 0x2000: 128 return False 129 130 # CHECK 3: Verify that VMCS_LINK_POINTER is 131 # 0xFFFFFFFFFFFFFFFF. 132 if vmcs_obj.VMCS_LINK_PTR_FULL != 0xFFFFFFFFFFFFFFFF: 133 return False 134 135 return True
136
137 138 -class VMCSScanner(scan.BaseScanner):
139 """Scans the memory attempting to find VMCS structures. 140 141 Uses the techniques discussed on "Hypervisor Memory Forensics" 142 (http://s3.eurecom.fr/docs/raid13_graziano.pdf) with slight changes 143 to identify VT-x hypervisors. 144 """ 145 146 overlap = 0 147 148 checks = [["VMCSCheck", {}]] 149
150 - def __init__(self, **kwargs):
151 super(VMCSScanner, self).__init__(**kwargs) 152 self.profile = self.session.LoadProfile("VMCS")
153
154 - def scan(self, offset=0, end=None, **_):
155 """Returns instances of VMCS objects found.""" 156 for offset in super(VMCSScanner, self).scan(offset=offset, end=end): 157 (revision_id,) = struct.unpack("<I", 158 self.address_space.read(offset, 4)) 159 revision_id = revision_id & 0x7FFFFFFF 160 vmcs_obj = self.profile.Object( 161 "%s_VMCS" % KNOWN_REVISION_IDS.get(revision_id), 162 offset=offset, vm=self.address_space) 163 164 yield vmcs_obj
165 166
167 - def skip(self, buffer_as, offset):
168 return 0x1000
169
170 171 -class VirtualMachine(object):
172 """Represents a virtual machine. 173 174 A virtual machine is made of VMCS. In Intel processors, each CPU that runs 175 a VM will have its own VMCS. 176 """ 177
178 - def __init__(self, host_rip=None, ept=None, parent=None, name=None, 179 session=None):
180 self.ept = long(ept) 181 self.host_rip = long(host_rip) 182 self.parent = parent 183 self.name = name 184 self.base_session = session 185 self.vmcss = set() 186 # Dictionary where the key is a VMCS object and the value 187 # represents whether the VMCS is valid, or not. 188 self.vmcs_validation = dict() 189 self.virtual_machines = set()
190 191 @utils.safe_property
192 - def is_valid(self):
193 """A VM is valid if at least one of its VMCS is valid.""" 194 if any([self.vmcs_validation.get(vmcs, False) for vmcs in self.vmcss]): 195 return True 196 return False
197 198 @utils.safe_property
199 - def is_nested(self):
200 """A VM is nested if it has a parent or all its VMCS are nested.""" 201 return self.parent != None
202 203 @utils.safe_property
204 - def hostname(self):
205 try: 206 session = self.GetSession() 207 return session.plugins.hostname().get_hostname() 208 except AttributeError: 209 return obj.NoneObject() 210 except InvalidVM: 211 return obj.NoneObject("**INVALID VM**")
212 213 @utils.safe_property
214 - def num_cores(self):
215 """The number of virtual cores of this VM.""" 216 valid_vmcss = filter(self.is_valid_vmcs, self.vmcss) 217 # Count only unique VPIDs if the hypervisor uses them. 218 uniq_vpids = set([v.VPID for v in valid_vmcss]) 219 if len(uniq_vpids) != 1: 220 return len(uniq_vpids) 221 else: 222 return len(valid_vmcss)
223 224 @utils.safe_property
225 - def host_arch(self):
226 """The architecture of the host that started this VM.""" 227 all_host_as = set([self.get_vmcs_host_as_type(v) for v in self.vmcss 228 if self.is_valid_vmcs(v)]) 229 if len(all_host_as) == 1: 230 return all_host_as.pop() 231 return "???"
232 233 @utils.safe_property
234 - def guest_arch(self):
235 """The architecture of the guest OS of the VM.""" 236 all_guest_as = set([self.get_vmcs_guest_as_type(v) for v in self.vmcss 237 if self.is_valid_vmcs(v)]) 238 if len(all_guest_as) == 1: 239 return all_guest_as.pop() 240 return "???"
241 242 @utils.safe_property
243 - def ept_list(self):
244 """The list of EPT values needed to instantiate VM guest physical AS. 245 246 This is used in conjunction with the VTxPagedMemory AS. 247 """ 248 249 if isinstance(self.parent, VirtualMachine): 250 ept_list = self.parent.ept_list 251 ept_list.extend([self.ept]) 252 else: 253 ept_list = [self.ept] 254 return ept_list
255 256 @utils.safe_property
257 - def physical_address_space(self):
258 """The physical address space of this VM's guest.""" 259 260 if self.is_nested: 261 base_as = self.parent.physical_address_space 262 else: 263 base_as = self.base_session.physical_address_space 264 265 return amd64.VTxPagedMemory( 266 session=self.base_session, ept=self.ept_list, base=base_as)
267 268 269 @classmethod
270 - def get_vmcs_guest_as_type(cls, vmcs):
271 """Returns the address space type of the guest of a VMCS. 272 273 One of I386, I386+PAE, AMD64 or None. 274 """ 275 if not vmcs.GUEST_CR4 & (1 << 5): # PAE bit 276 # No PAE 277 return "I386" 278 elif not vmcs.ENTRY_CONTROLS & (1 << 9): # long mode bit 279 # PAE and no long mode = 32bit PAE 280 return "I386+PAE" 281 elif vmcs.ENTRY_CONTROLS & (1 << 9): # long mode bit 282 # Long mode AND PAE = IA-32e 283 return "AMD64" 284 else: 285 # We don't have an address space for other paging modes 286 return None
287 288 @classmethod
289 - def get_vmcs_host_as_type(cls, vmcs):
290 """Returns the address space type of the host of a VMCS. 291 292 One of I386, I386+PAE, AMD64 or None. 293 """ 294 if not vmcs.HOST_CR4 & (1 << 5): # PAE bit 295 # No PAE 296 return "I386" 297 elif not vmcs.EXIT_CONTROLS & (1 << 9): # long mode bit 298 # PAE and no long mode = 32bit PAE 299 return "I386+PAE" 300 elif vmcs.EXIT_CONTROLS & (1 << 9): # long mode bit 301 # Long mode AND PAE = IA-32e 302 return "AMD64" 303 else: 304 # We don't have an address space for other paging modes 305 return None
306 307 @classmethod
308 - def get_vmcs_host_address_space(cls, vmcs, base_as=None):
309 """Returns the address_space of the host of the VMCS.""" 310 return cls.get_vmcs_address_space(vmcs, host=True, base_as=base_as)
311 312 @classmethod
313 - def get_vmcs_guest_address_space(cls, vmcs, base_as=None):
314 """Returns the address_space of the guest of the VMCS.""" 315 return cls.get_vmcs_address_space(vmcs, host=False, base_as=base_as)
316 317 @classmethod
318 - def get_vmcs_address_space(cls, vmcs, host=True, base_as=None):
319 """Returns the address_space of the host or guest process of a VMCS.""" 320 address_space = None 321 base_as = base_as or vmcs.obj_vm 322 323 if host: 324 cr4 = vmcs.HOST_CR4 325 cr3 = vmcs.HOST_CR3 326 controls = vmcs.EXIT_CONTROLS 327 else: 328 cr4 = vmcs.GUEST_CR4 329 cr3 = vmcs.GUEST_CR3 330 controls = vmcs.ENTRY_CONTROLS 331 332 if not cr4 & (1 << 5): # PAE bit 333 # No PAE 334 address_space = intel.IA32PagedMemory(dtb=cr3, base=base_as) 335 336 elif not controls & (1 << 9): # long mode bit 337 # PAE and no long mode = 32bit PAE 338 address_space = intel.IA32PagedMemoryPae(dtb=cr3, base=base_as) 339 340 elif controls & (1 << 9): # long mode bit 341 # Long mode AND PAE = IA-32e 342 address_space = amd64.AMD64PagedMemory(dtb=cr3, base=base_as) 343 return address_space
344
345 - def add_vmcs(self, vmcs, validate=True):
346 """Add a VMCS to this virtual machine. 347 348 Raises: 349 UnrelatedVmcsError if the VMCS doesn't match the VM's HOST_RIP or EPT. 350 """ 351 if self.host_rip == None: 352 self.host_rip = long(vmcs.HOST_RIP) 353 354 if self.ept == None: 355 self.ept = long(vmcs.m("EPT_POINTER_FULL")) 356 357 if self.host_rip != vmcs.HOST_RIP: 358 raise UnrelatedVmcsError("VMCS HOST_RIP differ from the VM's") 359 360 if vmcs.m("EPT_POINTER_FULL") != self.ept: 361 raise UnrelatedVmcsError("VMCS EPT differs from the VM's") 362 363 if validate: 364 self.validate_vmcs(vmcs) 365 366 self.vmcss.add(vmcs)
367
368 - def set_parent(self, parent):
369 """Sets the parent of this VM and resets the validation cache.""" 370 if self.parent != parent: 371 self.parent = parent 372 self.vmcs_validation.clear()
373
374 - def unset_parent(self):
375 self.set_parent(None)
376
377 - def validate_vmcs(self, vmcs):
378 """Validates a VMCS and returns if it's valid in this VM's context. 379 380 A VMCS is valid if the page where it's mapped is found in the HOST_CR3 381 that it points to. The result of this validation is cached. Use 382 the _reset_validation_state method if you need to invalidate cache 383 entries. 384 385 A VMCS object will only validate properly if its defined in the context 386 of the address space of the physical AS of the parent of the VM. 387 """ 388 if vmcs in self.vmcs_validation: 389 return self.vmcs_validation.get(vmcs) 390 391 validated = False 392 393 394 # EPTP bits 11:6 are reserved and must be set to 0 395 # and the page_walk_length, bits 5:3, must be 3 (4 - 1) 396 # 397 # Ref: Intel(r) 64 and IA-32 Architectures Software Developer's Manual - 398 # System Programming Guide Volume 3B, 21-20, 21.6.11 399 400 page_walk_length = (vmcs.EPT_POINTER_FULL & 0b111000) >> 3 401 402 if (vmcs.EPT_POINTER_FULL & 0b111111000000 or 403 page_walk_length != 3): 404 self.vmcs_validation[vmcs] = validated 405 return validated 406 407 # If we are dealing with L1 VMCS, the address space to validate 408 # is the same as the VMCS. 409 try: 410 validation_as = self.get_vmcs_host_address_space(vmcs) 411 except TypeError: 412 return False 413 414 for run in validation_as.get_mappings(): 415 if self.base_session: 416 self.base_session.report_progress( 417 "Validating VMCS %08X @ %08X" % ( 418 vmcs.obj_offset, run.start)) 419 if (vmcs.obj_offset >= run.file_offset and 420 vmcs.obj_offset < run.file_offset + run.length): 421 validated = True 422 break 423 424 self.vmcs_validation[vmcs] = validated 425 return validated
426
427 - def is_valid_vmcs(self, vmcs):
428 """Returns whether the vmcs is valid or None if it wasn't validated. 429 430 Doesn't force validation. 431 """ 432 return self.vmcs_validation.get(vmcs)
433
434 - def GetSession(self):
435 """Returns a session valid for this VM.""" 436 437 if not self.is_valid: 438 raise InvalidVM() 439 440 session_override = { 441 "ept": self.ept_list, 442 "profile": None, 443 "session_name": u"VM %s" % u','.join( 444 [u'0x%X' % s for s in self.ept_list]), 445 } 446 447 return self.base_session.clone(**session_override)
448
449 - def RunPlugin(self, plugin_name, *args, **kwargs):
450 """Runs a plugin in the context of this virtual machine.""" 451 vm_sess = self.GetSession() 452 return vm_sess.RunPlugin(plugin_name, *args, **kwargs)
453
454 - def add_nested_vms(self, vm_list, validate_all=True):
455 """Tries to add the list of VMs as nested VMs of this one. 456 457 To validate nested VMs, we need to see if its identifying VMCS are 458 mapped in our physical AS and then try to validate them via HOST_CR3 459 in our context. 460 """ 461 _ = validate_all # TODO: Not currently implemented. 462 if not vm_list: 463 return 464 465 # If a VM is running under us, its VMCS has to be mapped in our 466 # physical address space. 467 phys_as = self.physical_address_space 468 for run in phys_as.get_mappings(): 469 for vm in vm_list: 470 if self.base_session: 471 self.base_session.report_progress( 472 u"Validating VM(%X) > VM(%X) @ %#X", 473 self.ept, vm.ept, run.file_offset) 474 475 for vmcs in vm.vmcss: 476 # Skip VMCS that we already validated 477 if vm.is_valid_vmcs(vmcs): 478 continue 479 480 # This step makes sure the VMCS is mapped in the 481 # Level1 guest physical memory (us). 482 if (run.file_offset <= vmcs.obj_offset and 483 vmcs.obj_offset < run.file_offset + run.length): 484 # Now we need to validate the VMCS under our context. 485 # For this we need to fix the VMCS AS and its offset. 486 vm.set_parent(self) 487 vmcs_stored_vm = vmcs.obj_vm 488 vmcs_stored_offset = vmcs.obj_offset 489 # Change the VMCS to be mapped in this VM's physical AS. 490 vmcs.obj_vm = self.physical_address_space 491 492 # The new offset is the run.start + the offset within 493 # the physical page. We need to do this when we're 494 # dealing with large/huge pages. 495 496 # Note that run.start here really means the physical 497 # address of the L1 guest. run.file_offset means the 498 # physical address of the base AS (the host). 499 vmcs.obj_offset = (run.start + 500 (run.file_offset - vmcs.obj_offset)) 501 if vm.validate_vmcs(vmcs): 502 # This steps validates that the VMCS is mapped in 503 # the Level1 guest hypervisor AS. 504 self.virtual_machines.update([vm]) 505 else: 506 # Reset the VMCS settings 507 vmcs.obj_vm = vmcs_stored_vm 508 vmcs.obj_offset = vmcs_stored_offset 509 510 # If any of the VMs was found to be nested, remove it from the vm_list 511 for vm in self.virtual_machines: 512 try: 513 vm_list.remove(vm) 514 except ValueError: 515 pass
516
517 - def _reset_validation_state(self, vmcs):
518 """Invalidates the vmcs validation cache entry for vmcs.""" 519 self.vmcs_validation.pop(vmcs, None)
520
521 - def __str__(self):
522 return "VirtualMachine(Hypervisor=%#X, EPT=%#X)" % ( 523 self.host_rip, self.ept)
524
525 526 -class VmScan(plugin.PhysicalASMixin, 527 plugin.TypedProfileCommand, plugin.Command):
528 """Scan the physical memory attempting to find hypervisors. 529 530 Once EPT values are found, you can use them to inspect virtual machines 531 with any of the rekall modules by using the --ept parameter and 532 specifying the guest virtual machine profile. 533 534 Supports the detection of the following virtualization techonlogies: 535 * Intel VT-X with EPT. Microarchitectures: 536 + Westmere 537 + Nehalem 538 + Sandybridge 539 + Ivy Bridge 540 + Haswell 541 542 * Intel VT-X without EPT (unsupported page translation in rekall). 543 + Penryn 544 545 For the specific processor models that support EPT, please check: 546 http://ark.intel.com/products/virtualizationtechnology. 547 """ 548 __name = "vmscan" 549 550 __args = [ 551 dict(name="quick", type="Boolean", 552 help="Perform quick VM detection."), 553 554 dict(name="no_nested", type="Boolean", 555 help="Don't do nested VM detection."), 556 557 dict(name="offset", type="IntParser", default=0, 558 help="Offset in the physical image to start the scan."), 559 560 dict(name="show_all", default=False, type="Boolean", 561 help="Also show VMs that failed validation."), 562 563 dict(name="image_is_guest", default=False, type="Boolean", 564 help="The image is for a guest VM, not the host."), 565 566 dict(name="no_validation", default=False, type="Boolean", 567 help="[DEBUG SETTING] Disable validation of VMs.") 568 ] 569
570 - def __init__(self, *args, **kwargs):
571 super(VmScan, self).__init__(*args, **kwargs) 572 if self.plugin_args.no_validate: 573 self.plugin_args.show_all = True
574
575 - def get_vms(self):
576 """Finds virtual machines in physical memory and returns a list of them. 577 """ 578 579 all_vmcs = VMCSScanner( 580 address_space=self.physical_address_space, 581 session=self.session, 582 profile=obj.NoneObject).scan( 583 offset=self.plugin_args.offset, 584 end=self.physical_address_space.end()) 585 586 host_vms = [] 587 nested_vms = [] 588 589 # == HOST VM validation 590 # Group the host VMCSs by (HOST_RIP, EPTP) and validate if requested. 591 # You could use (HOST_RIP, HOST_CR3), but VMWare 10.X uses a different 592 # HOST_CR3 per core. The EPTP, however, is the same and hardly any 593 # virtualization product would ever want to have different EPTP's per 594 # core because more page tables would have to be maintained for the 595 # same VM. 596 for host_rip, rip_vmcs_list in groupby( 597 sorted(all_vmcs, key=lambda x: long(x.HOST_RIP)), 598 lambda x: long(x.HOST_RIP)): 599 600 sorted_rip_vmcs_list = sorted( 601 rip_vmcs_list, key=lambda x: long(x.m("EPT_POINTER_FULL"))) 602 603 for ept, rip_ept_vmcs_list in groupby( 604 sorted_rip_vmcs_list, 605 lambda x: long(x.m("EPT_POINTER_FULL"))): 606 607 vm = VirtualMachine(host_rip=host_rip, ept=ept, 608 session=self.session) 609 for vmcs in rip_ept_vmcs_list: 610 try: 611 # If a VMCS is nested we cannot do validation at this 612 # step unless the memory image is for a guest VM or the 613 # physical address_space is a VTxPagedMemory. The 614 # physical AS is a VTxPagedMemory when you specify the 615 # --ept parameter on the command line. 616 if vmcs.IS_NESTED: 617 if (self.plugin_args.image_is_guest or 618 self.physical_address_space.metadata( 619 "ept")): 620 vm.add_vmcs( 621 vmcs, 622 validate=not self.plugin_args.no_validate) 623 else: 624 vm.add_vmcs(vmcs, validate=False) 625 else: 626 vm.add_vmcs( 627 vmcs, 628 validate=not self.plugin_args.no_validate) 629 630 if vm.is_valid_vmcs(vmcs): 631 if self.plugin_args.quick: 632 break 633 except UnrelatedVmcsError: 634 # This may happen when we analyze live memory. When 635 # the HOST_RIP/EPT that we grouped with has changed 636 # between finding it and adding it to a vm, add_vmcs 637 # may raise an UnrelatedVmcsError. 638 # Not much we can do other than skipping this VMCS. 639 continue 640 641 # Discard empty VMs, which can happen if we skipped vmcss. 642 if not vm.vmcss: 643 continue 644 645 # We need to split nested and host VMs here. However, we 646 # cannot use the is_nested method of VirtualMachine, because the 647 # potentially nested VMs aren't technically nested yet 648 # (i.e: don't have a parent). So we resort to checking if 649 # all the VMCSs are of nested-type. 650 may_be_nested = all([v.IS_NESTED for v in vm.vmcss]) 651 if may_be_nested and not vm.is_valid: 652 # Only add as nested VMs ones that haven't been validated 653 # yet. This covers the case there image_is_guest is True 654 # and they were validated as hosts. 655 nested_vms.append(vm) 656 else: 657 host_vms.append(vm) 658 659 if self.plugin_args.no_nested: 660 return host_vms 661 662 # == NESTED VM validation 663 # Only 1 level of nesting supported at the moment. 664 # 665 # TODO: Detect turtles-type VMCSs and relate them to the proper VM. 666 # https://www.usenix.org/event/osdi10/tech/full_papers/Ben-Yehuda.pdf 667 # 668 # These should show up as another valid VM. 669 if not self.plugin_args.no_validate: 670 candidate_hosts = [vm for vm in host_vms if vm.is_valid] 671 else: 672 candidate_hosts = [] 673 674 # This step validates nested VMs. We try all candidate nested vms 675 # against all candidate hosts. 676 for candidate_host_vm in candidate_hosts: 677 candidate_host_vm.add_nested_vms( 678 nested_vms, validate_all=not self.plugin_args.quick) 679 680 # Add all remaining VMs that we weren't able to guess the hierarchy of 681 # to the output vm list. 682 host_vms.extend(nested_vms) 683 return host_vms
684 685
686 - def render(self, renderer=None):
687 renderer.table_header([ 688 dict(name="Description", type="TreeNode", max_depth=5, child=dict( 689 type="VirtualizationNode", style="light", 690 quick=self.plugin_args.quick)), 691 ("Type", "type", ">20s"), 692 ("Valid", "valid", ">8s"), 693 ("EPT", "ept", "s") 694 ]) 695 696 virtual_machines = self.get_vms() 697 698 # At this point the hierarchy has been discovered. 699 for vm in virtual_machines: 700 # Skip invalid VMs. 701 if not self.plugin_args.show_all and not vm.is_valid: 702 continue 703 self.render_vm(renderer, vm, indent_level=0) 704 705 if self.plugin_args.verbosity > 2: 706 for vm in virtual_machines: 707 for vmcs in vm.vmcss: 708 if (not self.plugin_args.show_all and 709 not vm.is_valid_vmcs(vmcs)): 710 continue 711 renderer.section("VMCS @ %#x" % vmcs.obj_offset) 712 renderer.table_header([("Details", "details", "s")]) 713 self.session.plugins.p(vmcs).render(renderer) 714 715 for nested_vm in vm.virtual_machines: 716 for vmcs in nested_vm.vmcss: 717 if (not self.plugin_args.show_all and 718 not vm.is_valid_vmcs(vmcs)): 719 continue 720 renderer.section("VMCS @ %#x" % vmcs.obj_offset) 721 renderer.table_header([("Details", "details", "s")]) 722 self.session.plugins.p(vmcs).render(renderer)
723
724 - def render_vm(self, renderer, vm, indent_level=0):
725 vm_ept = ','.join(["0x%X" % e for e in vm.ept_list]) 726 renderer.table_row(vm, 'VM', vm.is_valid, vm_ept, depth=indent_level) 727 728 if vm.is_valid and isinstance( 729 self.session, session_module.InteractiveSession): 730 self.session.session_list.append(vm.GetSession()) 731 732 if self.plugin_args.verbosity > 1: 733 for vmcs in sorted(vm.vmcss, 734 key=lambda x: x.m("VPID")): 735 if not self.plugin_args.show_all and not vm.is_valid_vmcs(vmcs): 736 continue 737 738 valid = vm.is_valid_vmcs(vmcs) 739 renderer.table_row( 740 vmcs, 741 vmcs.obj_name, valid, '', depth=indent_level+1) 742 743 for nested_vm in vm.virtual_machines: 744 if not self.plugin_args.show_all and not nested_vm.is_valid: 745 continue 746 self.render_vm(renderer, nested_vm, indent_level=indent_level+1)
747