1# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 2# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 3# 4 5import collections.abc 6import datetime 7import functools 8import logging 9import struct 10from typing import Iterable, Iterator, Optional, Union, Dict, Tuple, List 11 12from volatility.framework import constants, exceptions, interfaces, objects, renderers, symbols 13from volatility.framework.layers import intel 14from volatility.framework.renderers import conversion 15from volatility.framework.symbols import generic 16from volatility.framework.symbols.windows.extensions import pool 17 18vollog = logging.getLogger(__name__) 19 20# Keep these in a basic module, to prevent import cycles when symbol providers require them 21 22 23class KSYSTEM_TIME(objects.StructType): 24 """A system time structure that stores a high and low part.""" 25 26 def get_time(self): 27 wintime = (self.High1Time << 32) | self.LowPart 28 return conversion.wintime_to_datetime(wintime) 29 30 31class MMVAD_SHORT(objects.StructType): 32 """A class that represents process virtual memory ranges. 33 34 Each instance is a node in a binary tree structure and is pointed to 35 by VadRoot. 36 """ 37 38 @functools.lru_cache(maxsize = None) 39 def get_tag(self): 40 vad_address = self.vol.offset 41 42 # the offset is different on 32 and 64 bits 43 symbol_table_name = self.vol.type_name.split(constants.BANG)[0] 44 if not symbols.symbol_table_is_64bit(self._context, symbol_table_name): 45 vad_address -= 4 46 else: 47 vad_address -= 12 48 49 try: 50 # TODO: instantiate a _POOL_HEADER and return PoolTag 51 bytesobj = self._context.object(symbol_table_name + constants.BANG + "bytes", 52 layer_name = self.vol.layer_name, 53 offset = vad_address, 54 native_layer_name = self.vol.native_layer_name, 55 length = 4) 56 57 return bytesobj.decode() 58 except exceptions.InvalidAddressException: 59 return None 60 except UnicodeDecodeError: 61 return None 62 63 def traverse(self, visited = None, depth = 0): 64 """Traverse the VAD tree, determining each underlying VAD node type by 65 looking up the pool tag for the structure and then casting into a new 66 object.""" 67 68 # TODO: this is an arbitrary limit chosen based on past observations 69 if depth > 100: 70 vollog.log(constants.LOGLEVEL_VVV, "Vad tree is too deep, something went wrong!") 71 raise RuntimeError("Vad tree is too deep") 72 73 if visited is None: 74 visited = set() 75 76 vad_address = self.vol.offset 77 78 if vad_address in visited: 79 vollog.log(constants.LOGLEVEL_VVV, "VAD node already seen!") 80 return 81 82 visited.add(vad_address) 83 tag = self.get_tag() 84 85 if tag in ["VadS", "VadF"]: 86 target = "_MMVAD_SHORT" 87 elif tag != None and tag.startswith("Vad"): 88 target = "_MMVAD" 89 elif depth == 0: 90 # the root node at depth 0 is allowed to not have a tag 91 # but we still want to continue and access its right & left child 92 target = None 93 else: 94 # any node other than the root that doesn't have a recognized tag 95 # is just garbage and we skip the node entirely 96 vollog.log(constants.LOGLEVEL_VVV, 97 "Skipping VAD at {} depth {} with tag {}".format(self.vol.offset, depth, tag)) 98 return 99 100 if target: 101 vad_object = self.cast(target) 102 yield vad_object 103 104 try: 105 for vad_node in self.get_left_child().dereference().traverse(visited, depth + 1): 106 yield vad_node 107 except exceptions.InvalidAddressException as excp: 108 vollog.log(constants.LOGLEVEL_VVV, "Invalid address on LeftChild: {0:#x}".format(excp.invalid_address)) 109 110 try: 111 for vad_node in self.get_right_child().dereference().traverse(visited, depth + 1): 112 yield vad_node 113 except exceptions.InvalidAddressException as excp: 114 vollog.log(constants.LOGLEVEL_VVV, "Invalid address on RightChild: {0:#x}".format(excp.invalid_address)) 115 116 def get_right_child(self): 117 """Get the right child member.""" 118 119 if self.has_member("RightChild"): 120 return self.RightChild 121 122 elif self.has_member("Right"): 123 return self.Right 124 125 raise AttributeError("Unable to find the right child member") 126 127 def get_left_child(self): 128 """Get the left child member.""" 129 130 if self.has_member("LeftChild"): 131 return self.LeftChild 132 133 elif self.has_member("Left"): 134 return self.Left 135 136 raise AttributeError("Unable to find the left child member") 137 138 def get_parent(self): 139 """Get the VAD's parent member.""" 140 141 # this is for xp and 2003 142 if self.has_member("Parent"): 143 return self.Parent 144 145 # this is for vista through windows 7 146 elif self.has_member("u1") and self.u1.has_member("Parent"): 147 return self.u1.Parent & ~0x3 148 149 # this is for windows 8 and 10 150 elif self.has_member("VadNode"): 151 152 if self.VadNode.has_member("u1"): 153 return self.VadNode.u1.Parent & ~0x3 154 155 elif self.VadNode.has_member("ParentValue"): 156 return self.VadNode.ParentValue & ~0x3 157 158 # also for windows 8 and 10 159 elif self.has_member("Core"): 160 161 if self.Core.VadNode.has_member("u1"): 162 return self.Core.VadNode.u1.Parent & ~0x3 163 164 elif self.Core.VadNode.has_member("ParentValue"): 165 return self.Core.VadNode.ParentValue & ~0x3 166 167 raise AttributeError("Unable to find the parent member") 168 169 def get_start(self): 170 """Get the VAD's starting virtual address.""" 171 172 if self.has_member("StartingVpn"): 173 174 if self.has_member("StartingVpnHigh"): 175 return (self.StartingVpn << 12) | (self.StartingVpnHigh << 44) 176 else: 177 return self.StartingVpn << 12 178 179 elif self.has_member("Core"): 180 181 if self.Core.has_member("StartingVpnHigh"): 182 return (self.Core.StartingVpn << 12) | (self.Core.StartingVpnHigh << 44) 183 else: 184 return self.Core.StartingVpn << 12 185 186 raise AttributeError("Unable to find the starting VPN member") 187 188 def get_end(self): 189 """Get the VAD's ending virtual address.""" 190 191 if self.has_member("EndingVpn"): 192 193 if self.has_member("EndingVpnHigh"): 194 return (((self.EndingVpn + 1) << 12) | (self.EndingVpnHigh << 44)) - 1 195 else: 196 return ((self.EndingVpn + 1) << 12) - 1 197 198 elif self.has_member("Core"): 199 if self.Core.has_member("EndingVpnHigh"): 200 return (((self.Core.EndingVpn + 1) << 12) | (self.Core.EndingVpnHigh << 44)) - 1 201 else: 202 return ((self.Core.EndingVpn + 1) << 12) - 1 203 204 raise AttributeError("Unable to find the ending VPN member") 205 206 def get_commit_charge(self): 207 """Get the VAD's commit charge (number of committed pages)""" 208 209 if self.has_member("u1") and self.u1.has_member("VadFlags1"): 210 return self.u1.VadFlags1.CommitCharge 211 212 elif self.has_member("u") and self.u.has_member("VadFlags"): 213 return self.u.VadFlags.CommitCharge 214 215 elif self.has_member("Core"): 216 return self.Core.u1.VadFlags1.CommitCharge 217 218 raise AttributeError("Unable to find the commit charge member") 219 220 def get_private_memory(self): 221 """Get the VAD's private memory setting.""" 222 223 if self.has_member("u1") and self.u1.has_member("VadFlags1") and self.u1.VadFlags1.has_member("PrivateMemory"): 224 return self.u1.VadFlags1.PrivateMemory 225 226 elif self.has_member("u") and self.u.has_member("VadFlags") and self.u.VadFlags.has_member("PrivateMemory"): 227 return self.u.VadFlags.PrivateMemory 228 229 elif self.has_member("Core"): 230 if (self.Core.has_member("u1") and self.Core.u1.has_member("VadFlags1") 231 and self.Core.u1.VadFlags1.has_member("PrivateMemory")): 232 return self.Core.u1.VadFlags1.PrivateMemory 233 234 elif (self.Core.has_member("u") and self.Core.u.has_member("VadFlags") 235 and self.Core.u.VadFlags.has_member("PrivateMemory")): 236 return self.Core.u.VadFlags.PrivateMemory 237 238 raise AttributeError("Unable to find the private memory member") 239 240 def get_protection(self, protect_values, winnt_protections): 241 """Get the VAD's protection constants as a string.""" 242 243 protect = None 244 245 if self.has_member("u"): 246 protect = self.u.VadFlags.Protection 247 248 elif self.has_member("Core"): 249 protect = self.Core.u.VadFlags.Protection 250 251 try: 252 value = protect_values[protect] 253 except IndexError: 254 value = 0 255 256 names = [] 257 258 for name, mask in winnt_protections.items(): 259 if value & mask != 0: 260 names.append(name) 261 262 return "|".join(names) 263 264 def get_file_name(self): 265 """Only long(er) vads have mapped files.""" 266 return renderers.NotApplicableValue() 267 268 269class MMVAD(MMVAD_SHORT): 270 """A version of the process virtual memory range structure that contains 271 additional fields necessary to map files from disk.""" 272 273 def get_file_name(self): 274 """Get the name of the file mapped into the memory range (if any)""" 275 276 file_name = renderers.NotApplicableValue() 277 278 try: 279 # this is for xp and 2003 280 if self.has_member("ControlArea"): 281 file_name = self.ControlArea.FilePointer.FileName.get_string() 282 283 # this is for vista through windows 7 284 else: 285 file_name = self.Subsection.ControlArea.FilePointer.dereference().cast( 286 "_FILE_OBJECT").FileName.get_string() 287 288 except exceptions.InvalidAddressException: 289 pass 290 291 return file_name 292 293 294class EX_FAST_REF(objects.StructType): 295 """This is a standard Windows structure that stores a pointer to an object 296 but also leverages the least significant bits to encode additional details. 297 298 When dereferencing the pointer, we need to strip off the extra bits. 299 """ 300 301 def dereference(self) -> interfaces.objects.ObjectInterface: 302 303 if constants.BANG not in self.vol.type_name: 304 raise ValueError("Invalid symbol table name syntax (no {} found)".format(constants.BANG)) 305 306 # the mask value is different on 32 and 64 bits 307 symbol_table_name = self.vol.type_name.split(constants.BANG)[0] 308 if not symbols.symbol_table_is_64bit(self._context, symbol_table_name): 309 max_fast_ref = 7 310 else: 311 max_fast_ref = 15 312 313 return self._context.object(symbol_table_name + constants.BANG + "pointer", 314 layer_name = self.vol.layer_name, 315 offset = self.Object & ~max_fast_ref, 316 native_layer_name = self.vol.native_layer_name) 317 318 319class DEVICE_OBJECT(objects.StructType, pool.ExecutiveObject): 320 """A class for kernel device objects.""" 321 322 def get_device_name(self) -> str: 323 header = self.get_object_header() 324 return header.NameInfo.Name.String # type: ignore 325 326 327class DRIVER_OBJECT(objects.StructType, pool.ExecutiveObject): 328 """A class for kernel driver objects.""" 329 330 def get_driver_name(self) -> str: 331 header = self.get_object_header() 332 return header.NameInfo.Name.String # type: ignore 333 334 def is_valid(self) -> bool: 335 """Determine if the object is valid.""" 336 return True 337 338 339class OBJECT_SYMBOLIC_LINK(objects.StructType, pool.ExecutiveObject): 340 """A class for kernel link objects.""" 341 342 def get_link_name(self) -> str: 343 header = self.get_object_header() 344 return header.NameInfo.Name.String # type: ignore 345 346 def is_valid(self) -> bool: 347 """Determine if the object is valid.""" 348 return True 349 350 def get_create_time(self): 351 return conversion.wintime_to_datetime(self.CreationTime.QuadPart) 352 353 354class FILE_OBJECT(objects.StructType, pool.ExecutiveObject): 355 """A class for windows file objects.""" 356 357 def is_valid(self) -> bool: 358 """Determine if the object is valid.""" 359 return self.FileName.Length > 0 and self._context.layers[self.vol.layer_name].is_valid(self.FileName.Buffer) 360 361 def file_name_with_device(self) -> Union[str, interfaces.renderers.BaseAbsentValue]: 362 name = renderers.UnreadableValue() # type: Union[str, interfaces.renderers.BaseAbsentValue] 363 364 if self._context.layers[self.vol.layer_name].is_valid(self.DeviceObject): 365 name = "\\Device\\{}".format(self.DeviceObject.get_device_name()) 366 367 try: 368 name += self.FileName.String 369 except (TypeError, exceptions.InvalidAddressException): 370 pass 371 372 return name 373 374 375class KMUTANT(objects.StructType, pool.ExecutiveObject): 376 """A class for windows mutant objects.""" 377 378 def is_valid(self) -> bool: 379 """Determine if the object is valid.""" 380 return True 381 382 def get_name(self) -> str: 383 """Get the object's name from the object header.""" 384 header = self.get_object_header() 385 return header.NameInfo.Name.String # type: ignore 386 387 388class ETHREAD(objects.StructType): 389 """A class for executive thread objects.""" 390 391 def owning_process(self, kernel_layer: str = None) -> interfaces.objects.ObjectInterface: 392 """Return the EPROCESS that owns this thread.""" 393 return self.ThreadsProcess.dereference(kernel_layer) 394 395 396class UNICODE_STRING(objects.StructType): 397 """A class for Windows unicode string structures.""" 398 399 def get_string(self) -> interfaces.objects.ObjectInterface: 400 # We explicitly do *not* catch errors here, we allow an exception to be thrown 401 # (otherwise there's no way to determine anything went wrong) 402 # It's up to the user of this method to catch exceptions 403 return self.Buffer.dereference().cast("string", 404 max_length = self.Length, 405 errors = "replace", 406 encoding = "utf16") 407 408 String = property(get_string) 409 410 411class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject): 412 """A class for executive kernel processes objects.""" 413 414 def is_valid(self) -> bool: 415 """Determine if the object is valid.""" 416 417 try: 418 name = objects.utility.array_to_string(self.ImageFileName) 419 if not name or len(name) == 0 or name[0] == "\x00": 420 return False 421 422 # The System/PID 4 process has no create time 423 if not (str(name) == "System" and self.UniqueProcessId == 4): 424 if self.CreateTime.QuadPart == 0: 425 return False 426 427 ctime = self.get_create_time() 428 if not isinstance(ctime, datetime.datetime): 429 return False 430 431 if not (1998 < ctime.year < 2030): 432 return False 433 434 # NT pids are divisible by 4 435 if self.UniqueProcessId % 4 != 0: 436 return False 437 438 # check for all 0s besides the PCID entries 439 if isinstance(self.Pcb.DirectoryTableBase, objects.Array): 440 dtb = self.Pcb.DirectoryTableBase.cast("pointer") 441 else: 442 dtb = self.Pcb.DirectoryTableBase 443 444 if dtb == 0: 445 return False 446 447 # check for all 0s besides the PCID entries 448 if dtb & ~0xfff == 0: 449 return False 450 451 ## TODO: we can also add the thread Flink and Blink tests if necessary 452 453 except exceptions.InvalidAddressException: 454 return False 455 456 return True 457 458 def add_process_layer(self, config_prefix: str = None, preferred_name: str = None): 459 """Constructs a new layer based on the process's DirectoryTableBase.""" 460 461 parent_layer = self._context.layers[self.vol.layer_name] 462 463 if not isinstance(parent_layer, intel.Intel): 464 # We can't get bits_per_register unless we're an intel space (since that's not defined at the higher layer) 465 raise TypeError("Parent layer is not a translation layer, unable to construct process layer") 466 467 # Presumably for 64-bit systems, the DTB is defined as an array, rather than an unsigned long long 468 dtb = 0 # type: int 469 if isinstance(self.Pcb.DirectoryTableBase, objects.Array): 470 dtb = self.Pcb.DirectoryTableBase.cast("unsigned long long") 471 else: 472 dtb = self.Pcb.DirectoryTableBase 473 dtb = dtb & ((1 << parent_layer.bits_per_register) - 1) 474 475 if preferred_name is None: 476 preferred_name = self.vol.layer_name + "_Process{}".format(self.UniqueProcessId) 477 478 # Add the constructed layer and return the name 479 return self._add_process_layer(self._context, dtb, config_prefix, preferred_name) 480 481 def get_peb(self) -> interfaces.objects.ObjectInterface: 482 """Constructs a PEB object""" 483 if constants.BANG not in self.vol.type_name: 484 raise ValueError("Invalid symbol table name syntax (no {} found)".format(constants.BANG)) 485 486 # add_process_layer can raise InvalidAddressException. 487 # if that happens, we let the exception propagate upwards 488 proc_layer_name = self.add_process_layer() 489 490 proc_layer = self._context.layers[proc_layer_name] 491 if not proc_layer.is_valid(self.Peb): 492 raise exceptions.InvalidAddressException(proc_layer_name, self.Peb, 493 "Invalid address at {:0x}".format(self.Peb)) 494 495 sym_table = self.vol.type_name.split(constants.BANG)[0] 496 peb = self._context.object("{}{}_PEB".format(sym_table, constants.BANG), 497 layer_name = proc_layer_name, 498 offset = self.Peb) 499 return peb 500 501 def load_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: 502 """Generator for DLLs in the order that they were loaded.""" 503 504 try: 505 peb = self.get_peb() 506 for entry in peb.Ldr.InLoadOrderModuleList.to_list( 507 "{}{}_LDR_DATA_TABLE_ENTRY".format(self.get_symbol_table_name(), constants.BANG), 508 "InLoadOrderLinks"): 509 yield entry 510 except exceptions.InvalidAddressException: 511 return 512 513 def init_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: 514 """Generator for DLLs in the order that they were initialized""" 515 516 try: 517 peb = self.get_peb() 518 for entry in peb.Ldr.InInitializationOrderModuleList.to_list( 519 "{}{}_LDR_DATA_TABLE_ENTRY".format(self.get_symbol_table_name(), constants.BANG), 520 "InInitializationOrderLinks"): 521 yield entry 522 except exceptions.InvalidAddressException: 523 return 524 525 def mem_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: 526 """Generator for DLLs in the order that they appear in memory""" 527 528 try: 529 peb = self.get_peb() 530 for entry in peb.Ldr.InMemoryOrderModuleList.to_list( 531 "{}{}_LDR_DATA_TABLE_ENTRY".format(self.get_symbol_table_name(), constants.BANG), 532 "InMemoryOrderLinks"): 533 yield entry 534 except exceptions.InvalidAddressException: 535 return 536 537 def get_handle_count(self): 538 try: 539 if self.has_member("ObjectTable"): 540 if self.ObjectTable.has_member("HandleCount"): 541 return self.ObjectTable.HandleCount 542 543 except exceptions.InvalidAddressException: 544 vollog.log(constants.LOGLEVEL_VVV, 545 "Cannot access _EPROCESS.ObjectTable.HandleCount at {0:#x}".format(self.vol.offset)) 546 547 return renderers.UnreadableValue() 548 549 def get_session_id(self): 550 try: 551 if self.has_member("Session"): 552 if self.Session == 0: 553 return renderers.NotApplicableValue() 554 555 symbol_table_name = self.get_symbol_table_name() 556 kvo = self._context.layers[self.vol.native_layer_name].config['kernel_virtual_offset'] 557 ntkrnlmp = self._context.module(symbol_table_name, 558 layer_name = self.vol.native_layer_name, 559 offset = kvo, 560 native_layer_name = self.vol.native_layer_name) 561 session = ntkrnlmp.object(object_type = "_MM_SESSION_SPACE", offset = self.Session, absolute = True) 562 563 if session.has_member("SessionId"): 564 return session.SessionId 565 566 except exceptions.InvalidAddressException: 567 vollog.log(constants.LOGLEVEL_VVV, 568 "Cannot access _EPROCESS.Session.SessionId at {0:#x}".format(self.vol.offset)) 569 570 return renderers.UnreadableValue() 571 572 def get_create_time(self): 573 return conversion.wintime_to_datetime(self.CreateTime.QuadPart) 574 575 def get_exit_time(self): 576 return conversion.wintime_to_datetime(self.ExitTime.QuadPart) 577 578 def get_wow_64_process(self): 579 if self.has_member("Wow64Process"): 580 return self.Wow64Process 581 582 elif self.has_member("WoW64Process"): 583 return self.WoW64Process 584 585 raise AttributeError("Unable to find Wow64Process") 586 587 def get_is_wow64(self): 588 try: 589 value = self.get_wow_64_process() 590 except AttributeError: 591 return False 592 593 return value != 0 and value != None 594 595 def get_vad_root(self): 596 597 # windows 8 and 2012 (_MM_AVL_TABLE) 598 if self.VadRoot.has_member("BalancedRoot"): 599 return self.VadRoot.BalancedRoot 600 601 # windows 8.1 and windows 10 (_RTL_AVL_TREE) 602 elif self.VadRoot.has_member("Root"): 603 return self.VadRoot.Root.dereference() # .cast("_MMVAD") 604 605 else: 606 # windows xp and 2003 607 return self.VadRoot.dereference().cast("_MMVAD") 608 609 610class LIST_ENTRY(objects.StructType, collections.abc.Iterable): 611 """A class for double-linked lists on Windows.""" 612 613 def to_list(self, 614 symbol_type: str, 615 member: str, 616 forward: bool = True, 617 sentinel: bool = True, 618 layer: Optional[str] = None) -> Iterator[interfaces.objects.ObjectInterface]: 619 """Returns an iterator of the entries in the list.""" 620 621 layer = layer or self.vol.layer_name 622 623 relative_offset = self._context.symbol_space.get_type(symbol_type).relative_child_offset(member) 624 625 direction = 'Blink' 626 if forward: 627 direction = 'Flink' 628 629 trans_layer = self._context.layers[layer] 630 631 try: 632 trans_layer.is_valid(self.vol.offset) 633 link = getattr(self, direction).dereference() 634 except exceptions.InvalidAddressException: 635 return 636 637 if not sentinel: 638 yield self._context.object(symbol_type, 639 layer, 640 offset = self.vol.offset - relative_offset, 641 native_layer_name = layer or self.vol.native_layer_name) 642 643 seen = {self.vol.offset} 644 while link.vol.offset not in seen: 645 obj_offset = link.vol.offset - relative_offset 646 647 try: 648 trans_layer.is_valid(obj_offset) 649 except exceptions.InvalidAddressException: 650 return 651 652 obj = self._context.object(symbol_type, 653 layer, 654 offset = obj_offset, 655 native_layer_name = layer or self.vol.native_layer_name) 656 yield obj 657 658 seen.add(link.vol.offset) 659 660 try: 661 link = getattr(link, direction).dereference() 662 except exceptions.InvalidAddressException: 663 return 664 665 def __iter__(self) -> Iterator[interfaces.objects.ObjectInterface]: 666 return self.to_list(self.vol.parent.vol.type_name, self.vol.member_name) 667