mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 07:56:16 +00:00
Merge pull request #5712 from dannysmyda/6081-Use-Helper-For-Artifact-Creation
6081 use helper for artifact creation
This commit is contained in:
commit
8e052b0865
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -24,7 +24,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +40,7 @@ abstract class AbstractSingleEntityParser implements XRYFileParser {
|
|||||||
protected static final String PARSER_NAME = "XRY DSP";
|
protected static final String PARSER_NAME = "XRY DSP";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException {
|
public void parse(XRYFileReader reader, Content parent, SleuthkitCase currentCase) throws IOException, TskCoreException, BlackboardException {
|
||||||
Path reportPath = reader.getReportPath();
|
Path reportPath = reader.getReportPath();
|
||||||
logger.log(Level.INFO, String.format("[XRY DSP] Processing report at [ %s ]", reportPath.toString()));
|
logger.log(Level.INFO, String.format("[XRY DSP] Processing report at [ %s ]", reportPath.toString()));
|
||||||
|
|
||||||
@ -94,7 +96,7 @@ abstract class AbstractSingleEntityParser implements XRYFileParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!keyValuePairs.isEmpty()) {
|
if(!keyValuePairs.isEmpty()) {
|
||||||
makeArtifact(keyValuePairs, parent);
|
makeArtifact(keyValuePairs, parent, currentCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,9 +124,9 @@ abstract class AbstractSingleEntityParser implements XRYFileParser {
|
|||||||
*/
|
*/
|
||||||
abstract boolean isNamespace(String nameSpace);
|
abstract boolean isNamespace(String nameSpace);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes an artifact from the parsed key value pairs.
|
* Makes an artifact from the parsed key value pairs.
|
||||||
*/
|
*/
|
||||||
abstract void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException;
|
abstract void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, BlackboardException;
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,24 +18,22 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.temporal.TemporalAccessor;
|
|
||||||
import java.time.temporal.TemporalQueries;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.Account;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CallMediaType;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CommunicationDirection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses XRY Calls files and creates artifacts.
|
* Parses XRY Calls files and creates artifacts.
|
||||||
@ -44,27 +42,19 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName());
|
private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName());
|
||||||
|
|
||||||
//Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch()
|
|
||||||
//function for more details.
|
|
||||||
private static final DateTimeFormatter DATE_TIME_PARSER
|
|
||||||
= DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y");
|
|
||||||
|
|
||||||
private static final String DEVICE_LOCALE = "(device)";
|
|
||||||
private static final String NETWORK_LOCALE = "(network)";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All of the known XRY keys for call reports and their corresponding
|
* All of the known XRY keys for call reports and their corresponding
|
||||||
* blackboard attribute types, if any.
|
* blackboard attribute types, if any.
|
||||||
*/
|
*/
|
||||||
private enum XryKey {
|
private enum XryKey {
|
||||||
NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
|
NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
|
||||||
TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME),
|
TIME("time", null),
|
||||||
DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION),
|
DIRECTION("direction", null),
|
||||||
CALL_TYPE("call type", null),
|
CALL_TYPE("call type", null),
|
||||||
NUMBER("number", null),
|
NUMBER("number", null),
|
||||||
TEL("tel", null),
|
TEL("tel", null),
|
||||||
TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO),
|
TO("to", null),
|
||||||
FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM),
|
FROM("from", null),
|
||||||
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
|
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
|
||||||
DURATION("duration", null),
|
DURATION("duration", null),
|
||||||
STORAGE("storage", null),
|
STORAGE("storage", null),
|
||||||
@ -175,25 +165,18 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException {
|
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, BlackboardException {
|
||||||
List<BlackboardAttribute> attributes = new ArrayList<>();
|
// Transform all the data from XRY land into the appropriate CommHelper
|
||||||
for(XRYKeyValuePair pair : keyValuePairs) {
|
// data types.
|
||||||
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
String callerId = null;
|
||||||
if(attribute.isPresent()) {
|
final Collection<String> calleeList = new ArrayList<>();
|
||||||
attributes.add(attribute.get());
|
CommunicationDirection direction = CommunicationDirection.UNKNOWN;
|
||||||
}
|
long startTime = 0L;
|
||||||
}
|
final long endTime = 0L;
|
||||||
if(!attributes.isEmpty()) {
|
final CallMediaType callType = CallMediaType.UNKNOWN;
|
||||||
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG);
|
final Collection<BlackboardAttribute> otherAttributes = new ArrayList<>();
|
||||||
artifact.addAttributes(attributes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
for (XRYKeyValuePair pair : keyValuePairs) {
|
||||||
* Creates the appropriate blackboard attribute given a single XRY Key Value
|
|
||||||
* pair, if any. Most XRY keys are mapped to an attribute type in the enum above.
|
|
||||||
*/
|
|
||||||
private Optional<BlackboardAttribute> getBlackboardAttribute(XRYKeyValuePair pair) {
|
|
||||||
XryKey xryKey = XryKey.fromDisplayName(pair.getKey());
|
XryKey xryKey = XryKey.fromDisplayName(pair.getKey());
|
||||||
XryNamespace xryNamespace = XryNamespace.NONE;
|
XryNamespace xryNamespace = XryNamespace.NONE;
|
||||||
if (XryNamespace.contains(pair.getNamespace())) {
|
if (XryNamespace.contains(pair.getNamespace())) {
|
||||||
@ -203,38 +186,69 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
|
|||||||
switch (xryKey) {
|
switch (xryKey) {
|
||||||
case TEL:
|
case TEL:
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
//Apply the namespace
|
if(!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
switch (xryNamespace) {
|
continue;
|
||||||
case FROM:
|
}
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM,
|
// Apply namespace or direction
|
||||||
PARSER_NAME, pair.getValue()));
|
if (xryNamespace == XryNamespace.FROM || direction == CommunicationDirection.INCOMING) {
|
||||||
case TO:
|
callerId = pair.getValue();
|
||||||
return Optional.of(new BlackboardAttribute(
|
} else if (xryNamespace == XryNamespace.TO || direction == CommunicationDirection.OUTGOING) {
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO,
|
calleeList.add(pair.getValue());
|
||||||
PARSER_NAME, pair.getValue()));
|
} else {
|
||||||
default:
|
otherAttributes.add(new BlackboardAttribute(
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
||||||
PARSER_NAME, pair.getValue()));
|
PARSER_NAME, pair.getValue()));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
// Although confusing, as these are also 'name spaces', it appears
|
||||||
|
// later versions of XRY just made these standardized lines.
|
||||||
|
case TO:
|
||||||
|
if(!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
calleeList.add(pair.getValue());
|
||||||
|
break;
|
||||||
|
case FROM:
|
||||||
|
if(!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
callerId = pair.getValue();
|
||||||
|
break;
|
||||||
case TIME:
|
case TIME:
|
||||||
try {
|
try {
|
||||||
//Tranform value to seconds since epoch
|
//Tranform value to seconds since epoch
|
||||||
long dateTimeSinceEpoch = calculateSecondsSinceEpoch(pair.getValue());
|
long dateTimeSinceEpoch = XRYUtils.calculateSecondsSinceEpoch(pair.getValue());
|
||||||
return Optional.of(new BlackboardAttribute(xryKey.getType(),
|
startTime = dateTimeSinceEpoch;
|
||||||
PARSER_NAME, dateTimeSinceEpoch));
|
|
||||||
} catch (DateTimeParseException ex) {
|
} catch (DateTimeParseException ex) {
|
||||||
logger.log(Level.WARNING, String.format("[XRY DSP] Assumption"
|
logger.log(Level.WARNING, String.format("[XRY DSP] Assumption"
|
||||||
+ " about the date time formatting of call logs is "
|
+ " about the date time formatting of call logs is "
|
||||||
+ "not right. Here is the value [ %s ]", pair.getValue()), ex);
|
+ "not right. Here is the value [ %s ]", pair.getValue()), ex);
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case DIRECTION:
|
||||||
|
String directionString = pair.getValue().toLowerCase();
|
||||||
|
if (directionString.equals("incoming")) {
|
||||||
|
direction = CommunicationDirection.INCOMING;
|
||||||
|
} else {
|
||||||
|
direction = CommunicationDirection.OUTGOING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TYPE:
|
||||||
|
String typeString = pair.getValue();
|
||||||
|
if (typeString.equalsIgnoreCase("received")) {
|
||||||
|
direction = CommunicationDirection.INCOMING;
|
||||||
|
} else if (typeString.equalsIgnoreCase("dialed")) {
|
||||||
|
direction = CommunicationDirection.OUTGOING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
|
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
|
||||||
//type.
|
//type.
|
||||||
if (xryKey.getType() != null) {
|
if (xryKey.getType() != null) {
|
||||||
return Optional.of(new BlackboardAttribute(xryKey.getType(),
|
otherAttributes.add(new BlackboardAttribute(xryKey.getType(),
|
||||||
PARSER_NAME, pair.getValue()));
|
PARSER_NAME, pair.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,92 +256,66 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
|
|||||||
+ "(in brackets) [ %s ] was recognized but "
|
+ "(in brackets) [ %s ] was recognized but "
|
||||||
+ "more data or time is needed to finish implementation. Discarding... ",
|
+ "more data or time is needed to finish implementation. Discarding... ",
|
||||||
pair));
|
pair));
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Make sure we have the required fields, otherwise the CommHelper will
|
||||||
* Removes the locale from the date time value.
|
// complain about illegal arguments.
|
||||||
*
|
|
||||||
* Locale in this case being (Device) or (Network).
|
// These are all the invalid combinations.
|
||||||
*
|
if (callerId == null && calleeList.isEmpty()
|
||||||
* @param dateTime XRY datetime value to be sanitized.
|
|| direction == CommunicationDirection.INCOMING && callerId == null
|
||||||
* @return A purer date time value.
|
|| direction == CommunicationDirection.OUTGOING && calleeList.isEmpty()) {
|
||||||
*/
|
|
||||||
private String removeDateTimeLocale(String dateTime) {
|
// If the combo is invalid, just make an artifact with what we've got.
|
||||||
String result = dateTime;
|
if (direction != CommunicationDirection.UNKNOWN) {
|
||||||
int deviceIndex = result.toLowerCase().indexOf(DEVICE_LOCALE);
|
otherAttributes.add(new BlackboardAttribute(
|
||||||
if (deviceIndex != -1) {
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION,
|
||||||
result = result.substring(0, deviceIndex);
|
PARSER_NAME, direction.getDisplayName()));
|
||||||
}
|
|
||||||
int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE);
|
|
||||||
if (networkIndex != -1) {
|
|
||||||
result = result.substring(0, networkIndex);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (startTime > 0L) {
|
||||||
* Parses the date time value and calculates seconds since epoch.
|
otherAttributes.add(new BlackboardAttribute(
|
||||||
*
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START,
|
||||||
* @param dateTime
|
PARSER_NAME, startTime));
|
||||||
* @return
|
}
|
||||||
*/
|
|
||||||
private long calculateSecondsSinceEpoch(String dateTime) {
|
// If the DIRECTION check failed, just manually create accounts
|
||||||
String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim();
|
// for these phones. Note, there is no need to create relationships.
|
||||||
/**
|
// If both callerId and calleeList were non-null/non-empty, then
|
||||||
* The format of time in XRY Messages reports is of the form:
|
// it would have been a valid combination.
|
||||||
*
|
if (callerId != null) {
|
||||||
* 1/3/1990 1:23:54 AM UTC+4
|
currentCase.getCommunicationsManager().createAccountFileInstance(
|
||||||
*
|
Account.Type.PHONE, callerId, PARSER_NAME, parent);
|
||||||
* In our current version of Java (openjdk-1.8.0.222), there is a bug
|
|
||||||
* with having the timezone offset (UTC+4 or GMT-7) at the end of the
|
otherAttributes.add(new BlackboardAttribute(
|
||||||
* date time input. This is fixed in later versions of the JDK (9 and
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
||||||
* beyond). https://bugs.openjdk.java.net/browse/JDK-8154050 Rather than
|
PARSER_NAME, callerId));
|
||||||
* update the JDK to accommodate this, the components of the date time
|
}
|
||||||
* string are reversed:
|
|
||||||
*
|
for (String phone : calleeList) {
|
||||||
* UTC+4 AM 1:23:54 1/3/1990
|
currentCase.getCommunicationsManager().createAccountFileInstance(
|
||||||
*
|
Account.Type.PHONE, phone, PARSER_NAME, parent);
|
||||||
* The java time package will correctly parse this date time format.
|
|
||||||
*/
|
otherAttributes.add(new BlackboardAttribute(
|
||||||
String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale);
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
||||||
/**
|
PARSER_NAME, phone));
|
||||||
* Furthermore, the DateTimeFormatter's timezone offset letter ('O')
|
}
|
||||||
* does not recognize UTC but recognizes GMT. According to
|
|
||||||
* https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only
|
if (!otherAttributes.isEmpty()) {
|
||||||
* differs from UTC by at most 1 second and so substitution will only
|
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG);
|
||||||
* introduce a trivial amount of error.
|
artifact.addAttributes(otherAttributes);
|
||||||
*/
|
|
||||||
String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT");
|
currentCase.getBlackboard().postArtifact(artifact, PARSER_NAME);
|
||||||
TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT,
|
}
|
||||||
ZonedDateTime::from,
|
|
||||||
LocalDateTime::from,
|
|
||||||
OffsetDateTime::from);
|
|
||||||
//Query for the ZoneID
|
|
||||||
if (result.query(TemporalQueries.zoneId()) == null) {
|
|
||||||
//If none, assumed GMT+0.
|
|
||||||
return ZonedDateTime.of(LocalDateTime.from(result),
|
|
||||||
ZoneId.of("GMT")).toEpochSecond();
|
|
||||||
} else {
|
} else {
|
||||||
return Instant.from(result).getEpochSecond();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Otherwise we can safely use the helper.
|
||||||
* Reverses the order of the date time components.
|
CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
|
||||||
*
|
currentCase, PARSER_NAME, parent, Account.Type.PHONE);
|
||||||
* Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990
|
|
||||||
*
|
helper.addCalllog(direction, callerId, calleeList, startTime,
|
||||||
* @param dateTime
|
endTime, callType, otherAttributes);
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private String reverseOrderOfDateTimeComponents(String dateTime) {
|
|
||||||
StringBuilder reversedDateTime = new StringBuilder(dateTime.length());
|
|
||||||
String[] dateTimeComponents = dateTime.split(" ");
|
|
||||||
for (String component : dateTimeComponents) {
|
|
||||||
reversedDateTime.insert(0, " ").insert(0, component);
|
|
||||||
}
|
}
|
||||||
return reversedDateTime.toString().trim();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -19,16 +19,19 @@
|
|||||||
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import static org.sleuthkit.autopsy.datasourceprocessors.xry.AbstractSingleEntityParser.PARSER_NAME;
|
||||||
|
import org.sleuthkit.datamodel.Account;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses XRY Contacts-Contacts files and creates artifacts.
|
* Parses XRY Contacts-Contacts files and creates artifacts.
|
||||||
@ -37,31 +40,9 @@ final class XRYContactsFileParser extends AbstractSingleEntityParser {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(XRYContactsFileParser.class.getName());
|
private static final Logger logger = Logger.getLogger(XRYContactsFileParser.class.getName());
|
||||||
|
|
||||||
//All of the known XRY keys for contacts.
|
|
||||||
private static final Map<String, BlackboardAttribute.ATTRIBUTE_TYPE> XRY_KEYS =
|
|
||||||
new HashMap<String, BlackboardAttribute.ATTRIBUTE_TYPE>() {{
|
|
||||||
put("name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
|
|
||||||
put("tel", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER);
|
|
||||||
put("mobile", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE);
|
|
||||||
put("home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME);
|
|
||||||
put("related application", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME);
|
|
||||||
put("address home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION);
|
|
||||||
put("email home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_HOME);
|
|
||||||
put("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED);
|
|
||||||
|
|
||||||
//Ignoring or need more information to decide.
|
|
||||||
put("storage", null);
|
|
||||||
put("other", null);
|
|
||||||
put("picture", null);
|
|
||||||
put("index", null);
|
|
||||||
put("account name", null);
|
|
||||||
|
|
||||||
}};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean canProcess(XRYKeyValuePair pair) {
|
boolean canProcess(XRYKeyValuePair pair) {
|
||||||
String normalizedKey = pair.getKey().toLowerCase();
|
return XryKey.contains(pair.getKey());
|
||||||
return XRY_KEYS.containsKey(normalizedKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -70,36 +51,168 @@ final class XRYContactsFileParser extends AbstractSingleEntityParser {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Creates the appropriate blackboard attribute given a single XRY Key Value
|
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, Blackboard.BlackboardException {
|
||||||
* pair.
|
// Transform all the data from XRY land into the appropriate CommHelper
|
||||||
*/
|
// data types.
|
||||||
private Optional<BlackboardAttribute> getBlackboardAttribute(XRYKeyValuePair pair) {
|
String contactName = null;
|
||||||
String normalizedKey = pair.getKey().toLowerCase();
|
String phoneNumber = null;
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE attrType = XRY_KEYS.get(normalizedKey);
|
String homePhoneNumber = null;
|
||||||
if(attrType != null) {
|
String mobilePhoneNumber = null;
|
||||||
return Optional.of(new BlackboardAttribute(attrType, PARSER_NAME, pair.getValue()));
|
String emailAddr = null;
|
||||||
|
boolean hasAnEmail = false;
|
||||||
|
final Collection<BlackboardAttribute> additionalAttributes = new ArrayList<>();
|
||||||
|
|
||||||
|
for (XRYKeyValuePair pair : keyValuePairs) {
|
||||||
|
XryKey xryKey = XryKey.fromDisplayName(pair.getKey());
|
||||||
|
switch (xryKey) {
|
||||||
|
case NAME:
|
||||||
|
if (contactName != null) {
|
||||||
|
additionalAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, PARSER_NAME, pair.getValue()));
|
||||||
|
} else {
|
||||||
|
contactName = pair.getValue();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TEL:
|
||||||
|
if (!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phoneNumber != null) {
|
||||||
|
additionalAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, PARSER_NAME, pair.getValue()));
|
||||||
|
} else {
|
||||||
|
phoneNumber = pair.getValue();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MOBILE:
|
||||||
|
if (!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobilePhoneNumber != null) {
|
||||||
|
additionalAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE, PARSER_NAME, pair.getValue()));
|
||||||
|
} else {
|
||||||
|
mobilePhoneNumber = pair.getValue();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HOME:
|
||||||
|
if (!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (homePhoneNumber != null) {
|
||||||
|
additionalAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME, PARSER_NAME, pair.getValue()));
|
||||||
|
} else {
|
||||||
|
homePhoneNumber = pair.getValue();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EMAIL_HOME:
|
||||||
|
if (!XRYUtils.isEmailValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAnEmail = true;
|
||||||
|
additionalAttributes.add(new BlackboardAttribute(
|
||||||
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_HOME,
|
||||||
|
PARSER_NAME, pair.getValue()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
|
||||||
|
//type.
|
||||||
|
if (xryKey.getType() != null) {
|
||||||
|
additionalAttributes.add(new BlackboardAttribute(xryKey.getType(),
|
||||||
|
PARSER_NAME, pair.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(Level.INFO, String.format("[XRY DSP] Key value pair "
|
logger.log(Level.INFO, String.format("[XRY DSP] Key value pair "
|
||||||
+ "(in brackets) [ %s ] was recognized but we need "
|
+ "(in brackets) [ %s ] was recognized but "
|
||||||
+ "more data or time to finish implementation. Discarding... ",
|
+ "more data or time is needed to finish implementation. Discarding... ",
|
||||||
pair));
|
pair));
|
||||||
return Optional.empty();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Make sure we have the required fields, otherwise the CommHelper will
|
||||||
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException {
|
// complain about illegal arguments.
|
||||||
List<BlackboardAttribute> attributes = new ArrayList<>();
|
if (phoneNumber != null || homePhoneNumber != null || mobilePhoneNumber != null || hasAnEmail) {
|
||||||
for(XRYKeyValuePair pair : keyValuePairs) {
|
CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
|
||||||
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
currentCase, PARSER_NAME, parent, Account.Type.DEVICE);
|
||||||
if(attribute.isPresent()) {
|
|
||||||
attributes.add(attribute.get());
|
helper.addContact(contactName, phoneNumber, homePhoneNumber,
|
||||||
}
|
mobilePhoneNumber, emailAddr, additionalAttributes);
|
||||||
}
|
} else {
|
||||||
if(!attributes.isEmpty()) {
|
// Just create an artifact with the attributes that we do have.
|
||||||
|
if (!additionalAttributes.isEmpty()) {
|
||||||
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||||
artifact.addAttributes(attributes);
|
artifact.addAttributes(additionalAttributes);
|
||||||
|
|
||||||
|
currentCase.getBlackboard().postArtifact(artifact, PARSER_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum containing all known keys for contacts and their corresponding
|
||||||
|
* blackboard attribute. Some keys are intentionally null, because they are
|
||||||
|
* handled as special cases in makeArtifact(). Some are null because there's
|
||||||
|
* not an appropriate attribute type.
|
||||||
|
*/
|
||||||
|
private enum XryKey {
|
||||||
|
NAME("name", null),
|
||||||
|
TEL("tel", null),
|
||||||
|
MOBILE("mobile", null),
|
||||||
|
HOME("home", null),
|
||||||
|
RELATED_APPLICATION("related application", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME),
|
||||||
|
ADDRESS_HOME("address home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION),
|
||||||
|
EMAIL_HOME("email home", null),
|
||||||
|
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
|
||||||
|
//Ignoring or need more information to decide.
|
||||||
|
STORAGE("storage", null),
|
||||||
|
OTHER("other", null),
|
||||||
|
PICTURE("picture", null),
|
||||||
|
INDEX("index", null),
|
||||||
|
ACCOUNT_NAME("account name", null);
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final BlackboardAttribute.ATTRIBUTE_TYPE type;
|
||||||
|
|
||||||
|
XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlackboardAttribute.ATTRIBUTE_TYPE getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the display name of the XRY key is a recognized type.
|
||||||
|
*/
|
||||||
|
static boolean contains(String key) {
|
||||||
|
try {
|
||||||
|
XryKey.fromDisplayName(key);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the display name of the xry key to the appropriate enum type.
|
||||||
|
*
|
||||||
|
* It is assumed that XRY key string is recognized. Otherwise, an
|
||||||
|
* IllegalArgumentException is thrown. Test all membership with
|
||||||
|
* contains() before hand.
|
||||||
|
*/
|
||||||
|
static XryKey fromDisplayName(String key) {
|
||||||
|
String normalizedKey = key.trim().toLowerCase();
|
||||||
|
for (XryKey keyChoice : XryKey.values()) {
|
||||||
|
if (normalizedKey.equals(keyChoice.name)) {
|
||||||
|
return keyChoice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(String.format("Key [%s] was not found."
|
||||||
|
+ " All keys should be tested with contains.", key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -48,6 +48,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
|
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
|
||||||
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
|
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskDataException;
|
import org.sleuthkit.datamodel.TskDataException;
|
||||||
@ -204,12 +205,11 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
XRYFolder xryFolder = new XRYFolder(selectedPath);
|
XRYFolder xryFolder = new XRYFolder(selectedPath);
|
||||||
FileManager fileManager = Case.getCurrentCaseThrows()
|
Case currentCase = Case.getCurrentCaseThrows();
|
||||||
.getServices().getFileManager();
|
|
||||||
String uniqueUUID = UUID.randomUUID().toString();
|
String uniqueUUID = UUID.randomUUID().toString();
|
||||||
//Move heavy lifting to a background task.
|
//Move heavy lifting to a background task.
|
||||||
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
|
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
|
||||||
callback, fileManager, uniqueUUID);
|
callback, currentCase, uniqueUUID);
|
||||||
swingWorker.execute();
|
swingWorker.execute();
|
||||||
} catch (NoCurrentCaseException ex) {
|
} catch (NoCurrentCaseException ex) {
|
||||||
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
|
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
|
||||||
@ -238,11 +238,10 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
XRYFolder xryFolder = new XRYFolder(dataSourcePath);
|
XRYFolder xryFolder = new XRYFolder(dataSourcePath);
|
||||||
FileManager fileManager = Case.getCurrentCaseThrows()
|
Case currentCase = Case.getCurrentCaseThrows();
|
||||||
.getServices().getFileManager();
|
|
||||||
//Move heavy lifting to a background task.
|
//Move heavy lifting to a background task.
|
||||||
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
|
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
|
||||||
callBack, fileManager, deviceId);
|
callBack, currentCase, deviceId);
|
||||||
swingWorker.execute();
|
swingWorker.execute();
|
||||||
} catch (NoCurrentCaseException ex) {
|
} catch (NoCurrentCaseException ex) {
|
||||||
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
|
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
|
||||||
@ -273,20 +272,19 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
|||||||
|
|
||||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||||
private final DataSourceProcessorCallback callback;
|
private final DataSourceProcessorCallback callback;
|
||||||
private final FileManager fileManager;
|
private final Case currentCase;
|
||||||
private final XRYFolder xryFolder;
|
private final XRYFolder xryFolder;
|
||||||
private final String uniqueUUID;
|
private final String uniqueUUID;
|
||||||
|
|
||||||
public XRYReportProcessorSwingWorker(XRYFolder folder,
|
public XRYReportProcessorSwingWorker(XRYFolder folder,
|
||||||
DataSourceProcessorProgressMonitor progressMonitor,
|
DataSourceProcessorProgressMonitor progressMonitor,
|
||||||
DataSourceProcessorCallback callback,
|
DataSourceProcessorCallback callback,
|
||||||
FileManager fileManager,
|
Case currentCase, String uniqueUUID) {
|
||||||
String uniqueUUID) {
|
|
||||||
|
|
||||||
this.xryFolder = folder;
|
this.xryFolder = folder;
|
||||||
this.progressMonitor = progressMonitor;
|
this.progressMonitor = progressMonitor;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.fileManager = fileManager;
|
this.currentCase = currentCase;
|
||||||
this.uniqueUUID = uniqueUUID;
|
this.uniqueUUID = uniqueUUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +294,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
|||||||
"XRYDataSourceProcessor.processingFiles=Processing all XRY files..."
|
"XRYDataSourceProcessor.processingFiles=Processing all XRY files..."
|
||||||
})
|
})
|
||||||
protected LocalFilesDataSource doInBackground() throws TskCoreException,
|
protected LocalFilesDataSource doInBackground() throws TskCoreException,
|
||||||
TskDataException, IOException {
|
TskDataException, IOException, BlackboardException {
|
||||||
progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_preppingFiles());
|
progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_preppingFiles());
|
||||||
|
|
||||||
List<Path> nonXRYFiles = xryFolder.getNonXRYFiles();
|
List<Path> nonXRYFiles = xryFolder.getNonXRYFiles();
|
||||||
@ -304,7 +302,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
|||||||
//Map paths to string representations.
|
//Map paths to string representations.
|
||||||
.map(Path::toString)
|
.map(Path::toString)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
LocalFilesDataSource dataSource = fileManager.addLocalFilesDataSource(
|
LocalFilesDataSource dataSource = currentCase.getServices().getFileManager().addLocalFilesDataSource(
|
||||||
uniqueUUID,
|
uniqueUUID,
|
||||||
"XRY Text Export", //Name
|
"XRY Text Export", //Name
|
||||||
"", //Timezone
|
"", //Timezone
|
||||||
@ -313,7 +311,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
|||||||
|
|
||||||
//Process the report files.
|
//Process the report files.
|
||||||
progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_processingFiles());
|
progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_processingFiles());
|
||||||
XRYReportProcessor.process(xryFolder, dataSource);
|
XRYReportProcessor.process(xryFolder, dataSource, currentCase.getSleuthkitCase());
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -25,9 +25,11 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +91,7 @@ final class XRYDeviceGenInfoFileParser extends AbstractSingleEntityParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException {
|
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, Blackboard.BlackboardException {
|
||||||
List<BlackboardAttribute> attributes = new ArrayList<>();
|
List<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
for(int i = 0; i < keyValuePairs.size(); i+=2) {
|
for(int i = 0; i < keyValuePairs.size(); i+=2) {
|
||||||
Optional<BlackboardAttribute> attribute;
|
Optional<BlackboardAttribute> attribute;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -19,7 +19,9 @@
|
|||||||
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +42,7 @@ interface XRYFileParser {
|
|||||||
* @throws IOException If an I/O error occurs during reading.
|
* @throws IOException If an I/O error occurs during reading.
|
||||||
* @throws TskCoreException If an error occurs during artifact creation.
|
* @throws TskCoreException If an error occurs during artifact creation.
|
||||||
*/
|
*/
|
||||||
void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException;
|
void parse(XRYFileReader reader, Content parent, SleuthkitCase currentCase) throws IOException, TskCoreException, BlackboardException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -20,16 +20,9 @@ package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.temporal.TemporalAccessor;
|
|
||||||
import java.time.temporal.TemporalQueries;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -37,10 +30,15 @@ import java.util.Optional;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.Account;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CommunicationDirection;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.MessageReadStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses Messages-SMS files and creates artifacts.
|
* Parses Messages-SMS files and creates artifacts.
|
||||||
@ -52,31 +50,20 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
|||||||
|
|
||||||
private static final String PARSER_NAME = "XRY DSP";
|
private static final String PARSER_NAME = "XRY DSP";
|
||||||
|
|
||||||
//Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch()
|
|
||||||
//function for more details.
|
|
||||||
private static final DateTimeFormatter DATE_TIME_PARSER
|
|
||||||
= DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y");
|
|
||||||
|
|
||||||
private static final String DEVICE_LOCALE = "(device)";
|
|
||||||
private static final String NETWORK_LOCALE = "(network)";
|
|
||||||
|
|
||||||
private static final int READ = 1;
|
|
||||||
private static final int UNREAD = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All of the known XRY keys for message reports and their corresponding
|
* All of the known XRY keys for message reports and their corresponding
|
||||||
* blackboard attribute types, if any.
|
* blackboard attribute types, if any.
|
||||||
*/
|
*/
|
||||||
private enum XryKey {
|
private enum XryKey {
|
||||||
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
|
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
|
||||||
DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION),
|
DIRECTION("direction", null),
|
||||||
MESSAGE("message", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT),
|
MESSAGE("message", null),
|
||||||
NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON),
|
NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON),
|
||||||
TEXT("text", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT),
|
TEXT("text", null),
|
||||||
TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME),
|
TIME("time", null),
|
||||||
SERVICE_CENTER("service center", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER),
|
SERVICE_CENTER("service center", null),
|
||||||
FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM),
|
FROM("from", null),
|
||||||
TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO),
|
TO("to", null),
|
||||||
//The following keys either need special processing or more time and data to find a type.
|
//The following keys either need special processing or more time and data to find a type.
|
||||||
STORAGE("storage", null),
|
STORAGE("storage", null),
|
||||||
NUMBER("number", null),
|
NUMBER("number", null),
|
||||||
@ -272,7 +259,7 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
|||||||
* encountered.
|
* encountered.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException {
|
public void parse(XRYFileReader reader, Content parent, SleuthkitCase currentCase) throws IOException, TskCoreException, BlackboardException {
|
||||||
Path reportPath = reader.getReportPath();
|
Path reportPath = reader.getReportPath();
|
||||||
logger.log(Level.INFO, String.format("[XRY DSP] Processing report at"
|
logger.log(Level.INFO, String.format("[XRY DSP] Processing report at"
|
||||||
+ " [ %s ]", reportPath.toString()));
|
+ " [ %s ]", reportPath.toString()));
|
||||||
@ -282,26 +269,178 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
|||||||
|
|
||||||
while (reader.hasNextEntity()) {
|
while (reader.hasNextEntity()) {
|
||||||
String xryEntity = reader.nextEntity();
|
String xryEntity = reader.nextEntity();
|
||||||
List<BlackboardAttribute> attributes = getBlackboardAttributes(xryEntity, reader, referenceNumbersSeen);
|
|
||||||
//Only create artifacts with non-empty attributes.
|
// This call will combine all segmented text into a single key value pair
|
||||||
if (!attributes.isEmpty()) {
|
List<XRYKeyValuePair> pairs = getXRYKeyValuePairs(xryEntity, reader, referenceNumbersSeen);
|
||||||
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE);
|
|
||||||
artifact.addAttributes(attributes);
|
// Transform all the data from XRY land into the appropriate CommHelper
|
||||||
|
// data types.
|
||||||
|
final String messageType = PARSER_NAME;
|
||||||
|
CommunicationDirection direction = CommunicationDirection.UNKNOWN;
|
||||||
|
String senderId = null;
|
||||||
|
final List<String> recipientIdsList = new ArrayList<>();
|
||||||
|
long dateTime = 0L;
|
||||||
|
MessageReadStatus readStatus = MessageReadStatus.UNKNOWN;
|
||||||
|
final String subject = null;
|
||||||
|
String text = null;
|
||||||
|
final String threadId = null;
|
||||||
|
final Collection<BlackboardAttribute> otherAttributes = new ArrayList<>();
|
||||||
|
|
||||||
|
for(XRYKeyValuePair pair : pairs) {
|
||||||
|
XryNamespace namespace = XryNamespace.NONE;
|
||||||
|
if (XryNamespace.contains(pair.getNamespace())) {
|
||||||
|
namespace = XryNamespace.fromDisplayName(pair.getNamespace());
|
||||||
|
}
|
||||||
|
XryKey key = XryKey.fromDisplayName(pair.getKey());
|
||||||
|
String normalizedValue = pair.getValue().toLowerCase().trim();
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case TEL:
|
||||||
|
case NUMBER:
|
||||||
|
if(!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply namespace or direction
|
||||||
|
if(namespace == XryNamespace.FROM || direction == CommunicationDirection.INCOMING) {
|
||||||
|
senderId = pair.getValue();
|
||||||
|
} else if(namespace == XryNamespace.TO || direction == CommunicationDirection.OUTGOING) {
|
||||||
|
recipientIdsList.add(pair.getValue());
|
||||||
|
} else {
|
||||||
|
currentCase.getCommunicationsManager().createAccountFileInstance(
|
||||||
|
Account.Type.PHONE, pair.getValue(), PARSER_NAME, parent);
|
||||||
|
otherAttributes.add(new BlackboardAttribute(
|
||||||
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
||||||
|
PARSER_NAME, pair.getValue()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// Although confusing, as these are also 'name spaces', it appears
|
||||||
|
// later versions of XRY just made these standardized lines.
|
||||||
|
case FROM:
|
||||||
|
if(!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
senderId = pair.getValue();
|
||||||
|
break;
|
||||||
|
case TO:
|
||||||
|
if(!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
recipientIdsList.add(pair.getValue());
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
try {
|
||||||
|
//Tranform value to seconds since epoch
|
||||||
|
long dateTimeSinceInEpoch = XRYUtils.calculateSecondsSinceEpoch(pair.getValue());
|
||||||
|
dateTime = dateTimeSinceInEpoch;
|
||||||
|
} catch (DateTimeParseException ex) {
|
||||||
|
logger.log(Level.WARNING, String.format("[%s] Assumption"
|
||||||
|
+ " about the date time formatting of messages is "
|
||||||
|
+ "not right. Here is the pair [ %s ]", PARSER_NAME, pair), ex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TYPE:
|
||||||
|
switch (normalizedValue) {
|
||||||
|
case "incoming":
|
||||||
|
direction = CommunicationDirection.INCOMING;
|
||||||
|
break;
|
||||||
|
case "outgoing":
|
||||||
|
direction = CommunicationDirection.OUTGOING;
|
||||||
|
break;
|
||||||
|
case "deliver":
|
||||||
|
case "submit":
|
||||||
|
case "status report":
|
||||||
|
//Ignore for now.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.log(Level.WARNING, String.format("[%s] Unrecognized "
|
||||||
|
+ " value for key pair [ %s ].", PARSER_NAME, pair));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATUS:
|
||||||
|
switch (normalizedValue) {
|
||||||
|
case "read":
|
||||||
|
readStatus = MessageReadStatus.READ;
|
||||||
|
break;
|
||||||
|
case "unread":
|
||||||
|
readStatus = MessageReadStatus.UNREAD;
|
||||||
|
break;
|
||||||
|
case "deleted":
|
||||||
|
otherAttributes.add(new BlackboardAttribute(
|
||||||
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED,
|
||||||
|
PARSER_NAME, pair.getValue()));
|
||||||
|
break;
|
||||||
|
case "sending failed":
|
||||||
|
case "unsent":
|
||||||
|
case "sent":
|
||||||
|
//Ignoring for now.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.log(Level.WARNING, String.format("[%s] Unrecognized "
|
||||||
|
+ " value for key pair [ %s ].", PARSER_NAME, pair));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TEXT:
|
||||||
|
case MESSAGE:
|
||||||
|
text = pair.getValue();
|
||||||
|
break;
|
||||||
|
case DIRECTION:
|
||||||
|
switch (normalizedValue) {
|
||||||
|
case "incoming":
|
||||||
|
direction = CommunicationDirection.INCOMING;
|
||||||
|
break;
|
||||||
|
case "outgoing":
|
||||||
|
direction = CommunicationDirection.OUTGOING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
direction = CommunicationDirection.UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SERVICE_CENTER:
|
||||||
|
if(!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
otherAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
||||||
|
PARSER_NAME, pair.getValue()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
|
||||||
|
//type.
|
||||||
|
if (key.getType() != null) {
|
||||||
|
otherAttributes.add(new BlackboardAttribute(key.getType(),
|
||||||
|
PARSER_NAME, pair.getValue()));
|
||||||
|
} else {
|
||||||
|
logger.log(Level.INFO, String.format("[%s] Key value pair "
|
||||||
|
+ "(in brackets) [ %s ] was recognized but "
|
||||||
|
+ "more data or time is needed to finish implementation. Discarding... ",
|
||||||
|
PARSER_NAME, pair));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
|
||||||
|
currentCase, PARSER_NAME, parent, Account.Type.PHONE);
|
||||||
|
|
||||||
|
helper.addMessage(messageType, direction, senderId, recipientIdsList,
|
||||||
|
dateTime, readStatus, subject, text, threadId, otherAttributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts all blackboard attributes from the XRY Entity. This function will
|
* Extracts all pairs from the XRY Entity. This function
|
||||||
* unify any segmented text, if need be.
|
* will unify any segmented text, if need be.
|
||||||
*/
|
*/
|
||||||
private List<BlackboardAttribute> getBlackboardAttributes(String xryEntity,
|
private List<XRYKeyValuePair> getXRYKeyValuePairs(String xryEntity,
|
||||||
XRYFileReader reader, Set<Integer> referenceValues) throws IOException {
|
XRYFileReader reader, Set<Integer> referenceValues) throws IOException {
|
||||||
String[] xryLines = xryEntity.split("\n");
|
String[] xryLines = xryEntity.split("\n");
|
||||||
//First line of the entity is the title, each XRY entity is non-empty.
|
//First line of the entity is the title, each XRY entity is non-empty.
|
||||||
logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ]", xryLines[0]));
|
logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ]", xryLines[0]));
|
||||||
|
|
||||||
List<BlackboardAttribute> attributes = new ArrayList<>();
|
List<XRYKeyValuePair> pairs = new ArrayList<>();
|
||||||
|
|
||||||
//Count the key value pairs in the XRY entity.
|
//Count the key value pairs in the XRY entity.
|
||||||
int keyCount = getCountOfKeyValuePairs(xryLines);
|
int keyCount = getCountOfKeyValuePairs(xryLines);
|
||||||
@ -339,14 +478,10 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
|||||||
pair.getNamespace());
|
pair.getNamespace());
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the corresponding blackboard attribute, if any.
|
pairs.add(pair);
|
||||||
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
|
||||||
if (attribute.isPresent()) {
|
|
||||||
attributes.add(attribute.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributes;
|
return pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -531,191 +666,4 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
|||||||
|
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an attribute from the extracted key value pair.
|
|
||||||
*
|
|
||||||
* @param nameSpace The namespace of this key value pair. It will have been
|
|
||||||
* verified beforehand, otherwise it will be NONE.
|
|
||||||
* @param key The recognized XRY key.
|
|
||||||
* @param value The value associated with that key.
|
|
||||||
* @return Corresponding blackboard attribute, if any.
|
|
||||||
*/
|
|
||||||
private Optional<BlackboardAttribute> getBlackboardAttribute(XRYKeyValuePair pair) {
|
|
||||||
XryNamespace namespace = XryNamespace.NONE;
|
|
||||||
if (XryNamespace.contains(pair.getNamespace())) {
|
|
||||||
namespace = XryNamespace.fromDisplayName(pair.getNamespace());
|
|
||||||
}
|
|
||||||
XryKey key = XryKey.fromDisplayName(pair.getKey());
|
|
||||||
String normalizedValue = pair.getValue().toLowerCase().trim();
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case TEL:
|
|
||||||
case NUMBER:
|
|
||||||
switch (namespace) {
|
|
||||||
case FROM:
|
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM,
|
|
||||||
PARSER_NAME, pair.getValue()));
|
|
||||||
case TO:
|
|
||||||
case PARTICIPANT:
|
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO,
|
|
||||||
PARSER_NAME, pair.getValue()));
|
|
||||||
default:
|
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
|
||||||
PARSER_NAME, pair.getValue()));
|
|
||||||
}
|
|
||||||
case TIME:
|
|
||||||
try {
|
|
||||||
//Tranform value to seconds since epoch
|
|
||||||
long dateTimeSinceInEpoch = calculateSecondsSinceEpoch(pair.getValue());
|
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START,
|
|
||||||
PARSER_NAME, dateTimeSinceInEpoch));
|
|
||||||
} catch (DateTimeParseException ex) {
|
|
||||||
logger.log(Level.WARNING, String.format("[XRY DSP] Assumption"
|
|
||||||
+ " about the date time formatting of messages is "
|
|
||||||
+ "not right. Here is the pair [ %s ]", pair), ex);
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
case TYPE:
|
|
||||||
switch (normalizedValue) {
|
|
||||||
case "incoming":
|
|
||||||
case "outgoing":
|
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION,
|
|
||||||
PARSER_NAME, pair.getValue()));
|
|
||||||
case "deliver":
|
|
||||||
case "submit":
|
|
||||||
case "status report":
|
|
||||||
//Ignore for now.
|
|
||||||
return Optional.empty();
|
|
||||||
default:
|
|
||||||
logger.log(Level.WARNING, String.format("[XRY DSP] Unrecognized "
|
|
||||||
+ " value for key pair [ %s ].", pair));
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
case STATUS:
|
|
||||||
switch (normalizedValue) {
|
|
||||||
case "read":
|
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS,
|
|
||||||
PARSER_NAME, READ));
|
|
||||||
case "unread":
|
|
||||||
return Optional.of(new BlackboardAttribute(
|
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS,
|
|
||||||
PARSER_NAME, UNREAD));
|
|
||||||
case "sending failed":
|
|
||||||
case "deleted":
|
|
||||||
case "unsent":
|
|
||||||
case "sent":
|
|
||||||
//Ignore for now.
|
|
||||||
return Optional.empty();
|
|
||||||
default:
|
|
||||||
logger.log(Level.WARNING, String.format("[XRY DSP] Unrecognized "
|
|
||||||
+ " value for key pair [ %s ].", pair));
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
|
|
||||||
//type.
|
|
||||||
if (key.getType() != null) {
|
|
||||||
return Optional.of(new BlackboardAttribute(key.getType(),
|
|
||||||
PARSER_NAME, pair.getValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log(Level.INFO, String.format("[XRY DSP] Key value pair "
|
|
||||||
+ "(in brackets) [ %s ] was recognized but "
|
|
||||||
+ "more data or time is needed to finish implementation. Discarding... ", pair));
|
|
||||||
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the locale from the date time value.
|
|
||||||
*
|
|
||||||
* Locale in this case being (Device) or (Network).
|
|
||||||
*
|
|
||||||
* @param dateTime XRY datetime value to be sanitized.
|
|
||||||
* @return A purer date time value.
|
|
||||||
*/
|
|
||||||
private String removeDateTimeLocale(String dateTime) {
|
|
||||||
String result = dateTime;
|
|
||||||
int deviceIndex = result.toLowerCase().indexOf(DEVICE_LOCALE);
|
|
||||||
if (deviceIndex != -1) {
|
|
||||||
result = result.substring(0, deviceIndex);
|
|
||||||
}
|
|
||||||
int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE);
|
|
||||||
if (networkIndex != -1) {
|
|
||||||
result = result.substring(0, networkIndex);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the date time value and calculates seconds since epoch.
|
|
||||||
*
|
|
||||||
* @param dateTime
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private long calculateSecondsSinceEpoch(String dateTime) {
|
|
||||||
String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim();
|
|
||||||
/**
|
|
||||||
* The format of time in XRY Messages reports is of the form:
|
|
||||||
*
|
|
||||||
* 1/3/1990 1:23:54 AM UTC+4
|
|
||||||
*
|
|
||||||
* In our current version of Java (openjdk-1.8.0.222), there is a bug
|
|
||||||
* with having the timezone offset (UTC+4 or GMT-7) at the end of the
|
|
||||||
* date time input. This is fixed in later versions of the JDK (9 and
|
|
||||||
* beyond). https://bugs.openjdk.java.net/browse/JDK-8154050 Rather than
|
|
||||||
* update the JDK to accommodate this, the components of the date time
|
|
||||||
* string are reversed:
|
|
||||||
*
|
|
||||||
* UTC+4 AM 1:23:54 1/3/1990
|
|
||||||
*
|
|
||||||
* The java time package will correctly parse this date time format.
|
|
||||||
*/
|
|
||||||
String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale);
|
|
||||||
/**
|
|
||||||
* Furthermore, the DateTimeFormatter's timezone offset letter ('O')
|
|
||||||
* does not recognize UTC but recognizes GMT. According to
|
|
||||||
* https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only
|
|
||||||
* differs from UTC by at most 1 second and so substitution will only
|
|
||||||
* introduce a trivial amount of error.
|
|
||||||
*/
|
|
||||||
String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT");
|
|
||||||
TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT,
|
|
||||||
ZonedDateTime::from,
|
|
||||||
LocalDateTime::from,
|
|
||||||
OffsetDateTime::from);
|
|
||||||
//Query for the ZoneID
|
|
||||||
if (result.query(TemporalQueries.zoneId()) == null) {
|
|
||||||
//If none, assumed GMT+0.
|
|
||||||
return ZonedDateTime.of(LocalDateTime.from(result),
|
|
||||||
ZoneId.of("GMT")).toEpochSecond();
|
|
||||||
} else {
|
|
||||||
return Instant.from(result).getEpochSecond();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverses the order of the date time components.
|
|
||||||
*
|
|
||||||
* Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990
|
|
||||||
*
|
|
||||||
* @param dateTime
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private String reverseOrderOfDateTimeComponents(String dateTime) {
|
|
||||||
StringBuilder reversedDateTime = new StringBuilder(dateTime.length());
|
|
||||||
String[] dateTimeComponents = dateTime.split(" ");
|
|
||||||
for (String component : dateTimeComponents) {
|
|
||||||
reversedDateTime.insert(0, " ").insert(0, component);
|
|
||||||
}
|
|
||||||
return reversedDateTime.toString().trim();
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -22,7 +22,9 @@ import java.io.IOException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,7 +45,7 @@ final class XRYReportProcessor {
|
|||||||
* @throws IOException If an I/O exception occurs.
|
* @throws IOException If an I/O exception occurs.
|
||||||
* @throws TskCoreException If an error occurs adding artifacts.
|
* @throws TskCoreException If an error occurs adding artifacts.
|
||||||
*/
|
*/
|
||||||
static void process(XRYFolder folder, Content parent) throws IOException, TskCoreException {
|
static void process(XRYFolder folder, Content parent, SleuthkitCase currentCase) throws IOException, TskCoreException, BlackboardException {
|
||||||
//Get all XRY file readers from this folder.
|
//Get all XRY file readers from this folder.
|
||||||
List<XRYFileReader> xryFileReaders = folder.getXRYFileReaders();
|
List<XRYFileReader> xryFileReaders = folder.getXRYFileReaders();
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ final class XRYReportProcessor {
|
|||||||
String reportType = xryFileReader.getReportType();
|
String reportType = xryFileReader.getReportType();
|
||||||
if (XRYFileParserFactory.supports(reportType)) {
|
if (XRYFileParserFactory.supports(reportType)) {
|
||||||
XRYFileParser parser = XRYFileParserFactory.get(reportType);
|
XRYFileParser parser = XRYFileParserFactory.get(reportType);
|
||||||
parser.parse(xryFileReader, parent);
|
parser.parse(xryFileReader, parent, currentCase);
|
||||||
} else {
|
} else {
|
||||||
logger.log(Level.WARNING, String.format("[XRY DSP] XRY File (in brackets) "
|
logger.log(Level.WARNING, String.format("[XRY DSP] XRY File (in brackets) "
|
||||||
+ "[ %s ] was found, but no parser to support its report type exists. "
|
+ "[ %s ] was found, but no parser to support its report type exists. "
|
||||||
|
151
Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYUtils.java
Executable file
151
Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYUtils.java
Executable file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.TemporalAccessor;
|
||||||
|
import java.time.temporal.TemporalQueries;
|
||||||
|
import org.sleuthkit.datamodel.CommunicationsUtils;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common utility methods shared among all XRY parser implementations.
|
||||||
|
*/
|
||||||
|
final class XRYUtils {
|
||||||
|
|
||||||
|
// Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch()
|
||||||
|
// function for more details.
|
||||||
|
private static final DateTimeFormatter DATE_TIME_PARSER
|
||||||
|
= DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y");
|
||||||
|
|
||||||
|
private static final String DEVICE_LOCALE = "(device)";
|
||||||
|
private static final String NETWORK_LOCALE = "(network)";
|
||||||
|
|
||||||
|
public static boolean isPhoneValid(String phoneNumber) {
|
||||||
|
try {
|
||||||
|
CommunicationsUtils.normalizePhoneNum(phoneNumber);
|
||||||
|
return true;
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEmailValid(String email) {
|
||||||
|
try {
|
||||||
|
CommunicationsUtils.normalizeEmailAddress(email);
|
||||||
|
return true;
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the date time value and calculates seconds since epoch.
|
||||||
|
*
|
||||||
|
* @param dateTime
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static long calculateSecondsSinceEpoch(String dateTime) {
|
||||||
|
String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim();
|
||||||
|
/**
|
||||||
|
* The format of time in XRY reports is of the form:
|
||||||
|
*
|
||||||
|
* 1/3/1990 1:23:54 AM UTC+4
|
||||||
|
*
|
||||||
|
* In our current version of Java (openjdk-1.8.0.222), there is a bug
|
||||||
|
* with having the timezone offset (UTC+4 or GMT-7, for example) at the
|
||||||
|
* end of the date time input. This is fixed in later versions of the
|
||||||
|
* JDK (9 and beyond). https://bugs.openjdk.java.net/browse/JDK-8154050
|
||||||
|
* Rather than update the JDK to accommodate this, the components of the
|
||||||
|
* date time string are reversed:
|
||||||
|
*
|
||||||
|
* UTC+4 AM 1:23:54 1/3/1990
|
||||||
|
*
|
||||||
|
* The java time package will correctly parse this date time format.
|
||||||
|
*/
|
||||||
|
String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale);
|
||||||
|
/**
|
||||||
|
* Furthermore, the DateTimeFormatter's timezone offset letter ('O')
|
||||||
|
* does not recognize UTC but recognizes GMT. According to
|
||||||
|
* https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only
|
||||||
|
* differs from UTC by at most 1 second and so substitution will only
|
||||||
|
* introduce a trivial amount of error.
|
||||||
|
*/
|
||||||
|
String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT");
|
||||||
|
TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT,
|
||||||
|
ZonedDateTime::from,
|
||||||
|
LocalDateTime::from,
|
||||||
|
OffsetDateTime::from);
|
||||||
|
//Query for the ZoneID
|
||||||
|
if (result.query(TemporalQueries.zoneId()) == null) {
|
||||||
|
//If none, assumed GMT+0.
|
||||||
|
return ZonedDateTime.of(LocalDateTime.from(result),
|
||||||
|
ZoneId.of("GMT")).toEpochSecond();
|
||||||
|
} else {
|
||||||
|
return Instant.from(result).getEpochSecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverses the order of the date time components.
|
||||||
|
*
|
||||||
|
* Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990
|
||||||
|
*
|
||||||
|
* @param dateTime
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static String reverseOrderOfDateTimeComponents(String dateTime) {
|
||||||
|
StringBuilder reversedDateTime = new StringBuilder(dateTime.length());
|
||||||
|
String[] dateTimeComponents = dateTime.split(" ");
|
||||||
|
for (String component : dateTimeComponents) {
|
||||||
|
reversedDateTime.insert(0, " ").insert(0, component);
|
||||||
|
}
|
||||||
|
return reversedDateTime.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the locale from the date time value.
|
||||||
|
*
|
||||||
|
* Locale in this case being (Device) or (Network).
|
||||||
|
*
|
||||||
|
* @param dateTime XRY datetime value to be sanitized.
|
||||||
|
* @return A purer date time value.
|
||||||
|
*/
|
||||||
|
private static String removeDateTimeLocale(String dateTime) {
|
||||||
|
String result = dateTime;
|
||||||
|
int deviceIndex = result.toLowerCase().indexOf(DEVICE_LOCALE);
|
||||||
|
if (deviceIndex != -1) {
|
||||||
|
result = result.substring(0, deviceIndex);
|
||||||
|
}
|
||||||
|
int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE);
|
||||||
|
if (networkIndex != -1) {
|
||||||
|
result = result.substring(0, networkIndex);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XRYUtils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -23,9 +23,11 @@ import java.util.Map;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,7 +69,7 @@ final class XRYWebBookmarksFileParser extends AbstractSingleEntityParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException {
|
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, BlackboardException {
|
||||||
List<BlackboardAttribute> attributes = new ArrayList<>();
|
List<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
for(XRYKeyValuePair pair : keyValuePairs) {
|
for(XRYKeyValuePair pair : keyValuePairs) {
|
||||||
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2019 Basis Technology Corp.
|
* Copyright 2012-2020 Basis Technology Corp.
|
||||||
*
|
*
|
||||||
* Copyright 2012 42six Solutions.
|
* Copyright 2012 42six Solutions.
|
||||||
*
|
*
|
||||||
@ -46,7 +46,7 @@ import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
|||||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Blackboard;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
@ -55,6 +55,7 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chrome recent activity extraction
|
* Chrome recent activity extraction
|
||||||
@ -731,8 +732,13 @@ class Chrome extends Extract {
|
|||||||
|
|
||||||
// get form autofill artifacts
|
// get form autofill artifacts
|
||||||
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X));
|
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X));
|
||||||
|
try {
|
||||||
// get form address atifacts
|
// get form address atifacts
|
||||||
bbartifacts.addAll(getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X));
|
getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X);
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException | Blackboard.BlackboardException ex) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error adding artifacts to the case database "
|
||||||
|
+ "for chrome file %s [objId=%d]", webDataFile.getName(), webDataFile.getId()), ex);
|
||||||
|
}
|
||||||
|
|
||||||
dbFile.delete();
|
dbFile.delete();
|
||||||
}
|
}
|
||||||
@ -808,17 +814,23 @@ class Chrome extends Extract {
|
|||||||
*
|
*
|
||||||
* @return collection of TSK_WEB_FORM_ADDRESS artifacts
|
* @return collection of TSK_WEB_FORM_ADDRESS artifacts
|
||||||
*/
|
*/
|
||||||
private Collection<BlackboardArtifact> getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) {
|
private void getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) throws NoCurrentCaseException,
|
||||||
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
TskCoreException, Blackboard.BlackboardException {
|
||||||
|
|
||||||
String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X
|
String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X
|
||||||
: WEBFORM_ADDRESS_QUERY;
|
: WEBFORM_ADDRESS_QUERY;
|
||||||
|
|
||||||
|
// Helper to create web form address artifacts.
|
||||||
|
WebBrowserArtifactsHelper helper = new WebBrowserArtifactsHelper(
|
||||||
|
Case.getCurrentCaseThrows().getSleuthkitCase(),
|
||||||
|
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||||
|
webDataFile
|
||||||
|
);
|
||||||
|
|
||||||
// Get Web form addresses
|
// Get Web form addresses
|
||||||
List<HashMap<String, Object>> addresses = this.dbConnect(dbFilePath, webformAddressQuery);
|
List<HashMap<String, Object>> addresses = this.dbConnect(dbFilePath, webformAddressQuery);
|
||||||
logger.log(Level.INFO, "{0}- Now getting Web form addresses from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, addresses.size()}); //NON-NLS
|
logger.log(Level.INFO, "{0}- Now getting Web form addresses from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, addresses.size()}); //NON-NLS
|
||||||
for (HashMap<String, Object> result : addresses) {
|
for (HashMap<String, Object> result : addresses) {
|
||||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
|
||||||
|
|
||||||
// get name fields
|
// get name fields
|
||||||
String first_name = result.get("first_name").toString() != null ? result.get("first_name").toString() : "";
|
String first_name = result.get("first_name").toString() != null ? result.get("first_name").toString() : "";
|
||||||
@ -854,72 +866,25 @@ class Chrome extends Extract {
|
|||||||
street_address = String.join(" ", address_line_1, address_line_2);
|
street_address = String.join(" ", address_line_1, address_line_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an email address is found, create an account instance for it
|
|
||||||
if (email_Addr != null && !email_Addr.isEmpty()) {
|
|
||||||
try {
|
|
||||||
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email_Addr, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), webDataFile);
|
|
||||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Error creating email account instance for '%s' from Chrome WebData file '%s' .",
|
|
||||||
email_Addr, webDataFile.getName()), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If a phone number is found, create an account instance for it
|
|
||||||
if (phone_number != null && !phone_number.isEmpty()) {
|
|
||||||
try {
|
|
||||||
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, phone_number, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), webDataFile);
|
|
||||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Error creating phone account instance for '%s' from Chrome WebData file '%s' .",
|
|
||||||
phone_number, webDataFile.getName()), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create atrributes from extracted fields
|
// Create atrributes from extracted fields
|
||||||
if (full_name == null || full_name.isEmpty()) {
|
if (full_name == null || full_name.isEmpty()) {
|
||||||
full_name = String.join(" ", first_name, middle_name, last_name);
|
full_name = String.join(" ", first_name, middle_name, last_name);
|
||||||
}
|
}
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
full_name)); //NON-NLS
|
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
email_Addr)); //NON-NLS
|
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
phone_number)); //NON-NLS
|
|
||||||
|
|
||||||
String locationAddress = String.join(", ", street_address, city, state, zipcode, country_code);
|
String locationAddress = String.join(", ", street_address, city, state, zipcode, country_code);
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
locationAddress)); //NON-NLS
|
|
||||||
|
|
||||||
|
List<BlackboardAttribute> otherAttributes = new ArrayList<>();
|
||||||
if (date_modified > 0) {
|
if (date_modified > 0) {
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
|
otherAttributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
date_modified)); //NON-NLS
|
date_modified)); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_count > 0 ){
|
helper.addWebFormAddress(
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
|
full_name, email_Addr, phone_number,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
locationAddress, 0, use_date,
|
||||||
use_count)); //NON-NLS
|
use_count, otherAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_date > 0) {
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
use_date)); //NON-NLS
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create artifact
|
|
||||||
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS, webDataFile, bbattributes);
|
|
||||||
if (bbart != null) {
|
|
||||||
bbartifacts.add(bbart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// return all extracted artifacts
|
|
||||||
return bbartifacts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isChromePreVersion30(String temps) {
|
private boolean isChromePreVersion30(String temps) {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2019 Basis Technology Corp.
|
* Copyright 2012-2020 Basis Technology Corp.
|
||||||
*
|
*
|
||||||
* Copyright 2012 42six Solutions.
|
* Copyright 2012 42six Solutions.
|
||||||
* Contact: aebadirad <at> 42six <dot> com
|
* Contact: aebadirad <at> 42six <dot> com
|
||||||
@ -69,11 +69,14 @@ import java.util.HashSet;
|
|||||||
import static java.util.Locale.US;
|
import static java.util.Locale.US;
|
||||||
import static java.util.TimeZone.getTimeZone;
|
import static java.util.TimeZone.getTimeZone;
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
||||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
|
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
|
||||||
import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag;
|
import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
import org.sleuthkit.datamodel.Account;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
|
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
@ -944,7 +947,7 @@ class ExtractRegistry extends Extract {
|
|||||||
Map<String, String> userInfo = userInfoMap.remove(userID);
|
Map<String, String> userInfo = userInfoMap.remove(userID);
|
||||||
//if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it
|
//if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it
|
||||||
if (userInfo != null) {
|
if (userInfo != null) {
|
||||||
osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true));
|
osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true, regAbstractFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -953,7 +956,7 @@ class ExtractRegistry extends Extract {
|
|||||||
//add remaining userinfos as accounts;
|
//add remaining userinfos as accounts;
|
||||||
for (Map<String, String> userInfo : userInfoMap.values()) {
|
for (Map<String, String> userInfo : userInfoMap.values()) {
|
||||||
BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||||
bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false));
|
bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false, regAbstractFile));
|
||||||
// index the artifact for keyword search
|
// index the artifact for keyword search
|
||||||
newArtifacts.add(bbart);
|
newArtifacts.add(bbart);
|
||||||
}
|
}
|
||||||
@ -983,7 +986,7 @@ class ExtractRegistry extends Extract {
|
|||||||
*
|
*
|
||||||
* @throws ParseException
|
* @throws ParseException
|
||||||
*/
|
*/
|
||||||
Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser) throws ParseException {
|
Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException {
|
||||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||||
|
|
||||||
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'");
|
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'");
|
||||||
@ -1035,6 +1038,20 @@ class ExtractRegistry extends Extract {
|
|||||||
|
|
||||||
value = userInfo.get(INTERNET_NAME_KEY);
|
value = userInfo.get(INTERNET_NAME_KEY);
|
||||||
if (value != null && !value.isEmpty()) {
|
if (value != null && !value.isEmpty()) {
|
||||||
|
try {
|
||||||
|
// Create an account for this email, if it doesn't already exist.
|
||||||
|
Case.getCurrentCaseThrows()
|
||||||
|
.getSleuthkitCase()
|
||||||
|
.getCommunicationsManager()
|
||||||
|
.createAccountFileInstance(Account.Type.EMAIL,
|
||||||
|
value, getRAModuleName(), regAbstractFile);
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE,
|
||||||
|
String.format("Error adding email account with value "
|
||||||
|
+ "%s, to the case database for file %s [objId=%d]",
|
||||||
|
value, regAbstractFile.getName(), regAbstractFile.getId()), ex);
|
||||||
|
}
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
|
||||||
getRAModuleName(), value));
|
getRAModuleName(), value));
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2019 Basis Technology Corp.
|
* Copyright 2012-2020 Basis Technology Corp.
|
||||||
*
|
*
|
||||||
* Copyright 2012 42six Solutions.
|
* Copyright 2012 42six Solutions.
|
||||||
* Contact: aebadirad <at> 42six <dot> com
|
* Contact: aebadirad <at> 42six <dot> com
|
||||||
@ -54,7 +54,7 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
|||||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Blackboard;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
@ -62,6 +62,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
|||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
|
||||||
|
|
||||||
@Messages({
|
@Messages({
|
||||||
"Progress_Message_Firefox_History=Firefox History",
|
"Progress_Message_Firefox_History=Firefox History",
|
||||||
@ -858,7 +859,6 @@ class Firefox extends Extract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dataFound = true;
|
dataFound = true;
|
||||||
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
|
||||||
while (j < autofillProfilesFiles.size()) {
|
while (j < autofillProfilesFiles.size()) {
|
||||||
@ -916,6 +916,19 @@ class Firefox extends Extract {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebBrowserArtifactsHelper helper;
|
||||||
|
try {
|
||||||
|
// Helper to create web form address artifacts.
|
||||||
|
helper = new WebBrowserArtifactsHelper(
|
||||||
|
Case.getCurrentCaseThrows().getSleuthkitCase(),
|
||||||
|
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
|
||||||
|
profileFile
|
||||||
|
);
|
||||||
|
} catch (NoCurrentCaseException ex) {
|
||||||
|
logger.log(Level.SEVERE, "No case open, bailing.", ex); //NON-NLS
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (JsonElement result : jAddressesArray) {
|
for (JsonElement result : jAddressesArray) {
|
||||||
JsonObject address = result.getAsJsonObject();
|
JsonObject address = result.getAsJsonObject();
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
@ -959,63 +972,9 @@ class Firefox extends Extract {
|
|||||||
String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
|
String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
helper.addWebFormAddress(name, email, phoneNumber,
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON,
|
mailingAddress, datetimeCreated, datetimeLastUsed, timesUsed);
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
} catch (TskCoreException | Blackboard.BlackboardException ex) {
|
||||||
name)); //NON-NLS
|
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
email)); //NON-NLS
|
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
phoneNumber)); //NON-NLS
|
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
mailingAddress)); //NON-NLS
|
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
datetimeCreated)); //NON-NLS
|
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
datetimeLastUsed)); //NON-NLS
|
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
|
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
|
||||||
timesUsed)); //NON-NLS
|
|
||||||
|
|
||||||
BlackboardArtifact bbart = profileFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS);
|
|
||||||
|
|
||||||
if (bbart != null) {
|
|
||||||
bbart.addAttributes(bbattributes);
|
|
||||||
bbartifacts.add(bbart);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an email address is found, create an account instance for it
|
|
||||||
if (email != null && !email.isEmpty()) {
|
|
||||||
try {
|
|
||||||
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email, NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), profileFile);
|
|
||||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Error creating email account instance for '%s' from Firefox profiles file '%s' .",
|
|
||||||
email, profileFile.getName()), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a phone number is found, create an account instance for it
|
|
||||||
if (phoneNumber != null && !phoneNumber.isEmpty()) {
|
|
||||||
try {
|
|
||||||
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, phoneNumber, NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), profileFile);
|
|
||||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Error creating phone number account instance for '%s' from Chrome profiles file '%s' .",
|
|
||||||
phoneNumber, profileFile.getName()), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error while trying to insert Firefox Autofill profile artifact{0}", ex); //NON-NLS
|
logger.log(Level.SEVERE, "Error while trying to insert Firefox Autofill profile artifact{0}", ex); //NON-NLS
|
||||||
this.addErrorMessage(
|
this.addErrorMessage(
|
||||||
NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
|
NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
|
||||||
@ -1024,8 +983,6 @@ class Firefox extends Extract {
|
|||||||
}
|
}
|
||||||
dbFile.delete();
|
dbFile.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
postArtifacts(bbartifacts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user