Merge branch 'develop' into data-src-summaries

This commit is contained in:
Richard Cordovano 2020-08-13 15:29:37 -04:00
commit ec3cb76de0
22 changed files with 1518 additions and 191 deletions

View File

@ -36,3 +36,4 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
DataSourceSummaryCountsPanel.jLabel1.text=Results by Type
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run

View File

@ -61,9 +61,6 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
DataSourceSummaryCountsPanel.jLabel1.text=Results by Type
DataSourceSummaryDialog.countsTab.title=Counts
DataSourceSummaryDialog.detailsTab.title=Details
DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History
DataSourceSummaryDialog.window.title=Data Sources Summary
DataSourceSummaryNode.column.dataSourceName.header=Data Source Name
DataSourceSummaryNode.column.files.header=Files
@ -72,4 +69,14 @@ DataSourceSummaryNode.column.status.header=Ingest Status
DataSourceSummaryNode.column.tags.header=Tags
DataSourceSummaryNode.column.type.header=Type
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
DataSourceSummaryTabbedPane_countsTab_title=Counts
DataSourceSummaryTabbedPane_detailsTab_title=Details
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run
DataSourceSummaryUserActivityPanel_tab_title=User Activity
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program
ViewSummaryInformationAction.name.text=View Summary Information

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019 - 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,8 +18,11 @@
*/
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
@ -27,13 +30,20 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.DataSource;
/**
@ -193,6 +203,31 @@ final class DataSourceInfoUtilities {
return getBaseQueryResult(query, handler, errorMessage);
}
/**
* Generates a result set handler that will return a map of string to long.
*
* @param keyParam The named parameter in the result set representing the
* key.
* @param valueParam The named parameter in the result set representing the
* value.
*
* @return The result set handler to generate the map of string to long.
*/
private static ResultSetHandler<LinkedHashMap<String, Long>> getStringLongResultSetHandler(String keyParam, String valueParam) {
return (resultSet) -> {
LinkedHashMap<String, Long> toRet = new LinkedHashMap<>();
while (resultSet.next()) {
try {
toRet.put(resultSet.getString(keyParam), resultSet.getLong(valueParam));
} catch (SQLException ex) {
logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex);
}
}
return toRet;
};
}
/**
* Retrieves counts for each artifact type in a data source.
*
@ -215,24 +250,311 @@ final class DataSourceInfoUtilities {
+ " WHERE bba.data_source_obj_id =" + selectedDataSource.getId()
+ " GROUP BY bbt.display_name";
ResultSetHandler<Map<String, Long>> handler = (resultSet) -> {
Map<String, Long> toRet = new HashMap<>();
while (resultSet.next()) {
String errorMessage = "Unable to get artifact type counts; returning null.";
return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage);
}
/**
* Describes a result of a program run on a datasource.
*/
static class TopProgramsResult {
private final String programName;
private final String programPath;
private final Long runTimes;
private final Date lastRun;
/**
* Main constructor.
*
* @param programName The name of the program.
* @param programPath The path of the program.
* @param runTimes The number of runs.
*/
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
this.programName = programName;
this.programPath = programPath;
this.runTimes = runTimes;
this.lastRun = lastRun;
}
/**
* @return The name of the program
*/
String getProgramName() {
return programName;
}
/**
* @return The path of the program.
*/
String getProgramPath() {
return programPath;
}
/**
* @return The number of run times or null if not present.
*/
Long getRunTimes() {
return runTimes;
}
/**
* @return The last time the program was run or null if not present.
*/
public Date getLastRun() {
return lastRun;
}
}
/**
* A SQL join type.
*/
private enum JoinType {
LEFT,
RIGHT,
INNER,
OUTER
}
/**
* A blackboard attribute value column.
*/
private enum AttributeColumn {
value_text,
value_int32,
value_int64
}
/**
* The suffix joined to a key name for use as an identifier of a query.
*/
private static final String QUERY_SUFFIX = "_query";
/**
* Creates a sql statement querying the blackboard attributes table for a
* particular attribute type and returning a specified value. That query
* also joins with the blackboard artifact table.
*
* @param joinType The type of join statement to create.
* @param attributeColumn The blackboard attribute column that should be
* returned.
* @param attrType The attribute type to query for.
* @param keyName The aliased name of the attribute to return. This
* is also used to calculate the alias of the query
* same as getFullKey.
* @param bbaName The blackboard artifact table alias.
*
* @return The generated sql statement.
*/
private static String getAttributeJoin(JoinType joinType, AttributeColumn attributeColumn, ATTRIBUTE_TYPE attrType, String keyName, String bbaName) {
String queryName = keyName + QUERY_SUFFIX;
String innerQueryName = "inner_attribute_" + queryName;
return "\n" + joinType + " JOIN (\n"
+ " SELECT \n"
+ " " + innerQueryName + ".artifact_id,\n"
+ " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n"
+ " FROM blackboard_attributes " + innerQueryName + "\n"
+ " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n"
+ ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n";
}
/**
* Given a column key, creates the full name for the column key.
*
* @param key The column key.
*
* @return The full identifier for the column key.
*/
private static String getFullKey(String key) {
return key + QUERY_SUFFIX + "." + key;
}
/**
* Constructs a SQL 'where' statement from a list of clauses and puts
* parenthesis around each clause.
*
* @param clauses The clauses
*
* @return The generated 'where' statement.
*/
private static String getWhereString(List<String> clauses) {
if (clauses.isEmpty()) {
return "";
}
List<String> parenthesized = clauses.stream()
.map(c -> "(" + c + ")")
.collect(Collectors.toList());
return "\nWHERE " + String.join("\n AND ", parenthesized) + "\n";
}
/**
* Generates a [column] LIKE sql clause.
*
* @param column The column identifier.
* @param likeString The string that will be used as column comparison.
* @param isLike if false, the statement becomes NOT LIKE.
*
* @return The generated statement.
*/
private static String getLikeClause(String column, String likeString, boolean isLike) {
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
}
/**
* Retrieves a list of the top programs used on the data source. Currently
* determines this based off of which prefetch results return the highest
* count.
*
* @param dataSource The data source.
* @param count The number of programs to return.
*
* @return
*/
static List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) {
if (dataSource == null || count <= 0) {
return Collections.emptyList();
}
// ntosboot should be ignored
final String ntosBootIdentifier = "NTOSBOOT";
// programs in windows directory to be ignored
final String windowsDir = "/WINDOWS%";
final String nameParam = "name";
final String pathParam = "path";
final String runCountParam = "run_count";
final String lastRunParam = "last_run";
String bbaQuery = "bba";
final String query = "SELECT\n"
+ " " + getFullKey(nameParam) + " AS " + nameParam + ",\n"
+ " " + getFullKey(pathParam) + " AS " + pathParam + ",\n"
+ " MAX(" + getFullKey(runCountParam) + ") AS " + runCountParam + ",\n"
+ " MAX(" + getFullKey(lastRunParam) + ") AS " + lastRunParam + "\n"
+ "FROM blackboard_artifacts " + bbaQuery + "\n"
+ getAttributeJoin(JoinType.INNER, AttributeColumn.value_text, ATTRIBUTE_TYPE.TSK_PROG_NAME, nameParam, bbaQuery)
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_text, ATTRIBUTE_TYPE.TSK_PATH, pathParam, bbaQuery)
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int32, ATTRIBUTE_TYPE.TSK_COUNT, runCountParam, bbaQuery)
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int64, ATTRIBUTE_TYPE.TSK_DATETIME, lastRunParam, bbaQuery)
+ getWhereString(Arrays.asList(
bbaQuery + ".artifact_type_id = " + ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(),
bbaQuery + ".data_source_obj_id = " + dataSource.getId(),
// exclude ntosBootIdentifier from results
getLikeClause(getFullKey(nameParam), ntosBootIdentifier, false),
// exclude windows directory items from results
getFullKey(pathParam) + " IS NULL OR " + getLikeClause(getFullKey(pathParam), windowsDir, false)
))
+ "GROUP BY " + getFullKey(nameParam) + ", " + getFullKey(pathParam) + "\n"
+ "ORDER BY \n"
+ " MAX(" + getFullKey(runCountParam) + ") DESC,\n"
+ " MAX(" + getFullKey(lastRunParam) + ") DESC,\n"
+ " " + getFullKey(nameParam) + " ASC";
final String errorMessage = "Unable to get top program results; returning null.";
ResultSetHandler<List<TopProgramsResult>> handler = (resultSet) -> {
List<TopProgramsResult> progResults = new ArrayList<>();
boolean quitAtCount = false;
while (resultSet.next() && (!quitAtCount || progResults.size() < count)) {
try {
toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam));
long lastRunEpoch = resultSet.getLong(lastRunParam);
Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000);
Long runCount = resultSet.getLong(runCountParam);
if (resultSet.wasNull()) {
runCount = null;
}
if (lastRun != null || runCount != null) {
quitAtCount = true;
}
progResults.add(new TopProgramsResult(
resultSet.getString(nameParam),
resultSet.getString(pathParam),
runCount,
lastRun));
} catch (SQLException ex) {
logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex);
logger.log(Level.WARNING, "Failed to get a top program result from the result set.", ex);
}
}
return toRet;
return progResults;
};
String errorMessage = "Unable to get artifact type counts; returning null.";
return getBaseQueryResult(query, handler, errorMessage);
}
/**
* Functions that determine the folder name of a list of path elements. If
* not matched, function returns null.
*/
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
// handle Program Files and Program Files (x86) - if true, return the next folder
(pathList) -> {
if (pathList.size() < 2) {
return null;
}
String rootParent = pathList.get(0).toUpperCase();
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
return pathList.get(1);
} else {
return null;
}
},
// if there is a folder named "APPLICATION DATA" or "APPDATA"
(pathList) -> {
for (String pathEl : pathList) {
String uppered = pathEl.toUpperCase();
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
return "AppData";
}
}
return null;
}
);
/**
* Determines a short folder name if any. Otherwise, returns empty string.
*
* @param strPath The string path.
*
* @return The short folder name or empty string if not found.
*/
static String getShortFolderName(String strPath, String applicationName) {
if (strPath == null) {
return "";
}
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
File file = new File(strPath);
while (file != null && StringUtils.isNotBlank(file.getName())) {
pathEls.add(file.getName());
file = file.getParentFile();
}
Collections.reverse(pathEls);
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
String result = matchEntry.apply(pathEls);
if (StringUtils.isNotBlank(result)) {
return result;
}
}
return "";
}
/**
* Generates a string which is a concatenation of the value received from
* the result set.

View File

@ -49,10 +49,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
* datasource.
*/
@Messages({
"DataSourceSummaryDialog.window.title=Data Sources Summary",
"DataSourceSummaryDialog.countsTab.title=Counts",
"DataSourceSummaryDialog.detailsTab.title=Details",
"DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History"
"DataSourceSummaryDialog.window.title=Data Sources Summary"
})
DataSourceSummaryDialog(Frame owner) {
super(owner, Bundle.DataSourceSummaryDialog_window_title(), true);

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import javax.swing.JTabbedPane;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
import org.sleuthkit.datamodel.DataSource;
@ -27,13 +28,20 @@ import org.sleuthkit.datamodel.DataSource;
* DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and
* IngestJobInfoPanel.
*/
@Messages({
"DataSourceSummaryTabbedPane_countsTab_title=Counts",
"DataSourceSummaryTabbedPane_detailsTab_title=Details",
"DataSourceSummaryTabbedPane_userActivityTab_title=User Activity",
"DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History"
})
public class DataSourceSummaryTabbedPane extends JTabbedPane {
private static final long serialVersionUID = 1L;
private final DataSourceSummaryCountsPanel countsPanel;
private final DataSourceSummaryDetailsPanel detailsPanel;
private final IngestJobInfoPanel ingestHistoryPanel;
private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel();
private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel();
private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel();
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
private DataSource dataSource = null;
@ -41,13 +49,11 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
* Constructs a tabbed pane showing the summary of a data source.
*/
public DataSourceSummaryTabbedPane() {
countsPanel = new DataSourceSummaryCountsPanel();
detailsPanel = new DataSourceSummaryDetailsPanel();
ingestHistoryPanel = new IngestJobInfoPanel();
addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel);
addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel);
addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel);
addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel);
addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel);
addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel);
addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel);
}
/**
@ -69,6 +75,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
detailsPanel.setDataSource(dataSource);
countsPanel.setDataSource(dataSource);
userActivityPanel.setDataSource(dataSource);
ingestHistoryPanel.setDataSource(dataSource);
}
}

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="programsRunLabel" min="-2" pref="155" max="-2" attributes="0"/>
<Component id="topProgramsScrollPane" min="-2" pref="460" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="128" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="programsRunLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="topProgramsScrollPane" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="programsRunLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryUserActivityPanel.programsRunLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Container class="javax.swing.JScrollPane" name="topProgramsScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[750, 187]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="topProgramsTable">
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,291 @@
/*
* 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.casemodule.datasourcesummary;
import java.awt.Component;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.DataSource;
/**
* A panel to display user activity.
*/
@Messages({
"DataSourceSummaryUserActivityPanel_tab_title=User Activity",
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program",
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder",
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times",
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run"
})
public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
private static final int TOP_PROGS_COUNT = 10;
private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer();
static {
RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT);
}
private DataSource dataSource;
/**
* Creates new form DataSourceUserActivityPanel
*/
public DataSourceSummaryUserActivityPanel() {
initComponents();
topProgramsTable.getTableHeader().setReorderingAllowed(false);
}
/**
* The datasource currently used as the model in this panel.
*
* @return The datasource currently being used as the model in this panel.
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* Sets datasource to visualize in the panel.
*
* @param dataSource The datasource to use in this panel.
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
if (dataSource == null || !Case.isCaseOpen()) {
updateTopPrograms(new TopProgramsModel(null));
} else {
updateTopPrograms(getTopProgramsModel(dataSource));
}
}
/**
* Updates the Top Programs Table in the gui.
*
* @param data The data in Object[][] form to be used by the
* DefaultTableModel.
*/
private void updateTopPrograms(TopProgramsModel model) {
topProgramsTable.setModel(model);
topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(250);
topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER);
topProgramsTable.getColumnModel().getColumn(1).setPreferredWidth(150);
topProgramsTable.getColumnModel().getColumn(2).setCellRenderer(RIGHT_ALIGNED_RENDERER);
topProgramsTable.getColumnModel().getColumn(2).setPreferredWidth(80);
topProgramsTable.getColumnModel().getColumn(3).setPreferredWidth(150);
topProgramsScrollPane.getVerticalScrollBar().setValue(0);
this.repaint();
}
/**
* The counts of top programs run.
*
* @param selectedDataSource The DataSource.
*
* @return The JTable data model of counts of program runs.
*/
private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) {
List<DataSourceInfoUtilities.TopProgramsResult> topProgramList
= DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT);
if (topProgramList == null) {
return new TopProgramsModel(null);
} else {
return new TopProgramsModel(topProgramList);
}
}
/**
* A POJO defining the values present in the name cell. Defines the name as
* well as the path for the tooltip.
*/
private static class ProgramNameCellValue {
private final String programName;
private final String programPath;
ProgramNameCellValue(String programName, String programPath) {
this.programName = programName;
this.programPath = programPath;
}
@Override
public String toString() {
// override so that the value in the cell reads as programName
return programName;
}
/**
* @return The program name.
*/
String getProgramName() {
return programName;
}
/**
* @return The path of the program.
*/
String getProgramPath() {
return programPath;
}
}
/**
* Defines a cell renderer for the first cell rendering the name as the text
* and path as the tooltip.
*/
private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(
JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {
JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (value instanceof ProgramNameCellValue) {
ProgramNameCellValue cellValue = (ProgramNameCellValue) value;
c.setToolTipText(cellValue.getProgramPath());
}
return c;
}
};
/**
* Defines the table model for a JTable of the programs. Accepts a list of
* TopProgramsResult objects as rows data source.
*/
private static class TopProgramsModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
// column headers for artifact counts table
private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(),
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(),
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(),
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header()
};
private final List<DataSourceInfoUtilities.TopProgramsResult> programResults;
/**
* Main constructor.
*
* @param programResults The results to display.
*/
TopProgramsModel(List<DataSourceInfoUtilities.TopProgramsResult> programResults) {
this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults);
}
@Override
public String getColumnName(int column) {
return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column];
}
@Override
public int getRowCount() {
return programResults.size();
}
@Override
public int getColumnCount() {
return TOP_PROGS_COLUMN_HEADERS.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= programResults.size()) {
return null;
}
DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex);
switch (columnIndex) {
case 0:
return new ProgramNameCellValue(result.getProgramName(), result.getProgramPath());
case 1:
return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath(), result.getProgramName());
case 2:
return result.getRunTimes();
case 3:
return result.getLastRun() == null ? null : DATETIME_FORMAT.format(result.getLastRun());
default:
return null;
}
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JLabel programsRunLabel = new javax.swing.JLabel();
topProgramsScrollPane = new javax.swing.JScrollPane();
topProgramsTable = new javax.swing.JTable();
org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N
topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(750, 187));
topProgramsScrollPane.setViewportView(topProgramsTable);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 460, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(128, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(programsRunLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane topProgramsScrollPane;
private javax.swing.JTable topProgramsTable;
// End of variables declaration//GEN-END:variables
}

View File

@ -35,8 +35,12 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Node to represent an Account Device Instance in the CVT
@ -49,6 +53,8 @@ final class AccountDeviceInstanceNode extends AbstractNode {
private final CommunicationsManager commsManager;
private final Account account;
private static final BlackboardAttribute.Type NAME_ATTRIBUTE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_NAME.getTypeID()));
AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) {
super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager));
this.accountDeviceInstanceKey = accountDeviceInstanceKey;
@ -122,15 +128,17 @@ final class AccountDeviceInstanceNode extends AbstractNode {
@Override
public String getShortDescription() {
List<PersonaAccount> personaList;
List<BlackboardArtifact> contactArtifactList;
try {
personaList = CVTPersonaCache.getPersonaAccounts(account);
contactArtifactList = ContactCache.getContacts(account);
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex);
return getDisplayName();
}
String personaName;
if (!personaList.isEmpty()) {
if (personaList != null && !personaList.isEmpty()) {
personaName = personaList.get(0).getPersona().getName();
if (personaList.size() > 1) {
personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size()));
@ -139,6 +147,19 @@ final class AccountDeviceInstanceNode extends AbstractNode {
personaName = "None";
}
return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName);
String contactName = getDisplayName();
if (contactArtifactList != null && !contactArtifactList.isEmpty()) {
try {
BlackboardArtifact contactArtifact = contactArtifactList.get(0);
BlackboardAttribute attribute = contactArtifact.getAttribute(NAME_ATTRIBUTE);
if (attribute != null) {
contactName = attribute.getValueString();
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Failed to retrive name attribute from contact artifact.", ex);
}
}
return Bundle.AccountInstanceNode_Tooltip_Template(contactName, personaName);
}
}

View File

@ -0,0 +1,166 @@
/*
* 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.communications;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A singleton cache of the Contact artifacts for accounts. The map of account
* unique ids to list of contact artifacts is stored in a LoadingCache which
* expires after 10 of non-use.
*
*/
final class ContactCache {
private static final Logger logger = Logger.getLogger(ContactCache.class.getName());
private static ContactCache instance;
private final LoadingCache<String, Map<String, List<BlackboardArtifact>>> accountMap;
/**
* Returns the list of Contacts for the given Account.
*
* @param account Account instance.
*
* @return List of TSK_CONTACT artifacts that references the given Account.
* An empty list is returned if no contacts are found.
*
* @throws ExecutionException
*/
static synchronized List<BlackboardArtifact> getContacts(Account account) throws ExecutionException {
return getInstance().accountMap.get("realMap").get(account.getTypeSpecificID());
}
/**
* Force the cache to invalidate all entries.
*/
static synchronized void invalidateCache() {
getInstance().accountMap.invalidateAll();
}
/**
* Construct a new instance.
*/
private ContactCache() {
accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build(
new CacheLoader<String, Map<String, List<BlackboardArtifact>>>() {
@Override
public Map<String, List<BlackboardArtifact>> load(String key) {
try {
return buildMap();
} catch (SQLException | TskCoreException ex) {
logger.log(Level.WARNING, "Failed to build account to contact map", ex);
}
return new HashMap<>(); // Return an empty map if there is an exception to avoid NPE and continual trying.
}
});
PropertyChangeListener ingestListener = pce -> {
String eventType = pce.getPropertyName();
if (eventType.equals(DATA_ADDED.toString())) {
ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) {
invalidateCache();
}
}
};
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent event) -> {
if (event.getNewValue() == null) {
invalidateCache();
}
});
IngestManager.getInstance().addIngestModuleEventListener(EnumSet.of(DATA_ADDED), ingestListener);
}
/**
* Returns the singleton instance of the cache object.
*
* @return AccountCache instance.
*/
private static synchronized ContactCache getInstance() {
if (instance == null) {
instance = new ContactCache();
}
return instance;
}
/**
* Builds the map of account IDs to contacts that reference them.
*
* @return A map of account IDs to contact artifacts.
*
* @throws TskCoreException
* @throws SQLException
*/
private Map<String, List<BlackboardArtifact>> buildMap() throws TskCoreException, SQLException {
Map<String, List<BlackboardArtifact>> acctMap = new HashMap<>();
List<BlackboardArtifact> contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
for (BlackboardArtifact contactArtifact : contactList) {
List<BlackboardAttribute> contactAttributes = contactArtifact.getAttributes();
for (BlackboardAttribute attribute : contactAttributes) {
String typeName = attribute.getAttributeType().getTypeName();
if (typeName.startsWith("TSK_EMAIL")
|| typeName.startsWith("TSK_PHONE")
|| typeName.startsWith("TSK_NAME")
|| typeName.startsWith("TSK_ID")) {
String accountID = attribute.getValueString();
List<BlackboardArtifact> artifactList = acctMap.getOrDefault(accountID, new ArrayList<>());
acctMap.put(accountID, artifactList);
if (!artifactList.contains(contactArtifact)) {
artifactList.add(contactArtifact);
}
}
}
}
return acctMap;
}
}

View File

@ -26,6 +26,9 @@ CallLogArtifactViewer_label_from=From
CallLogArtifactViewer_label_to=To
CallLogArtifactViewer_suffix_local=(Local)
CallLogArtifactViewer_value_unknown=Unknown
#{0} - contact name
CommunicationArtifactViewerHelper_contact_label=Contact: {0}
CommunicationArtifactViewerHelper_contact_label_unknown=Unknown
CommunicationArtifactViewerHelper_menuitem_copy=Copy
CommunicationArtifactViewerHelper_persona_button_create=Create
CommunicationArtifactViewerHelper_persona_button_view=View
@ -61,6 +64,7 @@ DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
MessageAccountPanel_button_create_label=Create
MessageAccountPanel_button_view_label=View
MessageAccountPanel_contact_label=Contact:
MessageAccountPanel_no_matches=No matches found.
MessageAccountPanel_persona_label=Persona:
MessageAccountPanel_unknown_label=Unknown

View File

@ -36,6 +36,7 @@ import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.guiutils.ContactCache;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
@ -66,7 +67,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
private GridBagLayout m_gridBagLayout = new GridBagLayout();
private GridBagConstraints m_constraints = new GridBagConstraints();
private PersonaAccountFetcher currentAccountFetcher = null;
/**
@ -106,9 +107,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
// update the view with the call log data
if (callLogViewData != null) {
List<AccountPersonaSearcherData> personaSearchDataList = updateView(callLogViewData);
if(!personaSearchDataList.isEmpty()) {
if (!personaSearchDataList.isEmpty()) {
currentAccountFetcher = new PersonaAccountFetcher(artifact, personaSearchDataList, this);
currentAccountFetcher.execute();
currentAccountFetcher.execute();
} else {
currentAccountFetcher = null;
}
@ -143,6 +144,8 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
String fromAccountIdentifier = null;
String toAccountIdentifier = null;
List<String> otherParties = null;
List<String> toContactNames = null;
List<String> fromContactNames = null;
Content dataSource = artifact.getDataSource();
String deviceId = ((DataSource) dataSource).getDeviceId();
@ -186,6 +189,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
String fromAccountAttrValue = fromAccountAttr.getValueString();
if (fromAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
fromAccountIdentifier = fromAccountAttrValue;
fromContactNames = ContactCache.getContactNameList(fromAccountIdentifier);
}
}
@ -195,6 +199,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
String toAccountAttrValue = StringUtils.trim(numbers[0]);
if (toAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
toAccountIdentifier = toAccountAttrValue;
toContactNames = ContactCache.getContactNameList(toAccountIdentifier);
}
// if more than one To address, then stick the rest of them in the
@ -228,6 +233,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
}
callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
callLogViewData.setFromContactNameList(fromContactNames);
callLogViewData.setToContactNameList(toContactNames);
}
return callLogViewData;
@ -237,9 +245,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
* Extract the call time and duration from the artifact and saves in the
* CallLogViewData.
*
* @param artifact Call log artifact.
* @param artifact Call log artifact.
* @param callLogViewData CallLogViewData object to save the time & duration
* in.
* in.
*
* @throws TskCoreException
*/
@ -290,7 +298,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
* Update the viewer with the call log data.
*
* @param callLogViewData Call log data to update the view with.
*
*
* @return List of AccountPersonaSearcherData objects.
*/
@NbBundle.Messages({
@ -312,8 +320,13 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
List<String> contactNames = callLogViewData.getFromContactNameList();
for (String name : contactNames) {
CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
}
// show persona
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
} else {
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
}
@ -324,8 +337,13 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
List<String> contactNames = callLogViewData.getToContactNameList();
for (String name : contactNames) {
CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
}
dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
} else {
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
}
@ -335,7 +353,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, otherParty);
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty));
dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty));
}
updateMetadataView(callLogViewData);
@ -353,7 +371,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
this.setLayout(m_gridBagLayout);
this.revalidate();
this.repaint();
return dataList;
}
@ -441,7 +459,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
* local account, if it is known. If it is, it appends a "(Local)" suffix to
* account display string.
*
* @param accountIdentifier Account identifier to check.
* @param accountIdentifier Account identifier to check.
* @param callLogViewDataNew Call log data which may have the lock account.
*
* @return Account string to display.
@ -474,7 +492,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
private void resetComponent() {
// cancel any outstanding persona searching threads.
if(currentAccountFetcher != null && !currentAccountFetcher.isDone()) {
if (currentAccountFetcher != null && !currentAccountFetcher.isDone()) {
currentAccountFetcher.cancel(true);
currentAccountFetcher = null;
}

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -47,11 +48,14 @@ final class CallLogViewData {
private String dataSourceName = null;
private List<String> toContactNameList = null;
private List<String> fromContactNameList = null;
/**
* Constructor.
*
* @param fromAccount From account identifier, may be null;
* @param toAccount To account identifier, may be null;
* @param toAccount To account identifier, may be null;
*/
CallLogViewData(String fromAccount, String toAccount) {
this(fromAccount, toAccount, null);
@ -61,8 +65,8 @@ final class CallLogViewData {
* Constructor.
*
* @param fromAccount From account identifier, may be null;
* @param toAccount To account identifier, may be null;
* @param direction Direction, may be null.
* @param toAccount To account identifier, may be null;
* @param direction Direction, may be null.
*/
CallLogViewData(String fromAccount, String toAccount, String direction) {
this.fromAccount = fromAccount;
@ -146,4 +150,28 @@ final class CallLogViewData {
this.localAccountId = localAccountId;
}
public void setToContactNameList(List<String> contactNameList) {
if (contactNameList != null) {
this.toContactNameList = new ArrayList<>(contactNameList);
} else {
this.toContactNameList = new ArrayList<>();
}
}
public List<String> getToContactNameList() {
return Collections.unmodifiableList(this.toContactNameList);
}
public void setFromContactNameList(List<String> contactNameList) {
if (contactNameList != null) {
this.fromContactNameList = new ArrayList<>(contactNameList);
} else {
this.fromContactNameList = new ArrayList<>();
}
}
public List<String> getFromContactNameList() {
return Collections.unmodifiableList(this.fromContactNameList);
}
}

View File

@ -378,7 +378,7 @@ final class CommunicationArtifactViewerHelper {
"CommunicationArtifactViewerHelper_persona_button_view=View",
"CommunicationArtifactViewerHelper_persona_button_create=Create"
})
static List<AccountPersonaSearcherData> addPersonaRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String accountIdentifier) {
List<AccountPersonaSearcherData> dataList = new ArrayList<>();
@ -428,6 +428,35 @@ final class CommunicationArtifactViewerHelper {
return dataList;
}
/**
* Adds a contact row to the panel.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param contactId Contact name to display.
*
* @return A JLabel with the contact information.
*/
@NbBundle.Messages({
"#{0} - contact name",
"CommunicationArtifactViewerHelper_contact_label=Contact: {0}",
"CommunicationArtifactViewerHelper_contact_label_unknown=Unknown"
})
static JLabel addContactRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String contactId) {
// Increase the y value because we are not calling the addKey
constraints.gridy++;
//Don't change the origian constraints, just make a copy to modify
GridBagConstraints indentedConstraints = (GridBagConstraints) constraints.clone();
// Add an indent to match persona labels
indentedConstraints.insets = new java.awt.Insets(0, 2 * LEFT_INSET, 0, 0);
String contactInfo = Bundle.CommunicationArtifactViewerHelper_contact_label(contactId != null && !contactId.isEmpty() ? contactId : Bundle.CommunicationArtifactViewerHelper_contact_label_unknown());
return addValueAtCol(panel, gridbagLayout, indentedConstraints, contactInfo, 1);
}
/**
* Event handler for mouse click event. Attaches a 'Copy' menu item to right

View File

@ -16,7 +16,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.event.ActionEvent;
@ -39,6 +38,7 @@ import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
@ -46,8 +46,10 @@ import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallb
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.guiutils.ContactCache;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.TskCoreException;
@ -113,13 +115,24 @@ final class MessageAccountPanel extends JPanel {
return new ArrayList<>();
}
Collection<PersonaAccount> personAccounts = PersonaAccount.getPersonaAccountsForAccount(account);
if (personAccounts != null && !personAccounts.isEmpty()) {
for (PersonaAccount personaAccount : PersonaAccount.getPersonaAccountsForAccount(account)) {
dataList.add(new AccountContainer(account, personaAccount));
List<BlackboardArtifact> contactList = ContactCache.getContacts(account);
BlackboardArtifact contact = null;
if (contactList != null && !contactList.isEmpty()) {
contact = contactList.get(0);
}
if (CentralRepository.isEnabled()) {
Collection<PersonaAccount> personAccounts = PersonaAccount.getPersonaAccountsForAccount(account);
if (personAccounts != null && !personAccounts.isEmpty()) {
for (PersonaAccount personaAccount : PersonaAccount.getPersonaAccountsForAccount(account)) {
dataList.add(new AccountContainer(account, personaAccount, contact));
}
} else {
dataList.add(new AccountContainer(account, null, contact));
}
} else {
dataList.add(new AccountContainer(account, null));
dataList.add(new AccountContainer(account, null, contact));
}
}
@ -127,8 +140,7 @@ final class MessageAccountPanel extends JPanel {
}
@Messages({
"MessageAccountPanel_no_matches=No matches found.",
})
"MessageAccountPanel_no_matches=No matches found.",})
@Override
protected void done() {
try {
@ -199,6 +211,7 @@ final class MessageAccountPanel extends JPanel {
for (AccountContainer o : data) {
group.addGap(5)
.addComponent(o.getAccountLabel())
.addGroup(o.getContactLineVerticalGroup(layout))
.addGroup(o.getPersonLineVerticalGroup(layout));
}
@ -234,6 +247,7 @@ final class MessageAccountPanel extends JPanel {
group.addGap(10);
for (AccountContainer o : data) {
pgroup.addGroup(o.getPersonaSequentialGroup(layout));
pgroup.addGroup(o.getContactSequentialGroup(layout));
}
group.addGap(10)
.addGroup(pgroup)
@ -253,10 +267,13 @@ final class MessageAccountPanel extends JPanel {
private final Account account;
private Persona persona = null;
private final String contactName;
private JLabel accountLabel;
private JLabel personaHeader;
private JLabel personaDisplayName;
private JLabel contactHeader;
private JLabel contactDisplayName;
private JButton button;
/**
@ -265,16 +282,22 @@ final class MessageAccountPanel extends JPanel {
* @param account
* @param personaAccount
*/
AccountContainer(Account account, PersonaAccount personaAccount) {
AccountContainer(Account account, PersonaAccount personaAccount, BlackboardArtifact contactArtifact) throws TskCoreException {
if (contactArtifact != null && contactArtifact.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) {
throw new IllegalArgumentException("Failed to create AccountContainer object, passed in artifact was not a TSK_CONTACT");
}
this.account = account;
this.persona = personaAccount != null ? personaAccount.getPersona() : null;
this.contactName = getNameFromContactArtifact(contactArtifact);
}
@Messages({
"MessageAccountPanel_persona_label=Persona:",
"MessageAccountPanel_unknown_label=Unknown",
"MessageAccountPanel_button_view_label=View",
"MessageAccountPanel_button_create_label=Create"
"MessageAccountPanel_button_create_label=Create",
"MessageAccountPanel_contact_label=Contact:"
})
/**
* Swing components will not be initialized until this method is called.
@ -282,16 +305,29 @@ final class MessageAccountPanel extends JPanel {
private void initalizeSwingControls() {
accountLabel = new JLabel();
personaHeader = new JLabel(Bundle.MessageAccountPanel_persona_label());
contactHeader = new JLabel(Bundle.MessageAccountPanel_contact_label());
personaDisplayName = new JLabel();
contactDisplayName = new JLabel();
button = new JButton();
button.addActionListener(new PersonaButtonListener(this));
accountLabel.setText(account.getTypeSpecificID());
contactDisplayName.setText(contactName);
personaDisplayName.setText(persona != null ? persona.getName() : Bundle.MessageAccountPanel_unknown_label());
button.setText(persona != null ? Bundle.MessageAccountPanel_button_view_label() : Bundle.MessageAccountPanel_button_create_label());
}
private String getNameFromContactArtifact(BlackboardArtifact contactArtifact) throws TskCoreException {
if (contactArtifact != null) {
BlackboardAttribute attribute = contactArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
if (attribute != null) {
return attribute.getValueString();
}
}
return Bundle.MessageAccountPanel_unknown_label();
}
/**
* Sets a new persona for this object and update the controls.
*
@ -365,6 +401,17 @@ final class MessageAccountPanel extends JPanel {
return group;
}
private SequentialGroup getContactSequentialGroup(GroupLayout layout) {
SequentialGroup group = layout.createSequentialGroup();
group
.addComponent(contactHeader)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(contactDisplayName);
return group;
}
/**
* Generates the vertical layout code for the persona line.
@ -379,6 +426,12 @@ final class MessageAccountPanel extends JPanel {
.addComponent(personaDisplayName)
.addComponent(button);
}
private ParallelGroup getContactLineVerticalGroup(GroupLayout layout) {
return layout.createParallelGroup(Alignment.BASELINE)
.addComponent(contactHeader)
.addComponent(contactDisplayName);
}
}
/**

View File

@ -447,10 +447,8 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
resetComponent();
}
msgbodyTabbedPane.setEnabledAt(ACCT_TAB_INDEX, CentralRepository.isEnabled());
if(CentralRepository.isEnabled()) {
accountsPanel.setArtifact(artifact);
}
msgbodyTabbedPane.setEnabledAt(ACCT_TAB_INDEX, true);
accountsPanel.setArtifact(artifact);
}
/**

View File

@ -0,0 +1,192 @@
/*
* 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.guiutils;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.beans.PropertyChangeListener;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A singleton cache of the Contact artifacts for accounts. The map of account
* unique ids to list of contact artifacts is stored in a LoadingCache which
* expires after 10 of non-use.
*
*/
public final class ContactCache {
private static final Logger logger = Logger.getLogger(ContactCache.class.getName());
private static ContactCache instance;
private final LoadingCache<String, Map<String, List<BlackboardArtifact>>> accountMap;
/**
* Returns the list of Contacts for the given Account.
*
* @param account Account instance.
*
* @return List of TSK_CONTACT artifacts that references the given Account.
* An empty list is returned if no contacts are found.
*
* @throws ExecutionException
*/
public static synchronized List<BlackboardArtifact> getContacts(Account account) throws ExecutionException {
return getInstance().accountMap.get("realMap").get(account.getTypeSpecificID());
}
/**
* Returns a list of Contact TSK_NAME values for the given account type
* specific id.
*
* @param accountTypeSpecificID Account type specific id
*
* @return List of contact string names or empty list if none were found.
*
* @throws TskCoreException
*/
public static synchronized List<String> getContactNameList(String accountTypeSpecificID) throws TskCoreException {
List<BlackboardArtifact> contactList;
try {
contactList = getInstance().accountMap.get("realMap").get(accountTypeSpecificID);
} catch (ExecutionException ex) {
throw new TskCoreException("Unable to get contact list from cache", ex);
}
List<String> contactNameList = new ArrayList<>();
if (contactList != null) {
for (BlackboardArtifact artifact : contactList) {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_NAME.getTypeID())));
if (attribute != null && !contactNameList.contains(attribute.getValueString())) {
contactNameList.add(attribute.getValueString());
}
}
}
return contactNameList;
}
/**
* Force the cache to invalidate all entries.
*/
static synchronized void invalidateCache() {
getInstance().accountMap.invalidateAll();
}
/**
* Construct a new instance.
*/
private ContactCache() {
accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build(
new CacheLoader<String, Map<String, List<BlackboardArtifact>>>() {
@Override
public Map<String, List<BlackboardArtifact>> load(String key) {
try {
return buildMap();
} catch (SQLException | TskCoreException ex) {
logger.log(Level.WARNING, "Failed to build account to contact map", ex);
}
return new HashMap<>(); // Return an empty map if there is an exception to avoid NPE and continual trying.
}
});
PropertyChangeListener ingestListener = pce -> {
String eventType = pce.getPropertyName();
if (eventType.equals(DATA_ADDED.toString())) {
ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) {
invalidateCache();
}
}
};
IngestManager.getInstance().addIngestModuleEventListener(EnumSet.of(DATA_ADDED), ingestListener);
}
/**
* Returns the singleton instance of the cache object.
*
* @return AccountCache instance.
*/
private static synchronized ContactCache getInstance() {
if (instance == null) {
instance = new ContactCache();
}
return instance;
}
/**
* Builds the map of account IDs to contacts that reference them.
*
* @return A map of account IDs to contact artifacts.
*
* @throws TskCoreException
* @throws SQLException
*/
private Map<String, List<BlackboardArtifact>> buildMap() throws TskCoreException, SQLException {
Map<String, List<BlackboardArtifact>> acctMap = new HashMap<>();
List<BlackboardArtifact> contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
for (BlackboardArtifact contactArtifact : contactList) {
List<BlackboardAttribute> contactAttributes = contactArtifact.getAttributes();
for (BlackboardAttribute attribute : contactAttributes) {
String typeName = attribute.getAttributeType().getTypeName();
if (typeName.startsWith("TSK_EMAIL")
|| typeName.startsWith("TSK_PHONE")
|| typeName.startsWith("TSK_NAME")
|| typeName.startsWith("TSK_ID")) {
String accountID = attribute.getValueString();
List<BlackboardArtifact> artifactList = acctMap.get(accountID);
if (artifactList == null) {
artifactList = new ArrayList<>();
acctMap.put(accountID, artifactList);
}
if (!artifactList.contains(contactArtifact)) {
artifactList.add(contactArtifact);
}
}
}
}
return acctMap;
}
}

View File

@ -2,7 +2,14 @@ OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
OpenIDE-Module-Name=RecentActivity
OpenIDE-Module-Short-Description=Recent Activity finder ingest module
Chrome.moduleName=Chrome
Browser.name.Microsoft.Edge=Microsoft Edge
Browser.name.Yandex=Yandex
Browser.name.Opera=Opera
Browser.name.SalamWeb=SalamWeb
Browser.name.UC.Browser=UC Browser
Browser.name.Brave=Brave
Browser.name.Google.Chrome=Google Chrome
Chrome.moduleName=Chromium
Chrome.getHistory.errMsg.errGettingFiles=Error when trying to get Chrome history files.
Chrome.getHistory.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome history files.
Chrome.getHistory.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1}

View File

@ -5,15 +5,10 @@ ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for an
ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis.
ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s.
ChromeCacheExtractor.moduleName=ChromeCacheExtractor
# {0} - module name
# {1} - row number
# {2} - table length
# {3} - cache path
ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}
DataSourceUsage_AndroidMedia=Android Media Card
DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card
DataSourceUsage_FlashDrive=Flash Drive
# {0} - OS name
DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0})
DataSourceUsageAnalyzer.parentModuleName=Recent Activity
Extract.indexError.message=Failed to index artifact for keyword search.
@ -77,10 +72,17 @@ ExtractZone_progress_Msg=Extracting :Zone.Identifer files
ExtractZone_Restricted=Restricted Sites Zone
ExtractZone_Trusted=Trusted Sites Zone
OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
OpenIDE-Module-Name=RecentActivity
OpenIDE-Module-Short-Description=Recent Activity finder ingest module
Chrome.moduleName=Chrome
Browser.name.Microsoft.Edge=Microsoft Edge
Browser.name.Yandex=Yandex
Browser.name.Opera=Opera
Browser.name.SalamWeb=SalamWeb
Browser.name.UC.Browser=UC Browser
Browser.name.Brave=Brave
Browser.name.Google.Chrome=Google Chrome
Chrome.moduleName=Chromium
Chrome.getHistory.errMsg.errGettingFiles=Error when trying to get Chrome history files.
Chrome.getHistory.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome history files.
Chrome.getHistory.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1}
@ -148,14 +150,14 @@ Firefox.getDlV24.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{
Firefox.getDlV24.errMsg.errParsingArtifacts={0}: Error parsing {1} Firefox web download artifacts.
Progress_Message_Analyze_Registry=Analyzing Registry Files
Progress_Message_Analyze_Usage=Data Sources Usage Analysis
Progress_Message_Chrome_AutoFill=Chrome Auto Fill
Progress_Message_Chrome_Bookmarks=Chrome Bookmarks
Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}
Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}
Progress_Message_Chrome_Cache=Chrome Cache
Progress_Message_Chrome_Cookies=Chrome Cookies
Progress_Message_Chrome_Downloads=Chrome Downloads
Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}
Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}
Progress_Message_Chrome_FormHistory=Chrome Form History
Progress_Message_Chrome_History=Chrome History
Progress_Message_Chrome_Logins=Chrome Logins
Progress_Message_Chrome_History=Chrome History Browser {0}
Progress_Message_Chrome_Logins=Chrome Logins Browser {0}
Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks
Progress_Message_Edge_Cookies=Microsoft Edge Cookies
Progress_Message_Edge_History=Microsoft Edge History
@ -209,7 +211,6 @@ Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU
Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)
RegRipperFullNotFound=Full version RegRipper executable not found.
RegRipperNotFound=Autopsy RegRipper executable not found.
# {0} - file name
SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}.
SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine
SearchEngineURLQueryAnalyzer.engineName.none=NONE

View File

@ -22,6 +22,7 @@
*/
package org.sleuthkit.autopsy.recentactivity;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
@ -58,9 +59,9 @@ import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
/**
* Chrome recent activity extraction
* Chromium recent activity extraction
*/
class Chrome extends Extract {
class Chromium extends Extract {
private static final String HISTORY_QUERY = "SELECT urls.url, urls.title, urls.visit_count, urls.typed_count, " //NON-NLS
+ "last_visit_time, urls.hidden, visits.visit_time, (SELECT urls.url FROM urls WHERE urls.id=visits.url) AS from_visit, visits.transition FROM urls, visits WHERE urls.id = visits.url"; //NON-NLS
@ -83,20 +84,31 @@ class Chrome extends Extract {
private final Logger logger = Logger.getLogger(this.getClass().getName());
private Content dataSource;
private IngestJobContext context;
private static final Map<String, String> BROWSERS_MAP = ImmutableMap.<String, String>builder()
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Microsoft.Edge"), "Microsoft/Edge")
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Yandex"), "YandexBrowser")
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Opera"), "Opera Software")
.put(NbBundle.getMessage(Chromium.class, "Browser.name.SalamWeb"), "SalamWeb")
.put(NbBundle.getMessage(Chromium.class, "Browser.name.UC.Browser"), "UCBrowser")
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Brave"), "BraveSoftware")
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Google.Chrome"), "Chrome")
.build();
@Messages({
"Progress_Message_Chrome_History=Chrome History",
"Progress_Message_Chrome_Bookmarks=Chrome Bookmarks",
"Progress_Message_Chrome_Cookies=Chrome Cookies",
"Progress_Message_Chrome_Downloads=Chrome Downloads",
"Progress_Message_Chrome_History=Chrome History Browser {0}",
"Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}",
"Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}",
"Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}",
"Progress_Message_Chrome_FormHistory=Chrome Form History",
"Progress_Message_Chrome_AutoFill=Chrome Auto Fill",
"Progress_Message_Chrome_Logins=Chrome Logins",
"Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}",
"Progress_Message_Chrome_Logins=Chrome Logins Browser {0}",
"Progress_Message_Chrome_Cache=Chrome Cache",
})
Chrome() {
moduleName = NbBundle.getMessage(Chrome.class, "Chrome.moduleName");
Chromium() {
moduleName = NbBundle.getMessage(Chromium.class, "Chrome.moduleName");
}
@Override
@ -105,55 +117,60 @@ class Chrome extends Extract {
this.context = context;
dataFound = false;
progressBar.progress(Bundle.Progress_Message_Chrome_History());
this.getHistory();
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(Bundle.Progress_Message_Chrome_Bookmarks());
this.getBookmark();
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(Bundle.Progress_Message_Chrome_Cookies());
this.getCookie();
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(Bundle.Progress_Message_Chrome_Logins());
this.getLogins();
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(Bundle.Progress_Message_Chrome_AutoFill());
this.getAutofill();
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(Bundle.Progress_Message_Chrome_Downloads());
this.getDownload();
if (context.dataSourceIngestIsCancelled()) {
return;
for (Map.Entry<String, String> browser : BROWSERS_MAP.entrySet()) {
String browserName = browser.getKey();
String browserLocation = browser.getValue();
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_History", browserName));
this.getHistory(browser.getKey(), browser.getValue());
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Bookmarks", browserName));
this.getBookmark(browser.getKey(), browser.getValue());
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Cookies", browserName));
this.getCookie(browser.getKey(), browser.getValue());
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Logins", browserName));
this.getLogins(browser.getKey(), browser.getValue());
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_AutoFill", browserName));
this.getAutofill(browser.getKey(), browser.getValue());
if (context.dataSourceIngestIsCancelled()) {
return;
}
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Downloads", browserName));
this.getDownload(browser.getKey(), browser.getValue());
if (context.dataSourceIngestIsCancelled()) {
return;
}
}
progressBar.progress(Bundle.Progress_Message_Chrome_Cache());
ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar);
chromeCacheExtractor.processCaches();
}
/**
* Query for history databases and add artifacts
*/
private void getHistory() {
private void getHistory(String browser, String browserLocation) {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> historyFiles;
try {
historyFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS
historyFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
@ -179,10 +196,11 @@ class Chrome extends Extract {
dataFound = true;
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
int j = 0;
while (j < historyFiles.size()) {
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + historyFiles.get(j).getName() + j + ".db"; //NON-NLS
final AbstractFile historyFile = historyFiles.get(j++);
if (historyFile.getSize() == 0) {
while (j < allocatedHistoryFiles.size()) {
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + allocatedHistoryFiles.get(j).getName() + j + ".db"; //NON-NLS
final AbstractFile historyFile = allocatedHistoryFiles.get(j++);
if ((historyFile.getSize() == 0) || (historyFile.getName().toLowerCase().contains("-slack"))
|| (historyFile.getName().toLowerCase().contains("cache")) || (historyFile.getName().toLowerCase().contains("media"))) {
continue;
}
try {
@ -223,8 +241,7 @@ class Chrome extends Extract {
RecentActivityExtracterModuleFactory.getModuleName(),
((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
RecentActivityExtracterModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
RecentActivityExtracterModuleFactory.getModuleName(), browser));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
RecentActivityExtracterModuleFactory.getModuleName(),
(NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
@ -245,11 +262,11 @@ class Chrome extends Extract {
/**
* Search for bookmark files and make artifacts.
*/
private void getBookmark() {
private void getBookmark(String browser, String browserLocation) {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> bookmarkFiles;
try {
bookmarkFiles = fileManager.findFiles(dataSource, "Bookmarks", "Chrome"); //NON-NLS
bookmarkFiles = fileManager.findFiles(dataSource, "%Bookmarks%", browserLocation); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
@ -268,10 +285,12 @@ class Chrome extends Extract {
while (j < bookmarkFiles.size()) {
AbstractFile bookmarkFile = bookmarkFiles.get(j++);
if (bookmarkFile.getSize() == 0) {
if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack"))
|| (bookmarkFile.getName().toLowerCase().contains("extras")) || (bookmarkFile.getName().toLowerCase().contains("log"))
|| (bookmarkFile.getName().toLowerCase().contains("backup")) || (bookmarkFile.getName().toLowerCase().contains("visualized"))) {
continue;
}
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS
try {
ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
@ -359,8 +378,7 @@ class Chrome extends Extract {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
RecentActivityExtracterModuleFactory.getModuleName(), (date / 1000000) - Long.valueOf("11644473600")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
RecentActivityExtracterModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
RecentActivityExtracterModuleFactory.getModuleName(), browser));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
RecentActivityExtracterModuleFactory.getModuleName(), domain));
bbart.addAttributes(bbattributes);
@ -381,12 +399,12 @@ class Chrome extends Extract {
/**
* Queries for cookie files and adds artifacts
*/
private void getCookie() {
private void getCookie(String browser, String browserLocation) {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> cookiesFiles;
try {
cookiesFiles = fileManager.findFiles(dataSource, "Cookies", "Chrome"); //NON-NLS
cookiesFiles = fileManager.findFiles(dataSource, "%Cookies%", browserLocation); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
@ -404,10 +422,10 @@ class Chrome extends Extract {
int j = 0;
while (j < cookiesFiles.size()) {
AbstractFile cookiesFile = cookiesFiles.get(j++);
if (cookiesFile.getSize() == 0) {
if ((cookiesFile.getSize() == 0) || (cookiesFile.getName().toLowerCase().contains("-slack"))) {
continue;
}
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS
try {
ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
@ -447,8 +465,7 @@ class Chrome extends Extract {
RecentActivityExtracterModuleFactory.getModuleName(),
((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
RecentActivityExtracterModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
RecentActivityExtracterModuleFactory.getModuleName(), browser));
String domain = result.get("host_key").toString(); //NON-NLS
domain = domain.replaceFirst("^\\.+(?!$)", "");
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
@ -471,11 +488,11 @@ class Chrome extends Extract {
/**
* Queries for download files and adds artifacts
*/
private void getDownload() {
private void getDownload(String browser, String browserLocation) {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> downloadFiles;
try {
downloadFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS
downloadFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
@ -493,10 +510,12 @@ class Chrome extends Extract {
int j = 0;
while (j < downloadFiles.size()) {
AbstractFile downloadFile = downloadFiles.get(j++);
if (downloadFile.getSize() == 0) {
if ((downloadFile.getSize() == 0) || (downloadFile.getName().toLowerCase().contains("-slack"))
|| (downloadFile.getName().toLowerCase().contains("cache"))) {
continue;
}
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS
try {
ContentUtils.writeToFile(downloadFile, new File(temps), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
@ -552,8 +571,7 @@ class Chrome extends Extract {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
RecentActivityExtracterModuleFactory.getModuleName(), domain));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
RecentActivityExtracterModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
RecentActivityExtracterModuleFactory.getModuleName(), browser));
BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
if (webDownloadArtifact != null) {
@ -561,7 +579,7 @@ class Chrome extends Extract {
// find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
try {
String normalizedFullPath = FilenameUtils.normalize(fullPath, true);
String normalizedFullPath = FilenameUtils.normalize(fullPath, true);
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(normalizedFullPath), FilenameUtils.getPath(normalizedFullPath))) {
BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
associatedObjectArtifact.addAttribute(
@ -588,12 +606,12 @@ class Chrome extends Extract {
/**
* Gets user logins from Login Data sqlite database
*/
private void getLogins() {
private void getLogins(String browser, String browserLocation) {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> loginDataFiles;
try {
loginDataFiles = fileManager.findFiles(dataSource, "Login Data", "Chrome"); //NON-NLS
loginDataFiles = fileManager.findFiles(dataSource, "%Login Data%", browserLocation); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
@ -611,10 +629,10 @@ class Chrome extends Extract {
int j = 0;
while (j < loginDataFiles.size()) {
AbstractFile loginDataFile = loginDataFiles.get(j++);
if (loginDataFile.getSize() == 0) {
if ((loginDataFile.getSize() == 0) || (loginDataFile.getName().toLowerCase().contains("-slack"))) {
continue;
}
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS
try {
ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
@ -661,6 +679,9 @@ class Chrome extends Extract {
RecentActivityExtracterModuleFactory.getModuleName(),
((result.get("signon_realm").toString() != null) ? result.get("signon_realm").toString() : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
RecentActivityExtracterModuleFactory.getModuleName(), browser));
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes);
if (bbart != null) {
bbartifacts.add(bbart);
@ -679,12 +700,12 @@ class Chrome extends Extract {
* Gets and parses Autofill data from 'Web Data' database,
* and creates TSK_WEB_FORM_AUTOFILL, TSK_WEB_FORM_ADDRESS artifacts
*/
private void getAutofill() {
private void getAutofill(String browser, String browserLocation) {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> webDataFiles;
try {
webDataFiles = fileManager.findFiles(dataSource, "Web Data", "Chrome"); //NON-NLS
webDataFiles = fileManager.findFiles(dataSource, "%Web Data%", browserLocation); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getAutofills.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
@ -702,10 +723,10 @@ class Chrome extends Extract {
int j = 0;
while (j < webDataFiles.size()) {
AbstractFile webDataFile = webDataFiles.get(j++);
if (webDataFile.getSize() == 0) {
if ((webDataFile.getSize() == 0) || (webDataFile.getName().toLowerCase().contains("-slack"))) {
continue;
}
String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS
String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS
try {
ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
@ -731,7 +752,7 @@ class Chrome extends Extract {
boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath);
// get form autofill artifacts
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X));
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, browser));
try {
// get form address atifacts
getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X);
@ -757,7 +778,7 @@ class Chrome extends Extract {
*
* @return collection of TSK_WEB_FORM_AUTOFILL artifacts
*/
private Collection<BlackboardArtifact> getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) {
private Collection<BlackboardArtifact> getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X, String browser ) {
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
@ -783,7 +804,7 @@ class Chrome extends Extract {
RecentActivityExtracterModuleFactory.getModuleName(),
(Integer.valueOf(result.get("count").toString())))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
RecentActivityExtracterModuleFactory.getModuleName(),
Long.valueOf(result.get("date_created").toString()))); //NON-NLS
@ -794,6 +815,9 @@ class Chrome extends Extract {
Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS
}
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
RecentActivityExtracterModuleFactory.getModuleName(), browser));
// Add an artifact
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes);
if (bbart != null) {
@ -886,7 +910,7 @@ class Chrome extends Extract {
use_count, otherAttributes);
}
}
private boolean isChromePreVersion30(String temps) {
String query = "PRAGMA table_info(downloads)"; //NON-NLS
List<HashMap<String, Object>> columns = this.dbConnect(temps, query);

View File

@ -83,9 +83,12 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_AC
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME;
@ -353,12 +356,10 @@ class ExtractRegistry extends Extract {
} catch (IOException | TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex);
}
} else if (regFileNameLocal.toLowerCase().contains("system")) {
try {
createSystemArtifacts(regOutputFiles.fullPlugins, regFile);
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Unable to get artifacts from file %s", regOutputFiles.fullPlugins), ex);
}
} else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) {
this.addErrorMessage(
NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
this.getName(), regFileName));
}
try {
Report report = currentCase.addReport(regOutputFiles.fullPlugins,
@ -952,6 +953,119 @@ class ExtractRegistry extends Extract {
return false;
}
private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) {
File regfile = new File(regfilePath);
try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) {
String line = reader.readLine();
while (line != null) {
line = line.trim();
if (line.toLowerCase().matches("^bam v.*")) {
parseBamKey(regAbstractFile, reader, Bundle.Registry_System_Bam());
} else if (line.toLowerCase().matches("^bthport v..*")) {
parseBlueToothDevices(regAbstractFile, reader);
}
line = reader.readLine();
}
return true;
} catch (FileNotFoundException ex) {
logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS
} catch (IOException ex) {
logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS
}
return false;
}
/**
* Create recently used artifacts to parse the regripper plugin output, this
* format is used in several diffent plugins
*
* @param regFile registry file the artifact is associated with
*
* @param reader buffered reader to parse adobemru records
*
* @param comment string that will populate attribute TSK_COMMENT
*
* @throws FileNotFound and IOException
*/
private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException {
List<BlackboardArtifact> bbartifacts = new ArrayList<>();
String line = reader.readLine();
while ((line != null) && (!line.contains(SECTION_DIVIDER))) {
line = reader.readLine();
if (line != null) {
line = line.trim();
}
if ((line != null) && (line.toLowerCase().contains("device unique id"))) {
// Columns are seperated by colons :
// Data : Values
// Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected
while (line != null && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.toLowerCase().contains("radio support not found")) {
Collection<BlackboardAttribute> attributes = new ArrayList<>();
addBlueToothAttribute(line, attributes, TSK_DEVICE_ID);
line = reader.readLine();
// Name may not exist, check for it to make sure.
if ((line != null) && (line.toLowerCase().contains("name"))) {
addBlueToothAttribute(line, attributes, TSK_NAME);
line = reader.readLine();
}
addBlueToothAttribute(line, attributes, TSK_DATETIME);
line = reader.readLine();
addBlueToothAttribute(line, attributes, TSK_DATETIME_ACCESSED);
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes);
if(bba != null) {
bbartifacts.add(bba);
}
// Read blank line between records then next read line is start of next block
reader.readLine();
line = reader.readLine();
}
if (line != null) {
line = line.trim();
}
}
}
if (!bbartifacts.isEmpty()) {
postArtifacts(bbartifacts);
}
}
private void addBlueToothAttribute(String line, Collection<BlackboardAttribute> attributes, ATTRIBUTE_TYPE attributeType) {
if (line == null) {
return;
}
String tokens[] = line.split(": ");
if (tokens.length > 1 && !tokens[1].isEmpty()) {
String tokenString = tokens[1];
if (attributeType.getDisplayName().toLowerCase().contains("date")) {
String dateString = tokenString.toLowerCase().replace(" z", "");
// date format for plugin Tue Jun 23 10:27:54 2020 Z
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US);
Long dateLong = Long.valueOf(0);
try {
Date newDate = dateFormat.parse(dateString);
dateLong = newDate.getTime()/1000;
} catch (ParseException ex) {
// catching error and displaying date that could not be parsed
// we set the timestamp to 0 and continue on processing
logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", dateString), ex); //NON-NLS
}
attributes.add(new BlackboardAttribute(attributeType, getName(), dateLong));
} else {
attributes.add(new BlackboardAttribute(attributeType, getName(), tokenString));
}
}
}
/**
* Parse the output of the SAM regripper plugin to get additional Account
* information
@ -1280,30 +1394,6 @@ class ExtractRegistry extends Extract {
}
}
/**
* Create artifacts from the System registry Hive
*
* @param regFileName name of the regripper output file
*
* @param regFile registry file the artifact is associated with
*
* @throws FileNotFound and IOException
*/
private void createSystemArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException {
File regfile = new File(regFileName);
try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) {
String line = reader.readLine();
while (line != null) {
line = line.trim();
if (line.matches("^bam v.*")) {
parseBamKey(regFile, reader, Bundle.Registry_System_Bam());
}
line = reader.readLine();
}
}
}
/**
* Create artifacts from BAM Regripper Plugin records
*
@ -2031,4 +2121,4 @@ class ExtractRegistry extends Extract {
public String autopsyPlugins = "";
public String fullPlugins = "";
}
}
}

View File

@ -70,7 +70,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule {
Extract registry = new ExtractRegistry();
Extract recentDocuments = new RecentDocumentsByLnk();
Extract chrome = new Chrome();
Extract chrome = new Chromium();
Extract firefox = new Firefox();
Extract SEUQA = new SearchEngineURLQueryAnalyzer();
Extract osExtract = new ExtractOs();

View File

@ -5,6 +5,7 @@ auditfail
backuprestore
bam
bam_tln
btconfig
bthport
comfoo
compname