Skip to content

Commit

Permalink
Create the analyzemft/sql directory, add a couple starter tables, mod…
Browse files Browse the repository at this point in the history
…ify cli, filewriters, analyzer to (TESTING) output SQLite tables.
  • Loading branch information
rowingdude committed Sep 17, 2024
1 parent 40a7bc8 commit 0ecc181
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 4 deletions.
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
openpyxl==3.0.10
openpyxl==3.0.10
sqlite3
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
author_email='bjc@tdx.li',
package_dir={'': 'src'},
packages=find_packages(where='src'),
package_data={
'analyzeMFT': ['sql/*.sql'],
},
url='http://github.com/rowingdude/analyzeMFT',
license='LICENSE.txt',
description='Analyze the $MFT from a NTFS filesystem.',
Expand All @@ -31,6 +34,7 @@
python_requires=">=3.7",
install_requires=[
"pywin32;platform_system=='Windows'",
"openpyxl==3.0.10",
],
entry_points={
'console_scripts': [
Expand Down
3 changes: 3 additions & 0 deletions src/analyzeMFT/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ async def main():
help="Export as TSK timeline")
export_group.add_option("--l2t", action="store_const", const="l2t", dest="export_format",
help="Export as log2timeline CSV")
export_group.add_option("--sqlite", action="store_const", const="sqlite", dest="export_format",
help="Export as SQLite database")

parser.add_option_group(export_group)

verbosity_group = OptionGroup(parser, "Verbosity Options")
Expand Down
59 changes: 57 additions & 2 deletions src/analyzeMFT/file_writers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import asyncio
import csv
import os
import json
import sqlite3
import xml.etree.ElementTree as ET
import asyncio
from typing import List, Dict, Any
from .mft_record import MftRecord
from .constants import *
Expand Down Expand Up @@ -86,4 +88,57 @@ async def write_l2t(records: List[MftRecord], output_file: str) -> None:
date_str, time_str, 'UTC', macb, 'MFT', 'FILESYSTEM', time_type, '', '', '',
f"{record.filename} {time_type}", '', record.filename, record.recordnum, '', '', ''
])
await asyncio.sleep(0)
await asyncio.sleep(0)


@staticmethod
async def write_sqlite(records: List[MftRecord], output_file: str) -> None:
conn = sqlite3.connect(output_file)
cursor = conn.cursor()

# Create and populate static tables
sql_dir = os.path.join(os.path.dirname(__file__), 'sql')
for sql_file in os.listdir(sql_dir):
with open(os.path.join(sql_dir, sql_file), 'r') as f:
cursor.executescript(f.read())

# Create MFT records table
cursor.execute('''
CREATE TABLE mft_records (
record_number INTEGER PRIMARY KEY,
filename TEXT,
parent_record_number INTEGER,
file_size INTEGER,
is_directory INTEGER,
creation_time TEXT,
modification_time TEXT,
access_time TEXT,
entry_time TEXT,
attribute_types TEXT
)
''')

# Insert MFT records
for record in records:
cursor.execute('''
INSERT INTO mft_records (
record_number, filename, parent_record_number, file_size,
is_directory, creation_time, modification_time, access_time,
entry_time, attribute_types
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
record.recordnum,
record.filename,
record.get_parent_record_num(),
record.filesize,
1 if record.flags & FILE_RECORD_IS_DIRECTORY else 0,
record.fn_times['crtime'].dtstr,
record.fn_times['mtime'].dtstr,
record.fn_times['atime'].dtstr,
record.fn_times['ctime'].dtstr,
','.join(map(str, record.attribute_types))
))

conn.commit()
conn.close()
await asyncio.sleep(0)
43 changes: 42 additions & 1 deletion src/analyzeMFT/mft_analyzer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import asyncio
import csv
import io
import os
import signal
import sqlite3
import sys
import traceback
from typing import Dict, Set, List, Optional, Any
Expand Down Expand Up @@ -245,11 +247,50 @@ async def write_output(self) -> None:
await FileWriters.write_xml(list(self.mft_records.values()), self.output_file)
elif self.export_format == "excel":
await FileWriters.write_excel(list(self.mft_records.values()), self.output_file)
elif self.export_format == "sqlite":
await FileWriters.write_sqlite(list(self.mft_records.values()), self.output_file)
else:
print(f"Unsupported export format: {self.export_format}")

async def cleanup(self):
self.log("Performing cleanup...", 1)
# to-do add more cleanup after database stuff is integrated.
await self.write_remaining_records()
self.log("Cleanup complete.", 1)
self.log("Cleanup complete.", 1)

async def create_sqlite_database(self):
conn = sqlite3.connect(self.output_file)
cursor = conn.cursor()

# Create and populate static tables
sql_dir = os.path.join(os.path.dirname(__file__), 'sql')
for sql_file in os.listdir(sql_dir):
with open(os.path.join(sql_dir, sql_file), 'r') as f:
cursor.executescript(f.read())

# Create MFT records table
cursor.execute('''
CREATE TABLE mft_records (
record_number INTEGER PRIMARY KEY,
filename TEXT,
parent_record_number INTEGER,
-- Add other fields as needed
FOREIGN KEY (attribute_type) REFERENCES attribute_types(id)
)
''')

conn.commit()
return conn

async def write_sqlite(self):
conn = await self.create_sqlite_database()
cursor = conn.cursor()

for record in self.mft_records.values():
cursor.execute('''
INSERT INTO mft_records (record_number, filename, parent_record_number)
VALUES (?, ?, ?)
''', (record.recordnum, record.filename, record.get_parent_record_num()))

conn.commit()
conn.close()
21 changes: 21 additions & 0 deletions src/analyzeMFT/sql/attribute_types.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CREATE TABLE attribute_types (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);

INSERT INTO attribute_types (id, name) VALUES
(16, '$STANDARD_INFORMATION'),
(32, '$ATTRIBUTE_LIST'),
(48, '$FILE_NAME'),
(64, '$OBJECT_ID'),
(80, '$SECURITY_DESCRIPTOR'),
(96, '$VOLUME_NAME'),
(112, '$VOLUME_INFORMATION'),
(128, '$DATA'),
(144, '$INDEX_ROOT'),
(160, '$INDEX_ALLOCATION'),
(176, '$BITMAP'),
(192, '$REPARSE_POINT'),
(208, '$EA_INFORMATION'),
(224, '$EA'),
(256, '$LOGGED_UTILITY_STREAM');
10 changes: 10 additions & 0 deletions src/analyzeMFT/sql/file_record_flags.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE TABLE file_record_flags (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);

INSERT INTO file_record_flags (id, name) VALUES
(1, 'FILE_RECORD_IN_USE'),
(2, 'FILE_RECORD_IS_DIRECTORY'),
(4, 'FILE_RECORD_IS_EXTENSION'),
(8, 'FILE_RECORD_HAS_SPECIAL_INDEX');

0 comments on commit 0ecc181

Please sign in to comment.