From 06961ce53742a4f266d4892f7b7d8120dc34388b Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 5 Mar 2022 16:32:38 +0900 Subject: [PATCH 01/36] Initialize MBR Parser --- .../framework/plugins/windows/mbrparser.py | 76 ++++++++++++ .../symbols/windows/extensions/mbr.py | 111 ++++++++++++++++++ .../framework/symbols/windows/mbr.json | 30 +++++ 3 files changed, 217 insertions(+) create mode 100644 volatility3/framework/plugins/windows/mbrparser.py create mode 100644 volatility3/framework/symbols/windows/extensions/mbr.py create mode 100644 volatility3/framework/symbols/windows/mbr.json diff --git a/volatility3/framework/plugins/windows/mbrparser.py b/volatility3/framework/plugins/windows/mbrparser.py new file mode 100644 index 000000000..37a4612f8 --- /dev/null +++ b/volatility3/framework/plugins/windows/mbrparser.py @@ -0,0 +1,76 @@ +# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +import datetime +import logging + +from volatility3.framework import exceptions, interfaces, renderers +from volatility3.framework.configuration import requirements +from volatility3.framework.renderers import format_hints +from volatility3.framework.symbols import intermed +from volatility3.framework.symbols.windows.extensions import mbr +from volatility3.plugins import yarascan + +vollog = logging.getLogger(__name__) + + +class MBRParser(interfaces.plugins.PluginInterface): + """ Scans for and parses potential Master Boot Records (MBRs) """ + + _required_framework_version = (2, 0, 1) + + @classmethod + def get_requirements(cls): + return [ + requirements.TranslationLayerRequirement(name = 'primary', + description = 'Memory layer for the kernel', + architectures = ["Intel32", "Intel64"]), + requirements.VersionRequirement(name = 'yarascanner', component = yarascan.YaraScanner, + version = (2, 0, 0)), + ] + + @classmethod + def levenshtein(self, s1, s2): + if len(s1) < len(s2): + return self.levenshtein(s2, s1) + + if len(s2) == 0: + return len(s1) + + previous_row = range(len(s2) + 1) + for i, c1 in enumerate(s1): + current_row = [i + 1] + for j, c2 in enumerate(s2): + insertions = previous_row[j + 1] + 1 + deletions = current_row[j] + 1 + substitutions = previous_row[j] + (c1 != c2) + current_row.append(min(insertions, deletions, substitutions)) + previous_row = current_row + + return previous_row[-1] + + def _generator(self): + layer = self.context.layers[self.config['primary']] + rules = yarascan.YaraScan.process_yara_options({'yara_rules': '/\x55\xaa/'}) + symbol_table = intermed.IntermediateSymbolTable.create(context = self.context, + config_path = self.config_path, + sub_path = "windows", + filename = "mbr", + class_types = { + 'PARTITION_ENTRY': mbr.PARTITION_ENTRY, + }) + + for offset, _rule_name, _name, _value in layer.scan(context = self.context, + scanner = yarascan.YaraScanner(rules = rules)): + try: + yield 1, (format_hints.Hex(offset), _value) + + except exceptions.PagedInvalidAddressException: + pass + + def run(self): + return renderers.TreeGrid([ + ('Offset', format_hints.Hex), + ('Record Type', str), + ], self._generator()) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py new file mode 100644 index 000000000..eeb97d332 --- /dev/null +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -0,0 +1,111 @@ +# This file is Copyright 2022 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 objects + +import struct + +PartitionTypes = { + 0x00:"Empty", + 0x01:"FAT12,CHS", + 0x04:"FAT16 16-32MB,CHS", + 0x05:"Microsoft Extended", + 0x06:"FAT16 32MB,CHS", + 0x07:"NTFS", + 0x0b:"FAT32,CHS", + 0x0c:"FAT32,LBA", + 0x0e:"FAT16, 32MB-2GB,LBA", + 0x0f:"Microsoft Extended, LBA", + 0x11:"Hidden FAT12,CHS", + 0x14:"Hidden FAT16,16-32MB,CHS", + 0x16:"Hidden FAT16,32MB-2GB,CHS", + 0x18:"AST SmartSleep Partition", + 0x1b:"Hidden FAT32,CHS", + 0x1c:"Hidden FAT32,LBA", + 0x1e:"Hidden FAT16,32MB-2GB,LBA", + 0x27:"PQservice", + 0x39:"Plan 9 partition", + 0x3c:"PartitionMagic recovery partition", + 0x42:"Microsoft MBR,Dynamic Disk", + 0x44:"GoBack partition", + 0x51:"Novell", + 0x52:"CP/M", + 0x63:"Unix System V", + 0x64:"PC-ARMOUR protected partition", + 0x82:"Solaris x86 or Linux Swap", + 0x83:"Linux", + 0x84:"Hibernation", + 0x85:"Linux Extended", + 0x86:"NTFS Volume Set", + 0x87:"NTFS Volume Set", + 0x9f:"BSD/OS", + 0xa0:"Hibernation", + 0xa1:"Hibernation", + 0xa5:"FreeBSD", + 0xa6:"OpenBSD", + 0xa8:"Mac OSX", + 0xa9:"NetBSD", + 0xab:"Mac OSX Boot", + 0xaf:"MacOS X HFS", + 0xb7:"BSDI", + 0xb8:"BSDI Swap", + 0xbb:"Boot Wizard hidden", + 0xbe:"Solaris 8 boot partition", + 0xd8:"CP/M-86", + 0xde:"Dell PowerEdge Server utilities (FAT fs)", + 0xdf:"DG/UX virtual disk manager partition", + 0xeb:"BeOS BFS", + 0xee:"EFI GPT Disk", + 0xef:"EFI System Partition", + 0xfb:"VMWare File System", + 0xfc:"VMWare Swap", +} + +class PARTITION_ENTRY(objects.StructType): + def get_value(self, char): + padded = "\x00\x00\x00" + str(char) + val = int(struct.unpack('>I', padded)[0]) + return val + + def get_type(self): + return PartitionTypes.get(self.get_value(self.PartitionType), "Invalid") + + def is_bootable(self): + return self.get_value(self.BootableFlag) == 0x80 + + def is_bootable_and_used(self): + return self.is_bootable() and self.is_used() + + def is_valid(self): + return self.get_type() != "Invalid" + + def is_used(self): + return self.get_type() != "Empty" and self.is_valid() + + def StartingSector(self): + return self.StartingCHS[1] % 64 + + def StartingCylinder(self): + return (self.StartingCHS[1] - self.StartingSector()) * 4 + self.StartingCHS[2] + + def EndingSector(self): + return self.EndingCHS[1] % 64 + + def EndingCylinder(self): + return (self.EndingCHS[1] - self.EndingSector()) * 4 + self.EndingCHS[2] + + def __str__(self): + processed_entry = "" + bootable = self.get_value(self.BootableFlag) + processed_entry = "Boot flag: {0:#x} {1}\n".format(bootable, "(Bootable)" if self.is_bootable() else '') + processed_entry += "Partition type: {0:#x} ({1})\n".format(self.get_value(self.PartitionType), self.get_type()) + processed_entry += "Starting Sector (LBA): {0:#x} ({0})\n".format(self.StartingLBA) + processed_entry += "Starting CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format(self.StartingCylinder(), + self.StartingCHS[0], + self.StartingSector()) + processed_entry += "Ending CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format(self.EndingCylinder(), + self.EndingCHS[0], + self.EndingSector()) + processed_entry += "Size in sectors: {0:#x} ({0})\n\n".format(self.SizeInSectors) + return processed_entry \ No newline at end of file diff --git a/volatility3/framework/symbols/windows/mbr.json b/volatility3/framework/symbols/windows/mbr.json new file mode 100644 index 000000000..83bd48f13 --- /dev/null +++ b/volatility3/framework/symbols/windows/mbr.json @@ -0,0 +1,30 @@ +{ + "metadata": { + "producer": { + "version": "0.0.1", + "name": "Donghyun Kim", + "comment": "Using structures defined in File System Forensic Analysis pg 353+", + "datetime": "2022-01-03T13:37:00" + }, + "format": "6.1.0" + }, + { + 'PARTITION_ENTRY': [ 0x10, { + 'BootableFlag': [0x0, ['char']], # 0x80 is bootable + 'StartingCHS': [0x1, ['array', 3, ['unsigned char']]], + 'PartitionType': [0x4, ['char']], + 'EndingCHS': [0x5, ['array', 3, ['unsigned char']]], + 'StartingLBA': [0x8, ['unsigned int']], + 'SizeInSectors': [0xc, ['int']], + }], + 'PARTITION_TABLE': [ 0x200, { + 'DiskSignature': [ 0x1b8, ['array', 4, ['unsigned char']]], + 'Unused': [ 0x1bc, ['unsigned short']], + 'Entry1': [ 0x1be, ['PARTITION_ENTRY']], + 'Entry2': [ 0x1ce, ['PARTITION_ENTRY']], + 'Entry3': [ 0x1de, ['PARTITION_ENTRY']], + 'Entry4': [ 0x1ee, ['PARTITION_ENTRY']], + 'Signature': [0x1fe, ['unsigned short']], + }] + } +} \ No newline at end of file From 7570e82786f49db3e0aed591ce6a13b17a97570a Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 5 Mar 2022 17:47:37 +0900 Subject: [PATCH 02/36] Configuration Yara Rules --- .../framework/plugins/windows/mbrparser.py | 16 ++--- .../framework/symbols/windows/mbr.json | 64 +++++++++++++------ 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrparser.py b/volatility3/framework/plugins/windows/mbrparser.py index 37a4612f8..10afa53d2 100644 --- a/volatility3/framework/plugins/windows/mbrparser.py +++ b/volatility3/framework/plugins/windows/mbrparser.py @@ -2,13 +2,11 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -import datetime import logging from volatility3.framework import exceptions, interfaces, renderers from volatility3.framework.configuration import requirements from volatility3.framework.renderers import format_hints -from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows.extensions import mbr from volatility3.plugins import yarascan @@ -52,19 +50,13 @@ class MBRParser(interfaces.plugins.PluginInterface): def _generator(self): layer = self.context.layers[self.config['primary']] - rules = yarascan.YaraScan.process_yara_options({'yara_rules': '/\x55\xaa/'}) - symbol_table = intermed.IntermediateSymbolTable.create(context = self.context, - config_path = self.config_path, - sub_path = "windows", - filename = "mbr", - class_types = { - 'PARTITION_ENTRY': mbr.PARTITION_ENTRY, - }) + # TODO : YARA RULE HEX + rules = yarascan.YaraScan.process_yara_options({'yara_rules': "55 aa"}) for offset, _rule_name, _name, _value in layer.scan(context = self.context, scanner = yarascan.YaraScanner(rules = rules)): try: - yield 1, (format_hints.Hex(offset), _value) + yield 0, (format_hints.Hex(offset), _name) except exceptions.PagedInvalidAddressException: pass @@ -72,5 +64,5 @@ class MBRParser(interfaces.plugins.PluginInterface): def run(self): return renderers.TreeGrid([ ('Offset', format_hints.Hex), - ('Record Type', str), + ("Name", str) ], self._generator()) diff --git a/volatility3/framework/symbols/windows/mbr.json b/volatility3/framework/symbols/windows/mbr.json index 83bd48f13..84162c96d 100644 --- a/volatility3/framework/symbols/windows/mbr.json +++ b/volatility3/framework/symbols/windows/mbr.json @@ -8,23 +8,49 @@ }, "format": "6.1.0" }, - { - 'PARTITION_ENTRY': [ 0x10, { - 'BootableFlag': [0x0, ['char']], # 0x80 is bootable - 'StartingCHS': [0x1, ['array', 3, ['unsigned char']]], - 'PartitionType': [0x4, ['char']], - 'EndingCHS': [0x5, ['array', 3, ['unsigned char']]], - 'StartingLBA': [0x8, ['unsigned int']], - 'SizeInSectors': [0xc, ['int']], - }], - 'PARTITION_TABLE': [ 0x200, { - 'DiskSignature': [ 0x1b8, ['array', 4, ['unsigned char']]], - 'Unused': [ 0x1bc, ['unsigned short']], - 'Entry1': [ 0x1be, ['PARTITION_ENTRY']], - 'Entry2': [ 0x1ce, ['PARTITION_ENTRY']], - 'Entry3': [ 0x1de, ['PARTITION_ENTRY']], - 'Entry4': [ 0x1ee, ['PARTITION_ENTRY']], - 'Signature': [0x1fe, ['unsigned short']], - }] - } + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": true, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "int", + "size": 1, + "signed": false, + "endian": "little" + }, + "wchar": { + "kind": "int", + "size": 2, + "signed": true, + "endian": "little" + } + }, + "symbols": {} } \ No newline at end of file From acc3f6f352d9446c773fd3ebf5891f67ba9d214b Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 13:27:53 +0900 Subject: [PATCH 03/36] Update Symbol Table, Load Physical Layer --- .../framework/plugins/windows/mbrparser.py | 68 ------ .../framework/plugins/windows/mbrscan.py | 80 +++++++ .../symbols/windows/extensions/mbr.py | 110 +-------- .../framework/symbols/windows/mbr.json | 208 +++++++++++++++++- 4 files changed, 292 insertions(+), 174 deletions(-) delete mode 100644 volatility3/framework/plugins/windows/mbrparser.py create mode 100644 volatility3/framework/plugins/windows/mbrscan.py diff --git a/volatility3/framework/plugins/windows/mbrparser.py b/volatility3/framework/plugins/windows/mbrparser.py deleted file mode 100644 index 10afa53d2..000000000 --- a/volatility3/framework/plugins/windows/mbrparser.py +++ /dev/null @@ -1,68 +0,0 @@ -# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0 -# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 -# - -import logging - -from volatility3.framework import exceptions, interfaces, renderers -from volatility3.framework.configuration import requirements -from volatility3.framework.renderers import format_hints -from volatility3.framework.symbols.windows.extensions import mbr -from volatility3.plugins import yarascan - -vollog = logging.getLogger(__name__) - - -class MBRParser(interfaces.plugins.PluginInterface): - """ Scans for and parses potential Master Boot Records (MBRs) """ - - _required_framework_version = (2, 0, 1) - - @classmethod - def get_requirements(cls): - return [ - requirements.TranslationLayerRequirement(name = 'primary', - description = 'Memory layer for the kernel', - architectures = ["Intel32", "Intel64"]), - requirements.VersionRequirement(name = 'yarascanner', component = yarascan.YaraScanner, - version = (2, 0, 0)), - ] - - @classmethod - def levenshtein(self, s1, s2): - if len(s1) < len(s2): - return self.levenshtein(s2, s1) - - if len(s2) == 0: - return len(s1) - - previous_row = range(len(s2) + 1) - for i, c1 in enumerate(s1): - current_row = [i + 1] - for j, c2 in enumerate(s2): - insertions = previous_row[j + 1] + 1 - deletions = current_row[j] + 1 - substitutions = previous_row[j] + (c1 != c2) - current_row.append(min(insertions, deletions, substitutions)) - previous_row = current_row - - return previous_row[-1] - - def _generator(self): - layer = self.context.layers[self.config['primary']] - # TODO : YARA RULE HEX - rules = yarascan.YaraScan.process_yara_options({'yara_rules': "55 aa"}) - - for offset, _rule_name, _name, _value in layer.scan(context = self.context, - scanner = yarascan.YaraScanner(rules = rules)): - try: - yield 0, (format_hints.Hex(offset), _name) - - except exceptions.PagedInvalidAddressException: - pass - - def run(self): - return renderers.TreeGrid([ - ('Offset', format_hints.Hex), - ("Name", str) - ], self._generator()) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py new file mode 100644 index 000000000..3876d2ac8 --- /dev/null +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -0,0 +1,80 @@ +# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +import logging + +from volatility3.framework import constants, interfaces, renderers, symbols +from volatility3.framework.configuration import requirements +from volatility3.framework.layers import scanners +from volatility3.framework.renderers import format_hints +from volatility3.framework.symbols import intermed +from volatility3.framework.symbols.windows.extensions import mbr + +vollog = logging.getLogger(__name__) + +class MBRScan(interfaces.plugins.PluginInterface): + """ Scans for and parses potential Master Boot Records (MBRs) """ + + _required_framework_version = (2, 0, 1) + _version = (1, 0, 0) + + @classmethod + def get_requirements(cls): + return [ + requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel', + architectures = ["Intel32", "Intel64"]) + ] + + def _generator(self): + kernel = self.context.modules[self.config['kernel']] + physical_layer_name = self.context.layers[kernel.layer_name].config.get('memory_layer', None) + + layer = self.context.layers[physical_layer_name] + architecture = "intel" if not symbols.symbol_table_is_64bit(self.context, kernel.symbol_table_name) else "intel64" + + symbol_table = intermed.IntermediateSymbolTable.create(context = self.context, + config_path = self.config_path, + sub_path = "windows", + filename = "mbr", + class_types = { + 'PARTITION_TABLE': mbr.PARTITION_TABLE, + 'PARTITION_ENTRY': mbr.PARTITION_ENTRY + }) + + partition_table_object = symbol_table + constants.BANG + "PARTITION_TABLE" + + mbr_signature = b"\x55\xAA" + mbr_length = 0x200 + boot_code_length = 0x1B8 + + for offset, _value in layer.scan(context = self.context, scanner = scanners.MultiStringScanner(patterns = [mbr_signature])): + mbr_start_offset = offset - (mbr_length - len(mbr_signature)) + partition_table = self.context.object(partition_table_object, offset = mbr_start_offset, layer_name = layer.name) + + boot_code = layer.read(mbr_start_offset, boot_code_length, pad = True) + + if boot_code: + all_zeros = boot_code.count(b"\x00") == len(boot_code) + + if not all_zeros: + partition_type = partition_table.FirstEntry.PartitionType + + + if partition_type.is_valid_choice: + yield 0, ( + format_hints.Hex(offset), + partition_type.lookup(), + interfaces.renderers.Disassembly(boot_code, 0, architecture), + format_hints.HexBytes(boot_code) + ) + else: + vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") + + def run(self): + return renderers.TreeGrid([ + ('Offset', format_hints.Hex), + ('PartitionType', str), + ("Disasm", interfaces.renderers.Disassembly), + ("Hexdump", format_hints.HexBytes) + ], self._generator()) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index eeb97d332..9bcefe12e 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -6,106 +6,14 @@ from volatility3.framework import objects import struct -PartitionTypes = { - 0x00:"Empty", - 0x01:"FAT12,CHS", - 0x04:"FAT16 16-32MB,CHS", - 0x05:"Microsoft Extended", - 0x06:"FAT16 32MB,CHS", - 0x07:"NTFS", - 0x0b:"FAT32,CHS", - 0x0c:"FAT32,LBA", - 0x0e:"FAT16, 32MB-2GB,LBA", - 0x0f:"Microsoft Extended, LBA", - 0x11:"Hidden FAT12,CHS", - 0x14:"Hidden FAT16,16-32MB,CHS", - 0x16:"Hidden FAT16,32MB-2GB,CHS", - 0x18:"AST SmartSleep Partition", - 0x1b:"Hidden FAT32,CHS", - 0x1c:"Hidden FAT32,LBA", - 0x1e:"Hidden FAT16,32MB-2GB,LBA", - 0x27:"PQservice", - 0x39:"Plan 9 partition", - 0x3c:"PartitionMagic recovery partition", - 0x42:"Microsoft MBR,Dynamic Disk", - 0x44:"GoBack partition", - 0x51:"Novell", - 0x52:"CP/M", - 0x63:"Unix System V", - 0x64:"PC-ARMOUR protected partition", - 0x82:"Solaris x86 or Linux Swap", - 0x83:"Linux", - 0x84:"Hibernation", - 0x85:"Linux Extended", - 0x86:"NTFS Volume Set", - 0x87:"NTFS Volume Set", - 0x9f:"BSD/OS", - 0xa0:"Hibernation", - 0xa1:"Hibernation", - 0xa5:"FreeBSD", - 0xa6:"OpenBSD", - 0xa8:"Mac OSX", - 0xa9:"NetBSD", - 0xab:"Mac OSX Boot", - 0xaf:"MacOS X HFS", - 0xb7:"BSDI", - 0xb8:"BSDI Swap", - 0xbb:"Boot Wizard hidden", - 0xbe:"Solaris 8 boot partition", - 0xd8:"CP/M-86", - 0xde:"Dell PowerEdge Server utilities (FAT fs)", - 0xdf:"DG/UX virtual disk manager partition", - 0xeb:"BeOS BFS", - 0xee:"EFI GPT Disk", - 0xef:"EFI System Partition", - 0xfb:"VMWare File System", - 0xfc:"VMWare Swap", -} +class PARTITION_TABLE(objects.StructType): + + def get_disk_signature(self) -> str: + signature = self.DiskSignature.values + return signature class PARTITION_ENTRY(objects.StructType): - def get_value(self, char): - padded = "\x00\x00\x00" + str(char) - val = int(struct.unpack('>I', padded)[0]) - return val - - def get_type(self): - return PartitionTypes.get(self.get_value(self.PartitionType), "Invalid") - - def is_bootable(self): - return self.get_value(self.BootableFlag) == 0x80 - - def is_bootable_and_used(self): - return self.is_bootable() and self.is_used() - - def is_valid(self): - return self.get_type() != "Invalid" - - def is_used(self): - return self.get_type() != "Empty" and self.is_valid() - - def StartingSector(self): - return self.StartingCHS[1] % 64 - - def StartingCylinder(self): - return (self.StartingCHS[1] - self.StartingSector()) * 4 + self.StartingCHS[2] - - def EndingSector(self): - return self.EndingCHS[1] % 64 - - def EndingCylinder(self): - return (self.EndingCHS[1] - self.EndingSector()) * 4 + self.EndingCHS[2] - - def __str__(self): - processed_entry = "" - bootable = self.get_value(self.BootableFlag) - processed_entry = "Boot flag: {0:#x} {1}\n".format(bootable, "(Bootable)" if self.is_bootable() else '') - processed_entry += "Partition type: {0:#x} ({1})\n".format(self.get_value(self.PartitionType), self.get_type()) - processed_entry += "Starting Sector (LBA): {0:#x} ({0})\n".format(self.StartingLBA) - processed_entry += "Starting CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format(self.StartingCylinder(), - self.StartingCHS[0], - self.StartingSector()) - processed_entry += "Ending CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format(self.EndingCylinder(), - self.EndingCHS[0], - self.EndingSector()) - processed_entry += "Size in sectors: {0:#x} ({0})\n\n".format(self.SizeInSectors) - return processed_entry \ No newline at end of file + + def get_partition_type(self, type: int) -> str: + + return "Hello" diff --git a/volatility3/framework/symbols/windows/mbr.json b/volatility3/framework/symbols/windows/mbr.json index 84162c96d..382af403e 100644 --- a/volatility3/framework/symbols/windows/mbr.json +++ b/volatility3/framework/symbols/windows/mbr.json @@ -2,9 +2,9 @@ "metadata": { "producer": { "version": "0.0.1", - "name": "Donghyun Kim", - "comment": "Using structures defined in File System Forensic Analysis pg 353+", - "datetime": "2022-01-03T13:37:00" + "name": "Donghyun Kim (@digitalisx99)", + "comment": "Using structures defined in File System Forensic Analysis pg 88+", + "datetime": "2022-03-05T10:53:00" }, "format": "6.1.0" }, @@ -32,6 +32,12 @@ "size": 4, "signed": false, "endian": "little" + }, + "int": { + "endian": "little", + "kind": "int", + "signed": true, + "size": 4 }, "unsigned short": { "kind": "int", @@ -45,12 +51,204 @@ "signed": false, "endian": "little" }, + "char": { + "endian": "little", + "kind": "char", + "signed": true, + "size": 1 + }, "wchar": { "kind": "int", "size": 2, "signed": true, "endian": "little" } - }, - "symbols": {} + }, + "symbols": {}, + "enums": { + "BootableFlag":{ + "base": "unsigned char", + "constants": { + "Bootable": 0, + "Non-Bootable": 128 + }, + "size": 1 + }, + "PartitionTypes": { + "base": "unsigned char", + "constants": { + "Empty": 0, + "FAT12,CHS": 1, + "FAT16 16-32MB,CHS": 4, + "Microsoft Extended": 5, + "FAT16 32MB,CHS": 6, + "NTFS": 7, + "FAT32,CHS": 11, + "FAT32,LBA": 12, + "FAT16, 32MB-2GB,LBA": 14, + "Microsoft Extended, LBA": 15, + "Hidden FAT12,CHS": 17, + "Hidden FAT16,16-32MB,CHS": 20, + "Hidden FAT16,32MB-2GB,CHS": 22, + "AST SmartSleep Partition": 24, + "Hidden FAT32,CHS": 27, + "Hidden FAT32,LBA": 28, + "Hidden FAT16,32MB-2GB,LBA": 30, + "PQservice": 39, + "Plan 9 partition": 57, + "PartitionMagic recovery partition": 60, + "Microsoft MBR,Dynamic Disk": 66, + "GoBack partition": 68, + "Novell": 81, + "CP/M": 82, + "Unix System V": 99, + "PC-ARMOUR protected partition": 100, + "Solaris x86 or Linux Swap": 130, + "Linux": 131, + "Hibernation": 132, + "Linux Extended": 133, + "NTFS Volume Set": 134, + "NTFS Volume Set": 135, + "BSD/OS": 159, + "Hibernation": 160, + "Hibernation": 161, + "FreeBSD": 165, + "OpenBSD": 166, + "Mac OSX": 168, + "NetBSD": 169, + "Mac OSX Boot": 171, + "MacOS X HFS": 175, + "BSDI": 183, + "BSDI Swap": 184, + "Boot Wizard hidden": 187, + "Solaris 8 boot partition": 190, + "CP/M-86": 216, + "Dell PowerEdge Server utilities (FAT fs)": 222, + "DG/UX virtual disk manager partition": 223, + "BeOS BFS": 235, + "EFI GPT Disk": 238, + "EFI System Partition": 239, + "VMWare File System": 251, + "VMWare Swap": 252 + }, + "size": 1 + } + }, + "user_types": { + "PARTITION_ENTRY":{ + "fields": { + "BootableFlag": { + "offset": 0, + "type": { + "kind": "enum", + "name": "BootableFlag" + } + }, + "StartingCHS": { + "offset": 1, + "type": { + "count": 3, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned char" + } + } + }, + "PartitionType": { + "offset": 4, + "type": { + "kind": "enum", + "name": "PartitionTypes" + } + }, + "EndingCHS": { + "offset": 5, + "type": { + "count": 3, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned char" + } + } + }, + "StartingLBA": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "SizeInSectors": { + "offset": 12, + "type": { + "kind": "base", + "name": "int" + } + } + }, + "kind": "struct", + "size": 16 + }, + "PARTITION_TABLE":{ + "fields":{ + "DiskSignature": { + "offset": 440, + "type": { + "count": 4, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned char" + } + } + }, + "Unused": { + "offset": 444, + "type": { + "kind": "base", + "name": "unsigned short" + } + }, + "FirstEntry":{ + "offset": 446, + "type": { + "kind": "struct", + "name": "PARTITION_ENTRY" + } + }, + "SecondEntry":{ + "offset": 462, + "type": { + "kind": "struct", + "name": "PARTITION_ENTRY" + } + }, + "ThirdEntry":{ + "offset": 478, + "type": { + "kind": "struct", + "name": "PARTITION_ENTRY" + } + }, + "FourthEntry":{ + "offset": 494, + "type": { + "kind": "struct", + "name": "PARTITION_ENTRY" + } + }, + "Signature":{ + "offset": 510, + "type": { + "kind": "base", + "name": "unsigned short" + } + } + }, + "kind": "struct", + "size": 512 + } + } } \ No newline at end of file From eda765d61d316d1d49d0008164a310743d710d5a Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 16:12:32 +0900 Subject: [PATCH 04/36] Update MBR Partition Entry Object Function --- .../framework/plugins/windows/mbrscan.py | 32 ++++++------ .../symbols/windows/extensions/mbr.py | 51 +++++++++++++++++-- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 3876d2ac8..6bfed2bde 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -56,25 +56,27 @@ class MBRScan(interfaces.plugins.PluginInterface): if boot_code: all_zeros = boot_code.count(b"\x00") == len(boot_code) - - if not all_zeros: - partition_type = partition_table.FirstEntry.PartitionType - - if partition_type.is_valid_choice: - yield 0, ( - format_hints.Hex(offset), - partition_type.lookup(), - interfaces.renderers.Disassembly(boot_code, 0, architecture), - format_hints.HexBytes(boot_code) - ) + if not all_zeros: + partition_entry_list = ["FirstEntry", "SecondEntry", "ThirdEntry", "FourthEntry"] + #partition_type = getattr(partition_table, "FirstEntry").PartitionType + yield 0, ( + format_hints.Hex(offset), + partition_table.FirstEntry.get_bootable_flag(), + partition_table.FirstEntry.get_partition_type(), + format_hints.Hex(partition_table.FirstEntry.get_starting_chs()) + #interfaces.renderers.Disassembly(boot_code, 0, architecture), + #format_hints.HexBytes(boot_code) + ) else: vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") def run(self): return renderers.TreeGrid([ - ('Offset', format_hints.Hex), - ('PartitionType', str), - ("Disasm", interfaces.renderers.Disassembly), - ("Hexdump", format_hints.HexBytes) + ("Offset", format_hints.Hex), + ("Bootable", bool), + ("Partition Type", str), + ("Starting CHS",format_hints.Hex) + #("Disasm", interfaces.renderers.Disassembly), + #("Hexdump", format_hints.HexBytes) ], self._generator()) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index 9bcefe12e..c02d741df 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -4,8 +4,6 @@ from volatility3.framework import objects -import struct - class PARTITION_TABLE(objects.StructType): def get_disk_signature(self) -> str: @@ -14,6 +12,49 @@ class PARTITION_TABLE(objects.StructType): class PARTITION_ENTRY(objects.StructType): - def get_partition_type(self, type: int) -> str: - - return "Hello" + def get_bootable_flag(self) -> int: + return self.BootableFlag + + def is_bootable(self) -> bool: + return False if not (self.BootableFlag == 0x80) else True + + def get_partition_type(self) -> str: + return self.PartitionType.lookup() if self.PartitionType.is_valid_choice else "Not Defined PartitionType" + + def get_starting_chs(self): + return self.StartingCHS[0] + + def get_ending_chs(self): + return self.EndingCHS[0] + + def get_starting_sector(self): + return self.StartingCHS[1] % 64 + + def get_starting_cylinder(self): + return (self.StartingCHS[1] - self.get_starting_sector()) * 4 + self.StartingCHS[2] + + def get_ending_sector(self): + return self.EndingCHS[1] % 64 + + def get_ending_cylinder(self): + return (self.EndingCHS[1] - self.get_ending_sector()) * 4 + self.EndingCHS[2] + + def get_starting_lba(self): + return self.StartingLBA + + def get_size_in_sectors(self): + return self.SizeInSectors + + def __str__(self): + processed_entry = "" + processed_entry = "Boot flag: {0:#x} {1}\n".format(self.is_bootable(), "(Bootable)" if self.is_bootable() else '') + processed_entry += "Partition type: {0:#x} ({1})\n".format(self.get_value(self.PartitionType), self.get_type()) + processed_entry += "Starting Sector (LBA): {0:#x} ({0})\n".format(self.StartingLBA) + processed_entry += "Starting CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format(self.StartingCylinder(), + self.StartingCHS[0], + self.StartingSector()) + processed_entry += "Ending CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format(self.EndingCylinder(), + self.EndingCHS[0], + self.EndingSector()) + processed_entry += "Size in sectors: {0:#x} ({0})\n\n".format(self.SizeInSectors) + return processed_entry From e0a512e9ff17a81b7c36c79da01c894eab72bbc5 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 16:13:25 +0900 Subject: [PATCH 05/36] Add EOF of MBR Symbol --- volatility3/framework/symbols/windows/mft.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/windows/mft.json b/volatility3/framework/symbols/windows/mft.json index e5de8f3fa..6881c92be 100644 --- a/volatility3/framework/symbols/windows/mft.json +++ b/volatility3/framework/symbols/windows/mft.json @@ -466,4 +466,4 @@ "size": 1024 } } -} \ No newline at end of file +} From b01333115b06ba106250916e2390888870794b13 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 17:08:27 +0900 Subject: [PATCH 06/36] __str__ Formatting --- .../framework/plugins/windows/mbrscan.py | 26 ++++++------ .../symbols/windows/extensions/mbr.py | 40 +++++++++++++------ .../framework/symbols/windows/mbr.json | 2 +- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 6bfed2bde..beda342fb 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -58,15 +58,18 @@ class MBRScan(interfaces.plugins.PluginInterface): all_zeros = boot_code.count(b"\x00") == len(boot_code) if not all_zeros: - partition_entry_list = ["FirstEntry", "SecondEntry", "ThirdEntry", "FourthEntry"] - #partition_type = getattr(partition_table, "FirstEntry").PartitionType + + first_entry = partition_table.FirstEntry + second_entry = partition_table.SecondEntry + third_entry = partition_table.ThirdEntry + fourth_entry = partition_table.FourthEntry + yield 0, ( format_hints.Hex(offset), - partition_table.FirstEntry.get_bootable_flag(), - partition_table.FirstEntry.get_partition_type(), - format_hints.Hex(partition_table.FirstEntry.get_starting_chs()) - #interfaces.renderers.Disassembly(boot_code, 0, architecture), - #format_hints.HexBytes(boot_code) + partition_table.get_disk_signature(), + str(partition_table.FirstEntry), + interfaces.renderers.Disassembly(boot_code, 0, architecture), + format_hints.HexBytes(boot_code) ) else: vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") @@ -74,9 +77,8 @@ class MBRScan(interfaces.plugins.PluginInterface): def run(self): return renderers.TreeGrid([ ("Offset", format_hints.Hex), - ("Bootable", bool), - ("Partition Type", str), - ("Starting CHS",format_hints.Hex) - #("Disasm", interfaces.renderers.Disassembly), - #("Hexdump", format_hints.HexBytes) + ("Disk Signature", str), + ("First Entry", str), + ("Disasm", interfaces.renderers.Disassembly), + ("Hexdump", format_hints.HexBytes) ], self._generator()) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index c02d741df..e4aaefad1 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -7,8 +7,12 @@ from volatility3.framework import objects class PARTITION_TABLE(objects.StructType): def get_disk_signature(self) -> str: - signature = self.DiskSignature.values - return signature + return "{0:02x}-{1:02x}-{2:02x}-{3:02x}".format( + self.DiskSignature[0], + self.DiskSignature[1], + self.DiskSignature[2], + self.DiskSignature[3] + ) class PARTITION_ENTRY(objects.StructType): @@ -46,15 +50,25 @@ class PARTITION_ENTRY(objects.StructType): return self.SizeInSectors def __str__(self): - processed_entry = "" - processed_entry = "Boot flag: {0:#x} {1}\n".format(self.is_bootable(), "(Bootable)" if self.is_bootable() else '') - processed_entry += "Partition type: {0:#x} ({1})\n".format(self.get_value(self.PartitionType), self.get_type()) - processed_entry += "Starting Sector (LBA): {0:#x} ({0})\n".format(self.StartingLBA) - processed_entry += "Starting CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format(self.StartingCylinder(), - self.StartingCHS[0], - self.StartingSector()) - processed_entry += "Ending CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format(self.EndingCylinder(), - self.EndingCHS[0], - self.EndingSector()) - processed_entry += "Size in sectors: {0:#x} ({0})\n\n".format(self.SizeInSectors) + processed_entry = "========= Partition Info =========\n" + processed_entry += "Boot Flag: {0:#x} {1}\n".format( + self.is_bootable(), + "(Bootable)" if self.is_bootable() else '' + ) + processed_entry += "Partition Type: {0:#x} ({1})\n".format( + self.PartitionType, + self.get_partition_type() + ) + processed_entry += "Starting Sector (LBA): {0:#x} ({0})\n".format(self.get_starting_lba()) + processed_entry += "Starting CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format( + self.get_starting_cylinder(), + self.get_starting_chs(), + self.get_starting_sector() + ) + processed_entry += "Ending CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format( + self.get_ending_cylinder(), + self.get_ending_chs(), + self.get_ending_sector() + ) + processed_entry += "Size in sectors: {0:#x} ({0})\n\n".format(self.get_size_in_sectors()) return processed_entry diff --git a/volatility3/framework/symbols/windows/mbr.json b/volatility3/framework/symbols/windows/mbr.json index 382af403e..9173633d1 100644 --- a/volatility3/framework/symbols/windows/mbr.json +++ b/volatility3/framework/symbols/windows/mbr.json @@ -251,4 +251,4 @@ "size": 512 } } -} \ No newline at end of file +} From 5de6462fae23f4702d5b7c209e9d151c6589dd91 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 17:09:36 +0900 Subject: [PATCH 07/36] Restore mft.json --- volatility3/framework/symbols/windows/mft.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/windows/mft.json b/volatility3/framework/symbols/windows/mft.json index 6881c92be..e5de8f3fa 100644 --- a/volatility3/framework/symbols/windows/mft.json +++ b/volatility3/framework/symbols/windows/mft.json @@ -466,4 +466,4 @@ "size": 1024 } } -} +} \ No newline at end of file From b6a14e6de4fab2ec2473b1f3492a6a69f063f1b3 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 23:42:21 +0900 Subject: [PATCH 08/36] Add Symbol code comment, hash --- .../framework/plugins/windows/mbrscan.py | 29 +++++++++++++------ .../symbols/windows/extensions/mbr.py | 26 +++++++++++++---- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index beda342fb..fcc9dabb1 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -3,6 +3,7 @@ # import logging +import hashlib from volatility3.framework import constants, interfaces, renderers, symbols from volatility3.framework.configuration import requirements @@ -52,22 +53,30 @@ class MBRScan(interfaces.plugins.PluginInterface): mbr_start_offset = offset - (mbr_length - len(mbr_signature)) partition_table = self.context.object(partition_table_object, offset = mbr_start_offset, layer_name = layer.name) - boot_code = layer.read(mbr_start_offset, boot_code_length, pad = True) + full_mbr = layer.read(mbr_start_offset, mbr_length, pad = True) + boot_code = full_mbr[:boot_code_length] if boot_code: all_zeros = boot_code.count(b"\x00") == len(boot_code) if not all_zeros: - - first_entry = partition_table.FirstEntry - second_entry = partition_table.SecondEntry - third_entry = partition_table.ThirdEntry - fourth_entry = partition_table.FourthEntry + bootcode_hash = hashlib.md5(boot_code).hexdigest() + full_bootcode_hash = hashlib.md5(full_mbr).hexdigest() + partition_entries = [ partition_table.FirstEntry, partition_table.SecondEntry, + partition_table.ThirdEntry, partition_table.FourthEntry ] + partition_info = "" + + for index, partition_entry_object in enumerate(partition_entries): + partition_entry_object.set_index(index) + partition_info += str(partition_entry_object) + yield 0, ( format_hints.Hex(offset), partition_table.get_disk_signature(), - str(partition_table.FirstEntry), + bootcode_hash, + full_bootcode_hash, + partition_info, interfaces.renderers.Disassembly(boot_code, 0, architecture), format_hints.HexBytes(boot_code) ) @@ -76,9 +85,11 @@ class MBRScan(interfaces.plugins.PluginInterface): def run(self): return renderers.TreeGrid([ - ("Offset", format_hints.Hex), + ("Potential MBR at Physical Offset", format_hints.Hex), ("Disk Signature", str), - ("First Entry", str), + ("Bootcode md5", str), + ("Bootcode (FULL) md5", str), + ("Partition Entries Info", str), ("Disasm", interfaces.renderers.Disassembly), ("Hexdump", format_hints.HexBytes) ], self._generator()) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index e4aaefad1..8e02db822 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -7,6 +7,7 @@ from volatility3.framework import objects class PARTITION_TABLE(objects.StructType): def get_disk_signature(self) -> str: + """Get Disk Signature (GUID).""" return "{0:02x}-{1:02x}-{2:02x}-{3:02x}".format( self.DiskSignature[0], self.DiskSignature[1], @@ -15,42 +16,57 @@ class PARTITION_TABLE(objects.StructType): ) class PARTITION_ENTRY(objects.StructType): + + def set_index(self, index:int): + self.index = index def get_bootable_flag(self) -> int: + """Get Bootable Flag.""" return self.BootableFlag def is_bootable(self) -> bool: + """Check Bootable Partition.""" return False if not (self.BootableFlag == 0x80) else True def get_partition_type(self) -> str: + """Get Partition Type.""" return self.PartitionType.lookup() if self.PartitionType.is_valid_choice else "Not Defined PartitionType" def get_starting_chs(self): + """Get Starting CHS (Cylinder Header Sector) Address.""" return self.StartingCHS[0] def get_ending_chs(self): + """Get Ending CHS (Cylinder Header Sector) Address.""" return self.EndingCHS[0] def get_starting_sector(self): + """Get Starting Sector.""" return self.StartingCHS[1] % 64 - def get_starting_cylinder(self): - return (self.StartingCHS[1] - self.get_starting_sector()) * 4 + self.StartingCHS[2] - def get_ending_sector(self): + """Get Ending Sector.""" return self.EndingCHS[1] % 64 + def get_starting_cylinder(self): + """Get Starting Cylinder.""" + return (self.StartingCHS[1] - self.get_starting_sector()) * 4 + self.StartingCHS[2] + def get_ending_cylinder(self): + """Get Ending Cylinder.""" return (self.EndingCHS[1] - self.get_ending_sector()) * 4 + self.EndingCHS[2] def get_starting_lba(self): + """Get Starting LBA (Logical Block Addressing).""" return self.StartingLBA def get_size_in_sectors(self): + """Get Size in Sectors.""" return self.SizeInSectors def __str__(self): - processed_entry = "========= Partition Info =========\n" + """Get overall of Partition Entry Info""" + processed_entry = "\n===== Partition Table #{0} =====\n".format(self.index+1) processed_entry += "Boot Flag: {0:#x} {1}\n".format( self.is_bootable(), "(Bootable)" if self.is_bootable() else '' @@ -70,5 +86,5 @@ class PARTITION_ENTRY(objects.StructType): self.get_ending_chs(), self.get_ending_sector() ) - processed_entry += "Size in sectors: {0:#x} ({0})\n\n".format(self.get_size_in_sectors()) + processed_entry += "Size in Sectors: {0:#x} ({0})\n".format(self.get_size_in_sectors()) return processed_entry From 7c00b2f4ea04a9d7fa171ee37301e090d42ae383 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 10 Mar 2022 00:13:46 +0900 Subject: [PATCH 09/36] Add Code Comment, Hash Funtion, Exception --- .../framework/plugins/windows/mbrscan.py | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index fcc9dabb1..f0d52f418 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -5,7 +5,7 @@ import logging import hashlib -from volatility3.framework import constants, interfaces, renderers, symbols +from volatility3.framework import constants, exceptions, interfaces, renderers, symbols from volatility3.framework.configuration import requirements from volatility3.framework.layers import scanners from volatility3.framework.renderers import format_hints @@ -27,13 +27,19 @@ class MBRScan(interfaces.plugins.PluginInterface): architectures = ["Intel32", "Intel64"]) ] + @classmethod + def get_hash(cls, data:bytes) -> str: + return hashlib.md5(data).hexdigest() + def _generator(self): kernel = self.context.modules[self.config['kernel']] physical_layer_name = self.context.layers[kernel.layer_name].config.get('memory_layer', None) + # Decide of Memory Dump Architecture layer = self.context.layers[physical_layer_name] architecture = "intel" if not symbols.symbol_table_is_64bit(self.context, kernel.symbol_table_name) else "intel64" + # Read in the Symbol File symbol_table = intermed.IntermediateSymbolTable.create(context = self.context, config_path = self.config_path, sub_path = "windows", @@ -45,43 +51,51 @@ class MBRScan(interfaces.plugins.PluginInterface): partition_table_object = symbol_table + constants.BANG + "PARTITION_TABLE" + # Define Signature and Data Length mbr_signature = b"\x55\xAA" mbr_length = 0x200 - boot_code_length = 0x1B8 + bootcode_length = 0x1B8 + # Scan the Layer for Raw Master Boot Record (MBR) and parse the fields for offset, _value in layer.scan(context = self.context, scanner = scanners.MultiStringScanner(patterns = [mbr_signature])): - mbr_start_offset = offset - (mbr_length - len(mbr_signature)) - partition_table = self.context.object(partition_table_object, offset = mbr_start_offset, layer_name = layer.name) + try: + mbr_start_offset = offset - (mbr_length - len(mbr_signature)) + partition_table = self.context.object(partition_table_object, offset = mbr_start_offset, layer_name = layer.name) - full_mbr = layer.read(mbr_start_offset, mbr_length, pad = True) - boot_code = full_mbr[:boot_code_length] - - if boot_code: - all_zeros = boot_code.count(b"\x00") == len(boot_code) - - if not all_zeros: - bootcode_hash = hashlib.md5(boot_code).hexdigest() - full_bootcode_hash = hashlib.md5(full_mbr).hexdigest() - - partition_entries = [ partition_table.FirstEntry, partition_table.SecondEntry, - partition_table.ThirdEntry, partition_table.FourthEntry ] - partition_info = "" - - for index, partition_entry_object in enumerate(partition_entries): - partition_entry_object.set_index(index) - partition_info += str(partition_entry_object) + # Extract only BootCode + full_mbr = layer.read(mbr_start_offset, mbr_length, pad = True) + bootcode = full_mbr[:bootcode_length] - yield 0, ( - format_hints.Hex(offset), - partition_table.get_disk_signature(), - bootcode_hash, - full_bootcode_hash, - partition_info, - interfaces.renderers.Disassembly(boot_code, 0, architecture), - format_hints.HexBytes(boot_code) - ) - else: - vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") + if bootcode: + all_zeros = bootcode.count(b"\x00") == len(bootcode) + + if not all_zeros: + partition_entries = [ + partition_table.FirstEntry, + partition_table.SecondEntry, + partition_table.ThirdEntry, + partition_table.FourthEntry + ] + partition_info = "\n" + + for index, partition_entry_object in enumerate(partition_entries): + partition_entry_object.set_index(index) + partition_info += str(partition_entry_object) + + yield 0, ( + format_hints.Hex(offset), + partition_table.get_disk_signature(), + self.get_hash(bootcode), + self.get_hash(full_mbr), + partition_info, + interfaces.renderers.Disassembly(bootcode, 0, architecture), + format_hints.HexBytes(bootcode) + ) + else: + vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") + + except exceptions.PagedInvalidAddressException: + pass def run(self): return renderers.TreeGrid([ From a35fa04f00929dc1a4e50c3db5e107bdcf6b49b4 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 10 Mar 2022 01:12:49 +0900 Subject: [PATCH 10/36] Update BootableFlag Symbol --- .../symbols/windows/extensions/mbr.py | 7 ++++-- .../framework/symbols/windows/mbr.json | 22 ++++--------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index 8e02db822..7cb4c1463 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -2,6 +2,8 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # +import struct + from volatility3.framework import objects class PARTITION_TABLE(objects.StructType): @@ -18,6 +20,7 @@ class PARTITION_TABLE(objects.StructType): class PARTITION_ENTRY(objects.StructType): def set_index(self, index:int): + """Set Partition Entry Index.""" self.index = index def get_bootable_flag(self) -> int: @@ -26,7 +29,7 @@ class PARTITION_ENTRY(objects.StructType): def is_bootable(self) -> bool: """Check Bootable Partition.""" - return False if not (self.BootableFlag == 0x80) else True + return False if not (self.get_bootable_flag() == 0x80) else True def get_partition_type(self) -> str: """Get Partition Type.""" @@ -68,7 +71,7 @@ class PARTITION_ENTRY(objects.StructType): """Get overall of Partition Entry Info""" processed_entry = "\n===== Partition Table #{0} =====\n".format(self.index+1) processed_entry += "Boot Flag: {0:#x} {1}\n".format( - self.is_bootable(), + self.get_bootable_flag(), "(Bootable)" if self.is_bootable() else '' ) processed_entry += "Partition Type: {0:#x} ({1})\n".format( diff --git a/volatility3/framework/symbols/windows/mbr.json b/volatility3/framework/symbols/windows/mbr.json index 9173633d1..122c020c3 100644 --- a/volatility3/framework/symbols/windows/mbr.json +++ b/volatility3/framework/symbols/windows/mbr.json @@ -34,10 +34,10 @@ "endian": "little" }, "int": { - "endian": "little", "kind": "int", + "size": 4, "signed": true, - "size": 4 + "endian": "little" }, "unsigned short": { "kind": "int", @@ -51,12 +51,6 @@ "signed": false, "endian": "little" }, - "char": { - "endian": "little", - "kind": "char", - "signed": true, - "size": 1 - }, "wchar": { "kind": "int", "size": 2, @@ -66,14 +60,6 @@ }, "symbols": {}, "enums": { - "BootableFlag":{ - "base": "unsigned char", - "constants": { - "Bootable": 0, - "Non-Bootable": 128 - }, - "size": 1 - }, "PartitionTypes": { "base": "unsigned char", "constants": { @@ -140,8 +126,8 @@ "BootableFlag": { "offset": 0, "type": { - "kind": "enum", - "name": "BootableFlag" + "kind": "base", + "name": "unsigned char" } }, "StartingCHS": { From 1b71aad3669ea4325aecf564ffb1e1c5999de139 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 10 Mar 2022 01:12:49 +0900 Subject: [PATCH 11/36] Update BootableFlag Symbol --- .../symbols/windows/extensions/mbr.py | 7 ++++-- .../framework/symbols/windows/mbr.json | 22 ++++--------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index 8e02db822..7cb4c1463 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -2,6 +2,8 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # +import struct + from volatility3.framework import objects class PARTITION_TABLE(objects.StructType): @@ -18,6 +20,7 @@ class PARTITION_TABLE(objects.StructType): class PARTITION_ENTRY(objects.StructType): def set_index(self, index:int): + """Set Partition Entry Index.""" self.index = index def get_bootable_flag(self) -> int: @@ -26,7 +29,7 @@ class PARTITION_ENTRY(objects.StructType): def is_bootable(self) -> bool: """Check Bootable Partition.""" - return False if not (self.BootableFlag == 0x80) else True + return False if not (self.get_bootable_flag() == 0x80) else True def get_partition_type(self) -> str: """Get Partition Type.""" @@ -68,7 +71,7 @@ class PARTITION_ENTRY(objects.StructType): """Get overall of Partition Entry Info""" processed_entry = "\n===== Partition Table #{0} =====\n".format(self.index+1) processed_entry += "Boot Flag: {0:#x} {1}\n".format( - self.is_bootable(), + self.get_bootable_flag(), "(Bootable)" if self.is_bootable() else '' ) processed_entry += "Partition Type: {0:#x} ({1})\n".format( diff --git a/volatility3/framework/symbols/windows/mbr.json b/volatility3/framework/symbols/windows/mbr.json index 9173633d1..122c020c3 100644 --- a/volatility3/framework/symbols/windows/mbr.json +++ b/volatility3/framework/symbols/windows/mbr.json @@ -34,10 +34,10 @@ "endian": "little" }, "int": { - "endian": "little", "kind": "int", + "size": 4, "signed": true, - "size": 4 + "endian": "little" }, "unsigned short": { "kind": "int", @@ -51,12 +51,6 @@ "signed": false, "endian": "little" }, - "char": { - "endian": "little", - "kind": "char", - "signed": true, - "size": 1 - }, "wchar": { "kind": "int", "size": 2, @@ -66,14 +60,6 @@ }, "symbols": {}, "enums": { - "BootableFlag":{ - "base": "unsigned char", - "constants": { - "Bootable": 0, - "Non-Bootable": 128 - }, - "size": 1 - }, "PartitionTypes": { "base": "unsigned char", "constants": { @@ -140,8 +126,8 @@ "BootableFlag": { "offset": 0, "type": { - "kind": "enum", - "name": "BootableFlag" + "kind": "base", + "name": "unsigned char" } }, "StartingCHS": { From f97348616f559f0849264fbba2b8e9bb8cdae5b8 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 14 Mar 2022 09:47:34 +0900 Subject: [PATCH 12/36] Define 'all_zero' default value, Update output column name --- volatility3/framework/plugins/windows/mbrscan.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index f0d52f418..8f0d7a3a4 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -66,6 +66,8 @@ class MBRScan(interfaces.plugins.PluginInterface): full_mbr = layer.read(mbr_start_offset, mbr_length, pad = True) bootcode = full_mbr[:bootcode_length] + all_zeros = None + if bootcode: all_zeros = bootcode.count(b"\x00") == len(bootcode) @@ -101,8 +103,8 @@ class MBRScan(interfaces.plugins.PluginInterface): return renderers.TreeGrid([ ("Potential MBR at Physical Offset", format_hints.Hex), ("Disk Signature", str), - ("Bootcode md5", str), - ("Bootcode (FULL) md5", str), + ("Bootcode MD5", str), + ("Full MBR MD5", str), ("Partition Entries Info", str), ("Disasm", interfaces.renderers.Disassembly), ("Hexdump", format_hints.HexBytes) From fa723ec134e881cc7c3a4987bb1b6eb176a45ac8 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 16 Mar 2022 01:29:20 +0000 Subject: [PATCH 13/36] CLI: Implement specifying a config name to write --- volatility3/cli/__init__.py | 25 +++++++++++++++++++++++-- volatility3/cli/volshell/__init__.py | 15 +++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index 4cdbd26e8..8cf9621a3 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -19,6 +19,7 @@ import os import sys import tempfile import traceback +from datetime import datetime from typing import Any, Dict, Type, Union from urllib import parse, request @@ -157,6 +158,10 @@ class CommandLine: help = "Write configuration JSON file out to config.json", default = False, action = 'store_true') + parser.add_argument("--save-config", + help = "Save configuration JSON file to a file", + default = None, + type = str) parser.add_argument("--clear-cache", help = "Clears out all short-term cached items", default = False, @@ -320,8 +325,15 @@ class CommandLine: self.file_handler_class_factory()) if args.write_config: - vollog.debug("Writing out configuration data to config.json") - with open("config.json", "w") as f: + args.save_config = 'config.json' + if args.save_config: + vollog.debug("Writing out configuration data to {args.save_config}") + if os.path.exists(os.path.abspath(args.save_config)): + # Backup existing file + backup_filename = self.find_backup_filename(args.save_config) + vollog.debug(f"Backing up existing file to {backup_filename}") + os.rename(args.save_config, backup_filename) + with open(args.save_config, "w") as f: json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2) except exceptions.UnsatisfiedException as excp: self.process_unsatisfied_exceptions(excp) @@ -334,6 +346,15 @@ class CommandLine: except (exceptions.VolatilityException) as excp: self.process_exceptions(excp) + def find_backup_filename(self, original: str): + suffix = "" + new_name = f"{original}.{datetime.strftime(datetime.today(), '%y%m%d')}.bak" + while os.path.exists(f"{new_name}{suffix}"): + if not suffix: + suffix = 1 + suffix += 1 + return f"{new_name}{suffix}" + @classmethod def location_from_file(cls, filename: str) -> str: """Returns the URL location from a file parameter (which may be a URL) diff --git a/volatility3/cli/volshell/__init__.py b/volatility3/cli/volshell/__init__.py index 812d44337..42e82e5bf 100644 --- a/volatility3/cli/volshell/__init__.py +++ b/volatility3/cli/volshell/__init__.py @@ -85,6 +85,10 @@ class VolShell(cli.CommandLine): help = "Write configuration JSON file out to config.json", default = False, action = 'store_true') + parser.add_argument("--save-config", + help = "Save configuration JSON file to a file", + default = None, + type = str) parser.add_argument("--clear-cache", help = "Clears out all short-term cached items", default = False, @@ -234,8 +238,15 @@ class VolShell(cli.CommandLine): self.file_handler_class_factory()) if args.write_config: - vollog.debug("Writing out configuration data to config.json") - with open("config.json", "w") as f: + args.save_config = 'config.json' + if args.save_config: + vollog.debug("Writing out configuration data to {args.save_config}") + if os.path.exists(os.path.abspath(args.save_config)): + # Backup existing file + backup_filename = self.find_backup_filename(args.save_config) + vollog.debug(f"Backing up existing file to {backup_filename}") + os.rename(args.save_config, backup_filename) + with open(args.save_config, "w") as f: json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2) except exceptions.UnsatisfiedException as excp: self.process_unsatisfied_exceptions(excp) From eb38756dbebbd3a6cae366ab5cc5b045faa00b10 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 16 Mar 2022 01:35:47 +0000 Subject: [PATCH 14/36] CLI: Add deprecation warning to --write-config --- volatility3/cli/__init__.py | 5 ++++- volatility3/cli/volshell/__init__.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index 8cf9621a3..d22fa154a 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -324,11 +324,14 @@ class CommandLine: constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback, self.file_handler_class_factory()) + backup_filename = True if args.write_config: + vollog.warning('Use of --write-config has been deprecated, replaced by --save-config ') args.save_config = 'config.json' + backup_filename = False if args.save_config: vollog.debug("Writing out configuration data to {args.save_config}") - if os.path.exists(os.path.abspath(args.save_config)): + if os.path.exists(os.path.abspath(args.save_config)) and backup_filename: # Backup existing file backup_filename = self.find_backup_filename(args.save_config) vollog.debug(f"Backing up existing file to {backup_filename}") diff --git a/volatility3/cli/volshell/__init__.py b/volatility3/cli/volshell/__init__.py index 42e82e5bf..fbf79b117 100644 --- a/volatility3/cli/volshell/__init__.py +++ b/volatility3/cli/volshell/__init__.py @@ -237,11 +237,14 @@ class VolShell(cli.CommandLine): constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback, self.file_handler_class_factory()) + backup_filename = True if args.write_config: + vollog.warning('Use of --write-config has been deprecated, replaced by --save-config ') args.save_config = 'config.json' + backup_filename = False if args.save_config: vollog.debug("Writing out configuration data to {args.save_config}") - if os.path.exists(os.path.abspath(args.save_config)): + if os.path.exists(os.path.abspath(args.save_config)) and backup_filename: # Backup existing file backup_filename = self.find_backup_filename(args.save_config) vollog.debug(f"Backing up existing file to {backup_filename}") From 0f4f4f2b3ac652c0a2ed1a3eaf0ef464f8f939fa Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 16 Mar 2022 01:41:18 +0000 Subject: [PATCH 15/36] CLI: Add configuration option for blatting over config files --- volatility3/cli/__init__.py | 2 +- volatility3/cli/volshell/__init__.py | 2 +- volatility3/framework/constants/__init__.py | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index d22fa154a..1933607c3 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -324,7 +324,7 @@ class CommandLine: constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback, self.file_handler_class_factory()) - backup_filename = True + backup_filename = constants.BACKUP_EXISTING_CONFIG_OUTPUT if args.write_config: vollog.warning('Use of --write-config has been deprecated, replaced by --save-config ') args.save_config = 'config.json' diff --git a/volatility3/cli/volshell/__init__.py b/volatility3/cli/volshell/__init__.py index fbf79b117..f3bed1b73 100644 --- a/volatility3/cli/volshell/__init__.py +++ b/volatility3/cli/volshell/__init__.py @@ -237,7 +237,7 @@ class VolShell(cli.CommandLine): constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback, self.file_handler_class_factory()) - backup_filename = True + backup_filename = constants.BACKUP_EXISTING_CONFIG_OUTPUT if args.write_config: vollog.warning('Use of --write-config has been deprecated, replaced by --save-config ') args.save_config = 'config.json' diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index 665e62d30..4af4408af 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -9,7 +9,7 @@ volatility This includes default scanning block sizes, etc. import enum import os.path import sys -from typing import Optional, Callable +from typing import Callable, Optional import volatility3.framework.constants.linux import volatility3.framework.constants.windows @@ -80,6 +80,7 @@ ProgressCallback = Optional[Callable[[float, str], None]] OS_CATEGORIES = ['windows', 'mac', 'linux'] + class Parallelism(enum.IntEnum): """An enumeration listing the different types of parallelism applied to volatility.""" @@ -100,3 +101,6 @@ OFFLINE = False REMOTE_ISF_URL = None # 'http://localhost:8000/banners.json' """Remote URL to query for a list of ISF addresses""" + +BACKUP_EXISTING_CONFIG_OUTPUT = True +"""Whether existing files are backed up or overwritten when writing configuration output""" From 1645443d3bad7e672dec09d22ddc95f5f4d7e272 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 16 Mar 2022 22:30:57 +0900 Subject: [PATCH 16/36] Remove Hexdump Column --- volatility3/framework/plugins/windows/mbrscan.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 8f0d7a3a4..3db107567 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -90,8 +90,7 @@ class MBRScan(interfaces.plugins.PluginInterface): self.get_hash(bootcode), self.get_hash(full_mbr), partition_info, - interfaces.renderers.Disassembly(bootcode, 0, architecture), - format_hints.HexBytes(bootcode) + interfaces.renderers.Disassembly(bootcode, 0, architecture) ) else: vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") @@ -106,6 +105,5 @@ class MBRScan(interfaces.plugins.PluginInterface): ("Bootcode MD5", str), ("Full MBR MD5", str), ("Partition Entries Info", str), - ("Disasm", interfaces.renderers.Disassembly), - ("Hexdump", format_hints.HexBytes) + ("Disasm", interfaces.renderers.Disassembly) ], self._generator()) From b02783baf11861847681fc8a5362c173a7772baf Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 16 Mar 2022 22:37:44 +0900 Subject: [PATCH 17/36] Remove index initialize, __str__ method by partition entry logic update --- .../symbols/windows/extensions/mbr.py | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index 7cb4c1463..3fdb67ee3 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -18,10 +18,6 @@ class PARTITION_TABLE(objects.StructType): ) class PARTITION_ENTRY(objects.StructType): - - def set_index(self, index:int): - """Set Partition Entry Index.""" - self.index = index def get_bootable_flag(self) -> int: """Get Bootable Flag.""" @@ -66,28 +62,3 @@ class PARTITION_ENTRY(objects.StructType): def get_size_in_sectors(self): """Get Size in Sectors.""" return self.SizeInSectors - - def __str__(self): - """Get overall of Partition Entry Info""" - processed_entry = "\n===== Partition Table #{0} =====\n".format(self.index+1) - processed_entry += "Boot Flag: {0:#x} {1}\n".format( - self.get_bootable_flag(), - "(Bootable)" if self.is_bootable() else '' - ) - processed_entry += "Partition Type: {0:#x} ({1})\n".format( - self.PartitionType, - self.get_partition_type() - ) - processed_entry += "Starting Sector (LBA): {0:#x} ({0})\n".format(self.get_starting_lba()) - processed_entry += "Starting CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format( - self.get_starting_cylinder(), - self.get_starting_chs(), - self.get_starting_sector() - ) - processed_entry += "Ending CHS: Cylinder: {0} Head: {1} Sector: {2}\n".format( - self.get_ending_cylinder(), - self.get_ending_chs(), - self.get_ending_sector() - ) - processed_entry += "Size in Sectors: {0:#x} ({0})\n".format(self.get_size_in_sectors()) - return processed_entry From 02394f89a8120d9cfc09bdf1e123d3a3cd3984a5 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 17 Mar 2022 02:22:51 +0900 Subject: [PATCH 18/36] Add return type hint, Add full option, Update yield data --- .../framework/plugins/windows/mbrscan.py | 184 +++++++++++++++--- 1 file changed, 157 insertions(+), 27 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 3db107567..60b403ae0 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -5,6 +5,8 @@ import logging import hashlib +from typing import Iterator, List, Tuple + from volatility3.framework import constants, exceptions, interfaces, renderers, symbols from volatility3.framework.configuration import requirements from volatility3.framework.layers import scanners @@ -21,17 +23,21 @@ class MBRScan(interfaces.plugins.PluginInterface): _version = (1, 0, 0) @classmethod - def get_requirements(cls): + def get_requirements(cls)-> List[interfaces.configuration.RequirementInterface]: return [ requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel', - architectures = ["Intel32", "Intel64"]) + architectures = ["Intel32", "Intel64"]), + requirements.BooleanRequirement(name = 'full', + description ="It analyzes and provides all the information in the partition entry. (It returns a lot of information, so we recommend you render it in CSV.)", + default = False, + optional = True) ] @classmethod def get_hash(cls, data:bytes) -> str: return hashlib.md5(data).hexdigest() - def _generator(self): + def _generator(self) -> Iterator[Tuple]: kernel = self.context.modules[self.config['kernel']] physical_layer_name = self.context.layers[kernel.layer_name].config.get('memory_layer', None) @@ -72,38 +78,162 @@ class MBRScan(interfaces.plugins.PluginInterface): all_zeros = bootcode.count(b"\x00") == len(bootcode) if not all_zeros: - partition_entries = [ - partition_table.FirstEntry, - partition_table.SecondEntry, - partition_table.ThirdEntry, - partition_table.FourthEntry - ] - partition_info = "\n" - - for index, partition_entry_object in enumerate(partition_entries): - partition_entry_object.set_index(index) - partition_info += str(partition_entry_object) - - yield 0, ( + if not self.config.get("full", True): + yield (0, ( format_hints.Hex(offset), partition_table.get_disk_signature(), self.get_hash(bootcode), self.get_hash(full_mbr), - partition_info, + partition_table.FirstEntry.is_bootable(), + partition_table.FirstEntry.get_partition_type(), + format_hints.Hex(partition_table.FirstEntry.get_size_in_sectors()), + partition_table.SecondEntry.is_bootable(), + partition_table.SecondEntry.get_partition_type(), + format_hints.Hex(partition_table.SecondEntry.get_size_in_sectors()), + partition_table.ThirdEntry.is_bootable(), + partition_table.ThirdEntry.get_partition_type(), + format_hints.Hex(partition_table.ThirdEntry.get_size_in_sectors()), + partition_table.FourthEntry.is_bootable(), + partition_table.FourthEntry.get_partition_type(), + format_hints.Hex(partition_table.FourthEntry.get_size_in_sectors()), interfaces.renderers.Disassembly(bootcode, 0, architecture) - ) + )) + else: + yield (0, ( + format_hints.Hex(offset), + partition_table.get_disk_signature(), + self.get_hash(bootcode), + self.get_hash(full_mbr), + partition_table.FirstEntry.is_bootable(), + format_hints.Hex(partition_table.FirstEntry.get_bootable_flag()), + partition_table.FirstEntry.get_partition_type(), + format_hints.Hex(partition_table.FirstEntry.PartitionType), + format_hints.Hex(partition_table.FirstEntry.get_starting_lba()), + partition_table.FirstEntry.get_starting_cylinder(), + partition_table.FirstEntry.get_starting_chs(), + partition_table.FirstEntry.get_starting_sector(), + partition_table.FirstEntry.get_ending_cylinder(), + partition_table.FirstEntry.get_ending_chs(), + partition_table.FirstEntry.get_ending_sector(), + format_hints.Hex(partition_table.FirstEntry.get_size_in_sectors()), + partition_table.SecondEntry.is_bootable(), + format_hints.Hex(partition_table.SecondEntry.get_bootable_flag()), + partition_table.SecondEntry.get_partition_type(), + format_hints.Hex(partition_table.SecondEntry.PartitionType), + format_hints.Hex(partition_table.SecondEntry.get_starting_lba()), + partition_table.SecondEntry.get_starting_cylinder(), + partition_table.SecondEntry.get_starting_chs(), + partition_table.SecondEntry.get_starting_sector(), + partition_table.SecondEntry.get_ending_cylinder(), + partition_table.SecondEntry.get_ending_chs(), + partition_table.SecondEntry.get_ending_sector(), + format_hints.Hex(partition_table.SecondEntry.get_size_in_sectors()), + partition_table.ThirdEntry.is_bootable(), + format_hints.Hex(partition_table.ThirdEntry.get_bootable_flag()), + partition_table.ThirdEntry.get_partition_type(), + format_hints.Hex(partition_table.ThirdEntry.PartitionType), + format_hints.Hex(partition_table.ThirdEntry.get_starting_lba()), + partition_table.ThirdEntry.get_starting_cylinder(), + partition_table.ThirdEntry.get_starting_chs(), + partition_table.ThirdEntry.get_starting_sector(), + partition_table.ThirdEntry.get_ending_cylinder(), + partition_table.ThirdEntry.get_ending_chs(), + partition_table.ThirdEntry.get_ending_sector(), + format_hints.Hex(partition_table.ThirdEntry.get_size_in_sectors()), + partition_table.FourthEntry.is_bootable(), + format_hints.Hex(partition_table.FourthEntry.get_bootable_flag()), + partition_table.FourthEntry.get_partition_type(), + format_hints.Hex(partition_table.FourthEntry.PartitionType), + format_hints.Hex(partition_table.FourthEntry.get_starting_lba()), + partition_table.FourthEntry.get_starting_cylinder(), + partition_table.FourthEntry.get_starting_chs(), + partition_table.FourthEntry.get_starting_sector(), + partition_table.FourthEntry.get_ending_cylinder(), + partition_table.FourthEntry.get_ending_chs(), + partition_table.FourthEntry.get_ending_sector(), + format_hints.Hex(partition_table.FourthEntry.get_size_in_sectors()), + interfaces.renderers.Disassembly(bootcode, 0, architecture) + )) else: vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") except exceptions.PagedInvalidAddressException: pass - def run(self): - return renderers.TreeGrid([ - ("Potential MBR at Physical Offset", format_hints.Hex), - ("Disk Signature", str), - ("Bootcode MD5", str), - ("Full MBR MD5", str), - ("Partition Entries Info", str), - ("Disasm", interfaces.renderers.Disassembly) - ], self._generator()) + def run(self)-> renderers.TreeGrid: + if not self.config.get("full", True): + return renderers.TreeGrid([ + ("Potential MBR at Physical Offset", format_hints.Hex), + ("Disk Signature", str), + ("Bootcode MD5", str), + ("Full MBR MD5", str), + ("PartABootable", bool), + ("PartAType", str), + ("PartASectorInSize", format_hints.Hex), + ("PartBBootable", bool), + ("PartBType", str), + ("PartBSectorInSize", format_hints.Hex), + ("PartCBootable", bool), + ("PartCType", str), + ("PartCSectorInSize", format_hints.Hex), + ("PartDBootable", bool), + ("PartDType", str), + ("PartDSectorInSize", format_hints.Hex), + ("Disasm", interfaces.renderers.Disassembly) + ], self._generator()) + else: + return renderers.TreeGrid([ + ("Potential MBR at Physical Offset", format_hints.Hex), + ("Disk Signature", str), + ("Bootcode MD5", str), + ("Full MBR MD5", str), + ("PartABootable", bool), + ("PartABootFlag", format_hints.Hex), + ("PartAType", str), + ("PartATypeRaw", format_hints.Hex), + ("PartAStartingLBA", format_hints.Hex), + ("PartAStartingCylinder", int), + ("PartAStartingCHS", int), + ("PartAStartingSector", int), + ("PartAEndingCylinder", int), + ("PartAEndingCHS", int), + ("PartAEndingSector", int), + ("PartASectorInSize", format_hints.Hex), + ("PartBBootable", bool), + ("PartBBootFlag", format_hints.Hex), + ("PartBType", str), + ("PartBTypeRaw", format_hints.Hex), + ("PartBStartingLBA", format_hints.Hex), + ("PartBStartingCylinder", int), + ("PartBStartingCHS", int), + ("PartBStartingSector", int), + ("PartBEndingCylinder", int), + ("PartBEndingCHS", int), + ("PartBEndingSector", int), + ("PartBSectorInSize", format_hints.Hex), + ("PartCBootable", bool), + ("PartCBootFlag", format_hints.Hex), + ("PartCType", str), + ("PartCTypeRaw", format_hints.Hex), + ("PartCStartingLBA", format_hints.Hex), + ("PartCStartingCylinder", int), + ("PartCStartingCHS", int), + ("PartCStartingSector", int), + ("PartCEndingCylinder", int), + ("PartCEndingCHS", int), + ("PartCEndingSector", int), + ("PartCSectorInSize", format_hints.Hex), + ("PartDBootable", bool), + ("PartDBootFlag", format_hints.Hex), + ("PartDType", str), + ("PartDTypeRaw", format_hints.Hex), + ("PartDStartingLBA", format_hints.Hex), + ("PartDStartingCylinder", int), + ("PartDStartingCHS", int), + ("PartDStartingSector", int), + ("PartDEndingCylinder", int), + ("PartDEndingCHS", int), + ("PartDEndingSector", int), + ("PartDSectorInSize", format_hints.Hex), + ("Disasm", interfaces.renderers.Disassembly) + ], self._generator()) From cb8a1fb90c7e1571b82bc6bc58cd45f5ffcfff0e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 16 Mar 2022 20:36:26 +0000 Subject: [PATCH 19/36] CLI: Fail on overwriting a config file --- volatility3/cli/__init__.py | 9 ++------- volatility3/cli/volshell/__init__.py | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index 1933607c3..8d198e57c 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -324,18 +324,13 @@ class CommandLine: constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback, self.file_handler_class_factory()) - backup_filename = constants.BACKUP_EXISTING_CONFIG_OUTPUT if args.write_config: vollog.warning('Use of --write-config has been deprecated, replaced by --save-config ') args.save_config = 'config.json' - backup_filename = False if args.save_config: vollog.debug("Writing out configuration data to {args.save_config}") - if os.path.exists(os.path.abspath(args.save_config)) and backup_filename: - # Backup existing file - backup_filename = self.find_backup_filename(args.save_config) - vollog.debug(f"Backing up existing file to {backup_filename}") - os.rename(args.save_config, backup_filename) + if os.path.exists(os.path.abspath(args.save_config)): + parser.error(f"Cannot write configuration: file {args.save_config} already exists") with open(args.save_config, "w") as f: json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2) except exceptions.UnsatisfiedException as excp: diff --git a/volatility3/cli/volshell/__init__.py b/volatility3/cli/volshell/__init__.py index f3bed1b73..30fe75e06 100644 --- a/volatility3/cli/volshell/__init__.py +++ b/volatility3/cli/volshell/__init__.py @@ -237,18 +237,13 @@ class VolShell(cli.CommandLine): constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback, self.file_handler_class_factory()) - backup_filename = constants.BACKUP_EXISTING_CONFIG_OUTPUT if args.write_config: vollog.warning('Use of --write-config has been deprecated, replaced by --save-config ') args.save_config = 'config.json' - backup_filename = False if args.save_config: vollog.debug("Writing out configuration data to {args.save_config}") - if os.path.exists(os.path.abspath(args.save_config)) and backup_filename: - # Backup existing file - backup_filename = self.find_backup_filename(args.save_config) - vollog.debug(f"Backing up existing file to {backup_filename}") - os.rename(args.save_config, backup_filename) + if os.path.exists(os.path.abspath(args.save_config)): + parser.error(f"Cannot write configuration: file {args.save_config} already exists") with open(args.save_config, "w") as f: json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2) except exceptions.UnsatisfiedException as excp: From ed1a19d1ac2bb6267ce1627f60e80b34c59f1047 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 18 Mar 2022 01:07:59 +0900 Subject: [PATCH 20/36] Add hex dump column if full data option --- volatility3/framework/plugins/windows/mbrscan.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 60b403ae0..3602e5e78 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -28,7 +28,7 @@ class MBRScan(interfaces.plugins.PluginInterface): requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel', architectures = ["Intel32", "Intel64"]), requirements.BooleanRequirement(name = 'full', - description ="It analyzes and provides all the information in the partition entry. (It returns a lot of information, so we recommend you render it in CSV.)", + description ="It analyzes and provides all the information in the partition entry and bootcode hexdump. (It returns a lot of information, so we recommend you render it in CSV.)", default = False, optional = True) ] @@ -152,7 +152,8 @@ class MBRScan(interfaces.plugins.PluginInterface): partition_table.FourthEntry.get_ending_chs(), partition_table.FourthEntry.get_ending_sector(), format_hints.Hex(partition_table.FourthEntry.get_size_in_sectors()), - interfaces.renderers.Disassembly(bootcode, 0, architecture) + interfaces.renderers.Disassembly(bootcode, 0, architecture), + format_hints.HexBytes(bootcode) )) else: vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") @@ -235,5 +236,6 @@ class MBRScan(interfaces.plugins.PluginInterface): ("PartDEndingCHS", int), ("PartDEndingSector", int), ("PartDSectorInSize", format_hints.Hex), - ("Disasm", interfaces.renderers.Disassembly) + ("Disasm", interfaces.renderers.Disassembly), + ("Bootcode", format_hints.HexBytes) ], self._generator()) From 174036cc727b98a53e7d83dee9cfc82dcd370382 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 19 Mar 2022 15:26:10 +0900 Subject: [PATCH 21/36] Fix Typo Error for Disassembly rendering code comment --- volatility3/cli/text_renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index 8e07d58d1..1ddfcca84 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -101,7 +101,7 @@ def display_disassembly(disasm: interfaces.renderers.Disassembly) -> str: disasm: Input disassembly objects Returns: - A string as rendererd by capstone where available, otherwise output as if it were just bytes + A string as rendered by capstone where available, otherwise output as if it were just bytes """ if CAPSTONE_PRESENT: From 5af889b0593947854f161cbccd965f5b5036994b Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 21 Mar 2022 15:11:51 +0900 Subject: [PATCH 22/36] Fix operating system comparision syntax for create cache path constant. --- volatility3/framework/constants/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index 665e62d30..629d5db80 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -11,7 +11,6 @@ import os.path import sys from typing import Optional, Callable -import volatility3.framework.constants.linux import volatility3.framework.constants.windows PLUGINS_PATH = [ @@ -63,7 +62,7 @@ LOGLEVEL_VVVV = 6 CACHE_PATH = os.path.join(os.path.expanduser("~"), ".cache", "volatility3") """Default path to store cached data""" -if sys.platform == 'windows': +if sys.platform == 'win32': CACHE_PATH = os.path.join(os.environ.get("APPDATA", os.path.expanduser("~")), "volatility3") os.makedirs(CACHE_PATH, exist_ok = True) From 37f6750c92668407e07ec7e8d641a4195490a95f Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 23 Mar 2022 07:55:53 +0900 Subject: [PATCH 23/36] ReImport volatility.framework.constants.linux --- volatility3/framework/constants/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index 629d5db80..063862be8 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -11,6 +11,7 @@ import os.path import sys from typing import Optional, Callable +import volatility3.framework.constants.linux import volatility3.framework.constants.windows PLUGINS_PATH = [ From cf4ef0fa38eb7e68e51986a35ae91da4f9a04d5a Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 23 Mar 2022 15:12:54 +0900 Subject: [PATCH 24/36] Set __init__ and fix description of mac environment plugins --- volatility3/framework/plugins/mac/__init__.py | 8 ++++++++ volatility3/framework/plugins/mac/ifconfig.py | 2 +- volatility3/framework/plugins/mac/mount.py | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/mac/__init__.py b/volatility3/framework/plugins/mac/__init__.py index e69de29bb..ef6762bee 100644 --- a/volatility3/framework/plugins/mac/__init__.py +++ b/volatility3/framework/plugins/mac/__init__.py @@ -0,0 +1,8 @@ +# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# +"""All core mac plugins. + +These modules should only be imported from volatility3.plugins NOT +volatility3.framework.plugins +""" diff --git a/volatility3/framework/plugins/mac/ifconfig.py b/volatility3/framework/plugins/mac/ifconfig.py index c366a19f0..99666b763 100644 --- a/volatility3/framework/plugins/mac/ifconfig.py +++ b/volatility3/framework/plugins/mac/ifconfig.py @@ -9,7 +9,7 @@ from volatility3.framework.symbols import mac class Ifconfig(plugins.PluginInterface): - """Lists loaded kernel modules""" + """ Lists network interface information for all devices """ _required_framework_version = (2, 0, 0) diff --git a/volatility3/framework/plugins/mac/mount.py b/volatility3/framework/plugins/mac/mount.py index 398559446..6486d00ff 100644 --- a/volatility3/framework/plugins/mac/mount.py +++ b/volatility3/framework/plugins/mac/mount.py @@ -12,7 +12,7 @@ from volatility3.framework.symbols import mac class Mount(plugins.PluginInterface): """A module containing a collection of plugins that produce data typically - foundin Mac's mount command""" + founding Mac's mount command""" _required_framework_version = (2, 0, 0) From 95825e99dfe1dafae062e7c84dbd0f6ca96b26d6 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 23 Mar 2022 16:09:19 +0900 Subject: [PATCH 25/36] Update Vollog level if all zero mbr data, PagedInvalidAddressException handling --- volatility3/framework/plugins/windows/mbrscan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 3602e5e78..1e78c1c25 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -156,10 +156,10 @@ class MBRScan(interfaces.plugins.PluginInterface): format_hints.HexBytes(bootcode) )) else: - vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") + vollog.log(constants.LOGLEVEL_VVVV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") except exceptions.PagedInvalidAddressException: - pass + continue def run(self)-> renderers.TreeGrid: if not self.config.get("full", True): From 64b8f681f4c778f3ed20350baed69b8ea2e9b2de Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 23 Mar 2022 18:05:42 +0900 Subject: [PATCH 26/36] Update sentence by code review --- volatility3/framework/plugins/mac/ifconfig.py | 2 +- volatility3/framework/plugins/mac/mount.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/mac/ifconfig.py b/volatility3/framework/plugins/mac/ifconfig.py index 99666b763..330c13f07 100644 --- a/volatility3/framework/plugins/mac/ifconfig.py +++ b/volatility3/framework/plugins/mac/ifconfig.py @@ -9,7 +9,7 @@ from volatility3.framework.symbols import mac class Ifconfig(plugins.PluginInterface): - """ Lists network interface information for all devices """ + """Lists network interface information for all devices""" _required_framework_version = (2, 0, 0) diff --git a/volatility3/framework/plugins/mac/mount.py b/volatility3/framework/plugins/mac/mount.py index 6486d00ff..ba3ab83c8 100644 --- a/volatility3/framework/plugins/mac/mount.py +++ b/volatility3/framework/plugins/mac/mount.py @@ -12,7 +12,7 @@ from volatility3.framework.symbols import mac class Mount(plugins.PluginInterface): """A module containing a collection of plugins that produce data typically - founding Mac's mount command""" + found in Mac's mount command""" _required_framework_version = (2, 0, 0) From 02d90e9e42974959440fb9a45aa585ad9870d24b Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 24 Mar 2022 08:45:29 +0000 Subject: [PATCH 27/36] CLI: Remove unnecessary extra code --- volatility3/cli/__init__.py | 9 --------- volatility3/framework/constants/__init__.py | 3 --- 2 files changed, 12 deletions(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index 8d198e57c..35ad84011 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -344,15 +344,6 @@ class CommandLine: except (exceptions.VolatilityException) as excp: self.process_exceptions(excp) - def find_backup_filename(self, original: str): - suffix = "" - new_name = f"{original}.{datetime.strftime(datetime.today(), '%y%m%d')}.bak" - while os.path.exists(f"{new_name}{suffix}"): - if not suffix: - suffix = 1 - suffix += 1 - return f"{new_name}{suffix}" - @classmethod def location_from_file(cls, filename: str) -> str: """Returns the URL location from a file parameter (which may be a URL) diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index 4af4408af..f3d31dd2e 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -101,6 +101,3 @@ OFFLINE = False REMOTE_ISF_URL = None # 'http://localhost:8000/banners.json' """Remote URL to query for a list of ISF addresses""" - -BACKUP_EXISTING_CONFIG_OUTPUT = True -"""Whether existing files are backed up or overwritten when writing configuration output""" From 2cfc24f7d3c4af2b710819c80925963c216fcce9 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 25 Mar 2022 15:50:15 +0900 Subject: [PATCH 28/36] Changes in structure and data return for efficient partition entries data display --- .../framework/plugins/windows/mbrscan.py | 204 ++++++------------ .../symbols/windows/extensions/mbr.py | 2 - 2 files changed, 65 insertions(+), 141 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 1e78c1c25..991d48bf9 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -78,88 +78,57 @@ class MBRScan(interfaces.plugins.PluginInterface): all_zeros = bootcode.count(b"\x00") == len(bootcode) if not all_zeros: - if not self.config.get("full", True): - yield (0, ( - format_hints.Hex(offset), - partition_table.get_disk_signature(), - self.get_hash(bootcode), - self.get_hash(full_mbr), - partition_table.FirstEntry.is_bootable(), - partition_table.FirstEntry.get_partition_type(), - format_hints.Hex(partition_table.FirstEntry.get_size_in_sectors()), - partition_table.SecondEntry.is_bootable(), - partition_table.SecondEntry.get_partition_type(), - format_hints.Hex(partition_table.SecondEntry.get_size_in_sectors()), - partition_table.ThirdEntry.is_bootable(), - partition_table.ThirdEntry.get_partition_type(), - format_hints.Hex(partition_table.ThirdEntry.get_size_in_sectors()), - partition_table.FourthEntry.is_bootable(), - partition_table.FourthEntry.get_partition_type(), - format_hints.Hex(partition_table.FourthEntry.get_size_in_sectors()), - interfaces.renderers.Disassembly(bootcode, 0, architecture) - )) - else: - yield (0, ( - format_hints.Hex(offset), - partition_table.get_disk_signature(), - self.get_hash(bootcode), - self.get_hash(full_mbr), - partition_table.FirstEntry.is_bootable(), - format_hints.Hex(partition_table.FirstEntry.get_bootable_flag()), - partition_table.FirstEntry.get_partition_type(), - format_hints.Hex(partition_table.FirstEntry.PartitionType), - format_hints.Hex(partition_table.FirstEntry.get_starting_lba()), - partition_table.FirstEntry.get_starting_cylinder(), - partition_table.FirstEntry.get_starting_chs(), - partition_table.FirstEntry.get_starting_sector(), - partition_table.FirstEntry.get_ending_cylinder(), - partition_table.FirstEntry.get_ending_chs(), - partition_table.FirstEntry.get_ending_sector(), - format_hints.Hex(partition_table.FirstEntry.get_size_in_sectors()), - partition_table.SecondEntry.is_bootable(), - format_hints.Hex(partition_table.SecondEntry.get_bootable_flag()), - partition_table.SecondEntry.get_partition_type(), - format_hints.Hex(partition_table.SecondEntry.PartitionType), - format_hints.Hex(partition_table.SecondEntry.get_starting_lba()), - partition_table.SecondEntry.get_starting_cylinder(), - partition_table.SecondEntry.get_starting_chs(), - partition_table.SecondEntry.get_starting_sector(), - partition_table.SecondEntry.get_ending_cylinder(), - partition_table.SecondEntry.get_ending_chs(), - partition_table.SecondEntry.get_ending_sector(), - format_hints.Hex(partition_table.SecondEntry.get_size_in_sectors()), - partition_table.ThirdEntry.is_bootable(), - format_hints.Hex(partition_table.ThirdEntry.get_bootable_flag()), - partition_table.ThirdEntry.get_partition_type(), - format_hints.Hex(partition_table.ThirdEntry.PartitionType), - format_hints.Hex(partition_table.ThirdEntry.get_starting_lba()), - partition_table.ThirdEntry.get_starting_cylinder(), - partition_table.ThirdEntry.get_starting_chs(), - partition_table.ThirdEntry.get_starting_sector(), - partition_table.ThirdEntry.get_ending_cylinder(), - partition_table.ThirdEntry.get_ending_chs(), - partition_table.ThirdEntry.get_ending_sector(), - format_hints.Hex(partition_table.ThirdEntry.get_size_in_sectors()), - partition_table.FourthEntry.is_bootable(), - format_hints.Hex(partition_table.FourthEntry.get_bootable_flag()), - partition_table.FourthEntry.get_partition_type(), - format_hints.Hex(partition_table.FourthEntry.PartitionType), - format_hints.Hex(partition_table.FourthEntry.get_starting_lba()), - partition_table.FourthEntry.get_starting_cylinder(), - partition_table.FourthEntry.get_starting_chs(), - partition_table.FourthEntry.get_starting_sector(), - partition_table.FourthEntry.get_ending_cylinder(), - partition_table.FourthEntry.get_ending_chs(), - partition_table.FourthEntry.get_ending_sector(), - format_hints.Hex(partition_table.FourthEntry.get_size_in_sectors()), - interfaces.renderers.Disassembly(bootcode, 0, architecture), - format_hints.HexBytes(bootcode) - )) + + partition_entries = [ + partition_table.FirstEntry, partition_table.SecondEntry, + partition_table.ThirdEntry, partition_table.FourthEntry + ] + + for partition_index, partition_entry_object in enumerate(partition_entries, start=1): + # Output disassembly information and bootcode for each partition entry is inefficient, + # so it can only be processed in the last index. + last_partition_index = 4 + bootcode_buf = bootcode if(partition_index == last_partition_index) else b"" + + if not self.config.get("full", True): + yield (0, ( + format_hints.Hex(offset), + partition_table.get_disk_signature(), + self.get_hash(bootcode), + self.get_hash(full_mbr), + partition_index, + partition_entry_object.is_bootable(), + partition_entry_object.get_partition_type(), + format_hints.Hex(partition_entry_object.get_size_in_sectors()), + interfaces.renderers.Disassembly(bootcode_buf, 0, architecture) + )) + else: + yield (0, ( + format_hints.Hex(offset), + partition_table.get_disk_signature(), + self.get_hash(bootcode), + self.get_hash(full_mbr), + partition_index, + partition_entry_object.is_bootable(), + format_hints.Hex(partition_entry_object.get_bootable_flag()), + partition_entry_object.get_partition_type(), + format_hints.Hex(partition_entry_object.PartitionType), + format_hints.Hex(partition_entry_object.get_starting_lba()), + partition_entry_object.get_starting_cylinder(), + partition_entry_object.get_starting_chs(), + partition_entry_object.get_starting_sector(), + partition_entry_object.get_ending_cylinder(), + partition_entry_object.get_ending_chs(), + partition_entry_object.get_ending_sector(), + format_hints.Hex(partition_entry_object.get_size_in_sectors()), + interfaces.renderers.Disassembly(bootcode_buf, 0, architecture), + format_hints.HexBytes(bootcode_buf) + )) else: - vollog.log(constants.LOGLEVEL_VVVV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") + vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") except exceptions.PagedInvalidAddressException: - continue + pass def run(self)-> renderers.TreeGrid: if not self.config.get("full", True): @@ -168,18 +137,10 @@ class MBRScan(interfaces.plugins.PluginInterface): ("Disk Signature", str), ("Bootcode MD5", str), ("Full MBR MD5", str), - ("PartABootable", bool), - ("PartAType", str), - ("PartASectorInSize", format_hints.Hex), - ("PartBBootable", bool), - ("PartBType", str), - ("PartBSectorInSize", format_hints.Hex), - ("PartCBootable", bool), - ("PartCType", str), - ("PartCSectorInSize", format_hints.Hex), - ("PartDBootable", bool), - ("PartDType", str), - ("PartDSectorInSize", format_hints.Hex), + ("PartitionIndex", int), + ("Bootable", bool), + ("PartitionType", str), + ("SectorInSize", format_hints.Hex), ("Disasm", interfaces.renderers.Disassembly) ], self._generator()) else: @@ -188,54 +149,19 @@ class MBRScan(interfaces.plugins.PluginInterface): ("Disk Signature", str), ("Bootcode MD5", str), ("Full MBR MD5", str), - ("PartABootable", bool), - ("PartABootFlag", format_hints.Hex), - ("PartAType", str), - ("PartATypeRaw", format_hints.Hex), - ("PartAStartingLBA", format_hints.Hex), - ("PartAStartingCylinder", int), - ("PartAStartingCHS", int), - ("PartAStartingSector", int), - ("PartAEndingCylinder", int), - ("PartAEndingCHS", int), - ("PartAEndingSector", int), - ("PartASectorInSize", format_hints.Hex), - ("PartBBootable", bool), - ("PartBBootFlag", format_hints.Hex), - ("PartBType", str), - ("PartBTypeRaw", format_hints.Hex), - ("PartBStartingLBA", format_hints.Hex), - ("PartBStartingCylinder", int), - ("PartBStartingCHS", int), - ("PartBStartingSector", int), - ("PartBEndingCylinder", int), - ("PartBEndingCHS", int), - ("PartBEndingSector", int), - ("PartBSectorInSize", format_hints.Hex), - ("PartCBootable", bool), - ("PartCBootFlag", format_hints.Hex), - ("PartCType", str), - ("PartCTypeRaw", format_hints.Hex), - ("PartCStartingLBA", format_hints.Hex), - ("PartCStartingCylinder", int), - ("PartCStartingCHS", int), - ("PartCStartingSector", int), - ("PartCEndingCylinder", int), - ("PartCEndingCHS", int), - ("PartCEndingSector", int), - ("PartCSectorInSize", format_hints.Hex), - ("PartDBootable", bool), - ("PartDBootFlag", format_hints.Hex), - ("PartDType", str), - ("PartDTypeRaw", format_hints.Hex), - ("PartDStartingLBA", format_hints.Hex), - ("PartDStartingCylinder", int), - ("PartDStartingCHS", int), - ("PartDStartingSector", int), - ("PartDEndingCylinder", int), - ("PartDEndingCHS", int), - ("PartDEndingSector", int), - ("PartDSectorInSize", format_hints.Hex), + ("PartitionIndex", int), + ("Bootable", bool), + ("BootFlag", format_hints.Hex), + ("PartitionType", str), + ("PartitionTypeRaw", format_hints.Hex), + ("StartingLBA", format_hints.Hex), + ("StartingCylinder", int), + ("StartingCHS", int), + ("StartingSector", int), + ("EndingCylinder", int), + ("EndingCHS", int), + ("EndingSector", int), + ("SectorInSize", format_hints.Hex), ("Disasm", interfaces.renderers.Disassembly), ("Bootcode", format_hints.HexBytes) ], self._generator()) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index 3fdb67ee3..8100371fd 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -2,8 +2,6 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -import struct - from volatility3.framework import objects class PARTITION_TABLE(objects.StructType): From b07859db7f55bae7d9c9cb15aea978c06968beda Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 25 Mar 2022 17:02:41 +0900 Subject: [PATCH 29/36] Add vollog for PagedInvalidAddressException --- volatility3/framework/plugins/windows/mbrscan.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 991d48bf9..cdc40a7be 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -126,10 +126,12 @@ class MBRScan(interfaces.plugins.PluginInterface): )) else: vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") + continue - except exceptions.PagedInvalidAddressException: - pass - + except exceptions.PagedInvalidAddressException as excp: + vollog.debug(f"Invalid address identified in guessed MBR: {hex(excp.invalid_address)}") + continue + def run(self)-> renderers.TreeGrid: if not self.config.get("full", True): return renderers.TreeGrid([ From a49292ab483426d7db1c5be4c0a31db39859da36 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 25 Mar 2022 17:21:46 +0900 Subject: [PATCH 30/36] Fix type for partition --- volatility3/framework/symbols/windows/mbr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/windows/mbr.json b/volatility3/framework/symbols/windows/mbr.json index 122c020c3..2a6ec7779 100644 --- a/volatility3/framework/symbols/windows/mbr.json +++ b/volatility3/framework/symbols/windows/mbr.json @@ -170,7 +170,7 @@ "offset": 12, "type": { "kind": "base", - "name": "int" + "name": "unsigned int" } } }, From f8443b994dc4ac1f512e49928d555e6b2abcadd6 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 25 Mar 2022 17:29:07 +0900 Subject: [PATCH 31/36] Refactoring for partition index --- volatility3/framework/plugins/windows/mbrscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index cdc40a7be..fc473ae7e 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -87,7 +87,7 @@ class MBRScan(interfaces.plugins.PluginInterface): for partition_index, partition_entry_object in enumerate(partition_entries, start=1): # Output disassembly information and bootcode for each partition entry is inefficient, # so it can only be processed in the last index. - last_partition_index = 4 + last_partition_index = len(partition_entries) bootcode_buf = bootcode if(partition_index == last_partition_index) else b"" if not self.config.get("full", True): From a9dabf90d71543ee335ec1a20252d9f27b68ef93 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 25 Mar 2022 23:22:23 +0900 Subject: [PATCH 32/36] Adjust vollog log level of Exception --- volatility3/framework/plugins/windows/mbrscan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index fc473ae7e..2148efc41 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -125,11 +125,11 @@ class MBRScan(interfaces.plugins.PluginInterface): format_hints.HexBytes(bootcode_buf) )) else: - vollog.log(constants.LOGLEVEL_VV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") + vollog.log(constants.LOGLEVEL_VVV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") continue except exceptions.PagedInvalidAddressException as excp: - vollog.debug(f"Invalid address identified in guessed MBR: {hex(excp.invalid_address)}") + vollog.log(constants.LOGLEVEL_VVV, f"Invalid address identified in guessed MBR: {hex(excp.invalid_address)}") continue def run(self)-> renderers.TreeGrid: From 8a99c17d4266f7e869404f37a4e60e61bb6cfe90 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 25 Mar 2022 23:27:25 +0900 Subject: [PATCH 33/36] Adjust vollog log level of Exception --- volatility3/framework/plugins/windows/mbrscan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 2148efc41..ba74a7b7e 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -125,11 +125,11 @@ class MBRScan(interfaces.plugins.PluginInterface): format_hints.HexBytes(bootcode_buf) )) else: - vollog.log(constants.LOGLEVEL_VVV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") + vollog.log(constants.LOGLEVEL_VVVV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") continue except exceptions.PagedInvalidAddressException as excp: - vollog.log(constants.LOGLEVEL_VVV, f"Invalid address identified in guessed MBR: {hex(excp.invalid_address)}") + vollog.log(constants.LOGLEVEL_VVVV, f"Invalid address identified in guessed MBR: {hex(excp.invalid_address)}") continue def run(self)-> renderers.TreeGrid: From ff433dd4b8fb0bb3d7125614e7c295358f512709 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 26 Mar 2022 01:22:17 +0900 Subject: [PATCH 34/36] Change the empty byte to NotApplicableValue for efficient partition data output. --- .../framework/plugins/windows/mbrscan.py | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index ba74a7b7e..39e962c96 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -84,14 +84,45 @@ class MBRScan(interfaces.plugins.PluginInterface): partition_table.ThirdEntry, partition_table.FourthEntry ] + if not self.config.get("full", True): + yield (0, ( + format_hints.Hex(offset), + partition_table.get_disk_signature(), + self.get_hash(bootcode), + self.get_hash(full_mbr), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + interfaces.renderers.Disassembly(bootcode, 0, architecture) + )) + else: + yield (0, ( + format_hints.Hex(offset), + partition_table.get_disk_signature(), + self.get_hash(bootcode), + self.get_hash(full_mbr), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + renderers.NotApplicableValue(), + interfaces.renderers.Disassembly(bootcode, 0, architecture), + format_hints.HexBytes(bootcode) + )) + for partition_index, partition_entry_object in enumerate(partition_entries, start=1): - # Output disassembly information and bootcode for each partition entry is inefficient, - # so it can only be processed in the last index. - last_partition_index = len(partition_entries) - bootcode_buf = bootcode if(partition_index == last_partition_index) else b"" if not self.config.get("full", True): - yield (0, ( + yield (1, ( format_hints.Hex(offset), partition_table.get_disk_signature(), self.get_hash(bootcode), @@ -100,10 +131,10 @@ class MBRScan(interfaces.plugins.PluginInterface): partition_entry_object.is_bootable(), partition_entry_object.get_partition_type(), format_hints.Hex(partition_entry_object.get_size_in_sectors()), - interfaces.renderers.Disassembly(bootcode_buf, 0, architecture) + renderers.NotApplicableValue() )) else: - yield (0, ( + yield (1, ( format_hints.Hex(offset), partition_table.get_disk_signature(), self.get_hash(bootcode), @@ -121,8 +152,8 @@ class MBRScan(interfaces.plugins.PluginInterface): partition_entry_object.get_ending_chs(), partition_entry_object.get_ending_sector(), format_hints.Hex(partition_entry_object.get_size_in_sectors()), - interfaces.renderers.Disassembly(bootcode_buf, 0, architecture), - format_hints.HexBytes(bootcode_buf) + renderers.NotApplicableValue(), + renderers.NotApplicableValue() )) else: vollog.log(constants.LOGLEVEL_VVVV, f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}") From d17c56af1ce3799485700e2b613c18a2d219ee8e Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 26 Mar 2022 01:23:12 +0900 Subject: [PATCH 35/36] Remove space the plugin description --- volatility3/framework/plugins/windows/mbrscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index 39e962c96..d064e7d29 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -17,7 +17,7 @@ from volatility3.framework.symbols.windows.extensions import mbr vollog = logging.getLogger(__name__) class MBRScan(interfaces.plugins.PluginInterface): - """ Scans for and parses potential Master Boot Records (MBRs) """ + """Scans for and parses potential Master Boot Records (MBRs)""" _required_framework_version = (2, 0, 1) _version = (1, 0, 0) From 5c402f33e9bc967bd12a3fcfa36eb40b7a8b8b87 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 26 Mar 2022 01:28:36 +0900 Subject: [PATCH 36/36] Improvement of MBR extension's incomplete return type --- .../framework/symbols/windows/extensions/mbr.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/mbr.py b/volatility3/framework/symbols/windows/extensions/mbr.py index 8100371fd..fc7996c52 100644 --- a/volatility3/framework/symbols/windows/extensions/mbr.py +++ b/volatility3/framework/symbols/windows/extensions/mbr.py @@ -29,34 +29,34 @@ class PARTITION_ENTRY(objects.StructType): """Get Partition Type.""" return self.PartitionType.lookup() if self.PartitionType.is_valid_choice else "Not Defined PartitionType" - def get_starting_chs(self): + def get_starting_chs(self) -> int: """Get Starting CHS (Cylinder Header Sector) Address.""" return self.StartingCHS[0] - def get_ending_chs(self): + def get_ending_chs(self) -> int: """Get Ending CHS (Cylinder Header Sector) Address.""" return self.EndingCHS[0] - def get_starting_sector(self): + def get_starting_sector(self) -> int: """Get Starting Sector.""" return self.StartingCHS[1] % 64 - def get_ending_sector(self): + def get_ending_sector(self) -> int: """Get Ending Sector.""" return self.EndingCHS[1] % 64 - def get_starting_cylinder(self): + def get_starting_cylinder(self) -> int: """Get Starting Cylinder.""" return (self.StartingCHS[1] - self.get_starting_sector()) * 4 + self.StartingCHS[2] - def get_ending_cylinder(self): + def get_ending_cylinder(self) -> int: """Get Ending Cylinder.""" return (self.EndingCHS[1] - self.get_ending_sector()) * 4 + self.EndingCHS[2] - def get_starting_lba(self): + def get_starting_lba(self) -> int: """Get Starting LBA (Logical Block Addressing).""" return self.StartingLBA - def get_size_in_sectors(self): + def get_size_in_sectors(self) -> int: """Get Size in Sectors.""" return self.SizeInSectors