1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18  """Miscelaneous information gathering plugins.""" 
 19   
 20  __author__ = "Michael Cohen <scudette@google.com>" 
 21  import hashlib 
 22   
 23   
 24  from rekall import obj 
 25  from rekall.plugins import core 
 26  from rekall.plugins.overlays import basic 
 27  from rekall.plugins.windows import common 
 28  from rekall_lib import utils 
 32      """Prints the boot physical memory map.""" 
 33   
 34      __name = "phys_map" 
 35   
 36      table_header = [ 
 37          dict(name="phys_start", style="address"), 
 38          dict(name="phys_end", style="address"), 
 39          dict(name="pages"), 
 40      ] 
 41   
 43          descriptor = self.profile.get_constant_object( 
 44              "MmPhysicalMemoryBlock", 
 45              target="Pointer", 
 46              target_args=dict( 
 47                  target="_PHYSICAL_MEMORY_DESCRIPTOR", 
 48                  )) 
 49   
 50          for memory_range in descriptor.Run: 
 51              yield (memory_range.BasePage * 0x1000, 
 52                     (memory_range.BasePage + memory_range.PageCount) * 0x1000, 
 53                     memory_range.PageCount) 
   54   
 55   
 56 -class WindowsSetProcessContext(core.SetProcessContextMixin, 
 57                                 common.WinProcessFilter): 
  58      """A cc plugin for windows.""" 
  59   
 62      """Prints the Windows Kernel Virtual Address Map. 
 63   
 64      On 32 bit windows, the kernel virtual address space can be managed 
 65      dynamically. This plugin shows each region and what it is used for. 
 66   
 67      Note that on 64 bit windows the address space is large enough to not worry 
 68      about it. In that case, the offsets and regions are hard coded. 
 69   
 70      http://www.woodmann.com/forum/entry.php?219-Using-nt!_MiSystemVaType-to-navigate-dynamic-kernel-address-space-in-Windows7 
 71      """ 
 72   
 73      __name = "virt_map" 
 74   
 75      table_header = [ 
 76          dict(name="virt_start", style="address"), 
 77          dict(name="virt_end", style="address"), 
 78          dict(name="type", width=10), 
 79      ] 
 80   
 81      @classmethod 
 85   
 87          system_va_table = self.profile.get_constant_object( 
 88              "MiSystemVaType", 
 89              target="Array", 
 90              target_args=dict( 
 91                  target="Enumeration", 
 92                  target_args=dict( 
 93                      target="byte", 
 94                      enum_name="_MI_SYSTEM_VA_TYPE" 
 95                      ), 
 96                  ) 
 97              ) 
 98   
 99          system_range_start = self.profile.get_constant_object( 
100              "MiSystemRangeStart", "unsigned int") 
101   
102           
103          va_table_size = 0x1000 * 0x1000 / self.profile.get_obj_size("_MMPTE") 
104   
105           
106          range_type = range_start = range_length = 0 
107   
108          for offset in range(system_range_start, 0xffffffff, va_table_size): 
109              table_index = (offset - system_range_start) / va_table_size 
110              page_type = system_va_table[table_index] 
111              if page_type != range_type: 
112                  if range_type: 
113                      yield (range_start, range_start + range_length, range_type) 
114   
115                  range_type = page_type 
116                  range_start = offset 
117                  range_length = va_table_size 
118              else: 
119                  range_length += va_table_size 
  120   
121   
122 -class Objects(common.WindowsCommandPlugin): 
 123      """Displays all object Types on the system.""" 
124   
125      name = "object_types" 
126   
127      table_header = [ 
128          dict(name="type", style="address"), 
129          dict(name="index", align="r", width=5), 
130          dict(name="NumberOfObjects", align="r", width=15), 
131          dict(name="PoolType", width=20), 
132          dict(name="name") 
133      ] 
134   
136           
137           
138           
139          type_table = self.profile.get_constant_object( 
140              "ObpObjectTypes", 
141              target="Array", target_args=dict( 
142                  target="Pointer", 
143                  count=0, 
144                  target_args=dict( 
145                      target="_OBJECT_TYPE") 
146                  ) 
147              ) 
148   
149          type_type = type_table[0]   
150          type_table.count = type_type.TotalNumberOfObjects 
151   
152          for t in type_table: 
153              if t: 
154                  yield t 
 155   
157          for obj_type in self.object_types(): 
158              yield dict(type=obj_type, 
159                         index=obj_type.Index, 
160                         NumberOfObjects=obj_type.TotalNumberOfObjects, 
161                         PoolType=obj_type.TypeInfo.PoolType, 
162                         name=obj_type.Name) 
  163   
164   
165 -class ImageInfo(common.WindowsCommandPlugin): 
 166      """List overview information about this image.""" 
167   
168      name = "imageinfo" 
169   
170      table_header = [ 
171          dict(name="key", width=20), 
172          dict(name="value") 
173      ] 
174   
175      @staticmethod 
177          """Return the time of each tick (float). 
178   
179          dis "nt!KeQueryTimeIncrement" 
180          ------ nt!KeQueryTimeIncrement ------ 
181          MOV EAX, [RIP+0x24af66]        0x26161 nt!KeMaximumIncrement 
182          RET 
183          """ 
184          return profile.get_constant_object( 
185              "KeMaximumIncrement", target="unsigned int") * 100e-9 
 186   
188          """Returns the number of seconds since boot. 
189          Ref: 
190          KeQueryTickCount * KeQueryTimeIncrement 
191   
192          reactos/include/ddk/wdm.h: 
193   
194          #define SharedTickCount         (KI_USER_SHARED_DATA + 0x320) 
195   
196          #define KeQueryTickCount(CurrentCount) \ 
197            *(ULONG64*)(CurrentCount) = *(volatile ULONG64*)SharedTickCount 
198          """ 
199          current_tick_count = ( 
200              int(kuser_shared.TickCountQuad) or   
201              int(kuser_shared.TickCountLow))      
202   
203          return current_tick_count * self.KeQueryTimeIncrement(self.profile) 
 204   
206          yield ("Kernel DTB", "%#x" % self.kernel_address_space.dtb) 
207   
208          for desc, name, type in ( 
209                  ("NT Build", "NtBuildLab", "String"), 
210                  ("NT Build Ex", "NtBuildLabEx", "String"), 
211                  ("Signed Drivers", "g_CiEnabled", "bool"), 
212              ): 
213   
214              yield dict( 
215                  key=desc, 
216                  value=self.profile.get_constant_object(name, target=type)) 
217   
218           
219          kuser_shared = self.profile.get_constant_object( 
220              "KI_USER_SHARED_DATA", "_KUSER_SHARED_DATA") 
221   
222          yield ("Time (UTC)", kuser_shared.SystemTime) 
223   
224           
225          bias = kuser_shared.TimeZoneBias.cast("long long") / 1e7 
226          yield ("Time (Local)", kuser_shared.SystemTime.display( 
227              utc_shift=-bias)) 
228   
229          yield ("Sec Since Boot", self.GetBootTime(kuser_shared)) 
230          yield ("NtSystemRoot", kuser_shared.NtSystemRoot) 
  231   
234      """Fingerprint the current image. 
235   
236      This parameter tries to get something unique about the image quickly. The 
237      idea is that two different images (even of the same system at different 
238      points in time) will have very different fingerprints. The fingerprint is 
239      used as a key to cache persistent information about the system. 
240   
241      Live systems can not have a stable fingerprint and so return a NoneObject() 
242      here. 
243   
244      We return a list of tuples: 
245         (physical_offset, expected_data) 
246   
247      The list uniquely identifies the image. If one were to read all physical 
248      offsets and find the expected_data at these locations, then we have a very 
249      high level of confidence that the image is unique and matches the 
250      fingerprint. 
251      """ 
252      name = "image_fingerprint" 
253   
255          if not self.session.physical_address_space: 
256              return None 
257   
258          if self.session.physical_address_space.volatile: 
259              return obj.NoneObject("No fingerprint for volatile image.") 
260   
261          result = [] 
262          profile = self.session.profile 
263          phys_as = self.session.physical_address_space 
264   
265          address_space = self.session.GetParameter("default_address_space") 
266   
267          label = profile.get_constant_object("NtBuildLab", "String") 
268          result.append((address_space.vtop(label.obj_offset), label.v())) 
269   
270          label = profile.get_constant_object("NtBuildLabEx", "String") 
271          result.append((address_space.vtop(label.obj_offset), label.v())) 
272   
273          kuser_shared = profile.get_constant_object( 
274              "KI_USER_SHARED_DATA", "_KUSER_SHARED_DATA") 
275   
276          system_time_offset = address_space.vtop( 
277              kuser_shared.SystemTime.obj_offset) 
278   
279          result.append((system_time_offset, phys_as.read(system_time_offset, 8))) 
280   
281          tick_time_offset = address_space.vtop( 
282              kuser_shared.multi_m("TickCountQuad", "TickCountLow").obj_offset) 
283          result.append((tick_time_offset, phys_as.read(tick_time_offset, 8))) 
284   
285           
286          for task in self.session.plugins.pslist().filter_processes(): 
287              name = task.name.cast("String", length=30) 
288              task_name_offset = address_space.vtop(name.obj_offset) 
289   
290               
291               
292               
293              result.append((task_name_offset, name.v())) 
294   
295          return dict( 
296              hash=hashlib.sha1(unicode(result).encode("utf8")).hexdigest(), 
297              tests=result) 
  298   
299   
300 -class ObjectTree(common.WindowsCommandPlugin): 
 301      """Visualize the kernel object tree. 
302   
303      Ref: 
304      http://msdn.microsoft.com/en-us/library/windows/hardware/ff557762(v=vs.85).aspx 
305      """ 
306   
307      name = "object_tree" 
308   
309      __args = [ 
310          dict(name="type_regex", default=".", type="RegEx", 
311               help="Filter the type of objects shown.") 
312      ] 
313   
314      table_header = [ 
315          dict(name="_OBJECT_HEADER", style="address"), 
316          dict(name="type", width=20), 
317          dict(name="name", type="TreeNode"), 
318      ] 
319   
327   
329          """Tries to resolve the path back to something with a drive letter.""" 
330           
331          try: 
332              path = self.ResolveSymlinks(path) 
333              for prefix, drive_letter in self.session.GetParameter( 
334                      "drive_letter_device_map").iteritems(): 
335                  prefix = self.ResolveSymlinks(prefix) 
336                  if path.startswith(prefix): 
337                      return drive_letter + path[len(prefix):] 
338   
339           
340           
341          except KeyError: 
342              return path 
 343   
345          """Takes a path and resolves any intermediate symlinks in it. 
346   
347          Returns: 
348            A direct path to the object. 
349          """ 
350          components = path.split("\\") 
351          return "\\".join(self._parse_path_components(components)) 
 352   
354          node = self.session.GetParameter("object_tree") 
355          new_components = [] 
356   
357          for i, component in enumerate(components): 
358              if not component: 
359                  continue 
360   
361              if component == "??": 
362                  component = "GLOBAL??" 
363   
364              next_node = utils.CaseInsensitiveDictLookup( 
365                  component, node["Children"]) 
366   
367               
368               
369              if next_node is None and i == 0 and component != "GLOBAL??": 
370                  return self._parse_path_components(["GLOBAL??"] + components) 
371   
372              if next_node is None: 
373                  raise KeyError( 
374                      "component %r not found at %s" % ( 
375                          component, "\\".join(new_components))) 
376   
377              elif next_node["type"] == "SymbolicLink": 
378                  object_header = self.session.profile._OBJECT_HEADER( 
379                      next_node["offset"]) 
380   
381                  target = object_header.Object.LinkTarget.v() 
382   
383                   
384                  return self._parse_path_components( 
385                      target.split("\\") + components[i+1:]) 
386   
387              elif next_node["type"] != "Directory": 
388                  return new_components + components[i:] 
389   
390              new_components.append(component) 
391              node = next_node 
392   
393          return new_components 
 394   
396          for obj_header in directory.list(): 
397              if obj_header in seen: 
398                  continue 
399              seen.add(obj_header) 
400   
401              name = unicode(obj_header.NameInfo.Name) 
402              obj_type = str(obj_header.get_object_type()) 
403   
404              if obj_type == "SymbolicLink": 
405                  name += u"-> %s (%s)" % (obj_header.Object.LinkTarget, 
406                                           obj_header.Object.CreationTime) 
407   
408              if self.plugin_args.type_regex.search(obj_type): 
409                  yield dict(_OBJECT_HEADER=obj_header, type=obj_type, 
410                             name=name, depth=depth) 
411   
412              if obj_type == "Directory": 
413                  for x in self._collect_directory( 
414                          obj_header.Object, seen, depth=depth+1): 
415                      yield x 
 416   
418           
419          root = self.GetObjectByName("/") 
420   
421          seen = set() 
422          for x in self._collect_directory(root, seen): 
423              yield x 
  424   
427      """Return current time, as known to the kernel.""" 
428   
429      name = "times" 
430   
431      table_header = [ 
432          dict(name="Times"), 
433      ] 
434   
 449