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.poifs.filesystem.DirectoryEntry;
31 import org.apache.poi.poifs.filesystem.DocumentEntry;
32 import org.apache.poi.poifs.filesystem.DocumentInputStream;
33 import org.apache.poi.poifs.filesystem.Entry;
34 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
35 import org.openide.util.NbBundle.Messages;
57 final class ExtractJumpLists
extends Extract {
59 private static final Logger logger = Logger.getLogger(ExtractJumpLists.class.getName());
61 private IngestJobContext context;
63 private static final String JUMPLIST_TSK_COMMENT =
"Jumplist File";
64 private static final String RA_DIR_NAME =
"RecentActivity";
65 private static final String MODULE_OUTPUT_DIR =
"ModuleOutput";
66 private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY =
"%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/";
67 private static final String JUMPLIST_DIR_NAME =
"jumplists";
68 private static final String VERSION_NUMBER =
"1.0.0";
69 private String moduleName;
70 private FileManager fileManager;
71 private final IngestServices services = IngestServices.getInstance();
74 "Jumplist_module_name=Windows Jumplist Extractor",
75 "Jumplist_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis."
78 super(Bundle.Jumplist_module_name());
82 void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
84 this.context = context;
85 moduleName = Bundle.Jumplist_module_name();
86 fileManager = currentCase.getServices().getFileManager();
87 long ingestJobId = context.getJobId();
89 List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId);
91 if (jumpListFiles.isEmpty()) {
95 if (context.dataSourceIngestIsCancelled()) {
99 List<AbstractFile> derivedFiles =
new ArrayList<>();
100 String derivedPath = null;
101 String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME +
"_" + dataSource.getId(), ingestJobId);
102 for (AbstractFile jumplistFile : jumpListFiles) {
103 if (!jumplistFile.getName().toLowerCase().contains(
"-slack") && !jumplistFile.getName().equals(
"..") &&
104 !jumplistFile.getName().equals(
".") && jumplistFile.getSize() > 0) {
105 String jlFile = Paths.get(baseRaTempPath, jumplistFile.getName() +
"_" + jumplistFile.getId()).toString();
106 String moduleOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME +
"_" + dataSource.getId() + File.separator + jumplistFile.getName() +
"_" + jumplistFile.getId();
107 derivedPath = RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME +
"_" + dataSource.getId() + File.separator + jumplistFile.getName() +
"_" + jumplistFile.getId();
108 File jlDir =
new File(moduleOutPath);
109 if (jlDir.exists() ==
false) {
110 boolean dirMade = jlDir.mkdirs();
112 logger.log(Level.WARNING,
"Error creating directory to store Jumplist LNK files %s", moduleOutPath);
116 derivedFiles.addAll(extractLnkFiles(jlFile, moduleOutPath, jumplistFile, derivedPath));
121 progressBar.progress(String.format(Bundle.Jumplist_adding_extracted_files_msg(), derivedFiles.size()));
122 derivedFiles.forEach((derived) -> { services.fireModuleContentEvent(
new ModuleContentEvent(derived)); });
123 context.addFilesToJob(derivedFiles);
132 private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId) {
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 baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME+
"_" + dataSource.getId(), ingestJobId);
158 String jlFile = Paths.get(baseRaTempPath, fileName).toString();
160 ContentUtils.writeToFile(jumpListFile,
new File(jlFile));
161 }
catch (IOException ex) {
162 logger.log(Level.WARNING, String.format(
"Unable to write %s to temp directory. File name: %s", fileName, jlFile), ex);
167 return jumpListFiles;
174 private List<DerivedFile> extractLnkFiles(String jumpListFile, String moduleOutPath, AbstractFile jumpListAbsFile, String derivedPath) {
176 List<DerivedFile> derivedFiles =
new ArrayList<>();
177 DerivedFile derivedFile;
178 String lnkFileName =
"";
180 try (POIFSFileSystem fs =
new POIFSFileSystem(
new File(jumpListFile))) {
181 DirectoryEntry root = fs.getRoot();
182 for (Entry entry : root) {
183 if (entry instanceof DirectoryEntry) {
186 }
else if (entry instanceof DocumentEntry) {
187 String jmpListFileName = entry.getName();
188 int fileSize = ((DocumentEntry) entry).getSize();
191 try (DocumentInputStream stream = fs.createDocumentInputStream(jmpListFileName)) {
192 byte[] buffer =
new byte[stream.available()];
195 JLnkParser lnkParser =
new JLnkParser(fs.createDocumentInputStream(jmpListFileName), fileSize);
196 JLNK lnk = lnkParser.parse();
197 lnkFileName = lnk.getBestName() +
".lnk";
198 File targetFile =
new File(moduleOutPath + File.separator + entry.getName() +
"-" + lnkFileName);
199 String derivedFileName = MODULE_OUTPUT_DIR + 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 e) {
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), e);
229 }
catch (IOException | TskCoreException ex) {
230 logger.log(Level.WARNING, String.format(
"Error lnk parsing the file to get recent files $s", jumpListFile), ex);