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

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.