Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ILeappAnalyzerIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020-2021 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.modules.leappanalyzers;
20 
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.FileReader;
25 import java.io.IOException;
26 import java.io.UncheckedIOException;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.text.SimpleDateFormat;
31 import java.util.List;
32 import java.util.ArrayList;
33 import java.util.Locale;
34 import java.util.logging.Level;
35 import java.util.stream.Collectors;
36 import java.util.stream.Stream;
37 import org.apache.commons.io.FilenameUtils;
38 import org.openide.modules.InstalledFileLocator;
39 import org.openide.util.NbBundle;
55 import org.sleuthkit.datamodel.AbstractFile;
56 import org.sleuthkit.datamodel.Content;
57 import org.sleuthkit.datamodel.LocalFilesDataSource;
58 import org.sleuthkit.datamodel.ReadContentInputStream;
59 import org.sleuthkit.datamodel.TskCoreException;
60 
65 
66  private static final Logger logger = Logger.getLogger(ILeappAnalyzerIngestModule.class.getName());
67  private static final String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName();
68 
69  private static final String ILEAPP = "iLeapp"; //NON-NLS
70  private static final String ILEAPP_FS = "fs_"; //NON-NLS
71  private static final String ILEAPP_EXECUTABLE = "ileapp.exe";//NON-NLS
72  private static final String ILEAPP_PATHS_FILE = "iLeapp_paths.txt"; //NON-NLS
73 
74  private static final String XMLFILE = "ileapp-artifact-attribute-reference.xml"; //NON-NLS
75 
76  private File iLeappExecutable;
77 
79 
81 
83  // This constructor is intentionally empty. Nothing special is needed here.
84  }
85 
86  @NbBundle.Messages({
87  "ILeappAnalyzerIngestModule.executable.not.found=iLeapp Executable Not Found.",
88  "ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows.",
89  "ILeappAnalyzerIngestModule.error.ileapp.file.processor.init=Failure to initialize ILeappProcessFile"})
90  @Override
91  public void startUp(IngestJobContext context) throws IngestModuleException {
92  this.context = context;
93 
94  if (false == PlatformUtil.is64BitOS()) {
95  throw new IngestModuleException(NbBundle.getMessage(this.getClass(), "IleappAnalyzerIngestModule.not.64.bit.os"));
96  }
97 
98  if (false == PlatformUtil.isWindowsOS()) {
99  throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_requires_windows());
100  }
101 
102  try {
103  iLeappFileProcessor = new LeappFileProcessor(XMLFILE, ILeappAnalyzerModuleFactory.getModuleName(), ILEAPP, context);
104  } catch (IOException | IngestModuleException | NoCurrentCaseException ex) {
105  throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_error_ileapp_file_processor_init(), ex);
106  }
107 
108  try {
109  iLeappExecutable = locateExecutable(ILEAPP_EXECUTABLE);
110  } catch (FileNotFoundException exception) {
111  logger.log(Level.WARNING, "iLeapp executable not found.", exception); //NON-NLS
112  throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_executable_not_found(), exception);
113  }
114 
115  }
116 
117  @NbBundle.Messages({
118  "ILeappAnalyzerIngestModule.error.running.iLeapp=Error running iLeapp, see log file.",
119  "ILeappAnalyzerIngestModule.error.creating.output.dir=Error creating iLeapp module output directory.",
120  "ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp",
121  "ILeappAnalyzerIngestModule_processing_iLeapp_results=Processing iLeapp results",
122  "ILeappAnalyzerIngestModule.has.run=iLeapp",
123  "ILeappAnalyzerIngestModule.iLeapp.cancelled=iLeapp run was canceled",
124  "ILeappAnalyzerIngestModule.completed=iLeapp Processing Completed",
125  "ILeappAnalyzerIngestModule.report.name=iLeapp Html Report"})
126  @Override
127  public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
128 
129  statusHelper.switchToIndeterminate();
130  statusHelper.progress(Bundle.ILeappAnalyzerIngestModule_running_iLeapp());
131 
132  Case currentCase = Case.getCurrentCase();
133  Path tempOutputPath = Paths.get(currentCase.getTempDirectory(), ILEAPP, ILEAPP_FS + dataSource.getId());
134  try {
135  Files.createDirectories(tempOutputPath);
136  } catch (IOException ex) {
137  logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", tempOutputPath.toString()), ex);
139  return ProcessResult.ERROR;
140  }
141 
142  List<String> iLeappPathsToProcess;
143  ProcessBuilder iLeappCommand = buildiLeappListCommand(tempOutputPath);
144  try {
145  int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
146  if (result != 0) {
147  logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
149  return ProcessResult.ERROR;
150  }
151  iLeappPathsToProcess = loadIleappPathFile(tempOutputPath);
152  if (iLeappPathsToProcess.isEmpty()) {
153  logger.log(Level.SEVERE, String.format("Error getting file paths to search, list is empty"));
155  return ProcessResult.ERROR;
156  }
157  } catch (IOException ex) {
158  logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program getting file paths to search"), ex);
160  return ProcessResult.ERROR;
161  }
162 
163  if ((context.getDataSource() instanceof LocalFilesDataSource)) {
164  /*
165  * The data source may be local files from an iOS file system, or it
166  * may be a tarred/ZIP of an iOS file system. If it is the latter,
167  * extract the files we need to process.
168  */
169  List<AbstractFile> iLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource);
170  if (!iLeappFilesToProcess.isEmpty()) {
171  statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
172  Integer filesProcessedCount = 0;
173  for (AbstractFile iLeappFile : iLeappFilesToProcess) {
174  processILeappFile(dataSource, currentCase, statusHelper, filesProcessedCount, iLeappFile);
175  filesProcessedCount++;
176  }
177  }
178  }
179 
180  statusHelper.switchToIndeterminate();
181  statusHelper.progress(Bundle.ILeappAnalyzerIngestModule_processing_iLeapp_results());
182  extractFilesFromDataSource(dataSource, iLeappPathsToProcess, tempOutputPath);
183  processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
184 
186  Bundle.ILeappAnalyzerIngestModule_has_run(),
187  Bundle.ILeappAnalyzerIngestModule_completed());
189  return ProcessResult.OK;
190  }
191 
202  private void processILeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount,
203  AbstractFile iLeappFile) {
204  statusHelper.progress(NbBundle.getMessage(this.getClass(), "ILeappAnalyzerIngestModule.processing.file", iLeappFile.getName()), filesProcessedCount);
205 
206  String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
207  Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime);
208  try {
209  Files.createDirectories(moduleOutputPath);
210  } catch (IOException ex) {
211  logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", moduleOutputPath.toString()), ex);
212  return;
213  }
214 
215  ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, iLeappFile.getLocalAbsPath(), iLeappFile.getNameExtension());
216  try {
217  int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
218  if (result != 0) {
219  logger.log(Level.WARNING, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
220  return;
221  }
222 
223  addILeappReportToReports(moduleOutputPath, currentCase);
224 
225  } catch (IOException ex) {
226  logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file %s", iLeappFile.getLocalAbsPath()), ex);
227  return;
228  }
229 
230  if (context.dataSourceIngestIsCancelled()) {
231  logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
232  return;
233  }
234 
235  iLeappFileProcessor.processFiles(dataSource, moduleOutputPath, iLeappFile, statusHelper);
236  }
237 
246  private void processILeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess) {
247  statusHelper.progress(NbBundle.getMessage(this.getClass(), "ILeappAnalyzerIngestModule.processing.filesystem"));
248  String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
249  Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime);
250  try {
251  Files.createDirectories(moduleOutputPath);
252  } catch (IOException ex) {
253  logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", moduleOutputPath.toString()), ex);
254  return;
255  }
256 
257  ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, directoryToProcess, "fs");
258  try {
259  int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
260  if (result != 0) {
261  logger.log(Level.WARNING, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
262  return;
263  }
264 
265  addILeappReportToReports(moduleOutputPath, currentCase);
266 
267  } catch (IOException ex) {
268  logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file system"), ex);
269  return;
270  }
271 
272  if (context.dataSourceIngestIsCancelled()) {
273  logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
274  return;
275  }
276 
277  iLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath, statusHelper);
278  }
279 
289  private ProcessBuilder buildiLeappCommand(Path moduleOutputPath, String sourceFilePath, String iLeappFileSystemType) {
290 
291  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
292  iLeappExecutable.getAbsolutePath(), //NON-NLS
293  "-t", iLeappFileSystemType, //NON-NLS
294  "-i", sourceFilePath, //NON-NLS
295  "-o", moduleOutputPath.toString()
296  );
297  processBuilder.directory(moduleOutputPath.toFile());
298  processBuilder.redirectError(moduleOutputPath.resolve("iLeapp_err.txt").toFile()); //NON-NLS
299  processBuilder.redirectOutput(moduleOutputPath.resolve("iLeapp_out.txt").toFile()); //NON-NLS
300  return processBuilder;
301  }
302 
310  private ProcessBuilder buildiLeappListCommand(Path moduleOutputPath) {
311 
312  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
313  iLeappExecutable.getAbsolutePath(), //NON-NLS
314  "-p"
315  );
316  // leapp process also outputs a file to the working directory in addition to stdout.
317  processBuilder.directory(moduleOutputPath.toFile());
318  processBuilder.redirectError(moduleOutputPath.resolve("iLeapp_paths_error.txt").toFile()); //NON-NLS
319  processBuilder.redirectOutput(moduleOutputPath.resolve("iLeapp_paths.txt").toFile()); //NON-NLS
320  return processBuilder;
321  }
322 
323  static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
324  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
325  /*
326  * Add an environment variable to force iLeapp to run with the same
327  * permissions Autopsy uses.
328  */
329  processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
330  return processBuilder;
331  }
332 
333  private static File locateExecutable(String executableName) throws FileNotFoundException {
334  String executableToFindName = Paths.get(ILEAPP, executableName).toString();
335 
336  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, ILeappAnalyzerIngestModule.class.getPackage().getName(), false);
337  if (null == exeFile || exeFile.canExecute() == false) {
338  throw new FileNotFoundException(executableName + " executable not found.");
339  }
340  return exeFile;
341  }
342 
347  private void addILeappReportToReports(Path iLeappOutputDir, Case currentCase) {
348  List<String> allIndexFiles;
349 
350  try (Stream<Path> walk = Files.walk(iLeappOutputDir)) {
351 
352  allIndexFiles = walk.map(x -> x.toString())
353  .filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
354 
355  if (!allIndexFiles.isEmpty()) {
356  // Check for existance of directory that holds report data if does not exist then report contains no data
357  String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
358  File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
359  if (dataFilesDir.exists()) {
360  currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ILeappAnalyzerIngestModule_report_name());
361  }
362  }
363 
364  } catch (IOException | UncheckedIOException | TskCoreException ex) {
365  // catch the error and continue on as report is not added
366  logger.log(Level.WARNING, String.format("Error finding index file in path %s", iLeappOutputDir.toString()), ex);
367  }
368 
369  }
370 
371  /*
372  * Reads the iLeapp paths file to get the paths that we want to extract
373  *
374  * @param moduleOutputPath path where the file paths output will reside
375  */
376  private List<String> loadIleappPathFile(Path moduleOutputPath) throws FileNotFoundException, IOException {
377  List<String> iLeappPathsToProcess = new ArrayList<>();
378 
379  Path filePath = Paths.get(moduleOutputPath.toString(), ILEAPP_PATHS_FILE);
380 
381  try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toString()))) {
382  String line = reader.readLine();
383  while (line != null) {
384  if (line.contains("path list generation") || line.length() < 2) {
385  line = reader.readLine();
386  continue;
387  }
388  iLeappPathsToProcess.add(line.trim());
389  line = reader.readLine();
390  }
391  }
392 
393  return iLeappPathsToProcess;
394  }
395 
403  private void extractFilesFromDataSource(Content dataSource, List<String> iLeappPathsToProcess, Path moduleOutputPath) {
404  FileManager fileManager = getCurrentCase().getServices().getFileManager();
405 
406  for (String fullFilePath : iLeappPathsToProcess) {
407 
408  if (context.dataSourceIngestIsCancelled()) {
409  logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
410  break;
411  }
412 
413  String ffp = fullFilePath.replaceAll("\\*", "%");
414  ffp = FilenameUtils.normalize(ffp, true);
415  String fileName = FilenameUtils.getName(ffp);
416  String filePath = FilenameUtils.getPath(ffp);
417 
418  List<AbstractFile> iLeappFiles;
419  try {
420  if (filePath.isEmpty()) {
421  iLeappFiles = fileManager.findFiles(dataSource, fileName); //NON-NLS
422  } else {
423  iLeappFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
424  }
425  } catch (TskCoreException ex) {
426  logger.log(Level.WARNING, "No files found to process"); //NON-NLS
427  return;
428  }
429 
430  for (AbstractFile iLeappFile : iLeappFiles) {
431  Path parentPath = Paths.get(moduleOutputPath.toString(), iLeappFile.getParentPath());
432  File fileParentPath = new File(parentPath.toString());
433 
434  extractFileToOutput(dataSource, iLeappFile, fileParentPath, parentPath);
435  }
436  }
437  }
438 
447  private void extractFileToOutput(Content dataSource, AbstractFile iLeappFile, File fileParentPath, Path parentPath) {
448  if (fileParentPath.exists()) {
449  if (!iLeappFile.isDir()) {
450  writeiLeappFile(dataSource, iLeappFile, fileParentPath.toString());
451  } else {
452  try {
453  Files.createDirectories(Paths.get(parentPath.toString(), iLeappFile.getName()));
454  } catch (IOException ex) {
455  logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
456  }
457  }
458  } else {
459  try {
460  Files.createDirectories(parentPath);
461  } catch (IOException ex) {
462  logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
463  }
464  if (!iLeappFile.isDir()) {
465  writeiLeappFile(dataSource, iLeappFile, fileParentPath.toString());
466  } else {
467  try {
468  Files.createDirectories(Paths.get(parentPath.toString(), iLeappFile.getName()));
469  } catch (IOException ex) {
470  logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
471  }
472  }
473  }
474  }
475 
483  private void writeiLeappFile(Content dataSource, AbstractFile iLeappFile, String parentPath) {
484  String fileName = iLeappFile.getName().replace(":", "-");
485  if (!fileName.matches(".") && !fileName.matches("..") && !fileName.toLowerCase().endsWith("-slack")) {
486  Path filePath = Paths.get(parentPath, fileName);
487  File localFile = new File(filePath.toString());
488  try {
489  ContentUtils.writeToFile(iLeappFile, localFile, context::dataSourceIngestIsCancelled);
490  } catch (ReadContentInputStream.ReadContentInputStreamException ex) {
491  logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).",
492  iLeappFile.getName(), iLeappFile.getId()), ex); //NON-NLS
493  } catch (IOException ex) {
494  logger.log(Level.WARNING, String.format("Error writing file local file '%s' (id=%d).",
495  filePath.toString(), iLeappFile.getId()), ex); //NON-NLS
496  }
497  }
498  }
499 
504  private void writeErrorMsgToIngestInbox() {
506  MODULE_NAME,
507  Bundle.ILeappAnalyzerIngestModule_error_running_iLeapp());
509  }
510 
511 }
void processILeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess)
static int execute(ProcessBuilder processBuilder)
Definition: ExecUtil.java:172
List< AbstractFile > findFiles(String fileName)
void extractFileToOutput(Content dataSource, AbstractFile iLeappFile, File fileParentPath, Path parentPath)
static IngestMessage createMessage(MessageType messageType, String source, String subject, String detailsHtml)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1919
ProcessBuilder buildiLeappCommand(Path moduleOutputPath, String sourceFilePath, String iLeappFileSystemType)
void extractFilesFromDataSource(Content dataSource, List< String > iLeappPathsToProcess, Path moduleOutputPath)
void postMessage(final IngestMessage message)
ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath, DataSourceIngestModuleProgress progress)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper)
void processILeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount, AbstractFile iLeappFile)
void writeiLeappFile(Content dataSource, AbstractFile iLeappFile, String parentPath)
ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile, DataSourceIngestModuleProgress progress)
static synchronized IngestServices getInstance()

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.