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
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -38,7 +40,7 @@ abstract class AbstractSingleEntityParser implements XRYFileParser {
|
||||
protected static final String PARSER_NAME = "XRY DSP";
|
||||
|
||||
@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();
|
||||
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()) {
|
||||
makeArtifact(keyValuePairs, parent);
|
||||
makeArtifact(keyValuePairs, parent, currentCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,9 +124,9 @@ abstract class AbstractSingleEntityParser implements XRYFileParser {
|
||||
*/
|
||||
abstract boolean isNamespace(String nameSpace);
|
||||
|
||||
/**
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,24 +18,22 @@
|
||||
*/
|
||||
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.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalQueries;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
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.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
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.
|
||||
@ -44,27 +42,19 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
|
||||
|
||||
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
|
||||
* blackboard attribute types, if any.
|
||||
*/
|
||||
private enum XryKey {
|
||||
NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
|
||||
TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME),
|
||||
DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION),
|
||||
TIME("time", null),
|
||||
DIRECTION("direction", null),
|
||||
CALL_TYPE("call type", null),
|
||||
NUMBER("number", null),
|
||||
TEL("tel", null),
|
||||
TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO),
|
||||
FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM),
|
||||
TO("to", null),
|
||||
FROM("from", null),
|
||||
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
|
||||
DURATION("duration", null),
|
||||
STORAGE("storage", null),
|
||||
@ -175,159 +165,157 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException {
|
||||
List<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
for(XRYKeyValuePair pair : keyValuePairs) {
|
||||
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
||||
if(attribute.isPresent()) {
|
||||
attributes.add(attribute.get());
|
||||
}
|
||||
}
|
||||
if(!attributes.isEmpty()) {
|
||||
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG);
|
||||
artifact.addAttributes(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
XryNamespace xryNamespace = XryNamespace.NONE;
|
||||
if (XryNamespace.contains(pair.getNamespace())) {
|
||||
xryNamespace = XryNamespace.fromDisplayName(pair.getNamespace());
|
||||
}
|
||||
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, BlackboardException {
|
||||
// Transform all the data from XRY land into the appropriate CommHelper
|
||||
// data types.
|
||||
String callerId = null;
|
||||
final Collection<String> calleeList = new ArrayList<>();
|
||||
CommunicationDirection direction = CommunicationDirection.UNKNOWN;
|
||||
long startTime = 0L;
|
||||
final long endTime = 0L;
|
||||
final CallMediaType callType = CallMediaType.UNKNOWN;
|
||||
final Collection<BlackboardAttribute> otherAttributes = new ArrayList<>();
|
||||
|
||||
switch (xryKey) {
|
||||
case TEL:
|
||||
case NUMBER:
|
||||
//Apply the namespace
|
||||
switch (xryNamespace) {
|
||||
case FROM:
|
||||
return Optional.of(new BlackboardAttribute(
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM,
|
||||
PARSER_NAME, pair.getValue()));
|
||||
case TO:
|
||||
return Optional.of(new BlackboardAttribute(
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO,
|
||||
PARSER_NAME, pair.getValue()));
|
||||
default:
|
||||
return Optional.of(new BlackboardAttribute(
|
||||
for (XRYKeyValuePair pair : keyValuePairs) {
|
||||
XryKey xryKey = XryKey.fromDisplayName(pair.getKey());
|
||||
XryNamespace xryNamespace = XryNamespace.NONE;
|
||||
if (XryNamespace.contains(pair.getNamespace())) {
|
||||
xryNamespace = XryNamespace.fromDisplayName(pair.getNamespace());
|
||||
}
|
||||
|
||||
switch (xryKey) {
|
||||
case TEL:
|
||||
case NUMBER:
|
||||
if(!XRYUtils.isPhoneValid(pair.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply namespace or direction
|
||||
if (xryNamespace == XryNamespace.FROM || direction == CommunicationDirection.INCOMING) {
|
||||
callerId = pair.getValue();
|
||||
} else if (xryNamespace == XryNamespace.TO || direction == CommunicationDirection.OUTGOING) {
|
||||
calleeList.add(pair.getValue());
|
||||
} else {
|
||||
otherAttributes.add(new BlackboardAttribute(
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
||||
PARSER_NAME, pair.getValue()));
|
||||
}
|
||||
case TIME:
|
||||
try {
|
||||
//Tranform value to seconds since epoch
|
||||
long dateTimeSinceEpoch = calculateSecondsSinceEpoch(pair.getValue());
|
||||
return Optional.of(new BlackboardAttribute(xryKey.getType(),
|
||||
PARSER_NAME, dateTimeSinceEpoch));
|
||||
} catch (DateTimeParseException ex) {
|
||||
logger.log(Level.WARNING, String.format("[XRY DSP] Assumption"
|
||||
+ " about the date time formatting of call logs is "
|
||||
+ "not right. Here is the value [ %s ]", pair.getValue()), ex);
|
||||
return Optional.empty();
|
||||
}
|
||||
default:
|
||||
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
|
||||
//type.
|
||||
if (xryKey.getType() != null) {
|
||||
return Optional.of(new BlackboardAttribute(xryKey.getType(),
|
||||
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:
|
||||
try {
|
||||
//Tranform value to seconds since epoch
|
||||
long dateTimeSinceEpoch = XRYUtils.calculateSecondsSinceEpoch(pair.getValue());
|
||||
startTime = dateTimeSinceEpoch;
|
||||
} catch (DateTimeParseException ex) {
|
||||
logger.log(Level.WARNING, String.format("[XRY DSP] Assumption"
|
||||
+ " about the date time formatting of call logs is "
|
||||
+ "not right. Here is the value [ %s ]", pair.getValue()), ex);
|
||||
}
|
||||
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:
|
||||
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
|
||||
//type.
|
||||
if (xryKey.getType() != null) {
|
||||
otherAttributes.add(new BlackboardAttribute(xryKey.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();
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
// Make sure we have the required fields, otherwise the CommHelper will
|
||||
// complain about illegal arguments.
|
||||
|
||||
// These are all the invalid combinations.
|
||||
if (callerId == null && calleeList.isEmpty()
|
||||
|| direction == CommunicationDirection.INCOMING && callerId == null
|
||||
|| direction == CommunicationDirection.OUTGOING && calleeList.isEmpty()) {
|
||||
|
||||
/**
|
||||
* 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();
|
||||
// If the combo is invalid, just make an artifact with what we've got.
|
||||
if (direction != CommunicationDirection.UNKNOWN) {
|
||||
otherAttributes.add(new BlackboardAttribute(
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION,
|
||||
PARSER_NAME, direction.getDisplayName()));
|
||||
}
|
||||
|
||||
if (startTime > 0L) {
|
||||
otherAttributes.add(new BlackboardAttribute(
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START,
|
||||
PARSER_NAME, startTime));
|
||||
}
|
||||
|
||||
// If the DIRECTION check failed, just manually create accounts
|
||||
// for these phones. Note, there is no need to create relationships.
|
||||
// If both callerId and calleeList were non-null/non-empty, then
|
||||
// it would have been a valid combination.
|
||||
if (callerId != null) {
|
||||
currentCase.getCommunicationsManager().createAccountFileInstance(
|
||||
Account.Type.PHONE, callerId, PARSER_NAME, parent);
|
||||
|
||||
otherAttributes.add(new BlackboardAttribute(
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
||||
PARSER_NAME, callerId));
|
||||
}
|
||||
|
||||
for (String phone : calleeList) {
|
||||
currentCase.getCommunicationsManager().createAccountFileInstance(
|
||||
Account.Type.PHONE, phone, PARSER_NAME, parent);
|
||||
|
||||
otherAttributes.add(new BlackboardAttribute(
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
|
||||
PARSER_NAME, phone));
|
||||
}
|
||||
|
||||
if (!otherAttributes.isEmpty()) {
|
||||
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG);
|
||||
artifact.addAttributes(otherAttributes);
|
||||
|
||||
currentCase.getBlackboard().postArtifact(artifact, PARSER_NAME);
|
||||
}
|
||||
} 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);
|
||||
// Otherwise we can safely use the helper.
|
||||
CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
|
||||
currentCase, PARSER_NAME, parent, Account.Type.PHONE);
|
||||
|
||||
helper.addCalllog(direction, callerId, calleeList, startTime,
|
||||
endTime, callType, otherAttributes);
|
||||
}
|
||||
return reversedDateTime.toString().trim();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -19,49 +19,30 @@
|
||||
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
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.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
|
||||
|
||||
/**
|
||||
* Parses XRY Contacts-Contacts files and creates artifacts.
|
||||
*/
|
||||
final class XRYContactsFileParser extends AbstractSingleEntityParser {
|
||||
|
||||
|
||||
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
|
||||
boolean canProcess(XRYKeyValuePair pair) {
|
||||
String normalizedKey = pair.getKey().toLowerCase();
|
||||
return XRY_KEYS.containsKey(normalizedKey);
|
||||
return XryKey.contains(pair.getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,36 +51,168 @@ final class XRYContactsFileParser extends AbstractSingleEntityParser {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the appropriate blackboard attribute given a single XRY Key Value
|
||||
* pair.
|
||||
*/
|
||||
private Optional<BlackboardAttribute> getBlackboardAttribute(XRYKeyValuePair pair) {
|
||||
String normalizedKey = pair.getKey().toLowerCase();
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE attrType = XRY_KEYS.get(normalizedKey);
|
||||
if(attrType != null) {
|
||||
return Optional.of(new BlackboardAttribute(attrType, PARSER_NAME, pair.getValue()));
|
||||
}
|
||||
|
||||
logger.log(Level.INFO, String.format("[XRY DSP] Key value pair "
|
||||
+ "(in brackets) [ %s ] was recognized but we need "
|
||||
+ "more data or time to finish implementation. Discarding... ",
|
||||
pair));
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException {
|
||||
List<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
for(XRYKeyValuePair pair : keyValuePairs) {
|
||||
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
||||
if(attribute.isPresent()) {
|
||||
attributes.add(attribute.get());
|
||||
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, Blackboard.BlackboardException {
|
||||
// Transform all the data from XRY land into the appropriate CommHelper
|
||||
// data types.
|
||||
String contactName = null;
|
||||
String phoneNumber = null;
|
||||
String homePhoneNumber = null;
|
||||
String mobilePhoneNumber = null;
|
||||
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 "
|
||||
+ "(in brackets) [ %s ] was recognized but "
|
||||
+ "more data or time is needed to finish implementation. Discarding... ",
|
||||
pair));
|
||||
}
|
||||
}
|
||||
if(!attributes.isEmpty()) {
|
||||
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||
artifact.addAttributes(attributes);
|
||||
|
||||
// Make sure we have the required fields, otherwise the CommHelper will
|
||||
// complain about illegal arguments.
|
||||
if (phoneNumber != null || homePhoneNumber != null || mobilePhoneNumber != null || hasAnEmail) {
|
||||
CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
|
||||
currentCase, PARSER_NAME, parent, Account.Type.DEVICE);
|
||||
|
||||
helper.addContact(contactName, phoneNumber, homePhoneNumber,
|
||||
mobilePhoneNumber, emailAddr, additionalAttributes);
|
||||
} else {
|
||||
// Just create an artifact with the attributes that we do have.
|
||||
if (!additionalAttributes.isEmpty()) {
|
||||
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||
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
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.AutoIngestDataSourceProcessorException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
@ -204,12 +205,11 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
|
||||
try {
|
||||
XRYFolder xryFolder = new XRYFolder(selectedPath);
|
||||
FileManager fileManager = Case.getCurrentCaseThrows()
|
||||
.getServices().getFileManager();
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
String uniqueUUID = UUID.randomUUID().toString();
|
||||
//Move heavy lifting to a background task.
|
||||
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
|
||||
callback, fileManager, uniqueUUID);
|
||||
callback, currentCase, uniqueUUID);
|
||||
swingWorker.execute();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
|
||||
@ -238,11 +238,10 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
|
||||
try {
|
||||
XRYFolder xryFolder = new XRYFolder(dataSourcePath);
|
||||
FileManager fileManager = Case.getCurrentCaseThrows()
|
||||
.getServices().getFileManager();
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
//Move heavy lifting to a background task.
|
||||
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
|
||||
callBack, fileManager, deviceId);
|
||||
callBack, currentCase, deviceId);
|
||||
swingWorker.execute();
|
||||
} catch (NoCurrentCaseException 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 DataSourceProcessorCallback callback;
|
||||
private final FileManager fileManager;
|
||||
private final Case currentCase;
|
||||
private final XRYFolder xryFolder;
|
||||
private final String uniqueUUID;
|
||||
|
||||
public XRYReportProcessorSwingWorker(XRYFolder folder,
|
||||
DataSourceProcessorProgressMonitor progressMonitor,
|
||||
DataSourceProcessorCallback callback,
|
||||
FileManager fileManager,
|
||||
String uniqueUUID) {
|
||||
Case currentCase, String uniqueUUID) {
|
||||
|
||||
this.xryFolder = folder;
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.callback = callback;
|
||||
this.fileManager = fileManager;
|
||||
this.currentCase = currentCase;
|
||||
this.uniqueUUID = uniqueUUID;
|
||||
}
|
||||
|
||||
@ -296,7 +294,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
"XRYDataSourceProcessor.processingFiles=Processing all XRY files..."
|
||||
})
|
||||
protected LocalFilesDataSource doInBackground() throws TskCoreException,
|
||||
TskDataException, IOException {
|
||||
TskDataException, IOException, BlackboardException {
|
||||
progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_preppingFiles());
|
||||
|
||||
List<Path> nonXRYFiles = xryFolder.getNonXRYFiles();
|
||||
@ -304,7 +302,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
//Map paths to string representations.
|
||||
.map(Path::toString)
|
||||
.collect(Collectors.toList());
|
||||
LocalFilesDataSource dataSource = fileManager.addLocalFilesDataSource(
|
||||
LocalFilesDataSource dataSource = currentCase.getServices().getFileManager().addLocalFilesDataSource(
|
||||
uniqueUUID,
|
||||
"XRY Text Export", //Name
|
||||
"", //Timezone
|
||||
@ -313,7 +311,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
|
||||
//Process the report files.
|
||||
progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_processingFiles());
|
||||
XRYReportProcessor.process(xryFolder, dataSource);
|
||||
XRYReportProcessor.process(xryFolder, dataSource, currentCase.getSleuthkitCase());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@ -360,4 +358,4 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -89,7 +91,7 @@ final class XRYDeviceGenInfoFileParser extends AbstractSingleEntityParser {
|
||||
}
|
||||
|
||||
@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<>();
|
||||
for(int i = 0; i < keyValuePairs.size(); i+=2) {
|
||||
Optional<BlackboardAttribute> attribute;
|
||||
@ -168,4 +170,4 @@ final class XRYDeviceGenInfoFileParser extends AbstractSingleEntityParser {
|
||||
|
||||
return Optional.of(new BlackboardAttribute(attrType, PARSER_NAME, dataValue));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -19,7 +19,9 @@
|
||||
package org.sleuthkit.autopsy.datasourceprocessors.xry;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -40,7 +42,7 @@ interface XRYFileParser {
|
||||
* @throws IOException If an I/O error occurs during reading.
|
||||
* @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
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.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.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalQueries;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -37,10 +30,15 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
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.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
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.
|
||||
@ -52,31 +50,20 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
||||
|
||||
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
|
||||
* blackboard attribute types, if any.
|
||||
*/
|
||||
private enum XryKey {
|
||||
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
|
||||
DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION),
|
||||
MESSAGE("message", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT),
|
||||
DIRECTION("direction", null),
|
||||
MESSAGE("message", null),
|
||||
NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON),
|
||||
TEXT("text", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT),
|
||||
TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME),
|
||||
SERVICE_CENTER("service center", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER),
|
||||
FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM),
|
||||
TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO),
|
||||
TEXT("text", null),
|
||||
TIME("time", null),
|
||||
SERVICE_CENTER("service center", null),
|
||||
FROM("from", null),
|
||||
TO("to", null),
|
||||
//The following keys either need special processing or more time and data to find a type.
|
||||
STORAGE("storage", null),
|
||||
NUMBER("number", null),
|
||||
@ -272,7 +259,7 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
||||
* encountered.
|
||||
*/
|
||||
@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();
|
||||
logger.log(Level.INFO, String.format("[XRY DSP] Processing report at"
|
||||
+ " [ %s ]", reportPath.toString()));
|
||||
@ -282,26 +269,178 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
||||
|
||||
while (reader.hasNextEntity()) {
|
||||
String xryEntity = reader.nextEntity();
|
||||
List<BlackboardAttribute> attributes = getBlackboardAttributes(xryEntity, reader, referenceNumbersSeen);
|
||||
//Only create artifacts with non-empty attributes.
|
||||
if (!attributes.isEmpty()) {
|
||||
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE);
|
||||
artifact.addAttributes(attributes);
|
||||
|
||||
// This call will combine all segmented text into a single key value pair
|
||||
List<XRYKeyValuePair> pairs = getXRYKeyValuePairs(xryEntity, reader, referenceNumbersSeen);
|
||||
|
||||
// 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
|
||||
* unify any segmented text, if need be.
|
||||
* Extracts all pairs from the XRY Entity. This function
|
||||
* 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 {
|
||||
String[] xryLines = xryEntity.split("\n");
|
||||
//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]));
|
||||
|
||||
List<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
List<XRYKeyValuePair> pairs = new ArrayList<>();
|
||||
|
||||
//Count the key value pairs in the XRY entity.
|
||||
int keyCount = getCountOfKeyValuePairs(xryLines);
|
||||
@ -339,14 +478,10 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
||||
pair.getNamespace());
|
||||
}
|
||||
|
||||
//Get the corresponding blackboard attribute, if any.
|
||||
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
||||
if (attribute.isPresent()) {
|
||||
attributes.add(attribute.get());
|
||||
}
|
||||
pairs.add(pair);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
return pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -364,7 +499,7 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds up segmented message entities so that the text is unified for a
|
||||
* Builds up segmented message entities so that the text is unified for a
|
||||
* single artifact.
|
||||
*
|
||||
* @param reader File reader that is producing XRY entities.
|
||||
@ -461,7 +596,7 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
||||
|
||||
/**
|
||||
* Extracts the value of the XRY meta key, if any.
|
||||
*
|
||||
*
|
||||
* @param xryLines XRY entity to extract from.
|
||||
* @param metaKey The key type to extract.
|
||||
* @return
|
||||
@ -486,10 +621,10 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the ith XRY Key Value pair in the XRY Entity.
|
||||
*
|
||||
* Extracts the ith XRY Key Value pair in the XRY Entity.
|
||||
*
|
||||
* The total number of pairs can be determined via getCountOfKeyValuePairs().
|
||||
*
|
||||
*
|
||||
* @param xryLines XRY entity.
|
||||
* @param index The requested Key Value pair.
|
||||
* @return
|
||||
@ -531,191 +666,4 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
||||
|
||||
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
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -43,7 +45,7 @@ final class XRYReportProcessor {
|
||||
* @throws IOException If an I/O exception occurs.
|
||||
* @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.
|
||||
List<XRYFileReader> xryFileReaders = folder.getXRYFileReaders();
|
||||
|
||||
@ -52,7 +54,7 @@ final class XRYReportProcessor {
|
||||
String reportType = xryFileReader.getReportType();
|
||||
if (XRYFileParserFactory.supports(reportType)) {
|
||||
XRYFileParser parser = XRYFileParserFactory.get(reportType);
|
||||
parser.parse(xryFileReader, parent);
|
||||
parser.parse(xryFileReader, parent, currentCase);
|
||||
} else {
|
||||
logger.log(Level.WARNING, String.format("[XRY DSP] XRY File (in brackets) "
|
||||
+ "[ %s ] was found, but no parser to support its report type exists. "
|
||||
@ -76,4 +78,4 @@ final class XRYReportProcessor {
|
||||
private XRYReportProcessor() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
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
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.List;
|
||||
import java.util.Optional;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -67,7 +69,7 @@ final class XRYWebBookmarksFileParser extends AbstractSingleEntityParser {
|
||||
}
|
||||
|
||||
@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<>();
|
||||
for(XRYKeyValuePair pair : keyValuePairs) {
|
||||
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
|
||||
@ -80,4 +82,4 @@ final class XRYWebBookmarksFileParser extends AbstractSingleEntityParser {
|
||||
artifact.addAttributes(attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-2019 Basis Technology Corp.
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
*
|
||||
* 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.IngestJobContext;
|
||||
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.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
@ -55,6 +55,7 @@ import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
|
||||
|
||||
/**
|
||||
* Chrome recent activity extraction
|
||||
@ -731,8 +732,13 @@ class Chrome extends Extract {
|
||||
|
||||
// get form autofill artifacts
|
||||
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X));
|
||||
// get form address atifacts
|
||||
bbartifacts.addAll(getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X));
|
||||
try {
|
||||
// get form address atifacts
|
||||
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();
|
||||
}
|
||||
@ -808,17 +814,23 @@ class Chrome extends Extract {
|
||||
*
|
||||
* @return collection of TSK_WEB_FORM_ADDRESS artifacts
|
||||
*/
|
||||
private Collection<BlackboardArtifact> getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) {
|
||||
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
||||
private void getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) throws NoCurrentCaseException,
|
||||
TskCoreException, Blackboard.BlackboardException {
|
||||
|
||||
String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X
|
||||
: 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
|
||||
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
|
||||
for (HashMap<String, Object> result : addresses) {
|
||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
|
||||
// get name fields
|
||||
String first_name = result.get("first_name").toString() != null ? result.get("first_name").toString() : "";
|
||||
@ -853,73 +865,26 @@ class Chrome extends Extract {
|
||||
String address_line_2 = result.get("address_line_2").toString() != null ? result.get("address_line_2").toString() : "";
|
||||
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
|
||||
if (full_name == null || full_name.isEmpty()) {
|
||||
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);
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
|
||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||
locationAddress)); //NON-NLS
|
||||
|
||||
List<BlackboardAttribute> otherAttributes = new ArrayList<>();
|
||||
if (date_modified > 0) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
|
||||
otherAttributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
|
||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||
date_modified)); //NON-NLS
|
||||
}
|
||||
|
||||
if (use_count > 0 ){
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
|
||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||
use_count)); //NON-NLS
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
helper.addWebFormAddress(
|
||||
full_name, email_Addr, phone_number,
|
||||
locationAddress, 0, use_date,
|
||||
use_count, otherAttributes);
|
||||
}
|
||||
// return all extracted artifacts
|
||||
return bbartifacts;
|
||||
}
|
||||
|
||||
private boolean isChromePreVersion30(String temps) {
|
||||
@ -933,4 +898,4 @@ class Chrome extends Extract {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-2019 Basis Technology Corp.
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
*
|
||||
* Copyright 2012 42six Solutions.
|
||||
* Contact: aebadirad <at> 42six <dot> com
|
||||
@ -69,11 +69,14 @@ import java.util.HashSet;
|
||||
import static java.util.Locale.US;
|
||||
import static java.util.TimeZone.getTimeZone;
|
||||
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.IngestModule.IngestModuleException;
|
||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
|
||||
import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
@ -944,7 +947,7 @@ class ExtractRegistry extends Extract {
|
||||
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 (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;
|
||||
for (Map<String, String> userInfo : userInfoMap.values()) {
|
||||
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
|
||||
newArtifacts.add(bbart);
|
||||
}
|
||||
@ -983,7 +986,7 @@ class ExtractRegistry extends Extract {
|
||||
*
|
||||
* @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<>();
|
||||
|
||||
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);
|
||||
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,
|
||||
getRAModuleName(), value));
|
||||
}
|
||||
@ -1802,4 +1819,4 @@ class ExtractRegistry extends Extract {
|
||||
public String autopsyPlugins = "";
|
||||
public String fullPlugins = "";
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-2019 Basis Technology Corp.
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
*
|
||||
* Copyright 2012 42six Solutions.
|
||||
* 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.IngestJobContext;
|
||||
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.ARTIFACT_TYPE;
|
||||
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.ReadContentInputStream.ReadContentInputStreamException;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
|
||||
|
||||
@Messages({
|
||||
"Progress_Message_Firefox_History=Firefox History",
|
||||
@ -858,7 +859,6 @@ class Firefox extends Extract {
|
||||
}
|
||||
|
||||
dataFound = true;
|
||||
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
||||
int j = 0;
|
||||
|
||||
while (j < autofillProfilesFiles.size()) {
|
||||
@ -916,6 +916,19 @@ class Firefox extends Extract {
|
||||
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) {
|
||||
JsonObject address = result.getAsJsonObject();
|
||||
if (address == null) {
|
||||
@ -959,63 +972,9 @@ class Firefox extends Extract {
|
||||
String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
|
||||
|
||||
try {
|
||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON,
|
||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||
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) {
|
||||
helper.addWebFormAddress(name, email, phoneNumber,
|
||||
mailingAddress, datetimeCreated, datetimeLastUsed, timesUsed);
|
||||
} catch (TskCoreException | Blackboard.BlackboardException ex) {
|
||||
logger.log(Level.SEVERE, "Error while trying to insert Firefox Autofill profile artifact{0}", ex); //NON-NLS
|
||||
this.addErrorMessage(
|
||||
NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
|
||||
@ -1024,8 +983,6 @@ class Firefox extends Extract {
|
||||
}
|
||||
dbFile.delete();
|
||||
}
|
||||
|
||||
postArtifacts(bbartifacts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1118,4 +1075,4 @@ class Firefox extends Extract {
|
||||
return updatedAddress;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user