mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Implemented record parsing and testing for call logs, web bookmarks and contacts
This commit is contained in:
parent
8f7e5c44b2
commit
e30843c7c6
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 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.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template parse method for reports that make blackboard attributes from a
|
||||||
|
* single key value pairs.
|
||||||
|
*
|
||||||
|
* This parse implementation will create 1 artifact per XRY entity.
|
||||||
|
*/
|
||||||
|
abstract class AbstractSingleKeyValueParser implements XRYFileParser {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(AbstractSingleKeyValueParser.class.getName());
|
||||||
|
|
||||||
|
private static final char KEY_VALUE_DELIMITER = ':';
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException {
|
||||||
|
Path reportPath = reader.getReportPath();
|
||||||
|
logger.log(Level.INFO, String.format("INFO: Processing report at [ %s ]", reportPath.toString()));
|
||||||
|
|
||||||
|
while (reader.hasNextEntity()) {
|
||||||
|
String xryEntity = reader.nextEntity();
|
||||||
|
String[] xryLines = xryEntity.split("\n");
|
||||||
|
|
||||||
|
List<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
|
|
||||||
|
if (xryLines.length > 0) {
|
||||||
|
logger.log(Level.INFO, String.format("INFO: Processing [ %s ]", xryLines[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
String namespace = "";
|
||||||
|
for (int i = 1; i < xryLines.length; i++) {
|
||||||
|
String xryLine = xryLines[i];
|
||||||
|
|
||||||
|
if (isNamespace(xryLine)) {
|
||||||
|
logger.log(Level.INFO, String.format("INFO: Detected XRY "
|
||||||
|
+ "namespace keyword [ %s ]. Applying to all key value pairs following it.", xryLine));
|
||||||
|
namespace = xryLine.trim();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Find the XRY key on this line. Assume key is the value between
|
||||||
|
//the start of the line and the first delimiter.
|
||||||
|
int keyDelimiter = xryLine.indexOf(KEY_VALUE_DELIMITER);
|
||||||
|
if (keyDelimiter == -1) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Expected a key value "
|
||||||
|
+ "pair on this line (in brackets) [ %s ], but one was not detected."
|
||||||
|
+ " Here is the previous line (in brackets) [ %s ]. What does this key mean?", xryLine, xryLines[i - 1]));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String key = xryLine.substring(0, keyDelimiter).trim();
|
||||||
|
String value = xryLine.substring(keyDelimiter + 1).trim();
|
||||||
|
|
||||||
|
if (!isKey(key)) {
|
||||||
|
logger.log(Level.SEVERE, String.format("The following key, "
|
||||||
|
+ "value pair (in brackets, respectively) [ %s ], [ %s ] was not recognized. Discarding..."
|
||||||
|
+ " Here is the previous line [ %s ] for context. What is it?", key, value, xryLines[i - 1]));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
logger.log(Level.SEVERE, String.format("The following key "
|
||||||
|
+ "(in brackets) [ %s ] was recognized, but the value was empty. Discarding..."
|
||||||
|
+ " Here is the previous line for context [ %s ]. What does this mean?", key, xryLines[i - 1]));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlackboardAttribute attribute = makeAttribute(namespace, key, value);
|
||||||
|
//Returning null is temporary solution until we map out how we will deal with
|
||||||
|
//attributes we are currently ignoring.
|
||||||
|
if (attribute != null) {
|
||||||
|
attributes.add(makeAttribute(namespace, key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributes.size() > 0) {
|
||||||
|
makeArtifact(attributes, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the key candidate is a known key. A key candidate is a
|
||||||
|
* string literal that begins a line and is terminated by a semi-colon.
|
||||||
|
*
|
||||||
|
* Ex:
|
||||||
|
*
|
||||||
|
* Call Type : Missed
|
||||||
|
*
|
||||||
|
* "Call Type" would be the key candidate that was extracted.
|
||||||
|
*
|
||||||
|
* @param key Key to test. These keys are trimmed of whitespace only.
|
||||||
|
* @return Indication if this key can be processed.
|
||||||
|
*/
|
||||||
|
abstract boolean isKey(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the namespace candidate is a known namespace. A namespace
|
||||||
|
* candidate is a string literal that makes up an entire line.
|
||||||
|
*
|
||||||
|
* Ex:
|
||||||
|
*
|
||||||
|
* To Tel : +1245325
|
||||||
|
*
|
||||||
|
* "To" would be the candidate namespace that was extracted.
|
||||||
|
*
|
||||||
|
* @param nameSpace Namespace to test. Namespaces are trimmed of whitespace
|
||||||
|
* only.
|
||||||
|
* @return Indication if this namespace can be processed.
|
||||||
|
*/
|
||||||
|
abstract boolean isNamespace(String nameSpace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an attribute from the extracted key value pair.
|
||||||
|
*
|
||||||
|
* @param nameSpace The namespace of this key value pair.
|
||||||
|
* It will have been verified with isNamespace, otherwise it will be empty.
|
||||||
|
* @param key The key that was verified with isKey.
|
||||||
|
* @param value The value associated with that key.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
abstract BlackboardAttribute makeAttribute(String nameSpace, String key, String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes an artifact from the parsed attributes.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
abstract void makeArtifact(List<BlackboardAttribute> attributes, Content parent) throws TskCoreException;
|
||||||
|
|
||||||
|
}
|
165
Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java
Executable file
165
Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java
Executable file
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 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.LocalDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses XRY Calls files and creates artifacts.
|
||||||
|
*/
|
||||||
|
final class XRYCallsFileParser extends AbstractSingleKeyValueParser {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName());
|
||||||
|
|
||||||
|
//Human readable name of this parser.
|
||||||
|
private static final String PARSER_NAME = "XRY Calls";
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_TIME_PARSER
|
||||||
|
= DateTimeFormatter.ofPattern("M/d/y h:m:s [a][ z]");
|
||||||
|
|
||||||
|
private static final String INCOMING = "Incoming";
|
||||||
|
|
||||||
|
//All known XRY keys for call reports.
|
||||||
|
private static final Set<String> XRY_KEYS = new HashSet<String>() {
|
||||||
|
{
|
||||||
|
add("tel");
|
||||||
|
add("number");
|
||||||
|
add("call type");
|
||||||
|
add("name (matched)");
|
||||||
|
add("time");
|
||||||
|
add("duration");
|
||||||
|
add("storage");
|
||||||
|
add("index");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//All known XRY namespaces for call reports.
|
||||||
|
private static final Set<String> XRY_NAMESPACES = new HashSet<String>() {
|
||||||
|
{
|
||||||
|
add("to");
|
||||||
|
add("from");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isKey(String key) {
|
||||||
|
String normalizedKey = key.toLowerCase();
|
||||||
|
return XRY_KEYS.contains(normalizedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isNamespace(String nameSpace) {
|
||||||
|
String normalizedNamespace = nameSpace.toLowerCase();
|
||||||
|
return XRY_NAMESPACES.contains(normalizedNamespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
BlackboardAttribute makeAttribute(String nameSpace, String key, String value) {
|
||||||
|
String normalizedKey = key.toLowerCase();
|
||||||
|
String normalizedNamespace = nameSpace.toLowerCase();
|
||||||
|
|
||||||
|
switch (normalizedKey) {
|
||||||
|
case "time":
|
||||||
|
//Tranform value to epoch ms
|
||||||
|
String dateTime = removeDateTimeLocale(value);
|
||||||
|
String normalizedDateTime = dateTime.trim();
|
||||||
|
long dateTimeInEpoch = calculateMsSinceEpoch(normalizedDateTime);
|
||||||
|
return new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START, PARSER_NAME, dateTimeInEpoch);
|
||||||
|
case "duration":
|
||||||
|
//Ignore for now.
|
||||||
|
return null;
|
||||||
|
case "storage":
|
||||||
|
//Ignore for now.
|
||||||
|
return null;
|
||||||
|
case "index":
|
||||||
|
//Ignore for now.
|
||||||
|
return null;
|
||||||
|
case "tel":
|
||||||
|
//Apply the namespace
|
||||||
|
switch (normalizedNamespace) {
|
||||||
|
case "from":
|
||||||
|
return new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, PARSER_NAME, value);
|
||||||
|
default:
|
||||||
|
return new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, PARSER_NAME, value);
|
||||||
|
}
|
||||||
|
case "call type":
|
||||||
|
String normalizedValue = value.toLowerCase();
|
||||||
|
switch (normalizedValue) {
|
||||||
|
case "missed":
|
||||||
|
case "received":
|
||||||
|
return new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, PARSER_NAME, INCOMING);
|
||||||
|
case "dialed":
|
||||||
|
return null;
|
||||||
|
case "last dialed":
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
logger.log(Level.SEVERE, String.format("Call type (in brackets) [ %s ] not recognized.", value));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "number":
|
||||||
|
return new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, PARSER_NAME, value);
|
||||||
|
case "name (matched)":
|
||||||
|
return new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, PARSER_NAME, value);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(String.format("key [ %s ] was not recognized.", key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void makeArtifact(List<BlackboardAttribute> attributes, Content parent) throws TskCoreException {
|
||||||
|
//BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG);
|
||||||
|
//artifact.addAttributes(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the locale from the date time value.
|
||||||
|
*
|
||||||
|
* @param dateTime
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String removeDateTimeLocale(String dateTime) {
|
||||||
|
int index = dateTime.indexOf('(');
|
||||||
|
if (index == -1) {
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateTime.substring(0, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param dateTime
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private long calculateMsSinceEpoch(String dateTime) {
|
||||||
|
LocalDateTime localDateTime = LocalDateTime.parse(dateTime, DATE_TIME_PARSER);
|
||||||
|
//Assume dates have no offset.
|
||||||
|
return localDateTime.toInstant(ZoneOffset.UTC).toEpochMilli();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 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.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses XRY Contacts-Contacts files and creates artifacts.
|
||||||
|
*/
|
||||||
|
final class XRYContactsFileParser extends AbstractSingleKeyValueParser {
|
||||||
|
|
||||||
|
//Human readable name of this parser.
|
||||||
|
private static final String PARSER_NAME = "XRY Contacts";
|
||||||
|
|
||||||
|
//All of the known XRY keys for contacts.
|
||||||
|
private static final Set<String> XRY_KEYS = new HashSet<String>() {{
|
||||||
|
add("name");
|
||||||
|
add("tel");
|
||||||
|
add("storage");
|
||||||
|
}};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isKey(String key) {
|
||||||
|
String normalizedKey = key.toLowerCase();
|
||||||
|
return XRY_KEYS.contains(normalizedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isNamespace(String nameSpace) {
|
||||||
|
//No namespaces are currently known for this report type.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
BlackboardAttribute makeAttribute(String nameSpace, String key, String value) {
|
||||||
|
String normalizedKey = key.toLowerCase();
|
||||||
|
switch(normalizedKey) {
|
||||||
|
case "name":
|
||||||
|
return new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, PARSER_NAME, value);
|
||||||
|
case "tel":
|
||||||
|
return new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, PARSER_NAME, value);
|
||||||
|
case "storage":
|
||||||
|
//Ignore for now.
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(String.format("Key [ %s ] was not recognized", key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void makeArtifact(List<BlackboardAttribute> attributes, Content parent) throws TskCoreException {
|
||||||
|
//BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||||
|
//artifact.addAttributes(attributes);
|
||||||
|
}
|
||||||
|
}
|
46
Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileParser.java
Executable file
46
Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileParser.java
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 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.io.IOException;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for XRY file parsing.
|
||||||
|
*/
|
||||||
|
interface XRYFileParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses XRY entities and creates artifacts from the interpreted content.
|
||||||
|
*
|
||||||
|
* See XRYFileReader for more information on XRY entities. It is expected
|
||||||
|
* that implementations will create artifacts on the supplied Content
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @param reader Produces XRY entities from a given XRY file.
|
||||||
|
* @param parent Content object that will act as the source of the
|
||||||
|
* artifacts.
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates XRYFileParsers by report type.
|
||||||
|
*/
|
||||||
|
final class XRYFileParserFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the correct implementation of a XRYFileParser for the specified
|
||||||
|
* report type.
|
||||||
|
*
|
||||||
|
* It is assumed that the report type is supported, which means the client
|
||||||
|
* needs to have tested with supports beforehand. Otherwise, an
|
||||||
|
* IllegalArgumentException is thrown.
|
||||||
|
*
|
||||||
|
* @param reportType A supported XRY report type.
|
||||||
|
* @return A XRYFileParser with defined behavior for the report type.
|
||||||
|
* @throws IllegalArgumentException if the report type is not supported or
|
||||||
|
* is null. This is a misuse of the API. It is assumed that the report type
|
||||||
|
* has been tested with the supports method.
|
||||||
|
*/
|
||||||
|
public static XRYFileParser get(String reportType) {
|
||||||
|
if (reportType == null) {
|
||||||
|
throw new IllegalArgumentException("Report type cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reportType.toLowerCase()) {
|
||||||
|
case "calls":
|
||||||
|
return new XRYCallsFileParser();
|
||||||
|
case "contacts/contacts":
|
||||||
|
return new XRYContactsFileParser();
|
||||||
|
case "device/general information":
|
||||||
|
return new XRYDeviceGenInfoFileParser();
|
||||||
|
case "messages/sms":
|
||||||
|
return new XRYMessagesFileParser();
|
||||||
|
case "web/bookmarks":
|
||||||
|
return new XRYWebBookmarksFileParser();
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(reportType + " not recognized.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if a XRYFileParser implementation exists for the report type.
|
||||||
|
*
|
||||||
|
* @param reportType Report type to test.
|
||||||
|
* @return Indication if the report type can be parsed.
|
||||||
|
*/
|
||||||
|
public static boolean supports(String reportType) {
|
||||||
|
try {
|
||||||
|
//Attempt a get.
|
||||||
|
get(reportType);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Prevent direct instantiation
|
||||||
|
private XRYFileParserFactory() {
|
||||||
|
}
|
||||||
|
}
|
@ -45,36 +45,43 @@ import org.apache.commons.io.FilenameUtils;
|
|||||||
* From
|
* From
|
||||||
* Tel: 12345678
|
* Tel: 12345678
|
||||||
*/
|
*/
|
||||||
public final class XRYFileReader implements AutoCloseable {
|
final class XRYFileReader implements AutoCloseable {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(XRYFileReader.class.getName());
|
private static final Logger logger = Logger.getLogger(XRYFileReader.class.getName());
|
||||||
|
|
||||||
//Assume UTF_16LE
|
//Assume UTF_16LE
|
||||||
private static final Charset CHARSET = StandardCharsets.UTF_16LE;
|
private static final Charset CHARSET = StandardCharsets.UTF_16LE;
|
||||||
|
|
||||||
//Assume TXT extension
|
|
||||||
private static final String EXTENSION = "txt";
|
|
||||||
|
|
||||||
//Assume 0xFFFE is the BOM
|
|
||||||
private static final int[] BOM = {0xFF, 0xFE};
|
|
||||||
|
|
||||||
//Assume all XRY reports have the type on the 3rd line.
|
//Assume all XRY reports have the type on the 3rd line.
|
||||||
private static final int LINE_WITH_REPORT_TYPE = 3;
|
private static final int LINE_WITH_REPORT_TYPE = 3;
|
||||||
|
|
||||||
//Assume all headers are 5 lines in length.
|
//Assume all headers are 5 lines in length.
|
||||||
private static final int HEADER_LENGTH_IN_LINES = 5;
|
private static final int HEADER_LENGTH_IN_LINES = 5;
|
||||||
|
|
||||||
|
//Assume TXT extension
|
||||||
|
private static final String EXTENSION = "txt";
|
||||||
|
|
||||||
|
//Assume 0xFFFE is the BOM
|
||||||
|
private static final int[] BOM = {0xFF, 0xFE};
|
||||||
|
|
||||||
|
//Entity to be consumed during file iteration.
|
||||||
|
private final StringBuilder xryEntity;
|
||||||
|
|
||||||
//Underlying reader for the xry file.
|
//Underlying reader for the xry file.
|
||||||
private final BufferedReader reader;
|
private final BufferedReader reader;
|
||||||
|
|
||||||
private final StringBuilder xryEntity;
|
//Reference to the original xry file.
|
||||||
|
private final Path xryFilePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an XRYFileReader. As part of construction, the XRY file is opened
|
* Creates an XRYFileReader. As part of construction, the XRY file is opened
|
||||||
* and the reader is advanced past the header. This leaves the reader
|
* and the reader is advanced past the header. This leaves the reader
|
||||||
* positioned at the start of the first XRY entity.
|
* positioned at the start of the first XRY entity.
|
||||||
*
|
*
|
||||||
* The file is assumed to be encoded in UTF-16LE.
|
* The file is assumed to be encoded in UTF-16LE and is NOT verified to be
|
||||||
|
* an XRY file before reading. It is expected that the isXRYFile function
|
||||||
|
* has been called on the path beforehand. Otherwise, the behavior is
|
||||||
|
* undefined.
|
||||||
*
|
*
|
||||||
* @param xryFile XRY file to read. It is assumed that the caller has read
|
* @param xryFile XRY file to read. It is assumed that the caller has read
|
||||||
* access to the path.
|
* access to the path.
|
||||||
@ -82,6 +89,7 @@ public final class XRYFileReader implements AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
public XRYFileReader(Path xryFile) throws IOException {
|
public XRYFileReader(Path xryFile) throws IOException {
|
||||||
reader = Files.newBufferedReader(xryFile, CHARSET);
|
reader = Files.newBufferedReader(xryFile, CHARSET);
|
||||||
|
xryFilePath = xryFile;
|
||||||
|
|
||||||
//Advance the reader to the start of the first XRY entity.
|
//Advance the reader to the start of the first XRY entity.
|
||||||
for (int i = 0; i < HEADER_LENGTH_IN_LINES; i++) {
|
for (int i = 0; i < HEADER_LENGTH_IN_LINES; i++) {
|
||||||
@ -91,6 +99,35 @@ public final class XRYFileReader implements AutoCloseable {
|
|||||||
xryEntity = new StringBuilder();
|
xryEntity = new StringBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the report type from the XRY file.
|
||||||
|
*
|
||||||
|
* @return The XRY report type
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
* @throws IllegalArgumentExcepton If the XRY file does not have a report
|
||||||
|
* type. This is a misuse of the API. The validity of the Path should have
|
||||||
|
* been checked with isXRYFile before creating an XRYFileReader.
|
||||||
|
*/
|
||||||
|
public String getReportType() throws IOException {
|
||||||
|
Optional<String> reportType = getType(xryFilePath);
|
||||||
|
if (reportType.isPresent()) {
|
||||||
|
return reportType.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(xryFilePath.toString() + " does not "
|
||||||
|
+ "have a report type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw path of the XRY report file.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public Path getReportPath() throws IOException {
|
||||||
|
return xryFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advances the reader until a valid XRY entity is detected or EOF is
|
* Advances the reader until a valid XRY entity is detected or EOF is
|
||||||
* reached.
|
* reached.
|
||||||
@ -113,7 +150,7 @@ public final class XRYFileReader implements AutoCloseable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
xryEntity.append(line).append("\n");
|
xryEntity.append(line).append('\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +175,23 @@ public final class XRYFileReader implements AutoCloseable {
|
|||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peek at the next XRY entity without consuming it.
|
||||||
|
* If there are not more XRY entities left, an exception is thrown.
|
||||||
|
*
|
||||||
|
* @return A non-empty XRY entity.
|
||||||
|
* @throws IOException
|
||||||
|
* @throws NoSuchElementException if there are no more XRY entities to
|
||||||
|
* read.
|
||||||
|
*/
|
||||||
|
public String peek() throws IOException {
|
||||||
|
if(hasNextEntity()) {
|
||||||
|
return xryEntity.toString();
|
||||||
|
} else {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes any file handles this reader may have open.
|
* Closes any file handles this reader may have open.
|
||||||
|
@ -30,7 +30,7 @@ import java.util.stream.Stream;
|
|||||||
/**
|
/**
|
||||||
* Extracts XRY files and (optionally) non-XRY files from a XRY (Report) folder.
|
* Extracts XRY files and (optionally) non-XRY files from a XRY (Report) folder.
|
||||||
*/
|
*/
|
||||||
public final class XRYFolder {
|
final class XRYFolder {
|
||||||
|
|
||||||
//Depth that will contain XRY files. All XRY files will be immediate
|
//Depth that will contain XRY files. All XRY files will be immediate
|
||||||
//children of their parent folder.
|
//children of their parent folder.
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 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.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses XRY Web-Bookmark files and creates artifacts.
|
||||||
|
*/
|
||||||
|
final class XRYWebBookmarksFileParser extends AbstractSingleKeyValueParser {
|
||||||
|
|
||||||
|
//Human readable name of this parser.
|
||||||
|
private static final String PARSER_NAME = "XRY Web Bookmarks";
|
||||||
|
|
||||||
|
//All known XRY keys for web bookmarks.
|
||||||
|
private static final Map<String, BlackboardAttribute.ATTRIBUTE_TYPE> KEY_TO_TYPE
|
||||||
|
= new HashMap<String, BlackboardAttribute.ATTRIBUTE_TYPE>() {
|
||||||
|
{
|
||||||
|
put("web address", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL);
|
||||||
|
put("domain", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isKey(String key) {
|
||||||
|
String normalizedKey = key.toLowerCase();
|
||||||
|
return KEY_TO_TYPE.containsKey(normalizedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isNamespace(String nameSpace) {
|
||||||
|
//No known namespaces for web reports.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
BlackboardAttribute makeAttribute(String nameSpace, String key, String value) {
|
||||||
|
String normalizedKey = key.toLowerCase();
|
||||||
|
return new BlackboardAttribute(KEY_TO_TYPE.get(normalizedKey), PARSER_NAME, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void makeArtifact(List<BlackboardAttribute> attributes, Content parent) throws TskCoreException {
|
||||||
|
//BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.WEB_BOOKMARK);
|
||||||
|
//artifact.addAttributes(attributes);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user