19 package org.sleuthkit.autopsy.modules.plaso;
 
   21 import java.io.BufferedReader;
 
   22 import java.io.BufferedWriter;
 
   24 import java.io.FileNotFoundException;
 
   25 import java.io.IOException;
 
   26 import java.io.InputStreamReader;
 
   27 import java.nio.file.Files;
 
   28 import java.nio.file.Path;
 
   29 import java.nio.file.Paths;
 
   30 import java.sql.ResultSet;
 
   31 import java.sql.SQLException;
 
   32 import java.text.SimpleDateFormat;
 
   33 import java.util.Arrays;
 
   34 import java.util.Collection;
 
   35 import java.util.List;
 
   36 import java.util.Locale;
 
   37 import static java.util.Objects.nonNull;
 
   38 import java.util.concurrent.TimeUnit;
 
   39 import java.util.logging.Level;
 
   40 import java.util.stream.Collectors;
 
   41 import org.openide.modules.InstalledFileLocator;
 
   42 import org.openide.util.Cancellable;
 
   43 import org.openide.util.NbBundle;
 
   59 import org.
sleuthkit.datamodel.Blackboard.BlackboardException;
 
   61 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
 
   63 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
 
   64 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
 
   65 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TL_EVENT_TYPE;
 
   79     private static final String 
PLASO = 
"plaso"; 
 
   80     private static final String 
PLASO64 = 
"plaso-20180818-amd64";
 
   81     private static final String 
PLASO32 = 
"plaso-20180818-win32";
 
   84     private static final String 
COOKIE = 
"cookie";
 
  105         "PlasoIngestModule.executable.not.found=Plaso Executable Not Found.",
 
  106         "PlasoIngestModule.requires.windows=Plaso module requires windows."})
 
  118         } 
catch (FileNotFoundException exception) {
 
  119             logger.log(Level.WARNING, 
"Plaso executable not found.", exception); 
 
  126         "PlasoIngestModule.error.running.log2timeline=Error running log2timeline, see log file.",
 
  127         "PlasoIngestModule.error.running.psort=Error running Psort, see log file.",
 
  128         "PlasoIngestModule.error.creating.output.dir=Error creating Plaso module output directory.",
 
  129         "PlasoIngestModule.starting.log2timeline=Starting Log2timeline",
 
  130         "PlasoIngestModule.running.psort=Running Psort",
 
  131         "PlasoIngestModule.log2timeline.cancelled=Log2timeline run was canceled",
 
  132         "PlasoIngestModule.psort.cancelled=psort run was canceled",
 
  133         "PlasoIngestModule.bad.imageFile=Cannot find image file name and path",
 
  134         "PlasoIngestModule.completed=Plaso Processing Completed",
 
  135         "PlasoIngestModule.has.run=Plaso",
 
  136         "PlasoIngestModule.psort.fail=Plaso returned an error when sorting events.  Results are not complete.",
 
  137         "PlasoIngestModule.dataSource.not.an.image=Skipping non-disk image datasource"})
 
  141         if (!(dataSource instanceof Image)) {
 
  143                     Bundle.PlasoIngestModule_has_run(),
 
  144                     Bundle.PlasoIngestModule_dataSource_not_an_image());
 
  148             image = (Image) dataSource;
 
  154             String currentTime = 
new SimpleDateFormat(
"yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());
 
  157                 Files.createDirectories(moduleOutputPath);
 
  158             } 
catch (IOException ex) {
 
  159                 logger.log(Level.SEVERE, 
"Error creating Plaso module output directory.", ex); 
 
  164             logger.log(Level.INFO, 
"Starting Plaso Run.");
 
  165             statusHelper.
progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0);
 
  168                 Process log2TimeLineProcess = log2TimeLineCommand.start();
 
  169                 try (BufferedReader log2TimeLineOutpout = 
new BufferedReader(
new InputStreamReader(log2TimeLineProcess.getInputStream()))) {
 
  171                     new Thread(statusReader, 
"log2timeline status reader").start();  
 
  177                     logger.log(Level.INFO, 
"Log2timeline run was canceled"); 
 
  180                 if (Files.notExists(moduleOutputPath.resolve(PLASO))) {
 
  181                     logger.log(Level.WARNING, 
"Error running log2timeline: there was no storage file."); 
 
  186                 statusHelper.
progress(Bundle.PlasoIngestModule_running_psort(), 33);
 
  190                     logger.log(Level.SEVERE, String.format(
"Error running Psort, error code returned %d", result)); 
 
  196                     logger.log(Level.INFO, 
"psort run was canceled"); 
 
  199                 Path plasoFile = moduleOutputPath.resolve(
"plasodb.db3");  
 
  200                 if (Files.notExists(plasoFile)) {
 
  201                     logger.log(Level.SEVERE, 
"Error running Psort: there was no sqlite db file."); 
 
  208             } 
catch (IOException ex) {
 
  209                 logger.log(Level.SEVERE, 
"Error running Plaso.", ex);
 
  214                     Bundle.PlasoIngestModule_has_run(),
 
  215                     Bundle.PlasoIngestModule_completed());
 
  223         String parsersString = settings.getParsers().entrySet().stream()
 
  224                 .filter(entry -> entry.getValue() == 
false)
 
  225                 .map(entry -> 
"!" + entry.getKey()) 
 
  226                 .collect(Collectors.joining(
","));
 
  229                 "\"" + log2TimeLineExecutable + 
"\"", 
 
  230                 "--vss-stores", 
"all", 
 
  231                 "-z", image.getTimeZone(), 
 
  232                 "--partitions", 
"all", 
 
  233                 "--hasher_file_size_limit", 
"1", 
 
  235                 "--parsers", 
"\"" + parsersString + 
"\"",
 
  236                 "--no_dependencies_check", 
 
  237                 "--workers", String.valueOf(LOG2TIMELINE_WORKERS),
 
  238                 moduleOutputPath.resolve(PLASO).toString(),
 
  241         processBuilder.redirectError(moduleOutputPath.resolve(
"log2timeline_err.txt").toFile());  
 
  242         return processBuilder;
 
  246         ProcessBuilder processBuilder = 
new ProcessBuilder(commandLine);
 
  251         processBuilder.environment().put(
"__COMPAT_LAYER", 
"RunAsInvoker"); 
 
  252         return processBuilder;
 
  257                 "\"" + psortExecutable + 
"\"", 
 
  258                 "-o", 
"4n6time_sqlite", 
 
  259                 "-w", moduleOutputPath.resolve(
"plasodb.db3").toString(), 
 
  260                 moduleOutputPath.resolve(PLASO).toString()
 
  263         processBuilder.redirectOutput(moduleOutputPath.resolve(
"psort_output.txt").toFile()); 
 
  264         processBuilder.redirectError(moduleOutputPath.resolve(
"psort_err.txt").toFile());  
 
  265         return processBuilder;
 
  270         String executableToFindName = Paths.get(PLASO, architectureFolder, executableName).toString();
 
  272         File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, 
PlasoIngestModule.class.getPackage().getName(), 
false);
 
  273         if (null == exeFile || exeFile.canExecute() == 
false) {
 
  274             throw new FileNotFoundException(executableName + 
" executable not found.");
 
  280         "PlasoIngestModule.exception.posting.artifact=Exception Posting artifact.",
 
  281         "PlasoIngestModule.event.datetime=Event Date Time",
 
  282         "PlasoIngestModule.event.description=Event Description",
 
  283         "PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation ",
 
  284         "# {0} - file that events are from",
 
  285         "PlasoIngestModule.artifact.progress=Adding events to case: {0}",
 
  286         "PlasoIngestModule.info.empty.database=Plaso database was empty.",})
 
  290         String sqlStatement = 
"SELECT substr(filename,1) AS  filename, " 
  291                 + 
"   strftime('%s', datetime) AS epoch_date, " 
  296                 + 
" FROM log2timeline " 
  297                 + 
" WHERE source NOT IN ('FILE', " 
  299                 + 
"   AND sourcetype NOT IN ('UNKNOWN', " 
  300                 + 
"                          'PE Import Time');"; 
 
  303                 ResultSet resultSet = tempdbconnect.
executeQry(sqlStatement)) {
 
  305             boolean dbHasData = 
false;
 
  307             while (resultSet.next()) {
 
  311                     logger.log(Level.INFO, 
"Cancelled Plaso Artifact Creation."); 
 
  315                 String currentFileName = resultSet.getString(
"filename"); 
 
  316                 statusHelper.
progress(Bundle.PlasoIngestModule_artifact_progress(currentFileName), 66);
 
  318                 if (resolvedFile == null) {
 
  319                     logger.log(Level.INFO, 
"File {0} from Plaso output not found in case.  Associating it with the data source instead.", currentFileName);
 
  320                     resolvedFile = 
image;
 
  323                 String description = resultSet.getString(
"description");
 
  328                 if (description == null || description.isEmpty()) {
 
  329                     if (eventType != TimelineEventType.STANDARD_ARTIFACT_CATCH_ALL) {
 
  330                         description = eventType.getDisplayName();
 
  336                 Collection<BlackboardAttribute> bbattributes = Arrays.asList(
 
  337                         new BlackboardAttribute(
 
  338                                 TSK_DATETIME, MODULE_NAME,
 
  339                                 resultSet.getLong(
"epoch_date")), 
 
  340                         new BlackboardAttribute(
 
  341                                 TSK_DESCRIPTION, MODULE_NAME,
 
  343                         new BlackboardAttribute(
 
  344                                 TSK_TL_EVENT_TYPE, MODULE_NAME,
 
  345                                 eventType.getTypeID()));
 
  348                     BlackboardArtifact bbart = resolvedFile.newDataArtifact(
new BlackboardArtifact.Type(TSK_TL_EVENT), bbattributes);
 
  355                         blackboard.postArtifact(bbart, MODULE_NAME);
 
  356                     } 
catch (BlackboardException ex) {
 
  357                         logger.log(Level.SEVERE, 
"Error Posting Artifact.", ex);
 
  359                 } 
catch (TskCoreException ex) {
 
  360                     logger.log(Level.SEVERE, 
"Exception Adding Artifact.", ex);
 
  366                 logger.log(Level.INFO, String.format(
"PlasoDB was empty: %s", plasoDb));
 
  369         } 
catch (SQLException ex) {
 
  370             logger.log(Level.SEVERE, 
"Error while trying to read into a sqlite db.", ex);
 
  376         Path path = Paths.get(file);
 
  377         String fileName = path.getFileName().toString();
 
  378         String filePath = path.getParent().toString().replaceAll(
"\\\\", 
"/");
 
  379         if (filePath.endsWith(
"/") == 
false) {
 
  385         if (previousFile != null
 
  386                 && previousFile.getName().equalsIgnoreCase(fileName)
 
  387                 && previousFile.getParentPath().equalsIgnoreCase(filePath)) {
 
  392             List<AbstractFile> abstractFiles = fileManager.
findFiles(fileName, filePath);
 
  393             if (abstractFiles.size() == 1) {
 
  394                 return abstractFiles.get(0);
 
  396             for (AbstractFile resolvedFile : abstractFiles) {
 
  398                 if (filePath.equalsIgnoreCase(resolvedFile.getParentPath())) {
 
  400                     previousFile = resolvedFile;
 
  404         } 
catch (TskCoreException ex) {
 
  405             logger.log(Level.SEVERE, 
"Exception finding file.", ex);
 
  421     private TimelineEventType 
findEventSubtype(String fileName, ResultSet row) 
throws SQLException {
 
  422         switch (row.getString(
"source")) {
 
  424                 if (fileName.toLowerCase().contains(COOKIE)
 
  425                         || row.getString(
"type").toLowerCase().contains(COOKIE)) {
 
  427                     return TimelineEventType.WEB_COOKIE;
 
  429                     return TimelineEventType.WEB_HISTORY;
 
  433                 return TimelineEventType.LOG_ENTRY;
 
  435                 switch (row.getString(
"sourcetype").toLowerCase()) {
 
  436                     case "unknown : usb entries":
 
  437                     case "unknown : usbstor entries":
 
  438                         return TimelineEventType.DEVICES_ATTACHED;
 
  440                         return TimelineEventType.REGISTRY;
 
  443                 return TimelineEventType.STANDARD_ARTIFACT_CATCH_ALL;
 
  467             try (BufferedWriter writer = Files.newBufferedWriter(outputPath.resolve(
"log2timeline_output.txt"));) {
 
  468                 String line = log2TimeLineOutpout.readLine();
 
  469                 while (cancelled == 
false && nonNull(line)) {
 
  473                     line = log2TimeLineOutpout.readLine();
 
  476             } 
catch (IOException ex) {
 
  477                 logger.log(Level.WARNING, 
"Error reading log2timeline output stream.", ex);
 
FileManager getFileManager()
 
void startUp(IngestJobContext context)
 
static int execute(ProcessBuilder processBuilder)
 
AbstractFile getAbstractFile(String file)
 
final PlasoModuleSettings settings
 
ResultSet executeQry(String sqlStatement)
 
List< AbstractFile > findFiles(String fileName)
 
L2TStatusProcessor(BufferedReader log2TimeLineOutpout, DataSourceIngestModuleProgress statusHelper, Path outputPath)
 
static ProcessBuilder buildProcessWithRunAsInvoker(String...commandLine)
 
final BufferedReader log2TimeLineOutpout
 
static int waitForTermination(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator)
 
static final String PLASO64
 
static final String PLASO
 
static IngestMessage createMessage(MessageType messageType, String source, String subject, String detailsHtml)
 
static final String COOKIE
 
static final String LOG2TIMELINE_EXECUTABLE
 
final DataSourceIngestModuleProgress statusHelper
 
static final Logger logger
 
static void info(String title, String message)
 
volatile boolean cancelled
 
static final String MODULE_NAME
 
ProcessBuilder buildPsortCommand(Path moduleOutputPath)
 
void postMessage(final IngestMessage message)
 
static final int LOG2TIMELINE_WORKERS
 
SleuthkitCase getSleuthkitCase()
 
String getModuleDirectory()
 
boolean dataSourceIngestIsCancelled()
 
static final TimeUnit TERMINATION_CHECK_INTERVAL_UNITS
 
static void error(String title, String message)
 
void switchToDeterminate(int workUnits)
 
static final long TERMINATION_CHECK_INTERVAL
 
static Case getCurrentCase()
 
synchronized static Logger getLogger(String name)
 
ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper)
 
ProcessBuilder buildLog2TimeLineCommand(Path moduleOutputPath, Image image)
 
static File locateExecutable(String executableName)
 
TimelineEventType findEventSubtype(String fileName, ResultSet row)
 
static final String PLASO32
 
void createPlasoArtifacts(String plasoDb, DataSourceIngestModuleProgress statusHelper)
 
void progress(int workUnits)
 
static final String PSORT_EXECUTABLE
 
File log2TimeLineExecutable
 
static synchronized IngestServices getInstance()
 
AbstractFile previousFile