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
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
106 return dict(Message="")
107
121
123 if self.profile.metadata("arch") == "I386":
124 return ptr
125
126 return ptr | 0xFFFFF00000000000
127
128 - def _SetKDBG(self, kdbg, member, symbol=None):
139
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
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
155 kdbg = self.profile._KDDEBUGGER_DATA64(
156 offset=kdbg_physical_address,
157 vm=crash_as)
158
159
160 crash_as.write(kdbg_physical_address, "\x00" * kdbg.obj_size)
161
162
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
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
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
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
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
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
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
279 PAGE_SIZE = 0x1000
280
281
282 out_as = standard.WritableAddressSpace(
283 filename=self.plugin_args.destination, session=self.session,
284 mode="w+b")
285
286
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
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
298 if getattr(self.kernel_address_space, "pae", None):
299 header.PaeEnabled = 1
300
301
302 number_of_pages = 0
303 i = None
304
305 for i, run in enumerate(self.physical_address_space.get_mappings()):
306
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
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
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
343 header.NumberProcessors = self.profile.get_constant_object(
344 "KeNumberProcessors", "unsigned int")
345
346
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
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
364 header.RequiredDumpSpace = number_of_pages + header.obj_size / PAGE_SIZE
365 header.DumpType = 1
366
367
368
369 out_as.write(header.ContextRecord.obj_offset,
370 "\x00" * (header.m("Exception").obj_offset -
371 header.ContextRecord.obj_offset))
372
373
374 out_as.write(header.Comment.obj_offset,
375 "Created with Rekall Memory Forensics\x00")
376
377
378 output_offset = header.obj_size
379 for run in self.physical_address_space.get_mappings():
380
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
402
403
404
405
406
407
408
409
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
421