""" Author: Andrew Case / andrew@dfir.org License: GPLv2 TOOLS PURPOSE: 64-bit Linux Physical Memory Acquistion from Userland NOT FOR PUBLIC RELEASE: This file is not to be distributed publicly until the release of the Art of Memory Forensics is published A cleaned up version of it will be released with the book's materials ACQUISTION ALGORITHM: This script relies on the static virtual mapping of all RAM kept by x64 Linux systems. This mapping is illustrated here: http://lxr.free-electrons.com/source/Documentation/x86/x86_64/mm.txt To reach these mappings we use the /proc/kcore file. This file exposes all of physical memory (including hardware devices) as ELF sections of a core dump file. To acquire memory, the script first parses /proc/iomem and determines ranges of "System RAM". It then parses the sections of /proc/kcore and matches "System RAM" regions to those found in the kcore file. This matching is possible by using the static offset (0xffff880000000000) of the virtual mapping of RAM. See the _find_kcore_sections function for this algorithm Each RAM region found is then written to a LiME formatted file so that it can be immediately analyzed with Volatility. """ import os, sys, struct try: import elftools except: print "pyelftools not found. Please install" exit(1) from elftools.elf.elffile import ELFFile ##### CHANGE THIS TO MAKE DEBUG MESSAGES PRINT DURING ACQUISTION ##### debug = 1 def _print_msg(msg): print msg def _debug_msg(msg): if debug: _print_msg(msg) def _die(msg): _print_msg(msg + " Exiting.") exit(1) def _do_startup_checks(): if sys.platform.find("linux") == -1: die("This script only acquires memory from Linux systems.") if os.getuid() != 0: die("This script must be run as root.") if not os.path.exists("/proc/kcore"): die("/proc/kcore not enabled on this system.") def _parse_proc_iomem(): ranges = {} ram_regions = [x for x in open("/proc/iomem", "r").readlines() if x.find("System RAM") != -1] # 00010000-0009efff : System RAM\n for region in ram_regions: # gets the addresses first = region.split()[0] ents = first.split("-") (start, end) = (int(ents[0], 16), int(ents[1], 16)) _debug_msg("_parse_proc_iomem: Adding range %#x-%#x" % (start, end)) ranges[start + 0xffff880000000000] = (start, end) return ranges def _find_kcore_sections(ram_ranges): sections = [] with open("/proc/kcore", 'rb') as f: elffile = ELFFile(f) for section in elffile.iter_segments(): file_off = section['p_offset'] vaddr = section['p_vaddr'] sz = section['p_memsz'] if not vaddr in ram_ranges: continue # the start address in physical memory of the system from /proc/iomem phys_off = ram_ranges[vaddr][0] _debug_msg("Found RAM region: %.08x %.08x %.08x" % (file_off, vaddr, sz)) sections.append((file_off, phys_off, sz)) return sections def _make_lime_header(phys_off, sz): magic = struct.pack("" % (sys.argv[0])) outfile = sys.argv[1] create_memory_dump(outfile) if __name__ == "__main__": main()