Initial commit of adding Jumplists

Initial commit of adding jumplists to recent activity.
This commit is contained in:
Mark McKinnon 2021-07-20 09:29:40 -04:00
parent b1b3c9058b
commit 4620403834
3 changed files with 237 additions and 14 deletions

View File

@ -5,15 +5,10 @@ ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for an
ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis.
ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s.
ChromeCacheExtractor.moduleName=ChromeCacheExtractor
# {0} - module name
# {1} - row number
# {2} - table length
# {3} - cache path
ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}
DataSourceUsage_AndroidMedia=Android Media Card
DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card
DataSourceUsage_FlashDrive=Flash Drive
# {0} - OS name
DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0})
DataSourceUsageAnalyzer.parentModuleName=Recent Activity
DefaultPriorityDomainCategorizer_searchEngineCategory=Search Engine
@ -28,7 +23,6 @@ ExtractEdge_process_errMsg_errGettingWebCacheFiles=Error trying to retrieving Ed
ExtractEdge_process_errMsg_spartanFail=Failure processing Microsoft Edge spartan.edb file
ExtractEdge_process_errMsg_unableFindESEViewer=Unable to find ESEDatabaseViewer
ExtractEdge_process_errMsg_webcacheFail=Failure processing Microsoft Edge WebCacheV01.dat file
# {0} - sub module name
ExtractIE_executePasco_errMsg_errorRunningPasco={0}: Error analyzing Internet Explorer web history
ExtractOs.androidOs.label=Android
ExtractOs.androidVolume.label=OS Drive (Android)
@ -61,7 +55,6 @@ ExtractOs.windowsVolume.label=OS Drive (Windows)
ExtractOs.yellowDogLinuxOs.label=Linux (Yellow Dog)
ExtractOs.yellowDogLinuxVolume.label=OS Drive (Linux Yellow Dog)
ExtractOS_progressMessage=Checking for OS
# {0} - sub module name
ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files
ExtractPrefetch_module_name=Windows Prefetch Extractor
ExtractRecycleBin_module_name=Recycle Bin
@ -88,6 +81,8 @@ ExtractZone_process_errMsg_find=A failure occured while searching for :Zone.Inde
ExtractZone_progress_Msg=Extracting :Zone.Identifer files
ExtractZone_Restricted=Restricted Sites Zone
ExtractZone_Trusted=Trusted Sites Zone
Jumplist_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis.
Jumplist_module_name=Windows Jumplist Extractor
OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
OpenIDE-Module-Name=RecentActivity
@ -157,19 +152,13 @@ Firefox.getDlV24.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{
Firefox.getDlV24.errMsg.errParsingArtifacts={0}: Error parsing {1} Firefox web download artifacts.
Progress_Message_Analyze_Registry=Analyzing Registry Files
Progress_Message_Analyze_Usage=Data Sources Usage Analysis
# {0} - browserName
Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}
# {0} - browserName
Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}
Progress_Message_Chrome_Cache=Chrome Cache
# {0} - browserName
Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}
# {0} - browserName
Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}
Progress_Message_Chrome_FormHistory=Chrome Form History
# {0} - browserName
Progress_Message_Chrome_History=Chrome History Browser {0}
# {0} - browserName
Progress_Message_Chrome_Logins=Chrome Logins Browser {0}
Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks
Progress_Message_Edge_Cookies=Microsoft Edge Cookies
@ -224,7 +213,6 @@ Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU
Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)
RegRipperFullNotFound=Full version RegRipper executable not found.
RegRipperNotFound=Autopsy RegRipper executable not found.
# {0} - file name
SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}.
SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine
SearchEngineURLQueryAnalyzer.engineName.none=NONE

View File

@ -0,0 +1,233 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.recentactivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.JLNK;
import org.sleuthkit.autopsy.coreutils.JLnkParser;
import org.sleuthkit.autopsy.coreutils.JLnkParserException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DerivedFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Extract the LNK files from the jumplists and save them to ModuleOutput\RecentActivity\Jumplists
* and then add them back into the case as a dervived file.
*/
final class ExtractJumpLists extends Extract {
private static final Logger logger = Logger.getLogger(ExtractJumpLists.class.getName());
private IngestJobContext context;
private static final String JUMPLIST_TSK_COMMENT = "Jumplist File";
private static final String RA_DIR_NAME = "RecentActivity"; //NON-NLS
private static final String MODULE_OUTPUT_DIR = "ModuleOutput"; //NON-NLS
private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY = "%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/";
private static final String JUMPLIST_DIR_NAME = "jumplists"; //NON-NLS
private static final String VERSION_NUMBER = "1.0.0"; //NON-NLS
private String moduleName;
private FileManager fileManager;
private final IngestServices services = IngestServices.getInstance();
@Messages({
"Jumplist_module_name=Windows Jumplist Extractor",
"Jumplist_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis."
})
ExtractJumpLists() {
super(Bundle.Jumplist_module_name());
}
@Override
void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
this.context = context;
moduleName = Bundle.Jumplist_module_name();
fileManager = currentCase.getServices().getFileManager();
long ingestJobId = context.getJobId();
List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId);
if (context.dataSourceIngestIsCancelled()) {
return;
}
List<AbstractFile> derivedFiles = new ArrayList<>();
String derivedPath = null;
String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), dataSource.getName() + "-" + JUMPLIST_DIR_NAME, ingestJobId);
for (AbstractFile jumplistFile : jumpListFiles) {
if (!jumplistFile.getName().toLowerCase().contains("-slack") && !jumplistFile.getName().equals("..") &&
!jumplistFile.getName().equals(".") && jumplistFile.getSize() > 0) {
String jlFile = Paths.get(baseRaTempPath, jumplistFile.getName()).toString();
String moduleOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME + File.separator + jumplistFile.getName();
derivedPath = RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME + File.separator + jumplistFile.getName();
File jlDir = new File(moduleOutPath);
if (jlDir.exists() == false) {
boolean dirMade = jlDir.mkdirs();
if (!dirMade) {
logger.log(Level.WARNING, "Error creating directory to store Jumplist LNK files %s", moduleOutPath); //NON-NLS
continue;
}
}
derivedFiles.addAll(extractLnkFiles(jlFile, moduleOutPath, jumplistFile, derivedPath));
}
}
// notify listeners of new files and schedule for analysis
progressBar.progress(String.format(Bundle.Jumplist_adding_extracted_files_msg(), derivedFiles.size()));
derivedFiles.forEach((derived) -> { services.fireModuleContentEvent(new ModuleContentEvent(derived)); });
context.addFilesToJob(derivedFiles);
}
/**
* Find jumplist and extract jumplist files to temp directory
*
* @return - list of jumplist abstractfiles or empty list
*/
private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId) {
List<AbstractFile> jumpListFiles = new ArrayList<>();;
List<AbstractFile> tempJumpListFiles = new ArrayList<>();;
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
try {
tempJumpListFiles = fileManager.findFiles(dataSource, "%", AUTOMATIC_DESTINATIONS_FILE_DIRECTORY); //NON-NLS
if (!tempJumpListFiles.isEmpty()) {
jumpListFiles.addAll(tempJumpListFiles);
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to find jumplist files.", ex); //NON-NLS
return jumpListFiles; // No need to continue
}
for (AbstractFile jumpListFile : jumpListFiles) {
if (context.dataSourceIngestIsCancelled()) {
return jumpListFiles;
}
if (!jumpListFile.getName().toLowerCase().contains("-slack") && !jumpListFile.getName().equals("..") &&
!jumpListFile.getName().equals(".") && jumpListFile.getSize() > 0) {
String fileName = jumpListFile.getName();
String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), dataSource.getName() + "-" + JUMPLIST_DIR_NAME, ingestJobId);
String jlFile = Paths.get(baseRaTempPath, fileName).toString();
try {
ContentUtils.writeToFile(jumpListFile, new File(jlFile));
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Unable to write %s to temp directory. File name: %s", fileName, jlFile), ex); //NON-NLS
}
}
}
return jumpListFiles;
}
/*
* Read each jumplist file and extract the lnk files to moduleoutput
*/
private List<DerivedFile> extractLnkFiles(String jumpListFile, String moduleOutPath, AbstractFile jumpListAbsFile, String derivedPath) {
List<DerivedFile> derivedFiles = new ArrayList<>();
DerivedFile derivedFile;
String lnkFileName = "";
try (POIFSFileSystem fs = new POIFSFileSystem(new File(jumpListFile))) {
DirectoryEntry root = fs.getRoot();
for (Entry entry : root) {
if (entry instanceof DirectoryEntry) {
//If this data structure needed to recurse this is where it would do it but jjumplists do not need to at this time
continue;
} else if (entry instanceof DocumentEntry) {
String jmpListFileName = entry.getName();
int fileSize = ((DocumentEntry) entry).getSize();
if (fileSize > 0) {
try (DocumentInputStream stream = fs.createDocumentInputStream(jmpListFileName)) {
byte[] buffer = new byte[stream.available()];
stream.read(buffer);
JLnkParser lnkParser = new JLnkParser(fs.createDocumentInputStream(jmpListFileName), fileSize);
JLNK lnk = lnkParser.parse();
lnkFileName = lnk.getBestName() + ".lnk";
File targetFile = new File(moduleOutPath + File.separator + entry.getName() + "-" + lnkFileName);
String derivedFileName = MODULE_OUTPUT_DIR + File.separator + derivedPath + File.separator + entry.getName() + "-" + lnkFileName;
OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer);
outStream.close();
derivedFile = fileManager.addDerivedFile(lnkFileName, derivedFileName,
fileSize,
0,
0,
0,
0, // TBD
true,
jumpListAbsFile,
"",
moduleName,
VERSION_NUMBER,
"",
TskData.EncodingType.NONE);
derivedFiles.add(derivedFile);
} catch (IOException | JLnkParserException e) {
logger.log(Level.WARNING, String.format("No such document, or the Entry represented by documentName is not a DocumentEntry link file is %s", lnkFileName), e); //NON-NLS
}
}
} else {
// currently, either an Entry is a DirectoryEntry or a DocumentEntry,
// but in the future, there may be other entry subinterfaces.
// The internal data structure certainly allows for a lot more entry types.
continue;
}
}
} catch (IOException | TskCoreException ex) {
logger.log(Level.WARNING, String.format("Error lnk parsing the file to get recent files $s", jumpListFile), ex); //NON-NLS
}
return derivedFiles;
}
}

View File

@ -84,8 +84,10 @@ public final class RAImageIngestModule implements DataSourceIngestModule {
Extract prefetch = new ExtractPrefetch();
Extract webAccountType = new ExtractWebAccountType();
Extract messageDomainType = new DomainCategoryRunner();
Extract jumpList = new ExtractJumpLists();
extractors.add(recycleBin);
extractors.add(jumpList);
extractors.add(recentDocuments);
extractors.add(registry); // needs to run before the DataSourceUsageAnalyzer
extractors.add(osExtract); // this needs to run before the DataSourceUsageAnalyzer