Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions BitsParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ def output_jobs(self, file_path, jobs):
"""Cleans up and outputs the parsed jobs from the qmgr database files"""

# If an output file is specified, open it and use it instead of stdout

if self.out_file:
orig_stdout = sys.stdout
sys.stdout = open(self.out_file, "w")
Expand All @@ -350,6 +351,20 @@ def output_jobs(self, file_path, jobs):
sys.stdout = orig_stdout


def get_unique_records(self, jobs):
records = list()
for job in jobs:
# Skip incomplete carved jobs as they do not contain useful info
if job.is_carved() and not job.is_useful_for_analysis():
continue

# Output unique jobs
if job.hash not in self.visited_jobs:
records.append(job.job_dict)
self.visited_jobs.add(job.hash)
return records


def process_file(self, file_path):
""" Processes the given BITS file. Attempts to find/parse jobs. """

Expand All @@ -363,7 +378,7 @@ def process_file(self, file_path):
# Parse as a qmgr database (support old and Win10 formats)
jobs = []
if BitsParser.is_qmgr_database(file_data):
jobs = self.load_qmgr_jobs(file_data)
jobs = self.load_qmgr_jobs(file_path)
elif BitsParser.is_qmgr10_database(file_data):
jobs = self.load_qmgr10_jobs(file_data)

Expand All @@ -374,7 +389,11 @@ def process_file(self, file_path):
else:
jobs = self.load_non_qmgr_jobs(file_data)

self.output_jobs(file_path, jobs)
if self.out_file and self.out_file.endswith(".csv"):
import csv_writer
csv_writer.write_csv(self.out_file, self.get_unique_records(jobs))
else:
self.output_jobs(file_path, jobs)

except Exception:
print(f'Exception occurred processing file {file_path}: ' + traceback.format_exc(), file=sys.stderr)
Expand Down
55 changes: 55 additions & 0 deletions csv_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import csv
import os


DEFAULT_VALUES = (
('JobId', None),
('JobName', None),
('JobType', None),
('JobPriority', None),
('OwnerSID', None),
('JobState', None),
('CommandExecuted', None),
('CommandArguments', None),
('FileID', 0),
('DestFile', None),
('SourceURL', None),
('TmpFile', None),
('DownloadByteSize', -1),
('TransferByteSize', -1),
('VolumeGUID', None),
('CreationTime', None),
('ModifiedTime', None),
('Carved', False)
)

def flattener(job):

def _f(index, file):
rv = {k: file.get(k, job.get(k, v)) for k, v in DEFAULT_VALUES}
rv['FileID'] = index
return rv

files = job.get('Files', [])

if files:
return [_f(index, f) for index, f in enumerate(files)]

return [_f(0, {})]


def write_csv(filename, records):
"""Write records to a CSV file."""
if not len(records):
return
if os.path.isfile(filename):
csvfile = open(filename, "a+", newline='', encoding='utf-8')
else:
csvfile = open(filename, "w", newline='', encoding='utf-8')

writer = csv.DictWriter(csvfile, fieldnames=[k for k, _ in DEFAULT_VALUES])
writer.writeheader()
for r in records:
for sub_r in flattener(r):
writer.writerow(sub_r)
csvfile.close()