def _method_offset(self, context: interfaces.context.ContextInterface, vlayer: layers.intel.Intel, pattern: bytes, result_offset: int, progress_callback: constants.ProgressCallback = None) -> Optional[ValidKernelType]: """Method for finding a suitable kernel offset based on a module table.""" vollog.debug("Kernel base determination - searching layer module list structure") valid_kernel: Optional[ValidKernelType] = None # If we're here, chances are high we're in a Win10 x64 image with kernel base randomization physical_layer_name = self.get_physical_layer_name(context, vlayer) physical_layer = context.layers[physical_layer_name] # TODO: On older windows, this might be \WINDOWS\system32\nt rather than \SystemRoot\system32\nt results = physical_layer.scan(context, scanners.BytesScanner(pattern), progress_callback = progress_callback) seen: Set[int] = set() # Because this will launch a scan of the virtual layer, we want to be careful for result in results: # TODO: Identify the specific structure we're finding and document this a bit better pointer = context.object("pdbscan!unsigned long long", offset = (result + result_offset), layer_name = physical_layer_name) address = pointer & vlayer.address_mask if address in seen: continue seen.add(address) valid_kernel = self.check_kernel_offset(context, vlayer, address, progress_callback) if valid_kernel: break return valid_kernel
def _generator(self, tasks): vmlinux = self.context.modules[self.config["kernel"]] is_32bit = not symbols.symbol_table_is_64bit(self.context, vmlinux.symbol_table_name) 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.comm) if task_name not in ["bash", "sh", "dash"]: continue proc_layer_name = task.add_process_layer() if not proc_layer_name: 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(heap_only=True)): bang_addrs.append(struct.pack(pack_format, address)) history_entries = [] if bang_addrs: for address, _ in proc_layer.scan( self.context, scanners.MultiStringScanner(bang_addrs), sections=task.get_process_memory_sections( heap_only=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, (task.pid, task_name, hist.get_time_object(), hist.get_command()))
def _find_csystems_with_scanning( self, proc_layer_name: str, cryptdll_types: interfaces.context.ModuleInterface, cryptdll_base: int, cryptdll_size: int) -> List[interfaces.context.ModuleInterface]: """ Performs scanning to find potential RC4 HMAC csystem instances This function may return several values as it cannot validate which is the active one Args: proc_layer_name: the lsass.exe process layer name cryptdll_types: the types from cryptdll binary analysis cryptdll_base: base address of cryptdll.dll inside of lsass.exe cryptdll_size: size of the VAD Returns: A list of csystem instances """ csystems = [] cryptdll_end = cryptdll_base + cryptdll_size proc_layer = self.context.layers[proc_layer_name] ecrypt_size = cryptdll_types.get_type("_KERB_ECRYPT").size # scan for potential instances of RC4 HMAC # the signature is based on the type being 0x17 # and the block size member being 1 in all test samples for address in proc_layer.scan( self.context, scanners.BytesScanner(b"\x17\x00\x00\x00\x01\x00\x00\x00"), sections=[(cryptdll_base, cryptdll_size)]): # this occurs across page boundaries if not proc_layer.is_valid(address, ecrypt_size): continue kerb = cryptdll_types.object("_KERB_ECRYPT", offset=address, absolute=True) # ensure the Encrypt and Finish pointers are inside the VAD # these are not manipulated in the attack if (cryptdll_base < kerb.Encrypt < cryptdll_end) and \ (cryptdll_base < kerb.Finish < cryptdll_end): csystems.append(kerb) return csystems
def find_version_info( cls, context: interfaces.context.ContextInterface, layer_name: str, filename: str) -> Optional[Tuple[int, int, int, int]]: """Searches for an original filename, then tracks back to find the VS_VERSION_INFO and read the fixed version information structure""" premable_max_distance = 0x500 filename = "OriginalFilename\x00" + filename iterator = context.layers[layer_name].scan( context=context, scanner=scanners.BytesScanner(bytes(filename, 'utf-16be'))) for offset in iterator: data = context.layers[layer_name].read( offset - premable_max_distance, premable_max_distance) vs_ver_info = b"\xbd\x04\xef\xfe" verinfo_offset = data.find(vs_ver_info) + len(vs_ver_info) if verinfo_offset >= 0: structure = '<IHHHHHHHH' struct_version, FV2, FV1, FV4, FV3, PV2, PV1, PV4, PV3 = struct.unpack( structure, data[verinfo_offset:verinfo_offset + struct.calcsize(structure)]) return (FV1, FV2, FV3, FV4) return None
def _generator(self): service_table_name = self.create_service_table( self.context, self.config["nt_symbols"], self.config_path) relative_tag_offset = self.context.symbol_space.get_type( service_table_name + constants.BANG + "_SERVICE_RECORD").relative_child_offset("Tag") filter_func = pslist.PsList.create_name_filter(["services.exe"]) is_vista_or_later = versions.is_vista_or_later( context=self.context, symbol_table=self.config["nt_symbols"]) if is_vista_or_later: service_tag = b"serH" else: service_tag = b"sErv" seen = [] for task in pslist.PsList.list_processes( context=self.context, layer_name=self.config['primary'], symbol_table=self.config['nt_symbols'], filter_func=filter_func): proc_id = "Unknown" try: proc_id = task.UniqueProcessId proc_layer_name = task.add_process_layer() except exceptions.InvalidAddressException as excp: vollog.debug( "Process {}: invalid address {} in layer {}".format( proc_id, excp.invalid_address, excp.layer_name)) continue layer = self.context.layers[proc_layer_name] for offset in layer.scan( context=self.context, scanner=scanners.BytesScanner(needle=service_tag), sections=vadyarascan.VadYaraScan.get_vad_maps(task)): if not is_vista_or_later: service_record = self.context.object( service_table_name + constants.BANG + "_SERVICE_RECORD", offset=offset - relative_tag_offset, layer_name=proc_layer_name) if not service_record.is_valid(): continue yield (0, self.get_record_tuple(service_record)) else: service_header = self.context.object( service_table_name + constants.BANG + "_SERVICE_HEADER", offset=offset, layer_name=proc_layer_name) if not service_header.is_valid(): continue # since we walk the s-list backwards, if we've seen # an object, then we've also seen all objects that # exist before it, thus we can break at that time. for service_record in service_header.ServiceRecord.traverse( ): if service_record in seen: break seen.append(service_record) yield (0, self.get_record_tuple(service_record))