Autopsy  4.21.0
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;
58 
64 final class ExtractPrefetch extends Extract {
65 
66  private static final Logger logger = Logger.getLogger(ExtractPrefetch.class.getName());
67 
68  private final IngestJobContext context;
69  private static final String PREFETCH_TSK_COMMENT = "Prefetch File";
70  private static final String PREFETCH_FILE_LOCATION = "/windows/prefetch";
71  private static final String PREFETCH_TOOL_FOLDER = "markmckinnon"; //NON-NLS
72  private static final String PREFETCH_TOOL_NAME_WINDOWS = "parse_prefetch.exe"; //NON-NLS
73  private static final String PREFETCH_TOOL_NAME_LINUX = "parse_prefetch_linux"; //NON-NLS
74  private static final String PREFETCH_OUTPUT_FILE_NAME = "Output.txt"; //NON-NLS
75  private static final String PREFETCH_ERROR_FILE_NAME = "Error.txt"; //NON-NLS
76  private static final String PREFETCH_PARSER_DB_FILE = "Autopsy_PF_DB.db3"; //NON-NLS
77  private static final String PREFETCH_DIR_NAME = "prefetch"; //NON-NLS
78 
79  @Messages({
80  "ExtractPrefetch_module_name=Windows Prefetch Analyzer",
81  "# {0} - sub module name",
82  "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
83  })
84  ExtractPrefetch(IngestJobContext context) {
85  super(Bundle.ExtractPrefetch_module_name(), context);
86  this.context = context;
87  }
88 
96  private String getPrefetchTempFolder(Content dataSource) {
97  return dataSource.getId() + "-" + PREFETCH_PARSER_DB_FILE;
98  }
99 
100  @Override
101  void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
102 
103  long ingestJobId = context.getJobId();
104 
105  String modOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + PREFETCH_DIR_NAME;
106  File dir = new File(modOutPath);
107  if (dir.exists() == false) {
108  boolean dirMade = dir.mkdirs();
109  if (!dirMade) {
110  logger.log(Level.SEVERE, "Error creating directory to store prefetch output database"); //NON-NLS
111  return; //If we cannot create the directory then we need to exit
112  }
113  }
114 
115  extractPrefetchFiles(dataSource, ingestJobId);
116 
117  final String prefetchDumper = getPathForPrefetchDumper();
118  if (prefetchDumper == null) {
119  logger.log(Level.SEVERE, "Error finding parse_prefetch program"); //NON-NLS
120  return; //If we cannot find the parse_prefetch program we cannot proceed
121  }
122 
123  if (context.dataSourceIngestIsCancelled()) {
124  return;
125  }
126 
127  String modOutFile = modOutPath + File.separator + getPrefetchTempFolder(dataSource);
128  try {
129  String tempDirPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), getPrefetchTempFolder(dataSource), ingestJobId);
130  parsePrefetchFiles(prefetchDumper, tempDirPath, modOutFile, modOutPath);
131  File prefetchDatabase = new File(modOutFile);
132  if (prefetchDatabase.exists()) {
133  createAppExecArtifacts(modOutFile, dataSource);
134  }
135  } catch (IOException ex) {
136  logger.log(Level.SEVERE, "Error parsing prefetch files", ex); //NON-NLS
137  addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
138  }
139  }
140 
147  void extractPrefetchFiles(Content dataSource, long ingestJobId) {
148  List<AbstractFile> pFiles;
149 
150  FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
151 
152  try {
153  pFiles = fileManager.findFiles(dataSource, "%.pf"); //NON-NLS
154  } catch (TskCoreException ex) {
155  logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
156  return; // No need to continue
157  }
158 
159  for (AbstractFile pFile : pFiles) {
160 
161  if (context.dataSourceIngestIsCancelled()) {
162  return;
163  }
164 
165  if (pFile.getParentPath().toLowerCase().contains(PREFETCH_FILE_LOCATION.toLowerCase()) && pFile.getSize() > 0) {
166  String origFileName = pFile.getName();
167  String ext = FilenameUtils.getExtension(origFileName);
168  String baseName = FilenameUtils.getBaseName(origFileName);
169  String fileName = escapeFileName(String.format("%s_%d.%s", baseName, pFile.getId(), ext));
170  String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), getPrefetchTempFolder(dataSource), ingestJobId);
171  String prefetchFile = Paths.get(baseRaTempPath, fileName).toString();
172  try {
173  ContentUtils.writeToFile(pFile, new File(prefetchFile));
174  } catch (IOException ex) {
175  logger.log(Level.WARNING, String.format("Unable to write %s to temp directory. File name: %s", pFile.getName(), prefetchFile), ex); //NON-NLS
176  }
177  }
178  }
179  }
180 
193  void parsePrefetchFiles(String prefetchExePath, String prefetchDir, String tempOutFile, String tempOutPath) throws FileNotFoundException, IOException {
194  final Path outputFilePath = Paths.get(tempOutPath, PREFETCH_OUTPUT_FILE_NAME);
195  final Path errFilePath = Paths.get(tempOutPath, PREFETCH_ERROR_FILE_NAME);
196 
197  List<String> commandLine = new ArrayList<>();
198  commandLine.add(prefetchExePath);
199  commandLine.add(prefetchDir); //NON-NLS
200  commandLine.add(tempOutFile);
201 
202  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
203  processBuilder.redirectOutput(outputFilePath.toFile());
204  processBuilder.redirectError(errFilePath.toFile());
205 
206  ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
207  }
208 
216  private String getPathForPrefetchDumper() {
217  Path path = null;
218  if (PlatformUtil.isWindowsOS()) {
219  path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_WINDOWS);
220  } else {
221  if ("Linux".equals(PlatformUtil.getOSName())) {
222  path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_LINUX);
223  }
224  }
225  File prefetchToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
226  ExtractPrefetch.class.getPackage().getName(), false);
227  if (prefetchToolFile != null) {
228  return prefetchToolFile.getAbsolutePath();
229  }
230 
231  return null;
232 
233  }
234 
243  private void createAppExecArtifacts(String prefetchDb, Content dataSource) {
244  List<BlackboardArtifact> blkBrdArtList = new ArrayList<>();
245 
246  String sqlStatement = "SELECT prefetch_File_Name, actual_File_Name, file_path, Number_time_file_run, Embeded_date_Time_Unix_1, "
247  + " Embeded_date_Time_Unix_2, Embeded_date_Time_Unix_3, Embeded_date_Time_Unix_4, Embeded_date_Time_Unix_5,"
248  + " Embeded_date_Time_Unix_6, Embeded_date_Time_Unix_7, Embeded_date_Time_Unix_8 "
249  + " FROM prefetch_file_info;"; //NON-NLS
250 
251  try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + prefetchDb); //NON-NLS
252  ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
253 
254  while (resultSet.next()) {
255 
256  if (context.dataSourceIngestIsCancelled()) {
257  logger.log(Level.INFO, "Cancelled Prefetch Artifact Creation."); //NON-NLS
258  return;
259  }
260 
261  String prefetchFileName = resultSet.getString("prefetch_File_Name");
262  String applicationName = resultSet.getString("actual_File_Name"); //NON-NLS
263  List<Long> executionTimes = new ArrayList<>();
264  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_1")));
265  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_2")));
266  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_3")));
267  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_4")));
268  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_5")));
269  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_6")));
270  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_7")));
271  executionTimes.add(Long.valueOf(resultSet.getInt("Embeded_date_Time_Unix_8")));
272  String timesProgramRun = resultSet.getString("Number_time_file_run");
273  String filePath = resultSet.getString("file_path");
274 
275  Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
276 
277  String baseName = FilenameUtils.getBaseName(prefetchFileName);
278  Matcher match = Pattern.compile("_(?<objId>\\d*)\\s*$").matcher(baseName);
279  if (!match.find()) {
280  logger.log(Level.WARNING, "Invalid format for PF file: " + prefetchFileName);//NON-NLS
281  continue;
282  }
283 
292  AbstractFile pfAbstractFile = null;
293  try {
294  Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group("objId")));
295  if (c instanceof AbstractFile) {
296  pfAbstractFile = (AbstractFile) c;
297  }
298  } catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex) {
299  logger.log(Level.SEVERE, "Unable to find content for: " + prefetchFileName, ex);
300  }
301 
302  if (pfAbstractFile != null) {
303  for (Long executionTime : prefetchExecutionTimes) {
304 
305  // only add prefetch file entries that have an actual date associated with them
306  Collection<BlackboardAttribute> blkBrdAttributes = Arrays.asList(
307  new BlackboardAttribute(
308  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getDisplayName(),
309  applicationName),//NON-NLS
310  new BlackboardAttribute(
311  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, getDisplayName(), filePath),
312  new BlackboardAttribute(
313  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getDisplayName(),
314  executionTime),
315  new BlackboardAttribute(
316  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, getDisplayName(), Integer.valueOf(timesProgramRun)),
317  new BlackboardAttribute(
318  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, getDisplayName(), PREFETCH_TSK_COMMENT));
319 
320  try {
321  BlackboardArtifact blkBrdArt = createArtifactWithAttributes(BlackboardArtifact.Type.TSK_PROG_RUN, pfAbstractFile, blkBrdAttributes);
322  blkBrdArtList.add(blkBrdArt);
323  BlackboardArtifact associatedBbArtifact = createAssociatedArtifact(applicationName.toLowerCase(), filePath, blkBrdArt, dataSource);
324  if (associatedBbArtifact != null) {
325  blkBrdArtList.add(associatedBbArtifact);
326  }
327  } catch (TskCoreException ex) {
328  logger.log(Level.SEVERE, "Exception Adding Artifact.", ex);//NON-NLS
329  }
330  }
331  } else {
332  logger.log(Level.WARNING, "File has a null value " + prefetchFileName);//NON-NLS
333  }
334 
335  }
336  } catch (SQLException ex) {
337  logger.log(Level.WARNING, String.format("Error while trying to read into a sqlite db %s.", prefetchDb));//NON-NLS
338  logger.log(Level.WARNING, ex.getMessage());
339  }
340 
341  if (!blkBrdArtList.isEmpty() && !context.dataSourceIngestIsCancelled()) {
342  postArtifacts(blkBrdArtList);
343  }
344  }
345 
357  private BlackboardArtifact createAssociatedArtifact(String fileName, String filePathName, BlackboardArtifact bba, Content dataSource) throws TskCoreException {
358  AbstractFile sourceFile = getAbstractFile(fileName, filePathName, dataSource);
359  if (sourceFile != null) {
360  return createAssociatedArtifact(sourceFile, bba);
361  }
362  return null;
363  }
364 
375  AbstractFile getAbstractFile(String fileName, String filePath, Content dataSource) {
376  List<AbstractFile> files;
377 
378  FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
379 
380  try {
381  files = fileManager.findFiles(dataSource, fileName); //NON-NLS
382 
383  } catch (TskCoreException ex) {
384  logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
385  return null; // No need to continue
386  }
387 
388  for (AbstractFile pFile : files) {
389  if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() + '/')) {
390  return pFile;
391  }
392  }
393 
394  return null;
395 
396  }
397 
407  private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
408  Set<Long> prefetchExecutionTimes = new HashSet<>();
409  for (Long executionTime : executionTimes) { // only add prefetch file entries that have an actual date associated with them
410  if (executionTime > 0) {
411  prefetchExecutionTimes.add(executionTime);
412  }
413  }
414  return prefetchExecutionTimes;
415  }
416 }
static String escapeFileName(String fileName)
Definition: FileUtil.java:169

Copyright © 2012-2024 Sleuth Kit Labs. Generated on: Mon Mar 17 2025
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.