1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 __author__ = "Michael Cohen <scudette@gmail.com>"
22 import re
23
24 import acora
25
26 from rekall import addrspace
27 from rekall import constants
28 from rekall_lib import registry
29
30
32 """A scanner check is a special class which is invoked on an AS to check
33 for a specific condition.
34
35 The main method is def check(self, buffer_as, offset):
36 This will return True if the condition is true or False otherwise.
37
38 This class is the base class for all checks.
39 """
40
41 __metaclass__ = registry.MetaclassRegistry
42 __abstract = True
43
44 - def __init__(self, profile=None, address_space=None, session=None,
45 **_kwargs):
50
53
54 - def check(self, buffer_as, offset):
55 """Is the needle found at 'offset'?
56
57 Arguments:
58 buffer_as: An address space object with a chunk of data that can be
59 checked for the needle.
60 offset: The offset in the address space to check.
61 """
62 _ = offset
63 _ = buffer_as
64 return False
65
66 - def skip(self, buffer_as, offset):
67 """Determine how many bytes we can skip.
68
69 If you want to speed up the scanning define this method - it
70 will be used to skip the data which is obviously not going to
71 match. You will need to return the number of bytes from offset
72 to skip to. We take the maximum number of bytes to guarantee
73 that all checks have a chance of passing.
74
75 Args:
76 buffer_as: A BufferAddressSpace instance wrapping self.address_space,
77 containing a copy of the data at the specified offset.
78
79 offset: The offset in the address space to check.
80
81 Returns:
82 Number of bytes to be skipped.
83 """
84 _ = buffer_as
85 _ = offset
86 return 0
87
88
90 """A scanner checker for multiple strings."""
91
92 - def __init__(self, needles=None, **kwargs):
93 """Init.
94
95 Args:
96 needles: A list of strings we search for.
97 **kwargs: passthrough.
98 Raises:
99 RuntimeError: No needles provided.
100 """
101 super(MultiStringFinderCheck, self).__init__(**kwargs)
102
103
104
105 if not needles:
106 raise RuntimeError("No needles provided to search.")
107
108
109
110 if max([len(x) for x in needles]) > 50:
111 raise RuntimeError("Pattern too large to search with ahocorasic.")
112
113 tree = acora.AcoraBuilder(*needles)
114 self.engine = tree.build()
115
116 self.base_offset = None
117 self.hits = None
118
119 - def check(self, buffer_as, offset):
140
141 - def skip(self, buffer_as, offset):
158
159
161 """Checks for a single string."""
162 maxlen = 100
163 needle = None
164 needle_offset = None
165
166 - def __init__(self, needle=None, needle_offset=0, **kwargs):
170
171 - def check(self, buffer_as, offset):
176
177 - def skip(self, buffer_as, offset):
186
187
189 """This check can be quite slow."""
190 maxlen = 100
191
192 - def __init__(self, regex=None, **kwargs):
195
196 - def check(self, buffer_as, offset):
201
202
204 """An object representing padding."""
207
208
214
215 - def pad(self, length):
216 if not self._fragments:
217 self.base_offset += length
218 else:
219 self._fragments.append(_Padding(length))
220 self.total_length += length
221
223 self._fragments.append(data)
224 self.total_length += len(data)
225
227 """Remove padding from the end and materialize any padding."""
228
229 while self._fragments:
230 item = self._fragments[-1]
231 if isinstance(item, _Padding):
232 self._fragments.pop(-1)
233 else:
234 break
235
236
237 expanded_result = []
238 start_index = 0
239 end_index = len(self._fragments)
240
241 for x in xrange(start_index, end_index):
242 item = self._fragments[x]
243 if isinstance(item, _Padding):
244 expanded_result.append(addrspace.ZEROER.GetZeros(item.length))
245 else:
246 expanded_result.append(item)
247
248 return "".join(expanded_result)
249
250
252 """A Generator of contiguous buffers read from the address space."""
266
269
271 """Python 3 protocol."""
272 return self.next()
273
275 """Get the next buffer address space from the generator."""
276
277
278 fragments = _BufferFragments(self.readptr)
279
280
281 readptr = self.readptr
282
283 if self.current_run is None:
284
285
286 self.current_run = next(self._generator)
287
288 while 1:
289
290 if fragments.total_length >= self.buffer_size:
291 break
292
293 if readptr >= self.end:
294 raise StopIteration
295
296
297
298
299
300
301
302 if self.current_run.start > readptr:
303 if fragments.total_length > 0:
304 padding_length = min(
305 self.current_run.start - readptr,
306 self.buffer_size - fragments.total_length)
307 fragments.pad(padding_length)
308 readptr += padding_length
309 else:
310 fragments.pad(self.current_run.start - readptr)
311 readptr = self.current_run.start
312
313
314
315 if self.current_run.start <= readptr < self.current_run.end:
316 phys_chunk_offset = (
317 self.current_run.file_offset + (
318 readptr - self.current_run.start))
319
320
321 chunk_size = min(self.buffer_size - fragments.total_length,
322 self.current_run.end - readptr)
323
324 fragments.append(self.current_run.address_space.read(
325 phys_chunk_offset, chunk_size))
326
327 readptr += chunk_size
328
329
330
331 if self.current_run.end <= readptr:
332 try:
333 self.current_run = next(self._generator)
334 except StopIteration:
335 self.finished = True
336
337
338 break
339
340
341 base_offset = fragments.base_offset
342 data = fragments.materialize()
343
344
345 if self.finished and not data:
346 raise StopIteration
347
348 self.buffer_as.assign_buffer(data, base_offset=base_offset)
349 self.readptr = readptr
350 return self.buffer_as
351
352
354 """Base class for all scanners."""
355
356 __metaclass__ = registry.MetaclassRegistry
357
358 progress_message = "Scanning 0x%(offset)08X with %(name)s"
359
360 checks = ()
361
362 - def __init__(self, profile=None, address_space=None, window_size=8,
363 session=None, checks=None):
385
396
398 """Check an address.
399
400 This calls our constraints on the offset and returns if any contraints
401 did not match.
402
403 Args:
404 offset: The offset to test (in self.address_space).
405
406 Returns:
407 None if the offset is not a hit, the hit if the hit is correct.
408 """
409 for check in self.constraints:
410
411 val = check.check(buffer_as, offset)
412
413
414 if not val:
415 return
416
417 return offset
418
419 - def skip(self, buffer_as, offset):
420 """Skip uninteresting regions.
421
422 Where should we go next? By default we go 1 byte ahead, but if some of
423 the checkers have skippers, we may actually go much farther. Checkers
424 with skippers basically tell us that there is no way they can match
425 anything before the skipped result, so there is no point in trying them
426 on all the data in between. This optimization is useful to really speed
427 things up.
428 """
429 skip = 1
430 for s in self.skippers:
431 skip_value = s.skip(buffer_as, offset)
432 skip = max(skip, skip_value)
433
434 return skip
435
436 overlap = 1024
437
438 - def scan(self, offset=0, maxlen=None, end=None):
439 """Scan the region from offset for maxlen.
440
441 Args:
442 offset: The starting offset in our current address space to scan.
443
444 maxlen: The maximum length to scan. If not provided we just scan until
445 there is no data.
446
447 Yields:
448 offsets where all the constrainst are satisfied.
449 """
450 if end is None:
451 if maxlen is None:
452 raise IOError("Range end must be specified.")
453
454 end = int(offset) + int(maxlen)
455
456
457
458 last_reported_hit = -1
459
460
461
462 if self.constraints is None:
463 self.build_constraints()
464
465 for buffer_as in BufferASGenerator(
466 self.session, self.address_space, offset, end):
467 self.session.report_progress(
468 "Scanning buffer %#x->%#x (%#x)",
469 buffer_as.base_offset, buffer_as.end(),
470 buffer_as.end() - buffer_as.base_offset)
471
472
473 scan_offset = buffer_as.base_offset
474 while scan_offset < buffer_as.end():
475
476 res = self.check_addr(scan_offset, buffer_as=buffer_as)
477
478
479
480 if res is not None and scan_offset > last_reported_hit:
481 last_reported_hit = scan_offset
482 yield res
483
484
485
486 scan_offset += min(len(buffer_as),
487 self.skip(buffer_as, scan_offset))
488
489
491 """This scanner looks for a struct in memory.
492
493 Arguments:
494 expected_values:
495 Provide a list/tuple of dicts mapping member names to their
496 expected values. Each dict in the list you provide will correspond
497 to a struct at the same index in an array. If you're only looking
498 for a single struct, pass a list with only one dict in it.
499 type_name: Name of the type to scan for.
500 """
501
502 type_name = None
503 prototype = None
504 expected_values = None
505
506 - def __init__(self, type_name=None, expected_values=None, *args, **kwargs):
523
525 array_offset = array_idx * self.prototype.obj_size
526 for member, expected_value in struct_members.iteritems():
527 self.prototype.SetMember(member, expected_value)
528 member_obj = self.prototype.m(member)
529 expected_bytes = member_obj.GetData()
530 rel_offset = member_obj.obj_offset
531 yield ("StringCheck", dict(needle=expected_bytes,
532 needle_offset=rel_offset + array_offset))
533
534
536 """A scanner for multiple strings at once."""
537
538
539 needles = []
540
541 - def __init__(self, needles=None, **kwargs):
557
563
564 - def skip(self, buffer_as, offset):
566
567
569 """Scan for a bunch of pointers at the same time.
570
571 This scanner takes advantage of the fact that usually the most significant
572 bytes of a group of pointers is the same. This common part is scanned for
573 first, thereby taking advantage of the scanner skippers.
574 """
575 - def __init__(self, pointers=None, **kwargs):
600
601
603 """Runs a bunch of scanners in one pass over the image."""
604
605 - def __init__(self, scanners=None, **kwargs):
606 """Create a new scanner group.
607
608 Args:
609 scanners: A dict of BaseScanner instances. Keys will be used to refer
610 to the scanner, while the value is the scanner instance.
611 """
612 super(ScannerGroup, self).__init__(**kwargs)
613 self.scanners = scanners
614 for scanner in scanners.values():
615 scanner.address_space = self.address_space
616
617
618 self.result = {}
619
620 - def scan(self, offset=0, maxlen=None):
637
638
640 """A scanner group which works over a virtual address space."""
641
642 - def scan(self, offset=0, maxlen=None):
650
651
653 """A check that breaks into the debugger when a condition is met.
654
655 Insert this check inside the check stack and we will break into the debugger
656 when all the conditions below us are met.
657 """
658 - def check(self, buffer_as, offset):
659 _ = offset
660 _ = buffer_as
661 import pdb; pdb.set_trace()
662 return True
663