Package rekall :: Package plugins :: Package windows :: Module pool
[frames] | no frames]

Source Code for Module rekall.plugins.windows.pool

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright 2016 Google Inc. All Rights Reserved. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or (at 
  8  # your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but 
 11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 13  # General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with this program; if not, write to the Free Software 
 17  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 18  """Plugins to inspect the windows pools.""" 
 19   
 20  __author__ = "Michael Cohen <scudette@google.com>" 
 21   
 22  # pylint: disable=protected-access 
 23   
 24  from rekall import obj 
 25  from rekall.plugins.windows import common 
 26  from rekall_lib import utils 
 27   
 28   
 29  # Some pool related utility functions. 
30 -def find_pool_alloc_before(session, offset, pool_tag):
31 """Searches address_space for a pool allocation containing offset.""" 32 # This method is only effective for small allocations right now because we 33 # need to find a pool tag (so allocation size is limited to one page). 34 # TODO: Extend this to big page pools. 35 base_offset = offset & ~0xFFF 36 data = session.kernel_address_space.read(base_offset, offset & 0xFFF) 37 buffer_offset = offset % 0x1000 38 pool_header_prototype = session.profile._POOL_HEADER() 39 40 while 1: 41 buffer_offset = data.rfind(pool_tag, 0, buffer_offset) 42 if buffer_offset == -1: 43 break 44 45 result = session.profile._POOL_HEADER( 46 (base_offset + buffer_offset - 47 pool_header_prototype.PoolTag.obj_offset), 48 vm=session.kernel_address_space) 49 50 end_of_allocation = result.obj_offset + result.size 51 52 # Allocation encompasses the required offset. 53 if end_of_allocation > offset: 54 yield result.obj_end 55 56 # After searching in small allocation, assume this is an allocation from 57 # Big Pool and go back several pages. 58 while base_offset > offset - 0x10000: 59 yield base_offset 60 base_offset -= 0x1000
61 62
63 -class Pools(common.WindowsCommandPlugin):
64 """Prints information about system pools. 65 66 Ref: 67 http://illmatics.com/Windows%208%20Heap%20Internals.pdf 68 https://media.blackhat.com/bh-dc-11/Mandt/BlackHat_DC_2011_Mandt_kernelpool-wp.pdf 69 https://immunityinc.com/infiltrate/archives/kernelpool_infiltrate2011.pdf 70 http://gate.upm.ro/os/LABs/Windows_OS_Internals_Curriculum_Resource_Kit-ACADEMIC/WindowsResearchKernel-WRK/WRK-v1.2/base/ntos/ex/pool.c 71 """ 72 73 name = "pools" 74 75 _pool_lookup = None 76 77 table_header = [ 78 dict(name="descriptor", width=20, style="address"), 79 dict(name="type", width=20), 80 dict(name="index", width=5), 81 dict(name="size", width=10, align="r"), 82 dict(name="start", style="address"), 83 dict(name="end", style="address"), 84 dict(name="comment") 85 ] 86
87 - def find_non_paged_pool(self):
88 vector_pool = self.profile.get_constant_object( 89 "PoolVector", 90 target="Array", 91 target_args=dict( 92 count=2, 93 target="Pointer", 94 ) 95 ) 96 97 resolver = self.session.address_resolver 98 99 for desc in vector_pool[0].dereference_as( 100 "Array", 101 target_args=dict( 102 count=self.profile.get_constant_object( 103 "ExpNumberOfNonPagedPools", "unsigned int").v(), 104 target="_POOL_DESCRIPTOR", 105 ) 106 ): 107 # Windows XP uses these globals. 108 start_va = resolver.get_constant_object( 109 "nt!MmNonPagedPoolStart", "Pointer").v() 110 111 end_va = resolver.get_constant_object( 112 "nt!MmNonPagedPoolEnd", "Pointer").v() 113 114 115 # Windows 7. 116 if start_va == None: 117 # First determine the addresses of non paged pool: 118 # dis 'nt!MiReturnNonPagedPoolVa' 119 start_va = resolver.get_constant_object( 120 "nt!MiNonPagedPoolStartAligned", "Pointer").v() 121 122 end_va = resolver.get_constant_object( 123 "nt!MiNonPagedPoolEnd", "Pointer").v() 124 125 if end_va == None: 126 bitmap = resolver.get_constant_object( 127 "nt!MiNonPagedPoolBitMap", "_RTL_BITMAP") 128 # ? MiNonPagedPoolVaBitMap 129 # We dont bother to check the bitmap itself, just consider the 130 # maximum size of the pool as the maximum allocated bitmap 131 # currently. This will overestimate the actual size somewhat. 132 end_va = start_va + bitmap.SizeOfBitMap * 8 * 0x1000 133 134 # In windows 10 the start va moved to the MiState global. 135 if start_va == None: 136 mistate = resolver.get_constant_object( 137 "nt!MiState", "_MI_SYSTEM_INFORMATION") 138 139 for node_index, node_info in enumerate(mistate.multi_m( 140 "Hardware.SystemNodeInformation", # Win10 2016 141 "SystemNodeInformation" # Win10 2015 142 )): 143 start_va = node_info.NonPagedPoolFirstVa.v() 144 end_va = start_va 145 # Just go to the last bitmap 146 for bitmap in node_info.NonPagedBitMap: 147 end_va = max(end_va, start_va + bitmap.SizeOfBitMap * 8) 148 149 desc.PoolStart = start_va 150 desc.PoolEnd = end_va 151 desc.Comment = "Node %i" % node_index 152 153 yield desc 154 155 else: 156 desc.PoolStart = start_va 157 desc.PoolEnd = end_va 158 desc.Comment = "" 159 160 yield desc
161
162 - def find_paged_pool(self):
163 vector_pool = self.profile.get_constant_object( 164 "PoolVector", 165 target="Array", 166 target_args=dict( 167 count=2, 168 target="Pointer", 169 ) 170 ) 171 172 # Paged pool. 173 paged_pool_start = self.profile.get_constant_object( 174 "MmPagedPoolStart", "Pointer").v() 175 176 if paged_pool_start == None: 177 paged_pool_start = self.profile.get_constant_object( 178 "MiPagedPoolStart", "Pointer").v() 179 180 paged_pool_end = ( 181 paged_pool_start + self.profile.get_constant_object( 182 "MmSizeOfPagedPoolInBytes", "address")) 183 184 if paged_pool_start == None: 185 # Windows 7 stores the end of the pool only 186 # (nt!MiFreePagedPoolPages). 187 paged_pool_end = self.profile.get_constant_object( 188 "MmPagedPoolEnd", "Pointer").v() 189 190 bitmap = self.profile.get_constant_object( 191 "MmPagedPoolInfo", "_MM_PAGED_POOL_INFO").PagedPoolAllocationMap 192 193 if bitmap: 194 paged_pool_start = ( 195 paged_pool_end - bitmap.SizeOfBitMap * 8 * 0x1000) 196 197 else: 198 paged_pool_start = ( 199 paged_pool_end - self.profile.get_constant_object( 200 "MmSizeOfPagedPoolInBytes", "unsigned long long")) 201 202 # Windows 10 build 10586.th2_release.160126-1819 uses dynamic Paged Pool 203 # VA. 204 if paged_pool_start == None: 205 mistate = self.session.address_resolver.get_constant_object( 206 "nt!MiState", "_MI_SYSTEM_INFORMATION") 207 dynamic_paged_pool = mistate.multi_m( 208 # 10586.th2_release.160126-1819 209 "SystemVa.DynamicBitMapPagedPool", 210 211 # 10074.fbl_impressive.150424-1350 212 "DynamicBitMapPagedPool" 213 ) 214 paged_pool_start = dynamic_paged_pool.BaseVa.v() 215 paged_pool_end = ( 216 paged_pool_start + 217 dynamic_paged_pool.MaximumSize * 0x1000) 218 219 comment = "" 220 if not paged_pool_start: 221 if self.profile.metadata("arch") == "I386": 222 # On Win7x86 the paged pool is distributed (see virt_map 223 # plugin). 224 comment = "Fragmented (See virt_map plugin)" 225 paged_pool_start = paged_pool_end = None 226 227 else: 228 # Hard coded on Windows 7. 229 # http://www.codemachine.com/article_x64kvas.html 230 # http://www.reactos.org/wiki/Techwiki:Memory_Layout 231 paged_pool_start = obj.Pointer.integer_to_address( 232 0xFFFFF8A000000000) 233 paged_pool_end = obj.Pointer.integer_to_address( 234 0xFFFFF8CFFFFFFFFF) 235 236 for desc in vector_pool[1].dereference_as( 237 "Array", 238 target_args=dict( 239 count=self.profile.get_constant_object( 240 "ExpNumberOfPagedPools", "unsigned int").v() + 1, 241 target="_POOL_DESCRIPTOR", 242 ) 243 ): 244 # Hard coded for 64 bit OS. 245 desc.PoolStart = paged_pool_start 246 desc.PoolEnd = paged_pool_end 247 desc.Comment = comment 248 249 yield desc
250
252 descriptors = {} 253 for task in self.session.plugins.pslist().list_eprocess(): 254 desc = task.Session.PagedPool.cast( 255 vm=task.get_process_address_space()) 256 if desc: 257 desc.PoolStart = task.Session.PagedPoolStart.v() 258 desc.PoolEnd = task.Session.PagedPoolEnd.v() 259 desc.Comment = "Session %s" % task.Session.SessionId 260 descriptors[desc.obj_offset] = desc 261 262 return descriptors.values()
263
265 """Finds all unique pool descriptors.""" 266 descriptors = set(self.find_non_paged_pool()) 267 descriptors.update(self.find_paged_pool()) 268 descriptors.update(self.find_session_pool_descriptors()) 269 return descriptors
270
271 - def is_address_in_pool(self, address):
272 if self._pool_lookup is None: 273 self._pool_lookup = utils.RangedCollection() 274 for descriptor in self.find_all_pool_descriptors(): 275 self._pool_lookup.insert(descriptor.PoolStart, 276 descriptor.PoolEnd, 277 descriptor) 278 279 return self._pool_lookup.get_containing_range(address)
280
281 - def collect(self):
282 descriptors = self.find_all_pool_descriptors() 283 for desc in sorted(descriptors): 284 yield dict(descriptor=desc, 285 type=desc.PoolType, 286 index=desc.PoolIndex, 287 size=desc.m("TotalBytes") or desc.TotalPages * 0x1000, 288 start=desc.PoolStart, 289 end=desc.PoolEnd, 290 comment=getattr(desc, "Comment", ""))
291 292
293 -class PoolTracker(common.WindowsCommandPlugin):
294 """Enumerate pool tag usage statistics.""" 295 296 name = "pool_tracker" 297 298 table_header = [ 299 dict(name="tag", width=4), 300 dict(name="nonpaged", width=20, align="r"), 301 dict(name="nonpaged_bytes", width=10, align="r"), 302 dict(name="paged", width=20, align="r"), 303 dict(name="paged_bytes", width=10, align="r"), 304 ] 305
306 - def collect(self):
307 table = self.profile.get_constant_object( 308 "PoolTrackTable", 309 target="Pointer", 310 target_args=dict( 311 target="Array", 312 target_args=dict( 313 count=self.profile.get_constant_object( 314 "PoolTrackTableSize", "unsigned int").v(), 315 target="_POOL_TRACKER_TABLE", 316 ) 317 ) 318 ) 319 320 for item in table: 321 if item.Key == 0: 322 continue 323 324 self.session.report_progress() 325 yield (# Show the pool tag as ascii. 326 item.Key.cast("String", length=4), 327 "%s (%s)" % (item.NonPagedAllocs, 328 item.NonPagedAllocs - item.NonPagedFrees), 329 item.NonPagedBytes, 330 "%s (%s)" % (item.PagedAllocs, 331 item.PagedAllocs - item.PagedFrees), 332 item.PagedBytes, 333 )
334