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;
59 final class ExtractJumpLists
extends Extract {
61 private static final Logger logger = Logger.getLogger(ExtractJumpLists.class.getName());
63 private IngestJobContext context;
65 private static final String JUMPLIST_TSK_COMMENT =
"Jumplist File";
66 private static final String RA_DIR_NAME =
"RecentActivity";
67 private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY =
"%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/";
68 private static final String JUMPLIST_DIR_NAME =
"jumplists";
69 private static final String VERSION_NUMBER =
"1.0.0";
70 private String moduleName;
71 private FileManager fileManager;
72 private final IngestServices services = IngestServices.getInstance();
75 "Jumplist_module_name=Windows Jumplist Extractor",
76 "Jumplist_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis."
79 super(Bundle.Jumplist_module_name());
83 void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
85 this.context = context;
86 moduleName = Bundle.Jumplist_module_name();
87 fileManager = currentCase.getServices().getFileManager();
88 long ingestJobId = context.getJobId();
90 String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME , ingestJobId);
91 List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId, baseRaTempPath);
93 if (jumpListFiles.isEmpty()) {
97 if (context.dataSourceIngestIsCancelled()) {
101 List<AbstractFile> derivedFiles =
new ArrayList<>();
102 String derivedPath = null;
103 String baseRaModPath = RAImageIngestModule.getRAOutputPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME, ingestJobId);
104 for (AbstractFile jumplistFile : jumpListFiles) {
105 if (!jumplistFile.getName().toLowerCase().contains(
"-slack") && !jumplistFile.getName().equals(
"..") &&
106 !jumplistFile.getName().equals(
".") && jumplistFile.getSize() > 0) {
107 String jlFile = Paths.get(baseRaTempPath, jumplistFile.getName() +
"_" + jumplistFile.getId()).toString();
108 String moduleOutPath = baseRaModPath + File.separator + jumplistFile.getName() +
"_" + jumplistFile.getId();
109 derivedPath = RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME +
"_" + ingestJobId + File.separator + jumplistFile.getName() +
"_" + jumplistFile.getId();
110 File jlDir =
new File(moduleOutPath);
111 if (jlDir.exists() ==
false) {
112 boolean dirMade = jlDir.mkdirs();
114 logger.log(Level.WARNING,
"Error creating directory to store Jumplist LNK files %s", moduleOutPath);
118 derivedFiles.addAll(extractLnkFiles(jlFile, moduleOutPath, jumplistFile, derivedPath));
123 progressBar.progress(String.format(Bundle.Jumplist_adding_extracted_files_msg(), derivedFiles.size()));
124 derivedFiles.forEach((derived) -> { services.fireModuleContentEvent(
new ModuleContentEvent(derived)); });
125 context.addFilesToJob(derivedFiles);
134 private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId, String baseRaTempPath) {
135 List<AbstractFile> jumpListFiles =
new ArrayList<>();;
136 List<AbstractFile> tempJumpListFiles =
new ArrayList<>();;
138 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
141 tempJumpListFiles = fileManager.findFiles(dataSource,
"%", AUTOMATIC_DESTINATIONS_FILE_DIRECTORY);
142 if (!tempJumpListFiles.isEmpty()) {
143 jumpListFiles.addAll(tempJumpListFiles);
145 }
catch (TskCoreException ex) {
146 logger.log(Level.WARNING,
"Unable to find jumplist files.", ex);
147 return jumpListFiles;
150 for (AbstractFile jumpListFile : jumpListFiles) {
152 if (context.dataSourceIngestIsCancelled()) {
153 return jumpListFiles;
156 if (!jumpListFile.getName().toLowerCase().contains(
"-slack") && !jumpListFile.getName().equals(
"..") &&
157 !jumpListFile.getName().equals(
".") && jumpListFile.getSize() > 0) {
158 String fileName = jumpListFile.getName() +
"_" + jumpListFile.getId();
159 String jlFile = Paths.get(baseRaTempPath, fileName).toString();
161 ContentUtils.writeToFile(jumpListFile,
new File(jlFile));
162 }
catch (IOException ex) {
163 logger.log(Level.WARNING, String.format(
"Unable to write %s to temp directory. File name: %s", fileName, jlFile), ex);
168 return jumpListFiles;
175 private List<DerivedFile> extractLnkFiles(String jumpListFile, String moduleOutPath, AbstractFile jumpListAbsFile, String derivedPath) {
177 List<DerivedFile> derivedFiles =
new ArrayList<>();
178 DerivedFile derivedFile;
179 String lnkFileName =
"";
181 try (POIFSFileSystem fs =
new POIFSFileSystem(
new File(jumpListFile))) {
182 DirectoryEntry root = fs.getRoot();
183 for (Entry entry : root) {
184 if (entry instanceof DirectoryEntry) {
187 }
else if (entry instanceof DocumentEntry) {
188 String jmpListFileName = entry.getName();
189 int fileSize = ((DocumentEntry) entry).getSize();
192 try (DocumentInputStream stream = fs.createDocumentInputStream(jmpListFileName)) {
193 byte[] buffer =
new byte[stream.available()];
196 JLnkParser lnkParser =
new JLnkParser(fs.createDocumentInputStream(jmpListFileName), fileSize);
197 JLNK lnk = lnkParser.parse();
198 lnkFileName = lnk.getBestName() +
".lnk";
199 File targetFile =
new File(moduleOutPath + File.separator + entry.getName() +
"-" + lnkFileName);
200 String relativePath = Case.getCurrentCase().getModuleOutputDirectoryRelativePath();
201 String derivedFileName = Case.getCurrentCase().getModuleOutputDirectoryRelativePath() + File.separator + derivedPath + File.separator + entry.getName() +
"-" + lnkFileName;
202 OutputStream outStream =
new FileOutputStream(targetFile);
203 outStream.write(buffer);
205 derivedFile = fileManager.addDerivedFile(lnkFileName, derivedFileName,
217 TskData.EncodingType.NONE);
218 derivedFiles.add(derivedFile);
220 }
catch (IOException | JLnkParserException ex) {
221 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);
222 }
catch (TskCoreException ex) {
223 logger.log(Level.WARNING, String.format(
"Error trying to add dervived file %s", lnkFileName), ex);
224 }
catch (IndexOutOfBoundsException ex) {
227 logger.log(Level.WARNING, String.format(
"Error parsing the the jumplist file %s", jumpListFile), ex);
237 }
catch (NotOLE2FileException | EmptyFileException ex1) {
238 logger.log(Level.WARNING, String.format(
"Error file not a valid OLE2 Document $s", jumpListFile));
239 }
catch (IOException ex) {
240 logger.log(Level.WARNING, String.format(
"Error lnk parsing the file to get recent files $s", jumpListFile), ex);