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;
 
   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;
 
  155             String currentTime = 
new SimpleDateFormat(
"yyyy-MM-dd HH-mm-ss Z", Locale.US).format(System.currentTimeMillis());
 
  158                 Files.createDirectories(moduleOutputPath);
 
  159             } 
catch (IOException ex) {
 
  160                 logger.log(Level.SEVERE, 
"Error creating Plaso module output directory.", ex); 
 
  165             logger.log(Level.INFO, 
"Starting Plaso Run.");
 
  166             statusHelper.
progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0);
 
  169                 Process log2TimeLineProcess = log2TimeLineCommand.start();
 
  170                 try (BufferedReader log2TimeLineOutpout = 
new BufferedReader(
new InputStreamReader(log2TimeLineProcess.getInputStream()))) {
 
  172                     new Thread(statusReader, 
"log2timeline status reader").start();  
 
  178                     logger.log(Level.INFO, 
"Log2timeline run was canceled"); 
 
  181                 if (Files.notExists(moduleOutputPath.resolve(PLASO))) {
 
  182                     logger.log(Level.WARNING, 
"Error running log2timeline: there was no storage file."); 
 
  187                 statusHelper.
progress(Bundle.PlasoIngestModule_running_psort(), 33);
 
  191                     logger.log(Level.SEVERE, String.format(
"Error running Psort, error code returned %d", result)); 
 
  197                     logger.log(Level.INFO, 
"psort run was canceled"); 
 
  200                 Path plasoFile = moduleOutputPath.resolve(
"plasodb.db3");  
 
  201                 if (Files.notExists(plasoFile)) {
 
  202                     logger.log(Level.SEVERE, 
"Error running Psort: there was no sqlite db file."); 
 
  209             } 
catch (IOException ex) {
 
  210                 logger.log(Level.SEVERE, 
"Error running Plaso.", ex);
 
  215                     Bundle.PlasoIngestModule_has_run(),
 
  216                     Bundle.PlasoIngestModule_completed());
 
  224         String parsersString = settings.getParsers().entrySet().stream()
 
  225                 .filter(entry -> entry.getValue() == 
false)
 
  226                 .map(entry -> 
"!" + entry.getKey()) 
 
  227                 .collect(Collectors.joining(
","));
 
  230                 "\"" + log2TimeLineExecutable + 
"\"", 
 
  231                 "--vss-stores", 
"all", 
 
  233                 "--partitions", 
"all", 
 
  234                 "--hasher_file_size_limit", 
"1", 
 
  236                 "--parsers", 
"\"" + parsersString + 
"\"",
 
  237                 "--no_dependencies_check", 
 
  238                 "--workers", String.valueOf(LOG2TIMELINE_WORKERS),
 
  239                 moduleOutputPath.resolve(PLASO).toString(),
 
  242         processBuilder.redirectError(moduleOutputPath.resolve(
"log2timeline_err.txt").toFile());  
 
  243         return processBuilder;
 
  247         ProcessBuilder processBuilder = 
new ProcessBuilder(commandLine);
 
  252         processBuilder.environment().put(
"__COMPAT_LAYER", 
"RunAsInvoker"); 
 
  253         return processBuilder;
 
  258                 "\"" + psortExecutable + 
"\"", 
 
  259                 "-o", 
"4n6time_sqlite", 
 
  260                 "-w", moduleOutputPath.resolve(
"plasodb.db3").toString(), 
 
  261                 moduleOutputPath.resolve(PLASO).toString()
 
  264         processBuilder.redirectOutput(moduleOutputPath.resolve(
"psort_output.txt").toFile()); 
 
  265         processBuilder.redirectError(moduleOutputPath.resolve(
"psort_err.txt").toFile());  
 
  266         return processBuilder;
 
  271         String executableToFindName = Paths.get(PLASO, architectureFolder, executableName).toString();
 
  273         File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, 
PlasoIngestModule.class.getPackage().getName(), 
false);
 
  274         if (null == exeFile || exeFile.canExecute() == 
false) {
 
  275             throw new FileNotFoundException(executableName + 
" executable not found.");
 
  281         "PlasoIngestModule.exception.posting.artifact=Exception Posting artifact.",
 
  282         "PlasoIngestModule.event.datetime=Event Date Time",
 
  283         "PlasoIngestModule.event.description=Event Description",
 
  284         "PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation ",
 
  285         "# {0} - file that events are from",
 
  286         "PlasoIngestModule.artifact.progress=Adding events to case: {0}",
 
  287         "PlasoIngestModule.info.empty.database=Plaso database was empty.",})
 
  291         String sqlStatement = 
"SELECT substr(filename,1) AS  filename, " 
  292                 + 
"   strftime('%s', datetime) AS epoch_date, " 
  297                 + 
" FROM log2timeline " 
  298                 + 
" WHERE source NOT IN ('FILE', " 
  300                 + 
"   AND sourcetype NOT IN ('UNKNOWN', " 
  301                 + 
"                          'PE Import Time');"; 
 
  304                 ResultSet resultSet = tempdbconnect.
executeQry(sqlStatement)) {
 
  306             boolean dbHasData = 
false;
 
  308             while (resultSet.next()) {
 
  312                     logger.log(Level.INFO, 
"Cancelled Plaso Artifact Creation."); 
 
  316                 String currentFileName = resultSet.getString(
"filename"); 
 
  317                 statusHelper.
progress(Bundle.PlasoIngestModule_artifact_progress(currentFileName), 66);
 
  319                 if (resolvedFile == null) {
 
  320                     logger.log(Level.INFO, 
"File {0} from Plaso output not found in case.  Associating it with the data source instead.", currentFileName);
 
  321                     resolvedFile = 
image;
 
  324                 String description = resultSet.getString(
"description");
 
  329                 if (description == null || description.isEmpty()) {
 
  337                 Collection<BlackboardAttribute> bbattributes = Arrays.asList(
 
  339                                 TSK_DATETIME, MODULE_NAME,
 
  340                                 resultSet.getLong(
"epoch_date")), 
 
  342                                 TSK_DESCRIPTION, MODULE_NAME,
 
  345                                 TSK_TL_EVENT_TYPE, MODULE_NAME,
 
  358                         logger.log(Level.SEVERE, 
"Error Posting Artifact.", ex);
 
  361                     logger.log(Level.SEVERE, 
"Exception Adding Artifact.", ex);
 
  367                 logger.log(Level.INFO, String.format(
"PlasoDB was empty: %s", plasoDb));
 
  370         } 
catch (SQLException ex) {
 
  371             logger.log(Level.SEVERE, 
"Error while trying to read into a sqlite db.", ex);
 
  377         Path path = Paths.get(file);
 
  378         String fileName = path.getFileName().
toString();
 
  379         String filePath = path.getParent().toString().replaceAll(
"\\\\", 
"/");
 
  380         if (filePath.endsWith(
"/") == 
false) {
 
  386         if (previousFile != null
 
  387                 && previousFile.
getName().equalsIgnoreCase(fileName)
 
  388                 && previousFile.
getParentPath().equalsIgnoreCase(filePath)) {
 
  393             List<AbstractFile> abstractFiles = fileManager.
findFiles(fileName, filePath);
 
  394             if (abstractFiles.size() == 1) {
 
  395                 return abstractFiles.get(0);
 
  399                 if (filePath.equalsIgnoreCase(resolvedFile.getParentPath())) {
 
  401                     previousFile = resolvedFile;
 
  406             logger.log(Level.SEVERE, 
"Exception finding file.", ex);
 
  423         switch (row.getString(
"source")) {
 
  425                 if (fileName.toLowerCase().contains(COOKIE)
 
  426                         || row.getString(
"type").toLowerCase().contains(COOKIE)) {
 
  436                 switch (row.getString(
"sourcetype").toLowerCase()) {
 
  437                     case "unknown : usb entries":
 
  438                     case "unknown : usbstor entries":
 
  468             try (BufferedWriter writer = Files.newBufferedWriter(outputPath.resolve(
"log2timeline_output.txt"));) {
 
  469                 String line = log2TimeLineOutpout.readLine();
 
  470                 while (cancelled == 
false && nonNull(line)) {
 
  474                     line = log2TimeLineOutpout.readLine();
 
  477             } 
catch (IOException ex) {
 
  478                 logger.log(Level.WARNING, 
"Error reading log2timeline output stream.", ex);
 
void postArtifact(BlackboardArtifact artifact, String moduleName)
FileManager getFileManager()
void startUp(IngestJobContext context)
TimelineEventType REGISTRY
static int execute(ProcessBuilder processBuilder)
AbstractFile getAbstractFile(String file)
Blackboard getBlackboard()
final PlasoModuleSettings settings
ResultSet executeQry(String sqlStatement)
List< AbstractFile > findFiles(String fileName)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributesList)
L2TStatusProcessor(BufferedReader log2TimeLineOutpout, DataSourceIngestModuleProgress statusHelper, Path outputPath)
static ProcessBuilder buildProcessWithRunAsInvoker(String...commandLine)
final BufferedReader log2TimeLineOutpout
TimelineEventType LOG_ENTRY
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
TimelineEventType WEB_COOKIE
static void info(String title, String message)
volatile boolean cancelled
String toString(boolean preserveState)
static final String MODULE_NAME
ProcessBuilder buildPsortCommand(Path moduleOutputPath)
void postMessage(final IngestMessage message)
static final int LOG2TIMELINE_WORKERS
SleuthkitCase getSleuthkitCase()
String getModuleDirectory()
TimelineEventType WEB_HISTORY
TimelineEventType DEVICES_ATTACHED
boolean dataSourceIngestIsCancelled()
static final TimeUnit TERMINATION_CHECK_INTERVAL_UNITS
TimelineEventType STANDARD_ARTIFACT_CATCH_ALL
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