Package modules :: Package processing :: Module procmemory
[hide private]
[frames] | no frames]

Source Code for Module modules.processing.procmemory

  1  # Copyright (C) 2010-2013 Claudio Guarnieri. 
  2  # Copyright (C) 2014-2016 Cuckoo Foundation. 
  3  # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org 
  4  # See the file 'docs/LICENSE' for copying permission. 
  5   
  6  import hashlib 
  7  import logging 
  8  import os 
  9  import re 
 10  import struct 
 11   
 12  from lib.cuckoo.common.abstracts import Processing 
 13  from lib.cuckoo.common.objects import File 
 14   
 15  try: 
 16      import pefile 
 17      HAVE_PEFILE = True 
 18  except ImportError: 
 19      HAVE_PEFILE = False 
 20   
 21  log = logging.getLogger(__name__) 
 22   
 23  PAGE_READONLY = 0x00000002 
 24  PAGE_READWRITE = 0x00000004 
 25  PAGE_WRITECOPY = 0x00000008 
 26  PAGE_EXECUTE = 0x00000010 
 27  PAGE_EXECUTE_READ = 0x00000020 
 28  PAGE_EXECUTE_READWRITE = 0x00000040 
 29  PAGE_EXECUTE_WRITECOPY = 0x00000080 
 30   
 31  page_access = { 
 32      PAGE_READONLY: "r", 
 33      PAGE_READWRITE: "rw", 
 34      PAGE_WRITECOPY: "rwc", 
 35      PAGE_EXECUTE: "rx", 
 36      PAGE_EXECUTE_READ: "rx", 
 37      PAGE_EXECUTE_READWRITE: "rwx", 
 38      PAGE_EXECUTE_WRITECOPY: "rwxc", 
 39  } 
 40   
41 -class ProcessMemory(Processing):
42 """Analyze process memory dumps."""
43 - def read_dump(self, filepath):
44 f = open(filepath, "rb") 45 46 while True: 47 buf = f.read(24) 48 if not buf: 49 break 50 51 row = struct.unpack("QIIII", buf) 52 addr, size, state, typ, protect = row 53 54 yield { 55 "addr": "0x%08x" % addr, 56 "end": "0x%08x" % (addr + size), 57 "size": size, 58 "type": typ, 59 "protect": page_access.get(protect), 60 "offset": f.tell(), 61 } 62 63 f.seek(size, 1)
64
65 - def create_idapy(self, process):
66 i = open(process["file"], "rb") 67 o = open(process["file"].replace(".dmp", ".py"), "wb") 68 69 print>>o, "from idaapi import add_segm, mem2base, autoMark, AU_CODE" 70 print>>o, "from idaapi import set_processor_type, SETPROC_ALL" 71 print>>o, "set_processor_type('80386r', SETPROC_ALL)" 72 73 for idx, region in enumerate(process["regions"]): 74 i.seek(region["offset"]) 75 76 if not region["protect"]: 77 section = "unk_%d" % idx 78 type_ = "DATA" 79 elif "x" in region["protect"]: 80 section = "text_%d" % idx 81 type_ = "CODE" 82 elif "w" in region["protect"]: 83 section = "data_%d" % idx 84 type_ = "DATA" 85 else: 86 section = "rdata_%d" % idx 87 type_ = "DATA" 88 89 print>>o, "add_segm(0, %s, %s, '%s', '%s')" % ( 90 region["addr"], region["end"], section, type_ 91 ) 92 print>>o, "mem2base('%s'.decode('base64'), %s)" % ( 93 i.read(region["size"]).encode("base64").replace("\n", ""), 94 region["addr"] 95 ) 96 if type_ == "CODE": 97 print>>o, "autoMark(%s, AU_CODE)" % region["addr"]
98
99 - def _fixup_pe_header(self, pe):
100 """Fixes the PE header from an in-memory representation to an 101 on-disk representation.""" 102 for section in pe.sections: 103 section.PointerToRawData = section.VirtualAddress 104 section.SizeOfRawData = max( 105 section.Misc_VirtualSize, section.SizeOfRawData 106 ) 107 108 reloc = pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_BASERELOC"] 109 if len(pe.OPTIONAL_HEADER.DATA_DIRECTORY) < reloc: 110 return 111 112 reloc = pe.OPTIONAL_HEADER.DATA_DIRECTORY[reloc] 113 if not reloc.VirtualAddress or not reloc.Size: 114 return 115 116 # Disable relocations as those have already been applied. 117 reloc.VirtualAddress = reloc.Size = 0 118 pe.FILE_HEADER.Characteristics |= \ 119 pefile.IMAGE_CHARACTERISTICS["IMAGE_FILE_RELOCS_STRIPPED"]
120
121 - def dump_images(self, process, drop_dlls=False):
122 """Dump executable images from this process memory dump.""" 123 buf = open(process["file"], "rb").read() 124 125 images, capture, regions, end, pe = [], False, [], None, None 126 for r in process["regions"]: 127 off, size = r["offset"], r["size"] 128 129 if capture: 130 if int(r["end"], 16) > end: 131 images.append((pe, regions)) 132 capture = False 133 else: 134 regions.append(r) 135 continue 136 137 # We're going to take a couple of assumptions for granted here. 138 # Namely, the PE header is fully intact, has not been tampered 139 # with, and the DOS header, the NT header, and the Optional header 140 # all remain in the first page/chunk of this PE file. 141 if buf[off:off+2] != "MZ": 142 continue 143 144 try: 145 pe = pefile.PE(data=buf[off:off+size], fast_load=True) 146 except pefile.PEFormatError: 147 continue 148 149 # Enable the capture of memory regions. 150 capture, regions = True, [r] 151 end = int(r["addr"], 16) + pe.OPTIONAL_HEADER.SizeOfImage 152 153 # If present, also process the last loaded executable. 154 if capture and regions: 155 images.append((pe, regions)) 156 157 for pe, regions in images: 158 img = [] 159 160 # Skip DLLs if requested to do so (the default). 161 if pe.is_dll() and not drop_dlls: 162 continue 163 164 self._fixup_pe_header(pe) 165 166 img.append(str(pe.write())) 167 for r in regions: 168 img.append(buf[r["offset"]:r["offset"]+r["size"]]) 169 170 sha1 = hashlib.sha1("".join(img)).hexdigest() 171 172 if pe.is_dll(): 173 filename = "%s-%s.dll_" % (process["pid"], sha1[:16]) 174 elif pe.is_exe(): 175 filename = "%s-%s.exe_" % (process["pid"], sha1[:16]) 176 else: 177 log.warning( 178 "Unknown injected executable for pid=%s", process["pid"] 179 ) 180 continue 181 182 filepath = os.path.join(self.pmemory_path, filename) 183 open(filepath, "wb").write("".join(img)) 184 185 yield File(filepath).get_all()
186
187 - def run(self):
188 """Run analysis. 189 @return: structured results. 190 """ 191 self.key = "procmemory" 192 results = [] 193 194 if self.options.get("extract_img") and not HAVE_PEFILE: 195 log.warning( 196 "In order to extract PE files from memory dumps it is " 197 "required to have pefile installed (`pip install pefile`)." 198 ) 199 200 if os.path.exists(self.pmemory_path): 201 for dmp in os.listdir(self.pmemory_path): 202 if not dmp.endswith(".dmp"): 203 continue 204 205 dump_path = os.path.join(self.pmemory_path, dmp) 206 dump_file = File(dump_path) 207 208 pid, num = map(int, re.findall("(\\d+)", dmp)) 209 210 proc = dict( 211 file=dump_path, pid=pid, num=num, 212 yara=dump_file.get_yara("memory"), 213 urls=list(dump_file.get_urls()), 214 regions=list(self.read_dump(dump_path)), 215 ) 216 217 if self.options.get("idapro"): 218 self.create_idapy(proc) 219 220 if self.options.get("extract_img") and HAVE_PEFILE: 221 proc["extracted"] = list(self.dump_images(proc)) 222 223 if self.options.get("dump_delete"): 224 try: 225 os.remove(dump_path) 226 except OSError: 227 log.error("Unable to delete memory dump file at path \"%s\"", dump_path) 228 229 results.append(proc) 230 231 results.sort(key=lambda x: (x["pid"], x["num"])) 232 return results
233