Merge pull request #5712 from dannysmyda/6081-Use-Helper-For-Artifact-Creation

6081 use helper for artifact creation
This commit is contained in:
Richard Cordovano 2020-04-01 16:10:59 -04:00 committed by GitHub
commit 8e052b0865
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 770 additions and 623 deletions

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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));
}
}
}

View File

@ -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
}
}
}
}
}

View File

@ -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));
}
}
}

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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() {
}
}
}

View 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() {
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 = "";
}
}
}

View File

@ -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;
}
}
}