19package org.sleuthkit.autopsy.modules.plaso;
21import java.io.BufferedReader;
22import java.io.BufferedWriter;
24import java.io.FileNotFoundException;
25import java.io.IOException;
26import java.io.InputStreamReader;
27import java.nio.file.Files;
28import java.nio.file.Path;
29import java.nio.file.Paths;
30import java.sql.ResultSet;
31import java.sql.SQLException;
32import java.text.SimpleDateFormat;
33import java.util.Arrays;
34import java.util.Collection;
36import java.util.Locale;
37import static java.util.Objects.nonNull;
38import java.util.concurrent.TimeUnit;
39import java.util.logging.Level;
40import java.util.stream.Collectors;
41import org.openide.modules.InstalledFileLocator;
42import org.openide.util.Cancellable;
43import org.openide.util.NbBundle;
44import org.sleuthkit.autopsy.casemodule.Case;
45import org.sleuthkit.autopsy.casemodule.services.FileManager;
46import org.sleuthkit.autopsy.coreutils.ExecUtil;
47import org.sleuthkit.autopsy.coreutils.Logger;
48import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
49import org.sleuthkit.autopsy.coreutils.PlatformUtil;
50import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect;
51import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
52import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
53import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
54import org.sleuthkit.autopsy.ingest.IngestJobContext;
55import org.sleuthkit.autopsy.ingest.IngestMessage;
56import org.sleuthkit.autopsy.ingest.IngestServices;
57import org.sleuthkit.datamodel.AbstractFile;
58import org.sleuthkit.datamodel.Blackboard;
59import org.sleuthkit.datamodel.Blackboard.BlackboardException;
60import org.sleuthkit.datamodel.BlackboardArtifact;
61import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
62import org.sleuthkit.datamodel.BlackboardAttribute;
63import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
64import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
65import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TL_EVENT_TYPE;
66import org.sleuthkit.datamodel.Content;
67import org.sleuthkit.datamodel.Image;
68import org.sleuthkit.datamodel.TskCoreException;
69import org.sleuthkit.datamodel.TimelineEventType;
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());
156 Path moduleOutputPath = Paths.get(
currentCase.getModuleDirectory(),
PLASO, currentTime);
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();
177 if (
context.dataSourceIngestIsCancelled()) {
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));
196 if (
context.dataSourceIngestIsCancelled()) {
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(
","));
231 "--vss-stores",
"all",
232 "-z",
image.getTimeZone(),
233 "--partitions",
"all",
234 "--hasher_file_size_limit",
"1",
236 "--parsers",
"\"" + parsersString +
"\"",
237 "--no_dependencies_check",
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;
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.",})
289 Blackboard blackboard =
currentCase.getSleuthkitCase().getBlackboard();
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()) {
311 if (
context.dataSourceIngestIsCancelled()) {
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()) {
330 if (eventType != TimelineEventType.STANDARD_ARTIFACT_CATCH_ALL) {
331 description = eventType.getDisplayName();
337 Collection<BlackboardAttribute> bbattributes = Arrays.asList(
338 new BlackboardAttribute(
340 resultSet.getLong(
"epoch_date")),
341 new BlackboardAttribute(
344 new BlackboardAttribute(
346 eventType.getTypeID()));
349 BlackboardArtifact bbart = resolvedFile.newDataArtifact(
new BlackboardArtifact.Type(TSK_TL_EVENT), bbattributes);
357 }
catch (BlackboardException ex) {
358 logger.log(Level.SEVERE,
"Error Posting Artifact.", ex);
360 }
catch (TskCoreException 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) {
388 &&
previousFile.getParentPath().equalsIgnoreCase(filePath)) {
393 List<AbstractFile> abstractFiles =
fileManager.findFiles(fileName, filePath);
394 if (abstractFiles.size() == 1) {
395 return abstractFiles.get(0);
397 for (AbstractFile resolvedFile : abstractFiles) {
399 if (filePath.equalsIgnoreCase(resolvedFile.getParentPath())) {
405 }
catch (TskCoreException ex) {
406 logger.log(Level.SEVERE,
"Exception finding file.", ex);
422 private TimelineEventType
findEventSubtype(String fileName, ResultSet row)
throws SQLException {
423 switch (row.getString(
"source")) {
425 if (fileName.toLowerCase().contains(
COOKIE)
426 || row.getString(
"type").toLowerCase().contains(
COOKIE)) {
428 return TimelineEventType.WEB_COOKIE;
430 return TimelineEventType.WEB_HISTORY;
434 return TimelineEventType.LOG_ENTRY;
436 switch (row.getString(
"sourcetype").toLowerCase()) {
437 case "unknown : usb entries":
438 case "unknown : usbstor entries":
439 return TimelineEventType.DEVICES_ATTACHED;
441 return TimelineEventType.REGISTRY;
444 return TimelineEventType.STANDARD_ARTIFACT_CATCH_ALL;
468 try (BufferedWriter writer = Files.newBufferedWriter(
outputPath.resolve(
"log2timeline_output.txt"));) {
470 while (
cancelled ==
false && nonNull(line)) {
477 }
catch (IOException ex) {
478 logger.log(Level.WARNING,
"Error reading log2timeline output stream.", ex);
static Case getCurrentCase()
static int execute(ProcessBuilder processBuilder)
static int waitForTermination(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator)
synchronized static Logger getLogger(String name)
static void error(String title, String message)
static void info(String title, String message)
ResultSet executeQry(String sqlStatement)
void progress(int workUnits)
void switchToDeterminate(int workUnits)
static IngestMessage createMessage(MessageType messageType, String source, String subject, String detailsHtml)
void postMessage(final IngestMessage message)
static synchronized IngestServices getInstance()
final DataSourceIngestModuleProgress statusHelper
L2TStatusProcessor(BufferedReader log2TimeLineOutpout, DataSourceIngestModuleProgress statusHelper, Path outputPath)
final BufferedReader log2TimeLineOutpout
volatile boolean cancelled
File log2TimeLineExecutable
static final Logger logger
ProcessBuilder buildPsortCommand(Path moduleOutputPath)
static final String PLASO32
AbstractFile previousFile
static final String PLASO64
void createPlasoArtifacts(String plasoDb, DataSourceIngestModuleProgress statusHelper)
ProcessBuilder buildLog2TimeLineCommand(Path moduleOutputPath, Image image)
static final String MODULE_NAME
ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper)
AbstractFile getAbstractFile(String file)
void startUp(IngestJobContext context)
static final String LOG2TIMELINE_EXECUTABLE
static final String PSORT_EXECUTABLE
final PlasoModuleSettings settings
static final int LOG2TIMELINE_WORKERS
static File locateExecutable(String executableName)
static final long TERMINATION_CHECK_INTERVAL
static ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine)
static final TimeUnit TERMINATION_CHECK_INTERVAL_UNITS
static final String PLASO
TimelineEventType findEventSubtype(String fileName, ResultSet row)
static final String COOKIE