Autopsy  4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
EvalFileObj.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.modules.stix;
20 
22 import org.sleuthkit.datamodel.SleuthkitCase;
23 import org.sleuthkit.datamodel.AbstractFile;
24 import org.sleuthkit.datamodel.BlackboardArtifact;
25 import org.sleuthkit.datamodel.BlackboardAttribute;
26 import org.sleuthkit.datamodel.TskCoreException;
27 
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;
35 
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;
44 
48 class EvalFileObj extends EvaluatableObject {
49 
50  private final FileObjectType obj;
51 
52  public EvalFileObj(FileObjectType a_obj, String a_id, String a_spacing) {
53  obj = a_obj;
54  id = a_id;
55  spacing = a_spacing;
56  }
57 
58  @Override
59  @SuppressWarnings("deprecation")
60  public synchronized ObservableResult evaluate() {
61 
62  Case case1 = Case.getCurrentCase();
63  SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
64 
65  setWarnings("");
66  String whereClause = "";
67 
68  if (obj.getSizeInBytes() != null) {
69  try {
70  String newClause = processULongObject(obj.getSizeInBytes(), "size"); //NON-NLS
71  whereClause = addClause(whereClause, newClause);
72  } catch (TskCoreException ex) {
73  addWarning(ex.getLocalizedMessage());
74  }
75  }
76 
77  if (obj.getFileName() != null) {
78  try {
79  String newClause = processStringObject(obj.getFileName(), "name"); //NON-NLS
80  whereClause = addClause(whereClause, newClause);
81  } catch (TskCoreException ex) {
82  addWarning(ex.getLocalizedMessage());
83  }
84  }
85 
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() + "\')"; //NON-NLS
90  whereClause = addClause(whereClause, newClause);
91  } else {
92  addWarning(
93  "Could not process condition " + obj.getFileExtension().getCondition().value() + " on file extension"); //NON-NLS
94  }
95  }
96 
97  if (obj.getFilePath() != null) {
98  try {
99 
100  String[] parts = obj.getFilePath().getValue().toString().split("##comma##"); //NON-NLS
101  String finalPathStr = "";
102 
103  for (String filePath : parts) {
104  // First, we need to normalize the path
105  String currentFilePath = filePath;
106 
107  // Remove the drive letter
108  if (currentFilePath.matches("^[A-Za-z]:.*")) {
109  currentFilePath = currentFilePath.substring(2);
110  }
111 
112  // Change any backslashes to forward slashes
113  currentFilePath = currentFilePath.replace("\\", "/");
114 
115  // The path needs to start with a slash
116  if (!currentFilePath.startsWith("/")) {
117  currentFilePath = "/" + currentFilePath;
118  }
119 
120  // If the path does not end in a slash, the final part should be the file name.
121  if (!currentFilePath.endsWith("/")) {
122  int lastSlash = currentFilePath.lastIndexOf('/');
123  if (lastSlash >= 0) {
124  currentFilePath = currentFilePath.substring(0, lastSlash + 1);
125  }
126  }
127 
128  // Reconstruct the path string (which may be multi-part)
129  if (!finalPathStr.isEmpty()) {
130  finalPathStr += "##comma##"; //NON-NLS
131  }
132  finalPathStr += currentFilePath;
133  }
134 
135  String newClause = processStringObject(finalPathStr, obj.getFilePath().getCondition(),
136  obj.getFilePath().getApplyCondition(), "parent_path"); //NON-NLS
137 
138  whereClause = addClause(whereClause, newClause);
139  } catch (TskCoreException ex) {
140  addWarning(ex.getLocalizedMessage());
141  }
142  }
143 
144  if (obj.getCreatedTime() != null) {
145  try {
146  String newClause = processTimestampObject(obj.getCreatedTime(), "crtime"); //NON-NLS
147  whereClause = addClause(whereClause, newClause);
148  } catch (TskCoreException ex) {
149  addWarning(ex.getLocalizedMessage());
150  }
151  }
152 
153  if (obj.getModifiedTime() != null) {
154  try {
155  String newClause = processTimestampObject(obj.getModifiedTime(), "mtime"); //NON-NLS
156  whereClause = addClause(whereClause, newClause);
157  } catch (TskCoreException ex) {
158  addWarning(ex.getLocalizedMessage());
159  }
160  }
161 
162  if (obj.getAccessedTime() != null) {
163  try {
164  String newClause = processTimestampObject(obj.getAccessedTime(), "atime"); //NON-NLS
165  whereClause = addClause(whereClause, newClause);
166  } catch (TskCoreException ex) {
167  addWarning(ex.getLocalizedMessage());
168  }
169  }
170 
171  if (obj.getHashes() != null) {
172  for (HashType h : obj.getHashes().getHashes()) {
173  if (h.getSimpleHashValue() != null) {
174  if (h.getType().getValue().equals("MD5")) { //NON-NLS
175  String newClause = "";
176  if (h.getSimpleHashValue().getValue().toString().toLowerCase().contains("##comma##")) { //NON-NLS
177  String[] parts = h.getSimpleHashValue().getValue().toString().toLowerCase().split("##comma##"); //NON-NLS
178  String hashList = "";
179  for (String s : parts) {
180  if (!hashList.isEmpty()) {
181  hashList += ", ";
182  }
183  hashList += "\'" + s + "\'";
184  }
185  newClause = "md5 IN (" + hashList + ")"; //NON-NLS
186  } else {
187  newClause = "md5=\'" + h.getSimpleHashValue().getValue().toString().toLowerCase() + "\'"; //NON-NLS
188  }
189  whereClause = addClause(whereClause, newClause);
190  } else {
191  addWarning("Could not process hash type " + h.getType().getValue().toString()); //NON-NLS
192  }
193  } else {
194  addWarning("Could not process non-simple hash value"); //NON-NLS
195  }
196  }
197  }
198 
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) {
204  try {
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(),
209  "crtime"); //NON-NLS
210  whereClause = addClause(whereClause, newClause);
211  } catch (TskCoreException ex) {
212  addWarning(ex.getLocalizedMessage());
213  }
214  }
215  }
216  }
217  }
218 
219  String unsupportedFields = listUnsupportedFields();
220  if (!unsupportedFields.isEmpty()) {
221  addWarning("Unsupported fields: " + unsupportedFields); //NON-NLS
222  }
223 
224  if (whereClause.length() > 0) {
225  try {
226  List<AbstractFile> matchingFiles = sleuthkitCase.findAllFilesWhere(whereClause);
227 
228  if (!matchingFiles.isEmpty()) {
229 
230  if (listSecondaryFields().isEmpty()) {
231 
232  List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
233  for (AbstractFile a : matchingFiles) {
234  artData.add(new StixArtifactData(a, id, "FileObject")); //NON-NLS
235  }
236 
237  return new ObservableResult(id, "FileObject: Found " + matchingFiles.size() + " matches for " + whereClause + getPrintableWarnings(), //NON-NLS
238  spacing, ObservableResult.ObservableState.TRUE, artData);
239  } else {
240 
241  // We need to tag the matching files in Autopsy, so keep track of them
242  List<AbstractFile> secondaryHits = new ArrayList<AbstractFile>();
243 
244  for (AbstractFile file : matchingFiles) {
245  boolean passedTests = true;
246 
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()) {
251  isMasq = true;
252  }
253 
254  if (obj.isIsMasqueraded() != isMasq) {
255  passedTests = false;
256  }
257 
258  }
259 
260  if (obj.getFileFormat() != null) {
261 
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() //NON-NLS
266  + " against " + formatsFound); //NON-NLS
267  }
268  } else {
269  addWarning("Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() //NON-NLS
270  + " (no file formats found)"); //NON-NLS
271  }
272  // It looks like the STIX file formats can be different than what Autopsy stores
273  // (mime vs. unix file), so don't kill a file based on this field not matching.
274  //if (!foundMatch) {
275  // passedTests = false;
276  //}
277  }
278  if (passedTests) {
279  secondaryHits.add(file);
280  }
281  }
282 
283  if (secondaryHits.isEmpty()) {
284 
285  return new ObservableResult(id, "FileObject: Found " + matchingFiles.size() + " matches for " + whereClause //NON-NLS
286  + " but none for secondary tests on " + listSecondaryFields() + getPrintableWarnings(), //NON-NLS
287  spacing, ObservableResult.ObservableState.FALSE, null);
288  } else {
289  List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
290  for (AbstractFile a : secondaryHits) {
291  artData.add(new StixArtifactData(a, id, "FileObject")); //NON-NLS
292  }
293  return new ObservableResult(id, "FileObject: Found " + secondaryHits.size() + " matches for " + whereClause //NON-NLS
294  + " and secondary tests on " + listSecondaryFields() + getPrintableWarnings(), //NON-NLS
295  spacing, ObservableResult.ObservableState.TRUE, artData);
296  }
297  }
298  } else {
299  return new ObservableResult(id, "FileObject: Found no matches for " + whereClause + getPrintableWarnings(), //NON-NLS
300  spacing, ObservableResult.ObservableState.FALSE, null);
301  }
302  } catch (TskCoreException ex) {
303  return new ObservableResult(id, "FileObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
304  spacing, ObservableResult.ObservableState.INDETERMINATE, null);
305  }
306  } else {
307 
308  }
309 
310  return new ObservableResult(id, "FileObject: No evaluatable fields " + getPrintableWarnings(), spacing, //NON-NLS
311  ObservableResult.ObservableState.INDETERMINATE, null);
312  }
313 
320  private String listSecondaryFields() {
321  String secondaryFields = "";
322 
323  if (obj.isIsMasqueraded() != null) {
324  secondaryFields += "is_masqueraded "; //NON-NLS
325  }
326 
327  if (obj.getFileFormat() != null) {
328  secondaryFields += "File_Format "; //NON-NLS
329  }
330 
331  return secondaryFields;
332  }
333 
339  private String listUnsupportedFields() {
340  String unsupportedFields = "";
341 
342  if (obj.isIsPacked() != null) {
343  unsupportedFields += "is_packed "; //NON-NLS
344  }
345  if (obj.getDevicePath() != null) {
346  unsupportedFields += "Device_Path "; //NON-NLS
347  }
348  if (obj.getFullPath() != null) {
349  unsupportedFields += "Full_Path "; //NON-NLS
350  }
351  if (obj.getMagicNumber() != null) {
352  unsupportedFields += "Magic_Number "; //NON-NLS
353  }
354  if (obj.getDigitalSignatures() != null) {
355  unsupportedFields += "Digital_Signatures "; //NON-NLS
356  }
357  if (obj.getFileAttributesList() != null) {
358  unsupportedFields += "File_Attributes_List "; //NON-NLS
359  }
360  if (obj.getPermissions() != null) {
361  unsupportedFields += "Permissions "; //NON-NLS
362  }
363  if (obj.getUserOwner() != null) {
364  unsupportedFields += "User_Owner "; //NON-NLS
365  }
366  if (obj.getPackerList() != null) {
367  unsupportedFields += "Packer_List "; //NON-NLS
368  }
369  if (obj.getPeakEntropy() != null) {
370  unsupportedFields += "Peak_Entropy "; //NON-NLS
371  }
372  if (obj.getSymLinks() != null) {
373  unsupportedFields += "Sym_Links "; //NON-NLS
374  }
375  if (obj.getByteRuns() != null) {
376  unsupportedFields += "Bytes_Runs "; //NON-NLS
377  }
378  if (obj.getExtractedFeatures() != null) {
379  unsupportedFields += "Extracted_Features "; //NON-NLS
380  }
381  if (obj.getEncryptionAlgorithm() != null) {
382  unsupportedFields += "Encryption_Algorithm "; //NON-NLS
383  }
384  if (obj.getDecryptionKey() != null) {
385  unsupportedFields += "Decryption_Key "; //NON-NLS
386  }
387  if (obj.getCompressionMethod() != null) {
388  unsupportedFields += "Compression_Method "; //NON-NLS
389  }
390  if (obj.getCompressionVersion() != null) {
391  unsupportedFields += "Compression_Version "; //NON-NLS
392  }
393  if (obj.getCompressionComment() != null) {
394  unsupportedFields += "Compression_Comment "; //NON-NLS
395  }
396 
397  return unsupportedFields;
398  }
399 
409  private static long convertTimestamp(String timeStr) throws ParseException {
410  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); //NON-NLS
411  dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); //NON-NLS
412  Date parsedDate = dateFormat.parse(timeStr);
413 
414  Long unixTime = parsedDate.getTime() / 1000;
415 
416  return unixTime;
417  }
418 
430  private static String processULongObject(UnsignedLongObjectPropertyType longObj, String fieldName)
431  throws TskCoreException {
432 
433  return processNumericFields(longObj.getValue().toString(), longObj.getCondition(),
434  longObj.getApplyCondition(), fieldName);
435  }
436 
449  private static String processNumericFields(String valueStr, ConditionTypeEnum typeCondition,
450  ConditionApplicationEnum applyCondition, String fieldName)
451  throws TskCoreException {
452 
453  if ((typeCondition == null)
454  || ((typeCondition != ConditionTypeEnum.INCLUSIVE_BETWEEN)
455  && (typeCondition != ConditionTypeEnum.EXCLUSIVE_BETWEEN))) {
456 
457  String fullClause = "";
458 
459  if (valueStr.isEmpty()) {
460  throw new TskCoreException("Empty value field"); //NON-NLS
461  }
462 
463  String[] parts = valueStr.split("##comma##"); //NON-NLS
464 
465  for (String valuePart : parts) {
466  String partialClause;
467 
468  if ((typeCondition == null)
469  || (typeCondition == ConditionTypeEnum.EQUALS)) {
470 
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;
482  } else {
483  throw new TskCoreException("Could not process condition " + typeCondition.value() + " on " + fieldName); //NON-NLS
484  }
485 
486  if (fullClause.isEmpty()) {
487 
488  if (parts.length > 1) {
489  fullClause += "( ";
490  }
491  if (applyCondition == ConditionApplicationEnum.NONE) {
492  fullClause += " NOT "; //NON-NLS
493  }
494  fullClause += partialClause;
495  } else {
496  if (applyCondition == ConditionApplicationEnum.ALL) {
497  fullClause += " AND " + partialClause; //NON-NLS
498  } else if (applyCondition == ConditionApplicationEnum.NONE) {
499  fullClause += " AND NOT " + partialClause; //NON-NLS
500  } else {
501  fullClause += " OR " + partialClause; //NON-NLS
502  }
503  }
504  }
505 
506  if (parts.length > 1) {
507  fullClause += " )";
508  }
509 
510  return fullClause;
511  } else {
512  // I don't think apply conditions make sense for these two.
513  if (typeCondition == ConditionTypeEnum.INCLUSIVE_BETWEEN) {
514  String[] parts = valueStr.split("##comma##"); //NON-NLS
515  if (parts.length != 2) {
516  throw new TskCoreException("Unexpected number of arguments in INCLUSIVE_BETWEEN on " + fieldName //NON-NLS
517  + "(" + valueStr + ")");
518  }
519  return (fieldName + ">=" + parts[0] + " AND " + fieldName + "<=" + parts[1]); //NON-NLS
520  } else {
521  String[] parts = valueStr.split("##comma##"); //NON-NLS
522  if (parts.length != 2) {
523  throw new TskCoreException("Unexpected number of arguments in EXCLUSIVE_BETWEEN on " + fieldName //NON-NLS
524  + "(" + valueStr + ")");
525  }
526  return (fieldName + ">" + parts[0] + " AND " + fieldName + "<" + parts[1]); //NON-NLS
527  }
528  }
529  }
530 
541  private static String processStringObject(StringObjectPropertyType stringObj, String fieldName)
542  throws TskCoreException {
543 
544  return processStringObject(stringObj.getValue().toString(), stringObj.getCondition(),
545  stringObj.getApplyCondition(), fieldName);
546  }
547 
560  public static String processStringObject(String valueStr, ConditionTypeEnum condition,
561  ConditionApplicationEnum applyCondition, String fieldName)
562  throws TskCoreException {
563 
564  String fullClause = "";
565  String lowerFieldName = "lower(" + fieldName + ")"; //NON-NLS
566 
567  if (valueStr.isEmpty()) {
568  throw new TskCoreException("Empty value field"); //NON-NLS
569  }
570 
571  String[] parts = valueStr.split("##comma##"); //NON-NLS
572 
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 + "%\'"; //NON-NLS
583  } else if (condition == ConditionTypeEnum.DOES_NOT_CONTAIN) {
584  partialClause = lowerFieldName + " NOT LIKE \'%" + lowerValue + "%\'"; //NON-NLS
585  } else if (condition == ConditionTypeEnum.STARTS_WITH) {
586  partialClause = lowerFieldName + " LIKE \'" + lowerValue + "%\'"; //NON-NLS
587  } else if (condition == ConditionTypeEnum.ENDS_WITH) {
588  partialClause = lowerFieldName + " LIKE \'%" + lowerValue + "\'"; //NON-NLS
589  } else {
590  throw new TskCoreException("Could not process condition " + condition.value() + " on " + fieldName); //NON-NLS
591  }
592 
593  if (fullClause.isEmpty()) {
594 
595  if (parts.length > 1) {
596  fullClause += "( ";
597  }
598  if (applyCondition == ConditionApplicationEnum.NONE) {
599  fullClause += " NOT "; //NON-NLS
600  }
601  fullClause += partialClause;
602  } else {
603  if (applyCondition == ConditionApplicationEnum.ALL) {
604  fullClause += " AND " + partialClause; //NON-NLS
605  } else if (applyCondition == ConditionApplicationEnum.NONE) {
606  fullClause += " AND NOT " + partialClause; //NON-NLS
607  } else {
608  fullClause += " OR " + partialClause; //NON-NLS
609  }
610  }
611  }
612 
613  if (parts.length > 1) {
614  fullClause += " )";
615  }
616 
617  return fullClause;
618  }
619 
631  private static String processTimestampObject(DateTimeObjectPropertyType dateObj, String fieldName)
632  throws TskCoreException {
633 
634  if (DatatypeEnum.DATE_TIME == dateObj.getDatatype()) {
635 
636  // Change the string into unix timestamps
637  String result = convertTimestampString(dateObj.getValue().toString());
638  return processNumericFields(result, dateObj.getCondition(), dateObj.getApplyCondition(), fieldName);
639 
640  } else {
641  throw new TskCoreException("Found non DATE_TIME field on " + fieldName); //NON-NLS
642  }
643  }
644 
655  private static String convertTimestampString(String timestampStr)
656  throws TskCoreException {
657  try {
658  String result = "";
659  if (timestampStr.length() > 0) {
660  String[] parts = timestampStr.split("##comma##"); //NON-NLS
661 
662  for (int i = 0; i < parts.length - 1; i++) {
663  long unixTime = convertTimestamp(parts[i]);
664  result += unixTime + "##comma##"; //NON-NLS
665  }
666  result += convertTimestamp(parts[parts.length - 1]);
667  }
668  return result;
669  } catch (java.text.ParseException ex) {
670  throw new TskCoreException("Error parsing timestamp string " + timestampStr); //NON-NLS
671  }
672 
673  }
674 
683  private static String addClause(String a_clause, String a_newClause) {
684 
685  if ((a_clause == null) || a_clause.isEmpty()) {
686  return a_newClause;
687  }
688 
689  return (a_clause + " AND " + a_newClause); //NON-NLS
690  }
691 
692 }

Copyright © 2012-2016 Basis Technology. Generated on: Mon Jan 2 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.