21 package org.sleuthkit.autopsy.recentactivity;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.RandomAccessFile;
27 import java.nio.ByteBuffer;
28 import java.nio.ByteOrder;
29 import java.nio.channels.FileChannel;
30 import java.nio.charset.Charset;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.List;
38 import java.util.Map.Entry;
39 import java.util.Optional;
40 import java.util.logging.Level;
41 import org.openide.util.NbBundle;
42 import org.openide.util.NbBundle.Messages;
56 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
77 final class ChromeCacheExtractor {
79 private final static String DEFAULT_CACHE_PATH_STR =
"default/cache";
80 private final static String BROTLI_MIMETYPE =
"application/x-brotli";
82 private final static long UINT32_MASK = 0xFFFFFFFFl;
84 private final static int INDEXFILE_HDR_SIZE = 92*4;
85 private final static int DATAFILE_HDR_SIZE = 8192;
87 private final static Logger logger = Logger.getLogger(ChromeCacheExtractor.class.getName());
89 private static final String VERSION_NUMBER =
"1.0.0";
90 private final String moduleName;
92 private String absOutputFolderName;
93 private String relOutputFolderName;
95 private final Content dataSource;
96 private final IngestJobContext context;
97 private final DataSourceIngestModuleProgress progressBar;
98 private final IngestServices services = IngestServices.getInstance();
99 private Case currentCase;
100 private FileManager fileManager;
103 private final Map<String, CacheFileCopy> fileCopyCache =
new HashMap<>();
106 private final Map<String, AbstractFile> externalFilesTable =
new HashMap<>();
112 final class CacheFileCopy {
114 private final AbstractFile abstractFile;
115 private final RandomAccessFile fileCopy;
116 private final ByteBuffer byteBuffer;
118 CacheFileCopy (AbstractFile abstractFile, RandomAccessFile fileCopy, ByteBuffer buffer ) {
119 this.abstractFile = abstractFile;
120 this.fileCopy = fileCopy;
121 this.byteBuffer = buffer;
124 public RandomAccessFile getFileCopy() {
127 public ByteBuffer getByteBuffer() {
130 AbstractFile getAbstractFile() {
136 "ChromeCacheExtractor.moduleName=ChromeCacheExtractor",
137 "# {0} - module name",
138 "# {1} - row number",
139 "# {2} - table length",
140 "# {3} - cache path",
141 "ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}"
143 ChromeCacheExtractor(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar ) {
144 moduleName = Bundle.ChromeCacheExtractor_moduleName();
145 this.dataSource = dataSource;
146 this.context = context;
147 this.progressBar = progressBar;
156 private void moduleInit() throws IngestModuleException {
159 currentCase = Case.getCurrentCaseThrows();
160 fileManager = currentCase.getServices().getFileManager();
163 absOutputFolderName = RAImageIngestModule.getRAOutputPath(currentCase, moduleName);
164 relOutputFolderName = Paths.get( RAImageIngestModule.getRelModuleOutputPath(), moduleName).normalize().toString();
166 File dir =
new File(absOutputFolderName);
167 if (dir.exists() ==
false) {
170 }
catch (NoCurrentCaseException ex) {
171 String msg =
"Failed to get current case.";
172 throw new IngestModuleException(msg, ex);
183 private void resetForNewFolder(String cachePath)
throws IngestModuleException {
185 fileCopyCache.clear();
186 externalFilesTable.clear();
188 String cacheAbsOutputFolderName = this.getAbsOutputFolderName() + cachePath;
189 File outDir =
new File(cacheAbsOutputFolderName);
190 if (outDir.exists() ==
false) {
194 String cacheTempPath = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath;
195 File tempDir =
new File(cacheTempPath);
196 if (tempDir.exists() ==
false) {
207 private void cleanup () {
209 for (Entry<String, CacheFileCopy> entry : this.fileCopyCache.entrySet()) {
210 Path tempFilePath = Paths.get(RAImageIngestModule.getRATempPath(currentCase, moduleName), entry.getKey() );
212 entry.getValue().getFileCopy().getChannel().close();
213 entry.getValue().getFileCopy().close();
215 File tmpFile = tempFilePath.toFile();
216 if (!tmpFile.delete()) {
217 tmpFile.deleteOnExit();
219 }
catch (IOException ex) {
220 logger.log(Level.WARNING, String.format(
"Failed to delete cache file copy %s", tempFilePath.toString()), ex);
230 private String getAbsOutputFolderName() {
231 return absOutputFolderName;
239 private String getRelOutputFolderName() {
240 return relOutputFolderName;
253 }
catch (IngestModuleException ex) {
254 String msg =
"Failed to initialize ChromeCacheExtractor.";
255 logger.log(Level.SEVERE, msg, ex);
260 List<AbstractFile> indexFiles;
262 indexFiles = findCacheIndexFiles();
265 for (AbstractFile indexFile: indexFiles) {
267 if (context.dataSourceIngestIsCancelled()) {
271 processCacheIndexFile(indexFile);
274 }
catch (TskCoreException ex) {
275 String msg =
"Failed to find cache index files";
276 logger.log(Level.WARNING, msg, ex);
281 "ChromeCacheExtract_adding_extracted_files_msg=Adding %d extracted files for analysis."
289 private void processCacheIndexFile(AbstractFile indexAbstractFile) {
291 String cachePath = indexAbstractFile.getParentPath();
292 Optional<CacheFileCopy> indexFileCopy;
294 resetForNewFolder(cachePath);
297 indexFileCopy = this.getCacheFileCopy(indexAbstractFile.getName(), cachePath);
298 if (!indexFileCopy.isPresent()) {
299 String msg = String.format(
"Failed to find copy cache index file %s", indexAbstractFile.getUniquePath());
300 logger.log(Level.WARNING, msg);
306 for (
int i = 0; i < 4; i ++) {
307 Optional<CacheFileCopy> dataFile = findAndCopyCacheFile(String.format(
"data_%1d",i), cachePath );
308 if (!dataFile.isPresent()) {
314 findExternalFiles(cachePath);
316 }
catch (TskCoreException | IngestModuleException ex) {
317 String msg =
"Failed to find cache files in path " + cachePath;
318 logger.log(Level.WARNING, msg, ex);
324 logger.log(Level.INFO,
"{0}- Now reading Cache index file from path {1}",
new Object[]{moduleName, cachePath });
326 List<AbstractFile> derivedFiles =
new ArrayList<>();
327 Collection<BlackboardArtifact> sourceArtifacts =
new ArrayList<>();
328 Collection<BlackboardArtifact> webCacheArtifacts =
new ArrayList<>();
330 ByteBuffer indexFileROBuffer = indexFileCopy.get().getByteBuffer();
331 IndexFileHeader indexHdr =
new IndexFileHeader(indexFileROBuffer);
334 indexFileROBuffer.position(INDEXFILE_HDR_SIZE);
337 for (
int i = 0; i < indexHdr.getTableLen(); i++) {
339 if (context.dataSourceIngestIsCancelled()) {
344 CacheAddress addr =
new CacheAddress(indexFileROBuffer.getInt() & UINT32_MASK, cachePath);
345 if (addr.isInitialized()) {
346 progressBar.progress( NbBundle.getMessage(
this.getClass(),
347 "ChromeCacheExtractor.progressMsg",
348 moduleName, i, indexHdr.getTableLen(), cachePath) );
350 List<DerivedFile> addedFiles = this.processCacheEntry(addr, sourceArtifacts, webCacheArtifacts);
351 derivedFiles.addAll(addedFiles);
353 catch (TskCoreException | IngestModuleException ex) {
354 logger.log(Level.WARNING, String.format(
"Failed to get cache entry at address %s", addr), ex);
359 if (context.dataSourceIngestIsCancelled()) {
364 progressBar.progress(String.format(Bundle.ChromeCacheExtract_adding_extracted_files_msg(), derivedFiles.size()));
366 derivedFiles.forEach((derived) -> {
367 services.fireModuleContentEvent(
new ModuleContentEvent(derived));
370 context.addFilesToJob(derivedFiles);
372 Blackboard blackboard = currentCase.getSleuthkitCase().getBlackboard();
375 blackboard.postArtifacts(sourceArtifacts, moduleName);
376 blackboard.postArtifacts(webCacheArtifacts, moduleName);
377 }
catch (Blackboard.BlackboardException ex) {
378 logger.log(Level.WARNING, String.format(
"Failed to post cacheIndex artifacts "), ex);
395 private List<DerivedFile> processCacheEntry(CacheAddress cacheEntryAddress, Collection<BlackboardArtifact> associatedObjectArtifacts, Collection<BlackboardArtifact> webCacheArtifacts )
throws TskCoreException, IngestModuleException {
397 List<DerivedFile> derivedFiles =
new ArrayList<>();
400 String dataFileName = cacheEntryAddress.getFilename();
401 String cachePath = cacheEntryAddress.getCachePath();
404 Optional<CacheFileCopy> cacheEntryFile = this.getCacheFileCopy(dataFileName, cachePath);
405 if (!cacheEntryFile.isPresent()) {
406 String msg = String.format(
"Failed to get cache entry at address %s", cacheEntryAddress);
407 throw new IngestModuleException(msg);
412 CacheEntry cacheEntry =
new CacheEntry(cacheEntryAddress, cacheEntryFile.get() );
414 List<CacheData> dataEntries = cacheEntry.getData();
417 if (dataEntries.size() < 2) {
420 CacheData dataSegment = dataEntries.get(1);
424 String cachedFileName = dataSegment.getAddress().getFilename();
425 Optional<AbstractFile> cachedFileAbstractFile = this.findCacheFile(cachedFileName, cachePath);
426 if (!cachedFileAbstractFile.isPresent()) {
427 logger.log(Level.WARNING,
"Error finding file: " + cachePath +
"/" + cachedFileName);
431 boolean isBrotliCompressed =
false;
432 if (dataSegment.getType() != CacheDataTypeEnum.HTTP_HEADER && cacheEntry.isBrotliCompressed() ) {
433 isBrotliCompressed =
true;
437 BlackboardAttribute urlAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL,
439 ((cacheEntry.getKey() != null) ? cacheEntry.getKey() :
""));
440 BlackboardAttribute createTimeAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
442 cacheEntry.getCreationTime());
443 BlackboardAttribute httpHeaderAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS,
445 cacheEntry.getHTTPHeaders());
447 Collection<BlackboardAttribute> webCacheAttributes =
new ArrayList<>();
448 webCacheAttributes.add(urlAttr);
449 webCacheAttributes.add(createTimeAttr);
450 webCacheAttributes.add(httpHeaderAttr);
454 if (dataSegment.isInExternalFile() ) {
457 BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE);
458 if (webCacheArtifact != null) {
459 webCacheArtifact.addAttributes(webCacheAttributes);
462 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH,
464 cachedFileAbstractFile.get().getUniquePath()));
466 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
467 moduleName, cachedFileAbstractFile.get().getId()));
469 webCacheArtifacts.add(webCacheArtifact);
471 BlackboardArtifact associatedObjectArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
472 if (associatedObjectArtifact != null) {
473 associatedObjectArtifact.addAttribute(
474 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
475 moduleName, webCacheArtifact.getArtifactID()));
476 associatedObjectArtifacts.add(associatedObjectArtifact);
480 if (isBrotliCompressed) {
481 cachedFileAbstractFile.get().setMIMEType(BROTLI_MIMETYPE);
482 cachedFileAbstractFile.get().save();
484 }
catch (TskException ex) {
485 logger.log(Level.SEVERE,
"Error while trying to add an artifact", ex);
492 String filename = dataSegment.save();
493 String relPathname = getRelOutputFolderName() + dataSegment.getAddress().getCachePath() + filename;
495 DerivedFile derivedFile = fileManager.addDerivedFile(filename, relPathname,
496 dataSegment.getDataLength(),
497 cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(),
499 cachedFileAbstractFile.get(),
504 TskData.EncodingType.NONE);
507 BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE);
508 if (webCacheArtifact != null) {
509 webCacheArtifact.addAttributes(webCacheAttributes);
512 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH,
514 derivedFile.getUniquePath()));
516 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
517 moduleName, derivedFile.getId()));
519 webCacheArtifacts.add(webCacheArtifact);
521 BlackboardArtifact associatedObjectArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
522 if (associatedObjectArtifact != null) {
523 associatedObjectArtifact.addAttribute(
524 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
525 moduleName, webCacheArtifact.getArtifactID()));
526 associatedObjectArtifacts.add(associatedObjectArtifact);
530 if (isBrotliCompressed) {
531 derivedFile.setMIMEType(BROTLI_MIMETYPE);
535 derivedFiles.add(derivedFile);
536 }
catch (TskException ex) {
537 logger.log(Level.SEVERE,
"Error while trying to add an artifact", ex);
552 private void findExternalFiles(String cachePath)
throws TskCoreException {
554 List<AbstractFile> effFiles = fileManager.findFiles(dataSource,
"f_%", cachePath);
555 for (AbstractFile abstractFile : effFiles ) {
556 this.externalFilesTable.put(cachePath + abstractFile.getName(), abstractFile);
567 private Optional<AbstractFile> findCacheFile(String cacheFileName, String cachePath)
throws TskCoreException {
570 String fileTableKey = cachePath + cacheFileName;
571 if (cacheFileName.startsWith(
"f_") && externalFilesTable.containsKey(fileTableKey)) {
572 return Optional.of(externalFilesTable.get(fileTableKey));
574 if (fileCopyCache.containsKey(fileTableKey)) {
575 return Optional.of(fileCopyCache.get(fileTableKey).getAbstractFile());
579 List<AbstractFile> cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cachePath);
580 if (!cacheFiles.isEmpty()) {
581 for (AbstractFile abstractFile: cacheFiles ) {
582 if (abstractFile.getUniquePath().trim().endsWith(DEFAULT_CACHE_PATH_STR)) {
583 return Optional.of(abstractFile);
586 return Optional.of(cacheFiles.get(0));
589 return Optional.empty();
598 private List<AbstractFile> findCacheIndexFiles() throws TskCoreException {
599 return fileManager.findFiles(dataSource,
"index", DEFAULT_CACHE_PATH_STR);
612 private Optional<CacheFileCopy> getCacheFileCopy(String cacheFileName, String cachePath)
throws TskCoreException, IngestModuleException {
615 String fileTableKey = cachePath + cacheFileName;
616 if (fileCopyCache.containsKey(fileTableKey)) {
617 return Optional.of(fileCopyCache.get(fileTableKey));
620 return findAndCopyCacheFile(cacheFileName, cachePath);
630 private Optional<CacheFileCopy> findAndCopyCacheFile(String cacheFileName, String cachePath)
throws TskCoreException, IngestModuleException {
632 Optional<AbstractFile> cacheFileOptional = findCacheFile(cacheFileName, cachePath);
633 if (!cacheFileOptional.isPresent()) {
634 return Optional.empty();
641 AbstractFile cacheFile = cacheFileOptional.get();
642 RandomAccessFile randomAccessFile = null;
643 String tempFilePathname = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath + cacheFile.getName();
645 File newFile =
new File(tempFilePathname);
646 ContentUtils.writeToFile(cacheFile, newFile, context::dataSourceIngestIsCancelled);
648 randomAccessFile =
new RandomAccessFile(tempFilePathname,
"r");
649 FileChannel roChannel = randomAccessFile.getChannel();
650 ByteBuffer cacheFileROBuf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0,
651 (
int) roChannel.size());
653 cacheFileROBuf.order(ByteOrder.nativeOrder());
654 CacheFileCopy cacheFileCopy =
new CacheFileCopy(cacheFile, randomAccessFile, cacheFileROBuf );
656 if (!cacheFileName.startsWith(
"f_")) {
657 fileCopyCache.put(cachePath + cacheFileName, cacheFileCopy);
660 return Optional.of(cacheFileCopy);
662 catch (IOException ex) {
665 if (randomAccessFile != null) {
666 randomAccessFile.close();
669 catch (IOException ex2) {
670 logger.log(Level.SEVERE,
"Error while trying to close temp file after exception.", ex2);
672 String msg = String.format(
"Error reading/copying Chrome cache file '%s' (id=%d).",
673 cacheFile.getName(), cacheFile.getId());
674 throw new IngestModuleException(msg, ex);
681 final class IndexFileHeader {
683 private final long magic;
684 private final int version;
685 private final int numEntries;
686 private final int numBytes;
687 private final int lastFile;
688 private final int tableLen;
690 IndexFileHeader(ByteBuffer indexFileROBuf) {
692 magic = indexFileROBuf.getInt() & UINT32_MASK;
694 indexFileROBuf.position(indexFileROBuf.position()+2);
696 version = indexFileROBuf.getShort();
697 numEntries = indexFileROBuf.getInt();
698 numBytes = indexFileROBuf.getInt();
699 lastFile = indexFileROBuf.getInt();
701 indexFileROBuf.position(indexFileROBuf.position()+4);
702 indexFileROBuf.position(indexFileROBuf.position()+4);
704 tableLen = indexFileROBuf.getInt();
707 public long getMagic() {
711 public int getVersion() {
715 public int getNumEntries() {
719 public int getNumBytes() {
723 public int getLastFile() {
727 public int getTableLen() {
732 public String toString() {
733 StringBuilder sb =
new StringBuilder();
735 sb.append(String.format(
"Index Header:"))
736 .append(String.format(
"\tMagic = %x" , getMagic()) )
737 .append(String.format(
"\tVersion = %x" , getVersion()) )
738 .append(String.format(
"\tNumEntries = %x" , getNumEntries()) )
739 .append(String.format(
"\tNumBytes = %x" , getNumBytes()) )
740 .append(String.format(
"\tLastFile = %x" , getLastFile()) )
741 .append(String.format(
"\tTableLen = %x" , getTableLen()) );
743 return sb.toString();
750 enum CacheFileTypeEnum {
782 final class CacheAddress {
784 private static final long ADDR_INITIALIZED_MASK = 0x80000000l;
785 private static final long FILE_TYPE_MASK = 0x70000000;
786 private static final long FILE_TYPE_OFFSET = 28;
787 private static final long NUM_BLOCKS_MASK = 0x03000000;
788 private static final long NUM_BLOCKS_OFFSET = 24;
789 private static final long FILE_SELECTOR_MASK = 0x00ff0000;
790 private static final long FILE_SELECTOR_OFFSET = 16;
791 private static final long START_BLOCK_MASK = 0x0000FFFF;
792 private static final long EXTERNAL_FILE_NAME_MASK = 0x0FFFFFFF;
794 private final long uint32CacheAddr;
795 private final CacheFileTypeEnum fileType;
796 private final int numBlocks;
797 private final int startBlock;
798 private final String fileName;
799 private final int fileNumber;
801 private final String cachePath;
804 CacheAddress(
long uint32, String cachePath) {
806 uint32CacheAddr = uint32;
807 this.cachePath = cachePath;
809 int fileTypeEnc = (int)(uint32CacheAddr & FILE_TYPE_MASK) >> FILE_TYPE_OFFSET;
810 fileType = CacheFileTypeEnum.values()[fileTypeEnc];
812 if (isInitialized()) {
813 if (isInExternalFile()) {
814 fileNumber = (int)(uint32CacheAddr & EXTERNAL_FILE_NAME_MASK);
815 fileName = String.format(
"f_%06x", getFileNumber() );
819 fileNumber = (int)((uint32CacheAddr & FILE_SELECTOR_MASK) >> FILE_SELECTOR_OFFSET);
820 fileName = String.format(
"data_%d", getFileNumber() );
821 numBlocks = (int)(uint32CacheAddr & NUM_BLOCKS_MASK >> NUM_BLOCKS_OFFSET);
822 startBlock = (int)(uint32CacheAddr & START_BLOCK_MASK);
833 boolean isInitialized() {
834 return ((uint32CacheAddr & ADDR_INITIALIZED_MASK) != 0);
837 CacheFileTypeEnum getFileType() {
845 String getFilename() {
849 String getCachePath() {
853 boolean isInExternalFile() {
854 return (fileType == CacheFileTypeEnum.EXTERNAL);
857 int getFileNumber() {
861 int getStartBlock() {
890 public long getUint32CacheAddr() {
891 return uint32CacheAddr;
895 public String toString() {
896 StringBuilder sb =
new StringBuilder();
897 sb.append(String.format(
"CacheAddr %08x : %s : filename %s",
899 isInitialized() ?
"Initialized" :
"UnInitialized",
902 if ((fileType == CacheFileTypeEnum.BLOCK_256) ||
903 (fileType == CacheFileTypeEnum.BLOCK_1K) ||
904 (fileType == CacheFileTypeEnum.BLOCK_4K) ) {
905 sb.append(String.format(
" (%d blocks starting at %08X)",
911 return sb.toString();
919 enum CacheDataTypeEnum {
933 final class CacheData {
936 private final CacheAddress address;
937 private CacheDataTypeEnum type;
939 private boolean isHTTPHeaderHint;
941 private CacheFileCopy cacheFileCopy = null;
942 private byte[] data = null;
944 private String httpResponse;
945 private final Map<String, String> httpHeaders =
new HashMap<>();
947 CacheData(CacheAddress cacheAdress,
int len) {
948 this(cacheAdress, len,
false);
951 CacheData(CacheAddress cacheAdress,
int len,
boolean isHTTPHeader ) {
952 this.type = CacheDataTypeEnum.UNKNOWN;
954 this.address = cacheAdress;
955 this.isHTTPHeaderHint = isHTTPHeader;
958 boolean isInExternalFile() {
959 return address.isInExternalFile();
962 boolean hasHTTPHeaders() {
963 return this.type == CacheDataTypeEnum.HTTP_HEADER;
966 String getHTTPHeader(String key) {
967 return this.httpHeaders.get(key);
975 String getHTTPHeaders() {
976 if (!hasHTTPHeaders()) {
980 StringBuilder sb =
new StringBuilder();
981 httpHeaders.entrySet().forEach((entry) -> {
982 if (sb.length() > 0) {
985 sb.append(String.format(
"%s : %s",
986 entry.getKey(), entry.getValue()));
989 return sb.toString();
992 String getHTTPRespone() {
1001 void extract() throws TskCoreException, IngestModuleException {
1009 if (!address.isInExternalFile() ) {
1011 cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).
get();
1013 this.data =
new byte [length];
1014 ByteBuffer buf = cacheFileCopy.getByteBuffer();
1015 int dataOffset = DATAFILE_HDR_SIZE + address.getStartBlock() * address.getBlockSize();
1016 buf.position(dataOffset);
1017 buf.get(data, 0, length);
1020 if ((isHTTPHeaderHint)) {
1021 String strData =
new String(data);
1022 if (strData.contains(
"HTTP")) {
1033 type = CacheDataTypeEnum.HTTP_HEADER;
1035 int startOff = strData.indexOf(
"HTTP");
1036 Charset charset = Charset.forName(
"UTF-8");
1037 boolean done =
false;
1044 while (i < data.length && data[i] != 0) {
1049 if (i == data.length || data[i+1] == 0) {
1053 int len = (i - start);
1054 String headerLine =
new String(data, start, len, charset);
1058 httpResponse = headerLine;
1060 int nPos = headerLine.indexOf(
':');
1062 String key = headerLine.substring(0, nPos);
1063 String val= headerLine.substring(nPos+1);
1064 httpHeaders.put(key.toLowerCase(), val);
1076 String getDataString() throws TskCoreException, IngestModuleException {
1080 return new String(data);
1083 byte[] getDataBytes() throws TskCoreException, IngestModuleException {
1087 return data.clone();
1090 int getDataLength() {
1094 CacheDataTypeEnum getType() {
1098 CacheAddress getAddress() {
1111 String save() throws TskCoreException, IngestModuleException {
1114 if (address.isInExternalFile()) {
1115 fileName = address.getFilename();
1117 fileName = String.format(
"%s__%08x", address.getFilename(), address.getUint32CacheAddr());
1120 String filePathName = getAbsOutputFolderName() + address.getCachePath() + fileName;
1135 void save(String filePathName)
throws TskCoreException, IngestModuleException {
1143 if (!this.isInExternalFile()) {
1145 try (FileOutputStream stream =
new FileOutputStream(filePathName)) {
1147 }
catch (IOException ex) {
1148 throw new TskCoreException(String.format(
"Failed to write output file %s", filePathName), ex);
1154 public String toString() {
1155 StringBuilder strBuilder =
new StringBuilder();
1156 strBuilder.append(String.format(
"\t\tData type = : %s, Data Len = %d ",
1157 this.type.toString(), this.length ));
1159 if (hasHTTPHeaders()) {
1160 String str = getHTTPHeader(
"content-encoding");
1162 strBuilder.append(String.format(
"\t%s=%s",
"content-encoding", str ));
1166 return strBuilder.toString();
1175 enum EntryStateEnum {
1214 final class CacheEntry {
1217 private static final int MAX_KEY_LEN = 256-24*4;
1219 private final CacheAddress selfAddress;
1220 private final CacheFileCopy cacheFileCopy;
1222 private final long hash;
1223 private final CacheAddress nextAddress;
1224 private final CacheAddress rankingsNodeAddress;
1226 private final int reuseCount;
1227 private final int refetchCount;
1228 private final EntryStateEnum state;
1230 private final long creationTime;
1231 private final int keyLen;
1233 private final CacheAddress longKeyAddresses;
1235 private final int dataSizes[];
1236 private final CacheAddress dataAddresses[];
1237 private List<CacheData> dataList;
1239 private final long flags;
1243 CacheEntry(CacheAddress cacheAdress, CacheFileCopy cacheFileCopy ) {
1244 this.selfAddress = cacheAdress;
1245 this.cacheFileCopy = cacheFileCopy;
1247 ByteBuffer fileROBuf = cacheFileCopy.getByteBuffer();
1249 int entryOffset = DATAFILE_HDR_SIZE + cacheAdress.getStartBlock() * cacheAdress.getBlockSize();
1252 fileROBuf.position(entryOffset);
1254 hash = fileROBuf.getInt() & UINT32_MASK;
1256 long uint32 = fileROBuf.getInt() & UINT32_MASK;
1257 nextAddress = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1259 uint32 = fileROBuf.getInt() & UINT32_MASK;
1260 rankingsNodeAddress = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1262 reuseCount = fileROBuf.getInt();
1263 refetchCount = fileROBuf.getInt();
1265 state = EntryStateEnum.values()[fileROBuf.getInt()];
1266 creationTime = (fileROBuf.getLong() / 1000000) - Long.valueOf(
"11644473600");
1268 keyLen = fileROBuf.getInt();
1270 uint32 = fileROBuf.getInt() & UINT32_MASK;
1271 longKeyAddresses = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1274 dataSizes=
new int[4];
1275 for (
int i = 0; i < 4; i++) {
1276 dataSizes[i] = fileROBuf.getInt();
1278 dataAddresses =
new CacheAddress[4];
1279 for (
int i = 0; i < 4; i++) {
1280 dataAddresses[i] =
new CacheAddress(fileROBuf.getInt() & UINT32_MASK, selfAddress.getCachePath());
1283 flags = fileROBuf.getInt() & UINT32_MASK;
1285 for (
int i = 0; i < 4; i++) {
1293 if (longKeyAddresses != null) {
1296 CacheData data =
new CacheData(longKeyAddresses, this.keyLen,
true);
1297 key = data.getDataString();
1298 }
catch (TskCoreException | IngestModuleException ex) {
1299 logger.log(Level.WARNING, String.format(
"Failed to get external key from address %s", longKeyAddresses));
1303 StringBuilder strBuilder =
new StringBuilder(MAX_KEY_LEN);
1305 while (fileROBuf.remaining() > 0 && keyLen < MAX_KEY_LEN) {
1306 char keyChar = (char)fileROBuf.get();
1307 if (keyChar ==
'\0') {
1310 strBuilder.append(keyChar);
1314 key = strBuilder.toString();
1318 public CacheAddress getAddress() {
1322 public long getHash() {
1326 public CacheAddress getNextAddress() {
1330 public int getReuseCount() {
1334 public int getRefetchCount() {
1335 return refetchCount;
1338 public EntryStateEnum getState() {
1342 public long getCreationTime() {
1343 return creationTime;
1346 public long getFlags() {
1350 public String getKey() {
1362 public List<CacheData> getData() throws TskCoreException, IngestModuleException {
1364 if (dataList == null) {
1365 dataList =
new ArrayList<>();
1366 for (
int i = 0; i < 4; i++) {
1367 if (dataSizes[i] > 0) {
1368 CacheData cacheData =
new CacheData(dataAddresses[i], dataSizes[i],
true );
1370 cacheData.extract();
1371 dataList.add(cacheData);
1385 boolean hasHTTPHeaders() {
1386 if ((dataList == null) || dataList.isEmpty()) {
1389 return dataList.get(0).hasHTTPHeaders();
1398 String getHTTPHeader(String key) {
1399 if ((dataList == null) || dataList.isEmpty()) {
1403 return dataList.get(0).getHTTPHeader(key);
1411 String getHTTPHeaders() {
1412 if ((dataList == null) || dataList.isEmpty()) {
1416 return dataList.get(0).getHTTPHeaders();
1427 boolean isBrotliCompressed() {
1429 if (hasHTTPHeaders() ) {
1430 String encodingHeader = getHTTPHeader(
"content-encoding");
1431 if (encodingHeader!= null) {
1432 return encodingHeader.trim().equalsIgnoreCase(
"br");
1440 public String toString() {
1441 StringBuilder sb =
new StringBuilder();
1442 sb.append(String.format(
"Entry = Hash: %08x, State: %s, ReuseCount: %d, RefetchCount: %d",
1443 this.hash,
this.state.toString(), this.reuseCount, this.refetchCount ))
1444 .append(String.format(
"\n\tKey: %s, Keylen: %d",
1445 this.key,
this.keyLen,
this.reuseCount,
this.refetchCount ))
1446 .append(String.format(
"\n\tCreationTime: %s",
1447 TimeUtilities.epochToTime(
this.creationTime) ))
1448 .append(String.format(
"\n\tNext Address: %s",
1449 (nextAddress != null) ? nextAddress.toString() :
"None"));
1451 for (
int i = 0; i < 4; i++) {
1452 if (dataSizes[i] > 0) {
1453 sb.append(String.format(
"\n\tData %d: cache address = %s, Data = %s",
1454 i, dataAddresses[i].toString(),
1456 ? dataList.get(i).toString()
1457 :
"Data not retrived yet."));
1461 return sb.toString();