diff --git a/src/analyzeMFT/cli.py b/src/analyzeMFT/cli.py index f595912..0607e9e 100644 --- a/src/analyzeMFT/cli.py +++ b/src/analyzeMFT/cli.py @@ -1,5 +1,5 @@ import asyncio -from optparse import OptionParser +from optparse import OptionParser, OptionGroup import sys from .mft_analyzer import MftAnalyzer from .constants import VERSION @@ -21,6 +21,12 @@ async def main(): help="Export as XML") export_group.add_option("--excel", action="store_const", const="excel", dest="export_format", help="Export as Excel") + export_group.add_option("--body", action="store_const", const="body", dest="export_format", + help="Export as body file (for mactime)") + export_group.add_option("--timeline", action="store_const", const="timeline", dest="export_format", + help="Export as TSK timeline") + export_group.add_option("--l2t", action="store_const", const="l2t", dest="export_format", + help="Export as log2timeline CSV") parser.add_option_group(export_group) parser.add_option("-d", "--debug", action="store_true", dest="debug", @@ -35,7 +41,7 @@ async def main(): sys.exit(1) if not options.export_format: - options.export_format = "csv" + 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() diff --git a/src/analyzeMFT/file_writers.py b/src/analyzeMFT/file_writers.py index b65222b..fc840a8 100644 --- a/src/analyzeMFT/file_writers.py +++ b/src/analyzeMFT/file_writers.py @@ -47,4 +47,42 @@ async def write_excel(records: List[MftRecord], output_file: str) -> None: for record in records: ws.append(record.to_csv()) wb.save(output_file) - await asyncio.sleep(0) \ No newline at end of file + await asyncio.sleep(0) + + @staticmethod + async def write_body(records: List[MftRecord], output_file: str) -> None: + with open(output_file, 'w', encoding='utf-8') as bodyfile: + for record in records: + # Format: MD5|name|inode|mode_as_string|UID|GID|size|atime|mtime|ctime|crtime + bodyfile.write(f"0|{record.filename}|{record.recordnum}|{record.flags:04o}|0|0|" + f"{record.filesize}|{record.fn_times['atime'].unixtime}|" + f"{record.fn_times['mtime'].unixtime}|{record.fn_times['ctime'].unixtime}|" + f"{record.fn_times['crtime'].unixtime}\n") + await asyncio.sleep(0) + + @staticmethod + async def write_timeline(records: List[MftRecord], output_file: str) -> None: + with open(output_file, 'w', encoding='utf-8') as timeline: + for record in records: + # Format: Time|Source|Type|User|Host|Short|Desc|Version|Filename|Inode|Notes|Format|Extra + timeline.write(f"{record.fn_times['crtime'].unixtime}|MFT|CREATE|||||{record.filename}|{record.recordnum}||||\n") + timeline.write(f"{record.fn_times['mtime'].unixtime}|MFT|MODIFY|||||{record.filename}|{record.recordnum}||||\n") + timeline.write(f"{record.fn_times['atime'].unixtime}|MFT|ACCESS|||||{record.filename}|{record.recordnum}||||\n") + timeline.write(f"{record.fn_times['ctime'].unixtime}|MFT|CHANGE|||||{record.filename}|{record.recordnum}||||\n") + await asyncio.sleep(0) + + @staticmethod + async def write_l2t(records: List[MftRecord], output_file: str) -> None: + with open(output_file, 'w', newline='', encoding='utf-8') as l2tfile: + writer = csv.writer(l2tfile) + writer.writerow(['date', 'time', 'timezone', 'MACB', 'source', 'sourcetype', 'type', 'user', 'host', 'short', 'desc', 'version', 'filename', 'inode', 'notes', 'format', 'extra']) + for record in records: + for time_type, time_obj in record.fn_times.items(): + macb = 'M' if time_type == 'mtime' else 'A' if time_type == 'atime' else 'C' if time_type == 'ctime' else 'B' + date_str = time_obj.dt.strftime('%m/%d/%Y') if time_obj.dt else '' + time_str = time_obj.dt.strftime('%H:%M:%S') if time_obj.dt else '' + writer.writerow([ + date_str, time_str, 'UTC', macb, 'MFT', 'FILESYSTEM', time_type, '', '', '', + f"{record.filename} {time_type}", '', record.filename, record.recordnum, '', '', '' + ]) + await asyncio.sleep(0) \ No newline at end of file