20 package org.sleuthkit.autopsy.recentactivity;
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;
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;
64 final class ExtractPrefetch
extends Extract {
66 private static final Logger logger = Logger.getLogger(ExtractPrefetch.class.getName());
68 private IngestJobContext context;
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";
73 private static final String PREFETCH_TOOL_NAME_WINDOWS_64 =
"parse_prefetch_x64.exe";
74 private static final String PREFETCH_TOOL_NAME_WINDOWS_32 =
"parse_prefetch_x32.exe";
75 private static final String PREFETCH_TOOL_NAME_MACOS =
"parse_prefetch_macos";
76 private static final String PREFETCH_TOOL_NAME_LINUX =
"parse_prefetch_linux";
77 private static final String PREFETCH_OUTPUT_FILE_NAME =
"Output.txt";
78 private static final String PREFETCH_ERROR_FILE_NAME =
"Error.txt";
79 private static final String PREFETCH_PARSER_DB_FILE =
"Autopsy_PF_DB.db3";
80 private static final String PREFETCH_DIR_NAME =
"prefetch";
83 "ExtractPrefetch_module_name=Windows Prefetch Extractor",
84 "# {0} - sub module name",
85 "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
88 super(Bundle.ExtractPrefetch_module_name());
92 void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
94 this.context = context;
95 long ingestJobId = context.getJobId();
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();
102 logger.log(Level.SEVERE,
"Error creating directory to store prefetch output database");
107 extractPrefetchFiles(dataSource, ingestJobId);
109 final String prefetchDumper = getPathForPrefetchDumper();
110 if (prefetchDumper == null) {
111 logger.log(Level.SEVERE,
"Error finding parse_prefetch program");
115 if (context.dataSourceIngestIsCancelled()) {
119 String modOutFile = modOutPath + File.separator + dataSource.getName() +
"-" + PREFETCH_PARSER_DB_FILE;
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);
127 }
catch (IOException ex) {
128 logger.log(Level.SEVERE,
"Error parsing prefetch files", ex);
129 addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
139 void extractPrefetchFiles(Content dataSource,
long ingestJobId) {
140 List<AbstractFile> pFiles;
142 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
145 pFiles = fileManager.findFiles(dataSource,
"%.pf");
146 }
catch (TskCoreException ex) {
147 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
151 for (AbstractFile pFile : pFiles) {
153 if (context.dataSourceIngestIsCancelled()) {
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();
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);
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);
190 List<String> commandLine =
new ArrayList<>();
191 commandLine.add(prefetchExePath);
192 commandLine.add(prefetchDir);
193 commandLine.add(tempOutFile);
195 ProcessBuilder processBuilder =
new ProcessBuilder(commandLine);
196 processBuilder.redirectOutput(outputFilePath.toFile());
197 processBuilder.redirectError(errFilePath.toFile());
199 ExecUtil.execute(processBuilder,
new DataSourceIngestModuleProcessTerminator(context,
true));
209 private String getPathForPrefetchDumper() {
211 if (PlatformUtil.isWindowsOS()) {
212 if (PlatformUtil.is64BitOS()) {
213 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_WINDOWS_64);
215 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_WINDOWS_32);
218 if (
"Linux".equals(PlatformUtil.getOSName())) {
219 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_LINUX);
221 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_MACOS);
224 File prefetchToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
225 ExtractPrefetch.class.getPackage().getName(),
false);
226 if (prefetchToolFile != null) {
227 return prefetchToolFile.getAbsolutePath();
242 private void createAppExecArtifacts(String prefetchDb, Content dataSource) {
243 List<BlackboardArtifact> blkBrdArtList =
new ArrayList<>();
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;";
250 try (SQLiteDBConnect tempdbconnect =
new SQLiteDBConnect(
"org.sqlite.JDBC",
"jdbc:sqlite:" + prefetchDb);
251 ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
253 while (resultSet.next()) {
255 if (context.dataSourceIngestIsCancelled()) {
256 logger.log(Level.INFO,
"Cancelled Prefetch Artifact Creation.");
260 String prefetchFileName = resultSet.getString(
"prefetch_File_Name");
261 String applicationName = resultSet.getString(
"actual_File_Name");
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");
274 Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
276 String baseName = FilenameUtils.getBaseName(prefetchFileName);
277 Matcher match = Pattern.compile(
"_(?<objId>\\d*)\\s*$").matcher(baseName);
279 logger.log(Level.WARNING,
"Invalid format for PF file: " + prefetchFileName);
290 AbstractFile pfAbstractFile = null;
292 Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group(
"objId")));
293 if (c instanceof AbstractFile) {
294 pfAbstractFile = (AbstractFile) c;
296 }
catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex ) {
297 logger.log(Level.SEVERE,
"Unable to find content for: " + prefetchFileName, ex);
300 if (pfAbstractFile != null) {
301 for (Long executionTime : prefetchExecutionTimes) {
304 Collection<BlackboardAttribute> blkBrdAttributes = Arrays.asList(
305 new BlackboardAttribute(
306 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
308 new BlackboardAttribute(
309 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, getName(), filePath),
310 new BlackboardAttribute(
311 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getName(),
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));
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);
325 }
catch (TskCoreException ex) {
326 logger.log(Level.SEVERE,
"Exception Adding Artifact.", ex);
330 logger.log(Level.WARNING,
"File has a null value " + prefetchFileName);
334 }
catch (SQLException ex) {
335 logger.log(Level.WARNING, String.format(
"Error while trying to read into a sqlite db %s.", prefetchDb));
336 logger.log(Level.WARNING, ex.getMessage());
339 if (!blkBrdArtList.isEmpty() && !context.dataSourceIngestIsCancelled()) {
340 postArtifacts(blkBrdArtList);
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);
373 AbstractFile getAbstractFile(String fileName, String filePath, Content dataSource) {
374 List<AbstractFile> files;
376 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
379 files = fileManager.findFiles(dataSource, fileName);
381 }
catch (TskCoreException ex) {
382 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
386 for (AbstractFile pFile : files) {
387 if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() +
'/')) {
405 private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
406 Set<Long> prefetchExecutionTimes =
new HashSet<>();
407 for (Long executionTime : executionTimes) {
408 if (executionTime > 0) {
409 prefetchExecutionTimes.add(executionTime);
412 return prefetchExecutionTimes;
static String escapeFileName(String fileName)