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

Source Code for Module rekall.plugins.windows.crashinfo

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # Authors: 
  6  # Michael Cohen <scudette@gmail.com> 
  7  # Based on code by Aaron Walters. 
  8  # 
  9  # This program is free software; you can redistribute it and/or modify 
 10  # it under the terms of the GNU General Public License as published by 
 11  # the Free Software Foundation; either version 2 of the License, or (at 
 12  # your option) any later version. 
 13  # 
 14  # This program is distributed in the hope that it will be useful, but 
 15  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 16  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 17  # General Public License for more details. 
 18  # 
 19  # You should have received a copy of the GNU General Public License 
 20  # along with this program; if not, write to the Free Software 
 21  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 22  # 
 23   
 24  # pylint: disable=protected-access 
 25   
 26  from rekall import plugin 
 27  from rekall import testlib 
 28  from rekall.plugins.windows import common 
 29  from rekall.plugins.addrspaces import crash 
 30  from rekall.plugins.addrspaces import standard 
 31  from rekall.plugins.overlays.windows import crashdump 
 32   
 33   
34 -class WritableCrashDump(crash.WindowsCrashDumpSpace64, 35 standard.WritableAddressSpace):
36 """A writable crash dump address space. 37 38 When creating a new crash dump, we need to moodify the image: 39 1) To rebuild the KDBG block. 40 2) To decrypt the KDBG block in images which obfuscate it. 41 """
42 43
44 -class Raw2Dump(common.WindowsCommandPlugin):
45 """Convert the physical address space to a crash dump. 46 47 The Windows debugger (Windbg) works only with memory dumps stored 48 in the proprietary 'crashdump' file format. This file format 49 contains the following features: 50 51 1) Physical memory ranges are stored in a sparse way - there is a 52 'Runs' table which specifies the mapping between the physical 53 offset and the file offset of each page. This allows the format 54 to omit unmapped regions (unlike raw format which must pad them 55 with zero to maintain alignment). 56 57 2) The crash dump header contains metadata about the 58 image. Specifically, the header contain a copy of the Kernel 59 Debugger Data Block (AKA the KDBG). This data is used to 60 bootstrap the windows debugger by providing critical initial 61 hints to the debugger. 62 63 Since the KDBG block is created at system boot and never used 64 (until the crash dump is written) it is trivial for malware to 65 overwrite it - making it really hard for responders since windbg 66 will not be able to read the file. In later versions of windows, 67 the kdbg is also obfuscated (See the function "nt!KdCopyDataBlock" 68 which decrypts it.). 69 70 Rekall itself does not use the KDBG block any more, although older 71 memory forensic tools still do use it. Rekall instead relies on 72 accurate debugging symbols to locate critical kernel data 73 structures, reducing the level of trust we place on the image 74 itself (so Rekall is more resilient to manipulation). 75 76 In order to ensure that the windows debugger is able to read the 77 produced crash dump, we recreate the kernel debugger block from 78 the symbol information we already have. 79 80 NOTE: The crashdump file format can be deduced by: 81 82 dis 'nt!IoFillDumpHeader' 83 84 This is the reference for this plugin. 85 """ 86 87 __name = "raw2dmp" 88 89 __args = [ 90 dict(name="destination", positional=True, required=True, 91 help="The destination path to write the crash dump."), 92 93 dict(name="rebuild", type="Boolean", 94 help="Rebuild the KDBG data block."), 95 ] 96 97 table_header = [ 98 dict(name="Message") 99 ] 100 101 table_options = dict( 102 suppress_headers=True 103 ) 104
105 - def column_types(self):
106 return dict(Message="")
107
108 - def __init__(self, *args, **kwargs):
109 super(Raw2Dump, self).__init__(*args, **kwargs) 110 if self.session.profile.metadata("arch") == "I386": 111 self.profile = crashdump.CrashDump32Profile.Initialize( 112 self.profile.copy()) 113 elif self.session.profile.metadata("arch") == "AMD64": 114 self.profile = crashdump.CrashDump64Profile.Initialize( 115 self.profile.copy()) 116 else: 117 raise plugin.PluginError( 118 "Unable to write crashdump for this architecture.") 119 120 self.buffer_size = 10 * 1024 * 1024
121
122 - def _pointer_to_int(self, ptr):
123 if self.profile.metadata("arch") == "I386": 124 return ptr 125 126 return ptr | 0xFFFFF00000000000
127
128 - def _SetKDBG(self, kdbg, member, symbol=None):
129 if symbol is None: 130 symbol = "nt!%s" % member 131 132 symbol = self.session.address_resolver.get_address_by_name(symbol) 133 134 # If the symbol does not exist in the profile, we ignore it here. 135 if symbol == None: 136 return 137 138 kdbg.SetMember(member, self._pointer_to_int(symbol))
139
140 - def RebuildKDBG(self, out_fd):
141 """Modify the destination image to rebuild the KDBG.""" 142 if self.profile.metadata("arch") == "I386": 143 crash_as_cls = crash.WindowsCrashDumpSpace32 144 else: 145 crash_as_cls = crash.WindowsCrashDumpSpace64 146 147 # Open the crash file for writing. 148 crash_as = crash_as_cls(base=out_fd, session=self.session) 149 150 kdbg_virtual_address = self.session.GetParameter("kdbg") 151 kdbg_physical_address = self.kernel_address_space.vtop( 152 kdbg_virtual_address) 153 154 # Construct a _KDDEBUGGER_DATA64 over the old one. 155 kdbg = self.profile._KDDEBUGGER_DATA64( 156 offset=kdbg_physical_address, 157 vm=crash_as) 158 159 # Clear the old data just in case. 160 crash_as.write(kdbg_physical_address, "\x00" * kdbg.obj_size) 161 162 # The KDBG header. 163 kdbg.Header.OwnerTag = "KDBG" 164 kdbg.Header.Size = kdbg.obj_size 165 kdbg.Header.List.Flink = kdbg.Header.List.Blink = ( 166 self._pointer_to_int(kdbg_virtual_address)) 167 168 kdbg.MmPageSize = 0x1000 169 170 # _KTHREAD offsets. 171 kthread = self.profile._KTHREAD() 172 ethread = self.profile._ETHREAD() 173 kdbg.SizeEThread = ethread.obj_size 174 kdbg.OffsetKThreadNextProcessor = kthread.NextProcessor.obj_offset 175 176 kdbg.OffsetKThreadTeb = kthread.Teb.obj_offset 177 kdbg.OffsetKThreadKernelStack = kthread.KernelStack.obj_offset 178 kdbg.OffsetKThreadInitialStack = kthread.InitialStack.obj_offset 179 180 kdbg.OffsetKThreadState = kthread.State.obj_offset 181 kdbg.OffsetKThreadApcProcess = kthread.ApcState.Process.obj_offset 182 183 # _EPROCESS offsets. 184 eprocess = self.profile._EPROCESS() 185 kdbg.SizeEProcess = eprocess.obj_size 186 kdbg.OffsetEprocessPeb = eprocess.m("Peb").obj_offset 187 kdbg.OffsetEprocessParentCID = ( 188 eprocess.InheritedFromUniqueProcessId.obj_offset) 189 kdbg.OffsetEprocessDirectoryTableBase = ( 190 eprocess.Pcb.DirectoryTableBase.obj_offset) 191 192 # _KPRCB offsets. 193 prcb = self.profile._KPRCB() 194 kdbg.SizePrcb = prcb.obj_size 195 kdbg.OffsetPrcbDpcRoutine = prcb.DpcRoutineActive.obj_offset 196 kdbg.OffsetPrcbCurrentThread = prcb.CurrentThread.obj_offset 197 kdbg.OffsetPrcbMhz = prcb.MHz.obj_offset 198 kdbg.OffsetPrcbCpuType = prcb.CpuType.obj_offset 199 kdbg.OffsetPrcbVendorString = prcb.VendorString.obj_offset 200 kdbg.OffsetPrcbProcStateSpecialReg = ( 201 prcb.ProcessorState.SpecialRegisters.obj_offset) 202 203 kdbg.OffsetPrcbProcStateContext = ( 204 prcb.ProcessorState.ContextFrame.obj_offset) 205 206 kdbg.OffsetPrcbNumber = prcb.Number.obj_offset 207 208 # _KPCR offsets. 209 pcr = self.profile._KPCR() 210 kdbg.SizePcr = pcr.obj_size 211 212 if self.profile.metadata("arch") == "AMD64": 213 kdbg.OffsetPrcbContext = prcb.Context.obj_offset 214 kdbg.OffsetPcrSelfPcr = pcr.Self.obj_offset 215 kdbg.OffsetPcrCurrentPrcb = pcr.CurrentPrcb.obj_offset 216 kdbg.OffsetPcrContainedPrcb = pcr.Prcb.obj_offset 217 218 # Clear the KdpDataBlockEncoded flag from the image. 219 flag = self.profile.get_constant_object( 220 "KdpDataBlockEncoded", "byte") 221 222 crash_as.write( 223 self.kernel_address_space.vtop(flag.obj_offset), "\x01") 224 225 # Global constants. 226 self._SetKDBG(kdbg, "CmNtCSDVersion") 227 self._SetKDBG(kdbg, "ExpNumberOfPagedPools") 228 self._SetKDBG(kdbg, "ExpPagedPoolDescriptor") 229 self._SetKDBG(kdbg, "ExpPagedPoolDescriptor") 230 self._SetKDBG(kdbg, "ExpSystemResourcesList") 231 self._SetKDBG(kdbg, "KdPrintBufferSize") 232 self._SetKDBG(kdbg, "KdPrintCircularBuffer") 233 self._SetKDBG(kdbg, "KdPrintCircularBufferEnd", "nt!KdpBreakpointTable") 234 self._SetKDBG(kdbg, "KdPrintRolloverCount") 235 self._SetKDBG(kdbg, "KdPrintWritePointer") 236 self._SetKDBG(kdbg, "KeLoaderBlock", "nt!KdpLoaderDebuggerBlock") 237 self._SetKDBG(kdbg, "KeTimeIncrement") 238 self._SetKDBG(kdbg, "KernBase", "nt") 239 self._SetKDBG(kdbg, "KiBugCheckData") 240 self._SetKDBG(kdbg, "KiCallUserMode") 241 self._SetKDBG(kdbg, "KiProcessorBlock") 242 self._SetKDBG(kdbg, "KiProcessorBlock") 243 self._SetKDBG(kdbg, "MmAvailablePages") 244 self._SetKDBG(kdbg, "MmFreePageListHead") 245 self._SetKDBG(kdbg, "MmHighestPhysicalPage") 246 self._SetKDBG(kdbg, "MmHighestUserAddress") 247 self._SetKDBG(kdbg, "MmLastUnloadedDriver") 248 self._SetKDBG(kdbg, "MmLoadedUserImageList") 249 self._SetKDBG(kdbg, "MmLowestPhysicalPage") 250 self._SetKDBG(kdbg, "MmMaximumNonPagedPoolInBytes") 251 self._SetKDBG(kdbg, "MmModifiedNoWritePageListHead") 252 self._SetKDBG(kdbg, "MmModifiedPageListHead") 253 self._SetKDBG(kdbg, "MmNonPagedPoolStart") 254 self._SetKDBG(kdbg, "MmNumberOfPagingFiles") 255 self._SetKDBG(kdbg, "MmNumberOfPhysicalPages") 256 self._SetKDBG(kdbg, "MmPagedPoolEnd") 257 self._SetKDBG(kdbg, "MmPagedPoolInformation", "nt!MmPagedPoolInfo") 258 self._SetKDBG(kdbg, "MmPfnDatabase") 259 self._SetKDBG(kdbg, "MmPhysicalMemoryBlock") 260 self._SetKDBG(kdbg, "MmResidentAvailablePages") 261 self._SetKDBG(kdbg, "MmSizeOfPagedPoolInBytes") 262 self._SetKDBG(kdbg, "MmStandbyPageListHead") 263 self._SetKDBG(kdbg, "MmSubsectionBase") 264 self._SetKDBG(kdbg, "MmSystemCacheWs") 265 self._SetKDBG(kdbg, "MmSystemRangeStart") 266 self._SetKDBG(kdbg, "MmUnloadedDrivers") 267 self._SetKDBG(kdbg, "MmUserProbeAddress") 268 self._SetKDBG(kdbg, "MmZeroedPageListHead") 269 self._SetKDBG(kdbg, "NonPagedPoolDescriptor") 270 self._SetKDBG(kdbg, "NtBuildLab") 271 self._SetKDBG(kdbg, "ObpRootDirectoryObject") 272 self._SetKDBG(kdbg, "ObpTypeObjectType") 273 self._SetKDBG(kdbg, "PoolTrackTable") 274 self._SetKDBG(kdbg, "PsActiveProcessHead") 275 self._SetKDBG(kdbg, "PsLoadedModuleList") 276 self._SetKDBG(kdbg, "PspCidTable")
277
278 - def collect(self):
279 PAGE_SIZE = 0x1000 280 281 # We write the image to the destination using the WritableAddressSpace. 282 out_as = standard.WritableAddressSpace( 283 filename=self.plugin_args.destination, session=self.session, 284 mode="w+b") 285 286 # Pad the header area with PAGE pattern: 287 if self.profile.metadata("arch") == "AMD64": 288 header = self.profile._DMP_HEADER64(vm=out_as) 289 out_as.write(0, "PAGE" * (header.obj_size / 4)) 290 out_as.write(4, "DU64") 291 else: 292 # 32 bit systems use a smaller structure. 293 header = self.profile._DMP_HEADER(vm=out_as) 294 out_as.write(0, "PAGE" * (header.obj_size / 4)) 295 out_as.write(4, "DUMP") 296 297 # PEA address spaces. 298 if getattr(self.kernel_address_space, "pae", None): 299 header.PaeEnabled = 1 300 301 # Write the runs from our physical address space. 302 number_of_pages = 0 303 i = None 304 305 for i, run in enumerate(self.physical_address_space.get_mappings()): 306 # Convert to pages 307 start = run.start / PAGE_SIZE 308 length = run.length / PAGE_SIZE 309 310 header.PhysicalMemoryBlockBuffer.Run[i].BasePage = start 311 header.PhysicalMemoryBlockBuffer.Run[i].PageCount = length 312 number_of_pages += length 313 314 # There must be at least one run. 315 if i is None: 316 raise plugin.PluginError( 317 "Physical address space has no available data.") 318 319 header.PhysicalMemoryBlockBuffer.NumberOfRuns = i + 1 320 header.PhysicalMemoryBlockBuffer.NumberOfPages = number_of_pages 321 322 resolver = self.session.address_resolver 323 324 # Set members of the crash header 325 header.MajorVersion = 0xf 326 header.MinorVersion = 0x1db1 327 header.DirectoryTableBase = self.session.GetParameter("dtb") 328 header.PfnDataBase = self._pointer_to_int( 329 resolver.get_address_by_name("nt!MmPfnDatabase")) 330 331 header.PsLoadedModuleList = self._pointer_to_int( 332 resolver.get_address_by_name("nt!PsLoadedModuleList")) 333 334 header.PsActiveProcessHead = self._pointer_to_int( 335 resolver.get_address_by_name("nt!PsActiveProcessHead")) 336 337 header.KdDebuggerDataBlock = self._pointer_to_int( 338 resolver.get_address_by_name("nt!KdDebuggerDataBlock")) 339 340 header.MachineImageType = 0x8664 341 342 # Find the number of processors 343 header.NumberProcessors = self.profile.get_constant_object( 344 "KeNumberProcessors", "unsigned int") 345 346 # Copy some stuff from _KUSER_SHARED_DATA. 347 kuser_shared = self.profile.get_constant_object( 348 "KI_USER_SHARED_DATA", "_KUSER_SHARED_DATA") 349 header.SystemTime = kuser_shared.SystemTime.as_windows_timestamp() 350 header.SystemUpTime = ( 351 kuser_shared.InterruptTime.LowPart + 352 kuser_shared.InterruptTime.High1Time << 32) / 100000 353 header.ProductType = kuser_shared.NtProductType 354 header.SuiteMask = kuser_shared.SuiteMask 355 356 # Zero out the BugCheck members 357 header.BugCheckCode = 0x00000000 358 header.BugCheckCodeParameter[0] = 0x00000000 359 header.BugCheckCodeParameter[1] = 0x00000000 360 header.BugCheckCodeParameter[2] = 0x00000000 361 header.BugCheckCodeParameter[3] = 0x00000000 362 363 # Set the sample run information 364 header.RequiredDumpSpace = number_of_pages + header.obj_size / PAGE_SIZE 365 header.DumpType = 1 366 367 # Zero out the remaining non-essential fields from ContextRecordOffset 368 # to ExceptionOffset. 369 out_as.write(header.ContextRecord.obj_offset, 370 "\x00" * (header.m("Exception").obj_offset - 371 header.ContextRecord.obj_offset)) 372 373 # Set the "converted" comment 374 out_as.write(header.Comment.obj_offset, 375 "Created with Rekall Memory Forensics\x00") 376 377 # Now copy the physical address space to the output file. 378 output_offset = header.obj_size 379 for run in self.physical_address_space.get_mappings(): 380 # Convert to pages 381 start = run.start / PAGE_SIZE 382 length = run.length / PAGE_SIZE 383 384 yield ("\nRun [0x%08X, 0x%08X] \n" % (start, length),) 385 data_length = length * PAGE_SIZE 386 start_offset = start * PAGE_SIZE 387 offset = 0 388 while data_length > 0: 389 to_read = min(data_length, self.buffer_size) 390 391 data = self.physical_address_space.read( 392 start_offset + offset, to_read) 393 394 out_as.write(output_offset, data) 395 output_offset += len(data) 396 offset += len(data) 397 data_length -= len(data) 398 self.session.render_progress( 399 "Wrote %sMB.", (start_offset + offset) / 1024 / 1024) 400 401 # Rebuild the KDBG data block if needed. According to the 402 # disassembly of nt!KdCopyDataBlock the data block is 403 # encrypted when nt!KdpDataBlockEncoded is non zero: 404 405 # ------ nt!KdCopyDataBlock ------ 406 # SUB RSP, 0x28 407 # CMP BYTE [RIP+0x10fac7], 0x0 0x0 nt!KdpDataBlockEncoded 408 # MOV RDX, RCX 409 # JZ 0xf8000291e6a7 nt!KdCopyDataBlock + 0x57 410 411 if self.plugin_args.rebuild or self.profile.get_constant_object( 412 "KdpDataBlockEncoded", "byte") > 0: 413 yield ("Rebuilding KDBG data block.\n",) 414 self.RebuildKDBG(out_as)
415 416
417 -class TestRaw2Dump(testlib.HashChecker):
418 PARAMETERS = dict( 419 commandline="raw2dmp --rebuild %(tempdir)s/output.dmp " 420 )
421