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;
 
   49 class EvalFileObj 
extends EvaluatableObject {
 
   51     private final FileObjectType obj;
 
   53     public EvalFileObj(FileObjectType a_obj, String a_id, String a_spacing) {
 
   60     @SuppressWarnings(
"deprecation")
 
   61     public synchronized ObservableResult evaluate() {
 
   65             case1 = Case.getCurrentCaseThrows();
 
   66         } 
catch (NoCurrentCaseException ex) { 
 
   67            return new ObservableResult(
id, 
"Exception while getting open case.", 
 
   68                             spacing, ObservableResult.ObservableState.FALSE, null);
 
   70         SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
 
   73         String whereClause = 
"";
 
   75         if (obj.getSizeInBytes() != null) {
 
   77                 String newClause = processULongObject(obj.getSizeInBytes(), 
"size"); 
 
   78                 whereClause = addClause(whereClause, newClause);
 
   79             } 
catch (TskCoreException ex) {
 
   80                 addWarning(ex.getLocalizedMessage());
 
   84         if (obj.getFileName() != null) {
 
   86                 String newClause = processStringObject(obj.getFileName(), 
"name"); 
 
   87                 whereClause = addClause(whereClause, newClause);
 
   88             } 
catch (TskCoreException ex) {
 
   89                 addWarning(ex.getLocalizedMessage());
 
   93         if (obj.getFileExtension() != null) {
 
   94             if ((obj.getFileExtension().getCondition() == null)
 
   95                     || (obj.getFileExtension().getCondition() == ConditionTypeEnum.EQUALS)) {
 
   96                 String newClause = 
"LOWER(name) LIKE LOWER(\'%" + obj.getFileExtension().getValue() + 
"\')"; 
 
   97                 whereClause = addClause(whereClause, newClause);
 
  100                         "Could not process condition " + obj.getFileExtension().getCondition().value() + 
" on file extension"); 
 
  104         if (obj.getFilePath() != null) {
 
  107                 String[] parts = obj.getFilePath().getValue().toString().split(
"##comma##"); 
 
  108                 String finalPathStr = 
"";
 
  110                 for (String filePath : parts) {
 
  112                     String currentFilePath = filePath;
 
  115                     if (currentFilePath.matches(
"^[A-Za-z]:.*")) {
 
  116                         currentFilePath = currentFilePath.substring(2);
 
  120                     currentFilePath = currentFilePath.replace(
"\\", 
"/");
 
  123                     if (!currentFilePath.startsWith(
"/")) {
 
  124                         currentFilePath = 
"/" + currentFilePath;
 
  128                     if (!currentFilePath.endsWith(
"/")) {
 
  129                         int lastSlash = currentFilePath.lastIndexOf(
'/');
 
  130                         if (lastSlash >= 0) {
 
  131                             currentFilePath = currentFilePath.substring(0, lastSlash + 1);
 
  136                     if (!finalPathStr.isEmpty()) {
 
  137                         finalPathStr += 
"##comma##"; 
 
  139                     finalPathStr += currentFilePath;
 
  142                 String newClause = processStringObject(finalPathStr, obj.getFilePath().getCondition(),
 
  143                         obj.getFilePath().getApplyCondition(), 
"parent_path"); 
 
  145                 whereClause = addClause(whereClause, newClause);
 
  146             } 
catch (TskCoreException ex) {
 
  147                 addWarning(ex.getLocalizedMessage());
 
  151         if (obj.getCreatedTime() != null) {
 
  153                 String newClause = processTimestampObject(obj.getCreatedTime(), 
"crtime"); 
 
  154                 whereClause = addClause(whereClause, newClause);
 
  155             } 
catch (TskCoreException ex) {
 
  156                 addWarning(ex.getLocalizedMessage());
 
  160         if (obj.getModifiedTime() != null) {
 
  162                 String newClause = processTimestampObject(obj.getModifiedTime(), 
"mtime"); 
 
  163                 whereClause = addClause(whereClause, newClause);
 
  164             } 
catch (TskCoreException ex) {
 
  165                 addWarning(ex.getLocalizedMessage());
 
  169         if (obj.getAccessedTime() != null) {
 
  171                 String newClause = processTimestampObject(obj.getAccessedTime(), 
"atime"); 
 
  172                 whereClause = addClause(whereClause, newClause);
 
  173             } 
catch (TskCoreException ex) {
 
  174                 addWarning(ex.getLocalizedMessage());
 
  178         if (obj.getHashes() != null) {
 
  179             for (HashType h : obj.getHashes().getHashes()) {
 
  180                 if (h.getSimpleHashValue() != null) {
 
  181                     if (h.getType().getValue().equals(
"MD5")) { 
 
  182                         String newClause = 
"";
 
  183                         if (h.getSimpleHashValue().getValue().toString().toLowerCase().contains(
"##comma##")) { 
 
  184                             String[] parts = h.getSimpleHashValue().getValue().toString().toLowerCase().split(
"##comma##"); 
 
  185                             String hashList = 
"";
 
  186                             for (String s : parts) {
 
  187                                 if (!hashList.isEmpty()) {
 
  190                                 hashList += 
"\'" + s + 
"\'";
 
  192                             newClause = 
"md5 IN (" + hashList + 
")"; 
 
  194                             newClause = 
"md5=\'" + h.getSimpleHashValue().getValue().toString().toLowerCase() + 
"\'"; 
 
  196                         whereClause = addClause(whereClause, newClause);
 
  198                         addWarning(
"Could not process hash type " + h.getType().getValue().toString()); 
 
  201                     addWarning(
"Could not process non-simple hash value"); 
 
  206         if (obj instanceof WindowsExecutableFileObjectType) {
 
  207             WindowsExecutableFileObjectType winExe = (WindowsExecutableFileObjectType) obj;
 
  208             if (winExe.getHeaders() != null) {
 
  209                 if (winExe.getHeaders().getFileHeader() != null) {
 
  210                     if (winExe.getHeaders().getFileHeader().getTimeDateStamp() != null) {
 
  212                             String result = convertTimestampString(winExe.getHeaders().getFileHeader().getTimeDateStamp().getValue().toString());
 
  213                             String newClause = processNumericFields(result,
 
  214                                     winExe.getHeaders().getFileHeader().getTimeDateStamp().getCondition(),
 
  215                                     winExe.getHeaders().getFileHeader().getTimeDateStamp().getApplyCondition(),
 
  217                             whereClause = addClause(whereClause, newClause);
 
  218                         } 
catch (TskCoreException ex) {
 
  219                             addWarning(ex.getLocalizedMessage());
 
  226         String unsupportedFields = listUnsupportedFields();
 
  227         if (!unsupportedFields.isEmpty()) {
 
  228             addWarning(
"Unsupported fields: " + unsupportedFields); 
 
  231         if (whereClause.length() > 0) {
 
  233                 List<AbstractFile> matchingFiles = sleuthkitCase.findAllFilesWhere(whereClause);
 
  235                 if (!matchingFiles.isEmpty()) {
 
  237                     if (listSecondaryFields().isEmpty()) {
 
  239                         List<StixArtifactData> artData = 
new ArrayList<StixArtifactData>();
 
  240                         for (AbstractFile a : matchingFiles) {
 
  241                             artData.add(
new StixArtifactData(a, 
id, 
"FileObject")); 
 
  244                         return new ObservableResult(
id, 
"FileObject: Found " + matchingFiles.size() + 
" matches for " + whereClause + getPrintableWarnings(), 
 
  245                                 spacing, ObservableResult.ObservableState.TRUE, artData);
 
  249                         List<AbstractFile> secondaryHits = 
new ArrayList<AbstractFile>();
 
  251                         for (AbstractFile file : matchingFiles) {
 
  252                             boolean passedTests = 
true;
 
  254                             if (obj.isIsMasqueraded() != null) {
 
  255                                 List<BlackboardArtifact> arts = file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED);
 
  256                                 boolean isMasq = 
false;
 
  257                                 if (!arts.isEmpty()) {
 
  261                                 if (obj.isIsMasqueraded() != isMasq) {
 
  267                             if (obj.getFileFormat() != null) {
 
  269                                 String formatsFound = file.getMIMEType();
 
  270                                 if (formatsFound != null) {
 
  271                                     if (!(formatsFound.equalsIgnoreCase(obj.getFileFormat().getValue().toString()))) {
 
  272                                         addWarning(
"Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() 
 
  273                                                 + 
" against " + formatsFound); 
 
  276                                     addWarning(
"Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() 
 
  277                                             + 
" (no file formats found)"); 
 
  286                                 secondaryHits.add(file);
 
  290                         if (secondaryHits.isEmpty()) {
 
  292                             return new ObservableResult(
id, 
"FileObject: Found " + matchingFiles.size() + 
" matches for " + whereClause 
 
  293                                     + 
" but none for secondary tests on " + listSecondaryFields() + getPrintableWarnings(), 
 
  294                                     spacing, ObservableResult.ObservableState.FALSE, null);
 
  296                             List<StixArtifactData> artData = 
new ArrayList<StixArtifactData>();
 
  297                             for (AbstractFile a : secondaryHits) {
 
  298                                 artData.add(
new StixArtifactData(a, 
id, 
"FileObject")); 
 
  300                             return new ObservableResult(
id, 
"FileObject: Found " + secondaryHits.size() + 
" matches for " + whereClause 
 
  301                                     + 
" and secondary tests on " + listSecondaryFields() + getPrintableWarnings(), 
 
  302                                     spacing, ObservableResult.ObservableState.TRUE, artData);
 
  306                     return new ObservableResult(
id, 
"FileObject: Found no matches for " + whereClause + getPrintableWarnings(), 
 
  307                             spacing, ObservableResult.ObservableState.FALSE, null);
 
  309             } 
catch (TskCoreException ex) {
 
  310                 return new ObservableResult(
id, 
"FileObject: Exception during evaluation: " + ex.getLocalizedMessage(), 
 
  311                         spacing, ObservableResult.ObservableState.INDETERMINATE, null);
 
  317         return new ObservableResult(
id, 
"FileObject: No evaluatable fields " + getPrintableWarnings(), spacing, 
 
  318                 ObservableResult.ObservableState.INDETERMINATE, null);
 
  327     private String listSecondaryFields() {
 
  328         String secondaryFields = 
"";
 
  330         if (obj.isIsMasqueraded() != null) {
 
  331             secondaryFields += 
"is_masqueraded "; 
 
  334         if (obj.getFileFormat() != null) {
 
  335             secondaryFields += 
"File_Format "; 
 
  338         return secondaryFields;
 
  346     private String listUnsupportedFields() {
 
  347         String unsupportedFields = 
"";
 
  349         if (obj.isIsPacked() != null) {
 
  350             unsupportedFields += 
"is_packed "; 
 
  352         if (obj.getDevicePath() != null) {
 
  353             unsupportedFields += 
"Device_Path "; 
 
  355         if (obj.getFullPath() != null) {
 
  356             unsupportedFields += 
"Full_Path "; 
 
  358         if (obj.getMagicNumber() != null) {
 
  359             unsupportedFields += 
"Magic_Number "; 
 
  361         if (obj.getDigitalSignatures() != null) {
 
  362             unsupportedFields += 
"Digital_Signatures "; 
 
  364         if (obj.getFileAttributesList() != null) {
 
  365             unsupportedFields += 
"File_Attributes_List "; 
 
  367         if (obj.getPermissions() != null) {
 
  368             unsupportedFields += 
"Permissions "; 
 
  370         if (obj.getUserOwner() != null) {
 
  371             unsupportedFields += 
"User_Owner "; 
 
  373         if (obj.getPackerList() != null) {
 
  374             unsupportedFields += 
"Packer_List "; 
 
  376         if (obj.getPeakEntropy() != null) {
 
  377             unsupportedFields += 
"Peak_Entropy "; 
 
  379         if (obj.getSymLinks() != null) {
 
  380             unsupportedFields += 
"Sym_Links "; 
 
  382         if (obj.getByteRuns() != null) {
 
  383             unsupportedFields += 
"Bytes_Runs "; 
 
  385         if (obj.getExtractedFeatures() != null) {
 
  386             unsupportedFields += 
"Extracted_Features "; 
 
  388         if (obj.getEncryptionAlgorithm() != null) {
 
  389             unsupportedFields += 
"Encryption_Algorithm "; 
 
  391         if (obj.getDecryptionKey() != null) {
 
  392             unsupportedFields += 
"Decryption_Key "; 
 
  394         if (obj.getCompressionMethod() != null) {
 
  395             unsupportedFields += 
"Compression_Method "; 
 
  397         if (obj.getCompressionVersion() != null) {
 
  398             unsupportedFields += 
"Compression_Version "; 
 
  400         if (obj.getCompressionComment() != null) {
 
  401             unsupportedFields += 
"Compression_Comment "; 
 
  404         return unsupportedFields;
 
  416     private static long convertTimestamp(String timeStr) 
throws ParseException {
 
  417         SimpleDateFormat dateFormat = 
new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss'Z'"); 
 
  418         dateFormat.setTimeZone(TimeZone.getTimeZone(
"GMT")); 
 
  419         Date parsedDate = dateFormat.parse(timeStr);
 
  421         Long unixTime = parsedDate.getTime() / 1000;
 
  437     private static String processULongObject(UnsignedLongObjectPropertyType longObj, String fieldName)
 
  438             throws TskCoreException {
 
  440         return processNumericFields(longObj.getValue().toString(), longObj.getCondition(),
 
  441                 longObj.getApplyCondition(), fieldName);
 
  456     private static String processNumericFields(String valueStr, ConditionTypeEnum typeCondition,
 
  457             ConditionApplicationEnum applyCondition, String fieldName)
 
  458             throws TskCoreException {
 
  460         if ((typeCondition == null)
 
  461                 || ((typeCondition != ConditionTypeEnum.INCLUSIVE_BETWEEN)
 
  462                 && (typeCondition != ConditionTypeEnum.EXCLUSIVE_BETWEEN))) {
 
  464             String fullClause = 
"";
 
  466             if (valueStr.isEmpty()) {
 
  467                 throw new TskCoreException(
"Empty value field"); 
 
  470             String[] parts = valueStr.split(
"##comma##"); 
 
  472             for (String valuePart : parts) {
 
  473                 String partialClause;
 
  475                 if ((typeCondition == null)
 
  476                         || (typeCondition == ConditionTypeEnum.EQUALS)) {
 
  478                     partialClause = fieldName + 
"=" + valuePart;
 
  479                 } 
else if (typeCondition == ConditionTypeEnum.DOES_NOT_EQUAL) {
 
  480                     partialClause = fieldName + 
"!=" + valuePart;
 
  481                 } 
else if (typeCondition == ConditionTypeEnum.GREATER_THAN) {
 
  482                     partialClause = fieldName + 
">" + valuePart;
 
  483                 } 
else if (typeCondition == ConditionTypeEnum.GREATER_THAN_OR_EQUAL) {
 
  484                     partialClause = fieldName + 
">=" + valuePart;
 
  485                 } 
else if (typeCondition == ConditionTypeEnum.LESS_THAN) {
 
  486                     partialClause = fieldName + 
"<" + valuePart;
 
  487                 } 
else if (typeCondition == ConditionTypeEnum.LESS_THAN_OR_EQUAL) {
 
  488                     partialClause = fieldName + 
"<=" + valuePart;
 
  490                     throw new TskCoreException(
"Could not process condition " + typeCondition.value() + 
" on " + fieldName); 
 
  493                 if (fullClause.isEmpty()) {
 
  495                     if (parts.length > 1) {
 
  498                     if (applyCondition == ConditionApplicationEnum.NONE) {
 
  499                         fullClause += 
" NOT "; 
 
  501                     fullClause += partialClause;
 
  503                     if (applyCondition == ConditionApplicationEnum.ALL) {
 
  504                         fullClause += 
" AND " + partialClause; 
 
  505                     } 
else if (applyCondition == ConditionApplicationEnum.NONE) {
 
  506                         fullClause += 
" AND NOT " + partialClause; 
 
  508                         fullClause += 
" OR " + partialClause; 
 
  513             if (parts.length > 1) {
 
  520             if (typeCondition == ConditionTypeEnum.INCLUSIVE_BETWEEN) {
 
  521                 String[] parts = valueStr.split(
"##comma##"); 
 
  522                 if (parts.length != 2) {
 
  523                     throw new TskCoreException(
"Unexpected number of arguments in INCLUSIVE_BETWEEN on " + fieldName 
 
  524                             + 
"(" + valueStr + 
")");
 
  526                 return (fieldName + 
">=" + parts[0] + 
" AND " + fieldName + 
"<=" + parts[1]); 
 
  528                 String[] parts = valueStr.split(
"##comma##"); 
 
  529                 if (parts.length != 2) {
 
  530                     throw new TskCoreException(
"Unexpected number of arguments in EXCLUSIVE_BETWEEN on " + fieldName 
 
  531                             + 
"(" + valueStr + 
")");
 
  533                 return (fieldName + 
">" + parts[0] + 
" AND " + fieldName + 
"<" + parts[1]); 
 
  548     private static String processStringObject(StringObjectPropertyType stringObj, String fieldName)
 
  549             throws TskCoreException {
 
  551         return processStringObject(stringObj.getValue().toString(), stringObj.getCondition(),
 
  552                 stringObj.getApplyCondition(), fieldName);
 
  567     public static String processStringObject(String valueStr, ConditionTypeEnum condition,
 
  568             ConditionApplicationEnum applyCondition, String fieldName)
 
  569             throws TskCoreException {
 
  571         String fullClause = 
"";
 
  572         String lowerFieldName = 
"lower(" + fieldName + 
")"; 
 
  574         if (valueStr.isEmpty()) {
 
  575             throw new TskCoreException(
"Empty value field"); 
 
  578         String[] parts = valueStr.split(
"##comma##"); 
 
  580         for (String value : parts) {
 
  581             String lowerValue = value.toLowerCase();
 
  582             String partialClause;
 
  583             if ((condition == null)
 
  584                     || (condition == ConditionTypeEnum.EQUALS)) {
 
  585                 partialClause = lowerFieldName + 
"=\'" + lowerValue + 
"\'";
 
  586             } 
else if (condition == ConditionTypeEnum.DOES_NOT_EQUAL) {
 
  587                 partialClause = lowerFieldName + 
" !=\'%" + lowerValue + 
"%\'";
 
  588             } 
else if (condition == ConditionTypeEnum.CONTAINS) {
 
  589                 partialClause = lowerFieldName + 
" LIKE \'%" + lowerValue + 
"%\'"; 
 
  590             } 
else if (condition == ConditionTypeEnum.DOES_NOT_CONTAIN) {
 
  591                 partialClause = lowerFieldName + 
" NOT LIKE \'%" + lowerValue + 
"%\'"; 
 
  592             } 
else if (condition == ConditionTypeEnum.STARTS_WITH) {
 
  593                 partialClause = lowerFieldName + 
" LIKE \'" + lowerValue + 
"%\'"; 
 
  594             } 
else if (condition == ConditionTypeEnum.ENDS_WITH) {
 
  595                 partialClause = lowerFieldName + 
" LIKE \'%" + lowerValue + 
"\'"; 
 
  597                 throw new TskCoreException(
"Could not process condition " + condition.value() + 
" on " + fieldName); 
 
  600             if (fullClause.isEmpty()) {
 
  602                 if (parts.length > 1) {
 
  605                 if (applyCondition == ConditionApplicationEnum.NONE) {
 
  606                     fullClause += 
" NOT "; 
 
  608                 fullClause += partialClause;
 
  610                 if (applyCondition == ConditionApplicationEnum.ALL) {
 
  611                     fullClause += 
" AND " + partialClause; 
 
  612                 } 
else if (applyCondition == ConditionApplicationEnum.NONE) {
 
  613                     fullClause += 
" AND NOT " + partialClause; 
 
  615                     fullClause += 
" OR " + partialClause; 
 
  620         if (parts.length > 1) {
 
  638     private static String processTimestampObject(DateTimeObjectPropertyType dateObj, String fieldName)
 
  639             throws TskCoreException {
 
  641         if (DatatypeEnum.DATE_TIME == dateObj.getDatatype()) {
 
  644             String result = convertTimestampString(dateObj.getValue().toString());
 
  645             return processNumericFields(result, dateObj.getCondition(), dateObj.getApplyCondition(), fieldName);
 
  648             throw new TskCoreException(
"Found non DATE_TIME field on " + fieldName); 
 
  662     private static String convertTimestampString(String timestampStr)
 
  663             throws TskCoreException {
 
  666             if (timestampStr.length() > 0) {
 
  667                 String[] parts = timestampStr.split(
"##comma##"); 
 
  669                 for (
int i = 0; i < parts.length - 1; i++) {
 
  670                     long unixTime = convertTimestamp(parts[i]);
 
  671                     result += unixTime + 
"##comma##"; 
 
  673                 result += convertTimestamp(parts[parts.length - 1]);
 
  676         } 
catch (java.text.ParseException ex) {
 
  677             throw new TskCoreException(
"Error parsing timestamp string " + timestampStr); 
 
  690     private static String addClause(String a_clause, String a_newClause) {
 
  692         if ((a_clause == null) || a_clause.isEmpty()) {
 
  696         return (a_clause + 
" AND " + a_newClause);