Autopsy  4.19.3
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 + "\"", //NON-NLS
293  "-t", aLeappFileSystemType, //NON-NLS
294  "-i", sourceFilePath, //NON-NLS
295  "-o", moduleOutputPath.toString(),
296  "-w"
297  );
298  processBuilder.redirectError(moduleOutputPath.resolve("aLeapp_err.txt").toFile()); //NON-NLS
299  processBuilder.redirectOutput(moduleOutputPath.resolve("aLeapp_out.txt").toFile()); //NON-NLS
300  return processBuilder;
301  }
302 
303  private ProcessBuilder buildaLeappListCommand(Path moduleOutputPath) {
304 
305  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
306  "\"" + aLeappExecutable + "\"", //NON-NLS
307  "-p"
308  );
309  processBuilder.redirectError(moduleOutputPath.resolve("aLeapp_paths_error.txt").toFile()); //NON-NLS
310  processBuilder.redirectOutput(moduleOutputPath.resolve("aLeapp_paths.txt").toFile()); //NON-NLS
311  return processBuilder;
312  }
313 
314  static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
315  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
316  /*
317  * Add an environment variable to force aLeapp to run with the same
318  * permissions Autopsy uses.
319  */
320  processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
321  return processBuilder;
322  }
323 
324  private static File locateExecutable(String executableName) throws FileNotFoundException {
325  String executableToFindName = Paths.get(ALEAPP, executableName).toString();
326 
327  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, ALeappAnalyzerIngestModule.class.getPackage().getName(), false);
328  if (null == exeFile || exeFile.canExecute() == false) {
329  throw new FileNotFoundException(executableName + " executable not found.");
330  }
331  return exeFile;
332  }
333 
338  private void addALeappReportToReports(Path aLeappOutputDir, Case currentCase) {
339  List<String> allIndexFiles = new ArrayList<>();
340 
341  try (Stream<Path> walk = Files.walk(aLeappOutputDir)) {
342 
343  allIndexFiles = walk.map(x -> x.toString())
344  .filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
345 
346  if (!allIndexFiles.isEmpty()) {
347  // Check for existance of directory that holds report data if does not exist then report contains no data
348  String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
349  File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
350  if (dataFilesDir.exists()) {
351  currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ALeappAnalyzerIngestModule_report_name());
352  }
353  }
354 
355  } catch (IOException | UncheckedIOException | TskCoreException ex) {
356  // catch the error and continue on as report is not added
357  logger.log(Level.WARNING, String.format("Error finding index file in path %s", aLeappOutputDir.toString()), ex);
358  }
359 
360  }
361 
362  /*
363  * Reads the aLeapp paths file to get the paths that we want to extract
364  *
365  */
366  private List<String> loadAleappPathFile(Path moduleOutputPath) throws FileNotFoundException, IOException {
367  List<String> aLeappPathsToProcess = new ArrayList<>();
368 
369  Path filePath = Paths.get(moduleOutputPath.toString(), ALEAPP_PATHS_FILE);
370 
371  try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toString()))) {
372  String line = reader.readLine();
373  while (line != null) {
374  if (line.contains("path list generation") || line.length() < 2) {
375  line = reader.readLine();
376  continue;
377  }
378  aLeappPathsToProcess.add(line.trim());
379  line = reader.readLine();
380  }
381  }
382 
383  return aLeappPathsToProcess;
384  }
385 
386  private void extractFilesFromDataSource(Content dataSource, List<String> aLeappPathsToProcess, Path moduleOutputPath) {
387  FileManager fileManager = getCurrentCase().getServices().getFileManager();
388 
389  for (String fullFilePath : aLeappPathsToProcess) {
390 
391  if (context.dataSourceIngestIsCancelled()) {
392  logger.log(Level.INFO, "aLeapp Analyser ingest module run was canceled"); //NON-NLS
393  break;
394  }
395 
396  String ffp = fullFilePath.replaceAll("\\*", "%");
397  ffp = FilenameUtils.normalize(ffp, true);
398  String fileName = FilenameUtils.getName(ffp);
399  String filePath = FilenameUtils.getPath(ffp);
400 
401  List<AbstractFile> aLeappFiles = new ArrayList<>();
402  try {
403  if (filePath.isEmpty()) {
404  aLeappFiles = fileManager.findFiles(dataSource, fileName); //NON-NLS
405  } else {
406  aLeappFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
407  }
408  } catch (TskCoreException ex) {
409  logger.log(Level.WARNING, "No files found to process"); //NON-NLS
410  return;
411  }
412 
413  for (AbstractFile aLeappFile : aLeappFiles) {
414  Path parentPath = Paths.get(moduleOutputPath.toString(), aLeappFile.getParentPath());
415  File fileParentPath = new File(parentPath.toString());
416 
417  extractFileToOutput(dataSource, aLeappFile, fileParentPath, parentPath);
418  }
419  }
420  }
421 
422  private void extractFileToOutput(Content dataSource, AbstractFile aLeappFile, File fileParentPath, Path parentPath) {
423  if (fileParentPath.exists()) {
424  if (!aLeappFile.isDir()) {
425  writeaLeappFile(dataSource, aLeappFile, fileParentPath.toString());
426  } else {
427  try {
428  Files.createDirectories(Paths.get(parentPath.toString(), aLeappFile.getName()));
429  } catch (IOException ex) {
430  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
431  }
432  }
433  } else {
434  try {
435  Files.createDirectories(parentPath);
436  } catch (IOException ex) {
437  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
438  }
439  if (!aLeappFile.isDir()) {
440  writeaLeappFile(dataSource, aLeappFile, fileParentPath.toString());
441  } else {
442  try {
443  Files.createDirectories(Paths.get(parentPath.toString(), aLeappFile.getName()));
444  } catch (IOException ex) {
445  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
446  }
447  }
448  }
449  }
450 
451  private void writeaLeappFile(Content dataSource, AbstractFile aLeappFile, String parentPath) {
452  String fileName = aLeappFile.getName().replace(":", "-");
453  if (!fileName.matches(".") && !fileName.matches("..") && !fileName.toLowerCase().endsWith("-slack")) {
454  Path filePath = Paths.get(parentPath, fileName);
455  File localFile = new File(filePath.toString());
456  try {
457  ContentUtils.writeToFile(aLeappFile, localFile, context::dataSourceIngestIsCancelled);
458  } catch (ReadContentInputStream.ReadContentInputStreamException ex) {
459  logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).",
460  aLeappFile.getName(), aLeappFile.getId()), ex); //NON-NLS
461  } catch (IOException ex) {
462  logger.log(Level.WARNING, String.format("Error writing file local file '%s' (id=%d).",
463  filePath.toString(), aLeappFile.getId()), ex); //NON-NLS
464  }
465  }
466  }
467 
472  private void writeErrorMsgToIngestInbox() {
474  MODULE_NAME,
475  Bundle.ALeappAnalyzerIngestModule_error_running_aLeapp());
477  }
478 
479 }
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:1926
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 Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.