From 441fff0d10b2053a1ee52e21dfb15ab0eb06b1a2 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 13 Oct 2021 17:00:47 +0100 Subject: [PATCH 01/64] Windows: Reduce DTB false positive rate --- volatility3/framework/automagic/windows.py | 49 ++++++++++++++-------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index eb63a75e5..ce09ca6a4 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -202,31 +202,44 @@ class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): for description, tests, sections in cls.test_sets: vollog.debug(description) # There is a very high chance that the DTB will live in these very narrow segments, assuming we couldn't find them previously - hits = context.layers[layer_name].scan(context, - PageMapScanner(tests = tests), - sections = sections, - progress_callback = progress_callback) + hits = base_layer.scan(context, + PageMapScanner(tests = tests), + sections = sections, + progress_callback = progress_callback) # Flatten the generator def sort_by_tests(x): + """Key used to sort by tests""" return tests.index(x[0]), x[1] + def get_max_pointer(page_table, test, ptr_size: int): + """Determines a pointer from a page_table""" + max_ptr = 0 + for index in range(0, len(page_table), ptr_size): + max_ptr = max(max_ptr, + struct.unpack(test.ptr_struct, page_table[index:index + ptr_size])[0] & test.mask) + return max_ptr + hits = sorted(list(hits), key = sort_by_tests) - if hits: - # TODO: Decide which to use if there are multiple options - test, page_map_offset = hits[0] - vollog.debug(f"{test.__class__.__name__} test succeeded at {hex(page_map_offset)}") - new_layer_name = context.layers.free_layer_name("IntelLayer") - config_path = interfaces.configuration.path_join("IntelHelper", new_layer_name) - context.config[interfaces.configuration.path_join(config_path, "memory_layer")] = layer_name - context.config[interfaces.configuration.path_join(config_path, "page_map_offset")] = page_map_offset - # TODO: Need to determine the layer type (chances are high it's x64, hence this default) - layer = test.layer_type(context, - config_path = config_path, - name = new_layer_name, - metadata = {'os': 'Windows'}) - break + for test, page_map_offset in hits: + # Turn the page tables into integers and find the largest one + page_table = base_layer.read(page_map_offset, 0x1000) + ptr_size = struct.calcsize(test.ptr_struct) + max_pointer = get_max_pointer(page_table, test, ptr_size) + + if max_pointer <= base_layer.maximum_address: + vollog.debug(f"{test.__class__.__name__} test succeeded at {hex(page_map_offset)}") + new_layer_name = context.layers.free_layer_name("IntelLayer") + config_path = interfaces.configuration.path_join("IntelHelper", new_layer_name) + context.config[interfaces.configuration.path_join(config_path, "memory_layer")] = layer_name + context.config[ + interfaces.configuration.path_join(config_path, "page_map_offset")] = page_map_offset + layer = test.layer_type(context, + config_path = config_path, + name = new_layer_name, + metadata = {'os': 'Windows'}) + break if layer is not None and config_path: vollog.debug("DTB was found at: 0x{:0x}".format(context.config[interfaces.configuration.path_join( From 865cb92527915490cebe35fd00763eb4693ac13d Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 17 Oct 2021 00:25:42 +0100 Subject: [PATCH 02/64] Windows: Check only valid page table entries --- volatility3/framework/automagic/windows.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index ce09ca6a4..f90f20b52 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -216,8 +216,9 @@ class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): """Determines a pointer from a page_table""" max_ptr = 0 for index in range(0, len(page_table), ptr_size): - max_ptr = max(max_ptr, - struct.unpack(test.ptr_struct, page_table[index:index + ptr_size])[0] & test.mask) + pointer = struct.unpack(test.ptr_struct, page_table[index:index + ptr_size])[0] + if pointer & 0x1: + max_ptr = max(max_ptr, pointer & test.mask) return max_ptr hits = sorted(list(hits), key = sort_by_tests) From fb6610ff48d165527ad99b6e127eb09b05194452 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 17 Oct 2021 00:36:16 +0100 Subject: [PATCH 03/64] Windows: Limit pointers to layer maximum address --- volatility3/framework/automagic/windows.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index f90f20b52..6968a1ac2 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -218,7 +218,7 @@ class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): for index in range(0, len(page_table), ptr_size): pointer = struct.unpack(test.ptr_struct, page_table[index:index + ptr_size])[0] if pointer & 0x1: - max_ptr = max(max_ptr, pointer & test.mask) + max_ptr = max(max_ptr, pointer & test.layer_type.maximum_address) return max_ptr hits = sorted(list(hits), key = sort_by_tests) @@ -241,6 +241,9 @@ class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): name = new_layer_name, metadata = {'os': 'Windows'}) break + else: + vollog.debug( + f"Max pointer for hit with test {test.__class__.__name__} not met: {hex(max_pointer)} > {hex(base_layer.maximum_address)}") if layer is not None and config_path: vollog.debug("DTB was found at: 0x{:0x}".format(context.config[interfaces.configuration.path_join( From 49fe653d9849c48d7179774705c768ec1011f680 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 17 Oct 2021 00:53:20 +0100 Subject: [PATCH 04/64] Windows: Max pointers at maximum layer address --- volatility3/framework/automagic/windows.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index 6968a1ac2..308ead157 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -217,8 +217,9 @@ class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): max_ptr = 0 for index in range(0, len(page_table), ptr_size): pointer = struct.unpack(test.ptr_struct, page_table[index:index + ptr_size])[0] - if pointer & 0x1: - max_ptr = max(max_ptr, pointer & test.layer_type.maximum_address) + # Make sure the pointer is valid, ignore large pages which would require more calculation + if pointer & 0x1 and not pointer & 0x80: + max_ptr = max(max_ptr, pointer % test.layer_type.maximum_address) return max_ptr hits = sorted(list(hits), key = sort_by_tests) From 9b4324adaae666e3649c27e8efced45853fc26eb Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 17 Oct 2021 01:53:41 +0100 Subject: [PATCH 05/64] Windows: Stop looking for DTBs when a good one is found --- volatility3/framework/automagic/windows.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index 308ead157..beda8d97b 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -219,7 +219,7 @@ class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): pointer = struct.unpack(test.ptr_struct, page_table[index:index + ptr_size])[0] # Make sure the pointer is valid, ignore large pages which would require more calculation if pointer & 0x1 and not pointer & 0x80: - max_ptr = max(max_ptr, pointer % test.layer_type.maximum_address) + max_ptr = max(max_ptr, (pointer ^ (pointer & 0xfff)) % test.layer_type.maximum_address) return max_ptr hits = sorted(list(hits), key = sort_by_tests) @@ -245,6 +245,8 @@ class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): else: vollog.debug( f"Max pointer for hit with test {test.__class__.__name__} not met: {hex(max_pointer)} > {hex(base_layer.maximum_address)}") + if layer is not None and config_path: + break if layer is not None and config_path: vollog.debug("DTB was found at: 0x{:0x}".format(context.config[interfaces.configuration.path_join( From 79a549f21170276cf1634b3599b89f5d8d2485e1 Mon Sep 17 00:00:00 2001 From: "Nick L. Petroni, Jr" Date: Sun, 16 Jan 2022 15:34:03 -0500 Subject: [PATCH 06/64] use doc/requirements.txt when building with readthedocs --- .readthedocs.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 764bb5a1a..4d21d9b40 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -16,7 +16,4 @@ formats: all python: version: 3.7 install: - - method: pip - path: . - extra_requirements: - - doc + - requirements: doc/requirements.txt From 374542d544265b650da4f516e17ce60e7cdff58e Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Wed, 9 Feb 2022 10:40:37 +0200 Subject: [PATCH 07/64] Added data_offset to pattern matching result, fixes pdb scanning bug --- volatility3/framework/symbols/windows/pdbutil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/windows/pdbutil.py b/volatility3/framework/symbols/windows/pdbutil.py index 4c3788a56..ce492831e 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -357,4 +357,4 @@ class PdbSignatureScanner(interfaces.layers.ScannerInterface): guid = (16 * '{:02X}').format(g0, g1, g2, g3, g4, g5, g6, g7, g8, g9, ga, gb, gc, gd, ge, gf) if match.start(0) < self.chunk_size: - yield (guid, a, pdb_name, match.start(0)) + yield (guid, a, pdb_name, data_offset + match.start(0)) From de87fd0b226cfca60395f9f2e67248d7e343e770 Mon Sep 17 00:00:00 2001 From: trashcan122 Date: Wed, 9 Feb 2022 16:30:40 +0200 Subject: [PATCH 08/64] fix pdb mz parsing --- volatility3/framework/symbols/windows/pdbutil.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/symbols/windows/pdbutil.py b/volatility3/framework/symbols/windows/pdbutil.py index ce492831e..cc7b22e03 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -131,8 +131,14 @@ class PDBUtility(interfaces.configuration.VersionableInterface): # Check it is actually the MZ header if mz_sig != b"MZ": return None - - nt_header_start = ord(layer.read(offset + 0x3C, 1)) + + nt_header_start = struct.unpack(" Date: Wed, 9 Feb 2022 20:18:04 +0000 Subject: [PATCH 09/64] Core: Prepare 2.0.1 release --- volatility3/framework/constants/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index 23598837b..4ce2dbe2d 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -40,7 +40,7 @@ BANG = "!" # We use the SemVer 2.0.0 versioning scheme VERSION_MAJOR = 2 # Number of releases of the library with a breaking change VERSION_MINOR = 0 # Number of changes that only add to the interface -VERSION_PATCH = 0 # Number of changes that do not change the interface +VERSION_PATCH = 1 # Number of changes that do not change the interface VERSION_SUFFIX = "" # TODO: At version 2.0.0, remove the symbol_shift feature From f156d237a4129da9394f81e72949bef58f3e0b76 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 28 Feb 2022 13:36:54 +0900 Subject: [PATCH 10/64] Add 'ImportError' handling of the capstone module on malfind plugin. --- volatility3/framework/plugins/windows/malfind.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/volatility3/framework/plugins/windows/malfind.py b/volatility3/framework/plugins/windows/malfind.py index bfd29a254..7fd032ef5 100644 --- a/volatility3/framework/plugins/windows/malfind.py +++ b/volatility3/framework/plugins/windows/malfind.py @@ -13,6 +13,12 @@ from volatility3.plugins.windows import pslist, vadinfo vollog = logging.getLogger(__name__) +try: + import capstone + + has_capstone = True +except ImportError: + has_capstone = False class Malfind(interfaces.plugins.PluginInterface): """Lists process memory ranges that potentially contain injected code.""" From 9868aeb9060687b240637470d690de4327cab3c3 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 28 Feb 2022 14:12:25 +0900 Subject: [PATCH 11/64] Add Error Raise point --- volatility3/framework/plugins/windows/malfind.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/malfind.py b/volatility3/framework/plugins/windows/malfind.py index 7fd032ef5..ae10a6c65 100644 --- a/volatility3/framework/plugins/windows/malfind.py +++ b/volatility3/framework/plugins/windows/malfind.py @@ -134,7 +134,11 @@ class Malfind(interfaces.plugins.PluginInterface): else: architecture = "intel64" - disasm = interfaces.renderers.Disassembly(data, vad.get_start(), architecture) + if has_capstone: + disasm = interfaces.renderers.Disassembly(data, vad.get_start(), architecture) + else: + raise exceptions.MissingModuleException( + "capstone", "Requires capstone to disassembly data") file_output = "Disabled" if self.config['dump']: From 58782fcfe1ab7f495897571f9fd1a682b6fc48f9 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 3 Mar 2022 02:00:32 +0900 Subject: [PATCH 12/64] Typo Error Fix - Context module object Args code comment --- volatility3/framework/contexts/__init__.py | 2 +- volatility3/framework/interfaces/context.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/contexts/__init__.py b/volatility3/framework/contexts/__init__.py index 518215ab4..ab81beb5e 100644 --- a/volatility3/framework/contexts/__init__.py +++ b/volatility3/framework/contexts/__init__.py @@ -141,7 +141,7 @@ class Context(interfaces.context.ContextInterface): layer_name: The layer within the context in which the module exists offset: The offset at which the module exists in the layer native_layer_name: The default native layer for objects constructed by the module - size: The size, in bytes, that the module occupys from offset location within the layer named layer_name + size: The size, in bytes, that the module occupies from offset location within the layer named layer_name """ if size: return SizedModule.create(self, diff --git a/volatility3/framework/interfaces/context.py b/volatility3/framework/interfaces/context.py index c52e1aaa5..b8470ae47 100644 --- a/volatility3/framework/interfaces/context.py +++ b/volatility3/framework/interfaces/context.py @@ -129,7 +129,7 @@ class ContextInterface(metaclass = ABCMeta): layer_name: The layer the module is associated with (which layer the module lives within) offset: The initial/base offset of the module (used as the offset for relative symbols) native_layer_name: The default native_layer_name to use when the module constructs objects - size: The size, in bytes, that the module occupys from offset location within the layer named layer_name + size: The size, in bytes, that the module occupies from offset location within the layer named layer_name Returns: A module object From dae88605778a7b627ccd924e0a1ea7b8742ca0e0 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 3 Mar 2022 02:02:43 +0900 Subject: [PATCH 13/64] Restore PR --- volatility3/framework/contexts/__init__.py | 2 +- volatility3/framework/interfaces/context.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/contexts/__init__.py b/volatility3/framework/contexts/__init__.py index ab81beb5e..2fd00c531 100644 --- a/volatility3/framework/contexts/__init__.py +++ b/volatility3/framework/contexts/__init__.py @@ -141,7 +141,7 @@ class Context(interfaces.context.ContextInterface): layer_name: The layer within the context in which the module exists offset: The offset at which the module exists in the layer native_layer_name: The default native layer for objects constructed by the module - size: The size, in bytes, that the module occupies from offset location within the layer named layer_name + size: The size, in bytes, that the module occupy from offset location within the layer named layer_name """ if size: return SizedModule.create(self, diff --git a/volatility3/framework/interfaces/context.py b/volatility3/framework/interfaces/context.py index b8470ae47..b70d55baa 100644 --- a/volatility3/framework/interfaces/context.py +++ b/volatility3/framework/interfaces/context.py @@ -129,7 +129,7 @@ class ContextInterface(metaclass = ABCMeta): layer_name: The layer the module is associated with (which layer the module lives within) offset: The initial/base offset of the module (used as the offset for relative symbols) native_layer_name: The default native_layer_name to use when the module constructs objects - size: The size, in bytes, that the module occupies from offset location within the layer named layer_name + size: The size, in bytes, that the module occupy from offset location within the layer named layer_name Returns: A module object From 49308eb18dd0035188ab7939dc825904d477066f Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 3 Mar 2022 02:03:31 +0900 Subject: [PATCH 14/64] Restore PR --- volatility3/framework/contexts/__init__.py | 2 +- volatility3/framework/interfaces/context.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/contexts/__init__.py b/volatility3/framework/contexts/__init__.py index 2fd00c531..518215ab4 100644 --- a/volatility3/framework/contexts/__init__.py +++ b/volatility3/framework/contexts/__init__.py @@ -141,7 +141,7 @@ class Context(interfaces.context.ContextInterface): layer_name: The layer within the context in which the module exists offset: The offset at which the module exists in the layer native_layer_name: The default native layer for objects constructed by the module - size: The size, in bytes, that the module occupy from offset location within the layer named layer_name + size: The size, in bytes, that the module occupys from offset location within the layer named layer_name """ if size: return SizedModule.create(self, diff --git a/volatility3/framework/interfaces/context.py b/volatility3/framework/interfaces/context.py index b70d55baa..c52e1aaa5 100644 --- a/volatility3/framework/interfaces/context.py +++ b/volatility3/framework/interfaces/context.py @@ -129,7 +129,7 @@ class ContextInterface(metaclass = ABCMeta): layer_name: The layer the module is associated with (which layer the module lives within) offset: The initial/base offset of the module (used as the offset for relative symbols) native_layer_name: The default native_layer_name to use when the module constructs objects - size: The size, in bytes, that the module occupy from offset location within the layer named layer_name + size: The size, in bytes, that the module occupys from offset location within the layer named layer_name Returns: A module object From ad1ef807e7c6d840a927ddb1a103706bfdc3b101 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 5 Mar 2022 16:27:07 +0900 Subject: [PATCH 15/64] Context Typo Error, MFT Symbol JSON Prettier --- volatility3/framework/contexts/__init__.py | 2 +- volatility3/framework/interfaces/context.py | 2 +- volatility3/framework/symbols/windows/mft.json | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/contexts/__init__.py b/volatility3/framework/contexts/__init__.py index 518215ab4..ab81beb5e 100644 --- a/volatility3/framework/contexts/__init__.py +++ b/volatility3/framework/contexts/__init__.py @@ -141,7 +141,7 @@ class Context(interfaces.context.ContextInterface): layer_name: The layer within the context in which the module exists offset: The offset at which the module exists in the layer native_layer_name: The default native layer for objects constructed by the module - size: The size, in bytes, that the module occupys from offset location within the layer named layer_name + size: The size, in bytes, that the module occupies from offset location within the layer named layer_name """ if size: return SizedModule.create(self, diff --git a/volatility3/framework/interfaces/context.py b/volatility3/framework/interfaces/context.py index c52e1aaa5..b8470ae47 100644 --- a/volatility3/framework/interfaces/context.py +++ b/volatility3/framework/interfaces/context.py @@ -129,7 +129,7 @@ class ContextInterface(metaclass = ABCMeta): layer_name: The layer the module is associated with (which layer the module lives within) offset: The initial/base offset of the module (used as the offset for relative symbols) native_layer_name: The default native_layer_name to use when the module constructs objects - size: The size, in bytes, that the module occupys from offset location within the layer named layer_name + size: The size, in bytes, that the module occupies from offset location within the layer named layer_name Returns: A module object diff --git a/volatility3/framework/symbols/windows/mft.json b/volatility3/framework/symbols/windows/mft.json index b71be6444..17edd45dd 100644 --- a/volatility3/framework/symbols/windows/mft.json +++ b/volatility3/framework/symbols/windows/mft.json @@ -270,7 +270,8 @@ "offset": 8, "type": { "kind": "base", - "name": "unsigned char" } + "name": "unsigned char" + } }, "NameLength": { "offset": 9, @@ -322,7 +323,8 @@ "offset": 8, "type": { "kind": "base", - "name": "unsigned short" } + "name": "unsigned short" + } } }, "kind": "struct", From f060562b278352e8c3cfadd65850688717c47b15 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 5 Mar 2022 16:29:09 +0900 Subject: [PATCH 16/64] Rebase --- volatility3/framework/plugins/windows/malfind.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/volatility3/framework/plugins/windows/malfind.py b/volatility3/framework/plugins/windows/malfind.py index ae10a6c65..e70fd0f75 100644 --- a/volatility3/framework/plugins/windows/malfind.py +++ b/volatility3/framework/plugins/windows/malfind.py @@ -13,12 +13,6 @@ from volatility3.plugins.windows import pslist, vadinfo vollog = logging.getLogger(__name__) -try: - import capstone - - has_capstone = True -except ImportError: - has_capstone = False class Malfind(interfaces.plugins.PluginInterface): """Lists process memory ranges that potentially contain injected code.""" @@ -134,12 +128,8 @@ class Malfind(interfaces.plugins.PluginInterface): else: architecture = "intel64" - if has_capstone: - disasm = interfaces.renderers.Disassembly(data, vad.get_start(), architecture) - else: - raise exceptions.MissingModuleException( - "capstone", "Requires capstone to disassembly data") - + disasm = interfaces.renderers.Disassembly(data, vad.get_start(), architecture) + file_output = "Disabled" if self.config['dump']: file_output = "Error outputting to file" From 639f87a0a4e642ce02c848b4ae2639efc64639ff Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 5 Mar 2022 16:29:40 +0900 Subject: [PATCH 17/64] Remove Tab --- volatility3/framework/plugins/windows/malfind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/malfind.py b/volatility3/framework/plugins/windows/malfind.py index e70fd0f75..bfd29a254 100644 --- a/volatility3/framework/plugins/windows/malfind.py +++ b/volatility3/framework/plugins/windows/malfind.py @@ -129,7 +129,7 @@ class Malfind(interfaces.plugins.PluginInterface): architecture = "intel64" disasm = interfaces.renderers.Disassembly(data, vad.get_start(), architecture) - + file_output = "Disabled" if self.config['dump']: file_output = "Error outputting to file" From 06961ce53742a4f266d4892f7b7d8120dc34388b Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 5 Mar 2022 16:32:38 +0900 Subject: [PATCH 18/64] 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 19/64] 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 244751e9aebf30c2f592c576b3a57ddbbe24b9ed Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 6 Mar 2022 18:48:00 +0000 Subject: [PATCH 20/64] Layers: Better checks on PAE page tables This checks that the very top level table points to the next four pages, as we'd expected in general. This relies on the same assumptions as the existing PAE detection did, ie that the PAE page_map maps the next four pages immediately. Previously we didn't check that the top page was valid, once we found the self-referential pointer. This adds in an appropriate check to reduce false positives. Closes #631. --- volatility3/framework/automagic/windows.py | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index 71548ca40..b93ee244c 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -28,9 +28,9 @@ The self-referential indices for older versions of windows are listed below: """ import logging import struct -from typing import Generator, List, Optional, Tuple, Type, Iterable +from typing import Generator, Iterable, List, Optional, Tuple, Type -from volatility3.framework import interfaces, layers, constants +from volatility3.framework import constants, interfaces, layers from volatility3.framework.configuration import requirements from volatility3.framework.layers import intel @@ -116,10 +116,27 @@ class DtbSelfRefPae(DtbSelfReferential): mask = 0x3FFFFFFFFFF000, reserved_bits = 0x0) - def __call__(self, *args, **kwargs): - dtb = super().__call__(*args, **kwargs) + @staticmethod + def _and_bytes(abytes, bbytes): + return bytes([a & b for a, b in zip(abytes[::-1], bbytes[::-1])][::-1]) + + def __call__(self, data: bytes, data_offset: int, page_offset: int) -> Optional[Tuple[int, int]]: + dtb = super().__call__(data, data_offset, page_offset) if dtb: - return dtb[0] - 0x4000, dtb[1] + # Find the top page + top_pae_page = dtb[0] - 0x4000 + # The top page should map to the next four pages after it + # Build what we expect the page table to be + expected_table = b''.join([struct.pack(self.ptr_struct, top_pae_page + (i * 0x1000)) for i in range(1, 5)]) + # Mask off the page bits of top level page map + page_table_mask = b"\x00\xf0\xff\xff\xff\xff\xff\xff" * 4 + page_table = data[top_pae_page - data_offset: top_pae_page - data_offset + (4 * self.ptr_size)] + # Compare them + anded_bytes = self._and_bytes(page_table, page_table_mask) + if (anded_bytes == expected_table): + return top_pae_page, dtb[1] + # Return None since the dtb value *isn't* None + return None return dtb From a1023e51f59aaf85b50d318b36a072772efaef1d Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 7 Mar 2022 13:54:35 +0900 Subject: [PATCH 21/64] Fix Renderes, Scanners, MFT Symbol Typo Error --- volatility3/framework/interfaces/renderers.py | 2 +- volatility3/framework/layers/scanners/__init__.py | 4 ++-- volatility3/framework/symbols/windows/mft.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/interfaces/renderers.py b/volatility3/framework/interfaces/renderers.py index 7f80425a4..9368009a9 100644 --- a/volatility3/framework/interfaces/renderers.py +++ b/volatility3/framework/interfaces/renderers.py @@ -1,7 +1,7 @@ # This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -"""All plugins output a TreeGrid object which must then be rendered (eithe by a +"""All plugins output a TreeGrid object which must then be rendered (either by a GUI, or as text output, html output or in some other form. This module defines both the output format (:class:`TreeGrid`) and the diff --git a/volatility3/framework/layers/scanners/__init__.py b/volatility3/framework/layers/scanners/__init__.py index 85c8390d9..ec66f2708 100644 --- a/volatility3/framework/layers/scanners/__init__.py +++ b/volatility3/framework/layers/scanners/__init__.py @@ -31,7 +31,7 @@ class BytesScanner(layers.ScannerInterface): class RegExScanner(layers.ScannerInterface): """A scanner that can be provided with a bytes-object regular expression pattern - The scanner will scqn all blocks for the regular expression and report the absolute offset of any finds + The scanner will scan all blocks for the regular expression and report the absolute offset of any finds The default flags include DOTALL, since the searches are through binary data and the newline character should have no specific significance in such searches""" @@ -95,7 +95,7 @@ class MultiStringScanner(layers.ScannerInterface): else: suffixes.append(re.escape(bytes([entry]))) else: - # If we've fininshed one of the strings at this point, remember it for later + # If we've finished one of the strings at this point, remember it for later finished = True if len(suffixes) == 1: diff --git a/volatility3/framework/symbols/windows/mft.json b/volatility3/framework/symbols/windows/mft.json index 17edd45dd..e5de8f3fa 100644 --- a/volatility3/framework/symbols/windows/mft.json +++ b/volatility3/framework/symbols/windows/mft.json @@ -134,7 +134,7 @@ "kind": "base", "name": "unsigned char" } - } + } }, "UpdateSequenceOffset": { "offset": 4, @@ -192,7 +192,7 @@ "name": "unsigned int" } }, - "AlocatedSize": { + "AllocatedSize": { "offset": 28, "type":{ "kind": "base", From 34a732a4f09f123746753c5b77661d44c92aeb2b Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 7 Mar 2022 14:00:03 +0900 Subject: [PATCH 22/64] Fix Object Typo Error --- volatility3/framework/objects/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/objects/__init__.py b/volatility3/framework/objects/__init__.py index 472370e6b..1fa6dd62a 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -704,7 +704,7 @@ class AggregateType(interfaces.objects.ObjectInterface): tmp_list[member] = (relative_offset, new_child) # If there's trouble with mutability, consider making update_vol return a clone with the changes # (there will be a few other places that will be necessary) and/or making these part of the - # permanent dictionaries rather than the non-clonable ones + # permanent dictionaries rather than the non-cloneable ones template.update_vol(members = tmp_list) @classmethod From 6c1fe42a3791f1702df1c2733c886cb96c7a1100 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 01:53:04 +0900 Subject: [PATCH 23/64] Fix Docs, Framework, Windows Plugin Typo Error --- doc/source/complex-plugin.rst | 2 +- doc/source/simple-plugin.rst | 4 ++-- volatility3/framework/interfaces/configuration.py | 2 +- volatility3/framework/objects/__init__.py | 2 +- volatility3/framework/plugins/windows/privileges.py | 4 ++-- volatility3/framework/plugins/windows/psscan.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/source/complex-plugin.rst b/doc/source/complex-plugin.rst index f06b398e8..8ab8a5186 100644 --- a/doc/source/complex-plugin.rst +++ b/doc/source/complex-plugin.rst @@ -300,7 +300,7 @@ This will mean that when a specific structure is loaded from the symbol_space, i `StructType`, but instead is instantiated using the NewStructureClass, meaning new methods can be called directly on it. If the situation really calls for an entirely new object, that isn't covered by one of the existing -:py:class:`~volatility3.framework.objects.PrimativeObject` objects (such as +:py:class:`~volatility3.framework.objects.PrimitiveObject` objects (such as :py:class:`~volatility3.framework.objects.Integer`, :py:class:`~volatility3.framework.objects.Boolean`, :py:class:`~volatility3.framework.objects.Float`, diff --git a/doc/source/simple-plugin.rst b/doc/source/simple-plugin.rst index 4e499b186..9360ccf40 100644 --- a/doc/source/simple-plugin.rst +++ b/doc/source/simple-plugin.rst @@ -206,9 +206,9 @@ information may not be provided. The plugin then takes the process's ``BaseDllName`` value, and calls :py:meth:`~volatility3.framework.symbols.windows.extensions.UNICODE_STRING.get_string` on it. All structure attributes, as defined by the symbols, are directly accessible and use the case-style of the symbol library it came from (in Windows, -attributes are CamelCase), such as ``entry.BaseDllName`` in this instance. Any attribtues not defined by the symbol but added +attributes are CamelCase), such as ``entry.BaseDllName`` in this instance. Any attributes not defined by the symbol but added by Volatility extensions cannot be properties (in case they overlap with the attributes defined in the symbol libraries) -and are therefore always methods and prepended with ``get_``, in this example ``BaseDllName.get_string()``. +and are therefore always methods and pretended with ``get_``, in this example ``BaseDllName.get_string()``. Finally, ``FullDllName`` is populated. These operations read from memory, and as such, the memory image may be unable to read the data at a particular offset. This will cause an exception to be thrown. In Volatility 3, exceptions are thrown diff --git a/volatility3/framework/interfaces/configuration.py b/volatility3/framework/interfaces/configuration.py index 7dc046a3e..c39dba680 100644 --- a/volatility3/framework/interfaces/configuration.py +++ b/volatility3/framework/interfaces/configuration.py @@ -73,7 +73,7 @@ class HierarchicalDict(collections.abc.Mapping): separator: str = CONFIG_SEPARATOR) -> None: """ Args: - initial_dict: A dictionary to populate the HierachicalDict with initially + initial_dict: A dictionary to populate the HierarchicalDict with initially separator: A custom hierarchy separator (defaults to CONFIG_SEPARATOR) """ if not (isinstance(separator, str) and len(separator) == 1): diff --git a/volatility3/framework/objects/__init__.py b/volatility3/framework/objects/__init__.py index 1fa6dd62a..e0f927ec9 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -206,7 +206,7 @@ class Bytes(PrimitiveObject, bytes): length: int = 1, **kwargs) -> 'Bytes': """Creates the appropriate class and returns it so that the native type - is inherritted. + is inherited. The only reason the kwargs is added, is so that the inheriting types can override __init__ without needing to diff --git a/volatility3/framework/plugins/windows/privileges.py b/volatility3/framework/plugins/windows/privileges.py index eaafbeae6..2d48a30f7 100644 --- a/volatility3/framework/plugins/windows/privileges.py +++ b/volatility3/framework/plugins/windows/privileges.py @@ -55,14 +55,14 @@ class Privs(interfaces.plugins.PluginInterface): try: process_token = task.Token.dereference().cast("_TOKEN") except exceptions.InvalidAddressException: - vollog.log(constants.LOGLEVEL_VVV, 'Skeep invalid token.') + vollog.log(constants.LOGLEVEL_VVV, 'Skip invalid token.') continue for value, present, enabled, default in process_token.privileges(): # Skip privileges whose bit positions cannot be # translated to a privilege name if not self.privilege_info.get(int(value)): - vollog.log(constants.LOGLEVEL_VVV, f'Skeep invalid privilege ({value}).') + vollog.log(constants.LOGLEVEL_VVV, f'Skip invalid privilege ({value}).') continue name, desc = self.privilege_info.get(int(value)) diff --git a/volatility3/framework/plugins/windows/psscan.py b/volatility3/framework/plugins/windows/psscan.py index 237c0edcd..a0601aef1 100644 --- a/volatility3/framework/plugins/windows/psscan.py +++ b/volatility3/framework/plugins/windows/psscan.py @@ -85,7 +85,7 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols - proc: the process object with phisical address + proc: the process object with physical address Returns: A process object on virtual address layer From 18770d0cd3b0dff138a33b98d39e77743086ac3c Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 02:09:47 +0900 Subject: [PATCH 24/64] Fix glossary.rst Typo Error --- doc/source/glossary.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/glossary.rst b/doc/source/glossary.rst index 68bc41e4c..66dabfafe 100644 --- a/doc/source/glossary.rst +++ b/doc/source/glossary.rst @@ -145,7 +145,7 @@ Struct, Structure Symbol This is used in many different contexts, as a short term for many things. Within Volatility, a symbol is a - construct that usually encompasses a specific type :ref:`type` at a specfific :ref:`offset`, + construct that usually encompasses a specific type :ref:`type` at a specific :ref:`offset`, representing a particular instance of that type within the memory of a compiled and running program. An example would be the location in memory of a list of active tcp endpoints maintained by the networking stack within an operating system. From acc3f6f352d9446c773fd3ebf5891f67ba9d214b Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 9 Mar 2022 13:27:53 +0900 Subject: [PATCH 25/64] 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 26/64] 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 27/64] 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 28/64] __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 29/64] 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 30/64] 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 31/64] 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 32/64] 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 33/64] 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 eba7ad1c0ddd875c4027a2b2e7c189ff74377820 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 13 Mar 2022 21:13:25 +0000 Subject: [PATCH 34/64] Renderers: Use built-in python CSV support --- volatility3/cli/text_renderer.py | 44 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index 8663bb995..eadf10d37 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -1,6 +1,7 @@ # This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # +import csv import datetime import json import logging @@ -8,7 +9,7 @@ import random import string import sys from functools import wraps -from typing import Callable, Any, List, Tuple, Dict +from typing import Any, Callable, Dict, List, Tuple from volatility3.framework import interfaces, renderers from volatility3.framework.renderers import format_hints @@ -66,7 +67,6 @@ def multitypedata_as_text(value: format_hints.MultiTypeData) -> str: def optional(func: Callable) -> Callable: - @wraps(func) def wrapped(x: Any) -> str: if isinstance(x, interfaces.renderers.BaseAbsentValue): @@ -80,7 +80,6 @@ def optional(func: Callable) -> Callable: def quoted_optional(func: Callable) -> Callable: - @wraps(func) def wrapped(x: Any) -> str: result = optional(func)(x) @@ -193,16 +192,17 @@ class NoneRenderer(CLIRenderer): if not grid.populated: grid.populate(lambda x, y: True, True) + class CSVRenderer(CLIRenderer): _type_renderers = { - format_hints.Bin: quoted_optional(lambda x: f"0b{x:b}"), - format_hints.Hex: quoted_optional(lambda x: f"0x{x:x}"), - format_hints.HexBytes: quoted_optional(hex_bytes_as_text), - format_hints.MultiTypeData: quoted_optional(multitypedata_as_text), - interfaces.renderers.Disassembly: quoted_optional(display_disassembly), - bytes: quoted_optional(lambda x: " ".join([f"{b:02x}" for b in x])), - datetime.datetime: quoted_optional(lambda x: x.strftime("%Y-%m-%d %H:%M:%S.%f %Z")), - 'default': quoted_optional(lambda x: f"{x}") + format_hints.Bin: optional(lambda x: f"0b{x:b}"), + format_hints.Hex: optional(lambda x: f"0x{x:x}"), + format_hints.HexBytes: optional(hex_bytes_as_text), + format_hints.MultiTypeData: optional(multitypedata_as_text), + interfaces.renderers.Disassembly: optional(display_disassembly), + bytes: optional(lambda x: " ".join([f"{b:02x}" for b in x])), + datetime.datetime: optional(lambda x: x.strftime("%Y-%m-%d %H:%M:%S.%f %Z")), + 'default': optional(lambda x: f"{x}") } name = "csv" @@ -219,28 +219,27 @@ class CSVRenderer(CLIRenderer): """ outfd = sys.stdout - line = ['"TreeDepth"'] + header_list = ['TreeDepth'] for column in grid.columns: # Ignore the type because namedtuples don't realize they have accessible attributes - line.append("{}".format('"' + column.name + '"')) - outfd.write(f"{','.join(line)}") + header_list.append(f"{column.name}") + + writer = csv.DictWriter(outfd, header_list) def visitor(node: interfaces.renderers.TreeNode, accumulator): - accumulator.write("\n") # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case - accumulator.write(str(max(0, node.path_depth - 1)) + ",") - line = [] + row = {'TreeDepth': str(max(0, node.path_depth - 1))} for column_index in range(len(grid.columns)): column = grid.columns[column_index] renderer = self._type_renderers.get(column.type, self._type_renderers['default']) - line.append(renderer(node.values[column_index])) - accumulator.write(f"{','.join(line)}") + row[f'{column.name}'] = renderer(node.values[column_index]) + accumulator.writerow(row) return accumulator if not grid.populated: - grid.populate(visitor, outfd) + grid.populate(visitor, writer) else: - grid.visit(node = None, function = visitor, initial_accumulator = outfd) + grid.visit(node = None, function = visitor, initial_accumulator = writer) outfd.write("\n") @@ -274,7 +273,8 @@ class PrettyTextRenderer(CLIRenderer): max_column_widths = dict([(column.name, len(column.name)) for column in grid.columns]) def visitor( - node: interfaces.renderers.TreeNode, accumulator: List[Tuple[int, Dict[interfaces.renderers.Column, bytes]]] + node: interfaces.renderers.TreeNode, + accumulator: List[Tuple[int, Dict[interfaces.renderers.Column, bytes]]] ) -> List[Tuple[int, Dict[interfaces.renderers.Column, bytes]]]: # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case max_column_widths[tree_indent_column] = max(max_column_widths.get(tree_indent_column, 0), node.path_depth) From 0de8c645a4d2159f36c960a6e2c27981798d8599 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 13 Mar 2022 21:20:04 +0000 Subject: [PATCH 35/64] Renderers: Add column headers for CSV --- volatility3/cli/text_renderer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index eadf10d37..8e07d58d1 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -225,6 +225,7 @@ class CSVRenderer(CLIRenderer): header_list.append(f"{column.name}") writer = csv.DictWriter(outfd, header_list) + writer.writeheader() def visitor(node: interfaces.renderers.TreeNode, accumulator): # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case From f97348616f559f0849264fbba2b8e9bb8cdae5b8 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 14 Mar 2022 09:47:34 +0900 Subject: [PATCH 36/64] 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 7c89fc3f070814ba27bd61fdd1141cb1eb0b883c Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Tue, 15 Mar 2022 14:22:36 +0200 Subject: [PATCH 37/64] bug fix :( --- volatility3/framework/plugins/windows/malfind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/malfind.py b/volatility3/framework/plugins/windows/malfind.py index bfd29a254..700ced8ee 100644 --- a/volatility3/framework/plugins/windows/malfind.py +++ b/volatility3/framework/plugins/windows/malfind.py @@ -53,7 +53,7 @@ class Malfind(interfaces.plugins.PluginInterface): """ CHUNK_SIZE = 0x1000 - all_zero_page = "\x00" * CHUNK_SIZE + all_zero_page = b"\x00" * CHUNK_SIZE offset = 0 vad_length = vad.get_end() - vad.get_start() From fa723ec134e881cc7c3a4987bb1b6eb176a45ac8 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 16 Mar 2022 01:29:20 +0000 Subject: [PATCH 38/64] 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 39/64] 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 40/64] 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 3ab3fa3ed8bbcd6f5b5e4a8d9f5361c587086249 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 16 Mar 2022 17:39:06 +0900 Subject: [PATCH 41/64] Remove Windows Symbol Initialize unuse import --- volatility3/framework/symbols/windows/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/windows/__init__.py b/volatility3/framework/symbols/windows/__init__.py index 3aa607574..f09dadedf 100755 --- a/volatility3/framework/symbols/windows/__init__.py +++ b/volatility3/framework/symbols/windows/__init__.py @@ -1,7 +1,7 @@ # This file is Copyright 2020 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -import volatility3.framework.symbols.windows.extensions.pool + from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows import extensions from volatility3.framework.symbols.windows.extensions import registry, pool From 1645443d3bad7e672dec09d22ddc95f5f4d7e272 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Wed, 16 Mar 2022 22:30:57 +0900 Subject: [PATCH 42/64] 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 43/64] 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 44/64] 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 a6217784cea49698975730ed6782a1c0023ecd46 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 16 Mar 2022 20:12:22 +0000 Subject: [PATCH 45/64] Windows: Tidy up hashdump plugin Shouldn't have merged this with mention of profiles. Also fixes #678. --- .../framework/plugins/windows/hashdump.py | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/volatility3/framework/plugins/windows/hashdump.py b/volatility3/framework/plugins/windows/hashdump.py index 4d0b25b5c..e9f8047e0 100644 --- a/volatility3/framework/plugins/windows/hashdump.py +++ b/volatility3/framework/plugins/windows/hashdump.py @@ -4,10 +4,10 @@ import binascii import hashlib import logging -from struct import unpack, pack -from typing import List, Tuple, Optional +from struct import pack, unpack +from typing import List, Optional, Tuple -from Crypto.Cipher import ARC4, DES, AES +from Crypto.Cipher import AES, ARC4, DES from Crypto.Hash import MD5 from volatility3.framework import interfaces, renderers @@ -28,7 +28,7 @@ class Hashdump(interfaces.plugins.PluginInterface): def get_requirements(cls): return [ requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel', - architectures = ["Intel32", "Intel64"]), + architectures = ["Intel32", "Intel64"]), requirements.PluginRequirement(name = 'hivelist', plugin = hivelist.HiveList, version = (1, 0, 0)) ] @@ -63,7 +63,8 @@ class Hashdump(interfaces.plugins.PluginInterface): def get_hive_key(cls, hive: registry.RegistryHive, key: str): result = None try: - result = hive.get_key(key) + if hive: + result = hive.get_key(key) except KeyError: vollog.info( f"Unable to load the required registry key {hive.get_name()}\\{key} from this memory image") @@ -132,7 +133,7 @@ class Hashdump(interfaces.plugins.PluginInterface): rc4_key = md5.digest() rc4 = ARC4.new(rc4_key) - hbootkey = rc4.encrypt(sam_data[0x80:0xA0]) # lgtm [py/weak-cryptographic-algorithm] + hbootkey = rc4.encrypt(sam_data[0x80:0xA0]) # lgtm [py/weak-cryptographic-algorithm] return hbootkey elif revision == 3: # AES encrypted @@ -151,7 +152,7 @@ class Hashdump(interfaces.plugins.PluginInterface): des2 = DES.new(des_k2, DES.MODE_ECB) cipher = AES.new(hbootkey[:16], AES.MODE_CBC, salt) obfkey = cipher.decrypt(enc_hash) - return des1.decrypt(obfkey[:8]) + des2.decrypt(obfkey[8:16]) # lgtm [py/weak-cryptographic-algorithm] + return des1.decrypt(obfkey[:8]) + des2.decrypt(obfkey[8:16]) # lgtm [py/weak-cryptographic-algorithm] @classmethod def get_user_hashes(cls, user: registry.CM_KEY_NODE, samhive: registry.RegistryHive, @@ -229,9 +230,9 @@ class Hashdump(interfaces.plugins.PluginInterface): md5.update(hbootkey[:0x10] + pack(" Optional[bytes]: @@ -253,13 +254,9 @@ class Hashdump(interfaces.plugins.PluginInterface): # replaces the dump_hashes method in vol2 def _generator(self, syshive: registry.RegistryHive, samhive: registry.RegistryHive): if syshive is None: - vollog.debug("SYSTEM address is None: Did you use the correct profile?") - yield (0, (renderers.NotAvailableValue(), renderers.NotAvailableValue(), renderers.NotAvailableValue(), - renderers.NotAvailableValue())) + vollog.debug("SYSTEM address is None: No system hive found") if samhive is None: - vollog.debug("SAM address is None: Did you use the correct profile?") - yield (0, (renderers.NotAvailableValue(), renderers.NotAvailableValue(), renderers.NotAvailableValue(), - renderers.NotAvailableValue())) + vollog.debug("SAM address is None: No SAM hive found") bootkey = self.get_bootkey(syshive) hbootkey = self.get_hbootkey(samhive, bootkey) if hbootkey: From cb8a1fb90c7e1571b82bc6bc58cd45f5ffcfff0e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 16 Mar 2022 20:36:26 +0000 Subject: [PATCH 46/64] 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 47/64] 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 59102aae2a165a3ea383f1e28b9713257a6e53de Mon Sep 17 00:00:00 2001 From: "Nick L. Petroni, Jr" Date: Sun, 16 Jan 2022 17:32:02 -0500 Subject: [PATCH 48/64] update doc copyright --- doc/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index eab5c2c94..3be41db5d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,4 +1,4 @@ -# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.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 # # @@ -126,7 +126,7 @@ master_doc = 'index' # General information about the project. project = 'Volatility 3' -copyright = '2012-2019, Volatility Foundation' +copyright = '2012-2022, Volatility Foundation' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 174036cc727b98a53e7d83dee9cfc82dcd370382 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 19 Mar 2022 15:26:10 +0900 Subject: [PATCH 49/64] 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 50/64] 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 51/64] 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 52/64] 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 53/64] 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 54/64] 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 55/64] 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 56/64] 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 57/64] 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 58/64] 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 59/64] 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 60/64] 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 61/64] 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 62/64] 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 63/64] 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 64/64] 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