diff --git a/scripts/ilap_artifacts.py b/scripts/ilap_artifacts.py index 7c01bf46..c5b95fd0 100644 --- a/scripts/ilap_artifacts.py +++ b/scripts/ilap_artifacts.py @@ -207,7 +207,7 @@ 'notificationsXI': ('Notifications', '*PushStore*'), 'notificationsXII': ('Notifications', '*private/var/mobile/Library/UserNotifications*'), 'ooklaSpeedtestData': ('Applications', '**/speedtest.sqlite*'), - 'photosMetadata': ('Photos', '**/Photos.sqlite'), + #'photosMetadata': ('Photos', '**/Photos.sqlite'), 'powerlogAll': ('Powerlog', '**/CurrentPowerlog.PLSQL'), 'powerlogGZ': ('Powerlog Backups', '**/Library/BatteryLife/Archives/powerlog_*.PLSQL.gz'), 'queryPredictions': ('SMS & iMessage', '**/query_predictions.db'), diff --git a/scripts/location_map_constants.py b/scripts/location_map_constants.py new file mode 100644 index 00000000..136d58b5 --- /dev/null +++ b/scripts/location_map_constants.py @@ -0,0 +1,106 @@ +# coding: utf-8 +iLEAPP_KMLs = "_KML Exports" + +defaultShadowUrl = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png' + +defaultIconUrl = 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-{}.png' + +colors = ['red','green','blue','orange','violet','yellow', 'sienna', 'indigo','purple','cyan','darkgray'] + +legend_tag = "
  • {1}
  • " + +legend_title_tag = "
    {0}
    " + +legend_div = """ +
    + +
    +""" + +template_part1 = """ +{% macro html(this, kwargs) %} + + + + + + + + jQuery UI Draggable - Default functionality + + + + + + + + + + +
    +""" +template_part2 = """ +
    + + + + + +{% endmacro %} +""" + diff --git a/scripts/location_map_report.py b/scripts/location_map_report.py new file mode 100644 index 00000000..20ed1b39 --- /dev/null +++ b/scripts/location_map_report.py @@ -0,0 +1,126 @@ +# coding: utf-8 +#Import the necessary Python modules +import pandas as pd +import folium +from folium.plugins import TimestampedGeoJson +from shapely.geometry import Point +import os +from datetime import datetime +from branca.element import Template, MacroElement +import html +from scripts.location_map_constants import iLEAPP_KMLs, defaultShadowUrl, defaultIconUrl, colors, legend_tag, legend_title_tag, legend_div, template_part1, template_part2 +import sqlite3 + +from scripts.artifact_report import ArtifactHtmlReport + +#Helpers +def htmlencode(string): + return string.encode(encoding='ascii',errors='xmlcharrefreplace').decode('utf-8') + +def geodfToFeatures(df, f, props): + coords = [] + times = [] + for i,row in df[df.Description.str.contains(f)].iterrows(): + coords.append( + [row.Point.x,row.Point.y] + ) + times.append(datetime.strptime(row.Name,'%Y-%m-%d %H:%M:%S').isoformat()) + return { + 'type': 'Feature', + 'geometry': { + 'type': props[f]['fType'], + 'coordinates': coords, + }, + 'properties': { + 'times': times, + 'style': {'color': props[f]['color']}, + 'icon': props[f]['icon'], + 'iconstyle': { + 'iconUrl': props[f]['iconUrl'], + 'shadowUrl': props[f]['shadowUrl'], + 'iconSize': [25, 41], + 'iconAnchor': [12, 41], + 'popupAnchor': [1, -34], + 'shadowSize': [41, 41], + 'radius': 5, + }, + }, + } + + +def generate_location_map(reportfolderbase,legend_title): + KML_path = os.path.join(reportfolderbase,iLEAPP_KMLs) + if not os.path.isdir(KML_path) or not os.listdir(KML_path): + return + + location_path = os.path.join(reportfolderbase, 'LOCATIONS') + os.makedirs(location_path,exist_ok=True) + db = sqlite3.connect(os.path.join(KML_path,"_latlong.db")) + df = pd.read_sql_query("SELECT key as Name, Activity as Description, latitude, longitude FROM data ;", db) + df["Point"] = df.apply(lambda row: Point(float(row['longitude']),float(row['latitude']),.0), axis=1) + + #sorting is needed for correct display + df.sort_values(by=['Name'],inplace=True) + + #Parse geo data and add to Folium Map + data_names = df[~df.Description.str.contains('Photos')].Description.unique() + featuresProp = {} + + for c,d in zip(colors, data_names): + descFilter = d + if 'ZRT' in d: + fType = 'LineString' + icon = 'marker' + iconUrl = defaultIconUrl.format(c) + shadowUrl = defaultShadowUrl + else: + fType = 'MultiPoint' + icon = 'circle' + iconUrl = '' + shadowUrl = '' + + color = c + + featuresProp[d] = { + 'fType': fType, + 'color': c, + 'icon': icon, + 'iconUrl': iconUrl, + 'shadowUrl': defaultShadowUrl, + } + + location_map = folium.Map([df.iloc[0].Point.y,df.iloc[0].Point.x], prefer_canvas=True, zoom_start = 6) + bounds = (df[~df.Description.str.contains('Photos')]['longitude'].min(), + df[~df.Description.str.contains('Photos')]['latitude'].min(), + df[~df.Description.str.contains('Photos')]['longitude'].max(), + df[~df.Description.str.contains('Photos')]['latitude'].max(), + ) + location_map.fit_bounds([ + (bounds[1],bounds[0]), + (bounds[3],bounds[2]), + ] + ) + + tsGeo = TimestampedGeoJson({ + 'type': 'FeatureCollection', + 'features': [ + geodfToFeatures(df, f, featuresProp) for f in data_names + ] + }, period="PT1M", duration="PT1H", loop=False, transition_time = 50, time_slider_drag_update=True, add_last_point=True, max_speed=200).add_to(location_map) + + + #legend + legend = '\n'.join([ legend_tag.format(featuresProp[f]['color'], htmlencode(f)) for f in data_names]) + template = '\n'.join([template_part1, legend_title_tag.format(htmlencode(legend_title)), legend_div.format(legend), template_part2]) + + macro = MacroElement() + macro._template = Template(template) + + location_map.get_root().add_child(macro) + + location_map.save(os.path.join(location_path,"Locations_Map.html")) + + report = ArtifactHtmlReport('Locations Map') + report.start_artifact_report(location_path, 'Locations Map', 'Map plotting all locations') + report.write_raw_html(open(os.path.join(location_path,"Locations_Map.html")).read()) + report.end_artifact_report() diff --git a/scripts/report.py b/scripts/report.py index 33663e14..ce82dfda 100644 --- a/scripts/report.py +++ b/scripts/report.py @@ -9,6 +9,7 @@ from scripts.html_parts import * from scripts.ilapfuncs import logfunc from scripts.version_info import aleapp_version, aleapp_contributors +from scripts.location_map_report import generate_location_map def get_icon_name(category, artifact): @@ -227,6 +228,8 @@ def get_icon_name(category, artifact): icon = 'user' return icon + #generate loaation map with all KML files + generate_location_map(reportfolderbase, "Legend") def generate_report(reportfolderbase, time_in_secs, time_HMS, extraction_type, image_input_path): control = None diff --git a/scripts/version_info.py b/scripts/version_info.py index 5877c63f..78f1b2de 100644 --- a/scripts/version_info.py +++ b/scripts/version_info.py @@ -4,31 +4,31 @@ # Format = [ Name, Blog-url, Twitter-handle, Github-url] # Leave blank if not available aleapp_contributors = [ - ['Alexis Brignoni', 'https://abrignoni.com', '@AlexisBrignoni', 'https://github.com/abrignoni'], - ['Yogesh Khatri', 'https://swiftforensics.com', '@SwiftForensics', 'https://github.com/ydkhatri'], - ['Agam Dua', 'https://loopback.dev', '@loopbackdev', 'https://github.com/agamdua'], - ['Sarah Edwards', 'https://www.mac4n6.com', '@iamevltwin', 'https://github.com/mac4n6'], - ['Heather Mahalik', 'https://smarterforensics.com', '@HeatherMahalik', ''], - ['Jessica Hyde', 'https://twitter.com/B1N2H3X', '@B1N2H3X', ''], - ['Phill Moore', 'https://thinkdfir.com', '@phillmoore', 'https://github.com/randomaccess3'], - ['Mattia Epifani', 'https://blog.digital-forensics.it', '@mattiaep', ''], - ['Mike Williamson', 'https://forensicmike1.com', '@forensicmike1', 'https://github.com/forensicmike'], - ['Geraldine Blay', 'https://gforce4n6.blogspot.com', '@i_am_the_gia', ''], - ['Christopher Vance', 'https://blog.d204n6.com', '@cScottVance', ''], - ['Brooke Gottlieb', '', '@xbrookego', ''], - ['Jack Farley', 'http://farleyforensics.com', '@JackFarley248', ''], - ['Shafik Punja', '', '@qubytelogic', ''], - ['Cheeky4N6Monkey', 'https://cheeky4n6monkey.blogspot.com', '@Cheeky4n6Monkey', 'https://github.com/cheeky4n6monkey'], - ['Edward Greybeard', '', '', 'https://github.com/edward-greybeard'], - ['Douglas Kein', '', '@DouglasKein', ''], - ['Claudia Meda', '', '@KlodiaMaida', 'https://github.com/KlodiaMaida'], - ['Silvia Spallarossa', '', '@SilviaSpallaro1', 'https://github.com/spatimbs'], - ['Francesca Maestri', '', '@franc3sca_m', 'https://github.com/francyM'], - ['Christopher Vance', 'https://blog.d204n6.com/', '@cScottVance', 'https://github.com/cScottVance'], - ['Jesse Spangenberger', 'https://cyberfenixtech.blogspot.com/', '@AzuleOnyx', 'https://github.com/flamusdiu'], - ['Jon Baumann', 'https://ciofecaforensics.com/', '@CiofecaForensic', 'https://github.com/threeplanetssoftware'], - ['Scott Koenig', '', '@Scott_Kjr', ''], - ['Kevin Pagano', 'https://stark4n6.com/', '@KevinPagano3', 'https://github.com/stark4n6'], + [ 'Alexis Brignoni', 'https://abrignoni.com', '@AlexisBrignoni', 'https://github.com/abrignoni'], + [ 'Yogesh Khatri', 'https://swiftforensics.com', '@SwiftForensics', 'https://github.com/ydkhatri'], + [ 'Agam Dua', 'https://loopback.dev', '@loopbackdev', 'https://github.com/agamdua'], + [ 'Sarah Edwards', 'https://www.mac4n6.com', '@iamevltwin', 'https://github.com/mac4n6'], + [ 'Heather Mahalik', 'https://smarterforensics.com', '@HeatherMahalik', ''], + [ 'Jessica Hyde', 'https://twitter.com/B1N2H3X', '@B1N2H3X', ''], + [ 'Phill Moore', 'https://thinkdfir.com', '@phillmoore', 'https://github.com/randomaccess3'], + [ 'Mattia Epifani', 'https://blog.digital-forensics.it', '@mattiaep', ''], + [ 'Mike Williamson', 'https://forensicmike1.com', '@forensicmike1', 'https://github.com/forensicmike'], + [ 'Geraldine Blay', 'https://gforce4n6.blogspot.com', '@i_am_the_gia', ''], + [ 'Christopher Vance', 'https://blog.d204n6.com', '@cScottVance', ''], + [ 'Brooke Gottlieb', '', '@xbrookego', ''], + [ 'Jack Farley', 'http://farleyforensics.com', '@JackFarley248', ''], + [ 'Shafik Punja', '', '@qubytelogic', ''], + [ 'Cheeky4N6Monkey', 'https://cheeky4n6monkey.blogspot.com', '@Cheeky4n6Monkey', 'https://github.com/cheeky4n6monkey'], + [ 'Edward Greybeard', '', '', 'https://github.com/edward-greybeard'], + [ 'Douglas Kein', '', '@DouglasKein', ''], + [ 'Claudia Meda', '', '@KlodiaMaida', 'https://github.com/KlodiaMaida'], + [ 'Silvia Spallarossa', '', '@SilviaSpallaro1', 'https://github.com/spatimbs'], + [ 'Francesca Maestri', '', '@franc3sca_m', 'https://github.com/francyM'], + [ 'Christopher Vance', 'https://blog.d204n6.com/', '@cScottVance', 'https://github.com/cScottVance'], + [ 'Jesse Spangenberger', 'https://cyberfenixtech.blogspot.com/', '@AzuleOnyx', 'https://github.com/flamusdiu'], + [ 'Jon Baumann', 'https://ciofecaforensics.com/', '@CiofecaForensic', 'https://github.com/threeplanetssoftware'], + [ 'Scott Koenig', '', '@Scott_Kjr', ''], + [ 'Kevin Pagano', 'https://stark4n6.com/', '@KevinPagano3', 'https://github.com/stark4n6'], ['Ed Michael', '', '@EdXlg123', 'https://github.com/edmichael'], ['Anna-Mariya Mateyna', '', '@any333_snickers', 'https://github.com/any333'], ['Tommy Harris', '', '@tobraha', 'https://github.com/tobraha']