19 package org.sleuthkit.autopsy.modules.embeddedfileextractor;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Date;
29 import java.util.HashMap;
30 import java.util.List;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.logging.Level;
34 import net.sf.sevenzipjbinding.ArchiveFormat;
35 import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
36 import net.sf.sevenzipjbinding.ExtractAskMode;
37 import net.sf.sevenzipjbinding.ISequentialOutStream;
38 import net.sf.sevenzipjbinding.ISevenZipInArchive;
39 import net.sf.sevenzipjbinding.SevenZip;
40 import net.sf.sevenzipjbinding.SevenZipException;
41 import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
42 import net.sf.sevenzipjbinding.ExtractOperationResult;
43 import net.sf.sevenzipjbinding.IArchiveExtractCallback;
44 import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
45 import net.sf.sevenzipjbinding.PropID;
46 import org.netbeans.api.progress.ProgressHandle;
47 import org.openide.util.NbBundle;
48 import org.openide.util.NbBundle.Messages;
74 class SevenZipExtractor {
76 private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName());
77 private IngestServices services = IngestServices.getInstance();
78 private final IngestJobContext context;
79 private final FileTypeDetector fileTypeDetector;
81 private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
82 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
83 private static final String ENCRYPTION_FULL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
84 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull");
86 private static final int MAX_DEPTH = 4;
87 private static final int MAX_COMPRESSION_RATIO = 600;
88 private static final long MIN_COMPRESSION_RATIO_SIZE = 500 * 1000000L;
89 private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L;
91 private String moduleDirRelative;
92 private String moduleDirAbsolute;
94 private Blackboard blackboard;
96 private ProgressHandle progress;
98 private String currentArchiveName;
100 private String getLocalRootAbsPath(String uniqueArchiveFileName) {
101 return moduleDirAbsolute + File.separator + uniqueArchiveFileName;
116 XRAR(
"application/x-rar-compressed");
121 this.mimeType = mimeType;
126 return this.mimeType;
131 SevenZipExtractor(
IngestJobContext context,
FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute)
throws SevenZipNativeInitializationException {
132 if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
133 SevenZip.initSevenZipFromPlatformJAR();
135 this.context = context;
136 this.fileTypeDetector = fileTypeDetector;
137 this.moduleDirRelative = moduleDirRelative;
138 this.moduleDirAbsolute = moduleDirAbsolute;
149 boolean isSevenZipExtractionSupported(AbstractFile file) {
150 String fileMimeType = fileTypeDetector.getMIMEType(file);
151 for (SupportedArchiveExtractionFormats mimeType : SupportedArchiveExtractionFormats.values()) {
152 if (mimeType.toString().equals(fileMimeType)) {
182 private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive,
int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
188 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)) {
193 final Long archiveItemSize = (Long) inArchive.getProperty(
194 inArchiveItemIndex, PropID.SIZE);
197 if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
201 final Long archiveItemPackedSize = (Long) inArchive.getProperty(
202 inArchiveItemIndex, PropID.PACKED_SIZE);
204 if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
205 logger.log(Level.WARNING,
"Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}",
206 new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)});
210 int cRatio = (int) (archiveItemSize / archiveItemPackedSize);
212 if (cRatio >= MAX_COMPRESSION_RATIO) {
213 Archive rootArchive = depthMap.get(depthMap.get(archiveFile.getId()).getRootArchiveId());
214 String details = NbBundle.getMessage(SevenZipExtractor.class,
215 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails",
216 cRatio, FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
218 flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedFilePath);
224 }
catch (SevenZipException ex) {
225 logger.log(Level.WARNING,
"Error getting archive item size and cannot detect if zipbomb. ", ex);
241 private void flagRootArchiveAsZipBomb(Archive rootArchive, AbstractFile archiveFile, String details, String escapedFilePath) {
242 rootArchive.flagAsZipBomb();
243 logger.log(Level.INFO, details);
244 String msg = NbBundle.getMessage(SevenZipExtractor.class,
245 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), escapedFilePath);
247 Collection<BlackboardAttribute> attributes =
new ArrayList<>();
248 attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, EmbeddedFileExtractorModuleFactory.getModuleName(),
249 "Possible Zip Bomb"));
250 attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION,
251 EmbeddedFileExtractorModuleFactory.getModuleName(),
252 Bundle.SevenZipExtractor_zipBombArtifactCreation_text(archiveFile.getName())));
253 attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT,
254 EmbeddedFileExtractorModuleFactory.getModuleName(),
257 SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
258 org.
sleuthkit.datamodel.Blackboard tskBlackboard = tskCase.getBlackboard();
260 if (!tskBlackboard.artifactExists(archiveFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) {
261 BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
262 artifact.addAttributes(attributes);
266 blackboard.indexArtifact(artifact);
267 }
catch (Blackboard.BlackboardException ex) {
268 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
269 MessageNotifyUtil.Notify.error(
270 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
273 services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
275 services.fireModuleDataEvent(
new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
277 }
catch (TskCoreException ex) {
278 logger.log(Level.SEVERE,
"Error creating blackboard artifact for Zip Bomb Detection for file: " + escapedFilePath, ex);
279 }
catch (NoCurrentCaseException ex) {
280 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
292 private ArchiveFormat get7ZipOptions(AbstractFile archiveFile) {
294 String detectedFormat;
295 detectedFormat = archiveFile.getMIMEType();
297 if (detectedFormat == null) {
298 logger.log(Level.WARNING,
"Could not detect format for file: {0}", archiveFile);
301 String extension = archiveFile.getNameExtension();
302 if (
"rar".equals(extension))
311 }
else if (detectedFormat.contains(
"application/x-rar-compressed"))
332 private long getRootArchiveId(AbstractFile file)
throws TskCoreException {
333 long id = file.getId();
334 Content parentContent = file.getParent();
335 while (parentContent != null) {
336 id = parentContent.getId();
337 parentContent = parentContent.getParent();
356 private List<AbstractFile> getAlreadyExtractedFiles(AbstractFile archiveFile, String archiveFilePath)
throws TskCoreException, NoCurrentCaseException {
359 if (archiveFile.hasChildren() &&
new File(moduleDirAbsolute, EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
360 return Case.getCurrentCaseThrows().getServices().getFileManager().findFilesByParentPath(getRootArchiveId(archiveFile), archiveFilePath);
362 return new ArrayList<>();
372 private String getArchiveFilePath(AbstractFile archiveFile) {
373 return archiveFile.getParentPath() + archiveFile.getName();
382 private void makeLocalDirectories(String uniqueArchiveFileName) {
383 final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName);
384 final File localRoot =
new File(localRootAbsPath);
385 if (!localRoot.exists()) {
402 private String getPathInArchive(ISevenZipInArchive archive,
int inArchiveItemIndex, AbstractFile archiveFile)
throws SevenZipException {
403 String pathInArchive = (String) archive.getProperty(
404 inArchiveItemIndex, PropID.PATH);
406 if (pathInArchive == null || pathInArchive.isEmpty()) {
412 String archName = archiveFile.getName();
413 int dotI = archName.lastIndexOf(
".");
414 String useName = null;
416 String base = archName.substring(0, dotI);
417 String ext = archName.substring(dotI);
418 int colonIndex = ext.lastIndexOf(
":");
419 if (colonIndex != -1) {
422 ext = ext.substring(0, colonIndex);
429 useName = base +
".tar";
436 if (useName == null) {
437 pathInArchive =
"/" + archName +
"/" + Integer.toString(inArchiveItemIndex);
439 pathInArchive =
"/" + useName;
441 String msg = NbBundle.getMessage(SevenZipExtractor.class,
442 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
443 getArchiveFilePath(archiveFile), pathInArchive);
444 logger.log(Level.WARNING, msg);
446 return pathInArchive;
453 private String getKeyAbstractFile(AbstractFile fileInDatabase) {
454 return fileInDatabase == null ? null : fileInDatabase.getParentPath() + fileInDatabase.getName();
461 private String getKeyFromUnpackedNode(UnpackedTree.UnpackedNode node, String archiveFilePath) {
462 return node == null ? null : archiveFilePath +
"/" + node.getFileName();
474 void unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) {
475 unpack(archiveFile, depthMap, null);
489 @Messages({
"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search.",
490 "# {0} - rootArchive",
491 "SevenZipExtractor.zipBombArtifactCreation.text=Zip Bomb Detected {0}"})
492 boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap, String password) {
493 boolean unpackSuccessful =
true;
494 boolean hasEncrypted =
false;
495 boolean fullEncryption =
true;
496 boolean progressStarted =
false;
497 final String archiveFilePath = getArchiveFilePath(archiveFile);
498 final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
499 HashMap<String, ZipFileStatusWrapper> statusMap =
new HashMap<>();
500 List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
501 ISevenZipInArchive inArchive = null;
502 currentArchiveName = archiveFile.getName();
504 SevenZipContentReadStream stream = null;
505 progress = ProgressHandle.createHandle(Bundle.EmbeddedFileExtractorIngestModule_ArchiveExtractor_moduleName());
509 blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard();
510 }
catch (NoCurrentCaseException ex) {
511 logger.log(Level.INFO,
"Exception while getting open case.", ex);
512 unpackSuccessful =
false;
513 return unpackSuccessful;
517 List<AbstractFile> existingFiles = getAlreadyExtractedFiles(archiveFile, archiveFilePath);
518 for (AbstractFile file : existingFiles) {
519 statusMap.put(getKeyAbstractFile(file),
new ZipFileStatusWrapper(file, ZipFileStatus.EXISTS));
521 }
catch (TskCoreException e) {
522 logger.log(Level.INFO,
"Error checking if file already has been processed, skipping: {0}", escapedArchiveFilePath);
523 unpackSuccessful =
false;
524 return unpackSuccessful;
525 }
catch (NoCurrentCaseException ex) {
526 logger.log(Level.INFO,
"No open case was found while trying to unpack the archive file {0}", escapedArchiveFilePath);
527 unpackSuccessful =
false;
528 return unpackSuccessful;
530 parentAr = depthMap.get(archiveFile.getId());
531 if (parentAr == null) {
532 parentAr =
new Archive(0, archiveFile.getId(), archiveFile);
533 depthMap.put(archiveFile.getId(), parentAr);
535 Archive rootArchive = depthMap.get(parentAr.getRootArchiveId());
536 if (rootArchive.isFlaggedAsZipBomb()) {
538 unpackSuccessful =
false;
539 return unpackSuccessful;
540 }
else if (parentAr.getDepth() == MAX_DEPTH) {
541 String details = NbBundle.getMessage(SevenZipExtractor.class,
542 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
543 parentAr.getDepth(), FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
544 flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedArchiveFilePath);
545 unpackSuccessful =
false;
546 return unpackSuccessful;
550 stream =
new SevenZipContentReadStream(
new ReadContentInputStream(archiveFile));
554 ArchiveFormat options = get7ZipOptions(archiveFile);
555 if (password == null) {
556 inArchive = SevenZip.openInArchive(options, stream);
558 inArchive = SevenZip.openInArchive(options, stream, password);
560 numItems = inArchive.getNumberOfItems();
561 progress.start(numItems);
562 progressStarted =
true;
565 final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
567 makeLocalDirectories(uniqueArchiveFileName);
568 }
catch (SecurityException e) {
569 logger.log(Level.SEVERE,
"Error setting up output path for archive root: {0}", getLocalRootAbsPath(uniqueArchiveFileName));
571 unpackSuccessful =
false;
572 return unpackSuccessful;
576 SevenZipExtractor.UnpackedTree unpackedTree =
new SevenZipExtractor.UnpackedTree(moduleDirRelative +
"/" + uniqueArchiveFileName, archiveFile);
580 freeDiskSpace = services.getFreeDiskSpace();
581 }
catch (NullPointerException ex) {
584 freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
587 Map<Integer, InArchiveItemDetails> archiveDetailsMap =
new HashMap<>();
588 for (
int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
589 progress.progress(String.format(
"%s: Analyzing archive metadata and creating local files (%d of %d)", currentArchiveName, inArchiveItemIndex + 1, numItems), 0);
590 if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
591 unpackSuccessful =
false;
592 return unpackSuccessful;
595 String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
596 UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
598 final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
600 if (isEncrypted && password == null) {
601 logger.log(Level.WARNING,
"Skipping encrypted file in archive: {0}", pathInArchive);
603 unpackSuccessful =
false;
606 fullEncryption =
false;
613 Long archiveItemSize = (Long) inArchive.getProperty(
614 inArchiveItemIndex, PropID.SIZE);
615 if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) {
616 String archiveItemPath = (String) inArchive.getProperty(
617 inArchiveItemIndex, PropID.PATH);
618 long newDiskSpace = freeDiskSpace - archiveItemSize;
619 if (newDiskSpace < MIN_FREE_DISK_SPACE) {
620 String msg = NbBundle.getMessage(SevenZipExtractor.class,
621 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
622 escapedArchiveFilePath, archiveItemPath);
623 String details = NbBundle.getMessage(SevenZipExtractor.class,
624 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
625 services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
626 logger.log(Level.INFO,
"Skipping archive item due to insufficient disk space: {0}, {1}",
new String[]{escapedArchiveFilePath, archiveItemPath});
627 logger.log(Level.INFO,
"Available disk space: {0}",
new Object[]{freeDiskSpace});
628 unpackSuccessful =
false;
632 freeDiskSpace = newDiskSpace;
635 final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex +
"_" +
new File(pathInArchive).getName());
636 final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
637 final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
640 File localFile =
new java.io.File(localAbsPath);
642 if (!localFile.exists()) {
644 if ((Boolean) inArchive.getProperty(
645 inArchiveItemIndex, PropID.IS_FOLDER)) {
648 localFile.getParentFile().mkdirs();
650 localFile.createNewFile();
651 }
catch (IOException e) {
652 logger.log(Level.SEVERE,
"Error creating extracted file: "
653 + localFile.getAbsolutePath(), e);
656 }
catch (SecurityException e) {
657 logger.log(Level.SEVERE,
"Error setting up output path for unpacked file: {0}",
664 if (localFile.exists() ==
false) {
672 archiveDetailsMap.put(inArchiveItemIndex,
new InArchiveItemDetails(
673 unpackedNode, localAbsPath, localRelPath));
676 int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
678 StandardIArchiveExtractCallback archiveCallBack
679 =
new StandardIArchiveExtractCallback(
680 inArchive, archiveFile, progress,
681 archiveDetailsMap, password, freeDiskSpace);
686 inArchive.extract(extractionIndices,
false, archiveCallBack);
688 unpackSuccessful = unpackSuccessful & archiveCallBack.wasSuccessful();
690 archiveDetailsMap = null;
695 unpackedTree.updateOrAddFileToCaseRec(statusMap, archiveFilePath);
696 unpackedFiles = unpackedTree.getAllFileObjects();
698 for (
int i = 0; i < unpackedFiles.size(); i++) {
699 progress.progress(String.format(
"%s: Searching for nested archives (%d of %d)", currentArchiveName, i + 1, unpackedFiles.size()));
700 AbstractFile unpackedFile = unpackedFiles.get(i);
701 if (unpackedFile == null) {
704 if (isSevenZipExtractionSupported(unpackedFile)) {
705 Archive child =
new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
706 parentAr.addChild(child);
707 depthMap.put(unpackedFile.getId(), child);
711 }
catch (TskCoreException | NoCurrentCaseException e) {
712 logger.log(Level.SEVERE,
"Error populating complete derived file hierarchy from the unpacked dir structure", e);
716 }
catch (SevenZipException ex) {
717 logger.log(Level.WARNING,
"Error unpacking file: " + archiveFile, ex);
721 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
722 String msg = NbBundle.getMessage(SevenZipExtractor.class,
723 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
725 String details = NbBundle.getMessage(SevenZipExtractor.class,
726 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
727 escapedArchiveFilePath, ex.getMessage());
728 services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
731 if (inArchive != null) {
734 }
catch (SevenZipException e) {
735 logger.log(Level.SEVERE,
"Error closing archive: " + archiveFile, e);
739 if (stream != null) {
742 }
catch (IOException ex) {
743 logger.log(Level.SEVERE,
"Error closing stream after unpacking archive: " + archiveFile, ex);
748 if (progressStarted) {
755 String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
757 BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
758 artifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType));
762 blackboard.indexArtifact(artifact);
763 }
catch (Blackboard.BlackboardException ex) {
764 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
765 MessageNotifyUtil.Notify.error(
766 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
769 services.fireModuleDataEvent(
new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
770 }
catch (TskCoreException ex) {
771 logger.log(Level.SEVERE,
"Error creating blackboard artifact for encryption detected for file: " + escapedArchiveFilePath, ex);
774 String msg = NbBundle.getMessage(SevenZipExtractor.class,
775 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
776 String details = NbBundle.getMessage(SevenZipExtractor.class,
777 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
778 currentArchiveName, EmbeddedFileExtractorModuleFactory.getModuleName());
779 services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
783 if (!unpackedFiles.isEmpty()) {
785 services.fireModuleContentEvent(
new ModuleContentEvent(archiveFile));
786 if (context != null) {
787 context.addFilesToJob(unpackedFiles);
790 return unpackSuccessful;
797 private int[] getExtractableFilesFromDetailsMap(
798 Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
800 Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
801 .toArray(
new Integer[archiveDetailsMap.size()]);
803 return Arrays.stream(wrappedExtractionIndices)
804 .mapToInt(Integer::intValue)
815 private final static class UnpackStream implements ISequentialOutStream {
822 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
824 this.bytesWritten = 0;
829 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
831 this.bytesWritten = 0;
839 public int write(byte[] bytes)
throws SevenZipException {
842 this.bytesWritten += bytes.length;
843 }
catch (IOException ex) {
844 throw new SevenZipException(
845 NbBundle.getMessage(SevenZipExtractor.class,
846 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
852 public void close() throws IOException {
853 try (EncodedFileOutputStream out = output) {
865 private final SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode;
870 SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode,
871 String localAbsPath, String localRelPath) {
895 implements IArchiveExtractCallback, ICryptoGetTextPassword {
912 private boolean unpackSuccessful =
true;
915 AbstractFile archiveFile, ProgressHandle progressHandle,
916 Map<Integer, InArchiveItemDetails> archiveDetailsMap,
917 String password,
long freeDiskSpace) {
941 public ISequentialOutStream
getStream(
int inArchiveItemIndex,
942 ExtractAskMode mode)
throws SevenZipException {
946 isFolder = (Boolean) inArchive
947 .getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
948 if (isFolder || mode != ExtractAskMode.EXTRACT) {
952 final String localAbsPath = archiveDetailsMap.get(
953 inArchiveItemIndex).getLocalAbsPath();
961 if (unpackStream != null) {
966 }
catch (IOException ex) {
967 logger.log(Level.WARNING, String.format(
"Error opening or setting new stream "
968 +
"for archive file at %s", localAbsPath), ex.getMessage());
985 final Date createTime = (Date) inArchive.getProperty(
986 inArchiveItemIndex, PropID.CREATION_TIME);
987 final Date accessTime = (Date) inArchive.getProperty(
988 inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
989 final Date writeTime = (Date) inArchive.getProperty(
990 inArchiveItemIndex, PropID.LAST_WRITE_TIME);
992 createTimeInSeconds = createTime == null ? 0L
993 : createTime.getTime() / 1000;
994 modTimeInSeconds = writeTime == null ? 0L
995 : writeTime.getTime() / 1000;
996 accessTimeInSeconds = accessTime == null ? 0L
997 : accessTime.getTime() / 1000;
999 progressHandle.progress(archiveFile.getName() +
": "
1000 + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
1015 final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
1016 = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
1017 final String localRelPath = archiveDetailsMap.get(
1018 inArchiveItemIndex).getLocalRelPath();
1020 unpackedNode.addDerivedInfo(0,
1021 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1027 final String localAbsPath = archiveDetailsMap.get(
1028 inArchiveItemIndex).getLocalAbsPath();
1029 if (result != ExtractOperationResult.OK) {
1030 logger.log(Level.WARNING,
"Extraction of : {0} encountered error {1}",
1031 new Object[]{localAbsPath, result});
1032 unpackSuccessful =
false;
1036 unpackedNode.addDerivedInfo(unpackStream.
getSize(),
1037 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1041 unpackStream.
close();
1042 }
catch (IOException e) {
1043 logger.log(Level.WARNING,
"Error closing unpack stream for file: {0}", localAbsPath);
1048 public void setTotal(
long value)
throws SevenZipException {
1093 UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
1095 this.rootNode.setFile(archiveFile);
1096 this.rootNode.setFileName(archiveFile.getName());
1097 this.rootNode.setLocalRelPath(localPathRoot);
1109 UnpackedNode addNode(String filePath) {
1110 String[] toks = filePath.split(
"[\\/\\\\]");
1111 List<String> tokens =
new ArrayList<>();
1112 for (
int i = 0; i < toks.length; ++i) {
1113 if (!toks[i].isEmpty()) {
1114 tokens.add(toks[i]);
1117 return addNode(rootNode, tokens);
1130 if (tokenPath.isEmpty()) {
1135 String childName = tokenPath.remove(0);
1138 if (child == null) {
1140 parent.addChild(child);
1144 return addNode(child, tokenPath);
1153 List<AbstractFile> getRootFileObjects() {
1154 List<AbstractFile> ret =
new ArrayList<>();
1155 rootNode.getChildren().forEach((child) -> {
1156 ret.add(child.getFile());
1167 List<AbstractFile> getAllFileObjects() {
1168 List<AbstractFile> ret =
new ArrayList<>();
1169 rootNode.getChildren().forEach((child) -> {
1176 list.add(parent.getFile());
1177 parent.getChildren().forEach((child) -> {
1186 void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath)
throws TskCoreException,
NoCurrentCaseException {
1188 for (UnpackedNode child : rootNode.getChildren()) {
1189 updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
1209 progress.progress(String.format(
"%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems));
1211 String nameInDatabase = getKeyFromUnpackedNode(node, archiveFilePath);
1212 ZipFileStatusWrapper existingFile = nameInDatabase == null ? null : statusMap.get(nameInDatabase);
1213 if (existingFile == null) {
1214 df = fileManager.
addDerivedFile(node.getFileName(), node.getLocalRelPath(), node.getSize(),
1215 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1217 "",
"", TskData.EncodingType.XOR1);
1220 String key = getKeyAbstractFile(existingFile.
getFile());
1223 statusMap.put(key, existingFile);
1227 String mimeType = existingFile.
getFile().getMIMEType().equalsIgnoreCase(
"application/octet-stream") ? null : existingFile.
getFile().getMIMEType();
1229 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1231 "",
"", TskData.EncodingType.XOR1);
1235 df = (DerivedFile) existingFile.
getFile();
1239 }
catch (TskCoreException ex) {
1240 logger.log(Level.SEVERE,
"Error adding a derived file to db:" + node.getFileName(), ex);
1241 throw new TskCoreException(
1242 NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
1243 node.getFileName()), ex);
1247 updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
1258 private final List<UnpackedNode>
children =
new ArrayList<>();
1259 private String localRelPath =
"";
1261 private long ctime, crtime, atime, mtime;
1273 this.localRelPath = parent.getLocalRelPath() + File.separator +
fileName;
1292 void setFileName(String fileName) {
1301 void addChild(UnpackedNode child) {
1302 children.add(child);
1311 List<UnpackedNode> getChildren() {
1320 UnpackedNode getParent() {
1324 void addDerivedInfo(
long size,
1326 long ctime,
long crtime,
long atime,
long mtime, String relLocalPath) {
1330 this.crtime = crtime;
1333 this.localRelPath = relLocalPath;
1336 void setFile(AbstractFile file) {
1347 UnpackedNode getChild(String childFileName) {
1348 UnpackedNode ret = null;
1349 for (UnpackedNode child : children) {
1350 if (child.getFileName().equals(childFileName)) {
1358 String getFileName() {
1362 AbstractFile getFile() {
1366 String getLocalRelPath() {
1376 void setLocalRelPath(String localRelativePath) {
1377 localRelPath = localRelativePath;
1384 boolean isIsFile() {
1394 static class Archive {
1397 private final int depth;
1398 private final List<Archive> children;
1399 private final long rootArchiveId;
1400 private boolean flaggedAsZipBomb =
false;
1401 private final AbstractFile archiveFile;
1415 Archive(
int depth,
long rootArchiveId, AbstractFile archiveFile) {
1416 this.children =
new ArrayList<>();
1418 this.rootArchiveId = rootArchiveId;
1419 this.archiveFile = archiveFile;
1428 void addChild(Archive child) {
1429 children.add(child);
1436 synchronized void flagAsZipBomb() {
1437 flaggedAsZipBomb =
true;
1445 synchronized boolean isFlaggedAsZipBomb() {
1446 return flaggedAsZipBomb;
1454 AbstractFile getArchiveFile() {
1463 long getRootArchiveId() {
1464 return rootArchiveId;
1472 long getObjectId() {
1473 return archiveFile.getId();
1505 abstractFile = file;
FileManager getFileManager()
synchronized DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, Content parentObj, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)
static Case getCurrentCaseThrows()
synchronized DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, String mimeType, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)