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.ExtractOperationResult;
38 import net.sf.sevenzipjbinding.IArchiveExtractCallback;
39 import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
40 import net.sf.sevenzipjbinding.ISequentialOutStream;
41 import net.sf.sevenzipjbinding.ISevenZipInArchive;
42 import net.sf.sevenzipjbinding.PropID;
43 import net.sf.sevenzipjbinding.SevenZip;
44 import net.sf.sevenzipjbinding.SevenZipException;
45 import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
46 import org.netbeans.api.progress.ProgressHandle;
47 import org.openide.util.NbBundle;
48 import org.openide.util.NbBundle.Messages;
64 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT;
66 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
67 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
68 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
76 class SevenZipExtractor {
78 private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName());
79 private static final String MODULE_NAME = EmbeddedFileExtractorModuleFactory.getModuleName();
82 private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
83 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
84 private static final String ENCRYPTION_FULL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
85 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull");
88 private static final int MAX_DEPTH = 4;
89 private static final int MAX_COMPRESSION_RATIO = 600;
90 private static final long MIN_COMPRESSION_RATIO_SIZE = 500 * 1000000L;
91 private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L;
93 private IngestServices services = IngestServices.getInstance();
94 private final IngestJobContext context;
95 private final FileTypeDetector fileTypeDetector;
97 private String moduleDirRelative;
98 private String moduleDirAbsolute;
100 private Blackboard blackboard;
102 private ProgressHandle progress;
103 private int numItems;
104 private String currentArchiveName;
106 private String getLocalRootAbsPath(String uniqueArchiveFileName) {
107 return moduleDirAbsolute + File.separator + uniqueArchiveFileName;
122 XRAR(
"application/x-rar-compressed");
127 this.mimeType = mimeType;
132 return this.mimeType;
137 SevenZipExtractor(
IngestJobContext context,
FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute)
throws SevenZipNativeInitializationException {
138 if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
139 SevenZip.initSevenZipFromPlatformJAR();
141 this.context = context;
142 this.fileTypeDetector = fileTypeDetector;
143 this.moduleDirRelative = moduleDirRelative;
144 this.moduleDirAbsolute = moduleDirAbsolute;
155 boolean isSevenZipExtractionSupported(AbstractFile file) {
156 String fileMimeType = fileTypeDetector.getMIMEType(file);
157 for (SupportedArchiveExtractionFormats mimeType : SupportedArchiveExtractionFormats.values()) {
158 if (mimeType.toString().equals(fileMimeType)) {
188 private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive,
int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
198 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) || archiveFile.getMIMEType().equalsIgnoreCase(SupportedArchiveExtractionFormats.XGZIP.toString())) {
203 final Long archiveItemSize = (Long) inArchive.getProperty(
204 inArchiveItemIndex, PropID.SIZE);
207 if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
211 final Long archiveItemPackedSize = (Long) inArchive.getProperty(
212 inArchiveItemIndex, PropID.PACKED_SIZE);
214 if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
215 logger.log(Level.WARNING,
"Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}",
216 new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)});
220 int cRatio = (int) (archiveItemSize / archiveItemPackedSize);
222 if (cRatio >= MAX_COMPRESSION_RATIO) {
223 Archive rootArchive = depthMap.get(depthMap.get(archiveFile.getId()).getRootArchiveId());
224 String details = NbBundle.getMessage(SevenZipExtractor.class,
225 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails",
226 cRatio, FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
228 flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedFilePath);
234 }
catch (SevenZipException ex) {
235 logger.log(Level.WARNING,
"Error getting archive item size and cannot detect if zipbomb. ", ex);
251 private void flagRootArchiveAsZipBomb(Archive rootArchive, AbstractFile archiveFile, String details, String escapedFilePath) {
252 rootArchive.flagAsZipBomb();
253 logger.log(Level.INFO, details);
255 Collection<BlackboardAttribute> attributes = Arrays.asList(
256 new BlackboardAttribute(
257 TSK_SET_NAME, MODULE_NAME,
258 "Possible Zip Bomb"),
259 new BlackboardAttribute(
260 TSK_DESCRIPTION, MODULE_NAME,
261 Bundle.SevenZipExtractor_zipBombArtifactCreation_text(archiveFile.getName())),
262 new BlackboardAttribute(
263 TSK_COMMENT, MODULE_NAME,
266 if (!blackboard.artifactExists(archiveFile, TSK_INTERESTING_FILE_HIT, attributes)) {
267 BlackboardArtifact artifact = rootArchive.getArchiveFile().newArtifact(TSK_INTERESTING_FILE_HIT);
268 artifact.addAttributes(attributes);
275 blackboard.postArtifact(artifact, MODULE_NAME);
277 String msg = NbBundle.getMessage(SevenZipExtractor.class,
278 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), escapedFilePath);
280 services.postMessage(IngestMessage.createWarningMessage(MODULE_NAME, msg, details));
282 }
catch (Blackboard.BlackboardException ex) {
283 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
284 MessageNotifyUtil.Notify.error(
285 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
288 }
catch (TskCoreException ex) {
289 logger.log(Level.SEVERE,
"Error creating blackboard artifact for Zip Bomb Detection for file: " + escapedFilePath, ex);
301 private ArchiveFormat get7ZipOptions(AbstractFile archiveFile) {
303 String detectedFormat;
304 detectedFormat = archiveFile.getMIMEType();
306 if (detectedFormat == null) {
307 logger.log(Level.WARNING,
"Could not detect format for file: {0}", archiveFile);
310 String extension = archiveFile.getNameExtension();
311 if (
"rar".equals(extension))
320 }
else if (detectedFormat.contains(
"application/x-rar-compressed"))
341 private long getRootArchiveId(AbstractFile file)
throws TskCoreException {
342 long id = file.getId();
343 Content parentContent = file.getParent();
344 while (parentContent != null) {
345 id = parentContent.getId();
346 parentContent = parentContent.getParent();
365 private List<AbstractFile> getAlreadyExtractedFiles(AbstractFile archiveFile, String archiveFilePath)
throws TskCoreException, NoCurrentCaseException {
368 if (archiveFile.hasChildren() &&
new File(moduleDirAbsolute, EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
369 return Case.getCurrentCaseThrows().getServices().getFileManager().findFilesByParentPath(getRootArchiveId(archiveFile), archiveFilePath);
371 return new ArrayList<>();
381 private String getArchiveFilePath(AbstractFile archiveFile) {
382 return archiveFile.getParentPath() + archiveFile.getName();
391 private void makeLocalDirectories(String uniqueArchiveFileName) {
392 final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName);
393 final File localRoot =
new File(localRootAbsPath);
394 if (!localRoot.exists()) {
411 private String getPathInArchive(ISevenZipInArchive archive,
int inArchiveItemIndex, AbstractFile archiveFile)
throws SevenZipException {
412 String pathInArchive = (String) archive.getProperty(
413 inArchiveItemIndex, PropID.PATH);
415 if (pathInArchive == null || pathInArchive.isEmpty()) {
421 String archName = archiveFile.getName();
422 int dotI = archName.lastIndexOf(
".");
423 String useName = null;
425 String base = archName.substring(0, dotI);
426 String ext = archName.substring(dotI);
427 int colonIndex = ext.lastIndexOf(
":");
428 if (colonIndex != -1) {
431 ext = ext.substring(0, colonIndex);
438 useName = base +
".tar";
445 if (useName == null) {
446 pathInArchive =
"/" + archName +
"/" + Integer.toString(inArchiveItemIndex);
448 pathInArchive =
"/" + useName;
450 String msg = NbBundle.getMessage(SevenZipExtractor.class,
451 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
452 getArchiveFilePath(archiveFile), pathInArchive);
453 logger.log(Level.WARNING, msg);
455 return pathInArchive;
462 private String getKeyAbstractFile(AbstractFile fileInDatabase) {
463 return fileInDatabase == null ? null : fileInDatabase.getParentPath() + fileInDatabase.getName();
470 private String getKeyFromUnpackedNode(UnpackedTree.UnpackedNode node, String archiveFilePath) {
471 return node == null ? null : archiveFilePath +
"/" + node.getFileName();
481 void unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) {
482 unpack(archiveFile, depthMap, null);
496 @Messages({
"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search.",
497 "# {0} - rootArchive",
498 "SevenZipExtractor.zipBombArtifactCreation.text=Zip Bomb Detected {0}"})
499 boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap, String password) {
500 boolean unpackSuccessful =
true;
501 boolean hasEncrypted =
false;
502 boolean fullEncryption =
true;
503 boolean progressStarted =
false;
504 final String archiveFilePath = getArchiveFilePath(archiveFile);
505 final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
506 HashMap<String, ZipFileStatusWrapper> statusMap =
new HashMap<>();
507 List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
508 ISevenZipInArchive inArchive = null;
509 currentArchiveName = archiveFile.getName();
511 SevenZipContentReadStream stream = null;
512 progress = ProgressHandle.createHandle(Bundle.EmbeddedFileExtractorIngestModule_ArchiveExtractor_moduleName());
516 blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
517 }
catch (NoCurrentCaseException ex) {
518 logger.log(Level.INFO,
"Exception while getting open case.", ex);
519 unpackSuccessful =
false;
520 return unpackSuccessful;
524 List<AbstractFile> existingFiles = getAlreadyExtractedFiles(archiveFile, archiveFilePath);
525 for (AbstractFile file : existingFiles) {
526 statusMap.put(getKeyAbstractFile(file),
new ZipFileStatusWrapper(file, ZipFileStatus.EXISTS));
528 }
catch (TskCoreException e) {
529 logger.log(Level.INFO,
"Error checking if file already has been processed, skipping: {0}", escapedArchiveFilePath);
530 unpackSuccessful =
false;
531 return unpackSuccessful;
532 }
catch (NoCurrentCaseException ex) {
533 logger.log(Level.INFO,
"No open case was found while trying to unpack the archive file {0}", escapedArchiveFilePath);
534 unpackSuccessful =
false;
535 return unpackSuccessful;
537 parentAr = depthMap.get(archiveFile.getId());
538 if (parentAr == null) {
539 parentAr =
new Archive(0, archiveFile.getId(), archiveFile);
540 depthMap.put(archiveFile.getId(), parentAr);
542 Archive rootArchive = depthMap.get(parentAr.getRootArchiveId());
543 if (rootArchive.isFlaggedAsZipBomb()) {
545 unpackSuccessful =
false;
546 return unpackSuccessful;
547 }
else if (parentAr.getDepth() == MAX_DEPTH) {
548 String details = NbBundle.getMessage(SevenZipExtractor.class,
549 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
550 parentAr.getDepth(), FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
551 flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedArchiveFilePath);
552 unpackSuccessful =
false;
553 return unpackSuccessful;
557 stream =
new SevenZipContentReadStream(
new ReadContentInputStream(archiveFile));
561 ArchiveFormat options = get7ZipOptions(archiveFile);
562 if (password == null) {
563 inArchive = SevenZip.openInArchive(options, stream);
565 inArchive = SevenZip.openInArchive(options, stream, password);
567 numItems = inArchive.getNumberOfItems();
568 progress.start(numItems);
569 progressStarted =
true;
572 final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
574 makeLocalDirectories(uniqueArchiveFileName);
575 }
catch (SecurityException e) {
576 logger.log(Level.SEVERE,
"Error setting up output path for archive root: {0}", getLocalRootAbsPath(uniqueArchiveFileName));
578 unpackSuccessful =
false;
579 return unpackSuccessful;
583 SevenZipExtractor.UnpackedTree unpackedTree =
new SevenZipExtractor.UnpackedTree(moduleDirRelative +
"/" + uniqueArchiveFileName, archiveFile);
587 freeDiskSpace = services.getFreeDiskSpace();
588 }
catch (NullPointerException ex) {
591 freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
594 Map<Integer, InArchiveItemDetails> archiveDetailsMap =
new HashMap<>();
595 for (
int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
596 progress.progress(String.format(
"%s: Analyzing archive metadata and creating local files (%d of %d)", currentArchiveName, inArchiveItemIndex + 1, numItems), 0);
597 if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
598 unpackSuccessful =
false;
599 return unpackSuccessful;
602 String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
603 UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
605 final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
607 if (isEncrypted && password == null) {
608 logger.log(Level.WARNING,
"Skipping encrypted file in archive: {0}", pathInArchive);
610 unpackSuccessful =
false;
613 fullEncryption =
false;
620 Long archiveItemSize = (Long) inArchive.getProperty(
621 inArchiveItemIndex, PropID.SIZE);
622 if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) {
623 String archiveItemPath = (String) inArchive.getProperty(
624 inArchiveItemIndex, PropID.PATH);
625 long newDiskSpace = freeDiskSpace - archiveItemSize;
626 if (newDiskSpace < MIN_FREE_DISK_SPACE) {
627 String msg = NbBundle.getMessage(SevenZipExtractor.class,
628 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
629 escapedArchiveFilePath, archiveItemPath);
630 String details = NbBundle.getMessage(SevenZipExtractor.class,
631 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
632 services.postMessage(IngestMessage.createErrorMessage(MODULE_NAME, msg, details));
633 logger.log(Level.INFO,
"Skipping archive item due to insufficient disk space: {0}, {1}",
new String[]{escapedArchiveFilePath, archiveItemPath});
634 logger.log(Level.INFO,
"Available disk space: {0}",
new Object[]{freeDiskSpace});
635 unpackSuccessful =
false;
639 freeDiskSpace = newDiskSpace;
642 final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex +
"_" +
new File(pathInArchive).getName());
643 final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
644 final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
647 File localFile =
new java.io.File(localAbsPath);
649 if (!localFile.exists()) {
651 if ((Boolean) inArchive.getProperty(
652 inArchiveItemIndex, PropID.IS_FOLDER)) {
655 localFile.getParentFile().mkdirs();
657 localFile.createNewFile();
658 }
catch (IOException e) {
659 logger.log(Level.SEVERE,
"Error creating extracted file: "
660 + localFile.getAbsolutePath(), e);
663 }
catch (SecurityException e) {
664 logger.log(Level.SEVERE,
"Error setting up output path for unpacked file: {0}",
671 if (localFile.exists() ==
false) {
679 archiveDetailsMap.put(inArchiveItemIndex,
new InArchiveItemDetails(
680 unpackedNode, localAbsPath, localRelPath));
683 int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
685 StandardIArchiveExtractCallback archiveCallBack
686 =
new StandardIArchiveExtractCallback(
687 inArchive, archiveFile, progress,
688 archiveDetailsMap, password, freeDiskSpace);
693 inArchive.extract(extractionIndices,
false, archiveCallBack);
695 unpackSuccessful &= archiveCallBack.wasSuccessful();
697 archiveDetailsMap = null;
702 unpackedTree.updateOrAddFileToCaseRec(statusMap, archiveFilePath);
703 unpackedFiles = unpackedTree.getAllFileObjects();
705 for (
int i = 0; i < unpackedFiles.size(); i++) {
706 progress.progress(String.format(
"%s: Searching for nested archives (%d of %d)", currentArchiveName, i + 1, unpackedFiles.size()));
707 AbstractFile unpackedFile = unpackedFiles.get(i);
708 if (unpackedFile == null) {
711 if (isSevenZipExtractionSupported(unpackedFile)) {
712 Archive child =
new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
713 parentAr.addChild(child);
714 depthMap.put(unpackedFile.getId(), child);
716 unpackedFile.close();
719 }
catch (TskCoreException | NoCurrentCaseException e) {
720 logger.log(Level.SEVERE,
"Error populating complete derived file hierarchy from the unpacked dir structure", e);
724 }
catch (SevenZipException ex) {
725 logger.log(Level.WARNING,
"Error unpacking file: " + archiveFile, ex);
729 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
730 String msg = NbBundle.getMessage(SevenZipExtractor.class,
731 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
733 String details = NbBundle.getMessage(SevenZipExtractor.class,
734 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
735 escapedArchiveFilePath, ex.getMessage());
736 services.postMessage(IngestMessage.createErrorMessage(MODULE_NAME, msg, details));
739 if (inArchive != null) {
742 }
catch (SevenZipException e) {
743 logger.log(Level.SEVERE,
"Error closing archive: " + archiveFile, e);
747 if (stream != null) {
750 }
catch (IOException ex) {
751 logger.log(Level.SEVERE,
"Error closing stream after unpacking archive: " + archiveFile, ex);
756 if (progressStarted) {
763 String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
765 BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
766 artifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, encryptionType));
774 blackboard.postArtifact(artifact, MODULE_NAME);
775 }
catch (Blackboard.BlackboardException ex) {
776 logger.log(Level.SEVERE,
"Unable to post blackboard artifact " + artifact.getArtifactID(), ex);
777 MessageNotifyUtil.Notify.error(
778 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
781 }
catch (TskCoreException ex) {
782 logger.log(Level.SEVERE,
"Error creating blackboard artifact for encryption detected for file: " + escapedArchiveFilePath, ex);
785 String msg = NbBundle.getMessage(SevenZipExtractor.class,
786 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
787 String details = NbBundle.getMessage(SevenZipExtractor.class,
788 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
789 currentArchiveName, MODULE_NAME);
790 services.postMessage(IngestMessage.createWarningMessage(MODULE_NAME, msg, details));
794 if (!unpackedFiles.isEmpty()) {
796 services.fireModuleContentEvent(
new ModuleContentEvent(archiveFile));
797 if (context != null) {
798 context.addFilesToJob(unpackedFiles);
801 return unpackSuccessful;
808 private int[] getExtractableFilesFromDetailsMap(
809 Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
811 Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
812 .toArray(
new Integer[archiveDetailsMap.size()]);
814 return Arrays.stream(wrappedExtractionIndices)
815 .mapToInt(Integer::intValue)
826 private final static class UnpackStream implements ISequentialOutStream {
833 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
835 this.bytesWritten = 0;
840 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
842 this.bytesWritten = 0;
850 public int write(byte[] bytes)
throws SevenZipException {
853 this.bytesWritten += bytes.length;
854 }
catch (IOException ex) {
855 throw new SevenZipException(
856 NbBundle.getMessage(SevenZipExtractor.class,
857 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
863 public void close() throws IOException {
864 try (EncodedFileOutputStream out = output) {
876 private final SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode;
881 SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode,
882 String localAbsPath, String localRelPath) {
906 implements IArchiveExtractCallback, ICryptoGetTextPassword {
923 private boolean unpackSuccessful =
true;
926 AbstractFile archiveFile, ProgressHandle progressHandle,
927 Map<Integer, InArchiveItemDetails> archiveDetailsMap,
928 String password,
long freeDiskSpace) {
952 public ISequentialOutStream
getStream(
int inArchiveItemIndex,
953 ExtractAskMode mode)
throws SevenZipException {
957 isFolder = (Boolean) inArchive
958 .getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
959 if (isFolder || mode != ExtractAskMode.EXTRACT) {
963 final String localAbsPath = archiveDetailsMap.get(
964 inArchiveItemIndex).getLocalAbsPath();
972 if (unpackStream != null) {
977 }
catch (IOException ex) {
978 logger.log(Level.WARNING, String.format(
"Error opening or setting new stream "
979 +
"for archive file at %s", localAbsPath), ex.getMessage());
996 final Date createTime = (Date) inArchive.getProperty(
997 inArchiveItemIndex, PropID.CREATION_TIME);
998 final Date accessTime = (Date) inArchive.getProperty(
999 inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
1000 final Date writeTime = (Date) inArchive.getProperty(
1001 inArchiveItemIndex, PropID.LAST_WRITE_TIME);
1003 createTimeInSeconds = createTime == null ? 0L
1004 : createTime.getTime() / 1000;
1005 modTimeInSeconds = writeTime == null ? 0L
1006 : writeTime.getTime() / 1000;
1007 accessTimeInSeconds = accessTime == null ? 0L
1008 : accessTime.getTime() / 1000;
1010 progressHandle.progress(archiveFile.getName() +
": "
1011 + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
1027 final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
1028 = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
1029 final String localRelPath = archiveDetailsMap.get(
1030 inArchiveItemIndex).getLocalRelPath();
1032 unpackedNode.addDerivedInfo(0,
1033 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1039 final String localAbsPath = archiveDetailsMap.get(
1040 inArchiveItemIndex).getLocalAbsPath();
1041 if (result != ExtractOperationResult.OK) {
1042 logger.log(Level.WARNING,
"Extraction of : {0} encountered error {1}",
1043 new Object[]{localAbsPath, result});
1044 unpackSuccessful =
false;
1048 unpackedNode.addDerivedInfo(unpackStream.
getSize(),
1049 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1053 unpackStream.
close();
1054 }
catch (IOException e) {
1055 logger.log(Level.WARNING,
"Error closing unpack stream for file: {0}", localAbsPath);
1060 public void setTotal(
long value)
throws SevenZipException {
1105 UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
1107 this.rootNode.setFile(archiveFile);
1108 this.rootNode.setFileName(archiveFile.getName());
1109 this.rootNode.setLocalRelPath(localPathRoot);
1121 UnpackedNode addNode(String filePath) {
1122 String[] toks = filePath.split(
"[\\/\\\\]");
1123 List<String> tokens =
new ArrayList<>();
1124 for (
int i = 0; i < toks.length; ++i) {
1125 if (!toks[i].isEmpty()) {
1126 tokens.add(toks[i]);
1129 return addNode(rootNode, tokens);
1142 if (tokenPath.isEmpty()) {
1147 String childName = tokenPath.remove(0);
1150 if (child == null) {
1152 parent.addChild(child);
1156 return addNode(child, tokenPath);
1165 List<AbstractFile> getRootFileObjects() {
1166 List<AbstractFile> ret =
new ArrayList<>();
1167 rootNode.getChildren().forEach((child) -> {
1168 ret.add(child.getFile());
1179 List<AbstractFile> getAllFileObjects() {
1180 List<AbstractFile> ret =
new ArrayList<>();
1181 rootNode.getChildren().forEach((child) -> {
1188 list.add(parent.getFile());
1189 parent.getChildren().forEach((child) -> {
1198 void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath)
throws TskCoreException,
NoCurrentCaseException {
1200 for (UnpackedNode child : rootNode.getChildren()) {
1201 updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
1221 progress.progress(String.format(
"%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems));
1223 String nameInDatabase = getKeyFromUnpackedNode(node, archiveFilePath);
1224 ZipFileStatusWrapper existingFile = nameInDatabase == null ? null : statusMap.get(nameInDatabase);
1225 if (existingFile == null) {
1226 df = fileManager.
addDerivedFile(node.getFileName(), node.getLocalRelPath(), node.getSize(),
1227 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1228 node.isIsFile(), node.getParent().getFile(),
"", MODULE_NAME,
1229 "",
"", TskData.EncodingType.XOR1);
1232 String key = getKeyAbstractFile(existingFile.
getFile());
1235 statusMap.put(key, existingFile);
1239 String mimeType = existingFile.
getFile().getMIMEType().equalsIgnoreCase(
"application/octet-stream") ? null : existingFile.
getFile().getMIMEType();
1241 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1242 node.isIsFile(), mimeType,
"", MODULE_NAME,
1243 "",
"", TskData.EncodingType.XOR1);
1247 df = (DerivedFile) existingFile.
getFile();
1251 }
catch (TskCoreException ex) {
1252 logger.log(Level.SEVERE,
"Error adding a derived file to db:" + node.getFileName(), ex);
1253 throw new TskCoreException(
1254 NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
1255 node.getFileName()), ex);
1259 updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
1270 private final List<UnpackedNode>
children =
new ArrayList<>();
1271 private String localRelPath =
"";
1273 private long ctime, crtime, atime, mtime;
1285 this.localRelPath = parent.getLocalRelPath() + File.separator +
fileName;
1304 void setFileName(String fileName) {
1313 void addChild(UnpackedNode child) {
1314 children.add(child);
1323 List<UnpackedNode> getChildren() {
1332 UnpackedNode getParent() {
1336 void addDerivedInfo(
long size,
1338 long ctime,
long crtime,
long atime,
long mtime, String relLocalPath) {
1342 this.crtime = crtime;
1345 this.localRelPath = relLocalPath;
1348 void setFile(AbstractFile file) {
1359 UnpackedNode getChild(String childFileName) {
1360 UnpackedNode ret = null;
1361 for (UnpackedNode child : children) {
1362 if (child.getFileName().equals(childFileName)) {
1370 String getFileName() {
1374 AbstractFile getFile() {
1378 String getLocalRelPath() {
1388 void setLocalRelPath(String localRelativePath) {
1389 localRelPath = localRelativePath;
1396 boolean isIsFile() {
1406 static class Archive {
1409 private final int depth;
1410 private final List<Archive> children;
1411 private final long rootArchiveId;
1412 private boolean flaggedAsZipBomb =
false;
1413 private final AbstractFile archiveFile;
1427 Archive(
int depth,
long rootArchiveId, AbstractFile archiveFile) {
1428 this.children =
new ArrayList<>();
1430 this.rootArchiveId = rootArchiveId;
1431 this.archiveFile = archiveFile;
1440 void addChild(Archive child) {
1441 children.add(child);
1448 synchronized void flagAsZipBomb() {
1449 flaggedAsZipBomb =
true;
1457 synchronized boolean isFlaggedAsZipBomb() {
1458 return flaggedAsZipBomb;
1466 AbstractFile getArchiveFile() {
1475 long getRootArchiveId() {
1476 return rootArchiveId;
1484 long getObjectId() {
1485 return archiveFile.getId();
1517 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)