Skip to content

Commit fd617ea

Browse files
authored
Merge pull request #90 from rowingdude/testing-2.1.2
Version 3.0 - Now we're in Class!
2 parents fbb6b7b + cdc64d0 commit fd617ea

26 files changed

+661
-1179
lines changed

CHANGES.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33

44
This document lists the changes and version history for the AnalyzeMFT script and component scripts.
55

6-
## Version 2.1.2 (2024-08-13)
6+
## Version 3.0 (2024-08-15)
7+
8+
Work has completed on the class-based layout. The program has been split into individual files each composed of the class within.
9+
I believe this is the way to go (personal preference) as I like to work on one module at a time!
710

8-
Work has officially begun on the next minor revision of this application. The to do list is below. We'll update as things get finished.
911

1012
### To do list:
1113

12-
1. Finish the "class based layout"
13-
2. Improve the readabilityby breaking up some of the large functions like `parse_records`
14-
3. Implement a testing framework involving the sister project [GenerateMFT](https://github.com/rowingdude/GenerateMFT/).
15-
4. Implement multithreading as an option
14+
1. Implement a testing framework involving the sister project [GenerateMFT](https://github.com/rowingdude/GenerateMFT/).
15+
2. Implement multithreading as an option
1616

17+
<! ----------------- We are going to version up to 3.0 given the complete rewrite ------------------->
1718

1819
## Version 2.1.1 (2024-08-02)
1920

LICENSE.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
MIT License
2-
https://opensource.org/license/mit
3-
4-
Copyright 2024 Benjamin Cance
5-
6-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7-
8-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9-
10-
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1+
MIT License
2+
https://opensource.org/license/mit
3+
4+
Copyright 2024 Benjamin Cance
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9+
10+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

MANIFEST.in

Lines changed: 0 additions & 2 deletions
This file was deleted.

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ Basic usage:
2727

2828
## Versioning
2929

30-
Current version: 2.1
31-
Beta/Testing Version: 3.0
30+
Current version: 3.0
3231

3332
## Author
3433

README.txt

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,47 @@
1-
# AnalyzeMFT
2-
3-
AnalyzeMFT is a Python script designed to translate the NTFS Master File Table (MFT) into a human-readable and searchable format, such as CSV. This tool is useful for digital forensics, file system analysis, and understanding the structure of NTFS volumes.
4-
5-
## Features
6-
7-
- Parse NTFS MFT files
8-
- Generate CSV output of MFT records
9-
- Create timeline in CSV format
10-
- Produce bodyfile output for timeline analysis
11-
- Support for local timezone reporting
12-
- Anomaly detection (optional)
13-
- Debugging output (optional)
14-
15-
## Requirements
16-
17-
- Python 3.x
18-
19-
## Installation
20-
21-
1. Clone this repository or download the script files.
22-
2. Ensure you have Python 3.x installed on your system.
23-
24-
Basic usage:
25-
26-
`python AnalyzeMFT.py -f <mft_file> -o <output_file>`
27-
28-
## Versioning
29-
30-
Current version: 2.1
31-
Beta/Testing Version: 3.0
32-
33-
## Author
34-
35-
Benjamin Cance ([email protected])
36-
37-
## License
38-
39-
Copyright Benjamin Cance 2024
40-
41-
## Contributing
42-
43-
If you'd like to contribute to this project, please submit a pull request or open an issue on the project's repository.
44-
45-
## Disclaimer
46-
47-
This tool is provided as-is, without any warranties. Use at your own risk and ensure you have the necessary permissions before analyzing any file systems or MFT data.
48-
1+
# AnalyzeMFT
2+
3+
AnalyzeMFT is a Python script designed to translate the NTFS Master File Table (MFT) into a human-readable and searchable format, such as CSV. This tool is useful for digital forensics, file system analysis, and understanding the structure of NTFS volumes.
4+
5+
## Features
6+
7+
- Parse NTFS MFT files
8+
- Generate CSV output of MFT records
9+
- Create timeline in CSV format
10+
- Produce bodyfile output for timeline analysis
11+
- Support for local timezone reporting
12+
- Anomaly detection (optional)
13+
- Debugging output (optional)
14+
15+
## Requirements
16+
17+
- Python 3.x
18+
19+
## Installation
20+
21+
1. Clone this repository or download the script files.
22+
2. Ensure you have Python 3.x installed on your system.
23+
24+
Basic usage:
25+
26+
`python AnalyzeMFT.py -f <mft_file> -o <output_file>`
27+
28+
## Versioning
29+
30+
Current version: 3.0
31+
32+
## Author
33+
34+
Benjamin Cance ([email protected])
35+
36+
## License
37+
38+
Copyright Benjamin Cance 2024
39+
40+
## Contributing
41+
42+
If you'd like to contribute to this project, please submit a pull request or open an issue on the project's repository.
43+
44+
## Disclaimer
45+
46+
This tool is provided as-is, without any warranties. Use at your own risk and ensure you have the necessary permissions before analyzing any file systems or MFT data.
47+

analyzeMFT.py

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,23 @@
1-
# Version 2.1.1
2-
#
3-
# Author: Benjamin Cance ([email protected])
4-
#
5-
# 2-Aug-24
6-
# - Updating to current PEP
7-
8-
import logging
9-
import sys
10-
from analyzemft.mft_session import MftSession
11-
from analyzemft.config import Config
12-
from analyzemft.error_handler import setup_logging, MFTAnalysisError
13-
14-
def main():
15-
config = Config()
16-
config.parse_args()
17-
conf = config.get_config()
18-
19-
setup_logging(conf['log_level'])
20-
21-
try:
22-
session = MftSession(conf)
23-
session.run()
24-
except MFTAnalysisError as e:
25-
logging.error(f"MFT analysis failed: {str(e)}")
26-
sys.exit(1)
27-
except Exception as e:
28-
logging.exception(f"An unexpected error occurred: {str(e)}")
29-
sys.exit(1)
30-
31-
if __name__ == "__main__":
32-
main()
1+
from analyze_mft.common_imports import *
2+
from analyze_mft.mft_parser import MFTParser
3+
from analyze_mft.file_handler import FileHandler
4+
from analyze_mft.csv_writer import CSVWriter
5+
from analyze_mft.options_parser import OptionsParser
6+
from analyze_mft.attribute_parser import AttributeParser
7+
8+
def main():
9+
options_parser = OptionsParser()
10+
options = options_parser.parse_options()
11+
12+
file_handler = FileHandler(options)
13+
file_handler.open_files()
14+
15+
csv_writer = CSVWriter(options, file_handler)
16+
17+
mft_parser = MFTParser(options, file_handler, csv_writer)
18+
mft_parser.parse_mft_file()
19+
mft_parser.generate_filepaths()
20+
mft_parser.print_records()
21+
22+
if __name__ == "__main__":
23+
main()

analyze_mft/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from .mft_parser import MFTParser
2+
from .mft_record import MFTRecord
3+
from .attribute_parser import AttributeParser
4+
from .windows_time import WindowsTime
5+
from .file_handler import FileHandler
6+
from .csv_writer import CSVWriter
7+
from .options_parser import OptionsParser
8+
from .constants import VERSION
9+
10+
__all__ = ['MFTParser', 'MFTRecord', 'AttributeParser', 'WindowsTime',
11+
'FileHandler', 'CSVWriter', 'OptionsParser', 'VERSION']
12+
13+
__version__ = VERSION

analyze_mft/attribute_parser.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from .common_imports import *
2+
from .windows_time import WindowsTime
3+
4+
class AttributeParser:
5+
def __init__(self, raw_data, options):
6+
self.raw_data = raw_data
7+
self.options = options
8+
9+
def parse(self):
10+
return self.decode_attribute_header()
11+
12+
def decode_attribute_header(self):
13+
d = {}
14+
d['type'] = struct.unpack("<I", self.raw_data[:4])[0]
15+
if d['type'] == 0xffffffff:
16+
return d
17+
d['len'] = struct.unpack("<I", self.raw_data[4:8])[0]
18+
d['res'] = struct.unpack("B", self.raw_data[8:9])[0]
19+
d['name_off'] = struct.unpack("<H", self.raw_data[10:12])[0]
20+
d['flags'] = struct.unpack("<H", self.raw_data[12:14])[0]
21+
d['id'] = struct.unpack("<H", self.raw_data[14:16])[0]
22+
if d['res'] == 0:
23+
d['ssize'] = struct.unpack("<L", self.raw_data[16:20])[0]
24+
d['soff'] = struct.unpack("<H", self.raw_data[20:22])[0]
25+
d['idxflag'] = struct.unpack("<H", self.raw_data[22:24])[0]
26+
else:
27+
d['start_vcn'] = struct.unpack("<d", self.raw_data[16:24])[0]
28+
d['last_vcn'] = struct.unpack("<d", self.raw_data[24:32])[0]
29+
d['run_off'] = struct.unpack("<H", self.raw_data[32:34])[0]
30+
d['compusize'] = struct.unpack("<H", self.raw_data[34:36])[0]
31+
d['f1'] = struct.unpack("<I", self.raw_data[36:40])[0]
32+
d['alen'] = struct.unpack("<d", self.raw_data[40:48])[0]
33+
d['ssize'] = struct.unpack("<d", self.raw_data[48:56])[0]
34+
d['initsize'] = struct.unpack("<d", self.raw_data[56:64])[0]
35+
return d
36+
37+
def parse_standard_information(self):
38+
d = {}
39+
s = self.raw_data[self.decode_attribute_header()['soff']:]
40+
d['crtime'] = WindowsTime(struct.unpack("<L", s[:4])[0], struct.unpack("<L", s[4:8])[0], self.options.localtz)
41+
d['mtime'] = WindowsTime(struct.unpack("<L", s[8:12])[0], struct.unpack("<L", s[12:16])[0], self.options.localtz)
42+
d['ctime'] = WindowsTime(struct.unpack("<L", s[16:20])[0], struct.unpack("<L", s[20:24])[0], self.options.localtz)
43+
d['atime'] = WindowsTime(struct.unpack("<L", s[24:28])[0], struct.unpack("<L", s[28:32])[0], self.options.localtz)
44+
d['dos'] = struct.unpack("<I", s[32:36])[0]
45+
d['maxver'] = struct.unpack("<I", s[36:40])[0]
46+
d['ver'] = struct.unpack("<I", s[40:44])[0]
47+
d['class_id'] = struct.unpack("<I", s[44:48])[0]
48+
d['own_id'] = struct.unpack("<I", s[48:52])[0]
49+
d['sec_id'] = struct.unpack("<I", s[52:56])[0]
50+
d['quota'] = struct.unpack("<d", s[56:64])[0]
51+
d['usn'] = struct.unpack("<d", s[64:72])[0]
52+
return d
53+
54+
def parse_file_name(self, record):
55+
d = {}
56+
s = self.raw_data[self.decode_attribute_header()['soff']:]
57+
d['par_ref'] = struct.unpack("<Lxx", s[:6])[0]
58+
d['par_seq'] = struct.unpack("<H", s[6:8])[0]
59+
d['crtime'] = WindowsTime(struct.unpack("<L", s[8:12])[0], struct.unpack("<L", s[12:16])[0], self.options.localtz)
60+
d['mtime'] = WindowsTime(struct.unpack("<L", s[16:20])[0], struct.unpack("<L", s[20:24])[0], self.options.localtz)
61+
d['ctime'] = WindowsTime(struct.unpack("<L", s[24:28])[0], struct.unpack("<L", s[28:32])[0], self.options.localtz)
62+
d['atime'] = WindowsTime(struct.unpack("<L", s[32:36])[0], struct.unpack("<L", s[36:40])[0], self.options.localtz)
63+
d['alloc_fsize'] = struct.unpack("<q", s[40:48])[0]
64+
d['real_fsize'] = struct.unpack("<q", s[48:56])[0]
65+
d['flags'] = struct.unpack("<d", s[56:64])[0]
66+
d['nlen'] = struct.unpack("B", s[64:65])[0]
67+
d['nspace'] = struct.unpack("B", s[65:66])[0]
68+
69+
bytes_left = d['nlen']*2
70+
d['name'] = s[66:66+bytes_left].decode('utf-16-le')
71+
72+
return d

analyze_mft/common_imports.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import struct
2+
import datetime
3+
import os
4+
import csv
5+
import binascii
6+
import sys
7+
from datetime import datetime, timezone
8+
from optparse import OptionParser

analyze_mft/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VERSION = '3.0'

0 commit comments

Comments
 (0)