All the meat of the new module classes

This commit is contained in:
U-BASIS\dsmyda 2018-08-08 17:43:16 -04:00
parent d74c8b6212
commit 8a3daf4110
4 changed files with 185 additions and 128 deletions

View File

@ -16,14 +16,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.interestingdatabases;
package org.sleuthkit.autopsy.modules.databaseselector;
/**
*
* @author dsmyda
* Cell types used by the CellTypeDetector class
*/
public enum CellType {
EMAIL,
NOT_INTERESTING
}

View File

@ -16,18 +16,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.interestingdatabases;
package org.sleuthkit.autopsy.modules.databaseselector;
import org.apache.commons.validator.routines.EmailValidator;
/**
*
* @author dsmyda
* Determines the type of a database cell (EMAIL, PHONE, GPS COORD, MAC ADDRESS)
*/
public class CellTypeDetector {
private final EmailValidator emailValidator;
public CellTypeDetector() {
emailValidator = EmailValidator.getInstance();
}
public CellType getType(String cell) {
if(emailValidator.isValid(cell)) {
return CellType.EMAIL;
} else {
return CellType.NOT_INTERESTING;
}
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.interestingdatabases;
package org.sleuthkit.autopsy.modules.databaseselector;
import java.io.File;
import java.io.IOException;
@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector.FileTypeDetectorInitException;
import org.sleuthkit.autopsy.sqlitereader.SQLiteReader;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -46,50 +47,87 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* @author dsmyda
* Parses database files and marks them as having interesting records (emails,
* phone numbers, mac addresses, gps coordinates).
*/
public class InterestingDatabasesIngestModule extends FileIngestModuleAdapter {
public class DatabaseSelectorIngestModule extends FileIngestModuleAdapter {
private static final String SUPPORTED_MIME_TYPE = "application/x-sqlite3";
private static final String MODULE_NAME = InterestingDatabasesIngestModuleFactory.getModuleName();
private static final String MODULE_NAME = DatabaseSelectorIngestModuleFactory.getModuleName();
private final IngestServices services = IngestServices.getInstance();
private final Logger logger = services.getLogger(
InterestingDatabasesIngestModuleFactory.getModuleName());
DatabaseSelectorIngestModuleFactory.getModuleName());
private FileTypeDetector fileTypeDetector;
private CellTypeDetector cellTypeDetector;
private Blackboard blackboard;
private String localDiskPath;
/**
*
* @param context
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
*/
@NbBundle.Messages({
"CannotRunFileTypeDetection=Unable to initialize file type detection.",
"DatabaseSelectorIngestModule.CannotRunFileTypeDetection=Unable to initialize file type detection.",
})
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
try {
fileTypeDetector = new FileTypeDetector();
cellTypeDetector = new CellTypeDetector();
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
throw new IngestModuleException(Bundle.CannotRunFileTypeDetection(), ex);
} catch (FileTypeDetectorInitException ex) {
throw new IngestModuleException(Bundle.DatabaseSelectorIngestModule_CannotRunFileTypeDetection(), ex);
}
}
/**
*
* @param file
* @return
*/
@Override
@NbBundle.Messages({
"InterestingDatabasesIngestModule.indexError.message=Failed to index interesting artifact hit for keyword search."
})
public ProcessResult process(AbstractFile file) {
if(getBlackboardInstanceFromServices().equals(ProcessResult.ERROR)) {
return ProcessResult.ERROR;
}
//Qualify the MIMEType
String dataSourceMimeType = fileTypeDetector.getMIMEType(file);
if(SUPPORTED_MIME_TYPE.equals(dataSourceMimeType)) {
if(createLocalDiskPathFromCurrentCase(file).equals(ProcessResult.ERROR)) {
return ProcessResult.ERROR;
}
try (SQLiteReader sqliteReader = new SQLiteReader(file, localDiskPath)){
try {
Set<CellType> databaseCellTypes = getCellTypesInDatabase(sqliteReader);
//No interesting hits, don't flag this database, skip artifact creation.
if(!databaseCellTypes.isEmpty()) {
String cellTypesComment = createCellTypeCommentString(databaseCellTypes);
try {
BlackboardArtifact artifact = createArtifactGivenCellTypes(
file, cellTypesComment);
indexArtifactAndFireModuleDataEvent(artifact);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error creating blackboard artifact", ex); //NON-NLS
}
}
} catch(SQLException ex) {
logger.log(Level.WARNING, "Error attempting to read sqlite "
+ "file in DatabaseSelectorIngestModule", ex);
}
} catch (ClassNotFoundException | SQLException | IOException |
NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, "Cannot initialize sqliteReader class "
+ "in DatabaseSelectorIngestModule", ex);
return ProcessResult.ERROR;
}
}
//Whether we successfully read the sqlite database or determined the mime
//type is not supported, the process is OK.
return ProcessResult.OK;
}
/**
* Get a pointer to the current case blackboard for indexing of artifacts
*
* @return ProcessResult indicating a success or failure in getting current case
*/
private ProcessResult getBlackboardInstanceFromServices() {
try {
blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard();
} catch (NoCurrentCaseException ex) {
@ -97,10 +135,18 @@ public class InterestingDatabasesIngestModule extends FileIngestModuleAdapter {
return ProcessResult.ERROR;
}
String dataSourceMimeType = fileTypeDetector.getMIMEType(file);
if(SUPPORTED_MIME_TYPE.equals(dataSourceMimeType)) {
return ProcessResult.OK;
}
String localDiskPath;
/**
* Generates a local disk path for abstract file contents to be copied.
* All database sources must be copied to local disk to be opened by
* SQLiteReader
*
* @param file The database abstract file
* @return ProcessResult indicating a success or failure in creating a disk path
*/
private ProcessResult createLocalDiskPathFromCurrentCase(AbstractFile file) {
try {
localDiskPath = Case.getCurrentCaseThrows()
.getTempDirectory() + File.separator + file.getName();
@ -111,19 +157,124 @@ public class InterestingDatabasesIngestModule extends FileIngestModuleAdapter {
return ProcessResult.ERROR;
}
return ProcessResult.OK;
}
try (SQLiteReader sqliteReader =
new SQLiteReader(file, localDiskPath)){
/**
* Creates and populates a set of all CellTypes in the sqlite database
*
* @param sqliteReader Reader currently connected to database file
* @return A Set of distinct CellTypes
* @throws SQLException Caught during attempting to read sqlite database
*/
private Set<CellType> getCellTypesInDatabase(SQLiteReader sqliteReader) throws SQLException {
Map<String, String> tables = sqliteReader.getTableSchemas();
Set<CellType> aggregateCellTypes = new TreeSet<>();
Set<CellType> aggregatedCellTypes = cellTypesInDatabase(sqliteReader);
String cellTypesMessage = aggregatedCellTypes.toString()
.replace("]", "")
.replace("[", "");
//Aggregate cell types from all tables
for(String tableName : tables.keySet()) {
aggregateCellTypes.addAll(getCellTypesInTable(sqliteReader, tableName));
}
try {
BlackboardArtifact artifact = createArtifactGivenCellTypes(
file, cellTypesMessage);
return aggregateCellTypes;
}
/**
* Creates and populates a set of all CellTypes in a sqlite table
*
* @param sqliteReader Reader currently connected to database file
* @param tableName database table to be opened and read
* @return Set of all unique cell types in table
* @throws SQLException Caught during attempting to read sqlite database
*/
private Set<CellType> getCellTypesInTable(SQLiteReader sqliteReader,
String tableName) throws SQLException {
Set<CellType> tableCellTypes = new TreeSet<>();
List<Map<String, Object>> tableValues = sqliteReader.getRowsFromTable(tableName);
//Aggregate cell types from all table rows
tableValues.forEach((row) -> {
tableCellTypes.addAll(getCellTypesInRow(row));
});
return tableCellTypes;
}
/**
* Creates and populates a set of all CellTypes in a table row
*
* @param row Table row is represented as a column-value map
* @return
*/
private Set<CellType> getCellTypesInRow(Map<String, Object> row) {
Set<CellType> rowCellTypes = new TreeSet<>();
//Aggregate cell types from a row
row.values().forEach((Object cell) -> {
if(cell instanceof String) {
CellType type = cellTypeDetector.getType((String) cell);
if(!type.equals(CellType.NOT_INTERESTING)) {
rowCellTypes.add(type);
}
}
});
return rowCellTypes;
}
/**
* Creates a comma seperated string of all the cell types found in a database
* file. Used as the comment string for the blackboard artifact.
*
* @param databaseCellTypes The set of all database cell types detected
* @return
*/
private String createCellTypeCommentString(Set<CellType> databaseCellTypes) {
return databaseCellTypes.toString().replace("]", "").replace("[", "");
}
/**
* Initializes a new interesting file hit artifact and provides name and
* comment attributes
*
* @param file The database abstract file
* @param cellTypesComment String of all the cell types found in a database
* file
* @return Interesting file hit artifact
* @throws TskCoreException Thrown if the abstract file cannot create a blackboard
* artifact
*/
@NbBundle.Messages({
"DatabaseSelectorIngestModule.FlagDatabases.setName=Selectors identified"
})
private BlackboardArtifact createArtifactGivenCellTypes(AbstractFile file,
String cellTypesComment) throws TskCoreException {
BlackboardArtifact artifact = file.newArtifact(
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
BlackboardAttribute setNameAttribute = new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
Bundle.DatabaseSelectorIngestModule_FlagDatabases_setName());
artifact.addAttribute(setNameAttribute);
BlackboardAttribute commentAttribute = new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME,
cellTypesComment);
artifact.addAttribute(commentAttribute);
return artifact;
}
/**
* Pass the artifact to blackboard for indexing and fire module data event
* in the IngestServices
*
* @param artifact Blackboard artifact created for the interesting file hit
*/
@NbBundle.Messages({
"DatabaseSelectorIngestModule.indexError.message="
+ "Failed to index interesting file hit artifact for keyword search."
})
private void indexArtifactAndFireModuleDataEvent(BlackboardArtifact artifact) {
try {
// index the artifact for keyword search
blackboard.indexArtifact(artifact);
@ -132,111 +283,12 @@ public class InterestingDatabasesIngestModule extends FileIngestModuleAdapter {
"Unable to index blackboard artifact " + //NON-NLS
artifact.getArtifactID(), ex);
MessageNotifyUtil.Notify.error(
Bundle.InterestingDatabasesIngestModule_indexError_message(),
Bundle.DatabaseSelectorIngestModule_indexError_message(),
artifact.getDisplayName());
}
services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME,
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT,
Collections.singletonList(artifact)));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error posting to the blackboard", ex); //NOI18N NON-NLS
}
} catch (SQLException ex) {
//Could be thrown in init or close.. need to fix.
Exceptions.printStackTrace(ex);
} catch (ClassNotFoundException ex) {
Exceptions.printStackTrace(ex);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} catch (NoCurrentCaseException ex) {
Exceptions.printStackTrace(ex);
} catch (TskCoreException ex) {
Exceptions.printStackTrace(ex);
}
}
return ProcessResult.OK;
}
/**
*
* @param sqliteReader
* @return
* @throws SQLException
*/
private Set<CellType> cellTypesInDatabase(SQLiteReader sqliteReader) throws SQLException {
Map<String, String> tables = sqliteReader.getTableSchemas();
Set<CellType> aggregateCellTypes = new TreeSet<>();
//Aggregate all cell types from each table
for(String tableName : tables.keySet()) {
addCellTypesInTable(sqliteReader, tableName, aggregateCellTypes);
}
return aggregateCellTypes;
}
/**
*
* @param sqliteReader
* @param table
* @return
*/
private void addCellTypesInTable(SQLiteReader sqliteReader, String tableName,
Set<CellType> aggregateCellTypes) throws SQLException {
List<Map<String, Object>> tableValues = sqliteReader.getRowsFromTable(tableName);
tableValues.forEach((row) -> {
addCellTypeInRow(row, aggregateCellTypes);
});
}
/**
*
* @param row
* @return
*/
private void addCellTypeInRow(Map<String, Object> row,
Set<CellType> aggregateCellTypes) {
row.values().forEach((Object cell) -> {
if(cell instanceof String) {
aggregateCellTypes.add(cellTypeDetector.getType( (String) cell));
}
});
}
/**
*
* @param type
*/
@NbBundle.Messages({
"InterestingDatabasesIngestModule.FlagDatabases.setName=Selectors identified"
})
private BlackboardArtifact createArtifactGivenCellTypes(AbstractFile file,
String cellTypesMessage) throws TskCoreException {
BlackboardArtifact artifact = file.newArtifact(
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
BlackboardAttribute setNameAttribute = new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
Bundle.InterestingDatabasesIngestModule_FlagDatabases_setName());
artifact.addAttribute(setNameAttribute);
BlackboardAttribute commentAttribute = new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME,
cellTypesMessage);
artifact.addAttribute(commentAttribute);
return artifact;
}
/**
*
*/
@Override
public void shutDown() {
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.interestingdatabases;
package org.sleuthkit.autopsy.modules.databaseselector;
import org.sleuthkit.autopsy.coreutils.Version;
import org.openide.util.NbBundle;
@ -27,19 +27,18 @@ import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
/**
*
* @author dsmyda
* A factory that creates a DatabaseSelectorModule to parse database files and
* mark them as having interesting records (emails, phone numbers, mac addresses, gps coordinates).
*/
@NbBundle.Messages({
"InterestingDatabasesIngestModuleFactory.FlagDatabases.moduleName=Interesting Databases",
"InterestingDatabasesIngestModuleFactory.FlagDatabases.moduleDesc.text=Flags databases with interesting items (emails, phone numbers, gps coordinates, ip/mac addresses)"
"DatabaseSelectorIngestModuleFactory.FlagDatabases.moduleName=Database Selector",
"DatabaseSelectorIngestModuleFactory.FlagDatabases.moduleDesc.text=Flag databases with interesting items (emails, phone numbers, gps coordinates, ip/mac addresses)"
})
@ServiceProvider(service = IngestModuleFactory.class)
public class InterestingDatabasesIngestModuleFactory extends IngestModuleFactoryAdapter {
public class DatabaseSelectorIngestModuleFactory extends IngestModuleFactoryAdapter {
static String getModuleName() {
return Bundle.InterestingDatabasesIngestModuleFactory_FlagDatabases_moduleName();
return Bundle.DatabaseSelectorIngestModuleFactory_FlagDatabases_moduleName();
}
@Override
@ -49,7 +48,7 @@ public class InterestingDatabasesIngestModuleFactory extends IngestModuleFactory
@Override
public String getModuleDescription() {
return Bundle.InterestingDatabasesIngestModuleFactory_FlagDatabases_moduleDesc_text();
return Bundle.DatabaseSelectorIngestModuleFactory_FlagDatabases_moduleDesc_text();
}
@Override
@ -64,6 +63,6 @@ public class InterestingDatabasesIngestModuleFactory extends IngestModuleFactory
@Override
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings ingestOptions) {
return new InterestingDatabasesIngestModule();
return new DatabaseSelectorIngestModule();
}
}