mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
All the meat of the new module classes
This commit is contained in:
parent
d74c8b6212
commit
8a3daf4110
@ -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
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user