Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractPrefetch.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2020-2021 Basis Technology Corp.
6  *
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 package org.sleuthkit.autopsy.recentactivity;
21 
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.sql.ResultSet;
28 import java.sql.SQLException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.logging.Level;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38 import org.apache.commons.io.FilenameUtils;
39 import org.openide.modules.InstalledFileLocator;
40 import org.openide.util.NbBundle.Messages;
53 import org.sleuthkit.datamodel.AbstractFile;
54 import org.sleuthkit.datamodel.BlackboardArtifact;
55 import org.sleuthkit.datamodel.BlackboardAttribute;
56 import org.sleuthkit.datamodel.Content;
57 import org.sleuthkit.datamodel.TskCoreException;
58 
64 final class ExtractPrefetch extends Extract {
65 
66  private static final Logger logger = Logger.getLogger(ExtractPrefetch.class.getName());
67 
68  private IngestJobContext context;
69 
70  private static final String PREFETCH_TSK_COMMENT = "Prefetch File";
71  private static final String PREFETCH_FILE_LOCATION = "/windows/prefetch";
72  private static final String PREFETCH_TOOL_FOLDER = "markmckinnon"; //NON-NLS
73  private static final String PREFETCH_TOOL_NAME_WINDOWS_64 = "parse_prefetch_x64.exe"; //NON-NLS
74  private static final String PREFETCH_TOOL_NAME_WINDOWS_32 = "parse_prefetch_x32.exe"; //NON-NLS
75  private static final String PREFETCH_TOOL_NAME_MACOS = "parse_prefetch_macos"; //NON-NLS
76  private static final String PREFETCH_TOOL_NAME_LINUX = "parse_prefetch_linux"; //NON-NLS
77  private static final String PREFETCH_OUTPUT_FILE_NAME = "Output.txt"; //NON-NLS
78  private static final String PREFETCH_ERROR_FILE_NAME = "Error.txt"; //NON-NLS
79  private static final String PREFETCH_PARSER_DB_FILE = "Autopsy_PF_DB.db3"; //NON-NLS
80  private static final String PREFETCH_DIR_NAME = "prefetch"; //NON-NLS
81 
82  @Messages({
83  "ExtractPrefetch_module_name=Windows Prefetch Extractor",
84  "# {0} - sub module name",
85  "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
86  })
87  ExtractPrefetch() {
88  super(Bundle.ExtractPrefetch_module_name());
89  }
90 
97  private String getPrefetchTempFolder(Content dataSource) {
98  return dataSource.getId() + "-" + PREFETCH_PARSER_DB_FILE;
99  }
100 
101  @Override
102  void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
103 
104  this.context = context;
105  long ingestJobId = context.getJobId();
106 
107  String modOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + PREFETCH_DIR_NAME;
108  File dir = new File(modOutPath);
109  if (dir.exists() == false) {
110  boolean dirMade = dir.mkdirs();
111  if (!dirMade) {
112  logger.log(Level.SEVERE, "Error creating directory to store prefetch output database"); //NON-NLS
113  return; //If we cannot create the directory then we need to exit
114  }
115  }
116 
117  extractPrefetchFiles(dataSource, ingestJobId);
118 
119  final String prefetchDumper = getPathForPrefetchDumper();
120  if (prefetchDumper == null) {
121  logger.log(Level.SEVERE, "Error finding parse_prefetch program"); //NON-NLS
122  return; //If we cannot find the parse_prefetch program we cannot proceed
123  }
124 
125  if (context.dataSourceIngestIsCancelled()) {
126  return;
127  }
128 
129  String modOutFile = modOutPath + File.separator + getPrefetchTempFolder(dataSource);
130  try {
131  String tempDirPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), getPrefetchTempFolder(dataSource), ingestJobId);
132  parsePrefetchFiles(prefetchDumper, tempDirPath, modOutFile, modOutPath);
133  File prefetchDatabase = new File(modOutFile);
134  if (prefetchDatabase.exists()) {
135  createAppExecArtifacts(modOutFile, dataSource);
136  }
137  } catch (IOException ex) {
138  logger.log(Level.SEVERE, "Error parsing prefetch files", ex); //NON-NLS
139  addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
140  }
141  }
142 
149  void extractPrefetchFiles(Content dataSource, long ingestJobId) {
150  List<AbstractFile> pFiles;
151 
152  FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
153 
154  try {
155  pFiles = fileManager.findFiles(dataSource, "%.pf"); //NON-NLS
156  } catch (TskCoreException ex) {
157  logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
158  return; // No need to continue
159  }
160 
161  for (AbstractFile pFile : pFiles) {
162 
163  if (context.dataSourceIngestIsCancelled()) {
164  return;
165  }
166 
167  if (pFile.getParentPath().toLowerCase().contains(PREFETCH_FILE_LOCATION.toLowerCase()) && pFile.getSize() > 0) {
168  String origFileName = pFile.getName();
169  String ext = FilenameUtils.getExtension(origFileName);
170  String baseName = FilenameUtils.getBaseName(origFileName);
171  String fileName = escapeFileName(String.format("%s_%d.%s", baseName, pFile.getId(), ext));
172  String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), getPrefetchTempFolder(dataSource), ingestJobId);
173  String prefetchFile = Paths.get(baseRaTempPath, fileName).toString();
174  try {
175  ContentUtils.writeToFile(pFile, new File(prefetchFile));
176  } catch (IOException ex) {
177  logger.log(Level.WARNING, String.format("Unable to write %s to temp directory. File name: %s", pFile.getName(), prefetchFile), ex); //NON-NLS
178  }
179  }
180  }
181 
182  }
183 
196  void parsePrefetchFiles(String prefetchExePath, String prefetchDir, String tempOutFile, String tempOutPath) throws FileNotFoundException, IOException {
197  final Path outputFilePath = Paths.get(tempOutPath, PREFETCH_OUTPUT_FILE_NAME);
198  final Path errFilePath = Paths.get(tempOutPath, PREFETCH_ERROR_FILE_NAME);
199 
200  List<String> commandLine = new ArrayList<>();
201  commandLine.add(prefetchExePath);
202  commandLine.add(prefetchDir); //NON-NLS
203  commandLine.add(tempOutFile);
204 
205  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
206  processBuilder.redirectOutput(outputFilePath.toFile());
207  processBuilder.redirectError(errFilePath.toFile());
208 
209  ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
210  }
211 
219  private String getPathForPrefetchDumper() {
220  Path path = null;
221  if (PlatformUtil.isWindowsOS()) {
222  if (PlatformUtil.is64BitOS()) {
223  path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_WINDOWS_64);
224  } else {
225  path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_WINDOWS_32);
226  }
227  } else {
228  if ("Linux".equals(PlatformUtil.getOSName())) {
229  path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_LINUX);
230  } else {
231  path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_MACOS);
232  }
233  }
234  File prefetchToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
235  ExtractPrefetch.class.getPackage().getName(), false);
236  if (prefetchToolFile != null) {
237  return prefetchToolFile.getAbsolutePath();
238  }
239 
240  return null;
241 
242  }
243 
252  private void createAppExecArtifacts(String prefetchDb, Content dataSource) {
253  List<BlackboardArtifact> blkBrdArtList = new ArrayList<>();
254 
255  String sqlStatement = "SELECT prefetch_File_Name, actual_File_Name, file_path, Number_time_file_run, Embeded_date_Time_Unix_1, "
256  + " Embeded_date_Time_Unix_2, Embeded_date_Time_Unix_3, Embeded_date_Time_Unix_4, Embeded_date_Time_Unix_5,"
257  + " Embeded_date_Time_Unix_6, Embeded_date_Time_Unix_7, Embeded_date_Time_Unix_8 "
258  + " FROM prefetch_file_info;"; //NON-NLS
259 
260  try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + prefetchDb); //NON-NLS
261  ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
262 
263  while (resultSet.next()) {
264 
265  if (context.dataSourceIngestIsCancelled()) {
266  logger.log(Level.INFO, "Cancelled Prefetch Artifact Creation."); //NON-NLS
267  return;
268  }
269 
270  String prefetchFileName = resultSet.getString("prefetch_File_Name");
271  String applicationName = resultSet.getString("actual_File_Name"); //NON-NLS
272  List<Long> executionTimes = new ArrayList<>();
273  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_1")));
274  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_2")));
275  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_3")));
276  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_4")));
277  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_5")));
278  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_6")));
279  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_7")));
280  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_8")));
281  String timesProgramRun = resultSet.getString("Number_time_file_run");
282  String filePath = resultSet.getString("file_path");
283 
284  Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
285 
286  String baseName = FilenameUtils.getBaseName(prefetchFileName);
287  Matcher match = Pattern.compile("_(?<objId>\\d*)\\s*$").matcher(baseName);
288  if (!match.find()) {
289  logger.log(Level.WARNING, "Invalid format for PF file: " + prefetchFileName);//NON-NLS
290  continue;
291  }
292 
293 
300  AbstractFile pfAbstractFile = null;
301  try {
302  Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group("objId")));
303  if (c instanceof AbstractFile) {
304  pfAbstractFile = (AbstractFile) c;
305  }
306  } catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex ) {
307  logger.log(Level.SEVERE, "Unable to find content for: " + prefetchFileName, ex);
308  }
309 
310  if (pfAbstractFile != null) {
311  for (Long executionTime : prefetchExecutionTimes) {
312 
313  // only add prefetch file entries that have an actual date associated with them
314  Collection<BlackboardAttribute> blkBrdAttributes = Arrays.asList(
315  new BlackboardAttribute(
316  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
317  applicationName),//NON-NLS
318  new BlackboardAttribute(
319  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, getName(), filePath),
320  new BlackboardAttribute(
321  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getName(),
322  executionTime),
323  new BlackboardAttribute(
324  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, getName(), Integer.valueOf(timesProgramRun)),
325  new BlackboardAttribute(
326  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, getName(), PREFETCH_TSK_COMMENT));
327 
328  try {
329  BlackboardArtifact blkBrdArt = createArtifactWithAttributes(BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN, pfAbstractFile, blkBrdAttributes);
330  blkBrdArtList.add(blkBrdArt);
331  BlackboardArtifact associatedBbArtifact = createAssociatedArtifact(applicationName.toLowerCase(), filePath, blkBrdArt, dataSource);
332  if (associatedBbArtifact != null) {
333  blkBrdArtList.add(associatedBbArtifact);
334  }
335  } catch (TskCoreException ex) {
336  logger.log(Level.SEVERE, "Exception Adding Artifact.", ex);//NON-NLS
337  }
338  }
339  } else {
340  logger.log(Level.WARNING, "File has a null value " + prefetchFileName);//NON-NLS
341  }
342 
343  }
344  } catch (SQLException ex) {
345  logger.log(Level.WARNING, String.format("Error while trying to read into a sqlite db %s.", prefetchDb));//NON-NLS
346  logger.log(Level.WARNING, ex.getMessage());
347  }
348 
349  if (!blkBrdArtList.isEmpty() && !context.dataSourceIngestIsCancelled()) {
350  postArtifacts(blkBrdArtList);
351  }
352  }
353 
365  private BlackboardArtifact createAssociatedArtifact(String fileName, String filePathName, BlackboardArtifact bba, Content dataSource) throws TskCoreException {
366  AbstractFile sourceFile = getAbstractFile(fileName, filePathName, dataSource);
367  if (sourceFile != null) {
368  return createAssociatedArtifact(sourceFile, bba);
369  }
370  return null;
371  }
372 
383  AbstractFile getAbstractFile(String fileName, String filePath, Content dataSource) {
384  List<AbstractFile> files;
385 
386  FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
387 
388  try {
389  files = fileManager.findFiles(dataSource, fileName); //NON-NLS
390 
391  } catch (TskCoreException ex) {
392  logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
393  return null; // No need to continue
394  }
395 
396  for (AbstractFile pFile : files) {
397  if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() + '/')) {
398  return pFile;
399  }
400  }
401 
402  return null;
403 
404  }
405 
415  private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
416  Set<Long> prefetchExecutionTimes = new HashSet<>();
417  for (Long executionTime : executionTimes) { // only add prefetch file entries that have an actual date associated with them
418  if (executionTime > 0) {
419  prefetchExecutionTimes.add(executionTime);
420  }
421  }
422  return prefetchExecutionTimes;
423  }
424 }
static String escapeFileName(String fileName)
Definition: FileUtil.java:169

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