1
2
3
4
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
42 """Analyze process memory dumps."""
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
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
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
117 reloc.VirtualAddress = reloc.Size = 0
118 pe.FILE_HEADER.Characteristics |= \
119 pefile.IMAGE_CHARACTERISTICS["IMAGE_FILE_RELOCS_STRIPPED"]
120
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
138
139
140
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
150 capture, regions = True, [r]
151 end = int(r["addr"], 16) + pe.OPTIONAL_HEADER.SizeOfImage
152
153
154 if capture and regions:
155 images.append((pe, regions))
156
157 for pe, regions in images:
158 img = []
159
160
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
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