Merge branch 'volatilityfoundation:develop' into feature/device-tree

This commit is contained in:
Donghyun Kim
2022-03-26 02:26:34 +09:00
committed by GitHub
10 changed files with 539 additions and 9 deletions
+12 -2
View File
@@ -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,13 @@ 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:
vollog.warning('Use of --write-config has been deprecated, replaced by --save-config <filename>')
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)):
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:
self.process_unsatisfied_exceptions(excp)
+1 -1
View File
@@ -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:
+11 -2
View File
@@ -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,13 @@ 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:
vollog.warning('Use of --write-config has been deprecated, replaced by --save-config <filename>')
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)):
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:
self.process_unsatisfied_exceptions(excp)
+3 -2
View File
@@ -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
@@ -63,7 +63,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)
@@ -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."""
@@ -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
"""
@@ -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)
+1 -1
View File
@@ -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"""
found in Mac's mount command"""
_required_framework_version = (2, 0, 0)
@@ -0,0 +1,200 @@
# 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
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
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)-> List[interfaces.configuration.RequirementInterface]:
return [
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 and bootcode hexdump. (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) -> Iterator[Tuple]:
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",
filename = "mbr",
class_types = {
'PARTITION_TABLE': mbr.PARTITION_TABLE,
'PARTITION_ENTRY': mbr.PARTITION_ENTRY
})
partition_table_object = symbol_table + constants.BANG + "PARTITION_TABLE"
# Define Signature and Data Length
mbr_signature = b"\x55\xAA"
mbr_length = 0x200
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])):
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)
# Extract only BootCode
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)
if not all_zeros:
partition_entries = [
partition_table.FirstEntry, partition_table.SecondEntry,
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):
if not self.config.get("full", True):
yield (1, (
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()),
renderers.NotApplicableValue()
))
else:
yield (1, (
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()),
renderers.NotApplicableValue(),
renderers.NotApplicableValue()
))
else:
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_VVVV, 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([
("Potential MBR at Physical Offset", format_hints.Hex),
("Disk Signature", str),
("Bootcode MD5", str),
("Full MBR MD5", str),
("PartitionIndex", int),
("Bootable", bool),
("PartitionType", str),
("SectorInSize", 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),
("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())
@@ -0,0 +1,62 @@
# 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
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],
self.DiskSignature[2],
self.DiskSignature[3]
)
class PARTITION_ENTRY(objects.StructType):
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.get_bootable_flag() == 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) -> int:
"""Get Starting CHS (Cylinder Header Sector) Address."""
return self.StartingCHS[0]
def get_ending_chs(self) -> int:
"""Get Ending CHS (Cylinder Header Sector) Address."""
return self.EndingCHS[0]
def get_starting_sector(self) -> int:
"""Get Starting Sector."""
return self.StartingCHS[1] % 64
def get_ending_sector(self) -> int:
"""Get Ending Sector."""
return self.EndingCHS[1] % 64
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) -> int:
"""Get Ending Cylinder."""
return (self.EndingCHS[1] - self.get_ending_sector()) * 4 + self.EndingCHS[2]
def get_starting_lba(self) -> int:
"""Get Starting LBA (Logical Block Addressing)."""
return self.StartingLBA
def get_size_in_sectors(self) -> int:
"""Get Size in Sectors."""
return self.SizeInSectors
@@ -0,0 +1,240 @@
{
"metadata": {
"producer": {
"version": "0.0.1",
"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"
},
"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"
},
"int": {
"kind": "int",
"size": 4,
"signed": true,
"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": {},
"enums": {
"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": "base",
"name": "unsigned char"
}
},
"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": "unsigned 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
}
}
}