1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """The module implements user mode heap analysis.
20
21 Recent versions of windows use the Low Fragmentation Heap (LFH).
22
23 http://illmatics.com/Windows%208%20Heap%20Internals.pdf
24 http://illmatics.com/Understanding_the_LFH.pdf
25 http://www.leviathansecurity.com/blog/understanding-the-windows-allocator-a-redux/
26
27 """
28 from rekall import scan
29
30 from rekall.plugins import core
31 from rekall.plugins.windows import common
32 from rekall_lib import utils
33
34
36 """Inspect the process heap.
37
38 This prints a lot of interesting facts about the process heap. It is also
39 the foundation to many other plugins which find things in the process heaps.
40
41 NOTE: Currently we only support Windows 7 64 bit.
42 """
43
44 name = "inspect_heap"
45
46 __args = [
47 dict(name="free", type="Boolean",
48 help="Also show freed chunks."),
49
50 dict(name="heaps", type="ArrayIntParser",
51 help="Only show these heaps (default show all)")
52 ]
53
54 mode = "mode_amd64"
55
59
61 """Dump the low fragmentation heap."""
62 seen_blocks = set()
63
64 for lfh_block in heap.FrontEndHeap.SubSegmentZones.list_of_type(
65 "_LFH_BLOCK_ZONE", "ListEntry"):
66 block_length = lfh_block.FreePointer.v() - lfh_block.obj_end
67 segments = heap.obj_profile.Array(
68 target="_HEAP_SUBSEGMENT",
69 offset=lfh_block.obj_end,
70 size=block_length)
71
72 for segment in segments:
73 allocation_length = segment.BlockSize * 16
74
75 if segment.UserBlocks.v() in seen_blocks:
76 break
77
78 seen_blocks.add(segment.UserBlocks.v())
79
80 for entry in segment.UserBlocks.Entries:
81
82
83 if skip_freed and entry.UnusedBytes & 0x38:
84 continue
85
86 UnusedBytes = entry.UnusedBytes & 0x3f - 0x8
87
88
89
90
91 data_len = allocation_length - UnusedBytes
92
93
94
95
96
97
98
99 if data_len > allocation_length - 0x8:
100 data_len -= 0x8
101
102 yield (heap.obj_profile.String(entry.obj_end, term=None,
103 length=data_len),
104 allocation_length)
105
107 """Enumerate all allocations for _EPROCESS instance."""
108
109 for seg in heap.Segments:
110 seg_end = seg.LastValidEntry.v()
111
112
113 if seg.Heap.deref() != heap:
114 continue
115
116
117 if seg_end < seg.FirstEntry.v():
118 break
119
120 for entry in seg.FirstEntry.walk_list("NextEntry", True):
121
122
123 start = entry.obj_offset + 0x10
124 if start > seg_end:
125 break
126
127 allocation = entry.Allocation
128 yield allocation
129
148
159
161 """Displays information about the low fragmentation front end."""
162 renderer.format("Low Fragmentation Front End Information:\n")
163 renderer.table_header([
164 dict(name="Entry", style="address"),
165 ("Alloc", "allocation_length", "4"),
166 ("Length", "length", ">4"),
167 dict(name="Data"),
168 ])
169
170
171
172 entries_by_size = {}
173 for entry, allocation_length in self.enumerate_lfh_heap_allocations(
174 heap):
175 entries_by_size.setdefault(allocation_length, []).append(entry)
176
177 for allocation_length, entries in sorted(entries_by_size.iteritems()):
178 for entry in sorted(entries, key=lambda x: x.obj_offset):
179 data = entry.v()[:64]
180
181 renderer.table_row(
182 entry,
183 allocation_length,
184 entry.length,
185 utils.HexDumpedString(data),
186 )
187
189 if (self.plugin_args.heaps and
190 heap.ProcessHeapsListIndex not in self.plugin_args.heaps):
191 return
192
193 if 1 <= heap.ProcessHeapsListIndex <= 64:
194 renderer.format("Heap {0}: {1:#x} ({2})\nBackend Info:\n\n",
195 heap.ProcessHeapsListIndex,
196 heap.BaseAddress,
197 heap.FrontEndHeapType)
198
199 renderer.table_header([
200 dict(name="Segment", type="TreeNode", width=18,
201 child=dict(style="address")),
202 ("End", "segment_end", "[addr]"),
203 ("Length", "length", "8"),
204 dict(name="Data"),
205 ])
206
207 for seg in heap.Segments:
208 seg_start = seg.FirstEntry.obj_offset
209 seg_end = seg.LastValidEntry.v()
210
211 renderer.table_row(
212 seg_start, seg_end, seg_end - seg_start, depth=1)
213
214 for entry in seg.FirstEntry.walk_list("NextEntry", True):
215
216
217 start = entry.obj_offset + 0x10
218 if start > seg_end:
219 break
220
221 if entry.Flags.LAST_ENTRY:
222 end = seg.LastValidEntry.v()
223 else:
224 end = entry.obj_offset + entry.Size * 16
225
226 data = heap.obj_vm.read(start, min(16, end-start))
227
228 renderer.table_row(
229 entry,
230 end, end - start,
231 utils.HexDumpedString(data),
232 depth=2)
233
234
235 if heap.FrontEndHeapType.LOW_FRAG:
236 self.render_low_frag_info(heap, renderer)
237
238
240 """Show the allocation containing the address."""
241
242 name = "show_allocation"
243
244 __args = [
245 dict(name="address", type="ArrayIntParser", positional=True,
246 help="The address to display"),
247
248 dict(name="preamble", type="IntParser", default=32,
249 help="How many bytes prior to the address to display."),
250
251 dict(name="length", type="IntParser", default=50 * 16,
252 help="How many bytes after the address to display.")
253 ]
254
256 """Build a map of all allocations for fast looksup."""
257 allocations = utils.RangedCollection()
258 inspect_heap = self.session.plugins.inspect_heap()
259 for heap in inspect_heap.GenerateHeaps():
260
261 for allocation in inspect_heap.enumerate_backend_heap_allocations(
262 heap):
263
264
265 allocations.insert(
266 allocation.obj_offset - 16,
267 allocation.obj_offset + allocation.length + 16,
268 (allocation.obj_offset, allocation.length, "B"))
269
270 self.session.report_progress(
271 "Enumerating backend allocation: %#x",
272 lambda allocation=allocation: allocation.obj_offset)
273
274
275
276 for _ in inspect_heap.enumerate_lfh_heap_allocations(
277 heap, skip_freed=False):
278 allocation, allocation_length = _
279 self.session.report_progress(
280 "Enumerating frontend allocation: %#x",
281 lambda: allocation.obj_offset)
282
283
284 allocations.insert(
285 allocation.obj_offset,
286 allocation.obj_offset + allocation_length,
287 (allocation.obj_offset, allocation_length, "F"))
288
289 return allocations
290
304
306 return self.allocations.get_containing_range(address)
307
309 address_map = core.AddressMap()
310
311 if alloc_type == "B":
312 address_map.AddRange(alloc_start-16, alloc_start, "_HEAP_ENTRY")
313
314
315 count = length / 8
316 for pointer in self.profile.Array(
317 offset=start, count=count, target="Pointer"):
318 name = None
319 alloc_start, alloc_length, alloc_type = (
320 self.allocations.get_containing_range(pointer.v()))
321
322 if alloc_type is not None:
323
324 if alloc_start == start + 16:
325 name = "+%#x(%#x)" % (pointer.v() - start, pointer.v())
326 else:
327 name = "%#x(%s@%#x)" % (
328 pointer.v(), alloc_length, alloc_start)
329
330 else:
331
332 name = ",".join(self.session.address_resolver.format_address(
333 pointer.v(), max_distance=1024*1024))
334
335
336 if name:
337 address_map.AddRange(
338 pointer.obj_offset, pointer.obj_offset + 8,
339
340
341
342 "%s" % name, color_index=pointer.obj_offset)
343
344 return address_map
345
347 for address in self.plugin_args.address:
348
349
350 if len(self.plugin_args.address) > 1:
351 self.offset = None
352
353 alloc_start, alloc_length, alloc_type = (
354 self.allocations.get_containing_range(address))
355
356 if not alloc_type:
357 renderer.format("Allocation not found for address "
358 "{0:style=address} in any heap.\n", address)
359 alloc_start = address
360 alloc_length = 50 * 16
361 alloc_type = None
362
363 else:
364 renderer.format(
365 "Address {0:style=address} is {1} bytes into "
366 "{2} allocation of size {3} "
367 "({4:style=address} - {5:style=address})\n",
368 address, address - alloc_start, alloc_type,
369 alloc_length, alloc_start, alloc_start + alloc_length)
370
371
372
373
374 if self.offset is None:
375
376
377 start = max(alloc_start, address - self.plugin_args.preamble)
378 else:
379
380 start = self.offset
381
382
383
384 if alloc_type == "B":
385 start -= 16
386
387 length = min(alloc_start + alloc_length - start,
388 self.plugin_args.length)
389
390 dump = self.session.plugins.dump(
391 offset=start, length=length,
392 address_map=self.CreateAllocationMap(
393 start, length, alloc_start, alloc_type))
394
395 dump.render(renderer)
396
397 self.offset = dump.offset
398
399
401 """Show allocations that refer to an address."""
402
403 name = "show_referrer_alloc"
404
405 __args = [
406 dict(name="address", type="IntParser", positional=True, required=True,
407 help="The address to display")
408 ]
409
429
438