1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """This module implements dynamic profiles.
24
25 A Dynamic profile is a way of discovering certain parameters via running a
26 matching signature.
27 """
28 from rekall import obj
29 from rekall.plugins.tools import disassembler
30
31
33 """A matching engine for disassembler rules.
34
35 This matcher searcher for a sequence of rules in a disassmbly and tries to
36 match a certain rule pattern to the assembly. Ultimately if the rules match,
37 the rules may extract certain parameters from the patter.
38 """
39
40 - def __init__(self, name="", mode="AMD64", rules=None, session=None,
41 max_separation=10):
48
50 """Checks that capture variables are consistent in the vector.
51
52 The vector is a list of disassembly lines which match the rules, e.g.
53
54 [16, 60, 61]
55
56 The context is the capture variables from these rules. In order
57 to be valid, the capture variables must all be consistent. For
58 example the following is not consistent (since var1 is RAX in
59 the first rule and RCX in the second rule):
60
61 contexts[16]
62 {'var1': u'RAX'}
63
64 contexts[60]
65 {'var1': u'RCX', 'out': u'0x88'}
66
67 contexts[61]
68 {}
69 """
70 result = {}
71 for rule_number, item in enumerate(vector):
72 rule_context = contexts[rule_number]
73
74 rule_capture_vars_values = {}
75
76 for k, v in rule_context[item].iteritems():
77 var_name = k.rsplit("_", 1)[0]
78
79
80 if not var_name.startswith("$"):
81 continue
82
83
84
85 if var_name in result and v != result[var_name]:
86 return
87
88
89
90
91
92
93
94
95
96 if (v in rule_capture_vars_values and
97 rule_capture_vars_values[v] != var_name):
98 return
99
100 result[var_name] = v
101 rule_capture_vars_values[v] = var_name
102
103 return result
104
106 """Generate all rules that match the current instruction."""
107 for i, rule in enumerate(self.rules):
108 context = dict(
109 instruction=instruction.text, offset=instruction.address)
110 if instruction.match_rule(rule, context):
111 yield i, context
112
114 """Generate possible hit vectors which match the rules."""
115 for item in hits.get(level, []):
116 if vector:
117 if item < vector[-1]:
118 continue
119
120 if item > self.max_separation + vector[-1]:
121 break
122
123 new_vector = vector + [item]
124
125 if level + 1 == len(hits):
126 yield new_vector
127
128 elif level + 1 < len(hits):
129 for result in self.GenerateVector(hits, new_vector, level+1):
130 yield result
131
133 """Find the first vector that matches all the criteria."""
134 for vector in self.GenerateVector(hits, [], 0):
135 context = self._CheckCaptureVariables(vector, contexts)
136 if not context:
137 continue
138
139 return (vector, context)
140
141 return [], {}
142
144 return self.Match(
145 func.obj_offset, func.obj_vm.read(func.obj_offset, length))
146
147 - def Match(self, offset=0, data=""):
148 hits = {}
149 contexts = {}
150
151 for hit, instruction in enumerate(self.dis.disassemble(data, offset)):
152 for rule_idx, context in self._FindRuleIndex(instruction):
153 hits.setdefault(rule_idx, []).append(hit)
154 contexts.setdefault(rule_idx, {})[hit] = context
155
156
157 if len(hits) < len(self.rules):
158 self.session.logging.error("Failed to find match for %s", self.name)
159
160
161 for i, rule in enumerate(self.rules):
162 if i not in hits:
163 self.session.logging.debug("Unable to match rule: %s", rule)
164
165 return obj.NoneObject()
166
167 vector, context = self._GetMatch(hits, contexts)
168
169 if len(vector) < len(self.rules):
170 self.session.logging.error(
171 "Failed to find match for %s - Only matched %s/%s rules.",
172 self.name, len(vector), len(self.rules))
173 return obj.NoneObject()
174
175 self.session.logging.debug("Found match for %s", self.name)
176
177 result = {}
178 for i, hit in enumerate(vector):
179 hit_data = contexts[i][hit]
180 result.update(hit_data)
181 self.session.logging.debug(
182 "%#x %s", hit_data["offset"], hit_data["instruction"])
183
184 return result
185
186
188 """Search for the value of global constants using disassembly."""
189
190 - def __init__(self, session, profile, name, args):
200
213
214
216 """Try a list of callables until one works."""
217 - def __init__(self, list_of_callables, **kwargs):
218 self.list_of_callables = list_of_callables
219 self.kwargs = kwargs
220
222 for func in self.list_of_callables:
223 result = func(*args, **self.kwargs)
224 if result != None:
225 return result
226
227
229 """Produce a callable for a constant."""
230 name = "$DYNAMIC_CONSTANTS"
231
256
257
259 """Match a struct based on rules."""
260
261 - def __call__(self, struct, member=None):
262 resolver = struct.obj_session.address_resolver
263 func = struct.obj_profile.Function(resolver.get_address_by_name(
264 self.start_address))
265
266 matcher = DisassembleMatcher(
267 mode=func.mode, rules=self.rules, name=self.name,
268 max_separation=self.args.get("max_separation", 10),
269 session=struct.obj_session)
270
271 struct.obj_session.logging.info(
272 "DisassembleStructMatcher: %s %s", self.name,
273 self.args.get("comment", ""))
274 result = matcher.MatchFunction(func)
275 if result:
276
277 overlay = {self.name: [None, {}]}
278 fields = overlay[self.name][1]
279 for field, field_args in self.args["fields"].iteritems():
280 fields[field] = [result["$" + field], field_args]
281
282
283 if member not in fields:
284 return
285
286
287
288
289 struct.obj_profile.add_types(overlay)
290
291
292
293 struct.members.update(struct.obj_profile.Object(self.name).members)
294
295
296 return struct.m(member)
297
298
300 """Produce a callable for a constant."""
301 name = "$DYNAMIC_STRUCTS"
302
304 """Parse the constants detectors and make callables."""
305 overlay = {}
306 for struct_name, signatures in data.items():
307 detectors = {}
308
309
310 for rule in signatures:
311 detector_name = rule["type"]
312 detector_arg = rule["args"]
313
314
315 if detector_name != "DisassembleStructMatcher":
316 session.logging.error(
317 "Unimplemented detector %s", detector_name)
318 continue
319
320 detector = DisassembleStructMatcher(
321 None, None, struct_name, detector_arg)
322
323
324
325
326
327
328 def PassThrough(struct, member=None):
329 return struct.m(member)
330
331 for field in detector_arg["fields"]:
332 detectors.setdefault(field, [PassThrough]).append(detector)
333
334
335 overlay[struct_name] = [None, {}]
336 for field in detectors:
337 overlay[struct_name][1][field] = FirstOf(
338 detectors[field], member=field)
339
340 profile.add_overlay(overlay)
341 return profile
342