1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21   
 22   
 23   
 24  """ 
 25  @author:       Andreas Schuster 
 26  @license:      GNU General Public License 2.0 or later 
 27  @contact:      a.schuster@forensikblog.de 
 28  @organization: http://computer.forensikblog.de/en/ 
 29  """ 
 30  from rekall.plugins.windows import common 
 31   
 32   
 34      """PoolScanner for File objects""" 
 36          super(PoolScanFile, self).__init__(**kwargs) 
 37          self.checks = [ 
 38              ('PoolTagCheck', dict( 
 39                  tag=self.profile.get_constant("FILE_POOLTAG"))), 
 40   
 41              ('CheckPoolSize', dict( 
 42                  min_size=self.profile.get_obj_size("_FILE_OBJECT"))), 
 43   
 44              ('CheckPoolType', dict( 
 45                  paged=True, non_paged=True, free=True)), 
 46   
 47              ('CheckPoolIndex', dict(value=0)), 
 48              ] 
   49   
 50   
 51 -class FileScan(common.PoolScannerPlugin): 
  52      """ Scan Physical memory for _FILE_OBJECT pool allocations 
 53      """ 
 54      __name = "filescan" 
 55   
 56      table_header = [ 
 57          dict(name='a', width=1), 
 58          dict(name="offset", style="address"), 
 59          dict(name="ptr_no", width=6, align="r"), 
 60          dict(name="hnd_no", width=3, align="r"), 
 61          dict(name="access", width=6), 
 62          dict(name='Owner', type="_EPROCESS"), 
 63          dict(name="path") 
 64      ] 
 65   
 66      scanner_defaults = dict( 
 67          scan_kernel_nonpaged_pool=True 
 68      ) 
 69   
 71          """Generate possible hits.""" 
 72          for run in self.generate_memory_ranges(): 
 73              scanner = PoolScanFile(profile=self.profile, session=self.session, 
 74                                     address_space=run.address_space) 
 75   
 76              for pool_obj in scanner.scan(run.start, run.length): 
 77                  for object_obj in pool_obj.IterObject("File", freed=True): 
 78                       
 79                      file_obj = self.session.profile._FILE_OBJECT( 
 80                          offset=object_obj.obj_end, vm=run.address_space) 
 81   
 82                      if not file_obj.FileName.v(vm=self.kernel_address_space): 
 83                          continue 
 84   
 85                       
 86                      device_obj = file_obj.DeviceObject.deref( 
 87                          vm=self.session.kernel_address_space) 
 88   
 89                      if not device_obj.DeviceType.is_valid(): 
 90                          continue 
 91   
 92                       
 93                       
 94                      owner_process = ( 
 95                          object_obj.HandleInfo.SingleEntry.Process.deref( 
 96                              vm=self.kernel_address_space)) 
 97   
 98                      filename = file_obj.file_name_with_drive( 
 99                          vm=self.kernel_address_space) 
100   
101                      yield dict(a='F' if pool_obj.FreePool else "", 
102                                 offset=file_obj.obj_offset, 
103                                 ptr_no=object_obj.PointerCount, 
104                                 hnd_no=object_obj.HandleCount, 
105                                 access=file_obj.AccessString, 
106                                 Owner=owner_process, 
107                                 path=filename) 
  108   
109   
111      """ Scanner for _DRIVER_OBJECT """ 
112   
114          super(PoolScanDriver, self).__init__(**kwargs) 
115          self.checks = [ 
116              ('PoolTagCheck', dict( 
117                  tag=self.profile.get_constant("DRIVER_POOLTAG"))), 
118   
119               
120              ('CheckPoolSize', dict( 
121                  condition=lambda x: x > self.profile.get_obj_size( 
122                      "_DRIVER_OBJECT"))), 
123   
124              ('CheckPoolType', dict( 
125                  paged=True, non_paged=True, free=True)), 
126   
127              ('CheckPoolIndex', dict(value=0)), 
128              ] 
  129   
130   
132      "Scan for driver objects _DRIVER_OBJECT " 
133   
134      __name = "driverscan" 
135   
136      table_header = [ 
137          dict(name='a', width=1), 
138          dict(name="offset", style="address"), 
139          dict(name="ptr_no", width=6, align="r"), 
140          dict(name="hnd_no", width=3, align="r"), 
141          dict(name="start", style="address"), 
142          dict(name="size", style="address"), 
143          dict(name="servicekey", width=20), 
144          dict(name="name", width=12), 
145          dict(name="path") 
146      ] 
147   
148      scanner_defaults = dict( 
149          scan_kernel_nonpaged_pool=True 
150      ) 
151   
153          """Generate possible hits.""" 
154          for run in self.generate_memory_ranges(): 
155              scanner = PoolScanDriver(session=self.session, 
156                                       profile=self.profile, 
157                                       address_space=run.address_space) 
158   
159              for pool_obj in scanner.scan(run.start, run.length): 
160                  for object_obj in pool_obj.IterObject("Driver", freed=True): 
161                      object_name = object_obj.NameInfo.Name.v( 
162                          vm=self.kernel_address_space) 
163   
164                      driver_obj = self.profile._DRIVER_OBJECT( 
165                          object_obj.obj_end, vm=run.address_space) 
166   
167                      extension_obj = self.profile._DRIVER_EXTENSION( 
168                          driver_obj.obj_end, vm=run.address_space) 
169   
170                      yield dict(a='F' if pool_obj.FreePool else "", 
171                                 offset=driver_obj.obj_offset, 
172                                 ptr_no=object_obj.PointerCount, 
173                                 hnd_no=object_obj.HandleCount, 
174                                 start=driver_obj.DriverStart, 
175                                 size=driver_obj.DriverSize, 
176                                 servicekey=extension_obj.ServiceKeyName.v( 
177                                     vm=self.kernel_address_space), 
178                                 name=object_name, 
179                                 path=driver_obj.DriverName.v( 
180                                     vm=self.kernel_address_space) 
181                      ) 
  182   
183   
185      """ Scanner for symbolic link objects """ 
187          super(PoolScanSymlink, self).__init__(**kwargs) 
188          self.checks = [ 
189              ('PoolTagCheck', dict( 
190                  tag=self.profile.get_constant("SYMLINK_POOLTAG"))), 
191   
192              ('CheckPoolSize', dict( 
193                  min_size=self.profile.get_obj_size( 
194                      "_OBJECT_SYMBOLIC_LINK"))), 
195   
196              ('CheckPoolType', dict(paged=True, non_paged=True, free=True)), 
197              ] 
  198   
199   
201      "Scan for symbolic link objects " 
202   
203      __name = "symlinkscan" 
204   
205      table_header = [ 
206          dict(name='a', width=1), 
207          dict(name="offset", style="address"), 
208          dict(name="ptr_no", width=6, align="r"), 
209          dict(name="hnd_no", width=3, align="r"), 
210          dict(name="creation_time", width=24), 
211          dict(name="from_link"), 
212          dict(name="to_link", width=60), 
213      ] 
214   
215      scanner_defaults = dict( 
216           
217          scan_kernel_paged_pool=True 
218      ) 
219   
221          """Generate possible hits.""" 
222          for run in self.generate_memory_ranges(): 
223              scanner = PoolScanSymlink(profile=self.profile, 
224                                        session=self.session, 
225                                        address_space=run.address_space) 
226              for pool_obj in scanner.scan(run.start, run.length): 
227                  for object_obj in pool_obj.IterObject( 
228                          "SymbolicLink", freed=True): 
229                      object_name = object_obj.NameInfo.Name.v( 
230                          vm=self.kernel_address_space) 
231   
232                      link_obj = self.profile._OBJECT_SYMBOLIC_LINK( 
233                          object_obj.obj_end, vm=run.address_space) 
234   
235                      yield dict(a='F' if pool_obj.FreePool else "", 
236                                 offset=link_obj.obj_offset, 
237                                 ptr_no=object_obj.PointerCount, 
238                                 hnd_no=object_obj.HandleCount, 
239                                 creation_time=link_obj.CreationTime or '', 
240                                 from_link=object_name, 
241                                 to_link=link_obj.LinkTarget.v( 
242                                     vm=self.kernel_address_space)) 
  243   
244   
246      """ Scanner for Mutants _KMUTANT """ 
248          super(PoolScanMutant, self).__init__(**kwargs) 
249          self.checks = [ 
250              ('PoolTagCheck', dict(tag=self.profile.get_constant( 
251                  "MUTANT_POOLTAG"))), 
252   
253              ('CheckPoolSize', dict( 
254                  min_size=self.profile.get_obj_size("_KMUTANT"))), 
255   
256              ('CheckPoolType', dict( 
257                  paged=True, non_paged=True, free=True)), 
258   
259              ('CheckPoolIndex', dict(value=0)), 
260              ] 
  261   
262   
264      "Scan for mutant objects _KMUTANT " 
265   
266      __name = "mutantscan" 
267   
268      table_header = [ 
269          dict(name='a', width=1), 
270          dict(name="offset", style="address"), 
271          dict(name="ptr_no", width=6, align="r"), 
272          dict(name="hnd_no", width=3, align="r"), 
273          dict(name="signal", width=6), 
274          dict(name="thread", style="address"), 
275          dict(name="cid", width=9, align="r"), 
276          dict(name="name") 
277      ] 
278   
279      scanner_defaults = dict( 
280          scan_kernel_nonpaged_pool=True, 
281      ) 
282   
284          for run in self.generate_memory_ranges(): 
285              scanner = PoolScanMutant(profile=self.profile, session=self.session, 
286                                       address_space=run.address_space) 
287   
288              for pool_obj in scanner.scan(run.start, run.length): 
289                  for object_obj in pool_obj.IterObject("Mutant", freed=True): 
290                      object_name = object_obj.NameInfo.Name.v( 
291                          vm=self.kernel_address_space) 
292   
293                       
294                       
295                      if self.plugin_args.verbosity < 5 and not object_name: 
296                          continue 
297   
298                      mutant = self.profile._KMUTANT( 
299                          object_obj.obj_end, vm=run.address_space) 
300   
301                      if mutant.OwnerThread > 0x80000000: 
302                          thread = self.profile._ETHREAD( 
303                              offset=mutant.OwnerThread, 
304                              vm=self.kernel_address_space) 
305   
306                          CID = "{0}:{1}".format(thread.Cid.UniqueProcess, 
307                                                 thread.Cid.UniqueThread) 
308                      else: 
309                          CID = "" 
310   
311                      yield dict(a='F' if pool_obj.FreePool else "", 
312                                 offset=mutant.obj_offset, 
313                                 ptr_no=object_obj.PointerCount, 
314                                 hnd_no=object_obj.HandleCount, 
315                                 signal=mutant.Header.SignalState, 
316                                 thread=mutant.OwnerThread, 
317                                 cid=CID, 
318                                 name=object_obj.NameInfo.Name.v( 
319                                     vm=self.kernel_address_space)) 
  320   
321   
323      """PoolScanner for File objects""" 
324   
325       
326      kernel = 0x80000000 
327   
329          super(PoolScanProcess, self).__init__(**kwargs) 
330          self.kernel = self.profile.get_constant_object( 
331              "MmSystemRangeStart", "Pointer").v() or 0x80000000 
332   
333          self.checks = [ 
334               
335              ('PoolTagCheck', dict( 
336                  tag=self.profile.get_constant("EPROCESS_POOLTAG"))), 
337   
338               
339              ('CheckPoolSize', dict(min_size=self.profile.get_obj_size( 
340                  "_EPROCESS"))), 
341   
342               
343               
344              ('CheckPoolType', dict( 
345                  paged=True, non_paged=True, free=True)), 
346   
347              ('CheckPoolIndex', dict(value=0)), 
348          ] 
349   
350           
351           
352          if self.session.kernel_address_space.metadata("pae"): 
353              self.dtb_alignment = 0x20 
354          else: 
355              self.dtb_alignment = 0x1000 
 356   
357 -    def scan(self, **kwargs): 
  376   
377   
378 -class PSScan(common.WinScanner): 
 379      """Scan Physical memory for _EPROCESS pool allocations. 
380   
381      Status flags: 
382        E: A known _EPROCESS address from pslist. 
383        P: A known pid from pslist. 
384      """ 
385   
386      name = "psscan" 
387   
388      table_header = [ 
389          dict(name='a', width=1), 
390          dict(name="offset_p", type="_EPROCESS"), 
391          dict(name="offset_v", style="address"), 
392          dict(name="ppid", width=6, align="r"), 
393          dict(name="pdb", style="address"), 
394          dict(name='stat', width=4), 
395          dict(name="create_time", width=24), 
396          dict(name="exit_time", width=24), 
397      ] 
398   
399       
400      scanner_defaults = dict( 
401          scan_kernel_nonpaged_pool=True 
402      ) 
403   
405          """Render results in a table.""" 
406           
407           
408          pslist = self.session.plugins.pslist() 
409   
410           
411          known_eprocess = set() 
412          known_pids = set() 
413          for task in pslist.list_eprocess(): 
414              known_eprocess.add(task) 
415              known_pids.add(task.UniqueProcessId) 
416   
417           
418          for run in self.generate_memory_ranges(): 
419               
420              scanner = PoolScanProcess(session=self.session, 
421                                        profile=self.profile, 
422                                        address_space=run.address_space) 
423   
424              for pool_obj, eprocess in scanner.scan( 
425                      offset=run.start, maxlen=run.length): 
426                  if run.data["type"] == "PhysicalAS": 
427                       
428                      virtual_eprocess = ( 
429                          pslist.virtual_process_from_physical_offset(eprocess)) 
430                  else: 
431                      virtual_eprocess = eprocess 
432   
433                  known = "" 
434                  if virtual_eprocess in known_eprocess: 
435                      known += "E" 
436   
437                  if eprocess.UniqueProcessId in known_pids: 
438                      known += "P" 
439   
440                  yield dict(a='F' if pool_obj.FreePool else "", 
441                             offset_p=eprocess, 
442                             offset_v=virtual_eprocess.obj_offset, 
443                             ppid=eprocess.InheritedFromUniqueProcessId, 
444                             pdb=eprocess.Pcb.DirectoryTableBase, 
445                             stat=known, 
446                             create_time=eprocess.CreateTime or '', 
447                             exit_time=eprocess.ExitTime or '') 
  448