From 441fff0d10b2053a1ee52e21dfb15ab0eb06b1a2 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 13 Oct 2021 17:00:47 +0100 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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(