Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractJumpLists.java
Go to the documentation of this file.
1/*
2 *
3 * Autopsy Forensic Browser
4 *
5 * Copyright 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.FileOutputStream;
24import java.io.IOException;
25import java.io.OutputStream;
26import java.nio.file.Paths;
27import java.util.ArrayList;
28import java.util.List;
29import java.util.logging.Level;
30import org.apache.poi.EmptyFileException;
31import org.apache.poi.poifs.filesystem.DirectoryEntry;
32import org.apache.poi.poifs.filesystem.DocumentEntry;
33import org.apache.poi.poifs.filesystem.DocumentInputStream;
34import org.apache.poi.poifs.filesystem.Entry;
35import org.apache.poi.poifs.filesystem.NotOLE2FileException;
36import org.apache.poi.poifs.filesystem.POIFSFileSystem;
37import org.openide.util.NbBundle.Messages;
38import org.sleuthkit.autopsy.casemodule.Case;
39import org.sleuthkit.autopsy.casemodule.services.FileManager;
40import org.sleuthkit.autopsy.coreutils.JLNK;
41import org.sleuthkit.autopsy.coreutils.JLnkParser;
42import org.sleuthkit.autopsy.coreutils.JLnkParserException;
43import org.sleuthkit.autopsy.coreutils.Logger;
44import org.sleuthkit.autopsy.datamodel.ContentUtils;
45import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
46import org.sleuthkit.autopsy.ingest.IngestJobContext;
47import org.sleuthkit.autopsy.ingest.IngestServices;
48import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
49import org.sleuthkit.datamodel.AbstractFile;
50import org.sleuthkit.datamodel.Content;
51import org.sleuthkit.datamodel.DerivedFile;
52import org.sleuthkit.datamodel.TskCoreException;
53import org.sleuthkit.datamodel.TskData;
54
60final class ExtractJumpLists extends Extract {
61
62 private static final Logger logger = Logger.getLogger(ExtractJumpLists.class.getName());
63 private static final String RA_DIR_NAME = "RecentActivity"; //NON-NLS
64 private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY = "%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/";
65 private static final String JUMPLIST_DIR_NAME = "jumplists"; //NON-NLS
66 private static final String VERSION_NUMBER = "1.0.0"; //NON-NLS
67 private String moduleName;
68 private FileManager fileManager;
69 private final IngestServices services = IngestServices.getInstance();
70 private final IngestJobContext context;
71
72 @Messages({
73 "Jumplist_module_name=Windows Jumplist Analyzer",
74 "Jumplist_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis."
75 })
76 ExtractJumpLists(IngestJobContext context) {
77 super(Bundle.Jumplist_module_name(), context);
78 this.context = context;
79 }
80
81 @Override
82 void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
83 moduleName = Bundle.Jumplist_module_name();
84 fileManager = currentCase.getServices().getFileManager();
85 long ingestJobId = context.getJobId();
86
87 String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME, ingestJobId);
88 List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId, baseRaTempPath);
89 if (jumpListFiles.isEmpty()) {
90 return;
91 }
92
93 if (context.dataSourceIngestIsCancelled()) {
94 return;
95 }
96
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();
109 if (!dirMade) {
110 logger.log(Level.WARNING, "Error creating directory to store Jumplist LNK files %s", moduleOutPath); //NON-NLS
111 continue;
112 }
113 }
114 derivedFiles.addAll(extractLnkFiles(jlFile, moduleOutPath, jumplistFile, derivedPath));
115 }
116 }
117
118 // notify listeners of new files and schedule for analysis
119 progressBar.progress(String.format(Bundle.Jumplist_adding_extracted_files_msg(), derivedFiles.size()));
120 derivedFiles.forEach((derived) -> {
121 services.fireModuleContentEvent(new ModuleContentEvent(derived));
122 });
123 context.addFilesToJob(derivedFiles);
124
125 }
126
132 private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId, String baseRaTempPath) {
133 List<AbstractFile> jumpListFiles = new ArrayList<>();;
134 List<AbstractFile> tempJumpListFiles = new ArrayList<>();;
135
136 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
137
138 try {
139 tempJumpListFiles = fileManager.findFiles(dataSource, "%", AUTOMATIC_DESTINATIONS_FILE_DIRECTORY); //NON-NLS
140 if (!tempJumpListFiles.isEmpty()) {
141 jumpListFiles.addAll(tempJumpListFiles);
142 }
143 } catch (TskCoreException ex) {
144 logger.log(Level.WARNING, "Unable to find jumplist files.", ex); //NON-NLS
145 return jumpListFiles; // No need to continue
146 }
147
148 for (AbstractFile jumpListFile : jumpListFiles) {
149
150 if (context.dataSourceIngestIsCancelled()) {
151 return jumpListFiles;
152 }
153
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();
158 try {
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); //NON-NLS
162 }
163 }
164 }
165
166 return jumpListFiles;
167
168 }
169
170 /*
171 * Read each jumplist file and extract the lnk files to moduleoutput
172 */
173 private List<DerivedFile> extractLnkFiles(String jumpListFile, String moduleOutPath, AbstractFile jumpListAbsFile, String derivedPath) {
174
175 List<DerivedFile> derivedFiles = new ArrayList<>();
176 DerivedFile derivedFile;
177 String lnkFileName = "";
178
179 try (POIFSFileSystem fs = new POIFSFileSystem(new File(jumpListFile))) {
180 DirectoryEntry root = fs.getRoot();
181 for (Entry entry : root) {
182 if (entry instanceof DirectoryEntry) {
183 //If this data structure needed to recurse this is where it would do it but jumplists do not need to at this time
184 continue;
185 } else if (entry instanceof DocumentEntry) {
186 String jmpListFileName = entry.getName();
187 int fileSize = ((DocumentEntry) entry).getSize();
188
189 if (fileSize > 0) {
190 try (DocumentInputStream stream = fs.createDocumentInputStream(jmpListFileName)) {
191 byte[] buffer = new byte[stream.available()];
192 stream.read(buffer);
193
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);
202 outStream.close();
203 derivedFile = fileManager.addDerivedFile(lnkFileName, derivedFileName,
204 fileSize,
205 0,
206 0,
207 0,
208 0, // TBD
209 true,
210 jumpListAbsFile,
211 "",
212 moduleName,
213 VERSION_NUMBER,
214 "",
215 TskData.EncodingType.NONE);
216 derivedFiles.add(derivedFile);
217
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); //NON-NLS
220 } catch (TskCoreException ex) {
221 logger.log(Level.WARNING, String.format("Error trying to add dervived file %s", lnkFileName), ex); //NON-NLS
222 } catch (IndexOutOfBoundsException ex) {
223 // There is some type of corruption within the file that cannot be handled, ignoring it and moving on to next file
224 // in the jumplist.
225 logger.log(Level.WARNING, String.format("Error parsing the the jumplist file %s", jumpListFile), ex); //NON-NLS
226 }
227 }
228 } else {
229 // currently, either an Entry is a DirectoryEntry or a DocumentEntry,
230 // but in the future, there may be other entry subinterfaces.
231 // The internal data structure certainly allows for a lot more entry types.
232 continue;
233 }
234 }
235 } catch (NotOLE2FileException | EmptyFileException ex1) {
236 logger.log(Level.WARNING, String.format("Error file not a valid OLE2 Document $s", jumpListFile)); //NON-NLS
237 } catch (IOException ex) {
238 logger.log(Level.WARNING, String.format("Error lnk parsing the file to get recent files $s", jumpListFile), ex); //NON-NLS
239 }
240
241 return derivedFiles;
242
243 }
244
245}

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