Autopsy  4.19.3
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 + "\"", //NON-NLS
293  "-t", iLeappFileSystemType, //NON-NLS
294  "-i", sourceFilePath, //NON-NLS
295  "-o", moduleOutputPath.toString()
296  );
297  processBuilder.redirectError(moduleOutputPath.resolve("iLeapp_err.txt").toFile()); //NON-NLS
298  processBuilder.redirectOutput(moduleOutputPath.resolve("iLeapp_out.txt").toFile()); //NON-NLS
299  return processBuilder;
300  }
301 
309  private ProcessBuilder buildiLeappListCommand(Path moduleOutputPath) {
310 
311  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
312  "\"" + iLeappExecutable + "\"", //NON-NLS
313  "-p"
314  );
315  processBuilder.redirectError(moduleOutputPath.resolve("iLeapp_paths_error.txt").toFile()); //NON-NLS
316  processBuilder.redirectOutput(moduleOutputPath.resolve("iLeapp_paths.txt").toFile()); //NON-NLS
317  return processBuilder;
318  }
319 
320  static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
321  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
322  /*
323  * Add an environment variable to force iLeapp to run with the same
324  * permissions Autopsy uses.
325  */
326  processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
327  return processBuilder;
328  }
329 
330  private static File locateExecutable(String executableName) throws FileNotFoundException {
331  String executableToFindName = Paths.get(ILEAPP, executableName).toString();
332 
333  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, ILeappAnalyzerIngestModule.class.getPackage().getName(), false);
334  if (null == exeFile || exeFile.canExecute() == false) {
335  throw new FileNotFoundException(executableName + " executable not found.");
336  }
337  return exeFile;
338  }
339 
344  private void addILeappReportToReports(Path iLeappOutputDir, Case currentCase) {
345  List<String> allIndexFiles;
346 
347  try (Stream<Path> walk = Files.walk(iLeappOutputDir)) {
348 
349  allIndexFiles = walk.map(x -> x.toString())
350  .filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
351 
352  if (!allIndexFiles.isEmpty()) {
353  // Check for existance of directory that holds report data if does not exist then report contains no data
354  String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
355  File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
356  if (dataFilesDir.exists()) {
357  currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ILeappAnalyzerIngestModule_report_name());
358  }
359  }
360 
361  } catch (IOException | UncheckedIOException | TskCoreException ex) {
362  // catch the error and continue on as report is not added
363  logger.log(Level.WARNING, String.format("Error finding index file in path %s", iLeappOutputDir.toString()), ex);
364  }
365 
366  }
367 
368  /*
369  * Reads the iLeapp paths file to get the paths that we want to extract
370  *
371  * @param moduleOutputPath path where the file paths output will reside
372  */
373  private List<String> loadIleappPathFile(Path moduleOutputPath) throws FileNotFoundException, IOException {
374  List<String> iLeappPathsToProcess = new ArrayList<>();
375 
376  Path filePath = Paths.get(moduleOutputPath.toString(), ILEAPP_PATHS_FILE);
377 
378  try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toString()))) {
379  String line = reader.readLine();
380  while (line != null) {
381  if (line.contains("path list generation") || line.length() < 2) {
382  line = reader.readLine();
383  continue;
384  }
385  iLeappPathsToProcess.add(line.trim());
386  line = reader.readLine();
387  }
388  }
389 
390  return iLeappPathsToProcess;
391  }
392 
400  private void extractFilesFromDataSource(Content dataSource, List<String> iLeappPathsToProcess, Path moduleOutputPath) {
401  FileManager fileManager = getCurrentCase().getServices().getFileManager();
402 
403  for (String fullFilePath : iLeappPathsToProcess) {
404 
405  if (context.dataSourceIngestIsCancelled()) {
406  logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
407  break;
408  }
409 
410  String ffp = fullFilePath.replaceAll("\\*", "%");
411  ffp = FilenameUtils.normalize(ffp, true);
412  String fileName = FilenameUtils.getName(ffp);
413  String filePath = FilenameUtils.getPath(ffp);
414 
415  List<AbstractFile> iLeappFiles;
416  try {
417  if (filePath.isEmpty()) {
418  iLeappFiles = fileManager.findFiles(dataSource, fileName); //NON-NLS
419  } else {
420  iLeappFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
421  }
422  } catch (TskCoreException ex) {
423  logger.log(Level.WARNING, "No files found to process"); //NON-NLS
424  return;
425  }
426 
427  for (AbstractFile iLeappFile : iLeappFiles) {
428  Path parentPath = Paths.get(moduleOutputPath.toString(), iLeappFile.getParentPath());
429  File fileParentPath = new File(parentPath.toString());
430 
431  extractFileToOutput(dataSource, iLeappFile, fileParentPath, parentPath);
432  }
433  }
434  }
435 
444  private void extractFileToOutput(Content dataSource, AbstractFile iLeappFile, File fileParentPath, Path parentPath) {
445  if (fileParentPath.exists()) {
446  if (!iLeappFile.isDir()) {
447  writeiLeappFile(dataSource, iLeappFile, fileParentPath.toString());
448  } else {
449  try {
450  Files.createDirectories(Paths.get(parentPath.toString(), iLeappFile.getName()));
451  } catch (IOException ex) {
452  logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
453  }
454  }
455  } else {
456  try {
457  Files.createDirectories(parentPath);
458  } catch (IOException ex) {
459  logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
460  }
461  if (!iLeappFile.isDir()) {
462  writeiLeappFile(dataSource, iLeappFile, fileParentPath.toString());
463  } else {
464  try {
465  Files.createDirectories(Paths.get(parentPath.toString(), iLeappFile.getName()));
466  } catch (IOException ex) {
467  logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
468  }
469  }
470  }
471  }
472 
480  private void writeiLeappFile(Content dataSource, AbstractFile iLeappFile, String parentPath) {
481  String fileName = iLeappFile.getName().replace(":", "-");
482  if (!fileName.matches(".") && !fileName.matches("..") && !fileName.toLowerCase().endsWith("-slack")) {
483  Path filePath = Paths.get(parentPath, fileName);
484  File localFile = new File(filePath.toString());
485  try {
486  ContentUtils.writeToFile(iLeappFile, localFile, context::dataSourceIngestIsCancelled);
487  } catch (ReadContentInputStream.ReadContentInputStreamException ex) {
488  logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).",
489  iLeappFile.getName(), iLeappFile.getId()), ex); //NON-NLS
490  } catch (IOException ex) {
491  logger.log(Level.WARNING, String.format("Error writing file local file '%s' (id=%d).",
492  filePath.toString(), iLeappFile.getId()), ex); //NON-NLS
493  }
494  }
495  }
496 
501  private void writeErrorMsgToIngestInbox() {
503  MODULE_NAME,
504  Bundle.ILeappAnalyzerIngestModule_error_running_iLeapp());
506  }
507 
508 }
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:1926
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 Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.