20 package org.sleuthkit.autopsy.recentactivity;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.logging.Level;
30 import org.apache.poi.EmptyFileException;
31 import org.apache.poi.poifs.filesystem.DirectoryEntry;
32 import org.apache.poi.poifs.filesystem.DocumentEntry;
33 import org.apache.poi.poifs.filesystem.DocumentInputStream;
34 import org.apache.poi.poifs.filesystem.Entry;
35 import org.apache.poi.poifs.filesystem.NotOLE2FileException;
36 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
37 import org.openide.util.NbBundle.Messages;
60 final class ExtractJumpLists
extends Extract {
62 private static final Logger logger = Logger.getLogger(ExtractJumpLists.class.getName());
63 private static final String RA_DIR_NAME =
"RecentActivity";
64 private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY =
"%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/";
65 private static final String JUMPLIST_DIR_NAME =
"jumplists";
66 private static final String VERSION_NUMBER =
"1.0.0";
67 private String moduleName;
68 private FileManager fileManager;
69 private final IngestServices services = IngestServices.getInstance();
70 private final IngestJobContext context;
73 "Jumplist_module_name=Windows Jumplist Analyzer",
74 "Jumplist_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis."
76 ExtractJumpLists(IngestJobContext context) {
77 super(Bundle.Jumplist_module_name(), context);
78 this.context = context;
82 void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
83 moduleName = Bundle.Jumplist_module_name();
84 fileManager = currentCase.getServices().getFileManager();
85 long ingestJobId = context.getJobId();
87 String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME, ingestJobId);
88 List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId, baseRaTempPath);
89 if (jumpListFiles.isEmpty()) {
93 if (context.dataSourceIngestIsCancelled()) {
97 List<AbstractFile> derivedFiles =
new ArrayList<>();
98 String derivedPath = null;
99 String baseRaModPath = RAImageIngestModule.getRAOutputPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME, ingestJobId);
100 for (AbstractFile jumplistFile : jumpListFiles) {
101 if (!jumplistFile.getName().toLowerCase().contains(
"-slack") && !jumplistFile.getName().equals(
"..")
102 && !jumplistFile.getName().equals(
".") && jumplistFile.getSize() > 0) {
103 String jlFile = Paths.get(baseRaTempPath, jumplistFile.getName() +
"_" + jumplistFile.getId()).toString();
104 String moduleOutPath = baseRaModPath + File.separator + jumplistFile.getName() +
"_" + jumplistFile.getId();
105 derivedPath = RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME +
"_" + ingestJobId + File.separator + jumplistFile.getName() +
"_" + jumplistFile.getId();
106 File jlDir =
new File(moduleOutPath);
107 if (jlDir.exists() ==
false) {
108 boolean dirMade = jlDir.mkdirs();
110 logger.log(Level.WARNING,
"Error creating directory to store Jumplist LNK files %s", moduleOutPath);
114 derivedFiles.addAll(extractLnkFiles(jlFile, moduleOutPath, jumplistFile, derivedPath));
119 progressBar.progress(String.format(Bundle.Jumplist_adding_extracted_files_msg(), derivedFiles.size()));
120 derivedFiles.forEach((derived) -> {
121 services.fireModuleContentEvent(
new ModuleContentEvent(derived));
123 context.addFilesToJob(derivedFiles);
132 private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId, String baseRaTempPath) {
133 List<AbstractFile> jumpListFiles =
new ArrayList<>();;
134 List<AbstractFile> tempJumpListFiles =
new ArrayList<>();;
136 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
139 tempJumpListFiles = fileManager.findFiles(dataSource,
"%", AUTOMATIC_DESTINATIONS_FILE_DIRECTORY);
140 if (!tempJumpListFiles.isEmpty()) {
141 jumpListFiles.addAll(tempJumpListFiles);
143 }
catch (TskCoreException ex) {
144 logger.log(Level.WARNING,
"Unable to find jumplist files.", ex);
145 return jumpListFiles;
148 for (AbstractFile jumpListFile : jumpListFiles) {
150 if (context.dataSourceIngestIsCancelled()) {
151 return jumpListFiles;
154 if (!jumpListFile.getName().toLowerCase().contains(
"-slack") && !jumpListFile.getName().equals(
"..")
155 && !jumpListFile.getName().equals(
".") && jumpListFile.getSize() > 0) {
156 String fileName = jumpListFile.getName() +
"_" + jumpListFile.getId();
157 String jlFile = Paths.get(baseRaTempPath, fileName).toString();
159 ContentUtils.writeToFile(jumpListFile,
new File(jlFile));
160 }
catch (IOException ex) {
161 logger.log(Level.WARNING, String.format(
"Unable to write %s to temp directory. File name: %s", fileName, jlFile), ex);
166 return jumpListFiles;
173 private List<DerivedFile> extractLnkFiles(String jumpListFile, String moduleOutPath, AbstractFile jumpListAbsFile, String derivedPath) {
175 List<DerivedFile> derivedFiles =
new ArrayList<>();
176 DerivedFile derivedFile;
177 String lnkFileName =
"";
179 try (POIFSFileSystem fs =
new POIFSFileSystem(
new File(jumpListFile))) {
180 DirectoryEntry root = fs.getRoot();
181 for (Entry entry : root) {
182 if (entry instanceof DirectoryEntry) {
185 }
else if (entry instanceof DocumentEntry) {
186 String jmpListFileName = entry.getName();
187 int fileSize = ((DocumentEntry) entry).getSize();
190 try (DocumentInputStream stream = fs.createDocumentInputStream(jmpListFileName)) {
191 byte[] buffer =
new byte[stream.available()];
194 JLnkParser lnkParser =
new JLnkParser(fs.createDocumentInputStream(jmpListFileName), fileSize);
195 JLNK lnk = lnkParser.parse();
196 lnkFileName = lnk.getBestName() +
".lnk";
197 File targetFile =
new File(moduleOutPath + File.separator + entry.getName() +
"-" + lnkFileName);
198 String relativePath = Case.getCurrentCase().getModuleOutputDirectoryRelativePath();
199 String derivedFileName = Case.getCurrentCase().getModuleOutputDirectoryRelativePath() + File.separator + derivedPath + File.separator + entry.getName() +
"-" + lnkFileName;
200 OutputStream outStream =
new FileOutputStream(targetFile);
201 outStream.write(buffer);
203 derivedFile = fileManager.addDerivedFile(lnkFileName, derivedFileName,
215 TskData.EncodingType.NONE);
216 derivedFiles.add(derivedFile);
218 }
catch (IOException | JLnkParserException ex) {
219 logger.log(Level.WARNING, String.format(
"No such document, or the Entry represented by documentName is not a DocumentEntry link file is %s", jumpListFile), ex);
220 }
catch (TskCoreException ex) {
221 logger.log(Level.WARNING, String.format(
"Error trying to add dervived file %s", lnkFileName), ex);
222 }
catch (IndexOutOfBoundsException ex) {
225 logger.log(Level.WARNING, String.format(
"Error parsing the the jumplist file %s", jumpListFile), ex);
235 }
catch (NotOLE2FileException | EmptyFileException ex1) {
236 logger.log(Level.WARNING, String.format(
"Error file not a valid OLE2 Document $s", jumpListFile));
237 }
catch (IOException ex) {
238 logger.log(Level.WARNING, String.format(
"Error lnk parsing the file to get recent files $s", jumpListFile), ex);