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 = rootArchive.getArchiveFile().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);
709 unpackedFile.close();
712 }
catch (TskCoreException | NoCurrentCaseException e) {
713 logger.log(Level.SEVERE,
"Error populating complete derived file hierarchy from the unpacked dir structure", e);
717 }
catch (SevenZipException ex) {
718 logger.log(Level.WARNING,
"Error unpacking file: " + archiveFile, ex);
722 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
723 String msg = NbBundle.getMessage(SevenZipExtractor.class,
724 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
726 String details = NbBundle.getMessage(SevenZipExtractor.class,
727 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
728 escapedArchiveFilePath, ex.getMessage());
729 services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
732 if (inArchive != null) {
735 }
catch (SevenZipException e) {
736 logger.log(Level.SEVERE,
"Error closing archive: " + archiveFile, e);
740 if (stream != null) {
743 }
catch (IOException ex) {
744 logger.log(Level.SEVERE,
"Error closing stream after unpacking archive: " + archiveFile, ex);
749 if (progressStarted) {
756 String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
758 BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
759 artifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType));
763 blackboard.indexArtifact(artifact);
764 }
catch (Blackboard.BlackboardException ex) {
765 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
766 MessageNotifyUtil.Notify.error(
767 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
770 services.fireModuleDataEvent(
new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
771 }
catch (TskCoreException ex) {
772 logger.log(Level.SEVERE,
"Error creating blackboard artifact for encryption detected for file: " + escapedArchiveFilePath, ex);
775 String msg = NbBundle.getMessage(SevenZipExtractor.class,
776 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
777 String details = NbBundle.getMessage(SevenZipExtractor.class,
778 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
779 currentArchiveName, EmbeddedFileExtractorModuleFactory.getModuleName());
780 services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
784 if (!unpackedFiles.isEmpty()) {
786 services.fireModuleContentEvent(
new ModuleContentEvent(archiveFile));
787 if (context != null) {
788 context.addFilesToJob(unpackedFiles);
791 return unpackSuccessful;
798 private int[] getExtractableFilesFromDetailsMap(
799 Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
801 Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
802 .toArray(
new Integer[archiveDetailsMap.size()]);
804 return Arrays.stream(wrappedExtractionIndices)
805 .mapToInt(Integer::intValue)
816 private final static class UnpackStream implements ISequentialOutStream {
823 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
825 this.bytesWritten = 0;
830 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
832 this.bytesWritten = 0;
840 public int write(byte[] bytes)
throws SevenZipException {
843 this.bytesWritten += bytes.length;
844 }
catch (IOException ex) {
845 throw new SevenZipException(
846 NbBundle.getMessage(SevenZipExtractor.class,
847 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
853 public void close() throws IOException {
854 try (EncodedFileOutputStream out = output) {
866 private final SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode;
871 SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode,
872 String localAbsPath, String localRelPath) {
896 implements IArchiveExtractCallback, ICryptoGetTextPassword {
913 private boolean unpackSuccessful =
true;
916 AbstractFile archiveFile, ProgressHandle progressHandle,
917 Map<Integer, InArchiveItemDetails> archiveDetailsMap,
918 String password,
long freeDiskSpace) {
942 public ISequentialOutStream
getStream(
int inArchiveItemIndex,
943 ExtractAskMode mode)
throws SevenZipException {
947 isFolder = (Boolean) inArchive
948 .getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
949 if (isFolder || mode != ExtractAskMode.EXTRACT) {
953 final String localAbsPath = archiveDetailsMap.get(
954 inArchiveItemIndex).getLocalAbsPath();
962 if (unpackStream != null) {
967 }
catch (IOException ex) {
968 logger.log(Level.WARNING, String.format(
"Error opening or setting new stream "
969 +
"for archive file at %s", localAbsPath), ex.getMessage());
986 final Date createTime = (Date) inArchive.getProperty(
987 inArchiveItemIndex, PropID.CREATION_TIME);
988 final Date accessTime = (Date) inArchive.getProperty(
989 inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
990 final Date writeTime = (Date) inArchive.getProperty(
991 inArchiveItemIndex, PropID.LAST_WRITE_TIME);
993 createTimeInSeconds = createTime == null ? 0L
994 : createTime.getTime() / 1000;
995 modTimeInSeconds = writeTime == null ? 0L
996 : writeTime.getTime() / 1000;
997 accessTimeInSeconds = accessTime == null ? 0L
998 : accessTime.getTime() / 1000;
1000 progressHandle.progress(archiveFile.getName() +
": "
1001 + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
1016 final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
1017 = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
1018 final String localRelPath = archiveDetailsMap.get(
1019 inArchiveItemIndex).getLocalRelPath();
1021 unpackedNode.addDerivedInfo(0,
1022 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1028 final String localAbsPath = archiveDetailsMap.get(
1029 inArchiveItemIndex).getLocalAbsPath();
1030 if (result != ExtractOperationResult.OK) {
1031 logger.log(Level.WARNING,
"Extraction of : {0} encountered error {1}",
1032 new Object[]{localAbsPath, result});
1033 unpackSuccessful =
false;
1037 unpackedNode.addDerivedInfo(unpackStream.
getSize(),
1038 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1042 unpackStream.
close();
1043 }
catch (IOException e) {
1044 logger.log(Level.WARNING,
"Error closing unpack stream for file: {0}", localAbsPath);
1049 public void setTotal(
long value)
throws SevenZipException {
1094 UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
1096 this.rootNode.setFile(archiveFile);
1097 this.rootNode.setFileName(archiveFile.getName());
1098 this.rootNode.setLocalRelPath(localPathRoot);
1110 UnpackedNode addNode(String filePath) {
1111 String[] toks = filePath.split(
"[\\/\\\\]");
1112 List<String> tokens =
new ArrayList<>();
1113 for (
int i = 0; i < toks.length; ++i) {
1114 if (!toks[i].isEmpty()) {
1115 tokens.add(toks[i]);
1118 return addNode(rootNode, tokens);
1131 if (tokenPath.isEmpty()) {
1136 String childName = tokenPath.remove(0);
1139 if (child == null) {
1141 parent.addChild(child);
1145 return addNode(child, tokenPath);
1154 List<AbstractFile> getRootFileObjects() {
1155 List<AbstractFile> ret =
new ArrayList<>();
1156 rootNode.getChildren().forEach((child) -> {
1157 ret.add(child.getFile());
1168 List<AbstractFile> getAllFileObjects() {
1169 List<AbstractFile> ret =
new ArrayList<>();
1170 rootNode.getChildren().forEach((child) -> {
1177 list.add(parent.getFile());
1178 parent.getChildren().forEach((child) -> {
1187 void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath)
throws TskCoreException,
NoCurrentCaseException {
1189 for (UnpackedNode child : rootNode.getChildren()) {
1190 updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
1210 progress.progress(String.format(
"%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems));
1212 String nameInDatabase = getKeyFromUnpackedNode(node, archiveFilePath);
1213 ZipFileStatusWrapper existingFile = nameInDatabase == null ? null : statusMap.get(nameInDatabase);
1214 if (existingFile == null) {
1215 df = fileManager.
addDerivedFile(node.getFileName(), node.getLocalRelPath(), node.getSize(),
1216 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1218 "",
"", TskData.EncodingType.XOR1);
1221 String key = getKeyAbstractFile(existingFile.
getFile());
1224 statusMap.put(key, existingFile);
1228 String mimeType = existingFile.
getFile().getMIMEType().equalsIgnoreCase(
"application/octet-stream") ? null : existingFile.
getFile().getMIMEType();
1230 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1232 "",
"", TskData.EncodingType.XOR1);
1236 df = (DerivedFile) existingFile.
getFile();
1240 }
catch (TskCoreException ex) {
1241 logger.log(Level.SEVERE,
"Error adding a derived file to db:" + node.getFileName(), ex);
1242 throw new TskCoreException(
1243 NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
1244 node.getFileName()), ex);
1248 updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
1259 private final List<UnpackedNode>
children =
new ArrayList<>();
1260 private String localRelPath =
"";
1262 private long ctime, crtime, atime, mtime;
1274 this.localRelPath = parent.getLocalRelPath() + File.separator +
fileName;
1293 void setFileName(String fileName) {
1302 void addChild(UnpackedNode child) {
1303 children.add(child);
1312 List<UnpackedNode> getChildren() {
1321 UnpackedNode getParent() {
1325 void addDerivedInfo(
long size,
1327 long ctime,
long crtime,
long atime,
long mtime, String relLocalPath) {
1331 this.crtime = crtime;
1334 this.localRelPath = relLocalPath;
1337 void setFile(AbstractFile file) {
1348 UnpackedNode getChild(String childFileName) {
1349 UnpackedNode ret = null;
1350 for (UnpackedNode child : children) {
1351 if (child.getFileName().equals(childFileName)) {
1359 String getFileName() {
1363 AbstractFile getFile() {
1367 String getLocalRelPath() {
1377 void setLocalRelPath(String localRelativePath) {
1378 localRelPath = localRelativePath;
1385 boolean isIsFile() {
1395 static class Archive {
1398 private final int depth;
1399 private final List<Archive> children;
1400 private final long rootArchiveId;
1401 private boolean flaggedAsZipBomb =
false;
1402 private final AbstractFile archiveFile;
1416 Archive(
int depth,
long rootArchiveId, AbstractFile archiveFile) {
1417 this.children =
new ArrayList<>();
1419 this.rootArchiveId = rootArchiveId;
1420 this.archiveFile = archiveFile;
1429 void addChild(Archive child) {
1430 children.add(child);
1437 synchronized void flagAsZipBomb() {
1438 flaggedAsZipBomb =
true;
1446 synchronized boolean isFlaggedAsZipBomb() {
1447 return flaggedAsZipBomb;
1455 AbstractFile getArchiveFile() {
1464 long getRootArchiveId() {
1465 return rootArchiveId;
1473 long getObjectId() {
1474 return archiveFile.getId();
1506 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)