/* * Sample module in the public domain. Feel free to use this as a template * for your modules. * * Contact: Brian Carrier [carrier <at> 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. */ package org.sleuthkit.autopsy.examples; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.ErrorInfo; import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.externalresults.ExternalResults; import org.sleuthkit.autopsy.externalresults.ExternalResultsImporter; import org.sleuthkit.autopsy.externalresults.ExternalResultsXMLParser; import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestMessage; import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Image; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Sample data source ingest module that doesn't do much. Demonstrates use of * utility classes: ExecUtils and the org.sleuthkit.autopsy.externalresults * package. */ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestModule { private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); private static final String moduleName = SampleExecutableIngestModuleFactory.getModuleName(); private final String fileInCaseDatabase = "/WINDOWS/system32/ntmsapi.dll"; // Probably private IngestJobContext context; private String outputDirPath; private String derivedFileInCaseDatabase; @Override public void startUp(IngestJobContext context) throws IngestModuleException { this.context = context; if (refCounter.incrementAndGet(context.getJobId()) == 1) { // Create an output directory for this job. outputDirPath = Case.getCurrentCase().getModuleDirectory() + File.separator + moduleName; //NON-NLS File outputDir = new File(outputDirPath); if (outputDir.exists() == false) { outputDir.mkdirs(); } } } @Override public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) { if (refCounter.get(context.getJobId()) == 1) { try { // There will be two tasks: data source analysis and import of // the results of the analysis. progressBar.switchToDeterminate(2); // Do the analysis. The following sample code could be used to // run an executable. In this case the executable would take // two command line arguments, the path to the data source to be // analyzed and the path to a results file to be generated. The // results file would be an an XML file (see org.sleuthkit.autopsy.externalresults.autopsy_external_results.xsd) // with instructions for the import of blackboard artifacts, // derived files, and reports generated by the analysis. In this // sample ingest module, the generation of the analysis results is // simulated. String resultsFilePath = outputDirPath + File.separator + String.format("job_%d_results.xml", context.getJobId()); boolean haveRealExecutable = false; if (haveRealExecutable) { if (dataSource instanceof Image) { Image image = (Image) dataSource; String dataSourcePath = image.getPaths()[0]; List<String> commandLine = new ArrayList<>(); commandLine.add("some.exe"); commandLine.add(dataSourcePath); commandLine.add(resultsFilePath); ProcessBuilder processBuilder = new ProcessBuilder(commandLine); ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); } // not a disk image else { return ProcessResult.OK; } } else { generateSimulatedResults(resultsFilePath); } progressBar.progress(1); // Import the results of the analysis. ExternalResultsXMLParser resultsParser = new ExternalResultsXMLParser(dataSource, resultsFilePath); ExternalResults results = resultsParser.parse(); List<ErrorInfo> errors = resultsParser.getErrorInfo(); ExternalResultsImporter importer = new ExternalResultsImporter(); errors.addAll(importer.importResults(results)); for (ErrorInfo errorInfo : errors) { IngestServices.getInstance().postMessage(IngestMessage.createErrorMessage(moduleName, "External Results Import Error", errorInfo.getMessage())); } progressBar.progress(2); } catch (ParserConfigurationException | TransformerException | IOException ex) { Logger logger = IngestServices.getInstance().getLogger(moduleName); logger.log(Level.SEVERE, "Failed to simulate analysis and results import", ex); //NON-NLS return ProcessResult.ERROR; } } return ProcessResult.OK; } private void generateSimulatedResults(String resultsFilePath) throws ParserConfigurationException, IOException, TransformerConfigurationException, TransformerException { List<String> derivedFilePaths = generateSimulatedDerivedFiles(); List<String> reportFilePaths = generateSimulatedReports(); generateSimulatedResultsFile(derivedFilePaths, reportFilePaths, resultsFilePath); } private List<String> generateSimulatedDerivedFiles() throws IOException { List<String> filePaths = new ArrayList<>(); String fileContents = "This is a simulated derived file."; for (int i = 0; i < 2; ++i) { String fileName = String.format("job_%d_derived_file_%d.txt", context.getJobId(), i); filePaths.add(generateFile(fileName, fileContents.getBytes())); if (i == 0) { this.derivedFileInCaseDatabase = this.fileInCaseDatabase + "/" + fileName; } } return filePaths; } private List<String> generateSimulatedReports() throws IOException { List<String> filePaths = new ArrayList<>(); String fileContents = "This is a simulated report."; for (int i = 0; i < 2; ++i) { String fileName = String.format("job_%d_report_%d.txt", context.getJobId(), i); filePaths.add(generateFile(fileName, fileContents.getBytes())); } return filePaths; } private String generateFile(String fileName, byte[] fileContents) throws IOException { String filePath = outputDirPath + File.separator + fileName; File file = new File(filePath); if (!file.exists()) { file.createNewFile(); } try (FileOutputStream fileStream = new FileOutputStream(file)) { fileStream.write(fileContents); fileStream.flush(); } return filePath; } private void generateSimulatedResultsFile(List<String> derivedFilePaths, List<String> reportPaths, String resultsFilePath) throws ParserConfigurationException, TransformerConfigurationException, TransformerException { // SAMPLE GENERATED BY THE CODE BELOW: // // <?xml version="1.0" encoding="UTF-8" standalone="no"?> // <autopsy_results> // <derived_files> // <derived_file> // <local_path>C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_derived_file_0.txt</local_path> // <parent_file>/WINDOWS/system32/ntmsapi.dll</parent_file> // </derived_file> // <derived_file> // <local_path>C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_derived_file_1.txt</local_path> // <parent_file>/WINDOWS/system32/ntmsapi.dll/job_1_derived_file_0.txt</parent_file> // </derived_file> // </derived_files> // <artifacts> // <artifact type="TSK_INTERESTING_FILE_HIT"> // <source_file>/WINDOWS/system32/ntmsapi.dll</source_file> // <attribute type="TSK_SET_NAME"> // <value>SampleInterestingFilesSet</value> // <source_module>Sample Executable Ingest Module</source_module> // </attribute> // </artifact> // <artifact type="SampleArtifactType"> // <source_file>/WINDOWS/system32/ntmsapi.dll/job_1_derived_file_0.txt</source_file> // <attribute type="SampleArtifactAttributeType"> // <value type="text">One</value> // </attribute> // <attribute type="SampleArtifactAttributeType"> // <value type="int32">2</value> // </attribute> // <attribute type="SampleArtifactAttributeType"> // <value type="int64">3</value> // </attribute> // <attribute type="SampleArtifactAttributeType"> // <value type="double">4.0</value> // </attribute> // </artifact> // </artifacts> // <reports> // <report> // <local_path>C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_report_0.txt</local_path> // <source_module>Sample Executable Ingest Module</source_module> // <report_name>Sample Report</report_name> // </report> // <report> // <local_path>C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_report_1.txt</local_path> // <source_module>Sample Executable Ingest Module</source_module> // </report> // </reports> // </autopsy_results> // Create the XML DOM document and the root element. DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.newDocument(); Element rootElement = doc.createElement(ExternalResultsXMLParser.TagNames.ROOT_ELEM.toString()); doc.appendChild(rootElement); // Add a derived files list element to the root element. Element derivedFilesListElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILES_LIST_ELEM.toString()); rootElement.appendChild(derivedFilesListElement); // Add derived file elements to the derived files list element. Each // file element gets required local path and parent file child elements. // Note that the local path of the derived file must be to a location in // the case directory or a subdirectory of the case directory and the // parent file must be specified using the path format used in the case // database, e.g., /WINDOWS/system32/ntmsapi.dll, where volume, file // system, etc. are not in the path. for (int i = 0; i < derivedFilePaths.size(); ++i) { String filePath = derivedFilePaths.get(i); Element derivedFileElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILE_ELEM.toString()); derivedFilesListElement.appendChild(derivedFileElement); Element localPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString()); localPathElement.setTextContent(filePath); derivedFileElement.appendChild(localPathElement); Element parentPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.PARENT_FILE_ELEM.toString()); if (i == 0) { parentPathElement.setTextContent(this.fileInCaseDatabase); } else { parentPathElement.setTextContent(this.derivedFileInCaseDatabase); } derivedFileElement.appendChild(parentPathElement); } // Add an artifacts list element to the root element. Element artifactsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACTS_LIST_ELEM.toString()); rootElement.appendChild(artifactsListElement); // Add an artifact element to the artifacts list element with the required // artifact type attribute. A standard artifact type is used as the type // attribute of this artifact element. Element artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString()); artifactElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getLabel()); artifactsListElement.appendChild(artifactElement); // Add the required source file element to the artifact element. Note // that source file must be either the local path of a derived file or a // file in the case database. Element fileElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_FILE_ELEM.toString()); fileElement.setTextContent(this.fileInCaseDatabase); artifactElement.appendChild(fileElement); // Add an artifact attribute element to the artifact element. A standard // artifact attribute type is used as the required type XML attribute of // the artifact attribute element. Element artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString()); artifactAttrElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ATTRIBUTE_TYPE.TSK_SET_NAME.getLabel()); artifactElement.appendChild(artifactAttrElement); // Add the required value element to the artifact attribute element, // with an optional type XML attribute of ExternalXML.VALUE_TYPE_TEXT, // which is the default. Element artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString()); artifactAttributeValueElement.setTextContent("SampleInterestingFilesSet"); artifactAttrElement.appendChild(artifactAttributeValueElement); // Add an optional source module element to the artifact attribute // element. Element artifactAttrSourceElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_MODULE_ELEM.toString()); artifactAttrSourceElement.setTextContent(moduleName); artifactAttrElement.appendChild(artifactAttrSourceElement); // Add an artifact element with a user-defined type. artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString()); artifactElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), "SampleArtifactType"); artifactsListElement.appendChild(artifactElement); // Add the required source file element. fileElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_FILE_ELEM.toString()); fileElement.setTextContent(this.derivedFileInCaseDatabase); artifactElement.appendChild(fileElement); // Add artifact attribute elements with user-defined types to the // artifact element, adding value elements of assorted types. for (int i = 0; i < 5; ++i) { artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString()); artifactAttrElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), "SampleArtifactAttributeType" + i); artifactElement.appendChild(artifactAttrElement); artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString()); switch (i) { case 0: artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_TEXT.toString()); artifactAttributeValueElement.setTextContent("One"); break; case 1: artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_INT32.toString()); artifactAttributeValueElement.setTextContent("2"); break; case 2: artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_INT64.toString()); artifactAttributeValueElement.setTextContent("3"); break; case 3: artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_DOUBLE.toString()); artifactAttributeValueElement.setTextContent("4.0"); break; case 4: artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_DATETIME.toString()); artifactAttributeValueElement.setTextContent("7023372036854775839"); break; } artifactAttrElement.appendChild(artifactAttributeValueElement); } // Add a reports list element to the root element. Element reportsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORTS_LIST_ELEM.toString()); rootElement.appendChild(reportsListElement); // Add report elements to the reports list element. Each report element // gets required local path and source module child elements. There is // also an optional report name element. Note that the local path of the // report must be to a location in the case directory or a subdirectory // of the case directory and the parent file must be specified using the // path format used in the case database, e.g., /WINDOWS/system32/ntmsapi.dll, // where volume, file system, etc. are not in the path. for (int i = 0; i < reportPaths.size(); ++i) { String reportPath = reportPaths.get(i); Element reportElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_ELEM.toString()); reportsListElement.appendChild(reportElement); Element reportPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString()); reportPathElement.setTextContent(reportPath); reportElement.appendChild(reportPathElement); Element reportSourceModuleElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_MODULE_ELEM.toString()); reportSourceModuleElement.setTextContent(moduleName); reportElement.appendChild(reportSourceModuleElement); if (i == 0) { Element reportNameElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_NAME_ELEM.toString()); reportNameElement.setTextContent("Sample Report"); reportElement.appendChild(reportNameElement); } } TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(new File(resultsFilePath)); transformer.transform(source, result); } }