refactor load_segments() to fix 32-bit bitmap crashdumps

This commit is contained in:
iMHLv2
2021-05-11 15:59:42 -05:00
parent 00db33e0fe
commit 7152ca0ffc
2 changed files with 66 additions and 46 deletions
+39 -46
View File
@@ -8,6 +8,7 @@ from typing import Tuple, Optional
from volatility3.framework import constants, exceptions, interfaces
from volatility3.framework.layers import segmented
from volatility3.framework.symbols import intermed
from volatility3.framework.symbols.windows.extensions import crash
vollog = logging.getLogger(__name__)
@@ -50,7 +51,8 @@ class WindowsCrashDump32Layer(segmented.SegmentedLayer):
# the _SUMMARY_DUMP is shared between 32- and 64-bit
self._crash_common_table_name = intermed.IntermediateSymbolTable.create(context, self._config_path,
'windows',
'crash_common')
'crash_common',
class_types=crash.class_types)
# Check Header
hdr_layer = self._context.layers[self._base_layer]
@@ -88,9 +90,6 @@ class WindowsCrashDump32Layer(segmented.SegmentedLayer):
segments = []
# instead of hard coding 0x2000, use 0x1000 * self.headerpages so this works for
# both 32- and 64-bit dumps
summary_header = self.get_summary_header()
if self.dump_type == 0x1:
header = self.context.object(self._crash_table_name + constants.BANG + self.dump_header_name,
offset=0,
@@ -103,50 +102,44 @@ class WindowsCrashDump32Layer(segmented.SegmentedLayer):
offset += x.PageCount
elif self.dump_type == 0x05:
summary_header = self.get_summary_header()
first_bit = None # First bit in a run
first_offset = 0 # File offset of first bit
last_bit_seen = 0 # Most recent bit processed
offset = summary_header.HeaderSize # Size of file headers
buffer_char = summary_header.get_buffer_char()
buffer_long = summary_header.get_buffer_long()
## NOTE: In the original crash64.json, _SUMMARY_DUMP.Pages was offset 48 and
## _SUMMARY_DUMP.BitmapSize was offset 40. From the volatility2 vtypes, that is
## backwards! The correct offsets should be:
##
## 'Pages' : [ 0x28, ['unsigned long long']], -> This is 40 decimal
## 'BitmapSize': [0x30, ['unsigned long long']], -> This is 48 decimal
##
## Most likely, some of the code that follows needs to be adjusted with those
## newly refactored offsets in mind. I commented out the "+ 0x2000" below, and
## this still works on Win10x64_17763_crash.dmp, but it fails on
## the Win10x86_17763_crash.dmp version.
for outer_index in range(0, ((summary_header.BitmapSize + 31) // 32)):
if buffer_long[outer_index] == 0:
if first_bit is not None:
last_bit = ((outer_index - 1) * 32) + 31
segment_length = (last_bit - first_bit + 1) * 0x1000
segments.append((first_bit * 0x1000, first_offset, segment_length, segment_length))
first_bit = None
elif buffer_long[outer_index] == 0xFFFFFFFF:
if first_bit is None:
first_offset = offset
first_bit = outer_index * 32
offset = offset + (32 * 0x1000)
else:
for inner_index in range(0, 32):
bit_addr = outer_index * 32 + inner_index
if (buffer_char[bit_addr >> 3] >> (bit_addr & 0x7)) & 1:
if first_bit is None:
first_offset = offset
first_bit = bit_addr
offset = offset + 0x1000
else:
if first_bit is not None:
segment_length = ((bit_addr - 1) - first_bit + 1) * 0x1000
segments.append((first_bit * 0x1000, first_offset, segment_length, segment_length))
first_bit = None
last_bit_seen = (outer_index * 32) + 31
# Add 0x2000 as some bitmaps are too short by one offset
summary_header.BufferLong.count = (summary_header.BitmapSize + 31) // 32 # + 0x2000
previous_bit = 0
start_position = 0
# We cast as an int because we don't want to carry the context around with us for infinite loop reasons
mapped_offset = int(summary_header.HeaderSize)
current_word = None
bitmap_len = len(summary_header.BufferLong) * 32
for bit_position in range(bitmap_len):
if (bit_position % 32) == 0:
current_word = summary_header.BufferLong[bit_position // 32]
current_bit = (current_word >> (bit_position % 32)) & 1
if current_bit != previous_bit:
if previous_bit == 0:
# Start
start_position = bit_position
else:
# Finish
length = (bit_position - start_position) * 0x1000
segments.append((start_position * 0x1000, mapped_offset, length, length))
mapped_offset += length
# Find the last segment in a file which will be at the end or two pages from the end. We multiply by 32 as we want to offset bby words rather than bits
if (bit_position == bitmap_len - 1 or bit_position == bitmap_len - 1 - 32 * 0x2000) and current_bit == 1:
length = (bit_position - start_position) * 0x1000
segments.append((start_position * 0x1000, mapped_offset, length, length))
mapped_offset += length
break
previous_bit = current_bit
if first_bit is not None:
segment_length = (last_bit_seen - first_bit + 1) * 0x1000
segments.append((first_bit * 0x1000, first_offset, segment_length, segment_length))
else:
vollog.log(constants.LOGLEVEL_VVVV, "unsupported dump format 0x{:x}".format(self.dump_type))
raise WindowsCrashDumpFormatException(self.name, "unsupported dump format 0x{:x}".format(self.dump_type))
@@ -0,0 +1,27 @@
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
from volatility3.framework import interfaces, constants
from volatility3.framework import objects
class SUMMARY_DUMP(objects.StructType):
def get_buffer(self, sub_type: str, count: int) -> interfaces.objects.ObjectInterface:
symbol_table_name = self.get_symbol_table_name()
subtype = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + sub_type)
return self._context.object(object_type=symbol_table_name + constants.BANG + "array",
layer_name=self.vol.layer_name,
offset=self.BufferChar.vol.offset,
count=count,
subtype=subtype)
def get_buffer_char(self) -> interfaces.objects.ObjectInterface:
return self.get_buffer(sub_type="unsigned char", count=(self.BitmapSize + 7) // 8)
def get_buffer_long(self) -> interfaces.objects.ObjectInterface:
return self.get_buffer(sub_type="unsigned long", count=(self.BitmapSize + 31) // 32)
class_types = {'_SUMMARY_DUMP': SUMMARY_DUMP}