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_64 =
"parse_prefetch_x64.exe";
73 private static final String PREFETCH_TOOL_NAME_WINDOWS_32 =
"parse_prefetch_x32.exe";
74 private static final String PREFETCH_TOOL_NAME_MACOS =
"parse_prefetch_macos";
75 private static final String PREFETCH_TOOL_NAME_LINUX =
"parse_prefetch_linux";
76 private static final String PREFETCH_OUTPUT_FILE_NAME =
"Output.txt";
77 private static final String PREFETCH_ERROR_FILE_NAME =
"Error.txt";
78 private static final String PREFETCH_PARSER_DB_FILE =
"Autopsy_PF_DB.db3";
79 private static final String PREFETCH_DIR_NAME =
"prefetch";
82 "ExtractPrefetch_module_name=Windows Prefetch Analyzer",
83 "# {0} - sub module name",
84 "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
86 ExtractPrefetch(IngestJobContext context) {
87 super(Bundle.ExtractPrefetch_module_name(), context);
88 this.context = context;
98 private String getPrefetchTempFolder(Content dataSource) {
99 return dataSource.getId() +
"-" + PREFETCH_PARSER_DB_FILE;
103 void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
105 long ingestJobId = context.getJobId();
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();
112 logger.log(Level.SEVERE,
"Error creating directory to store prefetch output database");
117 extractPrefetchFiles(dataSource, ingestJobId);
119 final String prefetchDumper = getPathForPrefetchDumper();
120 if (prefetchDumper == null) {
121 logger.log(Level.SEVERE,
"Error finding parse_prefetch program");
125 if (context.dataSourceIngestIsCancelled()) {
129 String modOutFile = modOutPath + File.separator + getPrefetchTempFolder(dataSource);
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);
137 }
catch (IOException ex) {
138 logger.log(Level.SEVERE,
"Error parsing prefetch files", ex);
139 addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
149 void extractPrefetchFiles(Content dataSource,
long ingestJobId) {
150 List<AbstractFile> pFiles;
152 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
155 pFiles = fileManager.findFiles(dataSource,
"%.pf");
156 }
catch (TskCoreException ex) {
157 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
161 for (AbstractFile pFile : pFiles) {
163 if (context.dataSourceIngestIsCancelled()) {
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();
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);
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);
200 List<String> commandLine =
new ArrayList<>();
201 commandLine.add(prefetchExePath);
202 commandLine.add(prefetchDir);
203 commandLine.add(tempOutFile);
205 ProcessBuilder processBuilder =
new ProcessBuilder(commandLine);
206 processBuilder.redirectOutput(outputFilePath.toFile());
207 processBuilder.redirectError(errFilePath.toFile());
209 ExecUtil.execute(processBuilder,
new DataSourceIngestModuleProcessTerminator(context,
true));
219 private String getPathForPrefetchDumper() {
221 if (PlatformUtil.isWindowsOS()) {
222 if (PlatformUtil.is64BitOS()) {
223 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_WINDOWS_64);
225 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_WINDOWS_32);
228 if (
"Linux".equals(PlatformUtil.getOSName())) {
229 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_LINUX);
231 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_MACOS);
234 File prefetchToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
235 ExtractPrefetch.class.getPackage().getName(),
false);
236 if (prefetchToolFile != null) {
237 return prefetchToolFile.getAbsolutePath();
252 private void createAppExecArtifacts(String prefetchDb, Content dataSource) {
253 List<BlackboardArtifact> blkBrdArtList =
new ArrayList<>();
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;";
260 try (SQLiteDBConnect tempdbconnect =
new SQLiteDBConnect(
"org.sqlite.JDBC",
"jdbc:sqlite:" + prefetchDb);
261 ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
263 while (resultSet.next()) {
265 if (context.dataSourceIngestIsCancelled()) {
266 logger.log(Level.INFO,
"Cancelled Prefetch Artifact Creation.");
270 String prefetchFileName = resultSet.getString(
"prefetch_File_Name");
271 String applicationName = resultSet.getString(
"actual_File_Name");
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");
284 Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
286 String baseName = FilenameUtils.getBaseName(prefetchFileName);
287 Matcher match = Pattern.compile(
"_(?<objId>\\d*)\\s*$").matcher(baseName);
289 logger.log(Level.WARNING,
"Invalid format for PF file: " + prefetchFileName);
301 AbstractFile pfAbstractFile = null;
303 Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group(
"objId")));
304 if (c instanceof AbstractFile) {
305 pfAbstractFile = (AbstractFile) c;
307 }
catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex) {
308 logger.log(Level.SEVERE,
"Unable to find content for: " + prefetchFileName, ex);
311 if (pfAbstractFile != null) {
312 for (Long executionTime : prefetchExecutionTimes) {
315 Collection<BlackboardAttribute> blkBrdAttributes = Arrays.asList(
316 new BlackboardAttribute(
317 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getDisplayName(),
319 new BlackboardAttribute(
320 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, getDisplayName(), filePath),
321 new BlackboardAttribute(
322 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getDisplayName(),
324 new BlackboardAttribute(
325 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, getDisplayName(), Integer.valueOf(timesProgramRun)),
326 new BlackboardAttribute(
327 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, getDisplayName(), PREFETCH_TSK_COMMENT));
330 BlackboardArtifact blkBrdArt = createArtifactWithAttributes(BlackboardArtifact.Type.TSK_PROG_RUN, pfAbstractFile, blkBrdAttributes);
331 blkBrdArtList.add(blkBrdArt);
332 BlackboardArtifact associatedBbArtifact = createAssociatedArtifact(applicationName.toLowerCase(), filePath, blkBrdArt, dataSource);
333 if (associatedBbArtifact != null) {
334 blkBrdArtList.add(associatedBbArtifact);
336 }
catch (TskCoreException ex) {
337 logger.log(Level.SEVERE,
"Exception Adding Artifact.", ex);
341 logger.log(Level.WARNING,
"File has a null value " + prefetchFileName);
345 }
catch (SQLException ex) {
346 logger.log(Level.WARNING, String.format(
"Error while trying to read into a sqlite db %s.", prefetchDb));
347 logger.log(Level.WARNING, ex.getMessage());
350 if (!blkBrdArtList.isEmpty() && !context.dataSourceIngestIsCancelled()) {
351 postArtifacts(blkBrdArtList);
366 private BlackboardArtifact createAssociatedArtifact(String fileName, String filePathName, BlackboardArtifact bba, Content dataSource)
throws TskCoreException {
367 AbstractFile sourceFile = getAbstractFile(fileName, filePathName, dataSource);
368 if (sourceFile != null) {
369 return createAssociatedArtifact(sourceFile, bba);
384 AbstractFile getAbstractFile(String fileName, String filePath, Content dataSource) {
385 List<AbstractFile> files;
387 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
390 files = fileManager.findFiles(dataSource, fileName);
392 }
catch (TskCoreException ex) {
393 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
397 for (AbstractFile pFile : files) {
398 if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() +
'/')) {
416 private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
417 Set<Long> prefetchExecutionTimes =
new HashSet<>();
418 for (Long executionTime : executionTimes) {
419 if (executionTime > 0) {
420 prefetchExecutionTimes.add(executionTime);
423 return prefetchExecutionTimes;
static String escapeFileName(String fileName)