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);