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.logging.Level;
39 import java.util.stream.Collectors;
40 import org.openide.modules.InstalledFileLocator;
41 import org.openide.util.Cancellable;
42 import org.openide.util.NbBundle;
58 import org.
sleuthkit.datamodel.Blackboard.BlackboardException;
60 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
62 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
63 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
64 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TL_EVENT_TYPE;
78 private static final String
PLASO =
"plaso";
79 private static final String
PLASO64 =
"plaso-20180818-amd64";
80 private static final String
PLASO32 =
"plaso-20180818-win32";
83 private static final String
COOKIE =
"cookie";
102 "PlasoIngestModule.executable.not.found=Plaso Executable Not Found.",
103 "PlasoIngestModule.requires.windows=Plaso module requires windows.",
104 "PlasoIngestModule.dataSource.not.an.image=Datasource is not an Image."})
116 }
catch (FileNotFoundException exception) {
117 logger.log(Level.WARNING,
"Plaso executable not found.", exception);
122 if (!(dataSource instanceof Image)) {
125 image = (Image) dataSource;
129 "PlasoIngestModule.error.running.log2timeline=Error running log2timeline, see log file.",
130 "PlasoIngestModule.error.running.psort=Error running Psort, see log file.",
131 "PlasoIngestModule.error.creating.output.dir=Error creating Plaso module output directory.",
132 "PlasoIngestModule.starting.log2timeline=Starting Log2timeline",
133 "PlasoIngestModule.running.psort=Running Psort",
134 "PlasoIngestModule.log2timeline.cancelled=Log2timeline run was canceled",
135 "PlasoIngestModule.psort.cancelled=psort run was canceled",
136 "PlasoIngestModule.bad.imageFile=Cannot find image file name and path",
137 "PlasoIngestModule.completed=Plaso Processing Completed",
138 "PlasoIngestModule.has.run=Plaso Plugin has been run.",
139 "PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete."})
142 assert dataSource.equals(image);
148 String currentTime =
new SimpleDateFormat(
"yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());
151 Files.createDirectories(moduleOutputPath);
152 }
catch (IOException ex) {
153 logger.log(Level.SEVERE,
"Error creating Plaso module output directory.", ex);
158 logger.log(Level.INFO,
"Starting Plaso Run.");
159 statusHelper.
progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0);
162 Process log2TimeLineProcess = log2TimeLineCommand.start();
163 try (BufferedReader log2TimeLineOutpout =
new BufferedReader(
new InputStreamReader(log2TimeLineProcess.getInputStream()))) {
165 new Thread(statusReader,
"log2timeline status reader").start();
171 logger.log(Level.INFO,
"Log2timeline run was canceled");
174 if (Files.notExists(moduleOutputPath.resolve(PLASO))) {
175 logger.log(Level.WARNING,
"Error running log2timeline: there was no storage file.");
180 statusHelper.
progress(Bundle.PlasoIngestModule_running_psort(), 33);
184 logger.log(Level.SEVERE, String.format(
"Error running Psort, error code returned %d", result));
190 logger.log(Level.INFO,
"psort run was canceled");
193 Path plasoFile = moduleOutputPath.resolve(
"plasodb.db3");
194 if (Files.notExists(plasoFile)) {
195 logger.log(Level.SEVERE,
"Error running Psort: there was no sqlite db file.");
202 }
catch (IOException ex) {
203 logger.log(Level.SEVERE,
"Error running Plaso.", ex);
208 Bundle.PlasoIngestModule_has_run(),
209 Bundle.PlasoIngestModule_completed());
216 String parsersString = settings.getParsers().entrySet().stream()
217 .filter(entry -> entry.getValue() ==
false)
218 .map(entry ->
"!" + entry.getKey())
219 .collect(Collectors.joining(
","));
222 "\"" + log2TimeLineExecutable +
"\"",
223 "--vss-stores",
"all",
224 "-z", image.getTimeZone(),
225 "--partitions",
"all",
226 "--hasher_file_size_limit",
"1",
228 "--parsers",
"\"" + parsersString +
"\"",
229 "--no_dependencies_check",
230 "--workers", String.valueOf(LOG2TIMELINE_WORKERS),
231 moduleOutputPath.resolve(PLASO).toString(),
234 processBuilder.redirectError(moduleOutputPath.resolve(
"log2timeline_err.txt").toFile());
235 return processBuilder;
239 ProcessBuilder processBuilder =
new ProcessBuilder(commandLine);
242 processBuilder.environment().put(
"__COMPAT_LAYER",
"RunAsInvoker");
243 return processBuilder;
248 "\"" + psortExecutable +
"\"",
249 "-o",
"4n6time_sqlite",
250 "-w", moduleOutputPath.resolve(
"plasodb.db3").toString(),
251 moduleOutputPath.resolve(PLASO).toString()
254 processBuilder.redirectOutput(moduleOutputPath.resolve(
"psort_output.txt").toFile());
255 processBuilder.redirectError(moduleOutputPath.resolve(
"psort_err.txt").toFile());
256 return processBuilder;
261 String executableToFindName = Paths.get(PLASO, architectureFolder, executableName).toString();
263 File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName,
PlasoIngestModule.class.getPackage().getName(),
false);
264 if (null == exeFile || exeFile.canExecute() ==
false) {
265 throw new FileNotFoundException(executableName +
" executable not found.");
271 "PlasoIngestModule.exception.posting.artifact=Exception Posting artifact.",
272 "PlasoIngestModule.event.datetime=Event Date Time",
273 "PlasoIngestModule.event.description=Event Description",
274 "PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation ",
275 "# {0} - file that events are from",
276 "PlasoIngestModule.artifact.progress=Adding events to case: {0}",
277 "PlasoIngestModule.info.empty.database=Plaso database was empty.",
282 String sqlStatement =
"SELECT substr(filename,1) AS filename, "
283 +
" strftime('%s', datetime) AS epoch_date, "
288 +
" FROM log2timeline "
289 +
" WHERE source NOT IN ('FILE', "
291 +
" AND sourcetype NOT IN ('UNKNOWN', "
292 +
" 'PE Import Time');";
295 ResultSet resultSet = tempdbconnect.
executeQry(sqlStatement)) {
297 boolean dbHasData =
false;
299 while (resultSet.next()) {
303 logger.log(Level.INFO,
"Cancelled Plaso Artifact Creation.");
307 String currentFileName = resultSet.getString(
"filename");
308 statusHelper.
progress(Bundle.PlasoIngestModule_artifact_progress(currentFileName), 66);
310 if (resolvedFile == null) {
311 logger.log(Level.INFO,
"File {0} from Plaso output not found in case. Associating it with the data source instead.", currentFileName);
312 resolvedFile =
image;
315 String description = resultSet.getString(
"description");
320 if ( description == null || description.isEmpty() ) {
321 if (eventType != TimelineEventType.OTHER) {
322 description = eventType.getDisplayName();
328 Collection<BlackboardAttribute> bbattributes = Arrays.asList(
329 new BlackboardAttribute(
330 TSK_DATETIME, MODULE_NAME,
331 resultSet.getLong(
"epoch_date")),
332 new BlackboardAttribute(
333 TSK_DESCRIPTION, MODULE_NAME,
335 new BlackboardAttribute(
336 TSK_TL_EVENT_TYPE, MODULE_NAME,
337 eventType.getTypeID()));
340 BlackboardArtifact bbart = resolvedFile.newArtifact(TSK_TL_EVENT);
341 bbart.addAttributes(bbattributes);
346 blackboard.postArtifact(bbart, MODULE_NAME);
347 }
catch (BlackboardException ex) {
348 logger.log(Level.SEVERE,
"Error Posting Artifact.", ex);
350 }
catch (TskCoreException ex) {
351 logger.log(Level.SEVERE,
"Exception Adding Artifact.", ex);
357 logger.log(Level.INFO, String.format(
"PlasoDB was empty: %s", plasoDb));
360 }
catch (SQLException ex) {
361 logger.log(Level.SEVERE,
"Error while trying to read into a sqlite db.", ex);
367 Path path = Paths.get(file);
368 String fileName = path.getFileName().toString();
369 String filePath = path.getParent().toString().replaceAll(
"\\\\",
"/");
370 if (filePath.endsWith(
"/") ==
false) {
376 if (previousFile != null
377 && previousFile.getName().equalsIgnoreCase(fileName)
378 && previousFile.getParentPath().equalsIgnoreCase(filePath)) {
383 List<AbstractFile> abstractFiles = fileManager.
findFiles(fileName, filePath);
384 if (abstractFiles.size() == 1) {
385 return abstractFiles.get(0);
387 for (AbstractFile resolvedFile : abstractFiles) {
389 if (filePath.equalsIgnoreCase(resolvedFile.getParentPath())) {
391 previousFile = resolvedFile;
395 }
catch (TskCoreException ex) {
396 logger.log(Level.SEVERE,
"Exception finding file.", ex);
412 private TimelineEventType
findEventSubtype(String fileName, ResultSet row)
throws SQLException {
413 switch (row.getString(
"source")) {
415 if (fileName.toLowerCase().contains(COOKIE)
416 || row.getString(
"type").toLowerCase().contains(COOKIE)) {
418 return TimelineEventType.WEB_COOKIE;
420 return TimelineEventType.WEB_HISTORY;
424 return TimelineEventType.LOG_ENTRY;
426 switch (row.getString(
"sourcetype").toLowerCase()) {
427 case "unknown : usb entries":
428 case "unknown : usbstor entries":
429 return TimelineEventType.DEVICES_ATTACHED;
431 return TimelineEventType.REGISTRY;
434 return TimelineEventType.OTHER;
458 try (BufferedWriter writer = Files.newBufferedWriter(outputPath.resolve(
"log2timeline_output.txt"));) {
459 String line = log2TimeLineOutpout.readLine();
460 while (cancelled ==
false && nonNull(line)) {
464 line = log2TimeLineOutpout.readLine();
467 }
catch (IOException ex) {
468 logger.log(Level.WARNING,
"Error reading log2timeline output stream.", ex);
ResultSet executeQry(String instruction)
FileManager getFileManager()
void startUp(IngestJobContext context)
static int execute(ProcessBuilder processBuilder)
AbstractFile getAbstractFile(String file)
final PlasoModuleSettings settings
L2TStatusProcessor(BufferedReader log2TimeLineOutpout, DataSourceIngestModuleProgress statusHelper, Path outputPath)
static ProcessBuilder buildProcessWithRunAsInvoker(String...commandLine)
final BufferedReader log2TimeLineOutpout
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 int waitForTermination(String command, Process process, ProcessTerminator terminator)
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 void error(String title, String message)
void switchToDeterminate(int workUnits)
synchronized List< AbstractFile > findFiles(String fileName)
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