def _generator(self, procs): # determine if we're on a 32 or 64 bit kernel is_32bit_arch = not symbols.symbol_table_is_64bit( self.context, self.config["nt_symbols"]) for proc in procs: process_name = utility.array_to_string(proc.ImageFileName) for vad, data in self.list_injections(self.context, self.config["nt_symbols"], proc): # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64 if is_32bit_arch or proc.get_is_wow64(): architecture = "intel" else: architecture = "intel64" disasm = interfaces.renderers.Disassembly( data, vad.get_start(), architecture) yield (0, (proc.UniqueProcessId, process_name, format_hints.Hex(vad.get_start()), format_hints.Hex(vad.get_end()), vad.get_tag(), vad.get_protection( vadinfo.VadInfo.protect_values( self.context, proc.vol.layer_name, self.config["nt_symbols"]), vadinfo.winnt_protections), vad.get_commit_charge(), vad.get_private_memory(), format_hints.HexBytes(data), disasm))
def _generator(self, tasks): result = [] is_32bit = not symbols.symbol_table_is_64bit(self.context, self.config["vmlinux"]) if is_32bit: openssh_json_file = "openssh32" else: openssh_json_file = "openssh64" openssh_table_name = intermed.IntermediateSymbolTable.create( self.context, self.config_path, 'linux', openssh_json_file) for task in tasks: if not task.mm: continue name = utility.array_to_string(task.comm) vollog.info("Scanning process #{} ({} {})".format( task.pid, name, "")) proc_layer_name = task.add_process_layer() extractor = SSHKeyExtractor(self.context, openssh_table_name, proc_layer_name, task, self._progress_callback) keys = extractor.extract() result.extend(keys) return result
def get_symbols(self): ret_syms = [] if symbols.symbol_table_is_64bit(self._context, self.get_symbol_table().name): prefix = "Elf64_" else: prefix = "Elf32_" elf_table_name = intermed.IntermediateSymbolTable.create( self.context, self.config_path, "linux", "elf", native_types=None, class_types=extensions.elf.class_types) syms = self._context.object( self.get_symbol_table().name + constants.BANG + "array", layer_name=self.vol.layer_name, offset=self.section_symtab, subtype=self._context.symbol_space.get_type(elf_table_name + constants.BANG + prefix + "Sym"), count=self.num_symtab + 1) if self.section_strtab: for sym in syms: sym.set_cached_strtab(self.section_strtab) yield sym
def create_callback_table(context: interfaces.context.ContextInterface, symbol_table: str, config_path: str) -> str: """Creates a symbol table for a set of callbacks. Args: context: The context to retrieve required elements (layers, symbol tables) from symbol_table: The name of an existing symbol table containing the kernel symbols config_path: The configuration path within the context of the symbol table to create Returns: The name of the constructed callback table """ native_types = context.symbol_space[symbol_table].natives is_64bit = symbols.symbol_table_is_64bit(context, symbol_table) table_mapping = {"nt_symbols": symbol_table} if is_64bit: symbol_filename = "callbacks-x64" else: symbol_filename = "callbacks-x86" return intermed.IntermediateSymbolTable.create(context, config_path, "windows", symbol_filename, native_types = native_types, table_mapping = table_mapping)
def _get_pool_header_module(cls, context, layer_name, symbol_table): # Setup the pool header and offset differential try: module = context.module(symbol_table, layer_name, offset=0) module.get_type("_POOL_HEADER") except exceptions.SymbolError: # We have to manually load a symbol table if symbols.symbol_table_is_64bit(context, symbol_table): is_win_7 = cls.is_windows_7(context, symbol_table) if is_win_7: pool_header_json_filename = "poolheader-x64-win7" else: pool_header_json_filename = "poolheader-x64" else: pool_header_json_filename = "poolheader-x86" new_table_name = intermed.IntermediateSymbolTable.create( context=context, config_path=configuration.path_join( context.symbol_space[symbol_table].config_path, "poolheader"), sub_path="windows", filename=pool_header_json_filename, table_mapping={'nt_symbols': symbol_table}, class_types={'_POOL_HEADER': extensions.pool.POOL_HEADER}) module = context.module(new_table_name, layer_name, offset=0) return module
def _generator(self): vmlinux = contexts.Module(self.context, self.config['vmlinux'], self.config['primary'], 0) modules = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['vmlinux']) handlers = linux.LinuxUtilities.generate_kernel_handler_info(self.context, self.config['primary'], self.config['vmlinux'], modules) is_32bit = not symbols.symbol_table_is_64bit(self.context, self.config["vmlinux"]) idt_table_size = 256 address_mask = self.context.layers[self.config['primary']].address_mask # hw handlers + system call check_idxs = list(range(0, 20)) + [128] if is_32bit: if vmlinux.has_type("gate_struct"): idt_type = "gate_struct" else: idt_type = "desc_struct" else: if vmlinux.has_type("gate_struct64"): idt_type = "gate_struct64" elif vmlinux.has_type("gate_struct"): idt_type = "gate_struct" else: idt_type = "idt_desc" addrs = vmlinux.object_from_symbol("idt_table") table = vmlinux.object(object_type = 'array', offset = addrs.vol.offset, subtype = vmlinux.get_type(idt_type), count = idt_table_size) for i in check_idxs: ent = table[i] if not ent: continue if hasattr(ent, "Address"): idt_addr = ent.Address else: low = ent.offset_low middle = ent.offset_middle if hasattr(ent, "offset_high"): high = ent.offset_high else: high = 0 idt_addr = (high << 32) | (middle << 16) | low idt_addr = idt_addr & address_mask module_name, symbol_name = linux.LinuxUtilities.lookup_module_address(self.context, handlers, idt_addr) yield (0, [format_hints.Hex(i), format_hints.Hex(idt_addr), module_name, symbol_name])
def _generator(self, tasks): is_32bit = not symbols.symbol_table_is_64bit(self.context, self.config["darwin"]) if is_32bit: pack_format = "I" bash_json_file = "bash32" else: pack_format = "Q" bash_json_file = "bash64" bash_table_name = BashIntermedSymbols.create(self.context, self.config_path, "linux", bash_json_file) ts_offset = self.context.symbol_space.get_type( bash_table_name + constants.BANG + "hist_entry").relative_child_offset("timestamp") for task in tasks: task_name = utility.array_to_string(task.p_comm) if task_name not in ["bash", "sh", "dash"]: continue proc_layer_name = task.add_process_layer() if proc_layer_name is None: continue proc_layer = self.context.layers[proc_layer_name] bang_addrs = [] # find '#' values on the heap for address in proc_layer.scan( self.context, scanners.BytesScanner(b"#"), sections=task.get_process_memory_sections( self.context, self.config['darwin'], rw_no_file=True)): bang_addrs.append(struct.pack(pack_format, address)) history_entries = [] for address, _ in proc_layer.scan( self.context, scanners.MultiStringScanner(bang_addrs), sections=task.get_process_memory_sections( self.context, self.config['darwin'], rw_no_file=True)): hist = self.context.object(bash_table_name + constants.BANG + "hist_entry", offset=address - ts_offset, layer_name=proc_layer_name) if hist.is_valid(): history_entries.append(hist) for hist in sorted(history_entries, key=lambda x: x.get_time_as_integer()): yield (0, (int(task.p_pid), task_name, hist.get_time_object(), hist.get_command()))
def create_service_table(context: interfaces.context.ContextInterface, symbol_table: str, config_path: str) -> str: """Constructs a symbol table containing the symbols for services depending upon the operating system in use. Args: context: The context to retrieve required elements (layers, symbol tables) from symbol_table: The name of the table containing the kernel symbols config_path: The configuration path for any settings required by the new table Returns: A symbol table containing the symbols necessary for services """ native_types = context.symbol_space[symbol_table].natives is_64bit = symbols.symbol_table_is_64bit(context, symbol_table) if SvcScan.is_windows_xp(context=context, symbol_table=symbol_table) and not is_64bit: symbol_filename = "services-xp-x86" elif SvcScan.is_xp_or_2003(context=context, symbol_table=symbol_table) and is_64bit: symbol_filename = "services-xp-2003-x64" elif poolscanner.PoolScanner.is_windows_8_or_later( context=context, symbol_table=symbol_table) and is_64bit: symbol_filename = "services-win8-x64" elif poolscanner.PoolScanner.is_windows_8_or_later( context=context, symbol_table=symbol_table) and not is_64bit: symbol_filename = "services-win8-x86" elif SvcScan.is_win10_up_to_15063( context=context, symbol_table=symbol_table) and is_64bit: symbol_filename = "services-win10-15063-x64" elif SvcScan.is_win10_up_to_15063( context=context, symbol_table=symbol_table) and not is_64bit: symbol_filename = "services-win10-15063-x86" elif SvcScan.is_win10_16299_or_later( context=context, symbol_table=symbol_table) and is_64bit: symbol_filename = "services-win10-16299-x64" elif SvcScan.is_win10_16299_or_later( context=context, symbol_table=symbol_table) and not is_64bit: symbol_filename = "services-win10-16299-x86" elif SvcScan.is_vista_or_later(context=context, symbol_table=symbol_table) and is_64bit: symbol_filename = "services-vista-x64" elif SvcScan.is_vista_or_later( context=context, symbol_table=symbol_table) and not is_64bit: symbol_filename = "services-vista-x86" else: raise NotImplementedError( "This version of Windows is not supported!") return intermed.IntermediateSymbolTable.create( context, config_path, "windows", symbol_filename, class_types=services.class_types, native_types=native_types)
def dereference(self) -> interfaces.objects.ObjectInterface: if constants.BANG not in self.vol.type_name: raise ValueError("Invalid symbol table name syntax (no {} found)".format(constants.BANG)) # the mask value is different on 32 and 64 bits symbol_table_name = self.vol.type_name.split(constants.BANG)[0] if not symbols.symbol_table_is_64bit(self._context, symbol_table_name): max_fast_ref = 7 else: max_fast_ref = 15 return self._context.object(symbol_table_name + constants.BANG + "pointer", layer_name = self.vol.layer_name, offset = self.Object & ~max_fast_ref, native_layer_name = self.vol.native_layer_name)
def _generator(self, procs): # determine if we're on a 32 or 64 bit kernel is_32bit_arch = not symbols.symbol_table_is_64bit( self.context, self.config["nt_symbols"]) for proc in procs: process_name = utility.array_to_string(proc.ImageFileName) for vad, data in self.list_injections(self.context, self.config["primary"], self.config["nt_symbols"], proc): # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64 if is_32bit_arch or proc.get_is_wow64(): architecture = "intel" else: architecture = "intel64" disasm = interfaces.renderers.Disassembly( data, vad.get_start(), architecture) file_output = "Disabled" if self.config['dump']: file_output = "Error outputting to file" try: file_handle = vadinfo.VadInfo.vad_dump( self.context, proc, vad, self.open) file_handle.close() file_output = file_handle.preferred_filename except (exceptions.InvalidAddressException, OverflowError) as excp: vollog.debug( "Unable to dump PE with pid {0}.{1:#x}: {2}". format(proc.UniqueProcessId, vad.get_start(), excp)) yield (0, (proc.UniqueProcessId, process_name, format_hints.Hex(vad.get_start()), format_hints.Hex(vad.get_end()), vad.get_tag(), vad.get_protection( vadinfo.VadInfo.protect_values( self.context, self.config["primary"], self.config["nt_symbols"]), vadinfo.winnt_protections), vad.get_commit_charge(), vad.get_private_memory(), file_output, format_hints.HexBytes(data), disasm))
def _generator(self, procs): # determine if we're on a 32 or 64 bit kernel is_32bit_arch = not symbols.symbol_table_is_64bit( self.context, self.config["nt_symbols"]) for proc in procs: process_name = utility.array_to_string(proc.ImageFileName) for vad, data in self.list_injections(self.context, self.config["primary"], self.config["nt_symbols"], proc): # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64 if is_32bit_arch or proc.get_is_wow64(): architecture = "intel" else: architecture = "intel64" disasm = interfaces.renderers.Disassembly( data, vad.get_start(), architecture) dumped = False if self.config['dump']: filedata = vadinfo.VadInfo.vad_dump( self.context, proc, vad) if filedata: try: self.produce_file(filedata) dumped = True except Exception as excp: vollog.debug( "Unable to dump PE with pid {0}.{1:#x}: {2}". format(proc.UniqueProcessId, vad.get_start(), excp)) yield (0, (proc.UniqueProcessId, process_name, format_hints.Hex(vad.get_start()), format_hints.Hex(vad.get_end()), vad.get_tag(), vad.get_protection( vadinfo.VadInfo.protect_values( self.context, self.config["primary"], self.config["nt_symbols"]), vadinfo.winnt_protections), vad.get_commit_charge(), vad.get_private_memory(), dumped, format_hints.HexBytes(data), disasm))
def get_pool_header_table(cls, context: interfaces.context.ContextInterface, symbol_table: str) -> str: """Returns the appropriate symbol_table containing a _POOL_HEADER type, even if the original symbol table doesn't contain one. Args: context: The context that the symbol tables does (or will) reside in symbol_table: The expected symbol_table to contain the _POOL_HEADER type """ # Setup the pool header and offset differential try: context.symbol_space.get_type(symbol_table + constants.BANG + "_POOL_HEADER") table_name = symbol_table except exceptions.SymbolError: # We have to manually load a symbol table if symbols.symbol_table_is_64bit(context, symbol_table): is_win_7 = versions.is_windows_7(context, symbol_table) if is_win_7: pool_header_json_filename = "poolheader-x64-win7" else: pool_header_json_filename = "poolheader-x64" else: pool_header_json_filename = "poolheader-x86" # set the class_type to match the normal WindowsKernelIntermedSymbols is_vista_or_later = versions.is_vista_or_later( context, symbol_table) if is_vista_or_later: class_type = extensions.pool.POOL_HEADER_VISTA else: class_type = extensions.pool.POOL_HEADER table_name = intermed.IntermediateSymbolTable.create( context=context, config_path=configuration.path_join( context.symbol_space[symbol_table].config_path, "poolheader"), sub_path="windows", filename=pool_header_json_filename, table_mapping={'nt_symbols': symbol_table}, class_types={'_POOL_HEADER': class_type}) return table_name
def scan_hives(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str) -> \ Iterable[interfaces.objects.ObjectInterface]: """Scans for hives using the poolscanner module and constraints or bigpools module with tag. Args: context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols Returns: A list of Hive objects as found from the `layer_name` layer based on Hive pool signatures """ is_64bit = symbols.symbol_table_is_64bit(context, symbol_table) is_windows_8_1_or_later = HiveScan.is_windows_8_1_or_later( context=context, symbol_table=symbol_table) if is_windows_8_1_or_later and is_64bit: kvo = context.layers[layer_name].config['kernel_virtual_offset'] ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo) for pool in bigpools.BigPools.list_big_pools( context, layer_name=layer_name, symbol_table=symbol_table, tags=["CM10"]): cmhive = ntkrnlmp.object(object_type="_CMHIVE", offset=pool.Va, absolute=True) yield cmhive else: constraints = poolscanner.PoolScanner.builtin_constraints( symbol_table, [b'CM10']) for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints): _constraint, mem_object, _header = result yield mem_object
def get_tag(self): vad_address = self.vol.offset # the offset is different on 32 and 64 bits symbol_table_name = self.vol.type_name.split(constants.BANG)[0] if not symbols.symbol_table_is_64bit(self._context, symbol_table_name): vad_address -= 4 else: vad_address -= 12 try: # TODO: instantiate a _POOL_HEADER and return PoolTag bytesobj = self._context.object(symbol_table_name + constants.BANG + "bytes", layer_name = self.vol.layer_name, offset = vad_address, native_layer_name = self.vol.native_layer_name, length = 4) return bytesobj.decode() except exceptions.InvalidAddressException: return None except UnicodeDecodeError: return None
def generate_pool_scan(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str, constraints: List[PoolConstraint]) \ -> Generator[Tuple[ PoolConstraint, interfaces.objects.ObjectInterface, interfaces.objects.ObjectInterface], None, None]: """ Args: context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols constraints: List of pool constraints used to limit the scan results Returns: Iterable of tuples, containing the constraint that matched, the object from memory, the object header used to determine the object """ # get the object type map type_map = handles.Handles.get_type_map(context=context, layer_name=layer_name, symbol_table=symbol_table) cookie = handles.Handles.find_cookie(context=context, layer_name=layer_name, symbol_table=symbol_table) is_windows_10 = cls.is_windows_10(context, symbol_table) is_windows_8_or_later = cls.is_windows_8_or_later( context, symbol_table) # start off with the primary virtual layer scan_layer = layer_name # switch to a non-virtual layer if necessary if not is_windows_10: scan_layer = context.layers[scan_layer].config['memory_layer'] if symbols.symbol_table_is_64bit(context, symbol_table): alignment = 0x10 else: alignment = 8 for constraint, header in cls.pool_scan(context, scan_layer, symbol_table, constraints, alignment=alignment): mem_object = header.get_object(type_name=constraint.type_name, use_top_down=is_windows_8_or_later, executive=constraint.object_type is not None, native_layer_name='primary') if mem_object is None: vollog.log( constants.LOGLEVEL_VVV, "Cannot create an instance of {}".format( constraint.type_name)) continue if constraint.object_type is not None and not constraint.skip_type_test: try: if mem_object.get_object_header().get_object_type( type_map, cookie) != constraint.object_type: continue except exceptions.InvalidAddressException: vollog.log( constants.LOGLEVEL_VVV, "Cannot test instance type check for {}".format( constraint.type_name)) continue yield constraint, mem_object, header
def _generator(self) -> Iterator[Tuple[int, Tuple[int, int, Any, Any]]]: layer_name = self.config['primary'] collection = self.build_module_collection(self.context, self.config["primary"], self.config["nt_symbols"]) kvo = self.context.layers[layer_name].config['kernel_virtual_offset'] ntkrnlmp = self.context.module(self.config["nt_symbols"], layer_name=layer_name, offset=kvo) # this is just one way to enumerate the native (NT) service table. # to do the same thing for the Win32K service table, we would need Win32K.sys symbol support ## we could also find nt!KeServiceDescriptorTable (NT) and KeServiceDescriptorTableShadow (NT, Win32K) service_table_address = ntkrnlmp.get_symbol("KiServiceTable").address service_limit_address = ntkrnlmp.get_symbol("KiServiceLimit").address service_limit = ntkrnlmp.object(object_type="int", offset=service_limit_address) # on 32-bit systems the table indexes are 32-bits and contain pointers (unsigned) # on 64-bit systems the indexes are also 32-bits but they're offsets from the # base address of the table and can be negative, so we need a signed data type is_kernel_64 = symbols.symbol_table_is_64bit(self.context, self.config["nt_symbols"]) if is_kernel_64: array_subtype = "long" def kvo_calulator(func: int) -> int: return kvo + service_table_address + (func >> 4) find_address = kvo_calulator else: array_subtype = "unsigned long" def passthrough(func: int) -> int: return func find_address = passthrough functions = ntkrnlmp.object(object_type="array", offset=service_table_address, subtype=ntkrnlmp.get_type(array_subtype), count=service_limit) for idx, function_obj in enumerate(functions): function = find_address(function_obj) module_symbols = collection.get_module_symbols_by_absolute_location( function) for module_name, symbol_generator in module_symbols: symbols_found = False for symbol in symbol_generator: symbols_found = True yield (0, (idx, format_hints.Hex(function), module_name, symbol.split(constants.BANG)[1])) if not symbols_found: yield (0, (idx, format_hints.Hex(function), module_name, renderers.NotAvailableValue()))
def list_big_pools(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str, tags: Optional[list] = None): """Returns the big page pool objects from the kernel PoolBigPageTable array. Args: context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols tags: An optional list of pool tags to filter big page pool tags by Yields: A big page pool object """ kvo = context.layers[layer_name].config['kernel_virtual_offset'] ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo) big_page_table_offset = ntkrnlmp.get_symbol("PoolBigPageTable").address big_page_table = ntkrnlmp.object(object_type = "unsigned long long", offset = big_page_table_offset) big_page_table_size_offset = ntkrnlmp.get_symbol("PoolBigPageTableSize").address big_page_table_size = ntkrnlmp.object(object_type = "unsigned long", offset = big_page_table_size_offset) try: big_page_table_type = ntkrnlmp.get_type("_POOL_TRACKER_BIG_PAGES") except exceptions.SymbolError: # We have to manually load a symbol table is_vista_or_later = versions.is_vista_or_later(context, symbol_table) is_win10 = versions.is_win10(context, symbol_table) if is_win10: big_pools_json_filename = "bigpools-win10" elif is_vista_or_later: big_pools_json_filename = "bigpools-vista" else: big_pools_json_filename = "bigpools" if symbols.symbol_table_is_64bit(context, symbol_table): big_pools_json_filename += "-x64" else: big_pools_json_filename += "-x86" new_table_name = intermed.IntermediateSymbolTable.create( context = context, config_path = configuration.path_join(context.symbol_space[symbol_table].config_path, "bigpools"), sub_path = "windows", filename = big_pools_json_filename, table_mapping = {'nt_symbols': symbol_table}, class_types = {'_POOL_TRACKER_BIG_PAGES': extensions.pool.POOL_TRACKER_BIG_PAGES}) module = context.module(new_table_name, layer_name, offset = 0) big_page_table_type = module.get_type("_POOL_TRACKER_BIG_PAGES") big_pools = ntkrnlmp.object(object_type = "array", offset = big_page_table, subtype = big_page_table_type, count = big_page_table_size, absolute = True) for big_pool in big_pools: if big_pool.is_valid(): if tags is None or big_pool.get_key() in tags: yield big_pool
def get_available_pages(self) -> Iterable[Tuple[int, int, int]]: """Get the available pages that correspond to a cached file. The tuples generated are (physical_offset, file_offset, page_size). """ symbol_table_name = self.get_symbol_table_name() mmpte_type = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + "_MMPTE") mmpte_size = mmpte_type.size subsection = self.get_subsection() is_64bit = symbols.symbol_table_is_64bit(self._context, symbol_table_name) is_pae = self._context.layers[self.vol.layer_name].metadata.get( "pae", False) # This is a null-terminated single-linked list. while subsection != 0: try: if subsection.ControlArea != self.vol.offset: break except exceptions.InvalidAddressException: break # The offset into the file is stored implicitly based on the PTE location within the Subsection. starting_sector = subsection.StartingSector subsection_offset = starting_sector * 0x200 # Similar to the check in is_valid(), make sure the SubsectionBase is not page aligned. #if subsection.SubsectionBase & self.PAGE_MASK == 0: # break ptecount = 0 while ptecount < subsection.PtesInSubsection: pte_offset = subsection.SubsectionBase + (mmpte_size * ptecount) file_offset = subsection_offset + ptecount * 0x1000 try: mmpte = self.get_pte(pte_offset) except exceptions.InvalidAddressException: ptecount += 1 continue # First we check if the entry is valid. If so, then we get the physical offset. # The valid entries are actually handled by the hardware. if mmpte.u.Hard.Valid == 1: physoffset = mmpte.u.Hard.PageFrameNumber << 12 yield physoffset, file_offset, self.PAGE_SIZE elif mmpte.u.Soft.Prototype == 1: if not is_64bit and not is_pae: subsection_offset = ( (mmpte.u.Subsect.SubsectionAddressHigh << 7) | (mmpte.u.Subsect.SubsectionAddressLow << 3)) # If the entry is not a valid physical address then see if it is in transition. elif mmpte.u.Trans.Transition == 1: physoffset = mmpte.u.Trans.PageFrameNumber << 12 yield physoffset, file_offset, self.PAGE_SIZE # Go to the next PTE entry ptecount += 1 # Go to the next Subsection in the single-linked list subsection = subsection.NextSubsection
def determine_tcpip_version(cls, context: interfaces.context.ContextInterface, layer_name: str, nt_symbol_table: str) -> str: """Tries to determine which symbol filename to use for the image's tcpip driver. The logic is partially taken from the info plugin. Args: context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate nt_symbol_table: The name of the table containing the kernel symbols Returns: The filename of the symbol table to use. """ # while the failsafe way to determine the version of tcpip.sys would be to # extract the driver and parse its PE header containing the versionstring, # unfortunately that header is not guaranteed to persist within memory. # therefore we determine the version based on the kernel version as testing # with several windows versions has showed this to work out correctly. is_64bit = symbols.symbol_table_is_64bit(context, nt_symbol_table) if is_64bit: arch = "x64" else: arch = "x86" vers = info.Info.get_version_structure(context, layer_name, nt_symbol_table) kuser = info.Info.get_kuser_structure(context, layer_name, nt_symbol_table) try: vers_minor_version = int(vers.MinorVersion) nt_major_version = int(kuser.NtMajorVersion) nt_minor_version = int(kuser.NtMinorVersion) except ValueError: # vers struct exists, but is not an int anymore? raise NotImplementedError( "Kernel Debug Structure version format not supported!") except: # unsure what to raise here. Also, it might be useful to add some kind of fallback, # either to a user-provided version or to another method to determine tcpip.sys's version raise exceptions.VolatilityException( "Kernel Debug Structure missing VERSION/KUSER structure, unable to determine Windows version!" ) vollog.debug("Determined OS Version: {}.{} {}.{}".format( kuser.NtMajorVersion, kuser.NtMinorVersion, vers.MajorVersion, vers.MinorVersion)) if nt_major_version == 10 and arch == "x64": # win10 x64 has an additional class type we have to include. class_types = network.win10_x64_class_types else: # default to general class types class_types = network.class_types # these versions are listed explicitly because symbol files differ based on # version *and* architecture. this is currently the clearest way to show # the differences, even if it introduces a fair bit of redundancy. # furthermore, it is easy to append new versions. if arch == "x86": version_dict = { (6, 0, 6000): "netscan-vista-x86", (6, 0, 6001): "netscan-vista-x86", (6, 0, 6002): "netscan-vista-x86", (6, 0, 6003): "netscan-vista-x86", (6, 1, 7600): "netscan-win7-x86", (6, 1, 7601): "netscan-win7-x86", (6, 1, 8400): "netscan-win7-x86", (6, 2, 9200): "netscan-win8-x86", (6, 3, 9600): "netscan-win81-x86", (10, 0, 10240): "netscan-win10-x86", (10, 0, 10586): "netscan-win10-x86", (10, 0, 14393): "netscan-win10-14393-x86", (10, 0, 15063): "netscan-win10-15063-x86", (10, 0, 16299): "netscan-win10-15063-x86", (10, 0, 17134): "netscan-win10-15063-x86", (10, 0, 17763): "netscan-win10-15063-x86", (10, 0, 18362): "netscan-win10-15063-x86", (10, 0, 18363): "netscan-win10-15063-x86" } else: version_dict = { (6, 0, 6000): "netscan-vista-x64", (6, 0, 6001): "netscan-vista-sp12-x64", (6, 0, 6002): "netscan-vista-sp12-x64", (6, 0, 6003): "netscan-vista-sp12-x64", (6, 1, 7600): "netscan-win7-x64", (6, 1, 7601): "netscan-win7-x64", (6, 1, 8400): "netscan-win7-x64", (6, 2, 9200): "netscan-win8-x64", (6, 3, 9600): "netscan-win81-x64", (10, 0, 10240): "netscan-win10-x64", (10, 0, 10586): "netscan-win10-x64", (10, 0, 14393): "netscan-win10-x64", (10, 0, 15063): "netscan-win10-15063-x64", (10, 0, 16299): "netscan-win10-15063-x64", (10, 0, 17134): "netscan-win10-15063-x64", (10, 0, 17763): "netscan-win10-15063-x64", (10, 0, 18362): "netscan-win10-15063-x64", (10, 0, 18363): "netscan-win10-15063-x64" } # when determining the symbol file we have to consider the following cases: # the determined version's symbol file is found by intermed.create -> proceed # the determined version's symbol file is not found by intermed -> intermed will throw an exc and abort # the determined version has no mapped symbol file -> if win10 use latest, otherwise throw exc # windows version cannot be determined -> throw exc filename = version_dict.get( (nt_major_version, nt_minor_version, vers_minor_version)) if not filename: if nt_major_version == 10: # NtMajorVersion of 10 without a match means a newer version than listed # hence try the latest supported version. If this one throws an error, # support has to be added manually. win_10_versions = sorted( [key for key in list(version_dict.keys()) if key[0] == 10]) # as win10 MinorVersion counts upwards we can take the last entry latest_version = win_10_versions[-1] filename = version_dict.get(latest_version) vollog.debug( "Unable to find exact matching symbol file, going with latest: {}" .format(filename)) else: raise NotImplementedError( "This version of Windows is not supported: {}.{} {}.{}!". format(nt_major_version, nt_minor_version, vers.MajorVersion, vers_minor_version)) vollog.debug("Determined symbol filename: {}".format(filename)) return filename, class_types
def get_object(self, type_name: str, use_top_down: bool, executive: bool = False, native_layer_name: Optional[str] = None) -> Optional[interfaces.objects.ObjectInterface]: """Carve an object or data structure from a kernel pool allocation Args: type_name: the data structure type name native_layer_name: the name of the layer where the data originally lived object_type: the object type (executive kernel objects only) Returns: An object as found from a POOL_HEADER """ symbol_table_name = self.vol.type_name.split(constants.BANG)[0] if constants.BANG in type_name: symbol_table_name, type_name = type_name.split(constants.BANG)[0:2] object_header_type = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + "_OBJECT_HEADER") pool_header_size = self.vol.size # if there is no object type, then just instantiate a structure if not executive: mem_object = self._context.object(symbol_table_name + constants.BANG + type_name, layer_name = self.vol.layer_name, offset = self.vol.offset + pool_header_size, native_layer_name = native_layer_name) return mem_object # otherwise we have an executive object in the pool else: if symbols.symbol_table_is_64bit(self._context, symbol_table_name): alignment = 16 else: alignment = 8 # use the top down approach for windows 8 and later if use_top_down: body_offset = object_header_type.relative_child_offset('Body') infomask_offset = object_header_type.relative_child_offset('InfoMask') optional_headers, lengths_of_optional_headers = self._calculate_optional_header_lengths( self._context, symbol_table_name) padding_available = None if 'PADDING_INFO' not in optional_headers else optional_headers.index( 'PADDING_INFO') max_optional_headers_length = sum(lengths_of_optional_headers) # define the starting and ending bounds for the scan start_offset = self.vol.offset + pool_header_size addr_limit = min(max_optional_headers_length, self.BlockSize * alignment) # A single read is better than lots of little one-byte reads. # We're ok padding this, because the byte we'd check would be 0 which would only be valid if there # were no optional headers in the first place (ie, if we read too much for headers that don't exist, # but the bit we could read were valid) infomask_data = self._context.layers[self.vol.layer_name].read(start_offset, addr_limit + infomask_offset, pad = True) # Addr stores the offset to the potential start of the OBJECT_HEADER from just after the POOL_HEADER # It will always be aligned to a particular alignment for addr in range(0, addr_limit, alignment): infomask_value = infomask_data[addr + infomask_offset] padding_present = False optional_headers_length = 0 for i in range(len(lengths_of_optional_headers)): if infomask_value & (1 << i): optional_headers_length += lengths_of_optional_headers[i] if i == padding_available: padding_present = True # PADDING_INFO is a special case (4 bytes that contain the total padding length) padding_length = 0 if padding_present: # Read the four bytes from just before the next optional_headers_length minus the padding_info size # # --------------- # POOL_HEADER # --------------- # # start of PADDING_INFO # --------------- # End of other optional headers # --------------- # OBJECT_HEADER # --------------- if addr - optional_headers_length < 0: continue padding_length = struct.unpack( "<I", infomask_data[addr - optional_headers_length:addr - optional_headers_length + 4])[0] padding_length -= lengths_of_optional_headers[padding_available] # Certain versions of windows have PADDING_INFO lengths that are too long # So we now check that the padding length is at a minimum the right length # and that it doesn't go beyond the entirety of the data if addr - optional_headers_length >= padding_length > addr: continue try: mem_object = self._context.object(symbol_table_name + constants.BANG + type_name, layer_name = self.vol.layer_name, offset = addr + body_offset + start_offset, native_layer_name = native_layer_name) if mem_object.is_valid(): return mem_object except (TypeError, exceptions.InvalidAddressException): pass # use the bottom up approach for windows 7 and earlier else: type_size = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + type_name).size rounded_size = conversion.round(type_size, alignment, up = True) mem_object = self._context.object(symbol_table_name + constants.BANG + type_name, layer_name = self.vol.layer_name, offset = self.vol.offset + self.BlockSize * alignment - rounded_size, native_layer_name = native_layer_name) try: if mem_object.is_valid(): return mem_object except (TypeError, exceptions.InvalidAddressException): return None return None