diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java index 7a14d02610..089faa85ac 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java @@ -70,21 +70,16 @@ class SampleDataSourceIngestModule implements DataSourceIngestModule { @Override public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) { - if (context.dataSourceIngestIsCancelled()) { - return IngestModule.ProcessResult.OK; - } // There are two tasks to do. progressBar.switchToDeterminate(2); - Case autopsyCase = Case.getCurrentCase(); - SleuthkitCase sleuthkitCase = autopsyCase.getSleuthkitCase(); - Services services = new Services(sleuthkitCase); - FileManager fileManager = services.getFileManager(); try { // Get count of files with .doc extension. - long fileCount = 0; + FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); List docFiles = fileManager.findFiles(dataSource, "%.doc"); + + long fileCount = 0; for (AbstractFile docFile : docFiles) { if (!skipKnownFiles || docFile.getKnown() != TskData.FileKnown.KNOWN) { ++fileCount; @@ -92,6 +87,7 @@ class SampleDataSourceIngestModule implements DataSourceIngestModule { } progressBar.progress(1); + // check if we were cancelled if (context.dataSourceIngestIsCancelled()) { return IngestModule.ProcessResult.OK; } diff --git a/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py b/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py new file mode 100755 index 0000000000..4712ddacb9 --- /dev/null +++ b/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py @@ -0,0 +1,176 @@ +# Sample module in the public domain. Feel free to use this as a template +# for your modules (and you can remove this header and take complete credit +# and liability) +# +# Contact: Brian Carrier [carrier sleuthkit [dot] org] +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +# Simple data source-level ingest module for Autopsy. +# Used as part of Python tutorials from Basis Technology - August 2015 +# +# Looks for files of a given name, opens then in SQLite, queries the DB, +# and makes artifacts + +import jarray +import inspect +import os +from java.lang import Class +from java.lang import System +from java.sql import DriverManager, SQLException +from java.util.logging import Level +from java.io import File +from org.sleuthkit.datamodel import SleuthkitCase +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import ReadContentInputStream +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.autopsy.ingest import IngestModule +from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException +from org.sleuthkit.autopsy.ingest import DataSourceIngestModule +from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter +from org.sleuthkit.autopsy.ingest import IngestMessage +from org.sleuthkit.autopsy.ingest import IngestServices +from org.sleuthkit.autopsy.ingest import ModuleDataEvent +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Services +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.datamodel import ContentUtils + + +# Factory that defines the name and details of the module and allows Autopsy +# to create instances of the modules that will do the analysis. +class ContactsDbIngestModuleFactory(IngestModuleFactoryAdapter): + + moduleName = "Contacts Db Analyzer" + + def getModuleDisplayName(self): + return self.moduleName + + def getModuleDescription(self): + return "Sample module that parses contacts.db" + + def getModuleVersionNumber(self): + return "1.0" + + def isDataSourceIngestModuleFactory(self): + return True + + def createDataSourceIngestModule(self, ingestOptions): + return ContactsDbIngestModule() + + +# Data Source-level ingest module. One gets created per data source. +class ContactsDbIngestModule(DataSourceIngestModule): + + _logger = Logger.getLogger(ContactsDbIngestModuleFactory.moduleName) + + def log(self, level, msg): + self._logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg) + + def __init__(self): + self.context = None + + # Where any setup and configuration is done + # 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext. + # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html + def startUp(self, context): + self.context = context + # Throw an IngestModule.IngestModuleException exception if there was a problem setting up + # raise IngestModuleException("Oh No!") + + # Where the analysis is done. + # The 'dataSource' object being passed in is of type org.sleuthkit.datamodel.Content. + # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html + # 'progressBar' is of type org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress + # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html + def process(self, dataSource, progressBar): + + # we don't know how much work there is yet + progressBar.switchToIndeterminate() + + # Find files named contacts.db, regardless of parent path + fileManager = Case.getCurrentCase().getServices().getFileManager() + files = fileManager.findFiles(dataSource, "contacts.db") + + numFiles = len(files) + progressBar.switchToDeterminate(numFiles) + fileCount = 0; + for file in files: + + # Check if the user pressed cancel while we were busy + if self.context.isJobCancelled(): + return IngestModule.ProcessResult.OK + + self.log(Level.INFO, "Processing file: " + file.getName()) + fileCount += 1 + + # Save the DB locally in the temp folder. use file id as name to reduce collisions + lclDbPath = os.path.join(Case.getCurrentCase().getTempDirectory(), str(file.getId()) + ".db") + ContentUtils.writeToFile(file, File(lclDbPath)) + + # Open the DB using JDBC + Class.forName("org.sqlite.JDBC").newInstance() + dbConn = DriverManager.getConnection("jdbc:sqlite:%s" % lclDbPath) + + # Query the contacts table in the database and get all columns. + stmt = dbConn.createStatement() + resultSet = stmt.executeQuery("SELECT * FROM contacts") + + # Cycle through each row and create artifacts + while resultSet.next(): + + # Make an artifact on the blackboard, TSK_CONTACT and give it attributes for each of the fields + art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) + + name = resultSet.getString("name") + art.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON.getTypeID(), + ContactsDbIngestModuleFactory.moduleName, name)) + + email = resultSet.getString("email") + art.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getTypeID(), + ContactsDbIngestModuleFactory.moduleName, email)) + + phone = resultSet.getString("phone") + art.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), + ContactsDbIngestModuleFactory.moduleName, phone)) + + # Fire an event to notify the UI and others that there are new artifacts + IngestServices.getInstance().fireModuleDataEvent( + ModuleDataEvent(ContactsDbIngestModuleFactory.moduleName, + BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT, None)) + + # Clean up + stmt.close() + dbConn.close() + os.remove(lclDbPath) + + + # After all databases, post a message to the ingest messages in box. + message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, + "ContactsDb Analyzer", "Found %d files" % fileCount) + IngestServices.getInstance().postMessage(message) + + return IngestModule.ProcessResult.OK \ No newline at end of file diff --git a/pythonExamples/Aug2015DataSourceTutorial/README.txt b/pythonExamples/Aug2015DataSourceTutorial/README.txt new file mode 100755 index 0000000000..3740d9cdfa --- /dev/null +++ b/pythonExamples/Aug2015DataSourceTutorial/README.txt @@ -0,0 +1,7 @@ +This folder contains files that were created for an August 2015 Tutorial from Basis Technology. + +It contains the following: +- FindContactsDb.py: Script to find databases and parse them. +- Contacts.db: Sample database with the correct name and schema for the FindContactsDb.py script. +- RunExe.py: Script that runs img_stat.exe on disk images. +- img_stat.exe: 32-bit version of img_stat.exe from The Sleuth Kit that is needed by RunExe.py. \ No newline at end of file diff --git a/pythonExamples/Aug2015DataSourceTutorial/RunExe.py b/pythonExamples/Aug2015DataSourceTutorial/RunExe.py new file mode 100755 index 0000000000..606e73712d --- /dev/null +++ b/pythonExamples/Aug2015DataSourceTutorial/RunExe.py @@ -0,0 +1,144 @@ +# Sample module in the public domain. Feel free to use this as a template +# for your modules (and you can remove this header and take complete credit +# and liability) +# +# Contact: Brian Carrier [carrier sleuthkit [dot] org] +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +# Simple data source-level ingest module for Autopsy. +# Used as part of Python tutorials from Basis Technology - August 2015 +# +# Runs img_stat tool from The Sleuth Kit on each data source, saves the +# output, and adds a report to the Case for the output + +import jarray +import inspect +import os +import subprocess +from java.lang import Class +from java.lang import System +from java.util.logging import Level +from org.sleuthkit.datamodel import SleuthkitCase +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import ReadContentInputStream +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel import Image +from org.sleuthkit.autopsy.ingest import IngestModule +from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException +from org.sleuthkit.autopsy.ingest import DataSourceIngestModule +from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter +from org.sleuthkit.autopsy.ingest import IngestMessage +from org.sleuthkit.autopsy.ingest import IngestServices +from org.sleuthkit.autopsy.ingest import ModuleDataEvent +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import PlatformUtil +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Services +from org.sleuthkit.autopsy.datamodel import ContentUtils + + +# Factory that defines the name and details of the module and allows Autopsy +# to create instances of the modules that will do the analysis. +class RunExeIngestModuleFactory(IngestModuleFactoryAdapter): + + moduleName = "Run EXE Module" + + def getModuleDisplayName(self): + return self.moduleName + + def getModuleDescription(self): + return "Sample module that runs img_stat on each disk image." + + def getModuleVersionNumber(self): + return "1.0" + + def isDataSourceIngestModuleFactory(self): + return True + + def createDataSourceIngestModule(self, ingestOptions): + return RunExeIngestModule() + + +# Data Source-level ingest module. One gets created per data source. +class RunExeIngestModule(DataSourceIngestModule): + + _logger = Logger.getLogger(RunExeIngestModuleFactory.moduleName) + + def log(self, level, msg): + self._logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg) + + def __init__(self): + self.context = None + + # Where any setup and configuration is done + # 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext. + # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html + def startUp(self, context): + self.context = context + + # Get path to EXE based on where this script is run from. + # Assumes EXE is in same folder as script + # Verify it is there before any ingest starts + self.path_to_exe = os.path.join(os.path.dirname(os.path.abspath(__file__)), "img_stat.exe") + if not os.path.exists(self.path_to_exe): + raise IngestModuleException("EXE was not found in module folder") + + # Where the analysis is done. + # The 'dataSource' object being passed in is of type org.sleuthkit.datamodel.Content. + # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html + # 'progressBar' is of type org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress + # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html + def process(self, dataSource, progressBar): + + # we don't know how much work there will be + progressBar.switchToIndeterminate() + + # Example has only a Windows EXE, so bail if we aren't on Windows + if not PlatformUtil.isWindowsOS(): + self.log(Level.INFO, "Ignoring data source. Not running on Windows") + return IngestModule.ProcessResult.OK + + # Verify we have a disk image and not a folder of files + if not isinstance(dataSource, Image): + self.log(Level.INFO, "Ignoring data source. Not an image") + return IngestModule.ProcessResult.OK + + # Get disk image paths + imagePaths = dataSource.getPaths() + + # We'll save our output to a file in the reports folder, named based on EXE and data source ID + reportPath = os.path.join(Case.getCurrentCase().getCaseDirectory(), "Reports", "img_stat-" + str(dataSource.getId()) + ".txt") + reportHandle = open(reportPath, 'w') + + # Run the EXE, saving output to the report + self.log(Level.INFO, "Running program on data source") + subprocess.Popen([self.path_to_exe, imagePaths[0]], stdout=reportHandle).communicate()[0] + reportHandle.close() + + # Add the report to the case, so it shows up in the tree + Case.getCurrentCase().addReport(reportPath, "Run EXE", "img_stat output") + + return IngestModule.ProcessResult.OK \ No newline at end of file diff --git a/pythonExamples/Aug2015DataSourceTutorial/contacts.db b/pythonExamples/Aug2015DataSourceTutorial/contacts.db new file mode 100755 index 0000000000..0fe4d9168b Binary files /dev/null and b/pythonExamples/Aug2015DataSourceTutorial/contacts.db differ diff --git a/pythonExamples/Aug2015DataSourceTutorial/img_stat.exe b/pythonExamples/Aug2015DataSourceTutorial/img_stat.exe new file mode 100755 index 0000000000..7ccd9954ec Binary files /dev/null and b/pythonExamples/Aug2015DataSourceTutorial/img_stat.exe differ diff --git a/pythonExamples/dataSourceIngestModule.py b/pythonExamples/dataSourceIngestModule.py index 6dde622796..802a2fb619 100755 --- a/pythonExamples/dataSourceIngestModule.py +++ b/pythonExamples/dataSourceIngestModule.py @@ -98,7 +98,7 @@ class SampleJythonDataSourceIngestModule(DataSourceIngestModule): def startUp(self, context): self.context = context # Throw an IngestModule.IngestModuleException exception if there was a problem setting up - # raise IngestModuleException(IngestModule(), "Oh No!") + # raise IngestModuleException("Oh No!") # Where the analysis is done. # The 'dataSource' object being passed in is of type org.sleuthkit.datamodel.Content. @@ -107,20 +107,14 @@ class SampleJythonDataSourceIngestModule(DataSourceIngestModule): # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html # TODO: Add your analysis code in here. def process(self, dataSource, progressBar): - if self.context.isJobCancelled(): - return IngestModule.ProcessResult.OK # we don't know how much work there is yet progressBar.switchToIndeterminate() - autopsyCase = Case.getCurrentCase() - sleuthkitCase = autopsyCase.getSleuthkitCase() - services = Services(sleuthkitCase) - fileManager = services.getFileManager() - # For our example, we will use FileManager to get all # files with the word "test" # in the name and then count and read them + fileManager = Case.getCurrentCase().getServices().getFileManager() files = fileManager.findFiles(dataSource, "%test%") numFiles = len(files) diff --git a/pythonExamples/fileIngestModule.py b/pythonExamples/fileIngestModule.py index 0fa774a69a..42200dcb1a 100755 --- a/pythonExamples/fileIngestModule.py +++ b/pythonExamples/fileIngestModule.py @@ -99,7 +99,7 @@ class SampleJythonFileIngestModule(FileIngestModule): self.filesFound = 0 # Throw an IngestModule.IngestModuleException exception if there was a problem setting up - # raise IngestModuleException(IngestModule(), "Oh No!") + # raise IngestModuleException("Oh No!") pass # Where the analysis is done. Each file will be passed into here. diff --git a/pythonExamples/fileIngestModuleWithGui.py b/pythonExamples/fileIngestModuleWithGui.py index 842bce83be..f34c7289b6 100755 --- a/pythonExamples/fileIngestModuleWithGui.py +++ b/pythonExamples/fileIngestModuleWithGui.py @@ -130,7 +130,7 @@ class SampleFileIngestModuleWithUI(FileIngestModule): self.log(Level.INFO, "flag is not set") # Throw an IngestModule.IngestModuleException exception if there was a problem setting up - # raise IngestModuleException(IngestModule(), "Oh No!") + # raise IngestModuleException("Oh No!") pass # Where the analysis is done. Each file will be passed into here.