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 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";
72 private static final String PREFETCH_TOOL_NAME_WINDOWS =
"parse_prefetch.exe";
73 private static final String PREFETCH_TOOL_NAME_LINUX =
"parse_prefetch_linux";
74 private static final String PREFETCH_OUTPUT_FILE_NAME =
"Output.txt";
75 private static final String PREFETCH_ERROR_FILE_NAME =
"Error.txt";
76 private static final String PREFETCH_PARSER_DB_FILE =
"Autopsy_PF_DB.db3";
77 private static final String PREFETCH_DIR_NAME =
"prefetch";
80 "ExtractPrefetch_module_name=Windows Prefetch Analyzer",
81 "# {0} - sub module name",
82 "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
84 ExtractPrefetch(IngestJobContext context) {
85 super(Bundle.ExtractPrefetch_module_name(), context);
86 this.context = context;
96 private String getPrefetchTempFolder(Content dataSource) {
97 return dataSource.getId() +
"-" + PREFETCH_PARSER_DB_FILE;
101 void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
103 long ingestJobId = context.getJobId();
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();
110 logger.log(Level.SEVERE,
"Error creating directory to store prefetch output database");
115 extractPrefetchFiles(dataSource, ingestJobId);
117 final String prefetchDumper = getPathForPrefetchDumper();
118 if (prefetchDumper == null) {
119 logger.log(Level.SEVERE,
"Error finding parse_prefetch program");
123 if (context.dataSourceIngestIsCancelled()) {
127 String modOutFile = modOutPath + File.separator + getPrefetchTempFolder(dataSource);
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);
135 }
catch (IOException ex) {
136 logger.log(Level.SEVERE,
"Error parsing prefetch files", ex);
137 addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
147 void extractPrefetchFiles(Content dataSource,
long ingestJobId) {
148 List<AbstractFile> pFiles;
150 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
153 pFiles = fileManager.findFiles(dataSource,
"%.pf");
154 }
catch (TskCoreException ex) {
155 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
159 for (AbstractFile pFile : pFiles) {
161 if (context.dataSourceIngestIsCancelled()) {
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();
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);
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);
197 List<String> commandLine =
new ArrayList<>();
198 commandLine.add(prefetchExePath);
199 commandLine.add(prefetchDir);
200 commandLine.add(tempOutFile);
202 ProcessBuilder processBuilder =
new ProcessBuilder(commandLine);
203 processBuilder.redirectOutput(outputFilePath.toFile());
204 processBuilder.redirectError(errFilePath.toFile());
206 ExecUtil.execute(processBuilder,
new DataSourceIngestModuleProcessTerminator(context,
true));
216 private String getPathForPrefetchDumper() {
218 if (PlatformUtil.isWindowsOS()) {
219 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_WINDOWS);
221 if (
"Linux".equals(PlatformUtil.getOSName())) {
222 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_LINUX);
225 File prefetchToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
226 ExtractPrefetch.class.getPackage().getName(),
false);
227 if (prefetchToolFile != null) {
228 return prefetchToolFile.getAbsolutePath();
243 private void createAppExecArtifacts(String prefetchDb, Content dataSource) {
244 List<BlackboardArtifact> blkBrdArtList =
new ArrayList<>();
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;";
251 try (SQLiteDBConnect tempdbconnect =
new SQLiteDBConnect(
"org.sqlite.JDBC",
"jdbc:sqlite:" + prefetchDb);
252 ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
254 while (resultSet.next()) {
256 if (context.dataSourceIngestIsCancelled()) {
257 logger.log(Level.INFO,
"Cancelled Prefetch Artifact Creation.");
261 String prefetchFileName = resultSet.getString(
"prefetch_File_Name");
262 String applicationName = resultSet.getString(
"actual_File_Name");
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");
275 Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
277 String baseName = FilenameUtils.getBaseName(prefetchFileName);
278 Matcher match = Pattern.compile(
"_(?<objId>\\d*)\\s*$").matcher(baseName);
280 logger.log(Level.WARNING,
"Invalid format for PF file: " + prefetchFileName);
292 AbstractFile pfAbstractFile = null;
294 Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group(
"objId")));
295 if (c instanceof AbstractFile) {
296 pfAbstractFile = (AbstractFile) c;
298 }
catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex) {
299 logger.log(Level.SEVERE,
"Unable to find content for: " + prefetchFileName, ex);
302 if (pfAbstractFile != null) {
303 for (Long executionTime : prefetchExecutionTimes) {
306 Collection<BlackboardAttribute> blkBrdAttributes = Arrays.asList(
307 new BlackboardAttribute(
308 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getDisplayName(),
310 new BlackboardAttribute(
311 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, getDisplayName(), filePath),
312 new BlackboardAttribute(
313 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getDisplayName(),
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));
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);
327 }
catch (TskCoreException ex) {
328 logger.log(Level.SEVERE,
"Exception Adding Artifact.", ex);
332 logger.log(Level.WARNING,
"File has a null value " + prefetchFileName);
336 }
catch (SQLException ex) {
337 logger.log(Level.WARNING, String.format(
"Error while trying to read into a sqlite db %s.", prefetchDb));
338 logger.log(Level.WARNING, ex.getMessage());
341 if (!blkBrdArtList.isEmpty() && !context.dataSourceIngestIsCancelled()) {
342 postArtifacts(blkBrdArtList);
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);
375 AbstractFile getAbstractFile(String fileName, String filePath, Content dataSource) {
376 List<AbstractFile> files;
378 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
381 files = fileManager.findFiles(dataSource, fileName);
383 }
catch (TskCoreException ex) {
384 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
388 for (AbstractFile pFile : files) {
389 if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() +
'/')) {
407 private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
408 Set<Long> prefetchExecutionTimes =
new HashSet<>();
409 for (Long executionTime : executionTimes) {
410 if (executionTime > 0) {
411 prefetchExecutionTimes.add(executionTime);
414 return prefetchExecutionTimes;
static String escapeFileName(String fileName)