def windows_read_paged_file(pgd, addr, size, page_file_offset, page_file_number): import api # Step 1: Select the page file pagefile_filename = "pagefile.sys" # Step 2: Read the page file at the given offset f = None for fs in api.get_filesystems(): try: f = api.open_guest_path(fs["index"], pagefile_filename) break except: # The file cannot be open on such filesystem pass if not f: raise RuntimeError("Could not read memory from pagefile: file not found") f.seek(page_file_offset) data = f.read(size = size) f.close() # Step 3: Return the data return data
def windows_read_memory_mapped(pgd, addr, size, pte, is_pae, bitness): # Step 1: Traverse the VAD tree for the process with PGD, # and get the VAD that overlaps addr (if any) # Step 2: Check if the VAD has a ControlArea and a FilePointer, # and get the file path. # Step 3: Get Segment (pointed to by ControlArea), and get the pointer # to the first PrototypePTE. # Step 4: Compute offset of address with respect to the beginning # of the VAD, and compute which PrototypePTE corresponds to the address # No need to consider if the PTE points to the Prototype PTE here. # Step 6: Compute the offset in file for such PrototypePTE by looking at the # subsections pointed by the ControlArea. # Step 7: Finally, open the file, read the contents, and return them. import volatility.obj as obj import volatility.win32.tasks as tasks import volatility.plugins.vadinfo as vadinfo from utils import get_addr_space addr_space = get_addr_space(pgd) eprocs = [ t for t in tasks.pslist(addr_space) if t.Pcb.DirectoryTableBase.v() == pgd ] if len(eprocs) != 1: return None task = eprocs[0] vad = None # File name and offset for vad in task.VadRoot.traverse(): if addr >= vad.Start and addr < vad.End: break if vad is None: return None filename = None if vad.ControlArea is not None and vad.FileObject is not None: filename = str(vad.ControlArea.FileObject.FileName) if vad.ControlArea.Segment is None: return None # Compute page offset with respect to Start of the VAD, # and the corresponding prototype Page Table Entry pointed # by the Segment offset_on_vad = addr - vad.Start page_offset_on_vad = (offset_on_vad - (offset_on_vad & 0xFFF)) # Consider 4 KiB pages ppte_index = page_offset_on_vad / 0x1000 if ppte_index >= vad.ControlArea.Segment.TotalNumberOfPtes.v(): return None if bitness == 32 and is_pae: ppte_addr = vad.ControlArea.Segment.PrototypePte.v() + (ppte_index * 8) else: ppte_addr = vad.ControlArea.Segment.PrototypePte.v() + ( ppte_index * addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][0]) # Read Subsections pointed by ControlArea visited_subsections = {} if "Subsection" in vad.members: subsect = vad.Subsection # There is no Subsection pointer in VAD # structure, so we just read after the ControlArea else: subsect = obj.Object( "_SUBSECTION", offset=(vad.ControlArea.v() + addr_space.profile.vtypes["_CONTROL_AREA"][0]), vm=addr_space) file_offset_to_read = None while file_offset_to_read is None and subsect is not None or subsect.v( ) != 0 and subsect.v() not in visited_subsections: visited_subsections.append(subsect.v()) # Get the PPTE address where the Subsection starts, # and compute the virtual address that it corresponds # to. ppte_addr = subsect.SubsectionBase.v() if bitness == 32 and is_pae: ppte_index = (subsect.SubsectionBase.v() - vad.ControlArea.Segment.PrototypePte.v()) / 8 else: ppte_index = (subsect.SubsectionBase.v() - vad.ControlArea.Segment.PrototypePte.v() ) / addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][0] subsection_base = vad.Start + (ppte_index * 0x1000) subsection_size = subsect.PtesInSubsection.v() * 0x1000 subsection_file_offset = subsect.StartingSector.v() * 512 subsection_file_size = vad.Subsection.NumberOfFullSectors.v() * 512 visited_subsections[subsect.v()] = (subsection_base, subsection_size, subsection_file_offset, subsection_file_size) if (addr >= subsection_base) and (addr < (subsection_base + subsection_size)): file_offset_to_read = (addr - subsection_base) + subsection_file_offset subsect = subsect.NextSubsection f = None for fs in api.get_filesystems(): try: f = api.open_guest_path(fs["index"], filename) break except: # The file cannot be open on such filesystem pass if not f: raise RuntimeError( "Could not read memory from pagefile: file not found") print("Reading file %s at offset %x - Size: %x" % (filename, file_offset_to_read, size)) f.seek(file_offset_to_read) data = f.read(size=size) f.close()
def windows_insert_module_internal( p_pid, p_pgd, base, size, fullname, basename, checksum, update_symbols, do_stop = False): from utils import get_addr_space from vmi import add_symbols from vmi import get_symbols from vmi import has_symbols from vmi import Module from vmi import add_module from vmi import get_module from vmi import has_module from api_internal import dispatch_module_load_callback from api_internal import dispatch_module_remove_callback import pefile import api global filesystem global symbol_cache_must_be_saved if fullname.startswith("\\??\\"): fullname = fullname[4:] if fullname.upper().startswith("C:\\"): fullname = fullname[3:] if fullname.upper().startswith("\\SYSTEMROOT"): fullname = "\WINDOWS" + fullname[11:] fullname = fullname.replace("\\", "/") if fullname[-4:].upper() == ".SYS" and not "/" in fullname: fullname = "/WINDOWS/system32/DRIVERS/" + fullname fullname = fullname.lower() mod = Module(base, size, p_pid, p_pgd, checksum, basename, fullname) # First, we try to get the symbols from the cache if fullname != "" and has_symbols(fullname): mod.set_symbols(get_symbols(fullname)) # If we are updating symbols (a simple module retrieval would # not require symbol extraction), and we don't have any # symbols on the cache: elif fullname != "" and update_symbols: pp_debug("Symbols not found in cache, extracting from %s...\n" % fullname) unnamed_function_counter = 0 syms = {} # Here, fetch the file using the sleuthkit, and use # PE file to process it # First select the file system if not selected already if filesystem is None: for fs in api.get_filesystems(): file_list = api.open_guest_path(fs["index"], "") if isinstance(file_list, list) and len(file_list) > 0: if "windows" in [f.lower() for f in file_list]: filesystem = fs if filesystem is not None: # Try to read the file f = None try: f = api.open_guest_path(filesystem["index"], fullname) except Exception as e: pp_error("%s - %s\n" % (str(e), fullname)) if f is not None: data = f.read() pe = pefile.PE(data=data) if hasattr(pe, "DIRECTORY_ENTRY_EXPORT"): for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols: if exp.name is not None: syms[exp.name] = exp.address else: syms["unnamed_funcion_%d" % unnamed_function_counter] = exp.address unnamed_function_counter += 1 add_symbols(fullname, syms) mod.set_symbols(syms) # Even if it is empty, the module symbols are set # to an empty list, and thus are 'resolved'. # Anyway, in future updates, they could be resolved, # as we allow this in the first condition. symbol_cache_must_be_saved = True #Module load/del notification if has_module(p_pid, p_pgd, base): ex_mod = get_module(p_pid, p_pgd, base) # Module replacement, only if it is a different module, and also # take into consideration wow64 redirection. Never substitute the # wow64 version by the system32 version of the same dll if (ex_mod.get_fullname().lower() != fullname.lower()) and not ((ex_mod.get_name().lower() == basename.lower()) and ("windows/syswow64".lower() in ex_mod.get_fullname().lower()) and ("windows/system32" in fullname.lower())): # Notify of module deletion and module load dispatch_module_remove_callback(p_pid, p_pgd, base, ex_mod.get_size(), ex_mod.get_name(), ex_mod.get_fullname()) add_module(p_pid, p_pgd, base, mod) mod.set_present() dispatch_module_load_callback(p_pid, p_pgd, base, size, basename, fullname) # If we updated the symbols and have a bigger list now, dont substitute the module # but update its symbols instead elif len(mod.get_symbols()) > len(ex_mod.get_symbols()): ex_mod.set_symbols(mod.get_symbols()) # In any case, mark as present ex_mod.set_present() else: # Just notify of module load add_module(p_pid, p_pgd, base, mod) # Mark the module as present mod.set_present() dispatch_module_load_callback(p_pid, p_pgd, base, size, basename, fullname)
def windows_insert_module_internal(p_pid, p_pgd, base, size, fullname, basename, checksum, update_symbols): from utils import get_addr_space from vmi import modules from vmi import symbols from vmi import Module from api_internal import dispatch_module_load_callback from api_internal import dispatch_module_remove_callback import pefile import api global filesystem global symbol_cache_must_be_saved if fullname.startswith("\\??\\"): fullname = fullname[4:] if fullname.upper().startswith("C:\\"): fullname = fullname[3:] if fullname.upper().startswith("\\SYSTEMROOT"): fullname = "\WINDOWS" + fullname[11:] fullname = fullname.replace("\\", "/") if fullname[-4:].upper() == ".SYS" and not "/" in fullname: fullname = "/WINDOWS/system32/DRIVERS/" + fullname fullname = fullname.lower() mod = Module(base, size, p_pid, p_pgd, checksum, basename, fullname) # First, we try to get the symbols from the cache if fullname != "" and fullname in symbols.keys(): mod.set_symbols(symbols[fullname]) # If we are updating symbols (a simple module retrieval would # not require symbol extraction), and we don't have any # symbols on the cache: elif fullname != "" and update_symbols: unnamed_function_counter = 0 syms = {} # Here, fetch the file using the sleuthkit, and use # PE file to process it # First select the file system if not selected already if filesystem is None: for fs in api.get_filesystems(): file_list = api.open_guest_path(fs["index"], "") if isinstance(file_list, list) and len(file_list) > 0: if "windows" in [f.lower() for f in file_list]: filesystem = fs if filesystem is not None: # Try to read the file f = None try: f = api.open_guest_path(filesystem["index"], fullname) except Exception as e: pp_error("%s - %s\n" % (str(e), fullname)) if f is not None: data = f.read() pe = pefile.PE(data=data) if hasattr(pe, "DIRECTORY_ENTRY_EXPORT"): for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols: if exp.name is not None: syms[exp.name] = exp.address else: syms["unnamed_funcion_%d" % unnamed_function_counter] = exp.address unnamed_function_counter += 1 # If we managed to parse the export table, update the symbols # except if it is empty if fullname not in symbols.keys(): symbols[fullname] = syms # Even if it is empty, the module symbols are set # to an empty list, and thus are 'resolved'. # Anyway, in future updates, they could be resolved, # as we allow this in the first condition. mod.set_symbols(symbols[fullname]) symbol_cache_must_be_saved = True else: symbols[fullname] = {} mod.set_symbols(symbols[fullname]) #Module load/del notification if base in modules[(p_pid, p_pgd)]: if modules[(p_pid, p_pgd)][base].get_size() != size or \ modules[(p_pid, p_pgd)][base].get_checksum() != checksum or \ modules[(p_pid, p_pgd)][base].get_name() != basename or \ modules[(p_pid, p_pgd)][base].get_fullname() != fullname: # Notify of module deletion and module load dispatch_module_remove_callback( p_pid, p_pgd, base, modules[(p_pid, p_pgd)][base].get_size(), modules[(p_pid, p_pgd)][base].get_name(), modules[(p_pid, p_pgd)][base].get_fullname()) del modules[(p_pid, p_pgd)][base] modules[(p_pid, p_pgd)][base] = mod dispatch_module_load_callback(p_pid, p_pgd, base, size, basename, fullname) # If we updated the symbols and have a bigger list now, dont substitute the module # but update its symbols instead elif len(mod.get_symbols()) > len( modules[(p_pid, p_pgd)][base].get_symbols()): modules[(p_pid, p_pgd)][base].set_symbols(mod.get_symbols()) else: # Just notify of module load modules[(p_pid, p_pgd)][base] = mod dispatch_module_load_callback(p_pid, p_pgd, base, size, basename, fullname) # Mark the module as present modules[(p_pid, p_pgd)][base].set_present()