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 * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -24,7 +24,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -38,7 +40,7 @@ abstract class AbstractSingleEntityParser implements XRYFileParser {
protected static final String PARSER_NAME = "XRY DSP"; protected static final String PARSER_NAME = "XRY DSP";
@Override @Override
public void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException { public void parse(XRYFileReader reader, Content parent, SleuthkitCase currentCase) throws IOException, TskCoreException, BlackboardException {
Path reportPath = reader.getReportPath(); Path reportPath = reader.getReportPath();
logger.log(Level.INFO, String.format("[XRY DSP] Processing report at [ %s ]", reportPath.toString())); logger.log(Level.INFO, String.format("[XRY DSP] Processing report at [ %s ]", reportPath.toString()));
@ -94,7 +96,7 @@ abstract class AbstractSingleEntityParser implements XRYFileParser {
} }
if(!keyValuePairs.isEmpty()) { if(!keyValuePairs.isEmpty()) {
makeArtifact(keyValuePairs, parent); makeArtifact(keyValuePairs, parent, currentCase);
} }
} }
} }
@ -122,9 +124,9 @@ abstract class AbstractSingleEntityParser implements XRYFileParser {
*/ */
abstract boolean isNamespace(String nameSpace); abstract boolean isNamespace(String nameSpace);
/** /**
* Makes an artifact from the parsed key value pairs. * Makes an artifact from the parsed key value pairs.
*/ */
abstract void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException; abstract void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, BlackboardException;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,24 +18,22 @@
*/ */
package org.sleuthkit.autopsy.datasourceprocessors.xry; package org.sleuthkit.autopsy.datasourceprocessors.xry;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CallMediaType;
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CommunicationDirection;
/** /**
* Parses XRY Calls files and creates artifacts. * Parses XRY Calls files and creates artifacts.
@ -44,27 +42,19 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName()); private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName());
//Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch()
//function for more details.
private static final DateTimeFormatter DATE_TIME_PARSER
= DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y");
private static final String DEVICE_LOCALE = "(device)";
private static final String NETWORK_LOCALE = "(network)";
/** /**
* All of the known XRY keys for call reports and their corresponding * All of the known XRY keys for call reports and their corresponding
* blackboard attribute types, if any. * blackboard attribute types, if any.
*/ */
private enum XryKey { private enum XryKey {
NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME), NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME), TIME("time", null),
DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION), DIRECTION("direction", null),
CALL_TYPE("call type", null), CALL_TYPE("call type", null),
NUMBER("number", null), NUMBER("number", null),
TEL("tel", null), TEL("tel", null),
TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO), TO("to", null),
FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM), FROM("from", null),
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED), DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
DURATION("duration", null), DURATION("duration", null),
STORAGE("storage", null), STORAGE("storage", null),
@ -175,25 +165,18 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
} }
@Override @Override
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException { void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, BlackboardException {
List<BlackboardAttribute> attributes = new ArrayList<>(); // Transform all the data from XRY land into the appropriate CommHelper
for(XRYKeyValuePair pair : keyValuePairs) { // data types.
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair); String callerId = null;
if(attribute.isPresent()) { final Collection<String> calleeList = new ArrayList<>();
attributes.add(attribute.get()); CommunicationDirection direction = CommunicationDirection.UNKNOWN;
} long startTime = 0L;
} final long endTime = 0L;
if(!attributes.isEmpty()) { final CallMediaType callType = CallMediaType.UNKNOWN;
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); final Collection<BlackboardAttribute> otherAttributes = new ArrayList<>();
artifact.addAttributes(attributes);
}
}
/** for (XRYKeyValuePair pair : keyValuePairs) {
* Creates the appropriate blackboard attribute given a single XRY Key Value
* pair, if any. Most XRY keys are mapped to an attribute type in the enum above.
*/
private Optional<BlackboardAttribute> getBlackboardAttribute(XRYKeyValuePair pair) {
XryKey xryKey = XryKey.fromDisplayName(pair.getKey()); XryKey xryKey = XryKey.fromDisplayName(pair.getKey());
XryNamespace xryNamespace = XryNamespace.NONE; XryNamespace xryNamespace = XryNamespace.NONE;
if (XryNamespace.contains(pair.getNamespace())) { if (XryNamespace.contains(pair.getNamespace())) {
@ -203,38 +186,69 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
switch (xryKey) { switch (xryKey) {
case TEL: case TEL:
case NUMBER: case NUMBER:
//Apply the namespace if(!XRYUtils.isPhoneValid(pair.getValue())) {
switch (xryNamespace) { continue;
case FROM: }
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, // Apply namespace or direction
PARSER_NAME, pair.getValue())); if (xryNamespace == XryNamespace.FROM || direction == CommunicationDirection.INCOMING) {
case TO: callerId = pair.getValue();
return Optional.of(new BlackboardAttribute( } else if (xryNamespace == XryNamespace.TO || direction == CommunicationDirection.OUTGOING) {
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, calleeList.add(pair.getValue());
PARSER_NAME, pair.getValue())); } else {
default: otherAttributes.add(new BlackboardAttribute(
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
PARSER_NAME, pair.getValue())); PARSER_NAME, pair.getValue()));
} }
break;
// Although confusing, as these are also 'name spaces', it appears
// later versions of XRY just made these standardized lines.
case TO:
if(!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
calleeList.add(pair.getValue());
break;
case FROM:
if(!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
callerId = pair.getValue();
break;
case TIME: case TIME:
try { try {
//Tranform value to seconds since epoch //Tranform value to seconds since epoch
long dateTimeSinceEpoch = calculateSecondsSinceEpoch(pair.getValue()); long dateTimeSinceEpoch = XRYUtils.calculateSecondsSinceEpoch(pair.getValue());
return Optional.of(new BlackboardAttribute(xryKey.getType(), startTime = dateTimeSinceEpoch;
PARSER_NAME, dateTimeSinceEpoch));
} catch (DateTimeParseException ex) { } catch (DateTimeParseException ex) {
logger.log(Level.WARNING, String.format("[XRY DSP] Assumption" logger.log(Level.WARNING, String.format("[XRY DSP] Assumption"
+ " about the date time formatting of call logs is " + " about the date time formatting of call logs is "
+ "not right. Here is the value [ %s ]", pair.getValue()), ex); + "not right. Here is the value [ %s ]", pair.getValue()), ex);
return Optional.empty();
} }
break;
case DIRECTION:
String directionString = pair.getValue().toLowerCase();
if (directionString.equals("incoming")) {
direction = CommunicationDirection.INCOMING;
} else {
direction = CommunicationDirection.OUTGOING;
}
break;
case TYPE:
String typeString = pair.getValue();
if (typeString.equalsIgnoreCase("received")) {
direction = CommunicationDirection.INCOMING;
} else if (typeString.equalsIgnoreCase("dialed")) {
direction = CommunicationDirection.OUTGOING;
}
break;
default: default:
//Otherwise, the XryKey enum contains the correct BlackboardAttribute //Otherwise, the XryKey enum contains the correct BlackboardAttribute
//type. //type.
if (xryKey.getType() != null) { if (xryKey.getType() != null) {
return Optional.of(new BlackboardAttribute(xryKey.getType(), otherAttributes.add(new BlackboardAttribute(xryKey.getType(),
PARSER_NAME, pair.getValue())); PARSER_NAME, pair.getValue()));
} }
@ -242,92 +256,66 @@ final class XRYCallsFileParser extends AbstractSingleEntityParser {
+ "(in brackets) [ %s ] was recognized but " + "(in brackets) [ %s ] was recognized but "
+ "more data or time is needed to finish implementation. Discarding... ", + "more data or time is needed to finish implementation. Discarding... ",
pair)); pair));
return Optional.empty();
} }
} }
/** // Make sure we have the required fields, otherwise the CommHelper will
* Removes the locale from the date time value. // complain about illegal arguments.
*
* Locale in this case being (Device) or (Network). // These are all the invalid combinations.
* if (callerId == null && calleeList.isEmpty()
* @param dateTime XRY datetime value to be sanitized. || direction == CommunicationDirection.INCOMING && callerId == null
* @return A purer date time value. || direction == CommunicationDirection.OUTGOING && calleeList.isEmpty()) {
*/
private String removeDateTimeLocale(String dateTime) { // If the combo is invalid, just make an artifact with what we've got.
String result = dateTime; if (direction != CommunicationDirection.UNKNOWN) {
int deviceIndex = result.toLowerCase().indexOf(DEVICE_LOCALE); otherAttributes.add(new BlackboardAttribute(
if (deviceIndex != -1) { BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION,
result = result.substring(0, deviceIndex); PARSER_NAME, direction.getDisplayName()));
}
int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE);
if (networkIndex != -1) {
result = result.substring(0, networkIndex);
}
return result;
} }
/** if (startTime > 0L) {
* Parses the date time value and calculates seconds since epoch. otherAttributes.add(new BlackboardAttribute(
* BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START,
* @param dateTime PARSER_NAME, startTime));
* @return }
*/
private long calculateSecondsSinceEpoch(String dateTime) { // If the DIRECTION check failed, just manually create accounts
String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim(); // for these phones. Note, there is no need to create relationships.
/** // If both callerId and calleeList were non-null/non-empty, then
* The format of time in XRY Messages reports is of the form: // it would have been a valid combination.
* if (callerId != null) {
* 1/3/1990 1:23:54 AM UTC+4 currentCase.getCommunicationsManager().createAccountFileInstance(
* Account.Type.PHONE, callerId, PARSER_NAME, parent);
* In our current version of Java (openjdk-1.8.0.222), there is a bug
* with having the timezone offset (UTC+4 or GMT-7) at the end of the otherAttributes.add(new BlackboardAttribute(
* date time input. This is fixed in later versions of the JDK (9 and BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
* beyond). https://bugs.openjdk.java.net/browse/JDK-8154050 Rather than PARSER_NAME, callerId));
* update the JDK to accommodate this, the components of the date time }
* string are reversed:
* for (String phone : calleeList) {
* UTC+4 AM 1:23:54 1/3/1990 currentCase.getCommunicationsManager().createAccountFileInstance(
* Account.Type.PHONE, phone, PARSER_NAME, parent);
* The java time package will correctly parse this date time format.
*/ otherAttributes.add(new BlackboardAttribute(
String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale); BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
/** PARSER_NAME, phone));
* Furthermore, the DateTimeFormatter's timezone offset letter ('O') }
* does not recognize UTC but recognizes GMT. According to
* https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only if (!otherAttributes.isEmpty()) {
* differs from UTC by at most 1 second and so substitution will only BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG);
* introduce a trivial amount of error. artifact.addAttributes(otherAttributes);
*/
String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT"); currentCase.getBlackboard().postArtifact(artifact, PARSER_NAME);
TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT, }
ZonedDateTime::from,
LocalDateTime::from,
OffsetDateTime::from);
//Query for the ZoneID
if (result.query(TemporalQueries.zoneId()) == null) {
//If none, assumed GMT+0.
return ZonedDateTime.of(LocalDateTime.from(result),
ZoneId.of("GMT")).toEpochSecond();
} else { } else {
return Instant.from(result).getEpochSecond();
}
}
/** // Otherwise we can safely use the helper.
* Reverses the order of the date time components. CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
* currentCase, PARSER_NAME, parent, Account.Type.PHONE);
* Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990
* helper.addCalllog(direction, callerId, calleeList, startTime,
* @param dateTime endTime, callType, otherAttributes);
* @return
*/
private String reverseOrderOfDateTimeComponents(String dateTime) {
StringBuilder reversedDateTime = new StringBuilder(dateTime.length());
String[] dateTimeComponents = dateTime.split(" ");
for (String component : dateTimeComponents) {
reversedDateTime.insert(0, " ").insert(0, component);
} }
return reversedDateTime.toString().trim();
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,16 +19,19 @@
package org.sleuthkit.autopsy.datasourceprocessors.xry; package org.sleuthkit.autopsy.datasourceprocessors.xry;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.autopsy.datasourceprocessors.xry.AbstractSingleEntityParser.PARSER_NAME;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
/** /**
* Parses XRY Contacts-Contacts files and creates artifacts. * Parses XRY Contacts-Contacts files and creates artifacts.
@ -37,31 +40,9 @@ final class XRYContactsFileParser extends AbstractSingleEntityParser {
private static final Logger logger = Logger.getLogger(XRYContactsFileParser.class.getName()); private static final Logger logger = Logger.getLogger(XRYContactsFileParser.class.getName());
//All of the known XRY keys for contacts.
private static final Map<String, BlackboardAttribute.ATTRIBUTE_TYPE> XRY_KEYS =
new HashMap<String, BlackboardAttribute.ATTRIBUTE_TYPE>() {{
put("name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
put("tel", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER);
put("mobile", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE);
put("home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME);
put("related application", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME);
put("address home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION);
put("email home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_HOME);
put("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED);
//Ignoring or need more information to decide.
put("storage", null);
put("other", null);
put("picture", null);
put("index", null);
put("account name", null);
}};
@Override @Override
boolean canProcess(XRYKeyValuePair pair) { boolean canProcess(XRYKeyValuePair pair) {
String normalizedKey = pair.getKey().toLowerCase(); return XryKey.contains(pair.getKey());
return XRY_KEYS.containsKey(normalizedKey);
} }
@Override @Override
@ -70,36 +51,168 @@ final class XRYContactsFileParser extends AbstractSingleEntityParser {
return false; return false;
} }
/** @Override
* Creates the appropriate blackboard attribute given a single XRY Key Value void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, Blackboard.BlackboardException {
* pair. // Transform all the data from XRY land into the appropriate CommHelper
*/ // data types.
private Optional<BlackboardAttribute> getBlackboardAttribute(XRYKeyValuePair pair) { String contactName = null;
String normalizedKey = pair.getKey().toLowerCase(); String phoneNumber = null;
BlackboardAttribute.ATTRIBUTE_TYPE attrType = XRY_KEYS.get(normalizedKey); String homePhoneNumber = null;
if(attrType != null) { String mobilePhoneNumber = null;
return Optional.of(new BlackboardAttribute(attrType, PARSER_NAME, pair.getValue())); String emailAddr = null;
boolean hasAnEmail = false;
final Collection<BlackboardAttribute> additionalAttributes = new ArrayList<>();
for (XRYKeyValuePair pair : keyValuePairs) {
XryKey xryKey = XryKey.fromDisplayName(pair.getKey());
switch (xryKey) {
case NAME:
if (contactName != null) {
additionalAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, PARSER_NAME, pair.getValue()));
} else {
contactName = pair.getValue();
}
break;
case TEL:
if (!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
if (phoneNumber != null) {
additionalAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, PARSER_NAME, pair.getValue()));
} else {
phoneNumber = pair.getValue();
}
break;
case MOBILE:
if (!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
if (mobilePhoneNumber != null) {
additionalAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE, PARSER_NAME, pair.getValue()));
} else {
mobilePhoneNumber = pair.getValue();
}
break;
case HOME:
if (!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
if (homePhoneNumber != null) {
additionalAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME, PARSER_NAME, pair.getValue()));
} else {
homePhoneNumber = pair.getValue();
}
break;
case EMAIL_HOME:
if (!XRYUtils.isEmailValid(pair.getValue())) {
continue;
}
hasAnEmail = true;
additionalAttributes.add(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_HOME,
PARSER_NAME, pair.getValue()));
break;
default:
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
//type.
if (xryKey.getType() != null) {
additionalAttributes.add(new BlackboardAttribute(xryKey.getType(),
PARSER_NAME, pair.getValue()));
} }
logger.log(Level.INFO, String.format("[XRY DSP] Key value pair " logger.log(Level.INFO, String.format("[XRY DSP] Key value pair "
+ "(in brackets) [ %s ] was recognized but we need " + "(in brackets) [ %s ] was recognized but "
+ "more data or time to finish implementation. Discarding... ", + "more data or time is needed to finish implementation. Discarding... ",
pair)); pair));
return Optional.empty(); }
} }
@Override // Make sure we have the required fields, otherwise the CommHelper will
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException { // complain about illegal arguments.
List<BlackboardAttribute> attributes = new ArrayList<>(); if (phoneNumber != null || homePhoneNumber != null || mobilePhoneNumber != null || hasAnEmail) {
for(XRYKeyValuePair pair : keyValuePairs) { CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair); currentCase, PARSER_NAME, parent, Account.Type.DEVICE);
if(attribute.isPresent()) {
attributes.add(attribute.get()); helper.addContact(contactName, phoneNumber, homePhoneNumber,
} mobilePhoneNumber, emailAddr, additionalAttributes);
} } else {
if(!attributes.isEmpty()) { // Just create an artifact with the attributes that we do have.
if (!additionalAttributes.isEmpty()) {
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
artifact.addAttributes(attributes); artifact.addAttributes(additionalAttributes);
currentCase.getBlackboard().postArtifact(artifact, PARSER_NAME);
}
}
}
/**
* Enum containing all known keys for contacts and their corresponding
* blackboard attribute. Some keys are intentionally null, because they are
* handled as special cases in makeArtifact(). Some are null because there's
* not an appropriate attribute type.
*/
private enum XryKey {
NAME("name", null),
TEL("tel", null),
MOBILE("mobile", null),
HOME("home", null),
RELATED_APPLICATION("related application", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME),
ADDRESS_HOME("address home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION),
EMAIL_HOME("email home", null),
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
//Ignoring or need more information to decide.
STORAGE("storage", null),
OTHER("other", null),
PICTURE("picture", null),
INDEX("index", null),
ACCOUNT_NAME("account name", null);
private final String name;
private final BlackboardAttribute.ATTRIBUTE_TYPE type;
XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) {
this.name = name;
this.type = type;
}
BlackboardAttribute.ATTRIBUTE_TYPE getType() {
return type;
}
/**
* Indicates if the display name of the XRY key is a recognized type.
*/
static boolean contains(String key) {
try {
XryKey.fromDisplayName(key);
return true;
} catch (IllegalArgumentException ex) {
return false;
}
}
/**
* Matches the display name of the xry key to the appropriate enum type.
*
* It is assumed that XRY key string is recognized. Otherwise, an
* IllegalArgumentException is thrown. Test all membership with
* contains() before hand.
*/
static XryKey fromDisplayName(String key) {
String normalizedKey = key.trim().toLowerCase();
for (XryKey keyChoice : XryKey.values()) {
if (normalizedKey.equals(keyChoice.name)) {
return keyChoice;
}
}
throw new IllegalArgumentException(String.format("Key [%s] was not found."
+ " All keys should be tested with contains.", key));
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -48,6 +48,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskDataException;
@ -204,12 +205,11 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
try { try {
XRYFolder xryFolder = new XRYFolder(selectedPath); XRYFolder xryFolder = new XRYFolder(selectedPath);
FileManager fileManager = Case.getCurrentCaseThrows() Case currentCase = Case.getCurrentCaseThrows();
.getServices().getFileManager();
String uniqueUUID = UUID.randomUUID().toString(); String uniqueUUID = UUID.randomUUID().toString();
//Move heavy lifting to a background task. //Move heavy lifting to a background task.
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor, swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
callback, fileManager, uniqueUUID); callback, currentCase, uniqueUUID);
swingWorker.execute(); swingWorker.execute();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex); logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
@ -238,11 +238,10 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
try { try {
XRYFolder xryFolder = new XRYFolder(dataSourcePath); XRYFolder xryFolder = new XRYFolder(dataSourcePath);
FileManager fileManager = Case.getCurrentCaseThrows() Case currentCase = Case.getCurrentCaseThrows();
.getServices().getFileManager();
//Move heavy lifting to a background task. //Move heavy lifting to a background task.
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor, swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
callBack, fileManager, deviceId); callBack, currentCase, deviceId);
swingWorker.execute(); swingWorker.execute();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex); logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
@ -273,20 +272,19 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorProgressMonitor progressMonitor;
private final DataSourceProcessorCallback callback; private final DataSourceProcessorCallback callback;
private final FileManager fileManager; private final Case currentCase;
private final XRYFolder xryFolder; private final XRYFolder xryFolder;
private final String uniqueUUID; private final String uniqueUUID;
public XRYReportProcessorSwingWorker(XRYFolder folder, public XRYReportProcessorSwingWorker(XRYFolder folder,
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorProgressMonitor progressMonitor,
DataSourceProcessorCallback callback, DataSourceProcessorCallback callback,
FileManager fileManager, Case currentCase, String uniqueUUID) {
String uniqueUUID) {
this.xryFolder = folder; this.xryFolder = folder;
this.progressMonitor = progressMonitor; this.progressMonitor = progressMonitor;
this.callback = callback; this.callback = callback;
this.fileManager = fileManager; this.currentCase = currentCase;
this.uniqueUUID = uniqueUUID; this.uniqueUUID = uniqueUUID;
} }
@ -296,7 +294,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
"XRYDataSourceProcessor.processingFiles=Processing all XRY files..." "XRYDataSourceProcessor.processingFiles=Processing all XRY files..."
}) })
protected LocalFilesDataSource doInBackground() throws TskCoreException, protected LocalFilesDataSource doInBackground() throws TskCoreException,
TskDataException, IOException { TskDataException, IOException, BlackboardException {
progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_preppingFiles()); progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_preppingFiles());
List<Path> nonXRYFiles = xryFolder.getNonXRYFiles(); List<Path> nonXRYFiles = xryFolder.getNonXRYFiles();
@ -304,7 +302,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
//Map paths to string representations. //Map paths to string representations.
.map(Path::toString) .map(Path::toString)
.collect(Collectors.toList()); .collect(Collectors.toList());
LocalFilesDataSource dataSource = fileManager.addLocalFilesDataSource( LocalFilesDataSource dataSource = currentCase.getServices().getFileManager().addLocalFilesDataSource(
uniqueUUID, uniqueUUID,
"XRY Text Export", //Name "XRY Text Export", //Name
"", //Timezone "", //Timezone
@ -313,7 +311,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
//Process the report files. //Process the report files.
progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_processingFiles()); progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_processingFiles());
XRYReportProcessor.process(xryFolder, dataSource); XRYReportProcessor.process(xryFolder, dataSource, currentCase.getSleuthkitCase());
return dataSource; return dataSource;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -25,9 +25,11 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -89,7 +91,7 @@ final class XRYDeviceGenInfoFileParser extends AbstractSingleEntityParser {
} }
@Override @Override
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException { void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, Blackboard.BlackboardException {
List<BlackboardAttribute> attributes = new ArrayList<>(); List<BlackboardAttribute> attributes = new ArrayList<>();
for(int i = 0; i < keyValuePairs.size(); i+=2) { for(int i = 0; i < keyValuePairs.size(); i+=2) {
Optional<BlackboardAttribute> attribute; Optional<BlackboardAttribute> attribute;

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,9 @@
package org.sleuthkit.autopsy.datasourceprocessors.xry; package org.sleuthkit.autopsy.datasourceprocessors.xry;
import java.io.IOException; import java.io.IOException;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -40,7 +42,7 @@ interface XRYFileParser {
* @throws IOException If an I/O error occurs during reading. * @throws IOException If an I/O error occurs during reading.
* @throws TskCoreException If an error occurs during artifact creation. * @throws TskCoreException If an error occurs during artifact creation.
*/ */
void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException; void parse(XRYFileReader reader, Content parent, SleuthkitCase currentCase) throws IOException, TskCoreException, BlackboardException;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -20,16 +20,9 @@ package org.sleuthkit.autopsy.datasourceprocessors.xry;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -37,10 +30,15 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CommunicationDirection;
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.MessageReadStatus;
/** /**
* Parses Messages-SMS files and creates artifacts. * Parses Messages-SMS files and creates artifacts.
@ -52,31 +50,20 @@ final class XRYMessagesFileParser implements XRYFileParser {
private static final String PARSER_NAME = "XRY DSP"; private static final String PARSER_NAME = "XRY DSP";
//Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch()
//function for more details.
private static final DateTimeFormatter DATE_TIME_PARSER
= DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y");
private static final String DEVICE_LOCALE = "(device)";
private static final String NETWORK_LOCALE = "(network)";
private static final int READ = 1;
private static final int UNREAD = 0;
/** /**
* All of the known XRY keys for message reports and their corresponding * All of the known XRY keys for message reports and their corresponding
* blackboard attribute types, if any. * blackboard attribute types, if any.
*/ */
private enum XryKey { private enum XryKey {
DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED), DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION), DIRECTION("direction", null),
MESSAGE("message", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT), MESSAGE("message", null),
NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON), NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON),
TEXT("text", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT), TEXT("text", null),
TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME), TIME("time", null),
SERVICE_CENTER("service center", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER), SERVICE_CENTER("service center", null),
FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM), FROM("from", null),
TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO), TO("to", null),
//The following keys either need special processing or more time and data to find a type. //The following keys either need special processing or more time and data to find a type.
STORAGE("storage", null), STORAGE("storage", null),
NUMBER("number", null), NUMBER("number", null),
@ -272,7 +259,7 @@ final class XRYMessagesFileParser implements XRYFileParser {
* encountered. * encountered.
*/ */
@Override @Override
public void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException { public void parse(XRYFileReader reader, Content parent, SleuthkitCase currentCase) throws IOException, TskCoreException, BlackboardException {
Path reportPath = reader.getReportPath(); Path reportPath = reader.getReportPath();
logger.log(Level.INFO, String.format("[XRY DSP] Processing report at" logger.log(Level.INFO, String.format("[XRY DSP] Processing report at"
+ " [ %s ]", reportPath.toString())); + " [ %s ]", reportPath.toString()));
@ -282,26 +269,178 @@ final class XRYMessagesFileParser implements XRYFileParser {
while (reader.hasNextEntity()) { while (reader.hasNextEntity()) {
String xryEntity = reader.nextEntity(); String xryEntity = reader.nextEntity();
List<BlackboardAttribute> attributes = getBlackboardAttributes(xryEntity, reader, referenceNumbersSeen);
//Only create artifacts with non-empty attributes. // This call will combine all segmented text into a single key value pair
if (!attributes.isEmpty()) { List<XRYKeyValuePair> pairs = getXRYKeyValuePairs(xryEntity, reader, referenceNumbersSeen);
BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE);
artifact.addAttributes(attributes); // Transform all the data from XRY land into the appropriate CommHelper
// data types.
final String messageType = PARSER_NAME;
CommunicationDirection direction = CommunicationDirection.UNKNOWN;
String senderId = null;
final List<String> recipientIdsList = new ArrayList<>();
long dateTime = 0L;
MessageReadStatus readStatus = MessageReadStatus.UNKNOWN;
final String subject = null;
String text = null;
final String threadId = null;
final Collection<BlackboardAttribute> otherAttributes = new ArrayList<>();
for(XRYKeyValuePair pair : pairs) {
XryNamespace namespace = XryNamespace.NONE;
if (XryNamespace.contains(pair.getNamespace())) {
namespace = XryNamespace.fromDisplayName(pair.getNamespace());
}
XryKey key = XryKey.fromDisplayName(pair.getKey());
String normalizedValue = pair.getValue().toLowerCase().trim();
switch (key) {
case TEL:
case NUMBER:
if(!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
// Apply namespace or direction
if(namespace == XryNamespace.FROM || direction == CommunicationDirection.INCOMING) {
senderId = pair.getValue();
} else if(namespace == XryNamespace.TO || direction == CommunicationDirection.OUTGOING) {
recipientIdsList.add(pair.getValue());
} else {
currentCase.getCommunicationsManager().createAccountFileInstance(
Account.Type.PHONE, pair.getValue(), PARSER_NAME, parent);
otherAttributes.add(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
PARSER_NAME, pair.getValue()));
}
break;
// Although confusing, as these are also 'name spaces', it appears
// later versions of XRY just made these standardized lines.
case FROM:
if(!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
senderId = pair.getValue();
break;
case TO:
if(!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
recipientIdsList.add(pair.getValue());
break;
case TIME:
try {
//Tranform value to seconds since epoch
long dateTimeSinceInEpoch = XRYUtils.calculateSecondsSinceEpoch(pair.getValue());
dateTime = dateTimeSinceInEpoch;
} catch (DateTimeParseException ex) {
logger.log(Level.WARNING, String.format("[%s] Assumption"
+ " about the date time formatting of messages is "
+ "not right. Here is the pair [ %s ]", PARSER_NAME, pair), ex);
}
break;
case TYPE:
switch (normalizedValue) {
case "incoming":
direction = CommunicationDirection.INCOMING;
break;
case "outgoing":
direction = CommunicationDirection.OUTGOING;
break;
case "deliver":
case "submit":
case "status report":
//Ignore for now.
break;
default:
logger.log(Level.WARNING, String.format("[%s] Unrecognized "
+ " value for key pair [ %s ].", PARSER_NAME, pair));
}
break;
case STATUS:
switch (normalizedValue) {
case "read":
readStatus = MessageReadStatus.READ;
break;
case "unread":
readStatus = MessageReadStatus.UNREAD;
break;
case "deleted":
otherAttributes.add(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED,
PARSER_NAME, pair.getValue()));
break;
case "sending failed":
case "unsent":
case "sent":
//Ignoring for now.
break;
default:
logger.log(Level.WARNING, String.format("[%s] Unrecognized "
+ " value for key pair [ %s ].", PARSER_NAME, pair));
}
break;
case TEXT:
case MESSAGE:
text = pair.getValue();
break;
case DIRECTION:
switch (normalizedValue) {
case "incoming":
direction = CommunicationDirection.INCOMING;
break;
case "outgoing":
direction = CommunicationDirection.OUTGOING;
break;
default:
direction = CommunicationDirection.UNKNOWN;
break;
}
break;
case SERVICE_CENTER:
if(!XRYUtils.isPhoneValid(pair.getValue())) {
continue;
}
otherAttributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
PARSER_NAME, pair.getValue()));
break;
default:
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
//type.
if (key.getType() != null) {
otherAttributes.add(new BlackboardAttribute(key.getType(),
PARSER_NAME, pair.getValue()));
} else {
logger.log(Level.INFO, String.format("[%s] Key value pair "
+ "(in brackets) [ %s ] was recognized but "
+ "more data or time is needed to finish implementation. Discarding... ",
PARSER_NAME, pair));
} }
} }
} }
CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
currentCase, PARSER_NAME, parent, Account.Type.PHONE);
helper.addMessage(messageType, direction, senderId, recipientIdsList,
dateTime, readStatus, subject, text, threadId, otherAttributes);
}
}
/** /**
* Extracts all blackboard attributes from the XRY Entity. This function will * Extracts all pairs from the XRY Entity. This function
* unify any segmented text, if need be. * will unify any segmented text, if need be.
*/ */
private List<BlackboardAttribute> getBlackboardAttributes(String xryEntity, private List<XRYKeyValuePair> getXRYKeyValuePairs(String xryEntity,
XRYFileReader reader, Set<Integer> referenceValues) throws IOException { XRYFileReader reader, Set<Integer> referenceValues) throws IOException {
String[] xryLines = xryEntity.split("\n"); String[] xryLines = xryEntity.split("\n");
//First line of the entity is the title, each XRY entity is non-empty. //First line of the entity is the title, each XRY entity is non-empty.
logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ]", xryLines[0])); logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ]", xryLines[0]));
List<BlackboardAttribute> attributes = new ArrayList<>(); List<XRYKeyValuePair> pairs = new ArrayList<>();
//Count the key value pairs in the XRY entity. //Count the key value pairs in the XRY entity.
int keyCount = getCountOfKeyValuePairs(xryLines); int keyCount = getCountOfKeyValuePairs(xryLines);
@ -339,14 +478,10 @@ final class XRYMessagesFileParser implements XRYFileParser {
pair.getNamespace()); pair.getNamespace());
} }
//Get the corresponding blackboard attribute, if any. pairs.add(pair);
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
if (attribute.isPresent()) {
attributes.add(attribute.get());
}
} }
return attributes; return pairs;
} }
/** /**
@ -531,191 +666,4 @@ final class XRYMessagesFileParser implements XRYFileParser {
return Optional.empty(); return Optional.empty();
} }
/**
* Creates an attribute from the extracted key value pair.
*
* @param nameSpace The namespace of this key value pair. It will have been
* verified beforehand, otherwise it will be NONE.
* @param key The recognized XRY key.
* @param value The value associated with that key.
* @return Corresponding blackboard attribute, if any.
*/
private Optional<BlackboardAttribute> getBlackboardAttribute(XRYKeyValuePair pair) {
XryNamespace namespace = XryNamespace.NONE;
if (XryNamespace.contains(pair.getNamespace())) {
namespace = XryNamespace.fromDisplayName(pair.getNamespace());
}
XryKey key = XryKey.fromDisplayName(pair.getKey());
String normalizedValue = pair.getValue().toLowerCase().trim();
switch (key) {
case TEL:
case NUMBER:
switch (namespace) {
case FROM:
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM,
PARSER_NAME, pair.getValue()));
case TO:
case PARTICIPANT:
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO,
PARSER_NAME, pair.getValue()));
default:
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
PARSER_NAME, pair.getValue()));
}
case TIME:
try {
//Tranform value to seconds since epoch
long dateTimeSinceInEpoch = calculateSecondsSinceEpoch(pair.getValue());
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START,
PARSER_NAME, dateTimeSinceInEpoch));
} catch (DateTimeParseException ex) {
logger.log(Level.WARNING, String.format("[XRY DSP] Assumption"
+ " about the date time formatting of messages is "
+ "not right. Here is the pair [ %s ]", pair), ex);
return Optional.empty();
}
case TYPE:
switch (normalizedValue) {
case "incoming":
case "outgoing":
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION,
PARSER_NAME, pair.getValue()));
case "deliver":
case "submit":
case "status report":
//Ignore for now.
return Optional.empty();
default:
logger.log(Level.WARNING, String.format("[XRY DSP] Unrecognized "
+ " value for key pair [ %s ].", pair));
return Optional.empty();
}
case STATUS:
switch (normalizedValue) {
case "read":
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS,
PARSER_NAME, READ));
case "unread":
return Optional.of(new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS,
PARSER_NAME, UNREAD));
case "sending failed":
case "deleted":
case "unsent":
case "sent":
//Ignore for now.
return Optional.empty();
default:
logger.log(Level.WARNING, String.format("[XRY DSP] Unrecognized "
+ " value for key pair [ %s ].", pair));
return Optional.empty();
}
default:
//Otherwise, the XryKey enum contains the correct BlackboardAttribute
//type.
if (key.getType() != null) {
return Optional.of(new BlackboardAttribute(key.getType(),
PARSER_NAME, pair.getValue()));
}
logger.log(Level.INFO, String.format("[XRY DSP] Key value pair "
+ "(in brackets) [ %s ] was recognized but "
+ "more data or time is needed to finish implementation. Discarding... ", pair));
return Optional.empty();
}
}
/**
* Removes the locale from the date time value.
*
* Locale in this case being (Device) or (Network).
*
* @param dateTime XRY datetime value to be sanitized.
* @return A purer date time value.
*/
private String removeDateTimeLocale(String dateTime) {
String result = dateTime;
int deviceIndex = result.toLowerCase().indexOf(DEVICE_LOCALE);
if (deviceIndex != -1) {
result = result.substring(0, deviceIndex);
}
int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE);
if (networkIndex != -1) {
result = result.substring(0, networkIndex);
}
return result;
}
/**
* Parses the date time value and calculates seconds since epoch.
*
* @param dateTime
* @return
*/
private long calculateSecondsSinceEpoch(String dateTime) {
String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim();
/**
* The format of time in XRY Messages reports is of the form:
*
* 1/3/1990 1:23:54 AM UTC+4
*
* In our current version of Java (openjdk-1.8.0.222), there is a bug
* with having the timezone offset (UTC+4 or GMT-7) at the end of the
* date time input. This is fixed in later versions of the JDK (9 and
* beyond). https://bugs.openjdk.java.net/browse/JDK-8154050 Rather than
* update the JDK to accommodate this, the components of the date time
* string are reversed:
*
* UTC+4 AM 1:23:54 1/3/1990
*
* The java time package will correctly parse this date time format.
*/
String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale);
/**
* Furthermore, the DateTimeFormatter's timezone offset letter ('O')
* does not recognize UTC but recognizes GMT. According to
* https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only
* differs from UTC by at most 1 second and so substitution will only
* introduce a trivial amount of error.
*/
String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT");
TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT,
ZonedDateTime::from,
LocalDateTime::from,
OffsetDateTime::from);
//Query for the ZoneID
if (result.query(TemporalQueries.zoneId()) == null) {
//If none, assumed GMT+0.
return ZonedDateTime.of(LocalDateTime.from(result),
ZoneId.of("GMT")).toEpochSecond();
} else {
return Instant.from(result).getEpochSecond();
}
}
/**
* Reverses the order of the date time components.
*
* Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990
*
* @param dateTime
* @return
*/
private String reverseOrderOfDateTimeComponents(String dateTime) {
StringBuilder reversedDateTime = new StringBuilder(dateTime.length());
String[] dateTimeComponents = dateTime.split(" ");
for (String component : dateTimeComponents) {
reversedDateTime.insert(0, " ").insert(0, component);
}
return reversedDateTime.toString().trim();
}
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -22,7 +22,9 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -43,7 +45,7 @@ final class XRYReportProcessor {
* @throws IOException If an I/O exception occurs. * @throws IOException If an I/O exception occurs.
* @throws TskCoreException If an error occurs adding artifacts. * @throws TskCoreException If an error occurs adding artifacts.
*/ */
static void process(XRYFolder folder, Content parent) throws IOException, TskCoreException { static void process(XRYFolder folder, Content parent, SleuthkitCase currentCase) throws IOException, TskCoreException, BlackboardException {
//Get all XRY file readers from this folder. //Get all XRY file readers from this folder.
List<XRYFileReader> xryFileReaders = folder.getXRYFileReaders(); List<XRYFileReader> xryFileReaders = folder.getXRYFileReaders();
@ -52,7 +54,7 @@ final class XRYReportProcessor {
String reportType = xryFileReader.getReportType(); String reportType = xryFileReader.getReportType();
if (XRYFileParserFactory.supports(reportType)) { if (XRYFileParserFactory.supports(reportType)) {
XRYFileParser parser = XRYFileParserFactory.get(reportType); XRYFileParser parser = XRYFileParserFactory.get(reportType);
parser.parse(xryFileReader, parent); parser.parse(xryFileReader, parent, currentCase);
} else { } else {
logger.log(Level.WARNING, String.format("[XRY DSP] XRY File (in brackets) " logger.log(Level.WARNING, String.format("[XRY DSP] XRY File (in brackets) "
+ "[ %s ] was found, but no parser to support its report type exists. " + "[ %s ] was found, but no parser to support its report type exists. "

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 * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -23,9 +23,11 @@ import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -67,7 +69,7 @@ final class XRYWebBookmarksFileParser extends AbstractSingleEntityParser {
} }
@Override @Override
void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException { void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, BlackboardException {
List<BlackboardAttribute> attributes = new ArrayList<>(); List<BlackboardAttribute> attributes = new ArrayList<>();
for(XRYKeyValuePair pair : keyValuePairs) { for(XRYKeyValuePair pair : keyValuePairs) {
Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair); Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-2019 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* *
* Copyright 2012 42six Solutions. * Copyright 2012 42six Solutions.
* *
@ -46,7 +46,7 @@ import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -55,6 +55,7 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
/** /**
* Chrome recent activity extraction * Chrome recent activity extraction
@ -731,8 +732,13 @@ class Chrome extends Extract {
// get form autofill artifacts // get form autofill artifacts
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X)); bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X));
try {
// get form address atifacts // get form address atifacts
bbartifacts.addAll(getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X)); getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X);
} catch (NoCurrentCaseException | TskCoreException | Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, String.format("Error adding artifacts to the case database "
+ "for chrome file %s [objId=%d]", webDataFile.getName(), webDataFile.getId()), ex);
}
dbFile.delete(); dbFile.delete();
} }
@ -808,17 +814,23 @@ class Chrome extends Extract {
* *
* @return collection of TSK_WEB_FORM_ADDRESS artifacts * @return collection of TSK_WEB_FORM_ADDRESS artifacts
*/ */
private Collection<BlackboardArtifact> getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) { private void getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) throws NoCurrentCaseException,
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); TskCoreException, Blackboard.BlackboardException {
String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X
: WEBFORM_ADDRESS_QUERY; : WEBFORM_ADDRESS_QUERY;
// Helper to create web form address artifacts.
WebBrowserArtifactsHelper helper = new WebBrowserArtifactsHelper(
Case.getCurrentCaseThrows().getSleuthkitCase(),
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
webDataFile
);
// Get Web form addresses // Get Web form addresses
List<HashMap<String, Object>> addresses = this.dbConnect(dbFilePath, webformAddressQuery); List<HashMap<String, Object>> addresses = this.dbConnect(dbFilePath, webformAddressQuery);
logger.log(Level.INFO, "{0}- Now getting Web form addresses from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, addresses.size()}); //NON-NLS logger.log(Level.INFO, "{0}- Now getting Web form addresses from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, addresses.size()}); //NON-NLS
for (HashMap<String, Object> result : addresses) { for (HashMap<String, Object> result : addresses) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
// get name fields // get name fields
String first_name = result.get("first_name").toString() != null ? result.get("first_name").toString() : ""; String first_name = result.get("first_name").toString() != null ? result.get("first_name").toString() : "";
@ -854,72 +866,25 @@ class Chrome extends Extract {
street_address = String.join(" ", address_line_1, address_line_2); street_address = String.join(" ", address_line_1, address_line_2);
} }
// If an email address is found, create an account instance for it
if (email_Addr != null && !email_Addr.isEmpty()) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email_Addr, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), webDataFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating email account instance for '%s' from Chrome WebData file '%s' .",
email_Addr, webDataFile.getName()), ex); //NON-NLS
}
}
// If a phone number is found, create an account instance for it
if (phone_number != null && !phone_number.isEmpty()) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, phone_number, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), webDataFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating phone account instance for '%s' from Chrome WebData file '%s' .",
phone_number, webDataFile.getName()), ex); //NON-NLS
}
}
// Create atrributes from extracted fields // Create atrributes from extracted fields
if (full_name == null || full_name.isEmpty()) { if (full_name == null || full_name.isEmpty()) {
full_name = String.join(" ", first_name, middle_name, last_name); full_name = String.join(" ", first_name, middle_name, last_name);
} }
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON,
RecentActivityExtracterModuleFactory.getModuleName(),
full_name)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
RecentActivityExtracterModuleFactory.getModuleName(),
email_Addr)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
RecentActivityExtracterModuleFactory.getModuleName(),
phone_number)); //NON-NLS
String locationAddress = String.join(", ", street_address, city, state, zipcode, country_code); String locationAddress = String.join(", ", street_address, city, state, zipcode, country_code);
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
RecentActivityExtracterModuleFactory.getModuleName(),
locationAddress)); //NON-NLS
List<BlackboardAttribute> otherAttributes = new ArrayList<>();
if (date_modified > 0) { if (date_modified > 0) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED, otherAttributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
RecentActivityExtracterModuleFactory.getModuleName(), RecentActivityExtracterModuleFactory.getModuleName(),
date_modified)); //NON-NLS date_modified)); //NON-NLS
} }
if (use_count > 0 ){ helper.addWebFormAddress(
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, full_name, email_Addr, phone_number,
RecentActivityExtracterModuleFactory.getModuleName(), locationAddress, 0, use_date,
use_count)); //NON-NLS use_count, otherAttributes);
} }
if (use_date > 0) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
RecentActivityExtracterModuleFactory.getModuleName(),
use_date)); //NON-NLS
}
// Create artifact
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS, webDataFile, bbattributes);
if (bbart != null) {
bbartifacts.add(bbart);
}
}
// return all extracted artifacts
return bbartifacts;
} }
private boolean isChromePreVersion30(String temps) { private boolean isChromePreVersion30(String temps) {

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-2019 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* *
* Copyright 2012 42six Solutions. * Copyright 2012 42six Solutions.
* Contact: aebadirad <at> 42six <dot> com * Contact: aebadirad <at> 42six <dot> com
@ -69,11 +69,14 @@ import java.util.HashSet;
import static java.util.Locale.US; import static java.util.Locale.US;
import static java.util.TimeZone.getTimeZone; import static java.util.TimeZone.getTimeZone;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag; import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -944,7 +947,7 @@ class ExtractRegistry extends Extract {
Map<String, String> userInfo = userInfoMap.remove(userID); Map<String, String> userInfo = userInfoMap.remove(userID);
//if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it //if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it
if (userInfo != null) { if (userInfo != null) {
osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true)); osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true, regAbstractFile));
} }
} }
} }
@ -953,7 +956,7 @@ class ExtractRegistry extends Extract {
//add remaining userinfos as accounts; //add remaining userinfos as accounts;
for (Map<String, String> userInfo : userInfoMap.values()) { for (Map<String, String> userInfo : userInfoMap.values()) {
BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false)); bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false, regAbstractFile));
// index the artifact for keyword search // index the artifact for keyword search
newArtifacts.add(bbart); newArtifacts.add(bbart);
} }
@ -983,7 +986,7 @@ class ExtractRegistry extends Extract {
* *
* @throws ParseException * @throws ParseException
*/ */
Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser) throws ParseException { Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'"); SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'");
@ -1035,6 +1038,20 @@ class ExtractRegistry extends Extract {
value = userInfo.get(INTERNET_NAME_KEY); value = userInfo.get(INTERNET_NAME_KEY);
if (value != null && !value.isEmpty()) { if (value != null && !value.isEmpty()) {
try {
// Create an account for this email, if it doesn't already exist.
Case.getCurrentCaseThrows()
.getSleuthkitCase()
.getCommunicationsManager()
.createAccountFileInstance(Account.Type.EMAIL,
value, getRAModuleName(), regAbstractFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE,
String.format("Error adding email account with value "
+ "%s, to the case database for file %s [objId=%d]",
value, regAbstractFile.getName(), regAbstractFile.getId()), ex);
}
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL, bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
getRAModuleName(), value)); getRAModuleName(), value));
} }

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-2019 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* *
* Copyright 2012 42six Solutions. * Copyright 2012 42six Solutions.
* Contact: aebadirad <at> 42six <dot> com * Contact: aebadirad <at> 42six <dot> com
@ -54,7 +54,7 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -62,6 +62,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
@Messages({ @Messages({
"Progress_Message_Firefox_History=Firefox History", "Progress_Message_Firefox_History=Firefox History",
@ -858,7 +859,6 @@ class Firefox extends Extract {
} }
dataFound = true; dataFound = true;
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
int j = 0; int j = 0;
while (j < autofillProfilesFiles.size()) { while (j < autofillProfilesFiles.size()) {
@ -916,6 +916,19 @@ class Firefox extends Extract {
continue; continue;
} }
WebBrowserArtifactsHelper helper;
try {
// Helper to create web form address artifacts.
helper = new WebBrowserArtifactsHelper(
Case.getCurrentCaseThrows().getSleuthkitCase(),
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
profileFile
);
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "No case open, bailing.", ex); //NON-NLS
return;
}
for (JsonElement result : jAddressesArray) { for (JsonElement result : jAddressesArray) {
JsonObject address = result.getAsJsonObject(); JsonObject address = result.getAsJsonObject();
if (address == null) { if (address == null) {
@ -959,63 +972,9 @@ class Firefox extends Extract {
String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country ); String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
try { try {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); helper.addWebFormAddress(name, email, phoneNumber,
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON, mailingAddress, datetimeCreated, datetimeLastUsed, timesUsed);
RecentActivityExtracterModuleFactory.getModuleName(), } catch (TskCoreException | Blackboard.BlackboardException ex) {
name)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
RecentActivityExtracterModuleFactory.getModuleName(),
email)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
RecentActivityExtracterModuleFactory.getModuleName(),
phoneNumber)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
RecentActivityExtracterModuleFactory.getModuleName(),
mailingAddress)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
RecentActivityExtracterModuleFactory.getModuleName(),
datetimeCreated)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
RecentActivityExtracterModuleFactory.getModuleName(),
datetimeLastUsed)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
RecentActivityExtracterModuleFactory.getModuleName(),
timesUsed)); //NON-NLS
BlackboardArtifact bbart = profileFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS);
if (bbart != null) {
bbart.addAttributes(bbattributes);
bbartifacts.add(bbart);
}
// If an email address is found, create an account instance for it
if (email != null && !email.isEmpty()) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email, NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), profileFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating email account instance for '%s' from Firefox profiles file '%s' .",
email, profileFile.getName()), ex); //NON-NLS
}
}
// If a phone number is found, create an account instance for it
if (phoneNumber != null && !phoneNumber.isEmpty()) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, phoneNumber, NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), profileFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating phone number account instance for '%s' from Chrome profiles file '%s' .",
phoneNumber, profileFile.getName()), ex); //NON-NLS
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error while trying to insert Firefox Autofill profile artifact{0}", ex); //NON-NLS logger.log(Level.SEVERE, "Error while trying to insert Firefox Autofill profile artifact{0}", ex); //NON-NLS
this.addErrorMessage( this.addErrorMessage(
NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4", NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
@ -1024,8 +983,6 @@ class Firefox extends Extract {
} }
dbFile.delete(); dbFile.delete();
} }
postArtifacts(bbartifacts);
} }
/** /**