19 package org.sleuthkit.autopsy.modules.embeddedfileextractor;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.nio.charset.Charset;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.List;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.util.logging.Level;
35 import net.sf.sevenzipjbinding.ArchiveFormat;
36 import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
37 import net.sf.sevenzipjbinding.ExtractAskMode;
38 import net.sf.sevenzipjbinding.ExtractOperationResult;
39 import net.sf.sevenzipjbinding.IArchiveExtractCallback;
40 import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
41 import net.sf.sevenzipjbinding.IInArchive;
42 import net.sf.sevenzipjbinding.ISequentialOutStream;
43 import net.sf.sevenzipjbinding.PropID;
44 import net.sf.sevenzipjbinding.SevenZip;
45 import net.sf.sevenzipjbinding.SevenZipException;
46 import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
47 import org.apache.tika.parser.txt.CharsetDetector;
48 import org.apache.tika.parser.txt.CharsetMatch;
49 import org.netbeans.api.progress.ProgressHandle;
50 import org.openide.util.NbBundle;
51 import org.openide.util.NbBundle.Messages;
67 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT;
69 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
70 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
71 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
79 class SevenZipExtractor {
81 private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName());
83 private static final String MODULE_NAME = EmbeddedFileExtractorModuleFactory.getModuleName();
86 private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
87 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
88 private static final String ENCRYPTION_FULL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
89 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull");
92 private static final int MAX_DEPTH = 4;
93 private static final int MAX_COMPRESSION_RATIO = 600;
94 private static final long MIN_COMPRESSION_RATIO_SIZE = 500 * 1000000L;
95 private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L;
97 private IngestServices services = IngestServices.getInstance();
98 private final IngestJobContext context;
99 private final FileTypeDetector fileTypeDetector;
101 private String moduleDirRelative;
102 private String moduleDirAbsolute;
104 private Blackboard blackboard;
106 private ProgressHandle progress;
107 private int numItems;
108 private String currentArchiveName;
110 private String getLocalRootAbsPath(String uniqueArchiveFileName) {
111 return moduleDirAbsolute + File.separator + uniqueArchiveFileName;
126 XRAR(
"application/x-rar-compressed");
131 this.mimeType = mimeType;
136 return this.mimeType;
141 SevenZipExtractor(
IngestJobContext context,
FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute)
throws SevenZipNativeInitializationException {
142 if (!SevenZip.isInitializedSuccessfully()) {
143 throw new SevenZipNativeInitializationException(
"SevenZip has not been previously initialized.");
146 this.context = context;
147 this.fileTypeDetector = fileTypeDetector;
148 this.moduleDirRelative = moduleDirRelative;
149 this.moduleDirAbsolute = moduleDirAbsolute;
160 boolean isSevenZipExtractionSupported(AbstractFile file) {
161 String fileMimeType = fileTypeDetector.getMIMEType(file);
162 for (SupportedArchiveExtractionFormats mimeType : SupportedArchiveExtractionFormats.values()) {
163 if (mimeType.toString().equals(fileMimeType)) {
193 private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, IInArchive inArchive,
int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
203 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) || archiveFile.getMIMEType().equalsIgnoreCase(SupportedArchiveExtractionFormats.XGZIP.toString())) {
208 final Long archiveItemSize = (Long) inArchive.getProperty(
209 inArchiveItemIndex, PropID.SIZE);
212 if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
216 final Long archiveItemPackedSize = (Long) inArchive.getProperty(
217 inArchiveItemIndex, PropID.PACKED_SIZE);
219 if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
220 logger.log(Level.WARNING,
"Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}",
221 new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)});
225 int cRatio = (int) (archiveItemSize / archiveItemPackedSize);
227 if (cRatio >= MAX_COMPRESSION_RATIO) {
228 Archive rootArchive = depthMap.get(depthMap.get(archiveFile.getId()).getRootArchiveId());
229 String details = NbBundle.getMessage(SevenZipExtractor.class,
230 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails",
231 cRatio, FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
233 flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedFilePath);
239 }
catch (SevenZipException ex) {
240 logger.log(Level.WARNING,
"Error getting archive item size and cannot detect if zipbomb. ", ex);
256 private void flagRootArchiveAsZipBomb(Archive rootArchive, AbstractFile archiveFile, String details, String escapedFilePath) {
257 rootArchive.flagAsZipBomb();
258 logger.log(Level.INFO, details);
260 Collection<BlackboardAttribute> attributes = Arrays.asList(
261 new BlackboardAttribute(
262 TSK_SET_NAME, MODULE_NAME,
263 "Possible Zip Bomb"),
264 new BlackboardAttribute(
265 TSK_DESCRIPTION, MODULE_NAME,
266 Bundle.SevenZipExtractor_zipBombArtifactCreation_text(archiveFile.getName())),
267 new BlackboardAttribute(
268 TSK_COMMENT, MODULE_NAME,
271 if (!blackboard.artifactExists(archiveFile, TSK_INTERESTING_FILE_HIT, attributes)) {
272 BlackboardArtifact artifact = rootArchive.getArchiveFile().newArtifact(TSK_INTERESTING_FILE_HIT);
273 artifact.addAttributes(attributes);
280 blackboard.postArtifact(artifact, MODULE_NAME);
282 String msg = NbBundle.getMessage(SevenZipExtractor.class,
283 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), escapedFilePath);
285 services.postMessage(IngestMessage.createWarningMessage(MODULE_NAME, msg, details));
287 }
catch (Blackboard.BlackboardException ex) {
288 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
289 MessageNotifyUtil.Notify.error(
290 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
293 }
catch (TskCoreException ex) {
294 logger.log(Level.SEVERE,
"Error creating blackboard artifact for Zip Bomb Detection for file: " + escapedFilePath, ex);
306 private ArchiveFormat get7ZipOptions(AbstractFile archiveFile) {
308 String detectedFormat;
309 detectedFormat = archiveFile.getMIMEType();
311 if (detectedFormat == null) {
312 logger.log(Level.WARNING,
"Could not detect format for file: {0}", archiveFile);
315 String extension = archiveFile.getNameExtension();
316 if (
"rar".equals(extension))
325 }
else if (detectedFormat.contains(
"application/x-rar-compressed"))
346 private long getRootArchiveId(AbstractFile file)
throws TskCoreException {
347 long id = file.getId();
348 Content parentContent = file.getParent();
349 while (parentContent != null) {
350 id = parentContent.getId();
351 parentContent = parentContent.getParent();
370 private List<AbstractFile> getAlreadyExtractedFiles(AbstractFile archiveFile, String archiveFilePath)
throws TskCoreException, NoCurrentCaseException {
373 if (archiveFile.hasChildren() &&
new File(moduleDirAbsolute, EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
374 return Case.getCurrentCaseThrows().getServices().getFileManager().findFilesByParentPath(getRootArchiveId(archiveFile), archiveFilePath);
376 return new ArrayList<>();
386 private String getArchiveFilePath(AbstractFile archiveFile) {
387 return archiveFile.getParentPath() + archiveFile.getName();
396 private void makeLocalDirectories(String uniqueArchiveFileName) {
397 final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName);
398 final File localRoot =
new File(localRootAbsPath);
399 if (!localRoot.exists()) {
415 private String getPathInArchive(IInArchive archive,
int inArchiveItemIndex, AbstractFile archiveFile)
throws SevenZipException {
416 String pathInArchive = (String) archive.getProperty(inArchiveItemIndex, PropID.PATH);
418 if (pathInArchive == null || pathInArchive.isEmpty()) {
424 String archName = archiveFile.getName();
425 int dotI = archName.lastIndexOf(
".");
426 String useName = null;
428 String base = archName.substring(0, dotI);
429 String ext = archName.substring(dotI);
430 int colonIndex = ext.lastIndexOf(
":");
431 if (colonIndex != -1) {
434 ext = ext.substring(0, colonIndex);
441 useName = base +
".tar";
448 if (useName == null) {
449 pathInArchive =
"/" + archName +
"/" + Integer.toString(inArchiveItemIndex);
451 pathInArchive =
"/" + useName;
454 return pathInArchive;
457 private byte[] getPathBytesInArchive(IInArchive archive,
int inArchiveItemIndex, AbstractFile archiveFile)
throws SevenZipException {
458 return (byte[]) archive.getProperty(inArchiveItemIndex, PropID.PATH_BYTES);
465 private String getKeyAbstractFile(AbstractFile fileInDatabase) {
466 return fileInDatabase == null ? null : fileInDatabase.getParentPath() + fileInDatabase.getName();
473 private String getKeyFromUnpackedNode(UnpackedTree.UnpackedNode node, String archiveFilePath) {
474 return node == null ? null : archiveFilePath +
"/" + node.getFileName();
484 void unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) {
485 unpack(archiveFile, depthMap, null);
499 @Messages({
"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search.",
500 "# {0} - rootArchive",
501 "SevenZipExtractor.zipBombArtifactCreation.text=Zip Bomb Detected {0}"})
502 boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap, String password) {
503 boolean unpackSuccessful =
true;
504 boolean hasEncrypted =
false;
505 boolean fullEncryption =
true;
506 boolean progressStarted =
false;
507 final String archiveFilePath = getArchiveFilePath(archiveFile);
508 final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
509 HashMap<String, ZipFileStatusWrapper> statusMap =
new HashMap<>();
510 List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
512 currentArchiveName = archiveFile.getName();
514 SevenZipContentReadStream stream = null;
515 progress = ProgressHandle.createHandle(Bundle.EmbeddedFileExtractorIngestModule_ArchiveExtractor_moduleName());
519 blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
520 }
catch (NoCurrentCaseException ex) {
521 logger.log(Level.INFO,
"Exception while getting open case.", ex);
522 unpackSuccessful =
false;
523 return unpackSuccessful;
526 List<AbstractFile> existingFiles = getAlreadyExtractedFiles(archiveFile, archiveFilePath);
527 for (AbstractFile file : existingFiles) {
528 statusMap.put(getKeyAbstractFile(file),
new ZipFileStatusWrapper(file, ZipFileStatus.EXISTS));
530 }
catch (TskCoreException e) {
531 logger.log(Level.INFO,
"Error checking if file already has been processed, skipping: {0}", escapedArchiveFilePath);
532 unpackSuccessful =
false;
533 return unpackSuccessful;
534 }
catch (NoCurrentCaseException ex) {
535 logger.log(Level.INFO,
"No open case was found while trying to unpack the archive file {0}", escapedArchiveFilePath);
536 unpackSuccessful =
false;
537 return unpackSuccessful;
539 parentAr = depthMap.get(archiveFile.getId());
540 if (parentAr == null) {
541 parentAr =
new Archive(0, archiveFile.getId(), archiveFile);
542 depthMap.put(archiveFile.getId(), parentAr);
544 Archive rootArchive = depthMap.get(parentAr.getRootArchiveId());
545 if (rootArchive.isFlaggedAsZipBomb()) {
547 unpackSuccessful =
false;
548 return unpackSuccessful;
549 }
else if (parentAr.getDepth() == MAX_DEPTH) {
550 String details = NbBundle.getMessage(SevenZipExtractor.class,
551 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
552 parentAr.getDepth(), FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
553 flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedArchiveFilePath);
554 unpackSuccessful =
false;
555 return unpackSuccessful;
558 IInArchive inArchive = null;
560 stream =
new SevenZipContentReadStream(
new ReadContentInputStream(archiveFile));
564 ArchiveFormat options = get7ZipOptions(archiveFile);
565 if (password == null) {
566 inArchive = SevenZip.openInArchive(options, stream);
568 inArchive = SevenZip.openInArchive(options, stream, password);
570 numItems = inArchive.getNumberOfItems();
571 progress.start(numItems);
572 progressStarted =
true;
575 final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
577 makeLocalDirectories(uniqueArchiveFileName);
578 }
catch (SecurityException e) {
579 logger.log(Level.SEVERE,
"Error setting up output path for archive root: {0}", getLocalRootAbsPath(uniqueArchiveFileName));
581 unpackSuccessful =
false;
582 return unpackSuccessful;
586 SevenZipExtractor.UnpackedTree unpackedTree =
new SevenZipExtractor.UnpackedTree(moduleDirRelative +
"/" + uniqueArchiveFileName, archiveFile);
590 freeDiskSpace = services.getFreeDiskSpace();
591 }
catch (NullPointerException ex) {
594 freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
597 Map<Integer, InArchiveItemDetails> archiveDetailsMap =
new HashMap<>();
598 for (
int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
599 progress.progress(String.format(
"%s: Analyzing archive metadata and creating local files (%d of %d)", currentArchiveName, inArchiveItemIndex + 1, numItems), 0);
600 if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
601 unpackSuccessful =
false;
602 return unpackSuccessful;
605 String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
606 byte[] pathBytesInArchive = getPathBytesInArchive(inArchive, inArchiveItemIndex, archiveFile);
607 UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive, pathBytesInArchive);
609 final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
611 if (isEncrypted && password == null) {
612 logger.log(Level.WARNING,
"Skipping encrypted file in archive: {0}", pathInArchive);
614 unpackSuccessful =
false;
617 fullEncryption =
false;
624 Long archiveItemSize = (Long) inArchive.getProperty(
625 inArchiveItemIndex, PropID.SIZE);
626 if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) {
627 String archiveItemPath = (String) inArchive.getProperty(
628 inArchiveItemIndex, PropID.PATH);
629 long newDiskSpace = freeDiskSpace - archiveItemSize;
630 if (newDiskSpace < MIN_FREE_DISK_SPACE) {
631 String msg = NbBundle.getMessage(SevenZipExtractor.class,
632 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
633 escapedArchiveFilePath, archiveItemPath);
634 String details = NbBundle.getMessage(SevenZipExtractor.class,
635 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
636 services.postMessage(IngestMessage.createErrorMessage(MODULE_NAME, msg, details));
637 logger.log(Level.INFO,
"Skipping archive item due to insufficient disk space: {0}, {1}",
new String[]{escapedArchiveFilePath, archiveItemPath});
638 logger.log(Level.INFO,
"Available disk space: {0}",
new Object[]{freeDiskSpace});
639 unpackSuccessful =
false;
643 freeDiskSpace = newDiskSpace;
646 final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex +
"_" +
new File(pathInArchive).getName());
647 final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
648 final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
651 File localFile =
new java.io.File(localAbsPath);
653 if (!localFile.exists()) {
655 if ((Boolean) inArchive.getProperty(
656 inArchiveItemIndex, PropID.IS_FOLDER)) {
659 localFile.getParentFile().mkdirs();
661 localFile.createNewFile();
662 }
catch (IOException e) {
663 logger.log(Level.SEVERE,
"Error creating extracted file: "
664 + localFile.getAbsolutePath(), e);
667 }
catch (SecurityException e) {
668 logger.log(Level.SEVERE,
"Error setting up output path for unpacked file: {0}",
675 if (localFile.exists() ==
false) {
683 archiveDetailsMap.put(inArchiveItemIndex,
new InArchiveItemDetails(
684 unpackedNode, localAbsPath, localRelPath));
687 int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
689 StandardIArchiveExtractCallback archiveCallBack
690 =
new StandardIArchiveExtractCallback(
691 inArchive, archiveFile, progress,
692 archiveDetailsMap, password, freeDiskSpace);
697 inArchive.extract(extractionIndices,
false, archiveCallBack);
699 unpackSuccessful &= archiveCallBack.wasSuccessful();
701 archiveDetailsMap = null;
706 unpackedTree.updateOrAddFileToCaseRec(statusMap, archiveFilePath);
707 unpackedFiles = unpackedTree.getAllFileObjects();
709 for (
int i = 0; i < unpackedFiles.size(); i++) {
710 progress.progress(String.format(
"%s: Searching for nested archives (%d of %d)", currentArchiveName, i + 1, unpackedFiles.size()));
711 AbstractFile unpackedFile = unpackedFiles.get(i);
712 if (unpackedFile == null) {
715 if (isSevenZipExtractionSupported(unpackedFile)) {
716 Archive child =
new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
717 parentAr.addChild(child);
718 depthMap.put(unpackedFile.getId(), child);
720 unpackedFile.close();
723 }
catch (TskCoreException | NoCurrentCaseException e) {
724 logger.log(Level.SEVERE,
"Error populating complete derived file hierarchy from the unpacked dir structure", e);
728 }
catch (SevenZipException ex) {
729 logger.log(Level.WARNING,
"Error unpacking file: " + archiveFile, ex);
733 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
734 String msg = NbBundle.getMessage(SevenZipExtractor.class,
735 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
737 String details = NbBundle.getMessage(SevenZipExtractor.class,
738 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
739 escapedArchiveFilePath, ex.getMessage());
740 services.postMessage(IngestMessage.createErrorMessage(MODULE_NAME, msg, details));
743 if (inArchive != null) {
746 }
catch (SevenZipException e) {
747 logger.log(Level.SEVERE,
"Error closing archive: " + archiveFile, e);
751 if (stream != null) {
754 }
catch (IOException ex) {
755 logger.log(Level.SEVERE,
"Error closing stream after unpacking archive: " + archiveFile, ex);
760 if (progressStarted) {
767 String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
769 BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
770 artifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, encryptionType));
778 blackboard.postArtifact(artifact, MODULE_NAME);
779 }
catch (Blackboard.BlackboardException ex) {
780 logger.log(Level.SEVERE,
"Unable to post blackboard artifact " + artifact.getArtifactID(), ex);
781 MessageNotifyUtil.Notify.error(
782 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
785 }
catch (TskCoreException ex) {
786 logger.log(Level.SEVERE,
"Error creating blackboard artifact for encryption detected for file: " + escapedArchiveFilePath, ex);
789 String msg = NbBundle.getMessage(SevenZipExtractor.class,
790 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
791 String details = NbBundle.getMessage(SevenZipExtractor.class,
792 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
793 currentArchiveName, MODULE_NAME);
794 services.postMessage(IngestMessage.createWarningMessage(MODULE_NAME, msg, details));
798 if (!unpackedFiles.isEmpty()) {
800 services.fireModuleContentEvent(
new ModuleContentEvent(archiveFile));
801 if (context != null) {
802 context.addFilesToJob(unpackedFiles);
805 return unpackSuccessful;
808 private Charset detectFilenamesCharset(List<byte[]> byteDatas) {
809 Charset detectedCharset = null;
810 CharsetDetector charsetDetector =
new CharsetDetector();
813 for (byte[] byteData : byteDatas) {
815 byteSum += byteData.length;
817 if (byteSum >= 1000) {
821 byte[] allBytes =
new byte[byteSum];
823 for (
int i = 0; i < fileNum; i++) {
824 byte[] byteData = byteDatas.get(i);
825 System.arraycopy(byteData, 0, allBytes, start, byteData.length);
826 start += byteData.length;
828 charsetDetector.setText(allBytes);
829 CharsetMatch cm = charsetDetector.detect();
830 if (cm.getConfidence() >= 90 && Charset.isSupported(cm.getName())) {
831 detectedCharset = Charset.forName(cm.getName());
833 return detectedCharset;
840 private int[] getExtractableFilesFromDetailsMap(
841 Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
843 Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
844 .toArray(
new Integer[archiveDetailsMap.size()]);
846 return Arrays.stream(wrappedExtractionIndices)
847 .mapToInt(Integer::intValue)
858 private final static class UnpackStream implements ISequentialOutStream {
865 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
867 this.bytesWritten = 0;
872 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
874 this.bytesWritten = 0;
882 public int write(byte[] bytes)
throws SevenZipException {
885 this.bytesWritten += bytes.length;
886 }
catch (IOException ex) {
887 throw new SevenZipException(
888 NbBundle.getMessage(SevenZipExtractor.class,
889 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
895 public void close() throws IOException {
896 try (EncodedFileOutputStream out = output) {
908 private final SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode;
913 SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode,
914 String localAbsPath, String localRelPath) {
938 implements IArchiveExtractCallback, ICryptoGetTextPassword {
955 private boolean unpackSuccessful =
true;
958 AbstractFile archiveFile, ProgressHandle progressHandle,
959 Map<Integer, InArchiveItemDetails> archiveDetailsMap,
960 String password,
long freeDiskSpace) {
983 public ISequentialOutStream
getStream(
int inArchiveItemIndex,
984 ExtractAskMode mode)
throws SevenZipException {
988 isFolder = (Boolean) inArchive
989 .getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
990 if (isFolder || mode != ExtractAskMode.EXTRACT) {
994 final String localAbsPath = archiveDetailsMap.get(
995 inArchiveItemIndex).getLocalAbsPath();
1003 if (unpackStream != null) {
1008 }
catch (IOException ex) {
1009 logger.log(Level.WARNING, String.format(
"Error opening or setting new stream "
1010 +
"for archive file at %s", localAbsPath), ex.getMessage());
1027 final Date createTime = (Date) inArchive.getProperty(
1028 inArchiveItemIndex, PropID.CREATION_TIME);
1029 final Date accessTime = (Date) inArchive.getProperty(
1030 inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
1031 final Date writeTime = (Date) inArchive.getProperty(
1032 inArchiveItemIndex, PropID.LAST_MODIFICATION_TIME);
1034 createTimeInSeconds = createTime == null ? 0L
1035 : createTime.getTime() / 1000;
1036 modTimeInSeconds = writeTime == null ? 0L
1037 : writeTime.getTime() / 1000;
1038 accessTimeInSeconds = accessTime == null ? 0L
1039 : accessTime.getTime() / 1000;
1041 progressHandle.progress(archiveFile.getName() +
": "
1042 + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
1058 final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
1059 = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
1060 final String localRelPath = archiveDetailsMap.get(
1061 inArchiveItemIndex).getLocalRelPath();
1063 unpackedNode.addDerivedInfo(0,
1064 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1070 final String localAbsPath = archiveDetailsMap.get(
1071 inArchiveItemIndex).getLocalAbsPath();
1072 if (result != ExtractOperationResult.OK) {
1073 logger.log(Level.WARNING,
"Extraction of : {0} encountered error {1}",
1074 new Object[]{localAbsPath, result});
1075 unpackSuccessful =
false;
1079 unpackedNode.addDerivedInfo(unpackStream.
getSize(),
1080 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1084 unpackStream.
close();
1085 }
catch (IOException e) {
1086 logger.log(Level.WARNING,
"Error closing unpack stream for file: {0}", localAbsPath);
1091 public void setTotal(
long value)
throws SevenZipException {
1135 UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
1137 this.rootNode.setFile(archiveFile);
1138 this.rootNode.setFileName(archiveFile.getName());
1139 this.rootNode.setLocalRelPath(localPathRoot);
1151 UnpackedNode addNode(String filePath, byte[] filePathBytes) {
1152 String[] toks = filePath.split(
"[\\/\\\\]");
1153 List<String> tokens =
new ArrayList<>();
1154 for (
int i = 0; i < toks.length; ++i) {
1155 if (!toks[i].isEmpty()) {
1156 tokens.add(toks[i]);
1160 List<byte[]> byteTokens = null;
1161 if (filePathBytes == null) {
1162 return addNode(rootNode, tokens, null);
1164 byteTokens =
new ArrayList<>(tokens.size());
1166 for (
int i = 0; i < filePathBytes.length; i++) {
1167 if (filePathBytes[i] ==
'/') {
1169 byte[] arr =
new byte[len];
1170 System.arraycopy(filePathBytes, last, arr, 0, len);
1171 byteTokens.add(arr);
1175 int len = filePathBytes.length - last;
1177 byte[] arr =
new byte[len];
1178 System.arraycopy(filePathBytes, last, arr, 0, len);
1179 byteTokens.add(arr);
1182 if (tokens.size() != byteTokens.size()) {
1183 logger.log(Level.WARNING,
"Could not map path bytes to path string");
1184 return addNode(rootNode, tokens, null);
1188 return addNode(rootNode, tokens, byteTokens);
1201 List<String> tokenPath, List<byte[]> tokenPathBytes) {
1203 if (tokenPath.isEmpty()) {
1208 String childName = tokenPath.remove(0);
1209 byte[] childNameBytes = null;
1210 if (tokenPathBytes != null) {
1211 childNameBytes = tokenPathBytes.remove(0);
1215 if (child == null) {
1217 child.setFileNameBytes(childNameBytes);
1218 parent.addChild(child);
1222 return addNode(child, tokenPath, tokenPathBytes);
1231 List<AbstractFile> getRootFileObjects() {
1232 List<AbstractFile> ret =
new ArrayList<>();
1233 rootNode.getChildren().forEach((child) -> {
1234 ret.add(child.getFile());
1245 List<AbstractFile> getAllFileObjects() {
1246 List<AbstractFile> ret =
new ArrayList<>();
1247 rootNode.getChildren().forEach((child) -> {
1254 list.add(parent.getFile());
1255 parent.getChildren().forEach((child) -> {
1264 void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath)
throws TskCoreException,
NoCurrentCaseException {
1266 for (UnpackedNode child : rootNode.getChildren()) {
1267 updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
1287 progress.progress(String.format(
"%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems));
1289 String nameInDatabase = getKeyFromUnpackedNode(node, archiveFilePath);
1290 ZipFileStatusWrapper existingFile = nameInDatabase == null ? null : statusMap.get(nameInDatabase);
1291 if (existingFile == null) {
1292 df = fileManager.
addDerivedFile(node.getFileName(), node.getLocalRelPath(), node.getSize(),
1293 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1294 node.isIsFile(), node.getParent().getFile(),
"", MODULE_NAME,
1295 "",
"", TskData.EncodingType.XOR1);
1298 String key = getKeyAbstractFile(existingFile.
getFile());
1301 statusMap.put(key, existingFile);
1305 String mimeType = existingFile.
getFile().getMIMEType().equalsIgnoreCase(
"application/octet-stream") ? null : existingFile.
getFile().getMIMEType();
1307 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1308 node.isIsFile(), mimeType,
"", MODULE_NAME,
1309 "",
"", TskData.EncodingType.XOR1);
1313 df = (DerivedFile) existingFile.
getFile();
1317 }
catch (TskCoreException ex) {
1318 logger.log(Level.SEVERE,
"Error adding a derived file to db:" + node.getFileName(), ex);
1319 throw new TskCoreException(
1320 NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
1321 node.getFileName()), ex);
1325 if (node.getChildren().size() > 0) {
1327 ArrayList<byte[]> byteDatas =
new ArrayList<>();
1329 byte[] childBytes = child.getFileNameBytes();
1330 if (childBytes != null) {
1331 byteDatas.add(childBytes);
1333 names += child.getFileName();
1335 Charset detectedCharset = detectFilenamesCharset(byteDatas);
1338 if (detectedCharset != null && detectedCharset.canEncode()) {
1340 byte[] childBytes = child.getFileNameBytes();
1341 if (childBytes != null) {
1342 String decodedName =
new String(childBytes, detectedCharset);
1343 child.setFileName(decodedName);
1351 updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
1363 private final List<UnpackedNode>
children =
new ArrayList<>();
1364 private String localRelPath =
"";
1366 private long ctime, crtime, atime, mtime;
1378 this.localRelPath = parent.getLocalRelPath() + File.separator +
fileName;
1397 void setFileName(String fileName) {
1406 void addChild(UnpackedNode child) {
1407 children.add(child);
1416 List<UnpackedNode> getChildren() {
1425 UnpackedNode getParent() {
1429 void addDerivedInfo(
long size,
1431 long ctime,
long crtime,
long atime,
long mtime, String relLocalPath) {
1435 this.crtime = crtime;
1438 this.localRelPath = relLocalPath;
1441 void setFile(AbstractFile file) {
1452 UnpackedNode getChild(String childFileName) {
1453 UnpackedNode ret = null;
1454 for (UnpackedNode child : children) {
1455 if (child.getFileName().equals(childFileName)) {
1463 String getFileName() {
1467 AbstractFile getFile() {
1471 String getLocalRelPath() {
1481 void setLocalRelPath(String localRelativePath) {
1482 localRelPath = localRelativePath;
1489 boolean isIsFile() {
1493 void setFileNameBytes(byte[] fileNameBytes) {
1494 if (fileNameBytes != null) {
1495 this.fileNameBytes = Arrays.copyOf(fileNameBytes, fileNameBytes.length);
1499 byte[] getFileNameBytes() {
1500 if (fileNameBytes == null) {
1503 return Arrays.copyOf(fileNameBytes, fileNameBytes.length);
1512 static class Archive {
1515 private final int depth;
1516 private final List<Archive> children;
1517 private final long rootArchiveId;
1518 private boolean flaggedAsZipBomb =
false;
1519 private final AbstractFile archiveFile;
1533 Archive(
int depth,
long rootArchiveId, AbstractFile archiveFile) {
1534 this.children =
new ArrayList<>();
1536 this.rootArchiveId = rootArchiveId;
1537 this.archiveFile = archiveFile;
1546 void addChild(Archive child) {
1547 children.add(child);
1554 synchronized void flagAsZipBomb() {
1555 flaggedAsZipBomb =
true;
1563 synchronized boolean isFlaggedAsZipBomb() {
1564 return flaggedAsZipBomb;
1572 AbstractFile getArchiveFile() {
1581 long getRootArchiveId() {
1582 return rootArchiveId;
1590 long getObjectId() {
1591 return archiveFile.getId();
1623 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)