42
42
from java .lang import ProcessBuilder
43
43
from java .util import ArrayList
44
44
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
47
50
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
56
57
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
64
63
from org .sleuthkit .datamodel .Blackboard import BlackboardException
65
64
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
- )
70
65
from org .sleuthkit .datamodel .blackboardutils .CommunicationArtifactsHelper import (
71
66
CallMediaType ,
67
+ )
68
+ from org .sleuthkit .datamodel .blackboardutils .CommunicationArtifactsHelper import (
72
69
CommunicationDirection ,
70
+ )
71
+ from org .sleuthkit .datamodel .blackboardutils .CommunicationArtifactsHelper import (
73
72
MessageReadStatus ,
74
73
)
74
+ from org .sleuthkit .datamodel .blackboardutils .attributes import MessageAttachments
75
+ from org .sleuthkit .datamodel .blackboardutils .attributes .MessageAttachments import (
76
+ URLAttachment ,
77
+ )
75
78
76
79
# Common Prefix Shared for all artefacts
77
80
ARTIFACT_PREFIX = "Microsoft Teams"
@@ -141,11 +144,11 @@ def startUp(self, context):
141
144
)
142
145
if not os .path .exists (self .path_to_executable ):
143
146
raise IngestModuleException (
144
- "Could not find main .exe within the module directory."
147
+ "Could not find ms_teams_parser .exe within the module directory."
145
148
)
146
149
else :
147
150
raise IngestModuleException (
148
- "This Plugin currently only works on Windows based systems."
151
+ "This plugin currently only works on Windows based systems."
149
152
)
150
153
151
154
blackboard = Case .getCurrentCase ().getServices ().getBlackboard ()
@@ -210,11 +213,11 @@ def _parse_databases(self, content, progress_bar):
210
213
os .makedirs (temp_path_to_content )
211
214
self .log (
212
215
Level .INFO ,
213
- f "Created temporary directory: { temp_path_to_content } ." ,
216
+ "Created temporary directory: {}." . format ( temp_path_to_content ) ,
214
217
)
215
218
except OSError :
216
219
raise IngestModuleException (
217
- f "Could not create directory: { temp_path_to_content } ."
220
+ "Could not create directory: {}." . format ( temp_path_to_content )
218
221
)
219
222
220
223
# At first extract the desired artefacts to our newly created temp directory
@@ -238,15 +241,15 @@ def _extract(self, content, path):
238
241
# ignore relative paths
239
242
if child_name == "." or child_name == ".." :
240
243
continue
241
- elif child .isFile (): # noqa: RET507
244
+ elif child .isFile ():
242
245
ContentUtils .writeToFile (child , File (child_path ))
243
246
elif child .isDir ():
244
247
os .mkdir (child_path )
245
248
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 ) )
247
250
except OSError :
248
251
raise IngestModuleException (
249
- f "Could not extract files to directory: { path } ."
252
+ "Could not extract files to directory: {}." . format ( path )
250
253
)
251
254
252
255
def _analyze (self , content , path , progress_bar ):
@@ -286,20 +289,25 @@ def _process_imported_records(self, imported_records, content, progress_bar):
286
289
database_sub_files = [
287
290
i
288
291
for n , i in enumerate (imported_records )
289
- if i .get ("origin_file" )
292
+ if "origin_file" in i and i .get ("origin_file" )
290
293
not in [y .get ("origin_file" ) for y in imported_records [n + 1 :]]
291
294
]
292
295
try :
293
296
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
+
294
301
user_account_instance = None
295
302
teams_leveldb_file_path = self .get_level_db_file (
296
303
content , file ["origin_file" ]
297
304
)
305
+
298
306
# Get only the records per file
299
307
records = [
300
308
d
301
309
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" ]
303
311
]
304
312
try :
305
313
user_account_instance = self .get_user_account (records )
@@ -480,7 +488,12 @@ def parse_calllogs(self, calls, helper):
480
488
start_date = self .date_to_long (
481
489
call ["properties" ]["call-log" ]["startTime" ]
482
490
)
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
484
497
485
498
helper .addCalllog (
486
499
call_direction ,
@@ -508,18 +521,20 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
508
521
# each message artifact
509
522
try :
510
523
for message in messages :
524
+
511
525
message_type = ARTIFACT_PREFIX
512
526
message_id = message ["clientmessageid" ]
513
527
direction = self .deduce_message_direction (message ["isFromMe" ])
514
528
phone_number_from = message ["creator" ]
515
529
# TODO Fix To Number
516
- phone_number_to = ""
530
+ phone_number_to = []
517
531
message_date_time = self .date_to_long (message ["composetime" ])
518
532
message_read_status = MessageReadStatus .UNKNOWN
519
533
subject = None
520
534
message_text = message ["content" ]
521
535
# Group by the conversationId, these can be direct messages, but also posts
522
536
thread_id = message ["conversationId" ]
537
+ # Additional Attributes
523
538
524
539
additional_attributes = ArrayList ()
525
540
additional_attributes .add (
@@ -551,11 +566,15 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
551
566
552
567
if "properties" in message :
553
568
# process links
554
- if "links" in message ["properties" ]:
569
+ if not ( message ["properties" ]. get ( 'links' ) is None ) :
555
570
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
557
576
# process emotions
558
- if "emotions" in message ["properties" ]:
577
+ if not ( message ["properties" ]. get ( 'emotions' ) is None ) :
559
578
for emotion in message ["properties" ]["emotions" ]:
560
579
# emotions are grouped by their key like, heart..
561
580
# Add one reaction entry by per
@@ -579,7 +598,7 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
579
598
file_attachments , url_attachments
580
599
)
581
600
helper .addAttachments (artifact , message_attachments )
582
-
601
+
583
602
except TskCoreException as ex :
584
603
# Severe error trying to add to case database.. case is not complete.
585
604
# These exceptions are thrown by the CommunicationArtifactsHelper.
@@ -692,17 +711,26 @@ def get_level_db_file(self, content, filepath):
692
711
dir_name = os .path .join (content .getParentPath (), content .getName ())
693
712
results = file_manager .findFiles (data_source , filename , dir_name )
694
713
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 (
698
717
0
699
718
) # Expect a single match so retrieve the first (and only) file
719
+ return db_file
700
720
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
706
734
707
735
# Extract the direction of a phone call
708
736
def deduce_call_direction (self , direction ):
@@ -715,10 +743,10 @@ def deduce_call_direction(self, direction):
715
743
return call_direction
716
744
717
745
def deduce_message_direction (self , is_from_me ):
718
- call_direction = CommunicationDirection .INCOMING
746
+ message_direction = CommunicationDirection .INCOMING
719
747
if is_from_me is True :
720
- call_direction = CommunicationDirection .OUTGOING
721
- return call_direction
748
+ message_direction = CommunicationDirection .OUTGOING
749
+ return message_direction
722
750
723
751
def create_artifact_type (self , artifact_name , artifact_description , blackboard ):
724
752
try :
@@ -768,7 +796,9 @@ def process(self, data_source, progress_bar):
768
796
769
797
self .log (
770
798
Level .INFO ,
771
- f"Found { directories_to_process } { directory } directories to process." ,
799
+ "Found {} {} directories to process." .format (
800
+ directories_to_process , directory
801
+ ),
772
802
)
773
803
774
804
for i , content in enumerate (all_ms_teams_leveldbs ):
@@ -803,4 +833,4 @@ def process(self, data_source, progress_bar):
803
833
"Finished analysing the LeveLDB from Microsoft Teams." ,
804
834
)
805
835
IngestServices .getInstance ().postMessage (message )
806
- return IngestModule .ProcessResult .OK
836
+ return IngestModule .ProcessResult .OK
0 commit comments