Autopsy  4.18.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.is64BitOS()) {
96  throw new IngestModuleException(NbBundle.getMessage(this.getClass(), "AleappAnalyzerIngestModule.not.64.bit.os"));
97  }
98 
99  if (false == PlatformUtil.isWindowsOS()) {
100  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_requires_windows());
101  }
102 
103  try {
104  aLeappFileProcessor = new LeappFileProcessor(XMLFILE, ALeappAnalyzerModuleFactory.getModuleName());
105  } catch (IOException | IngestModuleException | NoCurrentCaseException ex) {
106  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_error_ileapp_file_processor_init(), ex);
107  }
108 
109  try {
110  aLeappExecutable = locateExecutable(ALEAPP_EXECUTABLE);
111  } catch (FileNotFoundException exception) {
112  logger.log(Level.WARNING, "aLeapp executable not found.", exception); //NON-NLS
113  throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_executable_not_found(), exception);
114  }
115 
116  }
117 
118  @NbBundle.Messages({
119  "ALeappAnalyzerIngestModule.error.running.aLeapp=Error running aLeapp, see log file.",
120  "ALeappAnalyzerIngestModule.error.creating.output.dir=Error creating aLeapp module output directory.",
121  "ALeappAnalyzerIngestModule.starting.aLeapp=Starting aLeapp",
122  "ALeappAnalyzerIngestModule.running.aLeapp=Running aLeapp",
123  "ALeappAnalyzerIngestModule.has.run=aLeapp",
124  "ALeappAnalyzerIngestModule.aLeapp.cancelled=aLeapp run was canceled",
125  "ALeappAnalyzerIngestModule.completed=aLeapp Processing Completed",
126  "ALeappAnalyzerIngestModule.report.name=aLeapp Html Report"})
127  @Override
128  public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
129 
130  Case currentCase = Case.getCurrentCase();
131  Path tempOutputPath = Paths.get(currentCase.getTempDirectory(), ALEAPP, ALEAPP_FS + dataSource.getId());
132  try {
133  Files.createDirectories(tempOutputPath);
134  } catch (IOException ex) {
135  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", tempOutputPath.toString()), ex);
136  return ProcessResult.ERROR;
137  }
138 
139  List<String> aLeappPathsToProcess = new ArrayList<>();
140  ProcessBuilder aLeappCommand = buildaLeappListCommand(tempOutputPath);
141  try {
142  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
143  if (result != 0) {
144  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
145  return ProcessResult.ERROR;
146  }
147  aLeappPathsToProcess = loadIleappPathFile(tempOutputPath);
148  } catch (IOException ex) {
149  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program getting file paths to search"), ex);
150  return ProcessResult.ERROR;
151  }
152 
153  statusHelper.progress(Bundle.ALeappAnalyzerIngestModule_starting_aLeapp(), 0);
154 
155  List<AbstractFile> aLeappFilesToProcess = new ArrayList<>();
156 
157  if (!(context.getDataSource() instanceof LocalFilesDataSource)) {
158  extractFilesFromImage(dataSource, aLeappPathsToProcess, tempOutputPath);
159  statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
160  processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
161  } else {
162  aLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource);
163  statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
164 
165  Integer filesProcessedCount = 0;
166  for (AbstractFile aLeappFile : aLeappFilesToProcess) {
167  processALeappFile(dataSource, currentCase, statusHelper, filesProcessedCount, aLeappFile);
168  filesProcessedCount++;
169  }
170  // Process the logical image as a fs in aLeapp to make sure this is not a logical fs that was added
171  extractFilesFromImage(dataSource, aLeappPathsToProcess, tempOutputPath);
172  processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
173  }
174 
176  Bundle.ALeappAnalyzerIngestModule_has_run(),
177  Bundle.ALeappAnalyzerIngestModule_completed());
179  return ProcessResult.OK;
180  }
181 
190  private void processALeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount,
191  AbstractFile aLeappFile) {
192  String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
193  Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ALEAPP, currentTime);
194  try {
195  Files.createDirectories(moduleOutputPath);
196  } catch (IOException ex) {
197  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", moduleOutputPath.toString()), ex);
198  return;
199  }
200 
201  statusHelper.progress(NbBundle.getMessage(this.getClass(), "ALeappAnalyzerIngestModule.processing.file", aLeappFile.getName()), filesProcessedCount);
202  ProcessBuilder aLeappCommand = buildaLeappCommand(moduleOutputPath, aLeappFile.getLocalAbsPath(), aLeappFile.getNameExtension());
203  try {
204  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
205  if (result != 0) {
206  logger.log(Level.WARNING, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
207  return;
208  }
209 
210  addILeappReportToReports(moduleOutputPath, currentCase);
211 
212  } catch (IOException ex) {
213  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program against file %s", aLeappFile.getLocalAbsPath()), ex);
214  return;
215  }
216 
217  if (context.dataSourceIngestIsCancelled()) {
218  logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
219  return;
220  }
221 
222  ProcessResult fileProcessorResult = aLeappFileProcessor.processFiles(dataSource, moduleOutputPath, aLeappFile);
223 
224  if (fileProcessorResult == ProcessResult.ERROR) {
225  return;
226  }
227  }
228 
236  private void processALeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess) {
237  String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
238  Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ALEAPP, currentTime);
239  try {
240  Files.createDirectories(moduleOutputPath);
241  } catch (IOException ex) {
242  logger.log(Level.SEVERE, String.format("Error creating aLeapp output directory %s", moduleOutputPath.toString()), ex);
243  return;
244  }
245 
246  statusHelper.progress(NbBundle.getMessage(this.getClass(), "ALeappAnalyzerIngestModule.processing.filesystem"));
247  ProcessBuilder aLeappCommand = buildaLeappCommand(moduleOutputPath, directoryToProcess, "fs");
248  try {
249  int result = ExecUtil.execute(aLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
250  if (result != 0) {
251  logger.log(Level.WARNING, String.format("Error when trying to execute aLeapp program getting file paths to search for result is %d", result));
252  return;
253  }
254 
255  addILeappReportToReports(moduleOutputPath, currentCase);
256 
257  } catch (IOException ex) {
258  logger.log(Level.SEVERE, String.format("Error when trying to execute aLeapp program against file system"), ex);
259  return;
260  }
261 
262  if (context.dataSourceIngestIsCancelled()) {
263  logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
264  return;
265  }
266 
267  ProcessResult fileProcessorResult = aLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath);
268 
269  if (fileProcessorResult == ProcessResult.ERROR) {
270  return;
271  }
272 
273  }
274 
275 
276 
286  private ProcessBuilder buildaLeappCommand(Path moduleOutputPath, String sourceFilePath, String aLeappFileSystemType) {
287 
288  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
289  "\"" + aLeappExecutable + "\"", //NON-NLS
290  "-t", aLeappFileSystemType, //NON-NLS
291  "-i", sourceFilePath, //NON-NLS
292  "-o", moduleOutputPath.toString(),
293  "-w"
294  );
295  processBuilder.redirectError(moduleOutputPath.resolve("aLeapp_err.txt").toFile()); //NON-NLS
296  processBuilder.redirectOutput(moduleOutputPath.resolve("aLeapp_out.txt").toFile()); //NON-NLS
297  return processBuilder;
298  }
299 
300  private ProcessBuilder buildaLeappListCommand(Path moduleOutputPath) {
301 
302  ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
303  "\"" + aLeappExecutable + "\"", //NON-NLS
304  "-p"
305  );
306  processBuilder.redirectError(moduleOutputPath.resolve("aLeapp_paths_error.txt").toFile()); //NON-NLS
307  processBuilder.redirectOutput(moduleOutputPath.resolve("aLeapp_paths.txt").toFile()); //NON-NLS
308  return processBuilder;
309  }
310 
311  static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
312  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
313  /*
314  * Add an environment variable to force aLeapp to run with
315  * the same permissions Autopsy uses.
316  */
317  processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
318  return processBuilder;
319  }
320 
321  private static File locateExecutable(String executableName) throws FileNotFoundException {
322  String executableToFindName = Paths.get(ALEAPP, executableName).toString();
323 
324  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, ALeappAnalyzerIngestModule.class.getPackage().getName(), false);
325  if (null == exeFile || exeFile.canExecute() == false) {
326  throw new FileNotFoundException(executableName + " executable not found.");
327  }
328  return exeFile;
329  }
330 
335  private void addILeappReportToReports(Path aLeappOutputDir, Case currentCase) {
336  List<String> allIndexFiles = new ArrayList<>();
337 
338  try (Stream<Path> walk = Files.walk(aLeappOutputDir)) {
339 
340  allIndexFiles = walk.map(x -> x.toString())
341  .filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
342 
343  if (!allIndexFiles.isEmpty()) {
344  // Check for existance of directory that holds report data if does not exist then report contains no data
345  String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
346  File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
347  if (dataFilesDir.exists()) {
348  currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ALeappAnalyzerIngestModule_report_name());
349  }
350  }
351 
352  } catch (IOException | UncheckedIOException | TskCoreException ex) {
353  // catch the error and continue on as report is not added
354  logger.log(Level.WARNING, String.format("Error finding index file in path %s", aLeappOutputDir.toString()), ex);
355  }
356 
357  }
358 
359  /*
360  * Reads the aLeapp paths file to get the paths that we want to extract
361  *
362  */
363  private List<String> loadIleappPathFile(Path moduleOutputPath) throws FileNotFoundException, IOException {
364  List<String> aLeappPathsToProcess = new ArrayList<>();
365 
366  Path filePath = Paths.get(moduleOutputPath.toString(), ALEAPP_PATHS_FILE);
367 
368  try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toString()))) {
369  String line = reader.readLine();
370  while (line != null) {
371  if (line.contains("path list generation") || line.length() < 2) {
372  line = reader.readLine();
373  continue;
374  }
375  aLeappPathsToProcess.add(line.trim());
376  line = reader.readLine();
377  }
378  }
379 
380  return aLeappPathsToProcess;
381  }
382 
383  private void extractFilesFromImage(Content dataSource, List<String> aLeappPathsToProcess, Path moduleOutputPath) {
384  FileManager fileManager = getCurrentCase().getServices().getFileManager();
385 
386  for (String fullFilePath : aLeappPathsToProcess) {
387 
388  if (context.dataSourceIngestIsCancelled()) {
389  logger.log(Level.INFO, "aLeapp Analyser ingest module run was canceled"); //NON-NLS
390  break;
391  }
392 
393  String ffp = fullFilePath.replaceAll("\\*", "%");
394  ffp = FilenameUtils.normalize(ffp, true);
395  String fileName = FilenameUtils.getName(ffp);
396  String filePath = FilenameUtils.getPath(ffp);
397 
398  List<AbstractFile> aLeappFiles = new ArrayList<>();
399  try {
400  if (filePath.isEmpty()) {
401  aLeappFiles = fileManager.findFiles(dataSource, fileName); //NON-NLS
402  } else {
403  aLeappFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
404  }
405  } catch (TskCoreException ex) {
406  logger.log(Level.WARNING, "No files found to process"); //NON-NLS
407  return;
408  }
409 
410  for (AbstractFile aLeappFile : aLeappFiles) {
411  Path parentPath = Paths.get(moduleOutputPath.toString(), aLeappFile.getParentPath());
412  File fileParentPath = new File(parentPath.toString());
413 
414  extractFileToOutput(dataSource, aLeappFile, fileParentPath, parentPath);
415  }
416  }
417  }
418 
419  private void extractFileToOutput(Content dataSource, AbstractFile aLeappFile, File fileParentPath, Path parentPath) {
420  if (fileParentPath.exists()) {
421  if (!aLeappFile.isDir()) {
422  writeaLeappFile(dataSource, aLeappFile, fileParentPath.toString());
423  } else {
424  try {
425  Files.createDirectories(Paths.get(parentPath.toString(), aLeappFile.getName()));
426  } catch (IOException ex) {
427  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
428  }
429  }
430  } else {
431  try {
432  Files.createDirectories(parentPath);
433  } catch (IOException ex) {
434  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
435  }
436  if (!aLeappFile.isDir()) {
437  writeaLeappFile(dataSource, aLeappFile, fileParentPath.toString());
438  } else {
439  try {
440  Files.createDirectories(Paths.get(parentPath.toString(), aLeappFile.getName()));
441  } catch (IOException ex) {
442  logger.log(Level.INFO, String.format("Error creating aLeapp output directory %s", parentPath.toString()), ex);
443  }
444  }
445  }
446  }
447 
448  private void writeaLeappFile(Content dataSource, AbstractFile aLeappFile, String parentPath) {
449  String fileName = aLeappFile.getName().replace(":", "-");
450  if (!fileName.matches(".") && !fileName.matches("..") && !fileName.toLowerCase().endsWith("-slack")) {
451  Path filePath = Paths.get(parentPath, fileName);
452  File localFile = new File(filePath.toString());
453  try {
454  ContentUtils.writeToFile(aLeappFile, localFile, context::dataSourceIngestIsCancelled);
455  } catch (ReadContentInputStream.ReadContentInputStreamException ex) {
456  logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).",
457  aLeappFile.getName(), aLeappFile.getId()), ex); //NON-NLS
458  } catch (IOException ex) {
459  logger.log(Level.WARNING, String.format("Error writing file local file '%s' (id=%d).",
460  filePath.toString(), aLeappFile.getId()), ex); //NON-NLS
461  }
462  }
463  }
464 }
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)
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:1891
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 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: Thu Jul 8 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.