Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ALeappAnalyzerIngestModule.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(ALeappAnalyzerIngestModule.class.getName());
67  private static final String MODULE_NAME = ALeappAnalyzerModuleFactory.getModuleName();
68 
69  private static final String ALEAPP = "aLeapp"; //NON-NLS
70  private static final String ALEAPP_FS = "fs_"; //NON-NLS
71  private static final String ALEAPP_EXECUTABLE = "aleapp.exe";//NON-NLS
72  private static final String ALEAPP_PATHS_FILE = "aLeapp_paths.txt"; //NON-NLS
73 
74  private static final String XMLFILE = "aleapp-artifact-attribute-reference.xml"; //NON-NLS
75 
76  private File aLeappExecutable;
77 
79 
81 
83  // This constructor is intentionally empty. Nothing special is needed here.
84  }
85 
86  @NbBundle.Messages({
87  "ALeappAnalyzerIngestModule.executable.not.found=aLeapp Executable Not Found.",
88  "ALeappAnalyzerIngestModule.requires.windows=aLeapp module requires windows.",
89  "ALeappAnalyzerIngestModule.error.aleapp.file.processor.init=Failure to initialize aLeappProcessFile"})
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(), "AleappAnalyzerIngestModule.not.64.bit.os"));
96  }
97 
98  if (false == PlatformUtil.isWindowsOS()) {
99  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_requires_windows());
100  }
101 
102  try {
103  aLeappFileProcessor = new LeappFileProcessor(XMLFILE, ALeappAnalyzerModuleFactory.getModuleName(), ALEAPP, context);
104  } catch (IOException | IngestModuleException | NoCurrentCaseException ex) {
105  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_error_aleapp_file_processor_init(), ex);
106  }
107 
108  try {
109  aLeappExecutable = locateExecutable(ALEAPP_EXECUTABLE);
110  } catch (FileNotFoundException exception) {
111  logger.log(Level.WARNING, "aLeapp executable not found.", exception); //NON-NLS
112  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_executable_not_found(), exception);
113  }
114 
115  }
116 
117  @NbBundle.Messages({
118  "ALeappAnalyzerIngestModule.error.running.aLeapp=Error running aLeapp, see log file.",
119  "ALeappAnalyzerIngestModule.error.creating.output.dir=Error creating aLeapp module output directory.",
120  "ALeappAnalyzerIngestModule.running.aLeapp=Running aLeapp",
121  "ALeappAnalyzerIngestModule_processing_aLeapp_results=Processing aLeapp results",
122  "ALeappAnalyzerIngestModule.has.run=aLeapp",
123  "ALeappAnalyzerIngestModule.aLeapp.cancelled=aLeapp run was canceled",
124  "ALeappAnalyzerIngestModule.completed=aLeapp Processing Completed",
125  "ALeappAnalyzerIngestModule.report.name=aLeapp Html Report"})
126  @Override
127  public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
128 
129  statusHelper.switchToIndeterminate();
130  statusHelper.progress(Bundle.ALeappAnalyzerIngestModule_running_aLeapp());
131 
132  Case currentCase = Case.getCurrentCase();
133  Path tempOutputPath = Paths.get(currentCase.getTempDirectory(), ALEAPP, ALEAPP_FS + dataSource.getId());
134  try {
135  Files.createDirectories(tempOutputPath);
136  } catch (IOException ex) {
137  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", tempOutputPath.toString()), ex);
139  return ProcessResult.ERROR;
140  }
141 
142  List<String> aLeappPathsToProcess;
143  ProcessBuilder aLeappCommand = buildaLeappListCommand(tempOutputPath);
144  try {
145  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
146  if (result != 0) {
147  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
149  return ProcessResult.ERROR;
150  }
151  aLeappPathsToProcess = loadAleappPathFile(tempOutputPath);
152  if (aLeappPathsToProcess.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 aLeapp 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> aLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource);
170  if (!aLeappFilesToProcess.isEmpty()) {
171  statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
172  Integer filesProcessedCount = 0;
173  for (AbstractFile aLeappFile : aLeappFilesToProcess) {
174  processALeappFile(dataSource, currentCase, statusHelper, filesProcessedCount, aLeappFile);
175  filesProcessedCount++;
176  }
177  }
178  }
179 
180  statusHelper.switchToIndeterminate();
181  statusHelper.progress(Bundle.ALeappAnalyzerIngestModule_processing_aLeapp_results());
182  extractFilesFromDataSource(dataSource, aLeappPathsToProcess, tempOutputPath);
183  processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
184 
186  Bundle.ALeappAnalyzerIngestModule_has_run(),
187  Bundle.ALeappAnalyzerIngestModule_completed());
189  return ProcessResult.OK;
190  }
191 
202  private void processALeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount,
203  AbstractFile aLeappFile) {
204  statusHelper.progress(NbBundle.getMessage(this.getClass(), "ALeappAnalyzerIngestModule.processing.file", aLeappFile.getName()), filesProcessedCount);
205  String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
206  Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ALEAPP, currentTime);
207  try {
208  Files.createDirectories(moduleOutputPath);
209  } catch (IOException ex) {
210  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", moduleOutputPath.toString()), ex);
211  return;
212  }
213 
214  ProcessBuilder aLeappCommand = buildaLeappCommand(moduleOutputPath, aLeappFile.getLocalAbsPath(), aLeappFile.getNameExtension());
215  try {
216  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
217  if (result != 0) {
218  logger.log(Level.WARNING, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
219  return;
220  }
221 
222  addALeappReportToReports(moduleOutputPath, currentCase);
223 
224  } catch (IOException ex) {
225  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program against file %s", aLeappFile.getLocalAbsPath()), ex);
226  return;
227  }
228 
229  if (context.dataSourceIngestIsCancelled()) {
230  logger.log(Level.INFO, "aLeapp Analyser ingest module run was canceled"); //NON-NLS
231  return;
232  }
233 
234  aLeappFileProcessor.processFiles(dataSource, moduleOutputPath, aLeappFile, statusHelper);
235  }
236 
246  private void processALeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess) {
247  statusHelper.progress(NbBundle.getMessage(this.getClass(), "ALeappAnalyzerIngestModule.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(), ALEAPP, currentTime);
250  try {
251  Files.createDirectories(moduleOutputPath);
252  } catch (IOException ex) {
253  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", moduleOutputPath.toString()), ex);
254  return;
255  }
256 
257  ProcessBuilder aLeappCommand = buildaLeappCommand(moduleOutputPath, directoryToProcess, "fs");
258  try {
259  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
260  if (result != 0) {
261  logger.log(Level.WARNING, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
262  return;
263  }
264 
265  addALeappReportToReports(moduleOutputPath, currentCase);
266 
267  } catch (IOException ex) {
268  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program against file system"), ex);
269  return;
270  }
271 
272  if (context.dataSourceIngestIsCancelled()) {
273  logger.log(Level.INFO, "aLeapp Analyser ingest module run was canceled"); //NON-NLS
274  return;
275  }
276 
277  aLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath, statusHelper);
278  }
279 
289  private ProcessBuilder buildaLeappCommand(Path moduleOutputPath, String sourceFilePath, String aLeappFileSystemType) {
290 
291  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
292  aLeappExecutable.getAbsolutePath(), //NON-NLS
293  "-t", aLeappFileSystemType, //NON-NLS
294  "-i", sourceFilePath, //NON-NLS
295  "-o", moduleOutputPath.toString(),
296  "-w"
297  );
298  processBuilder.directory(moduleOutputPath.toFile());
299  processBuilder.redirectError(moduleOutputPath.resolve("aLeapp_err.txt").toFile()); //NON-NLS
300  processBuilder.redirectOutput(moduleOutputPath.resolve("aLeapp_out.txt").toFile()); //NON-NLS
301  return processBuilder;
302  }
303 
304  private ProcessBuilder buildaLeappListCommand(Path moduleOutputPath) {
305 
306  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
307  aLeappExecutable.getAbsolutePath(), //NON-NLS
308  "-p"
309  );
310  // leapp process creates a text file in addition to outputting to stdout.
311  processBuilder.directory(moduleOutputPath.toFile());
312  processBuilder.redirectError(moduleOutputPath.resolve("aLeapp_paths_error.txt").toFile()); //NON-NLS
313  processBuilder.redirectOutput(moduleOutputPath.resolve("aLeapp_paths.txt").toFile()); //NON-NLS
314  return processBuilder;
315  }
316 
317  static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
318  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
319  /*
320  * Add an environment variable to force aLeapp to run with the same
321  * permissions Autopsy uses.
322  */
323  processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
324  return processBuilder;
325  }
326 
327  private static File locateExecutable(String executableName) throws FileNotFoundException {
328  String executableToFindName = Paths.get(ALEAPP, executableName).toString();
329 
330  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, ALeappAnalyzerIngestModule.class.getPackage().getName(), false);
331  if (null == exeFile || exeFile.canExecute() == false) {
332  throw new FileNotFoundException(executableName + " executable not found.");
333  }
334  return exeFile;
335  }
336 
341  private void addALeappReportToReports(Path aLeappOutputDir, Case currentCase) {
342  List<String> allIndexFiles = new ArrayList<>();
343 
344  try (Stream<Path> walk = Files.walk(aLeappOutputDir)) {
345 
346  allIndexFiles = walk.map(x -> x.toString())
347  .filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
348 
349  if (!allIndexFiles.isEmpty()) {
350  // Check for existance of directory that holds report data if does not exist then report contains no data
351  String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
352  File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
353  if (dataFilesDir.exists()) {
354  currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ALeappAnalyzerIngestModule_report_name());
355  }
356  }
357 
358  } catch (IOException | UncheckedIOException | TskCoreException ex) {
359  // catch the error and continue on as report is not added
360  logger.log(Level.WARNING, String.format("Error finding index file in path %s", aLeappOutputDir.toString()), ex);
361  }
362 
363  }
364 
365  /*
366  * Reads the aLeapp paths file to get the paths that we want to extract
367  *
368  */
369  private List<String> loadAleappPathFile(Path moduleOutputPath) throws FileNotFoundException, IOException {
370  List<String> aLeappPathsToProcess = new ArrayList<>();
371 
372  Path filePath = Paths.get(moduleOutputPath.toString(), ALEAPP_PATHS_FILE);
373 
374  try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toString()))) {
375  String line = reader.readLine();
376  while (line != null) {
377  if (line.contains("path list generation") || line.length() < 2) {
378  line = reader.readLine();
379  continue;
380  }
381  aLeappPathsToProcess.add(line.trim());
382  line = reader.readLine();
383  }
384  }
385 
386  return aLeappPathsToProcess;
387  }
388 
389  private void extractFilesFromDataSource(Content dataSource, List<String> aLeappPathsToProcess, Path moduleOutputPath) {
390  FileManager fileManager = getCurrentCase().getServices().getFileManager();
391 
392  for (String fullFilePath : aLeappPathsToProcess) {
393 
394  if (context.dataSourceIngestIsCancelled()) {
395  logger.log(Level.INFO, "aLeapp Analyser ingest module run was canceled"); //NON-NLS
396  break;
397  }
398 
399  String ffp = fullFilePath.replaceAll("\\*", "%");
400  ffp = FilenameUtils.normalize(ffp, true);
401  String fileName = FilenameUtils.getName(ffp);
402  String filePath = FilenameUtils.getPath(ffp);
403 
404  List<AbstractFile> aLeappFiles = new ArrayList<>();
405  try {
406  if (filePath.isEmpty()) {
407  aLeappFiles = fileManager.findFiles(dataSource, fileName); //NON-NLS
408  } else {
409  aLeappFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
410  }
411  } catch (TskCoreException ex) {
412  logger.log(Level.WARNING, "No files found to process"); //NON-NLS
413  return;
414  }
415 
416  for (AbstractFile aLeappFile : aLeappFiles) {
417  Path parentPath = Paths.get(moduleOutputPath.toString(), aLeappFile.getParentPath());
418  File fileParentPath = new File(parentPath.toString());
419 
420  extractFileToOutput(dataSource, aLeappFile, fileParentPath, parentPath);
421  }
422  }
423  }
424 
425  private void extractFileToOutput(Content dataSource, AbstractFile aLeappFile, File fileParentPath, Path parentPath) {
426  if (fileParentPath.exists()) {
427  if (!aLeappFile.isDir()) {
428  writeaLeappFile(dataSource, aLeappFile, fileParentPath.toString());
429  } else {
430  try {
431  Files.createDirectories(Paths.get(parentPath.toString(), aLeappFile.getName()));
432  } catch (IOException ex) {
433  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
434  }
435  }
436  } else {
437  try {
438  Files.createDirectories(parentPath);
439  } catch (IOException ex) {
440  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
441  }
442  if (!aLeappFile.isDir()) {
443  writeaLeappFile(dataSource, aLeappFile, fileParentPath.toString());
444  } else {
445  try {
446  Files.createDirectories(Paths.get(parentPath.toString(), aLeappFile.getName()));
447  } catch (IOException ex) {
448  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
449  }
450  }
451  }
452  }
453 
454  private void writeaLeappFile(Content dataSource, AbstractFile aLeappFile, String parentPath) {
455  String fileName = aLeappFile.getName().replace(":", "-");
456  if (!fileName.matches(".") && !fileName.matches("..") && !fileName.toLowerCase().endsWith("-slack")) {
457  Path filePath = Paths.get(parentPath, fileName);
458  File localFile = new File(filePath.toString());
459  try {
460  ContentUtils.writeToFile(aLeappFile, localFile, context::dataSourceIngestIsCancelled);
461  } catch (ReadContentInputStream.ReadContentInputStreamException ex) {
462  logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).",
463  aLeappFile.getName(), aLeappFile.getId()), ex); //NON-NLS
464  } catch (IOException ex) {
465  logger.log(Level.WARNING, String.format("Error writing file local file '%s' (id=%d).",
466  filePath.toString(), aLeappFile.getId()), ex); //NON-NLS
467  }
468  }
469  }
470 
475  private void writeErrorMsgToIngestInbox() {
477  MODULE_NAME,
478  Bundle.ALeappAnalyzerIngestModule_error_running_aLeapp());
480  }
481 
482 }
static int execute(ProcessBuilder processBuilder)
Definition: ExecUtil.java:172
void processALeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount, AbstractFile aLeappFile)
List< AbstractFile > findFiles(String fileName)
void extractFilesFromDataSource(Content dataSource, List< String > aLeappPathsToProcess, Path moduleOutputPath)
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)
ProcessBuilder buildaLeappCommand(Path moduleOutputPath, String sourceFilePath, String aLeappFileSystemType)
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1919
void processALeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess)
void writeaLeappFile(Content dataSource, AbstractFile aLeappFile, String parentPath)
void postMessage(final IngestMessage message)
ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper)
ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath, DataSourceIngestModuleProgress progress)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void extractFileToOutput(Content dataSource, AbstractFile aLeappFile, File fileParentPath, Path 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.