Skip to content

Commit afed70e

Browse files
authored
fix: string formatting errors in Autopsy (#84)
* fix: string formatting errors in Autopsy * feat: various small improvements
1 parent ab1d1d9 commit afed70e

File tree

1 file changed

+79
-49
lines changed

1 file changed

+79
-49
lines changed

tools/Forensicsim_Parser.py

Lines changed: 79 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -42,36 +42,39 @@
4242
from java.lang import ProcessBuilder
4343
from java.util import ArrayList
4444
from java.util.logging import Level
45-
from org.sleuthkit.autopsy.casemodule import Case, NoCurrentCaseException
46-
from org.sleuthkit.autopsy.coreutils import ExecUtil, Logger, PlatformUtil
45+
from org.sleuthkit.autopsy.casemodule import Case
46+
from org.sleuthkit.autopsy.casemodule import NoCurrentCaseException
47+
from org.sleuthkit.autopsy.coreutils import ExecUtil
48+
from org.sleuthkit.autopsy.coreutils import Logger
49+
from org.sleuthkit.autopsy.coreutils import PlatformUtil
4750
from org.sleuthkit.autopsy.datamodel import ContentUtils
48-
from org.sleuthkit.autopsy.ingest import (
49-
DataSourceIngestModule,
50-
DataSourceIngestModuleProcessTerminator,
51-
IngestMessage,
52-
IngestModule,
53-
IngestModuleFactoryAdapter,
54-
IngestServices,
55-
)
51+
from org.sleuthkit.autopsy.ingest import DataSourceIngestModule
52+
from org.sleuthkit.autopsy.ingest import DataSourceIngestModuleProcessTerminator
53+
from org.sleuthkit.autopsy.ingest import IngestMessage
54+
from org.sleuthkit.autopsy.ingest import IngestModule
55+
from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter
56+
from org.sleuthkit.autopsy.ingest import IngestServices
5657
from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException
57-
from org.sleuthkit.datamodel import (
58-
BlackboardArtifact,
59-
BlackboardAttribute,
60-
CommunicationsManager,
61-
TskCoreException,
62-
TskData,
63-
)
58+
from org.sleuthkit.datamodel import BlackboardArtifact
59+
from org.sleuthkit.datamodel import BlackboardAttribute
60+
from org.sleuthkit.datamodel import CommunicationsManager
61+
from org.sleuthkit.datamodel import TskCoreException
62+
from org.sleuthkit.datamodel import TskData
6463
from org.sleuthkit.datamodel.Blackboard import BlackboardException
6564
from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper
66-
from org.sleuthkit.datamodel.blackboardutils.attributes import MessageAttachments
67-
from org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments import (
68-
URLAttachment,
69-
)
7065
from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import (
7166
CallMediaType,
67+
)
68+
from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import (
7269
CommunicationDirection,
70+
)
71+
from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import (
7372
MessageReadStatus,
7473
)
74+
from org.sleuthkit.datamodel.blackboardutils.attributes import MessageAttachments
75+
from org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments import (
76+
URLAttachment,
77+
)
7578

7679
# Common Prefix Shared for all artefacts
7780
ARTIFACT_PREFIX = "Microsoft Teams"
@@ -141,11 +144,11 @@ def startUp(self, context):
141144
)
142145
if not os.path.exists(self.path_to_executable):
143146
raise IngestModuleException(
144-
"Could not find main.exe within the module directory."
147+
"Could not find ms_teams_parser.exe within the module directory."
145148
)
146149
else:
147150
raise IngestModuleException(
148-
"This Plugin currently only works on Windows based systems."
151+
"This plugin currently only works on Windows based systems."
149152
)
150153

151154
blackboard = Case.getCurrentCase().getServices().getBlackboard()
@@ -210,11 +213,11 @@ def _parse_databases(self, content, progress_bar):
210213
os.makedirs(temp_path_to_content)
211214
self.log(
212215
Level.INFO,
213-
f"Created temporary directory: {temp_path_to_content}.",
216+
"Created temporary directory: {}.".format(temp_path_to_content),
214217
)
215218
except OSError:
216219
raise IngestModuleException(
217-
f"Could not create directory: {temp_path_to_content}."
220+
"Could not create directory: {}.".format(temp_path_to_content)
218221
)
219222

220223
# At first extract the desired artefacts to our newly created temp directory
@@ -238,15 +241,15 @@ def _extract(self, content, path):
238241
# ignore relative paths
239242
if child_name == "." or child_name == "..":
240243
continue
241-
elif child.isFile(): # noqa: RET507
244+
elif child.isFile():
242245
ContentUtils.writeToFile(child, File(child_path))
243246
elif child.isDir():
244247
os.mkdir(child_path)
245248
self._extract(child, child_path)
246-
self.log(Level.INFO, f"Successfully extracted to {path}")
249+
self.log(Level.INFO, "Successfully extracted to {}".format(path))
247250
except OSError:
248251
raise IngestModuleException(
249-
f"Could not extract files to directory: {path}."
252+
"Could not extract files to directory: {}.".format(path)
250253
)
251254

252255
def _analyze(self, content, path, progress_bar):
@@ -286,20 +289,25 @@ def _process_imported_records(self, imported_records, content, progress_bar):
286289
database_sub_files = [
287290
i
288291
for n, i in enumerate(imported_records)
289-
if i.get("origin_file")
292+
if "origin_file" in i and i.get("origin_file")
290293
not in [y.get("origin_file") for y in imported_records[n + 1 :]]
291294
]
292295
try:
293296
for file in database_sub_files:
297+
# Skip empty files as these are invalid records
298+
if file["origin_file"] is None:
299+
continue
300+
294301
user_account_instance = None
295302
teams_leveldb_file_path = self.get_level_db_file(
296303
content, file["origin_file"]
297304
)
305+
298306
# Get only the records per file
299307
records = [
300308
d
301309
for d in imported_records
302-
if d["origin_file"] == file["origin_file"]
310+
if 'origin_file' in d and d["origin_file"] == file["origin_file"]
303311
]
304312
try:
305313
user_account_instance = self.get_user_account(records)
@@ -480,7 +488,12 @@ def parse_calllogs(self, calls, helper):
480488
start_date = self.date_to_long(
481489
call["properties"]["call-log"]["startTime"]
482490
)
483-
end_date = self.date_to_long(call["properties"]["call-log"]["endTime"])
491+
end_date = self.date_to_long(
492+
call["properties"]["call-log"]["endTime"]
493+
)
494+
# Skip empty callees
495+
if to_address is None:
496+
continue
484497

485498
helper.addCalllog(
486499
call_direction,
@@ -508,18 +521,20 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
508521
# each message artifact
509522
try:
510523
for message in messages:
524+
511525
message_type = ARTIFACT_PREFIX
512526
message_id = message["clientmessageid"]
513527
direction = self.deduce_message_direction(message["isFromMe"])
514528
phone_number_from = message["creator"]
515529
# TODO Fix To Number
516-
phone_number_to = ""
530+
phone_number_to = []
517531
message_date_time = self.date_to_long(message["composetime"])
518532
message_read_status = MessageReadStatus.UNKNOWN
519533
subject = None
520534
message_text = message["content"]
521535
# Group by the conversationId, these can be direct messages, but also posts
522536
thread_id = message["conversationId"]
537+
# Additional Attributes
523538

524539
additional_attributes = ArrayList()
525540
additional_attributes.add(
@@ -551,11 +566,15 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
551566

552567
if "properties" in message:
553568
# process links
554-
if "links" in message["properties"]:
569+
if not (message["properties"].get('links') is None):
555570
for link in message["properties"]["links"]:
556-
url_attachments.add(URLAttachment(link["url"]))
571+
try:
572+
url_to_store = str(link.get('url'))
573+
url_attachments.add(URLAttachment(url_to_store))
574+
except AttributeError:
575+
continue
557576
# process emotions
558-
if "emotions" in message["properties"]:
577+
if not (message["properties"].get('emotions') is None):
559578
for emotion in message["properties"]["emotions"]:
560579
# emotions are grouped by their key like, heart..
561580
# Add one reaction entry by per
@@ -579,7 +598,7 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
579598
file_attachments, url_attachments
580599
)
581600
helper.addAttachments(artifact, message_attachments)
582-
601+
583602
except TskCoreException as ex:
584603
# Severe error trying to add to case database.. case is not complete.
585604
# These exceptions are thrown by the CommunicationArtifactsHelper.
@@ -692,17 +711,26 @@ def get_level_db_file(self, content, filepath):
692711
dir_name = os.path.join(content.getParentPath(), content.getName())
693712
results = file_manager.findFiles(data_source, filename, dir_name)
694713
if results.isEmpty():
695-
self.log(Level.INFO, f"Unable to locate {filename}")
696-
return None
697-
return results.get(
714+
self.log(Level.INFO, "Unable to locate {}".format(filename))
715+
return
716+
db_file = results.get(
698717
0
699718
) # Expect a single match so retrieve the first (and only) file
719+
return db_file
700720

701-
def date_to_long(self, formatted_date):
702-
# Timestamp
703-
dt = datetime.strptime(formatted_date[:19], "%Y-%m-%dT%H:%M:%S")
704-
time_struct = dt.timetuple()
705-
return int(calendar.timegm(time_struct))
721+
def date_to_long(self, passed_date):
722+
try:
723+
# Newer versions store the dates as unix timestamps
724+
composetime = float(passed_date)
725+
timestamp = int(composetime)
726+
# Cut off the miliseconds
727+
timestamp = int(timestamp/1000)
728+
except ValueError:
729+
# Timestamp
730+
dt = datetime.strptime(passed_date[:19], "%Y-%m-%dT%H:%M:%S")
731+
time_struct = dt.timetuple()
732+
timestamp = int(calendar.timegm(time_struct))
733+
return timestamp
706734

707735
# Extract the direction of a phone call
708736
def deduce_call_direction(self, direction):
@@ -715,10 +743,10 @@ def deduce_call_direction(self, direction):
715743
return call_direction
716744

717745
def deduce_message_direction(self, is_from_me):
718-
call_direction = CommunicationDirection.INCOMING
746+
message_direction = CommunicationDirection.INCOMING
719747
if is_from_me is True:
720-
call_direction = CommunicationDirection.OUTGOING
721-
return call_direction
748+
message_direction = CommunicationDirection.OUTGOING
749+
return message_direction
722750

723751
def create_artifact_type(self, artifact_name, artifact_description, blackboard):
724752
try:
@@ -768,7 +796,9 @@ def process(self, data_source, progress_bar):
768796

769797
self.log(
770798
Level.INFO,
771-
f"Found {directories_to_process} {directory} directories to process.",
799+
"Found {} {} directories to process.".format(
800+
directories_to_process, directory
801+
),
772802
)
773803

774804
for i, content in enumerate(all_ms_teams_leveldbs):
@@ -803,4 +833,4 @@ def process(self, data_source, progress_bar):
803833
"Finished analysing the LeveLDB from Microsoft Teams.",
804834
)
805835
IngestServices.getInstance().postMessage(message)
806-
return IngestModule.ProcessResult.OK
836+
return IngestModule.ProcessResult.OK

0 commit comments

Comments
 (0)