20package org.sleuthkit.autopsy.recentactivity;
23import java.io.FileNotFoundException;
24import java.io.IOException;
25import java.nio.file.Path;
26import java.nio.file.Paths;
27import java.sql.ResultSet;
28import java.sql.SQLException;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Collection;
32import java.util.HashSet;
35import java.util.logging.Level;
36import java.util.regex.Matcher;
37import java.util.regex.Pattern;
38import org.apache.commons.io.FilenameUtils;
39import org.openide.modules.InstalledFileLocator;
40import org.openide.util.NbBundle.Messages;
41import org.sleuthkit.autopsy.casemodule.Case;
42import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
43import org.sleuthkit.autopsy.casemodule.services.FileManager;
44import org.sleuthkit.autopsy.coreutils.ExecUtil;
45import static org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName;
46import org.sleuthkit.autopsy.coreutils.Logger;
47import org.sleuthkit.autopsy.coreutils.PlatformUtil;
48import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect;
49import org.sleuthkit.autopsy.datamodel.ContentUtils;
50import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
51import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
52import org.sleuthkit.autopsy.ingest.IngestJobContext;
53import org.sleuthkit.datamodel.AbstractFile;
54import org.sleuthkit.datamodel.BlackboardArtifact;
55import org.sleuthkit.datamodel.BlackboardAttribute;
56import org.sleuthkit.datamodel.Content;
57import org.sleuthkit.datamodel.TskCoreException;
64final 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_X64_WINDOWS =
"mm_artifact_parser_x64_win.exe";
73 private static final String PREFETCH_TOOL_NAME_X64_LINUX =
"mm_artifact_parser_x64_linux";
74 private static final String PREFETCH_TOOL_NAME_X64_MACOS =
"mm_artifact_parser_x64_macos";
75 private static final String PREFETCH_TOOL_NAME_AARCH64_LINUX =
"mm_artifact_parser_aarch64_linux";
76 private static final String PREFETCH_TOOL_NAME_AARCH64_MACOS =
"mm_artifact_parser_aarch64_macos";
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 Analyzer",
84 "# {0} - sub module name",
85 "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
87 ExtractPrefetch(IngestJobContext context) {
88 super(Bundle.ExtractPrefetch_module_name(), context);
89 this.context = context;
99 private String getPrefetchTempFolder(Content dataSource) {
100 return dataSource.getId() +
"-" + PREFETCH_PARSER_DB_FILE;
104 void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
106 long ingestJobId = context.getJobId();
108 String modOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + PREFETCH_DIR_NAME;
109 File dir =
new File(modOutPath);
110 if (dir.exists() ==
false) {
111 boolean dirMade = dir.mkdirs();
113 logger.log(Level.SEVERE,
"Error creating directory to store prefetch output database");
118 extractPrefetchFiles(dataSource, ingestJobId);
120 final String prefetchDumper = getPathForPrefetchDumper();
121 if (prefetchDumper ==
null) {
122 logger.log(Level.SEVERE,
"Error finding parse_prefetch program");
126 if (context.dataSourceIngestIsCancelled()) {
130 String modOutFile = modOutPath + File.separator + getPrefetchTempFolder(dataSource);
132 String tempDirPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), getPrefetchTempFolder(dataSource), ingestJobId);
133 parsePrefetchFiles(prefetchDumper, tempDirPath, modOutFile, modOutPath);
134 File prefetchDatabase =
new File(modOutFile);
135 if (prefetchDatabase.exists()) {
136 createAppExecArtifacts(modOutFile, dataSource);
138 }
catch (IOException ex) {
139 logger.log(Level.SEVERE,
"Error parsing prefetch files", ex);
140 addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
150 void extractPrefetchFiles(Content dataSource,
long ingestJobId) {
151 List<AbstractFile> pFiles;
153 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
156 pFiles = fileManager.findFiles(dataSource,
"%.pf");
157 }
catch (TskCoreException ex) {
158 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
162 for (AbstractFile pFile : pFiles) {
164 if (context.dataSourceIngestIsCancelled()) {
168 if (pFile.getParentPath().toLowerCase().contains(PREFETCH_FILE_LOCATION.toLowerCase()) && pFile.getSize() > 0) {
169 String origFileName = pFile.getName();
170 String ext = FilenameUtils.getExtension(origFileName);
171 String baseName = FilenameUtils.getBaseName(origFileName);
172 String fileName = escapeFileName(String.format(
"%s_%d.%s", baseName, pFile.getId(), ext));
173 String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), getPrefetchTempFolder(dataSource), ingestJobId);
174 String prefetchFile = Paths.get(baseRaTempPath, fileName).toString();
176 ContentUtils.writeToFile(pFile,
new File(prefetchFile));
177 }
catch (IOException ex) {
178 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(
"-a");
203 commandLine.add(
"prefetch");
204 commandLine.add(
"-f");
205 commandLine.add(prefetchDir);
206 commandLine.add(
"-db");
207 commandLine.add(tempOutFile);
209 ProcessBuilder processBuilder =
new ProcessBuilder(commandLine);
210 processBuilder.redirectOutput(outputFilePath.toFile());
211 processBuilder.redirectError(errFilePath.toFile());
213 ExecUtil.execute(processBuilder,
new DataSourceIngestModuleProcessTerminator(context,
true));
223 private String getPathForPrefetchDumper() {
225 if (PlatformUtil.isWindowsOS()) {
226 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_X64_WINDOWS);
227 }
else if (PlatformUtil.isLinuxOS()) {
228 if (
"aarch64".equals(PlatformUtil.getOSArch())) {
229 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_AARCH64_LINUX);
231 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_X64_LINUX);
233 }
else if (PlatformUtil.isMacOS()) {
234 if (
"aarch64".equals(PlatformUtil.getOSArch())) {
235 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_AARCH64_MACOS);
237 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_X64_MACOS);
241 File prefetchToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
242 ExtractPrefetch.class.getPackage().getName(),
false);
243 if (prefetchToolFile !=
null) {
244 return prefetchToolFile.getAbsolutePath();
259 private void createAppExecArtifacts(String prefetchDb, Content dataSource) {
260 List<BlackboardArtifact> blkBrdArtList =
new ArrayList<>();
262 String sqlStatement =
"SELECT prefetch_File_Name, actual_File_Name, file_path, Number_time_file_run, Embeded_date_Time_Unix_1, "
263 +
" Embeded_date_Time_Unix_2, Embeded_date_Time_Unix_3, Embeded_date_Time_Unix_4, Embeded_date_Time_Unix_5,"
264 +
" Embeded_date_Time_Unix_6, Embeded_date_Time_Unix_7, Embeded_date_Time_Unix_8 "
265 +
" FROM prefetch_file_info;";
267 try (SQLiteDBConnect tempdbconnect =
new SQLiteDBConnect(
"org.sqlite.JDBC",
"jdbc:sqlite:" + prefetchDb);
268 ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
270 while (resultSet.next()) {
272 if (context.dataSourceIngestIsCancelled()) {
273 logger.log(Level.INFO,
"Cancelled Prefetch Artifact Creation.");
277 String prefetchFileName = resultSet.getString(
"prefetch_File_Name");
278 String applicationName = resultSet.getString(
"actual_File_Name");
279 List<Long> executionTimes =
new ArrayList<>();
280 executionTimes.add(Long.valueOf(resultSet.getInt(
"Embeded_date_Time_Unix_1")));
281 executionTimes.add(Long.valueOf(resultSet.getInt(
"Embeded_date_Time_Unix_2")));
282 executionTimes.add(Long.valueOf(resultSet.getInt(
"Embeded_date_Time_Unix_3")));
283 executionTimes.add(Long.valueOf(resultSet.getInt(
"Embeded_date_Time_Unix_4")));
284 executionTimes.add(Long.valueOf(resultSet.getInt(
"Embeded_date_Time_Unix_5")));
285 executionTimes.add(Long.valueOf(resultSet.getInt(
"Embeded_date_Time_Unix_6")));
286 executionTimes.add(Long.valueOf(resultSet.getInt(
"Embeded_date_Time_Unix_7")));
287 executionTimes.add(Long.valueOf(resultSet.getInt(
"Embeded_date_Time_Unix_8")));
288 String timesProgramRun = resultSet.getString(
"Number_time_file_run");
289 String filePath = resultSet.getString(
"file_path");
291 Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
293 String baseName = FilenameUtils.getBaseName(prefetchFileName);
294 Matcher match = Pattern.compile(
"_(?<objId>\\d*)\\s*$").matcher(baseName);
296 logger.log(Level.WARNING,
"Invalid format for PF file: " + prefetchFileName);
308 AbstractFile pfAbstractFile =
null;
310 Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group(
"objId")));
311 if (c instanceof AbstractFile) {
312 pfAbstractFile = (AbstractFile) c;
314 }
catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex) {
315 logger.log(Level.SEVERE,
"Unable to find content for: " + prefetchFileName, ex);
318 if (pfAbstractFile !=
null) {
319 for (Long executionTime : prefetchExecutionTimes) {
322 Collection<BlackboardAttribute> blkBrdAttributes = Arrays.asList(
323 new BlackboardAttribute(
324 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getDisplayName(),
326 new BlackboardAttribute(
327 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, getDisplayName(), filePath),
328 new BlackboardAttribute(
329 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getDisplayName(),
331 new BlackboardAttribute(
332 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, getDisplayName(), Integer.valueOf(timesProgramRun)),
333 new BlackboardAttribute(
334 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, getDisplayName(), PREFETCH_TSK_COMMENT));
337 BlackboardArtifact blkBrdArt = createArtifactWithAttributes(BlackboardArtifact.Type.TSK_PROG_RUN, pfAbstractFile, blkBrdAttributes);
338 blkBrdArtList.add(blkBrdArt);
339 BlackboardArtifact associatedBbArtifact = createAssociatedArtifact(applicationName.toLowerCase(), filePath, blkBrdArt, dataSource);
340 if (associatedBbArtifact !=
null) {
341 blkBrdArtList.add(associatedBbArtifact);
343 }
catch (TskCoreException ex) {
344 logger.log(Level.SEVERE,
"Exception Adding Artifact.", ex);
348 logger.log(Level.WARNING,
"File has a null value " + prefetchFileName);
352 }
catch (SQLException ex) {
353 logger.log(Level.WARNING, String.format(
"Error while trying to read into a sqlite db %s.", prefetchDb));
354 logger.log(Level.WARNING, ex.getMessage());
357 if (!blkBrdArtList.isEmpty() && !context.dataSourceIngestIsCancelled()) {
358 postArtifacts(blkBrdArtList);
373 private BlackboardArtifact createAssociatedArtifact(String fileName, String filePathName, BlackboardArtifact bba, Content dataSource)
throws TskCoreException {
374 AbstractFile sourceFile = getAbstractFile(fileName, filePathName, dataSource);
375 if (sourceFile !=
null) {
376 return createAssociatedArtifact(sourceFile, bba);
391 AbstractFile getAbstractFile(String fileName, String filePath, Content dataSource) {
392 List<AbstractFile> files;
394 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
397 files = fileManager.findFiles(dataSource, fileName);
399 }
catch (TskCoreException ex) {
400 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
404 for (AbstractFile pFile : files) {
405 if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() +
'/')) {
423 private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
424 Set<Long> prefetchExecutionTimes =
new HashSet<>();
425 for (Long executionTime : executionTimes) {
426 if (executionTime > 0) {
427 prefetchExecutionTimes.add(executionTime);
430 return prefetchExecutionTimes;