Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractPrefetch.java
Go to the documentation of this file.
1/*
2 *
3 * Autopsy Forensic Browser
4 *
5 * Copyright 2020-2021 Basis Technology Corp.
6 *
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20package org.sleuthkit.autopsy.recentactivity;
21
22import java.io.File;
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;
33import java.util.List;
34import java.util.Set;
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;
58
64final class ExtractPrefetch extends Extract {
65
66 private static final Logger logger = Logger.getLogger(ExtractPrefetch.class.getName());
67
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"; //NON-NLS
72 private static final String PREFETCH_TOOL_NAME_X64_WINDOWS = "mm_artifact_parser_x64_win.exe"; //NON-NLS
73 private static final String PREFETCH_TOOL_NAME_X64_LINUX = "mm_artifact_parser_x64_linux"; //NON-NLS
74 private static final String PREFETCH_TOOL_NAME_X64_MACOS = "mm_artifact_parser_x64_macos"; //NON-NLS
75 private static final String PREFETCH_TOOL_NAME_AARCH64_LINUX = "mm_artifact_parser_aarch64_linux"; //NON-NLS
76 private static final String PREFETCH_TOOL_NAME_AARCH64_MACOS = "mm_artifact_parser_aarch64_macos"; //NON-NLS
77 private static final String PREFETCH_OUTPUT_FILE_NAME = "Output.txt"; //NON-NLS
78 private static final String PREFETCH_ERROR_FILE_NAME = "Error.txt"; //NON-NLS
79 private static final String PREFETCH_PARSER_DB_FILE = "Autopsy_PF_DB.db3"; //NON-NLS
80 private static final String PREFETCH_DIR_NAME = "prefetch"; //NON-NLS
81
82 @Messages({
83 "ExtractPrefetch_module_name=Windows Prefetch Analyzer",
84 "# {0} - sub module name",
85 "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
86 })
87 ExtractPrefetch(IngestJobContext context) {
88 super(Bundle.ExtractPrefetch_module_name(), context);
89 this.context = context;
90 }
91
99 private String getPrefetchTempFolder(Content dataSource) {
100 return dataSource.getId() + "-" + PREFETCH_PARSER_DB_FILE;
101 }
102
103 @Override
104 void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
105
106 long ingestJobId = context.getJobId();
107
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();
112 if (!dirMade) {
113 logger.log(Level.SEVERE, "Error creating directory to store prefetch output database"); //NON-NLS
114 return; //If we cannot create the directory then we need to exit
115 }
116 }
117
118 extractPrefetchFiles(dataSource, ingestJobId);
119
120 final String prefetchDumper = getPathForPrefetchDumper();
121 if (prefetchDumper == null) {
122 logger.log(Level.SEVERE, "Error finding parse_prefetch program"); //NON-NLS
123 return; //If we cannot find the parse_prefetch program we cannot proceed
124 }
125
126 if (context.dataSourceIngestIsCancelled()) {
127 return;
128 }
129
130 String modOutFile = modOutPath + File.separator + getPrefetchTempFolder(dataSource);
131 try {
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);
137 }
138 } catch (IOException ex) {
139 logger.log(Level.SEVERE, "Error parsing prefetch files", ex); //NON-NLS
140 addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
141 }
142 }
143
150 void extractPrefetchFiles(Content dataSource, long ingestJobId) {
151 List<AbstractFile> pFiles;
152
153 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
154
155 try {
156 pFiles = fileManager.findFiles(dataSource, "%.pf"); //NON-NLS
157 } catch (TskCoreException ex) {
158 logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
159 return; // No need to continue
160 }
161
162 for (AbstractFile pFile : pFiles) {
163
164 if (context.dataSourceIngestIsCancelled()) {
165 return;
166 }
167
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();
175 try {
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); //NON-NLS
179 }
180 }
181 }
182 }
183
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);
199
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); //NON-NLS
206 commandLine.add("-db");
207 commandLine.add(tempOutFile);
208
209 ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
210 processBuilder.redirectOutput(outputFilePath.toFile());
211 processBuilder.redirectError(errFilePath.toFile());
212
213 ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
214 }
215
223 private String getPathForPrefetchDumper() {
224 Path path = null;
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);
230 } else {
231 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_X64_LINUX);
232 }
233 } else if (PlatformUtil.isMacOS()) {
234 if ("aarch64".equals(PlatformUtil.getOSArch())) {
235 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_AARCH64_MACOS);
236 } else {
237 path = Paths.get(PREFETCH_TOOL_FOLDER, PREFETCH_TOOL_NAME_X64_MACOS);
238 }
239
240 }
241 File prefetchToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
242 ExtractPrefetch.class.getPackage().getName(), false);
243 if (prefetchToolFile != null) {
244 return prefetchToolFile.getAbsolutePath();
245 }
246
247 return null;
248
249 }
250
259 private void createAppExecArtifacts(String prefetchDb, Content dataSource) {
260 List<BlackboardArtifact> blkBrdArtList = new ArrayList<>();
261
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;"; //NON-NLS
266
267 try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + prefetchDb); //NON-NLS
268 ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
269
270 while (resultSet.next()) {
271
272 if (context.dataSourceIngestIsCancelled()) {
273 logger.log(Level.INFO, "Cancelled Prefetch Artifact Creation."); //NON-NLS
274 return;
275 }
276
277 String prefetchFileName = resultSet.getString("prefetch_File_Name");
278 String applicationName = resultSet.getString("actual_File_Name"); //NON-NLS
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");
290
291 Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
292
293 String baseName = FilenameUtils.getBaseName(prefetchFileName);
294 Matcher match = Pattern.compile("_(?<objId>\\d*)\\s*$").matcher(baseName);
295 if (!match.find()) {
296 logger.log(Level.WARNING, "Invalid format for PF file: " + prefetchFileName);//NON-NLS
297 continue;
298 }
299
308 AbstractFile pfAbstractFile = null;
309 try {
310 Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group("objId")));
311 if (c instanceof AbstractFile) {
312 pfAbstractFile = (AbstractFile) c;
313 }
314 } catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex) {
315 logger.log(Level.SEVERE, "Unable to find content for: " + prefetchFileName, ex);
316 }
317
318 if (pfAbstractFile != null) {
319 for (Long executionTime : prefetchExecutionTimes) {
320
321 // only add prefetch file entries that have an actual date associated with them
322 Collection<BlackboardAttribute> blkBrdAttributes = Arrays.asList(
323 new BlackboardAttribute(
324 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getDisplayName(),
325 applicationName),//NON-NLS
326 new BlackboardAttribute(
327 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, getDisplayName(), filePath),
328 new BlackboardAttribute(
329 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getDisplayName(),
330 executionTime),
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));
335
336 try {
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);
342 }
343 } catch (TskCoreException ex) {
344 logger.log(Level.SEVERE, "Exception Adding Artifact.", ex);//NON-NLS
345 }
346 }
347 } else {
348 logger.log(Level.WARNING, "File has a null value " + prefetchFileName);//NON-NLS
349 }
350
351 }
352 } catch (SQLException ex) {
353 logger.log(Level.WARNING, String.format("Error while trying to read into a sqlite db %s.", prefetchDb));//NON-NLS
354 logger.log(Level.WARNING, ex.getMessage());
355 }
356
357 if (!blkBrdArtList.isEmpty() && !context.dataSourceIngestIsCancelled()) {
358 postArtifacts(blkBrdArtList);
359 }
360 }
361
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);
377 }
378 return null;
379 }
380
391 AbstractFile getAbstractFile(String fileName, String filePath, Content dataSource) {
392 List<AbstractFile> files;
393
394 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
395
396 try {
397 files = fileManager.findFiles(dataSource, fileName); //NON-NLS
398
399 } catch (TskCoreException ex) {
400 logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
401 return null; // No need to continue
402 }
403
404 for (AbstractFile pFile : files) {
405 if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() + '/')) {
406 return pFile;
407 }
408 }
409
410 return null;
411
412 }
413
423 private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
424 Set<Long> prefetchExecutionTimes = new HashSet<>();
425 for (Long executionTime : executionTimes) { // only add prefetch file entries that have an actual date associated with them
426 if (executionTime > 0) {
427 prefetchExecutionTimes.add(executionTime);
428 }
429 }
430 return prefetchExecutionTimes;
431 }
432}

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.