diff --git a/Core/build.xml b/Core/build.xml index 32fff13e6e..bbf612c3d9 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -48,6 +48,11 @@ + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/iosanalyser/IosAnalyserIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/iosanalyser/IosAnalyserIngestModule.java new file mode 100644 index 0000000000..132d19746e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/iosanalyser/IosAnalyserIngestModule.java @@ -0,0 +1,241 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018-2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.modules.iosanalyser; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.Locale; +import static java.util.Objects.nonNull; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.stream.Collectors; +import org.openide.modules.InstalledFileLocator; +import org.openide.util.Cancellable; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.coreutils.ExecUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect; +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.IngestServices; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Blackboard; +import org.sleuthkit.datamodel.Blackboard.BlackboardException; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TL_EVENT_TYPE; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TimelineEventType; + +/** + * Data source ingest module that runs Plaso against the image. + */ +public class IosAnalyserIngestModule implements DataSourceIngestModule { + + private static final Logger logger = Logger.getLogger(IosAnalyserIngestModule.class.getName()); + private static final String MODULE_NAME = IosAnalyserModuleFactory.getModuleName(); + + private static final String ILEAPP = "ILEAPP"; //NON-NLS + private static final String ILEAPP_EXECUTABLE = "iLeapp.exe";//NON-NLS + + private File iLeappExecutable; + + private IngestJobContext context; + private Case currentCase; + private FileManager fileManager; + + private Image image; + private AbstractFile previousFile = null; // cache used when looking up files in Autopsy DB + + IosAnalyserIngestModule() { + + } + + @NbBundle.Messages({ + "IosAnalyserIngestModule.executable.not.found=Plaso Executable Not Found.", + "IosAnalyserIngestModule.requires.windows=Plaso module requires windows."}) + @Override + public void startUp(IngestJobContext context) throws IngestModuleException { + this.context = context; + + if (false == PlatformUtil.isWindowsOS()) { + throw new IngestModuleException(Bundle.IosAnalyserIngestModule_requires_windows()); + } + + try { + iLeappExecutable = locateExecutable(ILEAPP_EXECUTABLE); + } catch (FileNotFoundException exception) { + logger.log(Level.WARNING, "iLeapp executable not found.", exception); //NON-NLS + throw new IngestModuleException(Bundle.IosAnalyserIngestModule_executable_not_found(), exception); + } + + } + + @NbBundle.Messages({ + "IosAnalyserIngestModule.error.running.log2timeline=Error running log2timeline, see log file.", + "IosAnalyserIngestModule.error.running.psort=Error running Psort, see log file.", + "IosAnalyserIngestModule.error.creating.output.dir=Error creating Plaso module output directory.", + "IosAnalyserIngestModule.starting.log2timeline=Starting Log2timeline", + "IosAnalyserIngestModule.running.psort=Running Psort", + "IosAnalyserIngestModule.log2timeline.cancelled=Log2timeline run was canceled", + "IosAnalyserIngestModule.psort.cancelled=psort run was canceled", + "IosAnalyserIngestModule.bad.imageFile=Cannot find image file name and path", + "IosAnalyserIngestModule.completed=Plaso Processing Completed"}) + @Override + public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) { + + currentCase = Case.getCurrentCase(); + fileManager = currentCase.getServices().getFileManager(); + + String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS + Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime); + try { + Files.createDirectories(moduleOutputPath); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Error creating iLeapp module output directory.", ex); //NON-NLS + return ProcessResult.ERROR; + } + + List iLeappFilesToProcess = findiLeappFilesToProcess(dataSource); + + if (!iLeappFilesToProcess.isEmpty()) { + // Run iLeapp + for (AbstractFile iLeappFile: iLeappFilesToProcess) { + logger.log(Level.INFO, "Starting iLeapp Run.");//NON-NLS + statusHelper.progress(Bundle.IosAnalyserIngestModule_starting_log2timeline(), 0); + ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, iLeappFile.getLocalAbsPath(), iLeappFile.getNameExtension()); + try { + int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context)); + if (result != 0) { + logger.log(Level.SEVERE, String.format("Error running iLeapp, error code returned %d", result)); //NON-NLS + return ProcessResult.ERROR; + } + } catch (IOException ex) { + logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file %s", iLeappFile.getLocalAbsPath()), ex); + } + + if (context.dataSourceIngestIsCancelled()) { + logger.log(Level.INFO, "Log2timeline run was canceled"); //NON-NLS + return ProcessResult.OK; + } + } + +// if (Files.notExists(moduleOutputPath.resolve(PLASO))) { +// logger.log(Level.WARNING, "Error running log2timeline: there was no storage file."); //NON-NLS +// return ProcessResult.ERROR; +// } + + // parse the output and make artifacts +// createPlasoArtifacts(plasoFile.toString(), statusHelper); + + } + + IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, + Bundle.IosAnalyserIngestModule_has_run(), + Bundle.IosAnalyserIngestModule_completed()); + IngestServices.getInstance().postMessage(message); + return ProcessResult.OK; + } + + private List findiLeappFilesToProcess(Content dataSource) { + + List iLeappFiles = new ArrayList<>(); + + FileManager fileManager = getCurrentCase().getServices().getFileManager(); + + // findFiles use the SQL wildcard # in the file name + try { + iLeappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "No files found to process");; //NON-NLS + return iLeappFiles; + } + + List iLeappFilesToProcess = new ArrayList<>(); + for (AbstractFile iLeappFile: iLeappFiles) { + if ((iLeappFile.getName().toLowerCase().contains(".zip") || (iLeappFile.getName().toLowerCase().contains(".tar")) + || iLeappFile.getName().toLowerCase().contains(".tgz"))) { + iLeappFilesToProcess.add(iLeappFile); + } + } + + return iLeappFilesToProcess; + } + + private ProcessBuilder buildiLeappCommand(Path moduleOutputPath, String sourceFilePath, String iLeappFileSystemType) { + + ProcessBuilder processBuilder = buildProcessWithRunAsInvoker( + "\"" + iLeappExecutable + "\"", //NON-NLS + "-fs", iLeappFileSystemType, //NON-NLS + "-i", sourceFilePath, //NON-NLS + "-o", moduleOutputPath.toString() + ); + processBuilder.redirectError(moduleOutputPath.resolve("iLeapp_err.txt").toFile()); //NON-NLS + return processBuilder; + } + + static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) { + ProcessBuilder processBuilder = new ProcessBuilder(commandLine); + /* + * Add an environment variable to force log2timeline/psort to run with + * the same permissions Autopsy uses. + */ + processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS + return processBuilder; + } + + private static File locateExecutable(String executableName) throws FileNotFoundException { + String executableToFindName = Paths.get(ILEAPP, ILEAPP, executableName).toString(); + + File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, IosAnalyserIngestModule.class.getPackage().getName(), false); + if (null == exeFile || exeFile.canExecute() == false) { + throw new FileNotFoundException(executableName + " executable not found."); + } + return exeFile; + } + + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/iosanalyser/IosAnalyserModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/iosanalyser/IosAnalyserModuleFactory.java new file mode 100644 index 0000000000..7b4713bd3b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/iosanalyser/IosAnalyserModuleFactory.java @@ -0,0 +1,93 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018-2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.modules.iosanalyser; + +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.coreutils.Version; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; +import org.sleuthkit.autopsy.ingest.FileIngestModule; +import org.sleuthkit.autopsy.ingest.IngestModuleFactory; +import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; + +/** + * A factory that creates data source ingest modules that will run iLeapp against an + * logical files and saves the output to module output. + */ +@ServiceProvider(service = IngestModuleFactory.class) +public class IosAnalyserModuleFactory implements IngestModuleFactory { + + @NbBundle.Messages({"IosAnalyserModuleFactory_moduleName=iOSAnalyser"}) + static String getModuleName() { + return Bundle.IosAnalyserModuleFactory_moduleName(); + } + + @Override + public String getModuleDisplayName() { + return getModuleName(); + } + + @NbBundle.Messages({"IosAnalyserModuleFactory_moduleDesc=Runs iLeapp against files."}) + @Override + public String getModuleDescription() { + return Bundle.IosAnalyserModuleFactory_moduleDesc(); + } + + @Override + public String getModuleVersionNumber() { + return Version.getVersion(); + } + + @Override + public boolean isDataSourceIngestModuleFactory() { + return true; + } + + @Override + public boolean hasGlobalSettingsPanel() { + return false; + } + + @Override + public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() { + throw new UnsupportedOperationException(); + } + + @Override + public IngestModuleIngestJobSettings getDefaultIngestJobSettings() { + return new PlasoModuleSettings(); + } + + @Override + public boolean hasIngestJobSettingsPanel() { + return false; + } + + @Override + public boolean isFileIngestModuleFactory() { + return false; + } + + @Override + public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { + throw new UnsupportedOperationException(); + } +} diff --git a/thirdparty/iLeapp/LICENSE b/thirdparty/iLeapp/LICENSE new file mode 100644 index 0000000000..ae8fc549fc --- /dev/null +++ b/thirdparty/iLeapp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Brigs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. diff --git a/thirdparty/iLeapp/ileapp.exe b/thirdparty/iLeapp/ileapp.exe new file mode 100644 index 0000000000..8176b4f679 Binary files /dev/null and b/thirdparty/iLeapp/ileapp.exe differ