19 package org.sleuthkit.autopsy.modules.stix;
28 import java.util.List;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.TimeZone;
32 import java.text.ParseException;
33 import java.text.SimpleDateFormat;
34 import org.mitre.cybox.common_2.ConditionApplicationEnum;
36 import org.mitre.cybox.objects.FileObjectType;
37 import org.mitre.cybox.objects.WindowsExecutableFileObjectType;
38 import org.mitre.cybox.common_2.ConditionTypeEnum;
39 import org.mitre.cybox.common_2.DatatypeEnum;
40 import org.mitre.cybox.common_2.HashType;
41 import org.mitre.cybox.common_2.DateTimeObjectPropertyType;
42 import org.mitre.cybox.common_2.StringObjectPropertyType;
43 import org.mitre.cybox.common_2.UnsignedLongObjectPropertyType;
48 class EvalFileObj
extends EvaluatableObject {
50 private final FileObjectType obj;
52 public EvalFileObj(FileObjectType a_obj, String a_id, String a_spacing) {
59 @SuppressWarnings(
"deprecation")
60 public synchronized ObservableResult evaluate() {
62 Case case1 = Case.getCurrentCase();
63 SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
66 String whereClause =
"";
68 if (obj.getSizeInBytes() != null) {
70 String newClause = processULongObject(obj.getSizeInBytes(),
"size");
71 whereClause = addClause(whereClause, newClause);
72 }
catch (TskCoreException ex) {
73 addWarning(ex.getLocalizedMessage());
77 if (obj.getFileName() != null) {
79 String newClause = processStringObject(obj.getFileName(),
"name");
80 whereClause = addClause(whereClause, newClause);
81 }
catch (TskCoreException ex) {
82 addWarning(ex.getLocalizedMessage());
86 if (obj.getFileExtension() != null) {
87 if ((obj.getFileExtension().getCondition() == null)
88 || (obj.getFileExtension().getCondition() == ConditionTypeEnum.EQUALS)) {
89 String newClause =
"LOWER(name) LIKE LOWER(\'%" + obj.getFileExtension().getValue() +
"\')";
90 whereClause = addClause(whereClause, newClause);
93 "Could not process condition " + obj.getFileExtension().getCondition().value() +
" on file extension");
97 if (obj.getFilePath() != null) {
100 String[] parts = obj.getFilePath().getValue().toString().split(
"##comma##");
101 String finalPathStr =
"";
103 for (String filePath : parts) {
105 String currentFilePath = filePath;
108 if (currentFilePath.matches(
"^[A-Za-z]:.*")) {
109 currentFilePath = currentFilePath.substring(2);
113 currentFilePath = currentFilePath.replace(
"\\",
"/");
116 if (!currentFilePath.startsWith(
"/")) {
117 currentFilePath =
"/" + currentFilePath;
121 if (!currentFilePath.endsWith(
"/")) {
122 int lastSlash = currentFilePath.lastIndexOf(
'/');
123 if (lastSlash >= 0) {
124 currentFilePath = currentFilePath.substring(0, lastSlash + 1);
129 if (!finalPathStr.isEmpty()) {
130 finalPathStr +=
"##comma##";
132 finalPathStr += currentFilePath;
135 String newClause = processStringObject(finalPathStr, obj.getFilePath().getCondition(),
136 obj.getFilePath().getApplyCondition(),
"parent_path");
138 whereClause = addClause(whereClause, newClause);
139 }
catch (TskCoreException ex) {
140 addWarning(ex.getLocalizedMessage());
144 if (obj.getCreatedTime() != null) {
146 String newClause = processTimestampObject(obj.getCreatedTime(),
"crtime");
147 whereClause = addClause(whereClause, newClause);
148 }
catch (TskCoreException ex) {
149 addWarning(ex.getLocalizedMessage());
153 if (obj.getModifiedTime() != null) {
155 String newClause = processTimestampObject(obj.getModifiedTime(),
"mtime");
156 whereClause = addClause(whereClause, newClause);
157 }
catch (TskCoreException ex) {
158 addWarning(ex.getLocalizedMessage());
162 if (obj.getAccessedTime() != null) {
164 String newClause = processTimestampObject(obj.getAccessedTime(),
"atime");
165 whereClause = addClause(whereClause, newClause);
166 }
catch (TskCoreException ex) {
167 addWarning(ex.getLocalizedMessage());
171 if (obj.getHashes() != null) {
172 for (HashType h : obj.getHashes().getHashes()) {
173 if (h.getSimpleHashValue() != null) {
174 if (h.getType().getValue().equals(
"MD5")) {
175 String newClause =
"";
176 if (h.getSimpleHashValue().getValue().toString().toLowerCase().contains(
"##comma##")) {
177 String[] parts = h.getSimpleHashValue().getValue().toString().toLowerCase().split(
"##comma##");
178 String hashList =
"";
179 for (String s : parts) {
180 if (!hashList.isEmpty()) {
183 hashList +=
"\'" + s +
"\'";
185 newClause =
"md5 IN (" + hashList +
")";
187 newClause =
"md5=\'" + h.getSimpleHashValue().getValue().toString().toLowerCase() +
"\'";
189 whereClause = addClause(whereClause, newClause);
191 addWarning(
"Could not process hash type " + h.getType().getValue().toString());
194 addWarning(
"Could not process non-simple hash value");
199 if (obj instanceof WindowsExecutableFileObjectType) {
200 WindowsExecutableFileObjectType winExe = (WindowsExecutableFileObjectType) obj;
201 if (winExe.getHeaders() != null) {
202 if (winExe.getHeaders().getFileHeader() != null) {
203 if (winExe.getHeaders().getFileHeader().getTimeDateStamp() != null) {
205 String result = convertTimestampString(winExe.getHeaders().getFileHeader().getTimeDateStamp().getValue().toString());
206 String newClause = processNumericFields(result,
207 winExe.getHeaders().getFileHeader().getTimeDateStamp().getCondition(),
208 winExe.getHeaders().getFileHeader().getTimeDateStamp().getApplyCondition(),
210 whereClause = addClause(whereClause, newClause);
211 }
catch (TskCoreException ex) {
212 addWarning(ex.getLocalizedMessage());
219 String unsupportedFields = listUnsupportedFields();
220 if (!unsupportedFields.isEmpty()) {
221 addWarning(
"Unsupported fields: " + unsupportedFields);
224 if (whereClause.length() > 0) {
226 List<AbstractFile> matchingFiles = sleuthkitCase.findAllFilesWhere(whereClause);
228 if (!matchingFiles.isEmpty()) {
230 if (listSecondaryFields().isEmpty()) {
232 List<StixArtifactData> artData =
new ArrayList<StixArtifactData>();
233 for (AbstractFile a : matchingFiles) {
234 artData.add(
new StixArtifactData(a,
id,
"FileObject"));
237 return new ObservableResult(
id,
"FileObject: Found " + matchingFiles.size() +
" matches for " + whereClause + getPrintableWarnings(),
238 spacing, ObservableResult.ObservableState.TRUE, artData);
242 List<AbstractFile> secondaryHits =
new ArrayList<AbstractFile>();
244 for (AbstractFile file : matchingFiles) {
245 boolean passedTests =
true;
247 if (obj.isIsMasqueraded() != null) {
248 List<BlackboardArtifact> arts = file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED);
249 boolean isMasq =
false;
250 if (!arts.isEmpty()) {
254 if (obj.isIsMasqueraded() != isMasq) {
260 if (obj.getFileFormat() != null) {
262 String formatsFound = file.getMIMEType();
263 if (formatsFound != null) {
264 if (!(formatsFound.equalsIgnoreCase(obj.getFileFormat().getValue().toString()))) {
265 addWarning(
"Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString()
266 +
" against " + formatsFound);
269 addWarning(
"Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString()
270 +
" (no file formats found)");
279 secondaryHits.add(file);
283 if (secondaryHits.isEmpty()) {
285 return new ObservableResult(
id,
"FileObject: Found " + matchingFiles.size() +
" matches for " + whereClause
286 +
" but none for secondary tests on " + listSecondaryFields() + getPrintableWarnings(),
287 spacing, ObservableResult.ObservableState.FALSE, null);
289 List<StixArtifactData> artData =
new ArrayList<StixArtifactData>();
290 for (AbstractFile a : secondaryHits) {
291 artData.add(
new StixArtifactData(a,
id,
"FileObject"));
293 return new ObservableResult(
id,
"FileObject: Found " + secondaryHits.size() +
" matches for " + whereClause
294 +
" and secondary tests on " + listSecondaryFields() + getPrintableWarnings(),
295 spacing, ObservableResult.ObservableState.TRUE, artData);
299 return new ObservableResult(
id,
"FileObject: Found no matches for " + whereClause + getPrintableWarnings(),
300 spacing, ObservableResult.ObservableState.FALSE, null);
302 }
catch (TskCoreException ex) {
303 return new ObservableResult(
id,
"FileObject: Exception during evaluation: " + ex.getLocalizedMessage(),
304 spacing, ObservableResult.ObservableState.INDETERMINATE, null);
310 return new ObservableResult(
id,
"FileObject: No evaluatable fields " + getPrintableWarnings(), spacing,
311 ObservableResult.ObservableState.INDETERMINATE, null);
320 private String listSecondaryFields() {
321 String secondaryFields =
"";
323 if (obj.isIsMasqueraded() != null) {
324 secondaryFields +=
"is_masqueraded ";
327 if (obj.getFileFormat() != null) {
328 secondaryFields +=
"File_Format ";
331 return secondaryFields;
339 private String listUnsupportedFields() {
340 String unsupportedFields =
"";
342 if (obj.isIsPacked() != null) {
343 unsupportedFields +=
"is_packed ";
345 if (obj.getDevicePath() != null) {
346 unsupportedFields +=
"Device_Path ";
348 if (obj.getFullPath() != null) {
349 unsupportedFields +=
"Full_Path ";
351 if (obj.getMagicNumber() != null) {
352 unsupportedFields +=
"Magic_Number ";
354 if (obj.getDigitalSignatures() != null) {
355 unsupportedFields +=
"Digital_Signatures ";
357 if (obj.getFileAttributesList() != null) {
358 unsupportedFields +=
"File_Attributes_List ";
360 if (obj.getPermissions() != null) {
361 unsupportedFields +=
"Permissions ";
363 if (obj.getUserOwner() != null) {
364 unsupportedFields +=
"User_Owner ";
366 if (obj.getPackerList() != null) {
367 unsupportedFields +=
"Packer_List ";
369 if (obj.getPeakEntropy() != null) {
370 unsupportedFields +=
"Peak_Entropy ";
372 if (obj.getSymLinks() != null) {
373 unsupportedFields +=
"Sym_Links ";
375 if (obj.getByteRuns() != null) {
376 unsupportedFields +=
"Bytes_Runs ";
378 if (obj.getExtractedFeatures() != null) {
379 unsupportedFields +=
"Extracted_Features ";
381 if (obj.getEncryptionAlgorithm() != null) {
382 unsupportedFields +=
"Encryption_Algorithm ";
384 if (obj.getDecryptionKey() != null) {
385 unsupportedFields +=
"Decryption_Key ";
387 if (obj.getCompressionMethod() != null) {
388 unsupportedFields +=
"Compression_Method ";
390 if (obj.getCompressionVersion() != null) {
391 unsupportedFields +=
"Compression_Version ";
393 if (obj.getCompressionComment() != null) {
394 unsupportedFields +=
"Compression_Comment ";
397 return unsupportedFields;
409 private static long convertTimestamp(String timeStr)
throws ParseException {
410 SimpleDateFormat dateFormat =
new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss'Z'");
411 dateFormat.setTimeZone(TimeZone.getTimeZone(
"GMT"));
412 Date parsedDate = dateFormat.parse(timeStr);
414 Long unixTime = parsedDate.getTime() / 1000;
430 private static String processULongObject(UnsignedLongObjectPropertyType longObj, String fieldName)
431 throws TskCoreException {
433 return processNumericFields(longObj.getValue().toString(), longObj.getCondition(),
434 longObj.getApplyCondition(), fieldName);
449 private static String processNumericFields(String valueStr, ConditionTypeEnum typeCondition,
450 ConditionApplicationEnum applyCondition, String fieldName)
451 throws TskCoreException {
453 if ((typeCondition == null)
454 || ((typeCondition != ConditionTypeEnum.INCLUSIVE_BETWEEN)
455 && (typeCondition != ConditionTypeEnum.EXCLUSIVE_BETWEEN))) {
457 String fullClause =
"";
459 if (valueStr.isEmpty()) {
460 throw new TskCoreException(
"Empty value field");
463 String[] parts = valueStr.split(
"##comma##");
465 for (String valuePart : parts) {
466 String partialClause;
468 if ((typeCondition == null)
469 || (typeCondition == ConditionTypeEnum.EQUALS)) {
471 partialClause = fieldName +
"=" + valuePart;
472 }
else if (typeCondition == ConditionTypeEnum.DOES_NOT_EQUAL) {
473 partialClause = fieldName +
"!=" + valuePart;
474 }
else if (typeCondition == ConditionTypeEnum.GREATER_THAN) {
475 partialClause = fieldName +
">" + valuePart;
476 }
else if (typeCondition == ConditionTypeEnum.GREATER_THAN_OR_EQUAL) {
477 partialClause = fieldName +
">=" + valuePart;
478 }
else if (typeCondition == ConditionTypeEnum.LESS_THAN) {
479 partialClause = fieldName +
"<" + valuePart;
480 }
else if (typeCondition == ConditionTypeEnum.LESS_THAN_OR_EQUAL) {
481 partialClause = fieldName +
"<=" + valuePart;
483 throw new TskCoreException(
"Could not process condition " + typeCondition.value() +
" on " + fieldName);
486 if (fullClause.isEmpty()) {
488 if (parts.length > 1) {
491 if (applyCondition == ConditionApplicationEnum.NONE) {
492 fullClause +=
" NOT ";
494 fullClause += partialClause;
496 if (applyCondition == ConditionApplicationEnum.ALL) {
497 fullClause +=
" AND " + partialClause;
498 }
else if (applyCondition == ConditionApplicationEnum.NONE) {
499 fullClause +=
" AND NOT " + partialClause;
501 fullClause +=
" OR " + partialClause;
506 if (parts.length > 1) {
513 if (typeCondition == ConditionTypeEnum.INCLUSIVE_BETWEEN) {
514 String[] parts = valueStr.split(
"##comma##");
515 if (parts.length != 2) {
516 throw new TskCoreException(
"Unexpected number of arguments in INCLUSIVE_BETWEEN on " + fieldName
517 +
"(" + valueStr +
")");
519 return (fieldName +
">=" + parts[0] +
" AND " + fieldName +
"<=" + parts[1]);
521 String[] parts = valueStr.split(
"##comma##");
522 if (parts.length != 2) {
523 throw new TskCoreException(
"Unexpected number of arguments in EXCLUSIVE_BETWEEN on " + fieldName
524 +
"(" + valueStr +
")");
526 return (fieldName +
">" + parts[0] +
" AND " + fieldName +
"<" + parts[1]);
541 private static String processStringObject(StringObjectPropertyType stringObj, String fieldName)
542 throws TskCoreException {
544 return processStringObject(stringObj.getValue().toString(), stringObj.getCondition(),
545 stringObj.getApplyCondition(), fieldName);
560 public static String processStringObject(String valueStr, ConditionTypeEnum condition,
561 ConditionApplicationEnum applyCondition, String fieldName)
562 throws TskCoreException {
564 String fullClause =
"";
565 String lowerFieldName =
"lower(" + fieldName +
")";
567 if (valueStr.isEmpty()) {
568 throw new TskCoreException(
"Empty value field");
571 String[] parts = valueStr.split(
"##comma##");
573 for (String value : parts) {
574 String lowerValue = value.toLowerCase();
575 String partialClause;
576 if ((condition == null)
577 || (condition == ConditionTypeEnum.EQUALS)) {
578 partialClause = lowerFieldName +
"=\'" + lowerValue +
"\'";
579 }
else if (condition == ConditionTypeEnum.DOES_NOT_EQUAL) {
580 partialClause = lowerFieldName +
" !=\'%" + lowerValue +
"%\'";
581 }
else if (condition == ConditionTypeEnum.CONTAINS) {
582 partialClause = lowerFieldName +
" LIKE \'%" + lowerValue +
"%\'";
583 }
else if (condition == ConditionTypeEnum.DOES_NOT_CONTAIN) {
584 partialClause = lowerFieldName +
" NOT LIKE \'%" + lowerValue +
"%\'";
585 }
else if (condition == ConditionTypeEnum.STARTS_WITH) {
586 partialClause = lowerFieldName +
" LIKE \'" + lowerValue +
"%\'";
587 }
else if (condition == ConditionTypeEnum.ENDS_WITH) {
588 partialClause = lowerFieldName +
" LIKE \'%" + lowerValue +
"\'";
590 throw new TskCoreException(
"Could not process condition " + condition.value() +
" on " + fieldName);
593 if (fullClause.isEmpty()) {
595 if (parts.length > 1) {
598 if (applyCondition == ConditionApplicationEnum.NONE) {
599 fullClause +=
" NOT ";
601 fullClause += partialClause;
603 if (applyCondition == ConditionApplicationEnum.ALL) {
604 fullClause +=
" AND " + partialClause;
605 }
else if (applyCondition == ConditionApplicationEnum.NONE) {
606 fullClause +=
" AND NOT " + partialClause;
608 fullClause +=
" OR " + partialClause;
613 if (parts.length > 1) {
631 private static String processTimestampObject(DateTimeObjectPropertyType dateObj, String fieldName)
632 throws TskCoreException {
634 if (DatatypeEnum.DATE_TIME == dateObj.getDatatype()) {
637 String result = convertTimestampString(dateObj.getValue().toString());
638 return processNumericFields(result, dateObj.getCondition(), dateObj.getApplyCondition(), fieldName);
641 throw new TskCoreException(
"Found non DATE_TIME field on " + fieldName);
655 private static String convertTimestampString(String timestampStr)
656 throws TskCoreException {
659 if (timestampStr.length() > 0) {
660 String[] parts = timestampStr.split(
"##comma##");
662 for (
int i = 0; i < parts.length - 1; i++) {
663 long unixTime = convertTimestamp(parts[i]);
664 result += unixTime +
"##comma##";
666 result += convertTimestamp(parts[parts.length - 1]);
669 }
catch (java.text.ParseException ex) {
670 throw new TskCoreException(
"Error parsing timestamp string " + timestampStr);
683 private static String addClause(String a_clause, String a_newClause) {
685 if ((a_clause == null) || a_clause.isEmpty()) {
689 return (a_clause +
" AND " + a_newClause);