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());
 
   97     private String getPrefetchTempFolder(Content dataSource) {
 
   98         return dataSource.getId() + 
"-" + PREFETCH_PARSER_DB_FILE;
 
  102     void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
 
  104         this.context = context;
 
  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);
 
  300                 AbstractFile pfAbstractFile = null;
 
  302                     Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group(
"objId")));
 
  303                     if (c instanceof AbstractFile) {
 
  304                         pfAbstractFile = (AbstractFile) c;
 
  306                 } 
catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex ) {
 
  307                     logger.log(Level.SEVERE, 
"Unable to find content for: " + prefetchFileName, ex);
 
  310                 if (pfAbstractFile != null) {
 
  311                     for (Long executionTime : prefetchExecutionTimes) {
 
  314                         Collection<BlackboardAttribute> blkBrdAttributes = Arrays.asList(
 
  315                                 new BlackboardAttribute(
 
  316                                         BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
 
  318                                 new BlackboardAttribute(
 
  319                                         BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, getName(), filePath),
 
  320                                 new BlackboardAttribute(
 
  321                                         BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getName(),
 
  323                                 new BlackboardAttribute(
 
  324                                         BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, getName(), Integer.valueOf(timesProgramRun)),
 
  325                                 new BlackboardAttribute(
 
  326                                         BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, getName(), PREFETCH_TSK_COMMENT));
 
  329                             BlackboardArtifact blkBrdArt = createArtifactWithAttributes(BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN, pfAbstractFile, blkBrdAttributes);
 
  330                             blkBrdArtList.add(blkBrdArt);
 
  331                             BlackboardArtifact associatedBbArtifact = createAssociatedArtifact(applicationName.toLowerCase(), filePath, blkBrdArt, dataSource);
 
  332                             if (associatedBbArtifact != null) {
 
  333                                 blkBrdArtList.add(associatedBbArtifact);
 
  335                         } 
catch (TskCoreException ex) {
 
  336                             logger.log(Level.SEVERE, 
"Exception Adding Artifact.", ex);
 
  340                     logger.log(Level.WARNING, 
"File has a null value " + prefetchFileName);
 
  344         } 
catch (SQLException ex) {
 
  345             logger.log(Level.WARNING, String.format(
"Error while trying to read into a sqlite db %s.", prefetchDb));
 
  346             logger.log(Level.WARNING, ex.getMessage());
 
  349         if (!blkBrdArtList.isEmpty() && !context.dataSourceIngestIsCancelled()) {
 
  350             postArtifacts(blkBrdArtList);
 
  365     private BlackboardArtifact createAssociatedArtifact(String fileName, String filePathName, BlackboardArtifact bba, Content dataSource) 
throws TskCoreException {
 
  366         AbstractFile sourceFile = getAbstractFile(fileName, filePathName, dataSource);
 
  367         if (sourceFile != null) {
 
  368             return createAssociatedArtifact(sourceFile, bba);         
 
  383     AbstractFile getAbstractFile(String fileName, String filePath, Content dataSource) {
 
  384         List<AbstractFile> files;
 
  386         FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
 
  389             files = fileManager.findFiles(dataSource, fileName); 
 
  391         } 
catch (TskCoreException ex) {
 
  392             logger.log(Level.WARNING, 
"Unable to find prefetch files.", ex); 
 
  396         for (AbstractFile pFile : files) {
 
  397             if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() + 
'/')) {
 
  415     private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
 
  416         Set<Long> prefetchExecutionTimes = 
new HashSet<>();
 
  417         for (Long executionTime : executionTimes) {                        
 
  418             if (executionTime > 0) {
 
  419                 prefetchExecutionTimes.add(executionTime);
 
  422         return prefetchExecutionTimes;
 
static String escapeFileName(String fileName)