Autopsy  4.17.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 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 = "aleap-artifact-attribute-reference.xml"; //NON-NLS
75 
76 
77  private File aLeappExecutable;
78 
80 
82 
84  // This constructor is intentionally empty. Nothing special is needed here.
85  }
86 
87  @NbBundle.Messages({
88  "ALeappAnalyzerIngestModule.executable.not.found=aLeapp Executable Not Found.",
89  "ALeappAnalyzerIngestModule.requires.windows=aLeapp module requires windows.",
90  "ALeappAnalyzerIngestModule.error.ileapp.file.processor.init=Failure to initialize aLeappProcessFile"})
91  @Override
92  public void startUp(IngestJobContext context) throws IngestModuleException {
93  this.context = context;
94 
95  if (false == PlatformUtil.isWindowsOS()) {
96  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_requires_windows());
97  }
98 
99  try {
100  aLeappFileProcessor = new LeappFileProcessor(XMLFILE);
101  } catch (IOException | IngestModuleException | NoCurrentCaseException ex) {
102  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_error_ileapp_file_processor_init(), ex);
103  }
104 
105  try {
106  aLeappExecutable = locateExecutable(ALEAPP_EXECUTABLE);
107  } catch (FileNotFoundException exception) {
108  logger.log(Level.WARNING, "aLeapp executable not found.", exception); //NON-NLS
109  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_executable_not_found(), exception);
110  }
111 
112  }
113 
114  @NbBundle.Messages({
115  "ALeappAnalyzerIngestModule.error.running.aLeapp=Error running aLeapp, see log file.",
116  "ALeappAnalyzerIngestModule.error.creating.output.dir=Error creating aLeapp module output directory.",
117  "ALeappAnalyzerIngestModule.starting.aLeapp=Starting aLeapp",
118  "ALeappAnalyzerIngestModule.running.aLeapp=Running aLeapp",
119  "ALeappAnalyzerIngestModule.has.run=aLeapp",
120  "ALeappAnalyzerIngestModule.aLeapp.cancelled=aLeapp run was canceled",
121  "ALeappAnalyzerIngestModule.completed=aLeapp Processing Completed",
122  "ALeappAnalyzerIngestModule.report.name=aLeapp Html Report"})
123  @Override
124  public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
125 
126  Case currentCase = Case.getCurrentCase();
127  Path tempOutputPath = Paths.get(currentCase.getTempDirectory(), ALEAPP, ALEAPP_FS + dataSource.getId());
128  try {
129  Files.createDirectories(tempOutputPath);
130  } catch (IOException ex) {
131  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", tempOutputPath.toString()), ex);
132  return ProcessResult.ERROR;
133  }
134 
135  List<String> aLeappPathsToProcess = new ArrayList<>();
136  ProcessBuilder aLeappCommand = buildaLeappListCommand(tempOutputPath);
137  try {
138  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
139  if (result != 0) {
140  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
141  return ProcessResult.ERROR;
142  }
143  aLeappPathsToProcess = loadIleappPathFile(tempOutputPath);
144  } catch (IOException ex) {
145  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program getting file paths to search"), ex);
146  return ProcessResult.ERROR;
147  }
148 
149  statusHelper.progress(Bundle.ALeappAnalyzerIngestModule_starting_aLeapp(), 0);
150 
151  List<AbstractFile> aLeappFilesToProcess = new ArrayList<>();
152 
153  if (!(context.getDataSource() instanceof LocalFilesDataSource)) {
154  extractFilesFromImage(dataSource, aLeappPathsToProcess, tempOutputPath);
155  statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
156  processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
157  } else {
158  aLeappFilesToProcess = findaLeappFilesToProcess(dataSource);
159  statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
160 
161  Integer filesProcessedCount = 0;
162  for (AbstractFile aLeappFile : aLeappFilesToProcess) {
163  processALeappFile(dataSource, currentCase, statusHelper, filesProcessedCount, aLeappFile);
164  filesProcessedCount++;
165  }
166  // Process the logical image as a fs in aLeapp to make sure this is not a logical fs that was added
167  extractFilesFromImage(dataSource, aLeappPathsToProcess, tempOutputPath);
168  processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
169  }
170 
172  Bundle.ALeappAnalyzerIngestModule_has_run(),
173  Bundle.ALeappAnalyzerIngestModule_completed());
175  return ProcessResult.OK;
176  }
177 
186  private void processALeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount,
187  AbstractFile aLeappFile) {
188  String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
189  Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ALEAPP, currentTime);
190  try {
191  Files.createDirectories(moduleOutputPath);
192  } catch (IOException ex) {
193  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", moduleOutputPath.toString()), ex);
194  return;
195  }
196 
197  statusHelper.progress(NbBundle.getMessage(this.getClass(), "ALeappAnalyzerIngestModule.processing.file", aLeappFile.getName()), filesProcessedCount);
198  ProcessBuilder aLeappCommand = buildaLeappCommand(moduleOutputPath, aLeappFile.getLocalAbsPath(), aLeappFile.getNameExtension());
199  try {
200  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
201  if (result != 0) {
202  logger.log(Level.WARNING, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
203  return;
204  }
205 
206  addILeappReportToReports(moduleOutputPath, currentCase);
207 
208  } catch (IOException ex) {
209  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program against file %s", aLeappFile.getLocalAbsPath()), ex);
210  return;
211  }
212 
213  if (context.dataSourceIngestIsCancelled()) {
214  logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
215  return;
216  }
217 
218  ProcessResult fileProcessorResult = aLeappFileProcessor.processFiles(dataSource, moduleOutputPath, aLeappFile);
219 
220  if (fileProcessorResult == ProcessResult.ERROR) {
221  return;
222  }
223  }
224 
232  private void processALeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess) {
233  String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
234  Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ALEAPP, currentTime);
235  try {
236  Files.createDirectories(moduleOutputPath);
237  } catch (IOException ex) {
238  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", moduleOutputPath.toString()), ex);
239  return;
240  }
241 
242  statusHelper.progress(NbBundle.getMessage(this.getClass(), "ALeappAnalyzerIngestModule.processing.filesystem"));
243  ProcessBuilder aLeappCommand = buildaLeappCommand(moduleOutputPath, directoryToProcess, "fs");
244  try {
245  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
246  if (result != 0) {
247  logger.log(Level.WARNING, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
248  return;
249  }
250 
251  addILeappReportToReports(moduleOutputPath, currentCase);
252 
253  } catch (IOException ex) {
254  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program against file system"), ex);
255  return;
256  }
257 
258  if (context.dataSourceIngestIsCancelled()) {
259  logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
260  return;
261  }
262 
263  ProcessResult fileProcessorResult = aLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath);
264 
265  if (fileProcessorResult == ProcessResult.ERROR) {
266  return;
267  }
268 
269  }
270 
278  private List<AbstractFile> findaLeappFilesToProcess(Content dataSource) {
279 
280  List<AbstractFile> aLeappFiles = new ArrayList<>();
281 
282  FileManager fileManager = getCurrentCase().getServices().getFileManager();
283 
284  // findFiles use the SQL wildcard % in the file name
285  try {
286  aLeappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS
287  } catch (TskCoreException ex) {
288  logger.log(Level.WARNING, "No files found to process"); //NON-NLS
289  return aLeappFiles;
290  }
291 
292  List<AbstractFile> aLeappFilesToProcess = new ArrayList<>();
293  for (AbstractFile aLeappFile : aLeappFiles) {
294  if (((aLeappFile.getLocalAbsPath() != null)
295  && (!aLeappFile.getNameExtension().isEmpty() && (!aLeappFile.isVirtual())))
296  && ((aLeappFile.getName().toLowerCase().contains(".zip") || (aLeappFile.getName().toLowerCase().contains(".tar")))
297  || aLeappFile.getName().toLowerCase().contains(".tgz"))) {
298  aLeappFilesToProcess.add(aLeappFile);
299 
300  }
301  }
302 
303  return aLeappFilesToProcess;
304  }
305 
315  private ProcessBuilder buildaLeappCommand(Path moduleOutputPath, String sourceFilePath, String aLeappFileSystemType) {
316 
317  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
318  "\"" + aLeappExecutable + "\"", //NON-NLS
319  "-t", aLeappFileSystemType, //NON-NLS
320  "-i", sourceFilePath, //NON-NLS
321  "-o", moduleOutputPath.toString(),
322  "-w"
323  );
324  processBuilder.redirectError(moduleOutputPath.resolve("aLeapp_err.txt").toFile()); //NON-NLS
325  processBuilder.redirectOutput(moduleOutputPath.resolve("aLeapp_out.txt").toFile()); //NON-NLS
326  return processBuilder;
327  }
328 
329  private ProcessBuilder buildaLeappListCommand(Path moduleOutputPath) {
330 
331  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
332  "\"" + aLeappExecutable + "\"", //NON-NLS
333  "-p"
334  );
335  processBuilder.redirectError(moduleOutputPath.resolve("aLeapp_paths_error.txt").toFile()); //NON-NLS
336  processBuilder.redirectOutput(moduleOutputPath.resolve("aLeapp_paths.txt").toFile()); //NON-NLS
337  return processBuilder;
338  }
339 
340  static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
341  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
342  /*
343  * Add an environment variable to force aLeapp to run with
344  * the same permissions Autopsy uses.
345  */
346  processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
347  return processBuilder;
348  }
349 
350  private static File locateExecutable(String executableName) throws FileNotFoundException {
351  String executableToFindName = Paths.get(ALEAPP, executableName).toString();
352 
353  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, ALeappAnalyzerIngestModule.class.getPackage().getName(), false);
354  if (null == exeFile || exeFile.canExecute() == false) {
355  throw new FileNotFoundException(executableName + " executable not found.");
356  }
357  return exeFile;
358  }
359 
364  private void addILeappReportToReports(Path aLeappOutputDir, Case currentCase) {
365  List<String> allIndexFiles = new ArrayList<>();
366 
367  try (Stream<Path> walk = Files.walk(aLeappOutputDir)) {
368 
369  allIndexFiles = walk.map(x -> x.toString())
370  .filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
371 
372  if (!allIndexFiles.isEmpty()) {
373  // Check for existance of directory that holds report data if does not exist then report contains no data
374  String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
375  File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
376  if (dataFilesDir.exists()) {
377  currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ALeappAnalyzerIngestModule_report_name());
378  }
379  }
380 
381  } catch (IOException | UncheckedIOException | TskCoreException ex) {
382  // catch the error and continue on as report is not added
383  logger.log(Level.WARNING, String.format("Error finding index file in path %s", aLeappOutputDir.toString()), ex);
384  }
385 
386  }
387 
388  /*
389  * Reads the aLeapp paths file to get the paths that we want to extract
390  *
391  */
392  private List<String> loadIleappPathFile(Path moduleOutputPath) throws FileNotFoundException, IOException {
393  List<String> aLeappPathsToProcess = new ArrayList<>();
394 
395  Path filePath = Paths.get(moduleOutputPath.toString(), ALEAPP_PATHS_FILE);
396 
397  try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toString()))) {
398  String line = reader.readLine();
399  while (line != null) {
400  if (line.contains("path list generation") || line.length() < 2) {
401  line = reader.readLine();
402  continue;
403  }
404  aLeappPathsToProcess.add(line.trim());
405  line = reader.readLine();
406  }
407  }
408 
409  return aLeappPathsToProcess;
410  }
411 
412  private void extractFilesFromImage(Content dataSource, List<String> aLeappPathsToProcess, Path moduleOutputPath) {
413  FileManager fileManager = getCurrentCase().getServices().getFileManager();
414 
415  for (String fullFilePath : aLeappPathsToProcess) {
416 
417  if (context.dataSourceIngestIsCancelled()) {
418  logger.log(Level.INFO, "aLeapp Analyser ingest module run was canceled"); //NON-NLS
419  break;
420  }
421 
422  String ffp = fullFilePath.replaceAll("\\*", "%");
423  ffp = FilenameUtils.normalize(ffp, true);
424  String fileName = FilenameUtils.getName(ffp);
425  String filePath = FilenameUtils.getPath(ffp);
426 
427  List<AbstractFile> aLeappFiles = new ArrayList<>();
428  try {
429  if (filePath.isEmpty()) {
430  aLeappFiles = fileManager.findFiles(dataSource, fileName); //NON-NLS
431  } else {
432  aLeappFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
433  }
434  } catch (TskCoreException ex) {
435  logger.log(Level.WARNING, "No files found to process"); //NON-NLS
436  return;
437  }
438 
439  for (AbstractFile aLeappFile : aLeappFiles) {
440  Path parentPath = Paths.get(moduleOutputPath.toString(), aLeappFile.getParentPath());
441  File fileParentPath = new File(parentPath.toString());
442 
443  extractFileToOutput(dataSource, aLeappFile, fileParentPath, parentPath);
444  }
445  }
446  }
447 
448  private void extractFileToOutput(Content dataSource, AbstractFile aLeappFile, File fileParentPath, Path parentPath) {
449  if (fileParentPath.exists()) {
450  if (!aLeappFile.isDir()) {
451  writeaLeappFile(dataSource, aLeappFile, fileParentPath.toString());
452  } else {
453  try {
454  Files.createDirectories(Paths.get(parentPath.toString(), aLeappFile.getName()));
455  } catch (IOException ex) {
456  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
457  }
458  }
459  } else {
460  try {
461  Files.createDirectories(parentPath);
462  } catch (IOException ex) {
463  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
464  }
465  if (!aLeappFile.isDir()) {
466  writeaLeappFile(dataSource, aLeappFile, fileParentPath.toString());
467  } else {
468  try {
469  Files.createDirectories(Paths.get(parentPath.toString(), aLeappFile.getName()));
470  } catch (IOException ex) {
471  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
472  }
473  }
474  }
475  }
476 
477  private void writeaLeappFile(Content dataSource, AbstractFile aLeappFile, String parentPath) {
478  String fileName = aLeappFile.getName().replace(":", "-");
479  if (!fileName.matches(".") && !fileName.matches("..") && !fileName.toLowerCase().endsWith("-slack")) {
480  Path filePath = Paths.get(parentPath, fileName);
481  File localFile = new File(filePath.toString());
482  try {
483  ContentUtils.writeToFile(aLeappFile, localFile, context::dataSourceIngestIsCancelled);
484  } catch (ReadContentInputStream.ReadContentInputStreamException ex) {
485  logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).",
486  aLeappFile.getName(), aLeappFile.getId()), ex); //NON-NLS
487  } catch (IOException ex) {
488  logger.log(Level.WARNING, String.format("Error writing file local file '%s' (id=%d).",
489  filePath.toString(), aLeappFile.getId()), ex); //NON-NLS
490  }
491  }
492  }
493 }
static int execute(ProcessBuilder processBuilder)
Definition: ExecUtil.java:172
void processALeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount, AbstractFile aLeappFile)
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:1654
void processALeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess)
void writeaLeappFile(Content dataSource, AbstractFile aLeappFile, String parentPath)
ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath)
ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile)
void postMessage(final IngestMessage message)
ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper)
void extractFilesFromImage(Content dataSource, List< String > aLeappPathsToProcess, Path moduleOutputPath)
synchronized List< AbstractFile > findFiles(String fileName)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void extractFileToOutput(Content dataSource, AbstractFile aLeappFile, File fileParentPath, Path parentPath)
static synchronized IngestServices getInstance()

Copyright © 2012-2021 Basis Technology. Generated on: Tue Jan 19 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.