Autopsy  3.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
SevenZipExtractor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2014 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.embeddedfileextractor;
20 
21 import java.io.BufferedOutputStream;
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.List;
31 import java.util.logging.Level;
32 import net.sf.sevenzipjbinding.ArchiveFormat;
33 import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
34 import net.sf.sevenzipjbinding.ISequentialOutStream;
35 import net.sf.sevenzipjbinding.ISevenZipInArchive;
36 import net.sf.sevenzipjbinding.SevenZip;
37 import net.sf.sevenzipjbinding.SevenZipException;
38 import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
39 import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
40 import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
41 import org.netbeans.api.progress.ProgressHandle;
42 import org.netbeans.api.progress.ProgressHandleFactory;
43 import org.openide.util.Exceptions;
44 import org.openide.util.NbBundle;
63 
64 class SevenZipExtractor {
65 
66  private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName());
67  private IngestServices services = IngestServices.getInstance();
68  private final IngestJobContext context;
69  private final FileTypeDetector fileTypeDetector;
70  static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS
71  //encryption type strings
72  private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
73  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
74  private static final String ENCRYPTION_FULL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
75  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull");
76  //zip bomb detection
77  private static final int MAX_DEPTH = 4;
78  private static final int MAX_COMPRESSION_RATIO = 600;
79  private static final long MIN_COMPRESSION_RATIO_SIZE = 500 * 1000000L;
80  private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L; //1GB
81  //counts archive depth
82  private ArchiveDepthCountTree archiveDepthCountTree;
83 
84  private String moduleDirRelative;
85  private String moduleDirAbsolute;
86 
87  private String getLocalRootAbsPath(String uniqueArchiveFileName) {
88  return moduleDirAbsolute + File.separator + uniqueArchiveFileName;
89  }
94  ZIP("application/zip"),
95  SEVENZ("application/x-7z-compressed"),
96  GZIP("application/gzip"),
97  XGZIP("application/x-gzip"),
98  XBZIP2("application/x-bzip2"),
99  XTAR("application/x-tar");
100 
101  private final String mimeType;
102 
103  SupportedArchiveExtractionFormats(final String mimeType) {
104  this.mimeType = mimeType;
105  }
106 
107  @Override
108  public String toString() {
109  return this.mimeType;
110  }
111  // TODO Expand to support more formats after upgrading Tika
112  }
113 
114  SevenZipExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws IngestModuleException {
115  if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
116  try {
117  SevenZip.initSevenZipFromPlatformJAR();
118  String platform = SevenZip.getUsedPlatform();
119  logger.log(Level.INFO, "7-Zip-JBinding library was initialized on supported platform: {0}", platform); //NON-NLS
120  } catch (SevenZipNativeInitializationException e) {
121  logger.log(Level.SEVERE, "Error initializing 7-Zip-JBinding library", e); //NON-NLS
122  String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.msg",
123  EmbeddedFileExtractorModuleFactory.getModuleName());
124  String details = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errCantInitLib",
125  e.getMessage());
126  services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
127  throw new IngestModuleException(e.getMessage());
128  }
129  }
130  this.context = context;
131  this.fileTypeDetector = fileTypeDetector;
132  this.moduleDirRelative = moduleDirRelative;
133  this.moduleDirAbsolute = moduleDirAbsolute;
134  this.archiveDepthCountTree = new ArchiveDepthCountTree();
135  }
136 
146  boolean isSevenZipExtractionSupported(AbstractFile abstractFile) {
147  try {
148  String abstractFileMimeType = fileTypeDetector.getFileType(abstractFile);
149  for (SupportedArchiveExtractionFormats s : SupportedArchiveExtractionFormats.values()) {
150  if (s.toString().equals(abstractFileMimeType)) {
151  return true;
152  }
153  }
154 
155  return false;
156  } catch (TskCoreException ex) {
157  logger.log(Level.WARNING, "Error executing FileTypeDetector.getFileType()", ex); // NON-NLS
158  }
159 
160  // attempt extension matching
161  final String extension = abstractFile.getNameExtension();
162  for (String supportedExtension : SUPPORTED_EXTENSIONS) {
163  if (extension.equals(supportedExtension)) {
164  return true;
165  }
166  }
167 
168  return false;
169  }
170 
182  private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISimpleInArchiveItem archiveFileItem) {
183  try {
184  final Long archiveItemSize = archiveFileItem.getSize();
185 
186  //skip the check for small files
187  if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
188  return false;
189  }
190 
191  final Long archiveItemPackedSize = archiveFileItem.getPackedSize();
192 
193  if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
194  logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", new Object[]{archiveFile.getName(), archiveFileItem.getPath()}); //NON-NLS
195  return false;
196  }
197 
198  int cRatio = (int) (archiveItemSize / archiveItemPackedSize);
199 
200  if (cRatio >= MAX_COMPRESSION_RATIO) {
201  String itemName = archiveFileItem.getPath();
202  logger.log(Level.INFO, "Possible zip bomb detected, compression ration: {0} for in archive item: {1}", new Object[]{cRatio, itemName}); //NON-NLS
203  String msg = NbBundle.getMessage(this.getClass(),
204  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), itemName);
205  String path;
206  try {
207  path = archiveFile.getUniquePath();
208  } catch (TskCoreException ex) {
209  path = archiveFile.getParentPath() + archiveFile.getName();
210  }
211  String details = NbBundle.getMessage(this.getClass(),
212  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails", cRatio, path);
213  //MessageNotifyUtil.Notify.error(msg, details);
214  services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
215  return true;
216  } else {
217  return false;
218  }
219 
220  } catch (SevenZipException ex) {
221  logger.log(Level.SEVERE, "Error getting archive item size and cannot detect if zipbomb. ", ex); //NON-NLS
222  return false;
223  }
224  }
225 
233  private ArchiveFormat get7ZipOptions(AbstractFile archiveFile) {
234  // try to get the file type from the BB
235  String detectedFormat = null;
236  try {
237  ArrayList<BlackboardAttribute> attributes = archiveFile.getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG);
238  for (BlackboardAttribute attribute : attributes) {
239  detectedFormat = attribute.getValueString();
240  break;
241  }
242  } catch (TskCoreException ex) {
243  logger.log(Level.WARNING, "Couldn't obtain file attributes for file: " + archiveFile.toString(), ex); //NON-NLS
244  }
245 
246  if (detectedFormat == null) {
247  logger.log(Level.WARNING, "Could not detect format for file: " + archiveFile); //NON-NLS
248 
249  // if we don't have attribute info then use file extension
250  String extension = archiveFile.getNameExtension();
251  if ("rar".equals(extension)) //NON-NLS
252  {
253  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
254  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality
255  return RAR;
256  }
257 
258  // Otherwise open the archive using 7zip's built-in auto-detect functionality
259  return null;
260  } else if (detectedFormat.contains("application/x-rar-compressed")) //NON-NLS
261  {
262  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
263  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality
264  return RAR;
265  }
266 
267  // Otherwise open the archive using 7zip's built-in auto-detect functionality
268  return null;
269  }
270 
278  void unpack(AbstractFile archiveFile) {
279  String archiveFilePath;
280  try {
281  archiveFilePath = archiveFile.getUniquePath();
282  } catch (TskCoreException ex) {
283  archiveFilePath = archiveFile.getParentPath() + archiveFile.getName();
284  }
285 
286  //check if already has derived files, skip
287  try {
288  if (archiveFile.hasChildren()) {
289  //check if local unpacked dir exists
290  if (new File(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
291  logger.log(Level.INFO, "File already has been processed as it has children and local unpacked file, skipping: {0}", archiveFilePath); //NON-NLS
292  return;
293  }
294  }
295  } catch (TskCoreException e) {
296  logger.log(Level.INFO, "Error checking if file already has been processed, skipping: {0}", archiveFilePath); //NON-NLS
297  return;
298  }
299 
300 
301  List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
302 
303  //recursion depth check for zip bomb
304  final long archiveId = archiveFile.getId();
305  SevenZipExtractor.ArchiveDepthCountTree.Archive parentAr = archiveDepthCountTree.findArchive(archiveId);
306  if (parentAr == null) {
307  parentAr = archiveDepthCountTree.addArchive(null, archiveId);
308  } else if (parentAr.getDepth() == MAX_DEPTH) {
309  String msg = NbBundle.getMessage(this.getClass(),
310  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb", archiveFile.getName());
311  String details = NbBundle.getMessage(this.getClass(),
312  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
313  parentAr.getDepth(), archiveFilePath);
314  //MessageNotifyUtil.Notify.error(msg, details);
315  services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
316  return;
317  }
318 
319  boolean hasEncrypted = false;
320  boolean fullEncryption = true;
321 
322  ISevenZipInArchive inArchive = null;
323  SevenZipContentReadStream stream = null;
324 
325  final ProgressHandle progress = ProgressHandleFactory.createHandle(
326  NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.moduleName"));
327  int processedItems = 0;
328 
329  boolean progressStarted = false;
330  try {
331  stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile));
332 
333  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
334  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality.
335  // All other archive formats are still opened using 7zip built-in auto-detect functionality.
336  ArchiveFormat options = get7ZipOptions(archiveFile);
337  inArchive = SevenZip.openInArchive(options, stream);
338 
339  int numItems = inArchive.getNumberOfItems();
340  logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{archiveFilePath, numItems}); //NON-NLS
341  progress.start(numItems);
342  progressStarted = true;
343 
344  final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
345 
346  //setup the archive local root folder
347  final String uniqueArchiveFileName = EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile);
348  final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName);
349  final File localRoot = new File(localRootAbsPath);
350  if (!localRoot.exists()) {
351  try {
352  localRoot.mkdirs();
353  } catch (SecurityException e) {
354  logger.log(Level.SEVERE, "Error setting up output path for archive root: {0}", localRootAbsPath); //NON-NLS
355  //bail
356  return;
357  }
358  }
359 
360  //initialize tree hierarchy to keep track of unpacked file structure
361  SevenZipExtractor.UnpackedTree unpackedTree = new SevenZipExtractor.UnpackedTree(moduleDirRelative + "/" + uniqueArchiveFileName, archiveFile);
362 
363  long freeDiskSpace = services.getFreeDiskSpace();
364 
365  //unpack and process every item in archive
366  int itemNumber = 0;
367  for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
368  String pathInArchive = item.getPath();
369 
370  if (pathInArchive == null || pathInArchive.isEmpty()) {
371  //some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path
372  //handle this for .tar.gz and tgz but assuming the child is tar,
373  //otherwise, unpack using itemNumber as name
374 
375  //TODO this should really be signature based, not extension based
376  String archName = archiveFile.getName();
377  int dotI = archName.lastIndexOf(".");
378  String useName = null;
379  if (dotI != -1) {
380  String base = archName.substring(0, dotI);
381  String ext = archName.substring(dotI);
382  switch (ext) {
383  case ".gz": //NON-NLS
384  useName = base;
385  break;
386  case ".tgz": //NON-NLS
387  useName = base + ".tar"; //NON-NLS
388  break;
389  }
390  }
391 
392  if (useName == null) {
393  pathInArchive = "/" + archName + "/" + Integer.toString(itemNumber);
394  } else {
395  pathInArchive = "/" + useName;
396  }
397 
398  String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
399  archiveFilePath, pathInArchive);
400  logger.log(Level.WARNING, msg);
401 
402  }
403  ++itemNumber;
404  logger.log(Level.INFO, "Extracted item path: {0}", pathInArchive); //NON-NLS
405 
406  //check if possible zip bomb
407  if (isZipBombArchiveItemCheck(archiveFile, item)) {
408  continue; //skip the item
409  }
410 
411  //find this node in the hierarchy, create if needed
412  SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
413 
414  String fileName = unpackedNode.getFileName();
415 
416  //update progress bar
417  progress.progress(archiveFile.getName() + ": " + fileName, processedItems);
418 
419  final boolean isEncrypted = item.isEncrypted();
420  final boolean isDir = item.isFolder();
421 
422  if (isEncrypted) {
423  logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS
424  hasEncrypted = true;
425  continue;
426  } else {
427  fullEncryption = false;
428  }
429 
430  final Long size = item.getSize();
431  if (size == null) {
432  // If the size property cannot be determined, out-of-disk-space
433  // situations cannot be ascertained.
434  // Hence skip this file.
435  logger.log(Level.WARNING, "Size cannot be determined. Skipping file in archive: {0}", pathInArchive); //NON-NLS
436  continue;
437  }
438 
439  //check if unpacking this file will result in out of disk space
440  //this is additional to zip bomb prevention mechanism
441  if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && size > 0) { //if known free space and file not empty
442  long newDiskSpace = freeDiskSpace - size;
443  if (newDiskSpace < MIN_FREE_DISK_SPACE) {
444  String msg = NbBundle.getMessage(this.getClass(),
445  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
446  archiveFilePath, fileName);
447  String details = NbBundle.getMessage(this.getClass(),
448  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
449  //MessageNotifyUtil.Notify.error(msg, details);
450  services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
451  logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new Object[]{archiveFilePath, fileName}); //NON-NLS
452  logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
453  continue; //skip this file
454  } else {
455  //update est. disk space during this archive, so we don't need to poll for every file extracted
456  freeDiskSpace = newDiskSpace;
457  }
458  }
459 
460  final String uniqueExtractedName = uniqueArchiveFileName + File.separator + (item.getItemIndex() / 1000) + File.separator + item.getItemIndex() + new File(pathInArchive).getName();
461 
462  //final String localRelPath = unpackDir + File.separator + localFileRelPath;
463  final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
464  final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
465 
466  //create local dirs and empty files before extracted
467  File localFile = new java.io.File(localAbsPath);
468  //cannot rely on files in top-bottom order
469  if (!localFile.exists()) {
470  try {
471  if (isDir) {
472  localFile.mkdirs();
473  } else {
474  localFile.getParentFile().mkdirs();
475  try {
476  localFile.createNewFile();
477  } catch (IOException ex) {
478  logger.log(Level.SEVERE, "Error creating extracted file: " + localFile.getAbsolutePath(), ex); //NON-NLS
479  }
480  }
481  } catch (SecurityException e) {
482  logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", pathInArchive); //NON-NLS
483  //TODO consider bail out / msg to the user
484  }
485  }
486 
487  // skip the rest of this loop if we couldn't create the file
488  if (localFile.exists() == false) {
489  continue;
490  }
491 
492  final Date createTime = item.getCreationTime();
493  final Date accessTime = item.getLastAccessTime();
494  final Date writeTime = item.getLastWriteTime();
495  final long createtime = createTime == null ? 0L : createTime.getTime() / 1000;
496  final long modtime = writeTime == null ? 0L : writeTime.getTime() / 1000;
497  final long accesstime = accessTime == null ? 0L : accessTime.getTime() / 1000;
498 
499  //record derived data in unode, to be traversed later after unpacking the archive
500  unpackedNode.addDerivedInfo(size, !isDir,
501  0L, createtime, accesstime, modtime, localRelPath);
502 
503  //unpack locally if a file
504  if (!isDir) {
505  SevenZipExtractor.UnpackStream unpackStream = null;
506  try {
507  unpackStream = new SevenZipExtractor.UnpackStream(localAbsPath);
508  item.extractSlow(unpackStream);
509  } catch (Exception e) {
510  //could be something unexpected with this file, move on
511  logger.log(Level.WARNING, "Could not extract file from archive: " + localAbsPath, e); //NON-NLS
512  } finally {
513  if (unpackStream != null) {
514  unpackStream.close();
515  }
516  }
517  }
518 
519  //update units for progress bar
520  ++processedItems;
521  }
522 
523  // add them to the DB. We wait until the end so that we have the metadata on all of the
524  // intermediate nodes since the order is not guaranteed
525  try {
526  unpackedTree.addDerivedFilesToCase();
527  unpackedFiles = unpackedTree.getAllFileObjects();
528 
529  //check if children are archives, update archive depth tracking
530  for (AbstractFile unpackedFile : unpackedFiles) {
531  if (isSevenZipExtractionSupported(unpackedFile)) {
532  archiveDepthCountTree.addArchive(parentAr, unpackedFile.getId());
533  }
534  }
535 
536  } catch (TskCoreException e) {
537  logger.log(Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure"); //NON-NLS
538  //TODO decide if anything to cleanup, for now bailing
539  }
540 
541  } catch (SevenZipException ex) {
542  logger.log(Level.SEVERE, "Error unpacking file: " + archiveFile, ex); //NON-NLS
543  //inbox message
544 
545  // print a message if the file is allocated
546  if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
547  String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
548  archiveFile.getName());
549  String details = NbBundle.getMessage(this.getClass(),
550  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
551  archiveFilePath, ex.getMessage());
552  services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
553  }
554  } finally {
555  if (inArchive != null) {
556  try {
557  inArchive.close();
558  } catch (SevenZipException e) {
559  logger.log(Level.SEVERE, "Error closing archive: " + archiveFile, e); //NON-NLS
560  }
561  }
562 
563  if (stream != null) {
564  try {
565  stream.close();
566  } catch (IOException ex) {
567  logger.log(Level.SEVERE, "Error closing stream after unpacking archive: " + archiveFile, ex); //NON-NLS
568  }
569  }
570 
571  //close progress bar
572  if (progressStarted) {
573  progress.finish();
574  }
575  }
576 
577  //create artifact and send user message
578  if (hasEncrypted) {
579  String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
580  try {
581  BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
582  artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType));
583  services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
584  } catch (TskCoreException ex) {
585  logger.log(Level.SEVERE, "Error creating blackboard artifact for encryption detected for file: " + archiveFilePath, ex); //NON-NLS
586  }
587 
588  String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
589  String details = NbBundle.getMessage(this.getClass(),
590  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
591  archiveFile.getName(), EmbeddedFileExtractorModuleFactory.getModuleName());
592  services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
593  }
594 
595  // adding unpacked extracted derived files to the job after closing relevant resources.
596  if (!unpackedFiles.isEmpty()) {
597  //currently sending a single event for all new files
598  services.fireModuleContentEvent(new ModuleContentEvent(archiveFile));
599  context.addFilesToJob(unpackedFiles);
600  }
601  }
602 
606  private static class UnpackStream implements ISequentialOutStream {
607 
608  private OutputStream output;
609  private String localAbsPath;
610 
611  UnpackStream(String localAbsPath) {
612  try {
613  output = new BufferedOutputStream(new FileOutputStream(localAbsPath));
614  } catch (FileNotFoundException ex) {
615  logger.log(Level.SEVERE, "Error writing extracted file: " + localAbsPath, ex); //NON-NLS
616  }
617 
618  }
619 
620  @Override
621  public int write(byte[] bytes) throws SevenZipException {
622  try {
623  output.write(bytes);
624  } catch (IOException ex) {
625  throw new SevenZipException(
626  NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
627  localAbsPath), ex);
628  }
629  return bytes.length;
630  }
631 
632  public void close() {
633  if (output != null) {
634  try {
635  output.flush();
636  output.close();
637  } catch (IOException e) {
638  logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS
639  }
640  }
641  }
642  }
643 
651  private class UnpackedTree {
652 
653  final UnpackedNode rootNode;
654 
662  UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
663  this.rootNode = new UnpackedNode();
664  this.rootNode.setFile(archiveFile);
665  this.rootNode.setFileName(archiveFile.getName());
666  this.rootNode.localRelPath = localPathRoot;
667  }
668 
677  UnpackedNode addNode(String filePath) {
678  String[] toks = filePath.split("[\\/\\\\]");
679  List<String> tokens = new ArrayList<>();
680  for (int i = 0; i < toks.length; ++i) {
681  if (!toks[i].isEmpty()) {
682  tokens.add(toks[i]);
683  }
684  }
685  return addNode(rootNode, tokens);
686  }
687 
694  private UnpackedNode addNode(UnpackedNode parent, List<String> tokenPath) {
695  // we found all of the tokens
696  if (tokenPath.isEmpty()) {
697  return parent;
698  }
699 
700  // get the next name in the path and look it up
701  String childName = tokenPath.remove(0);
702  UnpackedNode child = parent.getChild(childName);
703  // create new node
704  if (child == null) {
705  child = new UnpackedNode(childName, parent);
706  }
707 
708  // go down one more level
709  return addNode(child, tokenPath);
710  }
711 
718  List<AbstractFile> getRootFileObjects() {
719  List<AbstractFile> ret = new ArrayList<>();
720  for (UnpackedNode child : rootNode.children) {
721  ret.add(child.getFile());
722  }
723  return ret;
724  }
725 
732  List<AbstractFile> getAllFileObjects() {
733  List<AbstractFile> ret = new ArrayList<>();
734  for (UnpackedNode child : rootNode.children) {
735  getAllFileObjectsRec(ret, child);
736  }
737  return ret;
738  }
739 
740  private void getAllFileObjectsRec(List<AbstractFile> list, UnpackedNode parent) {
741  list.add(parent.getFile());
742  for (UnpackedNode child : parent.children) {
743  getAllFileObjectsRec(list, child);
744  }
745  }
746 
751  void addDerivedFilesToCase() throws TskCoreException {
752  final FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
753  for (UnpackedNode child : rootNode.children) {
754  addDerivedFilesToCaseRec(child, fileManager);
755  }
756  }
757 
758  private void addDerivedFilesToCaseRec(UnpackedNode node, FileManager fileManager) throws TskCoreException {
759  final String fileName = node.getFileName();
760 
761  try {
762  DerivedFile df = fileManager.addDerivedFile(fileName, node.getLocalRelPath(), node.getSize(),
763  node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
764  node.isIsFile(), node.getParent().getFile(), "", EmbeddedFileExtractorModuleFactory.getModuleName(), "", "");
765  node.setFile(df);
766 
767  } catch (TskCoreException ex) {
768  logger.log(Level.SEVERE, "Error adding a derived file to db:" + fileName, ex); //NON-NLS
769  throw new TskCoreException(
770  NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
771  fileName), ex);
772  }
773 
774  //recurse
775  for (UnpackedNode child : node.children) {
776  addDerivedFilesToCaseRec(child, fileManager);
777  }
778  }
779 
783  private class UnpackedNode {
784 
785  private String fileName;
787  private List<UnpackedNode> children = new ArrayList<>();
788  private String localRelPath = "";
789  private long size;
790  private long ctime, crtime, atime, mtime;
791  private boolean isFile;
793 
794  //root constructor
795  UnpackedNode() {
796  }
797 
798  //child node constructor
799  UnpackedNode(String fileName, UnpackedNode parent) {
800  this.fileName = fileName;
801  this.parent = parent;
802  //this.localRelPath = parent.localRelPath + File.separator + fileName;
803  //new child derived file will be set by unpack() method
804  parent.children.add(this);
805 
806  }
807 
808  public long getCtime() {
809  return ctime;
810  }
811 
812  public long getCrtime() {
813  return crtime;
814  }
815 
816  public long getAtime() {
817  return atime;
818  }
819 
820  public long getMtime() {
821  return mtime;
822  }
823 
824  public void setFileName(String fileName) {
825  this.fileName = fileName;
826  }
827 
828  UnpackedNode getParent() {
829  return parent;
830  }
831 
832  void addDerivedInfo(long size,
833  boolean isFile,
834  long ctime, long crtime, long atime, long mtime, String relLocalPath) {
835  this.size = size;
836  this.isFile = isFile;
837  this.ctime = ctime;
838  this.crtime = crtime;
839  this.atime = atime;
840  this.mtime = mtime;
841  this.localRelPath = relLocalPath;
842  }
843 
844  void setFile(AbstractFile file) {
845  this.file = file;
846  }
847 
854  UnpackedNode getChild(String childFileName) {
855  UnpackedNode ret = null;
856  for (UnpackedNode child : children) {
857  if (child.fileName.equals(childFileName)) {
858  ret = child;
859  break;
860  }
861  }
862  return ret;
863  }
864 
865  public String getFileName() {
866  return fileName;
867  }
868 
870  return file;
871  }
872 
873  public String getLocalRelPath() {
874  return localRelPath;
875  }
876 
877  public long getSize() {
878  return size;
879  }
880 
881  public boolean isIsFile() {
882  return isFile;
883  }
884  }
885  }
886 
890  private static class ArchiveDepthCountTree {
891 
892  //keeps all nodes refs for easy search
893  private final List<Archive> archives = new ArrayList<>();
894 
901  Archive findArchive(long objectId) {
902  for (Archive ar : archives) {
903  if (ar.objectId == objectId) {
904  return ar;
905  }
906  }
907 
908  return null;
909  }
910 
918  Archive addArchive(Archive parent, long objectId) {
919  Archive child = new Archive(parent, objectId);
920  archives.add(child);
921  return child;
922  }
923 
924  private static class Archive {
925 
926  int depth;
927  long objectId;
928  Archive parent;
929  List<Archive> children;
930 
931  Archive(Archive parent, long objectId) {
932  this.parent = parent;
933  this.objectId = objectId;
934  children = new ArrayList<>();
935  if (parent != null) {
936  parent.children.add(this);
937  this.depth = parent.depth + 1;
938  } else {
939  this.depth = 0;
940  }
941  }
942 
948  int getDepth() {
949  return depth;
950  }
951  }
952  }
953 
954 }
UnpackedNode addNode(UnpackedNode parent, List< String > tokenPath)
synchronized DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, AbstractFile parentFile, String rederiveDetails, String toolName, String toolVersion, String otherDetails)

Copyright © 2012-2015 Basis Technology. Generated on: Mon Oct 19 2015
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.