23 package org.sleuthkit.autopsy.recentactivity;
 
   25 import java.io.BufferedReader;
 
   26 import org.openide.util.NbBundle;
 
   30 import java.io.FileInputStream;
 
   31 import java.io.FileNotFoundException;
 
   32 import java.io.IOException;
 
   33 import java.io.InputStreamReader;
 
   34 import java.nio.file.Paths;
 
   35 import java.text.ParseException;
 
   36 import java.text.SimpleDateFormat;
 
   37 import java.util.ArrayList;
 
   38 import java.util.List;
 
   39 import java.util.logging.Level;
 
   41 import java.util.Collection;
 
   42 import java.util.Scanner;
 
   43 import java.util.stream.Collectors;
 
   44 import org.openide.modules.InstalledFileLocator;
 
   45 import org.openide.util.NbBundle.Messages;
 
   50 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
 
   52 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
 
   59 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY;
 
   67 class ExtractIE 
extends Extract {
 
   69     private static final Logger logger = Logger.getLogger(ExtractIE.class.getName());
 
   70     private String PASCO_LIB_PATH;
 
   71     private final String JAVA_PATH;
 
   72     private static final String RESOURCE_URL_PREFIX = 
"res://";
 
   73     private static final SimpleDateFormat dateFormatter = 
new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
 
   74     private Content dataSource;
 
   75     private IngestJobContext context;
 
   78         "Progress_Message_IE_History=IE History",
 
   79         "Progress_Message_IE_Bookmarks=IE Bookmarks",
 
   80         "Progress_Message_IE_Cookies=IE Cookies",
 
   81         "Progress_Message_IE_Downloads=IE Downloads",
 
   82         "Progress_Message_IE_FormHistory=IE Form History",
 
   83         "Progress_Message_IE_AutoFill=IE Auto Fill",
 
   84         "Progress_Message_IE_Logins=IE Logins",})
 
   87         super(NbBundle.getMessage(ExtractIE.class, 
"ExtractIE.moduleName.text"));
 
   88         JAVA_PATH = PlatformUtil.getJavaPath();
 
   92     public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
 
   93         String moduleTempDir = RAImageIngestModule.getRATempPath(getCurrentCase(), 
"IE", context.getJobId());
 
   94         String moduleTempResultsDir = Paths.get(moduleTempDir, 
"results").toString();
 
   96         this.dataSource = dataSource;
 
   97         this.context = context;
 
  100         progressBar.progress(Bundle.Progress_Message_IE_Bookmarks());
 
  103         if (context.dataSourceIngestIsCancelled()) {
 
  107         progressBar.progress(Bundle.Progress_Message_IE_Cookies());
 
  110         if (context.dataSourceIngestIsCancelled()) {
 
  114         progressBar.progress(Bundle.Progress_Message_IE_History());
 
  115         this.getHistory(moduleTempDir, moduleTempResultsDir);
 
  121     private void getBookmark() {
 
  123         List<AbstractFile> favoritesFiles;
 
  125             favoritesFiles = fileManager.
findFiles(dataSource, 
"%.url", 
"Favorites"); 
 
  126         } 
catch (TskCoreException ex) {
 
  127             logger.log(Level.WARNING, 
"Error fetching 'url' files for Internet Explorer bookmarks.", ex); 
 
  128             this.addErrorMessage(
 
  129                     NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getBookmark.errMsg.errGettingBookmarks",
 
  134         if (favoritesFiles.isEmpty()) {
 
  135             logger.log(Level.INFO, 
"Didn't find any IE bookmark files."); 
 
  140         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  141         for (AbstractFile fav : favoritesFiles) {
 
  142             if (fav.getSize() == 0) {
 
  146             if (context.dataSourceIngestIsCancelled()) {
 
  150             String url = getURLFromIEBookmarkFile(fav);
 
  152             String name = fav.getName();
 
  153             Long datetime = fav.getCrtime();
 
  154             String Tempdate = datetime.toString();
 
  155             datetime = Long.valueOf(Tempdate);
 
  156             String domain = extractDomain(url);
 
  158             Collection<BlackboardAttribute> bbattributes = 
new ArrayList<>();
 
  159             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
 
  160                     RecentActivityExtracterModuleFactory.getModuleName(), url));
 
  161             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
 
  162                     RecentActivityExtracterModuleFactory.getModuleName(), name));
 
  163             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
 
  164                     RecentActivityExtracterModuleFactory.getModuleName(), datetime));
 
  165             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
 
  166                     RecentActivityExtracterModuleFactory.getModuleName(),
 
  167                     NbBundle.getMessage(this.getClass(), 
"ExtractIE.moduleName.text")));
 
  168             if (domain != null && domain.isEmpty() == 
false) {
 
  169                 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
 
  170                         RecentActivityExtracterModuleFactory.getModuleName(), domain));
 
  174                 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, fav, bbattributes));
 
  175             } 
catch (TskCoreException ex) {
 
  176                 logger.log(Level.SEVERE, String.format(
"Failed to create %s for file %d",ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getDisplayName(), fav.getId() ), ex);
 
  180         if(!context.dataSourceIngestIsCancelled()) {
 
  181             postArtifacts(bbartifacts);
 
  185     private String getURLFromIEBookmarkFile(AbstractFile fav) {
 
  186         BufferedReader reader = 
new BufferedReader(
new InputStreamReader(
new ReadContentInputStream(fav)));
 
  187         String line, url = 
"";
 
  189             line = reader.readLine();
 
  190             while (null != line) {
 
  193                 if (line.startsWith(
"URL")) { 
 
  194                     url = line.substring(line.indexOf(
"=") + 1);
 
  197                 line = reader.readLine();
 
  199         } 
catch (IOException ex) {
 
  200             logger.log(Level.WARNING, 
"Failed to read from content: " + fav.getName(), ex); 
 
  201             this.addErrorMessage(
 
  202                     NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getURLFromIEBmkFile.errMsg", this.getName(),
 
  204         } 
catch (IndexOutOfBoundsException ex) {
 
  205             logger.log(Level.WARNING, 
"Failed while getting URL of IE bookmark. Unexpected format of the bookmark file: " + fav.getName(), ex); 
 
  206             this.addErrorMessage(
 
  207                     NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getURLFromIEBmkFile.errMsg2", this.getName(),
 
  212             } 
catch (IOException ex) {
 
  213                 logger.log(Level.WARNING, 
"Failed to close reader.", ex); 
 
  223     private void getCookie() {
 
  225         List<AbstractFile> cookiesFiles;
 
  227             cookiesFiles = fileManager.
findFiles(dataSource, 
"%.txt", 
"Cookies"); 
 
  228         } 
catch (TskCoreException ex) {
 
  229             logger.log(Level.WARNING, 
"Error getting cookie files for IE"); 
 
  230             this.addErrorMessage(
 
  231                     NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getCookie.errMsg.errGettingFile", this.getName()));
 
  235         if (cookiesFiles.isEmpty()) {
 
  236             logger.log(Level.INFO, 
"Didn't find any IE cookies files."); 
 
  241         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  242         for (AbstractFile cookiesFile : cookiesFiles) {
 
  243             if (context.dataSourceIngestIsCancelled()) {
 
  246             if (cookiesFile.getSize() == 0) {
 
  250             byte[] t = 
new byte[(int) cookiesFile.getSize()];
 
  252                 final int bytesRead = cookiesFile.read(t, 0, cookiesFile.getSize());
 
  253             } 
catch (TskCoreException ex) {
 
  254                 logger.log(Level.WARNING, 
"Error reading bytes of Internet Explorer cookie.", ex); 
 
  255                 this.addErrorMessage(
 
  256                         NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getCookie.errMsg.errReadingIECookie",
 
  257                                 this.getName(), cookiesFile.getName()));
 
  260             String cookieString = 
new String(t);
 
  261             String[] values = cookieString.split(
"\n");
 
  262             String url = values.length > 2 ? values[2] : 
"";
 
  263             String value = values.length > 1 ? values[1] : 
"";
 
  264             String name = values.length > 0 ? values[0] : 
"";
 
  265             Long datetime = cookiesFile.getCrtime();
 
  266             String tempDate = datetime.toString();
 
  267             datetime = Long.valueOf(tempDate);
 
  268             String domain = extractDomain(url);
 
  270             Collection<BlackboardAttribute> bbattributes = 
new ArrayList<>();
 
  271             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
 
  272                     RecentActivityExtracterModuleFactory.getModuleName(), url));
 
  273             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
 
  274                     RecentActivityExtracterModuleFactory.getModuleName(), datetime));
 
  275             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
 
  276                     RecentActivityExtracterModuleFactory.getModuleName(), (name != null) ? name : 
""));
 
  277             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
 
  278                     RecentActivityExtracterModuleFactory.getModuleName(), value));
 
  279             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
 
  280                     RecentActivityExtracterModuleFactory.getModuleName(),
 
  281                     NbBundle.getMessage(this.getClass(), 
"ExtractIE.moduleName.text")));
 
  282             if (domain != null && domain.isEmpty() == 
false) {
 
  283                 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
 
  284                         RecentActivityExtracterModuleFactory.getModuleName(), domain));
 
  288                 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes));
 
  289             } 
catch (TskCoreException ex) {
 
  290                 logger.log(Level.SEVERE, String.format(
"Failed to create %s for file %d",ARTIFACT_TYPE.TSK_WEB_COOKIE.getDisplayName(), cookiesFile.getId() ), ex);
 
  294         if(!context.dataSourceIngestIsCancelled()) {
 
  295             postArtifacts(bbartifacts);
 
  304     private void getHistory(String moduleTempDir, String moduleTempResultsDir) {
 
  305         logger.log(Level.INFO, 
"Pasco results path: {0}", moduleTempResultsDir); 
 
  306         boolean foundHistory = 
false;
 
  308         final File pascoRoot = InstalledFileLocator.getDefault().locate(
"pasco2", ExtractIE.class.getPackage().getName(), 
false); 
 
  309         if (pascoRoot == null) {
 
  310             this.addErrorMessage(
 
  311                     NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getHistory.errMsg.unableToGetHist", this.getName()));
 
  312             logger.log(Level.SEVERE, 
"Error finding pasco program "); 
 
  316         final String pascoHome = pascoRoot.getAbsolutePath();
 
  317         logger.log(Level.INFO, 
"Pasco2 home: {0}", pascoHome); 
 
  319         PASCO_LIB_PATH = pascoHome + File.separator + 
"pasco2.jar" + File.pathSeparator 
 
  320                 + pascoHome + File.separator + 
"*";
 
  322         File resultsDir = 
new File(moduleTempResultsDir);
 
  326         FileManager fileManager = currentCase.getServices().getFileManager();
 
  327         List<AbstractFile> indexFiles;
 
  329             indexFiles = fileManager.findFiles(dataSource, 
"index.dat"); 
 
  330         } 
catch (TskCoreException ex) {
 
  331             this.addErrorMessage(NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getHistory.errMsg.errGettingHistFiles",
 
  333             logger.log(Level.WARNING, 
"Error fetching 'index.data' files for Internet Explorer history."); 
 
  337         if (indexFiles.isEmpty()) {
 
  338             String msg = NbBundle.getMessage(this.getClass(), 
"ExtractIE.getHistory.errMsg.noHistFiles");
 
  339             logger.log(Level.INFO, msg);
 
  344         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  346         String indexFileName;
 
  347         for (AbstractFile indexFile : indexFiles) {
 
  354             indexFileName = 
"index" + Integer.toString((
int) indexFile.getId()) + 
".dat"; 
 
  356             temps = moduleTempDir + File.separator + indexFileName; 
 
  357             File datFile = 
new File(temps);
 
  358             if (context.dataSourceIngestIsCancelled()) {
 
  362                 ContentUtils.writeToFile(indexFile, datFile, context::dataSourceIngestIsCancelled);
 
  363             } 
catch (IOException e) {
 
  364                 logger.log(Level.WARNING, 
"Error while trying to write index.dat file " + datFile.getAbsolutePath(), e); 
 
  365                 this.addErrorMessage(
 
  366                         NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getHistory.errMsg.errWriteFile", this.getName(),
 
  367                                 datFile.getAbsolutePath()));
 
  371             String filename = 
"pasco2Result." + indexFile.getId() + 
".txt"; 
 
  372             boolean bPascProcSuccess = executePasco(temps, filename, moduleTempResultsDir);
 
  373             if (context.dataSourceIngestIsCancelled()) {
 
  379             if (bPascProcSuccess) {
 
  381                 bbartifacts.addAll(parsePascoOutput(indexFile, filename, moduleTempResultsDir).stream()
 
  382                         .filter(bbart -> bbart.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID())
 
  383                         .collect(Collectors.toList()));
 
  384                 if (context.dataSourceIngestIsCancelled()) {
 
  392                 logger.log(Level.WARNING, 
"pasco execution failed on: {0}", filename); 
 
  393                 this.addErrorMessage(
 
  394                         NbBundle.getMessage(
this.getClass(), 
"ExtractIE.getHistory.errMsg.errProcHist", this.getName()));
 
  398         if(!context.dataSourceIngestIsCancelled()) {
 
  399             postArtifacts(bbartifacts);
 
  413         "# {0} - sub module name", 
 
  414         "ExtractIE_executePasco_errMsg_errorRunningPasco={0}: Error analyzing Internet Explorer web history",
 
  416     private boolean executePasco(String indexFilePath, String outputFileName, String moduleTempResultsDir) {
 
  417         boolean success = 
true;
 
  419             final String outputFileFullPath = moduleTempResultsDir + File.separator + outputFileName;
 
  420             final String errFileFullPath = moduleTempResultsDir + File.separator + outputFileName + 
".err"; 
 
  421             logger.log(Level.INFO, 
"Writing pasco results to: {0}", outputFileFullPath); 
 
  422             List<String> commandLine = 
new ArrayList<>();
 
  423             commandLine.add(JAVA_PATH);
 
  424             commandLine.add(
"-cp"); 
 
  425             commandLine.add(PASCO_LIB_PATH);
 
  426             commandLine.add(
"isi.pasco2.Main"); 
 
  427             commandLine.add(
"-T"); 
 
  428             commandLine.add(
"history");  
 
  429             commandLine.add(indexFilePath);
 
  430             ProcessBuilder processBuilder = 
new ProcessBuilder(commandLine);
 
  431             processBuilder.redirectOutput(
new File(outputFileFullPath));
 
  432             processBuilder.redirectError(
new File(errFileFullPath));
 
  442             ExecUtil.execute(processBuilder, 
new DataSourceIngestModuleProcessTerminator(context, 
true));
 
  444         } 
catch (IOException ex) {
 
  445             logger.log(Level.SEVERE, 
"Error executing Pasco to process Internet Explorer web history", ex); 
 
  446             addErrorMessage(Bundle.ExtractIE_executePasco_errMsg_errorRunningPasco(getName()));            
 
  462     private Collection<BlackboardArtifact> parsePascoOutput(AbstractFile origFile, String pascoOutputFileName, String moduleTempResultsDir) {
 
  464         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  465         String fnAbs = moduleTempResultsDir + File.separator + pascoOutputFileName;
 
  467         File file = 
new File(fnAbs);
 
  468         if (file.exists() == 
false) {
 
  469             this.addErrorMessage(
 
  470                     NbBundle.getMessage(
this.getClass(), 
"ExtractIE.parsePascoOutput.errMsg.notFound", this.getName(),
 
  472             logger.log(Level.WARNING, 
"Pasco Output not found: {0}", file.getPath()); 
 
  478         if (file.length() == 0) {
 
  484             fileScanner = 
new Scanner(
new FileInputStream(file.toString()));
 
  485         } 
catch (FileNotFoundException ex) {
 
  486             this.addErrorMessage(
 
  487                     NbBundle.getMessage(
this.getClass(), 
"ExtractIE.parsePascoOutput.errMsg.errParsing", this.getName(),
 
  489             logger.log(Level.WARNING, 
"Unable to find the Pasco file at " + file.getPath(), ex); 
 
  492         while (fileScanner.hasNext()) {
 
  494             if (context.dataSourceIngestIsCancelled()) {
 
  498             String line = fileScanner.nextLine();
 
  499             if (!line.startsWith(
"URL")) {   
 
  503             String[] lineBuff = line.split(
"\\t"); 
 
  505             if (lineBuff.length < 4) {
 
  506                 logger.log(Level.INFO, 
"Found unrecognized IE history format."); 
 
  510             String actime = lineBuff[3];
 
  511             Long ftime = (long) 0;
 
  513             String realurl = null;
 
  520             if (lineBuff[1].contains(
"@")) {
 
  521                 String url[] = lineBuff[1].split(
"@", 2);
 
  526                 domain = extractDomain(url[0]);
 
  528                 if (domain != null && domain.isEmpty() == 
false) {
 
  532                     realurl = lineBuff[1].trim();
 
  539                     user = user.replace(
"Visited:", 
""); 
 
  540                     user = user.replace(
":Host:", 
""); 
 
  541                     user = user.replaceAll(
"(:)(.*?)(:)", 
"");
 
  544                     realurl = realurl.replace(
"Visited:", 
""); 
 
  545                     realurl = realurl.replaceAll(
":(.*?):", 
"");
 
  546                     realurl = realurl.replace(
":Host:", 
""); 
 
  547                     realurl = realurl.trim();
 
  548                     domain = extractDomain(realurl);
 
  554                 realurl = lineBuff[1].trim();
 
  555                 domain = extractDomain(realurl);
 
  558             if (!actime.isEmpty()) {
 
  560                     Long epochtime = dateFormatter.parse(actime).getTime();
 
  561                     ftime = epochtime / 1000;
 
  562                 } 
catch (ParseException e) {
 
  563                     this.addErrorMessage(
 
  564                             NbBundle.getMessage(
this.getClass(), 
"ExtractIE.parsePascoOutput.errMsg.errParsingEntry",
 
  566                     logger.log(Level.WARNING, String.format(
"Error parsing Pasco results, may have partial processing of corrupt file (id=%d)", origFile.getId()), e); 
 
  570             Collection<BlackboardAttribute> bbattributes = 
new ArrayList<>();
 
  571             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
 
  572                     RecentActivityExtracterModuleFactory.getModuleName(), realurl));
 
  575             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
 
  576                     RecentActivityExtracterModuleFactory.getModuleName(), ftime));
 
  577             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
 
  578                     RecentActivityExtracterModuleFactory.getModuleName(), 
""));
 
  580             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
 
  581                     RecentActivityExtracterModuleFactory.getModuleName(),
 
  582                     NbBundle.getMessage(this.getClass(),
 
  583                             "ExtractIE.moduleName.text")));
 
  584             if (domain != null && domain.isEmpty() == 
false) {
 
  585                 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
 
  586                         RecentActivityExtracterModuleFactory.getModuleName(), domain));
 
  588             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
 
  589                     RecentActivityExtracterModuleFactory.getModuleName(), user));
 
  592                 bbartifacts.add(createArtifactWithAttributes(TSK_WEB_HISTORY, origFile, bbattributes));
 
  593             } 
catch (TskCoreException ex) {
 
  594                 logger.log(Level.SEVERE, String.format(
"Failed to create %s for file %d",ARTIFACT_TYPE.TSK_WEB_HISTORY.getDisplayName(), origFile.getId() ), ex);
 
  609     private String extractDomain(String url) {
 
  610         if (url == null || url.isEmpty()) {
 
  614         if (url.toLowerCase().startsWith(RESOURCE_URL_PREFIX)) {
 
  621         return NetworkUtils.extractDomain(url);
 
List< AbstractFile > findFiles(String fileName)