diff --git a/analyzeMFT.py b/analyzeMFT.py index 72e7df8..bda2bd4 100755 --- a/analyzeMFT.py +++ b/analyzeMFT.py @@ -1,10 +1,8 @@ -import asyncio import sys +import asyncio from src.analyzeMFT.cli import main if __name__ == "__main__": if sys.platform == "win32": - # This sets the event loop policy to use the ProactorEventLoop on Windows - asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) - + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) asyncio.run(main()) \ No newline at end of file diff --git a/src/analyzeMFT/__init__.py b/src/analyzeMFT/__init__.py index c392eab..3fb9cbe 100644 --- a/src/analyzeMFT/__init__.py +++ b/src/analyzeMFT/__init__.py @@ -1,6 +1,18 @@ from .windows_time import WindowsTime from .mft_record import MftRecord from .mft_analyzer import MftAnalyzer -from .constants import VERSION +from .file_writers import FileWriters +from .constants import VERSION, CSV_HEADER +from .cli import main -__all__ = ['WindowsTime', 'MftRecord', 'MftAnalyzer', 'VERSION'] \ No newline at end of file +__all__ = [ + 'WindowsTime', + 'MftRecord', + 'MftAnalyzer', + 'FileWriters', + 'VERSION', + 'CSV_HEADER', + 'main' +] + +__version__ = VERSION \ No newline at end of file diff --git a/src/analyzeMFT/cli.py b/src/analyzeMFT/cli.py index 0607e9e..d678fc6 100644 --- a/src/analyzeMFT/cli.py +++ b/src/analyzeMFT/cli.py @@ -36,16 +36,35 @@ async def main(): (options, args) = parser.parse_args() - if not options.filename or not options.output_file: + if not options.filename: parser.print_help() + print("\nError: No input file specified. Use -f or --file to specify an MFT file.") + sys.exit(1) + + if not options.output_file: + parser.print_help() + print("\nError: No output file specified. Use -o or --output to specify an output file.") sys.exit(1) if not options.export_format: options.export_format = "csv" # Default to CSV if no format specified - analyzer = MftAnalyzer(options.filename, options.output_file, options.debug, options.compute_hashes, options.export_format) - await analyzer.analyze() - print(f"Analysis complete. Results written to {options.output_file}") + try: + analyzer = MftAnalyzer(options.filename, options.output_file, options.debug, options.compute_hashes, options.export_format) + await analyzer.analyze() + print(f"Analysis complete. Results written to {options.output_file}") + except FileNotFoundError: + print(f"Error: The file '{options.filename}' was not found.") + sys.exit(1) + except PermissionError: + print(f"Error: Permission denied when trying to read '{options.filename}' or write to '{options.output_file}'.") + sys.exit(1) + except Exception as e: + print(f"An unexpected error occurred: {str(e)}") + if options.debug: + import traceback + traceback.print_exc() + sys.exit(1) if __name__ == "__main__": asyncio.run(main()) \ No newline at end of file diff --git a/src/analyzeMFT/mft_record.py b/src/analyzeMFT/mft_record.py index 0f1370c..1bedd3c 100644 --- a/src/analyzeMFT/mft_record.py +++ b/src/analyzeMFT/mft_record.py @@ -4,7 +4,7 @@ import zlib from .constants import * from .windows_time import WindowsTime -from typing import Dict, Set, List, Optional, Any +from typing import Dict, Set, List, Optional, Any,Union class MftRecord: diff --git a/tests/constants.py b/tests/constants.py new file mode 100644 index 0000000..58bc229 --- /dev/null +++ b/tests/constants.py @@ -0,0 +1,339 @@ +VERSION = '3.0.5' + +# File Record Flags +FILE_RECORD_IN_USE = 0x0001 +FILE_RECORD_IS_DIRECTORY = 0x0002 +FILE_RECORD_IS_EXTENSION = 0x0004 +FILE_RECORD_HAS_SPECIAL_INDEX = 0x0008 + +# Attribute Types +STANDARD_INFORMATION_ATTRIBUTE = 0x10 +ATTRIBUTE_LIST_ATTRIBUTE = 0x20 +FILE_NAME_ATTRIBUTE = 0x30 +OBJECT_ID_ATTRIBUTE = 0x40 +SECURITY_DESCRIPTOR_ATTRIBUTE = 0x50 +VOLUME_NAME_ATTRIBUTE = 0x60 +VOLUME_INFORMATION_ATTRIBUTE = 0x70 +DATA_ATTRIBUTE = 0x80 +INDEX_ROOT_ATTRIBUTE = 0x90 +INDEX_ALLOCATION_ATTRIBUTE = 0xA0 +BITMAP_ATTRIBUTE = 0xB0 +REPARSE_POINT_ATTRIBUTE = 0xC0 +EA_INFORMATION_ATTRIBUTE = 0xD0 +EA_ATTRIBUTE = 0xE0 +LOGGED_UTILITY_STREAM_ATTRIBUTE = 0x100 + +# Attribute Names +ATTRIBUTE_NAMES = { + STANDARD_INFORMATION_ATTRIBUTE: "$STANDARD_INFORMATION", + ATTRIBUTE_LIST_ATTRIBUTE: "$ATTRIBUTE_LIST", + FILE_NAME_ATTRIBUTE: "$FILE_NAME", + OBJECT_ID_ATTRIBUTE: "$OBJECT_ID", + SECURITY_DESCRIPTOR_ATTRIBUTE: "$SECURITY_DESCRIPTOR", + VOLUME_NAME_ATTRIBUTE: "$VOLUME_NAME", + VOLUME_INFORMATION_ATTRIBUTE: "$VOLUME_INFORMATION", + DATA_ATTRIBUTE: "$DATA", + INDEX_ROOT_ATTRIBUTE: "$INDEX_ROOT", + INDEX_ALLOCATION_ATTRIBUTE: "$INDEX_ALLOCATION", + BITMAP_ATTRIBUTE: "$BITMAP", + REPARSE_POINT_ATTRIBUTE: "$REPARSE_POINT", + EA_INFORMATION_ATTRIBUTE: "$EA_INFORMATION", + EA_ATTRIBUTE: "$EA", + LOGGED_UTILITY_STREAM_ATTRIBUTE: "$LOGGED_UTILITY_STREAM" +} + +# Standard Information Attribute +STANDARD_INFORMATION = { + 0x00: "Creation time", + 0x08: "Last modification time", + 0x10: "Last change time", + 0x18: "Last access time", + 0x20: "File attributes", + 0x24: "Maximum versions", + 0x28: "Version number", + 0x2C: "Class ID", + 0x30: "Owner ID", + 0x34: "Security ID", + 0x38: "Quota charged", + 0x40: "Update sequence number (USN)" +} + +# Attribute List +ATTRIBUTE_LIST = { + 0x00: "Attribute type", + 0x04: "Record length", + 0x06: "Name length (N)", + 0x07: "Offset to Name", + 0x08: "Starting VCN", + 0x10: "Base File Reference of the attribute", + 0x18: "Attribute ID", + 0x1A: "Name in Unicode (if N > 0)" +} + +# File Name Attribute +FILE_NAME = { + 0x00: "Parent directory file reference", + 0x08: "Creation time", + 0x10: "Last modification time", + 0x18: "Last change time", + 0x20: "Last access time", + 0x28: "Allocated size", + 0x30: "Real size", + 0x38: "Flags", + 0x3C: "Used by EAs and Reparse", + 0x40: "Filename length in characters", + 0x41: "Filename namespace", + 0x42: "Filename (Unicode)" +} + +# Object ID Attribute +OBJECT_ID = { + 0x00: "Object ID GUID", + 0x10: "Birth Volume ID GUID", + 0x20: "Birth Object ID GUID", + 0x30: "Domain ID GUID" +} + +# Security Descriptor Attribute Header +SECURITY_DESCRIPTOR_HEADER = { + 0x00: "Revision", + 0x01: "Padding", + 0x02: "Control Flags", + 0x04: "Offset to Owner SID", + 0x08: "Offset to Group SID", + 0x0C: "Offset to SACL", + 0x10: "Offset to DACL" +} + +# Security Descriptor ACL +SECURITY_DESCRIPTOR_ACL = { + 0x00: "ACL Revision", + 0x01: "Padding", + 0x02: "ACL size", + 0x04: "ACE count", + 0x06: "Padding" +} + +# Security Descriptor ACE +SECURITY_DESCRIPTOR_ACE = { + 0x00: "Type", + 0x01: "Flags", + 0x02: "Size", + 0x04: "Access mask", + 0x08: "SID" +} + +# Volume Name Attribute +VOLUME_NAME = { + 0x00: "Volume name in Unicode" +} + +# Volume Information Attribute +VOLUME_INFORMATION = { + 0x00: "Reserved (always zero?)", + 0x08: "Major version", + 0x09: "Minor version", + 0x0A: "Flags", + 0x0C: "Reserved (always zero?)" +} + +# Data Attribute +DATA = { + 0x00: "Data content" +} + +# Index Root Attribute +INDEX_ROOT = { + 0x00: "Attribute type", + 0x04: "Collation rule", + 0x08: "Bytes per index record", + 0x0C: "Clusters per index record", + 0x10: "Index node header" +} + +# Index Allocation Attribute +INDEX_ALLOCATION = { + 0x00: "Data runs" +} + +# Bitmap Attribute +BITMAP = { + 0x00: "Bit field" +} + +# Reparse Point Attribute - Microsoft +REPARSE_POINT_MS = { + 0x00: "Reparse type and flags", + 0x04: "Reparse data length", + 0x06: "Padding", + 0x08: "Reparse data" +} + +# Reparse Point Attribute - Third Party +REPARSE_POINT_3RD = { + 0x00: "Reparse type and flags", + 0x04: "Reparse data length", + 0x06: "Padding", + 0x08: "Reparse GUID", + 0x18: "Reparse data" +} + +# EA Information Attribute +EA_INFORMATION = { + 0x00: "Size of packed Extended Attributes", + 0x02: "Number of EAs with NEED_EA set", + 0x04: "Size of unpacked Extended Attributes" +} + +# EA Attribute +EA = { + 0x00: "Offset to next EA", + 0x04: "Flags", + 0x05: "Name length", + 0x06: "Value length", + 0x08: "Name", + 0x09: "Value" +} + +# Logged Utility Stream +LOGGED_UTILITY_STREAM = { + 0x00: "Any data" +} + +# Filename Namespaces +FILENAME_NAMESPACE = { + 0: "POSIX", + 1: "Win32", + 2: "DOS", + 3: "Win32 & DOS" +} + +# Index Entry Flags +INDEX_ENTRY_NODE = 0x01 +INDEX_ENTRY_END = 0x02 + +# MFT Record Size +MFT_RECORD_SIZE = 1024 + +# Attribute Flags +ATTR_FLAG_COMPRESSED = 0x0001 +ATTR_FLAG_ENCRYPTED = 0x4000 +ATTR_FLAG_SPARSE = 0x8000 + +# Collation Rules +COLLATION_BINARY = 0x00 +COLLATION_FILENAME = 0x01 +COLLATION_UNICODE = 0x02 +COLLATION_ULONG = 0x10 +COLLATION_SID = 0x11 +COLLATION_SECURITY_HASH = 0x12 +COLLATION_ULONGS = 0x13 + +# Byte order +BYTE_ORDER = 'little' + +# Struct format strings +STRUCT_STANDARD_INFORMATION = '