Autopsy  4.19.0
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  */
20 package org.sleuthkit.autopsy.recentactivity;
21 
22 import java.io.File;
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;
47 import org.sleuthkit.datamodel.AbstractFile;
48 import org.sleuthkit.datamodel.Content;
49 import org.sleuthkit.datamodel.DerivedFile;
50 import org.sleuthkit.datamodel.TskCoreException;
51 import org.sleuthkit.datamodel.TskData;
52 
57 final class ExtractJumpLists extends Extract {
58 
59  private static final Logger logger = Logger.getLogger(ExtractJumpLists.class.getName());
60 
61  private IngestJobContext context;
62 
63  private static final String JUMPLIST_TSK_COMMENT = "Jumplist File";
64  private static final String RA_DIR_NAME = "RecentActivity"; //NON-NLS
65  private static final String MODULE_OUTPUT_DIR = "ModuleOutput"; //NON-NLS
66  private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY = "%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/";
67  private static final String JUMPLIST_DIR_NAME = "jumplists"; //NON-NLS
68  private static final String VERSION_NUMBER = "1.0.0"; //NON-NLS
69  private String moduleName;
70  private FileManager fileManager;
71  private final IngestServices services = IngestServices.getInstance();
72 
73  @Messages({
74  "Jumplist_module_name=Windows Jumplist Extractor",
75  "Jumplist_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis."
76  })
77  ExtractJumpLists() {
78  super(Bundle.Jumplist_module_name());
79  }
80 
81  @Override
82  void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
83 
84  this.context = context;
85  moduleName = Bundle.Jumplist_module_name();
86  fileManager = currentCase.getServices().getFileManager();
87  long ingestJobId = context.getJobId();
88 
89  List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId);
90 
91  if (jumpListFiles.isEmpty()) {
92  return;
93  }
94 
95  if (context.dataSourceIngestIsCancelled()) {
96  return;
97  }
98 
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();
111  if (!dirMade) {
112  logger.log(Level.WARNING, "Error creating directory to store Jumplist LNK files %s", moduleOutPath); //NON-NLS
113  continue;
114  }
115  }
116  derivedFiles.addAll(extractLnkFiles(jlFile, moduleOutPath, jumplistFile, derivedPath));
117  }
118  }
119 
120  // notify listeners of new files and schedule for analysis
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);
124 
125  }
126 
132  private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId) {
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 baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME+ "_" + dataSource.getId(), ingestJobId);
158  String jlFile = Paths.get(baseRaTempPath, fileName).toString();
159  try {
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); //NON-NLS
163  }
164  }
165  }
166 
167  return jumpListFiles;
168 
169  }
170 
171  /*
172  * Read each jumplist file and extract the lnk files to moduleoutput
173  */
174  private List<DerivedFile> extractLnkFiles(String jumpListFile, String moduleOutPath, AbstractFile jumpListAbsFile, String derivedPath) {
175 
176  List<DerivedFile> derivedFiles = new ArrayList<>();
177  DerivedFile derivedFile;
178  String lnkFileName = "";
179 
180  try (POIFSFileSystem fs = new POIFSFileSystem(new File(jumpListFile))) {
181  DirectoryEntry root = fs.getRoot();
182  for (Entry entry : root) {
183  if (entry instanceof DirectoryEntry) {
184  //If this data structure needed to recurse this is where it would do it but jumplists do not need to at this time
185  continue;
186  } else if (entry instanceof DocumentEntry) {
187  String jmpListFileName = entry.getName();
188  int fileSize = ((DocumentEntry) entry).getSize();
189 
190  if (fileSize > 0) {
191  try (DocumentInputStream stream = fs.createDocumentInputStream(jmpListFileName)) {
192  byte[] buffer = new byte[stream.available()];
193  stream.read(buffer);
194 
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);
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 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); //NON-NLS
220  }
221  }
222  } else {
223  // currently, either an Entry is a DirectoryEntry or a DocumentEntry,
224  // but in the future, there may be other entry subinterfaces.
225  // The internal data structure certainly allows for a lot more entry types.
226  continue;
227  }
228  }
229  } catch (IOException | TskCoreException ex) {
230  logger.log(Level.WARNING, String.format("Error lnk parsing the file to get recent files $s", jumpListFile), ex); //NON-NLS
231  }
232 
233  return derivedFiles;
234 
235  }
236 
237 }

Copyright © 2012-2021 Basis Technology. Generated on: Fri Aug 6 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.