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;
55 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
76 final class ChromeCacheExtractor {
78 private final static String DEFAULT_CACHE_PATH_STR =
"default/cache";
79 private final static String BROTLI_MIMETYPE =
"application/x-brotli";
81 private final static long UINT32_MASK = 0xFFFFFFFFl;
83 private final static int INDEXFILE_HDR_SIZE = 92*4;
84 private final static int DATAFILE_HDR_SIZE = 8192;
86 private final static Logger logger = Logger.getLogger(ChromeCacheExtractor.class.getName());
88 private static final String VERSION_NUMBER =
"1.0.0";
89 private final String moduleName;
91 private String absOutputFolderName;
92 private String relOutputFolderName;
94 private final Content dataSource;
95 private final IngestJobContext context;
96 private final DataSourceIngestModuleProgress progressBar;
97 private final IngestServices services = IngestServices.getInstance();
98 private Case currentCase;
99 private FileManager fileManager;
102 private final Map<String, CacheFileCopy> fileCopyCache =
new HashMap<>();
105 private final Map<String, AbstractFile> externalFilesTable =
new HashMap<>();
111 final class CacheFileCopy {
113 private final AbstractFile abstractFile;
114 private final RandomAccessFile fileCopy;
115 private final ByteBuffer byteBuffer;
117 CacheFileCopy (AbstractFile abstractFile, RandomAccessFile fileCopy, ByteBuffer buffer ) {
118 this.abstractFile = abstractFile;
119 this.fileCopy = fileCopy;
120 this.byteBuffer = buffer;
123 public RandomAccessFile getFileCopy() {
126 public ByteBuffer getByteBuffer() {
129 AbstractFile getAbstractFile() {
135 "ChromeCacheExtractor.moduleName=ChromeCacheExtractor",
136 "# {0} - module name",
137 "# {1} - row number",
138 "# {2} - table length",
139 "# {3} - cache path",
140 "ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}"
142 ChromeCacheExtractor(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar ) {
143 moduleName = Bundle.ChromeCacheExtractor_moduleName();
144 this.dataSource = dataSource;
145 this.context = context;
146 this.progressBar = progressBar;
155 private void moduleInit() throws IngestModuleException {
158 currentCase = Case.getCurrentCaseThrows();
159 fileManager = currentCase.getServices().getFileManager();
162 absOutputFolderName = RAImageIngestModule.getRAOutputPath(currentCase, moduleName);
163 relOutputFolderName = Paths.get( RAImageIngestModule.getRelModuleOutputPath(), moduleName).normalize().toString();
165 File dir =
new File(absOutputFolderName);
166 if (dir.exists() ==
false) {
169 }
catch (NoCurrentCaseException ex) {
170 String msg =
"Failed to get current case.";
171 throw new IngestModuleException(msg, ex);
182 private void resetForNewFolder(String cachePath)
throws IngestModuleException {
184 fileCopyCache.clear();
185 externalFilesTable.clear();
187 String cacheAbsOutputFolderName = this.getAbsOutputFolderName() + cachePath;
188 File outDir =
new File(cacheAbsOutputFolderName);
189 if (outDir.exists() ==
false) {
193 String cacheTempPath = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath;
194 File tempDir =
new File(cacheTempPath);
195 if (tempDir.exists() ==
false) {
206 private void cleanup () {
208 for (Entry<String, CacheFileCopy> entry : this.fileCopyCache.entrySet()) {
209 Path tempFilePath = Paths.get(RAImageIngestModule.getRATempPath(currentCase, moduleName), entry.getKey() );
211 entry.getValue().getFileCopy().getChannel().close();
212 entry.getValue().getFileCopy().close();
214 File tmpFile = tempFilePath.toFile();
215 if (!tmpFile.delete()) {
216 tmpFile.deleteOnExit();
218 }
catch (IOException ex) {
219 logger.log(Level.WARNING, String.format(
"Failed to delete cache file copy %s", tempFilePath.toString()), ex);
229 private String getAbsOutputFolderName() {
230 return absOutputFolderName;
238 private String getRelOutputFolderName() {
239 return relOutputFolderName;
252 }
catch (IngestModuleException ex) {
253 String msg =
"Failed to initialize ChromeCacheExtractor.";
254 logger.log(Level.SEVERE, msg, ex);
259 List<AbstractFile> indexFiles;
261 indexFiles = findCacheIndexFiles();
264 for (AbstractFile indexFile: indexFiles) {
266 if (context.dataSourceIngestIsCancelled()) {
270 processCacheIndexFile(indexFile);
273 }
catch (TskCoreException ex) {
274 String msg =
"Failed to find cache index files";
275 logger.log(Level.SEVERE, msg, ex);
284 private void processCacheIndexFile(AbstractFile indexAbstractFile) {
286 String cachePath = indexAbstractFile.getParentPath();
287 Optional<CacheFileCopy> indexFileCopy;
289 resetForNewFolder(cachePath);
292 indexFileCopy = this.getCacheFileCopy(indexAbstractFile.getName(), cachePath);
293 if (!indexFileCopy.isPresent()) {
294 String msg = String.format(
"Failed to find copy cache index file %s", indexAbstractFile.getUniquePath());
295 logger.log(Level.SEVERE, msg);
301 for (
int i = 0; i < 4; i ++) {
302 Optional<CacheFileCopy> dataFile = findAndCopyCacheFile(String.format(
"data_%1d",i), cachePath );
303 if (!dataFile.isPresent()) {
309 findExternalFiles(cachePath);
311 }
catch (TskCoreException | IngestModuleException ex) {
312 String msg =
"Failed to find cache files in path " + cachePath;
313 logger.log(Level.SEVERE, msg, ex);
319 logger.log(Level.INFO,
"{0}- Now reading Cache index file from path {1}",
new Object[]{moduleName, cachePath });
321 List<AbstractFile> derivedFiles =
new ArrayList<>();
322 Collection<BlackboardArtifact> sourceArtifacts =
new ArrayList<>();
323 Collection<BlackboardArtifact> webCacheArtifacts =
new ArrayList<>();
325 ByteBuffer indexFileROBuffer = indexFileCopy.get().getByteBuffer();
326 IndexFileHeader indexHdr =
new IndexFileHeader(indexFileROBuffer);
329 indexFileROBuffer.position(INDEXFILE_HDR_SIZE);
332 for (
int i = 0; i < indexHdr.getTableLen(); i++) {
334 if (context.dataSourceIngestIsCancelled()) {
339 CacheAddress addr =
new CacheAddress(indexFileROBuffer.getInt() & UINT32_MASK, cachePath);
340 if (addr.isInitialized()) {
341 progressBar.progress( NbBundle.getMessage(
this.getClass(),
342 "ChromeCacheExtractor.progressMsg",
343 moduleName, i, indexHdr.getTableLen(), cachePath) );
345 List<DerivedFile> addedFiles = this.processCacheEntry(addr, sourceArtifacts, webCacheArtifacts);
346 derivedFiles.addAll(addedFiles);
348 catch (TskCoreException | IngestModuleException ex) {
349 logger.log(Level.SEVERE, String.format(
"Failed to get cache entry at address %s", addr), ex);
354 if (context.dataSourceIngestIsCancelled()) {
359 derivedFiles.forEach((derived) -> {
360 services.fireModuleContentEvent(
new ModuleContentEvent(derived));
363 context.addFilesToJob(derivedFiles);
365 services.fireModuleDataEvent(
new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE, !sourceArtifacts.isEmpty() ? sourceArtifacts : null));
366 services.fireModuleDataEvent(
new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE, !webCacheArtifacts.isEmpty() ? webCacheArtifacts : null));
382 private List<DerivedFile> processCacheEntry(CacheAddress cacheEntryAddress, Collection<BlackboardArtifact> sourceArtifacts, Collection<BlackboardArtifact> webCacheArtifacts )
throws TskCoreException, IngestModuleException {
384 List<DerivedFile> derivedFiles =
new ArrayList<>();
387 String dataFileName = cacheEntryAddress.getFilename();
388 String cachePath = cacheEntryAddress.getCachePath();
391 Optional<CacheFileCopy> cacheEntryFile = this.getCacheFileCopy(dataFileName, cachePath);
392 if (!cacheEntryFile.isPresent()) {
393 String msg = String.format(
"Failed to get cache entry at address %s", cacheEntryAddress);
394 throw new IngestModuleException(msg);
399 CacheEntry cacheEntry =
new CacheEntry(cacheEntryAddress, cacheEntryFile.get() );
401 List<CacheData> dataEntries = cacheEntry.getData();
404 if (dataEntries.size() < 2) {
407 CacheData dataSegment = dataEntries.get(1);
411 String cachedFileName = dataSegment.getAddress().getFilename();
412 Optional<AbstractFile> cachedFileAbstractFile = this.findCacheFile(cachedFileName, cachePath);
413 if (!cachedFileAbstractFile.isPresent()) {
414 logger.log(Level.SEVERE,
"Error finding file: " + cachePath +
"/" + cachedFileName);
418 boolean isBrotliCompressed =
false;
419 if (dataSegment.getType() != CacheDataTypeEnum.HTTP_HEADER && cacheEntry.isBrotliCompressed() ) {
420 isBrotliCompressed =
true;
424 BlackboardAttribute urlAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL,
426 ((cacheEntry.getKey() != null) ? cacheEntry.getKey() :
""));
427 BlackboardAttribute createTimeAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
429 cacheEntry.getCreationTime());
430 BlackboardAttribute httpHeaderAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS,
432 cacheEntry.getHTTPHeaders());
434 Collection<BlackboardAttribute> sourceArtifactAttributes =
new ArrayList<>();
435 sourceArtifactAttributes.add(urlAttr);
436 sourceArtifactAttributes.add(createTimeAttr);
438 Collection<BlackboardAttribute> webCacheAttributes =
new ArrayList<>();
439 webCacheAttributes.add(urlAttr);
440 webCacheAttributes.add(createTimeAttr);
441 webCacheAttributes.add(httpHeaderAttr);
445 if (dataSegment.isInExternalFile() ) {
447 BlackboardArtifact sourceArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
448 if (sourceArtifact != null) {
449 sourceArtifact.addAttributes(sourceArtifactAttributes);
450 sourceArtifacts.add(sourceArtifact);
453 BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE);
454 if (webCacheArtifact != null) {
455 webCacheArtifact.addAttributes(webCacheAttributes);
458 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH,
460 cachedFileAbstractFile.get().getUniquePath()));
462 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
463 moduleName, cachedFileAbstractFile.get().getId()));
465 webCacheArtifacts.add(webCacheArtifact);
468 if (isBrotliCompressed) {
469 cachedFileAbstractFile.get().setMIMEType(BROTLI_MIMETYPE);
470 cachedFileAbstractFile.get().save();
472 }
catch (TskException ex) {
473 logger.log(Level.SEVERE,
"Error while trying to add an artifact", ex);
480 String filename = dataSegment.save();
481 String relPathname = getRelOutputFolderName() + dataSegment.getAddress().getCachePath() + filename;
483 DerivedFile derivedFile = fileManager.addDerivedFile(filename, relPathname,
484 dataSegment.getDataLength(),
485 cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(),
487 cachedFileAbstractFile.get(),
492 TskData.EncodingType.NONE);
494 BlackboardArtifact sourceArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
495 if (sourceArtifact != null) {
496 sourceArtifact.addAttributes(sourceArtifactAttributes);
497 sourceArtifacts.add(sourceArtifact);
500 BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE);
501 if (webCacheArtifact != null) {
502 webCacheArtifact.addAttributes(webCacheAttributes);
505 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH,
507 derivedFile.getUniquePath()));
509 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
510 moduleName, derivedFile.getId()));
512 webCacheArtifacts.add(webCacheArtifact);
515 if (isBrotliCompressed) {
516 derivedFile.setMIMEType(BROTLI_MIMETYPE);
520 derivedFiles.add(derivedFile);
521 }
catch (TskException ex) {
522 logger.log(Level.SEVERE,
"Error while trying to add an artifact", ex);
537 private void findExternalFiles(String cachePath)
throws TskCoreException {
539 List<AbstractFile> effFiles = fileManager.findFiles(dataSource,
"f_%", cachePath);
540 for (AbstractFile abstractFile : effFiles ) {
541 this.externalFilesTable.put(cachePath + abstractFile.getName(), abstractFile);
552 private Optional<AbstractFile> findCacheFile(String cacheFileName, String cachePath)
throws TskCoreException {
555 String fileTableKey = cachePath + cacheFileName;
556 if (cacheFileName.startsWith(
"f_") && externalFilesTable.containsKey(fileTableKey)) {
557 return Optional.of(externalFilesTable.get(fileTableKey));
559 if (fileCopyCache.containsKey(fileTableKey)) {
560 return Optional.of(fileCopyCache.get(fileTableKey).getAbstractFile());
564 List<AbstractFile> cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cachePath);
565 if (!cacheFiles.isEmpty()) {
566 for (AbstractFile abstractFile: cacheFiles ) {
567 if (abstractFile.getUniquePath().trim().endsWith(DEFAULT_CACHE_PATH_STR)) {
568 return Optional.of(abstractFile);
571 return Optional.of(cacheFiles.get(0));
574 return Optional.empty();
583 private List<AbstractFile> findCacheIndexFiles() throws TskCoreException {
584 return fileManager.findFiles(dataSource,
"index", DEFAULT_CACHE_PATH_STR);
597 private Optional<CacheFileCopy> getCacheFileCopy(String cacheFileName, String cachePath)
throws TskCoreException, IngestModuleException {
600 String fileTableKey = cachePath + cacheFileName;
601 if (fileCopyCache.containsKey(fileTableKey)) {
602 return Optional.of(fileCopyCache.get(fileTableKey));
605 return findAndCopyCacheFile(cacheFileName, cachePath);
615 private Optional<CacheFileCopy> findAndCopyCacheFile(String cacheFileName, String cachePath)
throws TskCoreException, IngestModuleException {
617 Optional<AbstractFile> cacheFileOptional = findCacheFile(cacheFileName, cachePath);
618 if (!cacheFileOptional.isPresent()) {
619 return Optional.empty();
626 AbstractFile cacheFile = cacheFileOptional.get();
627 RandomAccessFile randomAccessFile = null;
628 String tempFilePathname = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath + cacheFile.getName();
630 File newFile =
new File(tempFilePathname);
631 ContentUtils.writeToFile(cacheFile, newFile, context::dataSourceIngestIsCancelled);
633 randomAccessFile =
new RandomAccessFile(tempFilePathname,
"r");
634 FileChannel roChannel = randomAccessFile.getChannel();
635 ByteBuffer cacheFileROBuf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0,
636 (
int) roChannel.size());
638 cacheFileROBuf.order(ByteOrder.nativeOrder());
639 CacheFileCopy cacheFileCopy =
new CacheFileCopy(cacheFile, randomAccessFile, cacheFileROBuf );
641 if (!cacheFileName.startsWith(
"f_")) {
642 fileCopyCache.put(cachePath + cacheFileName, cacheFileCopy);
645 return Optional.of(cacheFileCopy);
647 catch (IOException ex) {
650 if (randomAccessFile != null) {
651 randomAccessFile.close();
654 catch (IOException ex2) {
655 logger.log(Level.SEVERE,
"Error while trying to close temp file after exception.", ex2);
657 String msg = String.format(
"Error reading/copying Chrome cache file '%s' (id=%d).",
658 cacheFile.getName(), cacheFile.getId());
659 throw new IngestModuleException(msg, ex);
666 final class IndexFileHeader {
668 private final long magic;
669 private final int version;
670 private final int numEntries;
671 private final int numBytes;
672 private final int lastFile;
673 private final int tableLen;
675 IndexFileHeader(ByteBuffer indexFileROBuf) {
677 magic = indexFileROBuf.getInt() & UINT32_MASK;
679 indexFileROBuf.position(indexFileROBuf.position()+2);
681 version = indexFileROBuf.getShort();
682 numEntries = indexFileROBuf.getInt();
683 numBytes = indexFileROBuf.getInt();
684 lastFile = indexFileROBuf.getInt();
686 indexFileROBuf.position(indexFileROBuf.position()+4);
687 indexFileROBuf.position(indexFileROBuf.position()+4);
689 tableLen = indexFileROBuf.getInt();
692 public long getMagic() {
696 public int getVersion() {
700 public int getNumEntries() {
704 public int getNumBytes() {
708 public int getLastFile() {
712 public int getTableLen() {
717 public String toString() {
718 StringBuilder sb =
new StringBuilder();
720 sb.append(String.format(
"Index Header:"))
721 .append(String.format(
"\tMagic = %x" , getMagic()) )
722 .append(String.format(
"\tVersion = %x" , getVersion()) )
723 .append(String.format(
"\tNumEntries = %x" , getNumEntries()) )
724 .append(String.format(
"\tNumBytes = %x" , getNumBytes()) )
725 .append(String.format(
"\tLastFile = %x" , getLastFile()) )
726 .append(String.format(
"\tTableLen = %x" , getTableLen()) );
728 return sb.toString();
735 enum CacheFileTypeEnum {
767 final class CacheAddress {
769 private static final long ADDR_INITIALIZED_MASK = 0x80000000l;
770 private static final long FILE_TYPE_MASK = 0x70000000;
771 private static final long FILE_TYPE_OFFSET = 28;
772 private static final long NUM_BLOCKS_MASK = 0x03000000;
773 private static final long NUM_BLOCKS_OFFSET = 24;
774 private static final long FILE_SELECTOR_MASK = 0x00ff0000;
775 private static final long FILE_SELECTOR_OFFSET = 16;
776 private static final long START_BLOCK_MASK = 0x0000FFFF;
777 private static final long EXTERNAL_FILE_NAME_MASK = 0x0FFFFFFF;
779 private final long uint32CacheAddr;
780 private final CacheFileTypeEnum fileType;
781 private final int numBlocks;
782 private final int startBlock;
783 private final String fileName;
784 private final int fileNumber;
786 private final String cachePath;
789 CacheAddress(
long uint32, String cachePath) {
791 uint32CacheAddr = uint32;
792 this.cachePath = cachePath;
794 int fileTypeEnc = (int)(uint32CacheAddr & FILE_TYPE_MASK) >> FILE_TYPE_OFFSET;
795 fileType = CacheFileTypeEnum.values()[fileTypeEnc];
797 if (isInitialized()) {
798 if (isInExternalFile()) {
799 fileNumber = (int)(uint32CacheAddr & EXTERNAL_FILE_NAME_MASK);
800 fileName = String.format(
"f_%06x", getFileNumber() );
804 fileNumber = (int)((uint32CacheAddr & FILE_SELECTOR_MASK) >> FILE_SELECTOR_OFFSET);
805 fileName = String.format(
"data_%d", getFileNumber() );
806 numBlocks = (int)(uint32CacheAddr & NUM_BLOCKS_MASK >> NUM_BLOCKS_OFFSET);
807 startBlock = (int)(uint32CacheAddr & START_BLOCK_MASK);
818 boolean isInitialized() {
819 return ((uint32CacheAddr & ADDR_INITIALIZED_MASK) != 0);
822 CacheFileTypeEnum getFileType() {
830 String getFilename() {
834 String getCachePath() {
838 boolean isInExternalFile() {
839 return (fileType == CacheFileTypeEnum.EXTERNAL);
842 int getFileNumber() {
846 int getStartBlock() {
875 public long getUint32CacheAddr() {
876 return uint32CacheAddr;
880 public String toString() {
881 StringBuilder sb =
new StringBuilder();
882 sb.append(String.format(
"CacheAddr %08x : %s : filename %s",
884 isInitialized() ?
"Initialized" :
"UnInitialized",
887 if ((fileType == CacheFileTypeEnum.BLOCK_256) ||
888 (fileType == CacheFileTypeEnum.BLOCK_1K) ||
889 (fileType == CacheFileTypeEnum.BLOCK_4K) ) {
890 sb.append(String.format(
" (%d blocks starting at %08X)",
896 return sb.toString();
904 enum CacheDataTypeEnum {
918 final class CacheData {
921 private final CacheAddress address;
922 private CacheDataTypeEnum type;
924 private boolean isHTTPHeaderHint;
926 private CacheFileCopy cacheFileCopy = null;
927 private byte[] data = null;
929 private String httpResponse;
930 private final Map<String, String> httpHeaders =
new HashMap<>();
932 CacheData(CacheAddress cacheAdress,
int len) {
933 this(cacheAdress, len,
false);
936 CacheData(CacheAddress cacheAdress,
int len,
boolean isHTTPHeader ) {
937 this.type = CacheDataTypeEnum.UNKNOWN;
939 this.address = cacheAdress;
940 this.isHTTPHeaderHint = isHTTPHeader;
943 boolean isInExternalFile() {
944 return address.isInExternalFile();
947 boolean hasHTTPHeaders() {
948 return this.type == CacheDataTypeEnum.HTTP_HEADER;
951 String getHTTPHeader(String key) {
952 return this.httpHeaders.get(key);
960 String getHTTPHeaders() {
961 if (!hasHTTPHeaders()) {
965 StringBuilder sb =
new StringBuilder();
966 httpHeaders.entrySet().forEach((entry) -> {
967 if (sb.length() > 0) {
970 sb.append(String.format(
"%s : %s",
971 entry.getKey(), entry.getValue()));
974 return sb.toString();
977 String getHTTPRespone() {
986 void extract() throws TskCoreException, IngestModuleException {
994 if (!address.isInExternalFile() ) {
996 cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).
get();
998 this.data =
new byte [length];
999 ByteBuffer buf = cacheFileCopy.getByteBuffer();
1000 int dataOffset = DATAFILE_HDR_SIZE + address.getStartBlock() * address.getBlockSize();
1001 buf.position(dataOffset);
1002 buf.get(data, 0, length);
1005 if ((isHTTPHeaderHint)) {
1006 String strData =
new String(data);
1007 if (strData.contains(
"HTTP")) {
1018 type = CacheDataTypeEnum.HTTP_HEADER;
1020 int startOff = strData.indexOf(
"HTTP");
1021 Charset charset = Charset.forName(
"UTF-8");
1022 boolean done =
false;
1029 while (i < data.length && data[i] != 0) {
1034 if (i == data.length || data[i+1] == 0) {
1038 int len = (i - start);
1039 String headerLine =
new String(data, start, len, charset);
1043 httpResponse = headerLine;
1045 int nPos = headerLine.indexOf(
':');
1047 String key = headerLine.substring(0, nPos);
1048 String val= headerLine.substring(nPos+1);
1049 httpHeaders.put(key.toLowerCase(), val);
1061 String getDataString() throws TskCoreException, IngestModuleException {
1065 return new String(data);
1068 byte[] getDataBytes() throws TskCoreException, IngestModuleException {
1072 return data.clone();
1075 int getDataLength() {
1079 CacheDataTypeEnum getType() {
1083 CacheAddress getAddress() {
1096 String save() throws TskCoreException, IngestModuleException {
1099 if (address.isInExternalFile()) {
1100 fileName = address.getFilename();
1102 fileName = String.format(
"%s__%08x", address.getFilename(), address.getUint32CacheAddr());
1105 String filePathName = getAbsOutputFolderName() + address.getCachePath() + fileName;
1120 void save(String filePathName)
throws TskCoreException, IngestModuleException {
1128 if (!this.isInExternalFile()) {
1130 try (FileOutputStream stream =
new FileOutputStream(filePathName)) {
1132 }
catch (IOException ex) {
1133 throw new TskCoreException(String.format(
"Failed to write output file %s", filePathName), ex);
1139 public String toString() {
1140 StringBuilder strBuilder =
new StringBuilder();
1141 strBuilder.append(String.format(
"\t\tData type = : %s, Data Len = %d ",
1142 this.type.toString(), this.length ));
1144 if (hasHTTPHeaders()) {
1145 String str = getHTTPHeader(
"content-encoding");
1147 strBuilder.append(String.format(
"\t%s=%s",
"content-encoding", str ));
1151 return strBuilder.toString();
1160 enum EntryStateEnum {
1199 final class CacheEntry {
1202 private static final int MAX_KEY_LEN = 256-24*4;
1204 private final CacheAddress selfAddress;
1205 private final CacheFileCopy cacheFileCopy;
1207 private final long hash;
1208 private final CacheAddress nextAddress;
1209 private final CacheAddress rankingsNodeAddress;
1211 private final int reuseCount;
1212 private final int refetchCount;
1213 private final EntryStateEnum state;
1215 private final long creationTime;
1216 private final int keyLen;
1218 private final CacheAddress longKeyAddresses;
1220 private final int dataSizes[];
1221 private final CacheAddress dataAddresses[];
1222 private List<CacheData> dataList;
1224 private final long flags;
1228 CacheEntry(CacheAddress cacheAdress, CacheFileCopy cacheFileCopy ) {
1229 this.selfAddress = cacheAdress;
1230 this.cacheFileCopy = cacheFileCopy;
1232 ByteBuffer fileROBuf = cacheFileCopy.getByteBuffer();
1234 int entryOffset = DATAFILE_HDR_SIZE + cacheAdress.getStartBlock() * cacheAdress.getBlockSize();
1237 fileROBuf.position(entryOffset);
1239 hash = fileROBuf.getInt() & UINT32_MASK;
1241 long uint32 = fileROBuf.getInt() & UINT32_MASK;
1242 nextAddress = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1244 uint32 = fileROBuf.getInt() & UINT32_MASK;
1245 rankingsNodeAddress = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1247 reuseCount = fileROBuf.getInt();
1248 refetchCount = fileROBuf.getInt();
1250 state = EntryStateEnum.values()[fileROBuf.getInt()];
1251 creationTime = (fileROBuf.getLong() / 1000000) - Long.valueOf(
"11644473600");
1253 keyLen = fileROBuf.getInt();
1255 uint32 = fileROBuf.getInt() & UINT32_MASK;
1256 longKeyAddresses = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1259 dataSizes=
new int[4];
1260 for (
int i = 0; i < 4; i++) {
1261 dataSizes[i] = fileROBuf.getInt();
1263 dataAddresses =
new CacheAddress[4];
1264 for (
int i = 0; i < 4; i++) {
1265 dataAddresses[i] =
new CacheAddress(fileROBuf.getInt() & UINT32_MASK, selfAddress.getCachePath());
1268 flags = fileROBuf.getInt() & UINT32_MASK;
1270 for (
int i = 0; i < 4; i++) {
1278 if (longKeyAddresses != null) {
1281 CacheData data =
new CacheData(longKeyAddresses, this.keyLen,
true);
1282 key = data.getDataString();
1283 }
catch (TskCoreException | IngestModuleException ex) {
1284 logger.log(Level.SEVERE, String.format(
"Failed to get external key from address %s", longKeyAddresses));
1288 StringBuilder strBuilder =
new StringBuilder(MAX_KEY_LEN);
1290 while (fileROBuf.remaining() > 0 && keyLen < MAX_KEY_LEN) {
1291 char keyChar = (char)fileROBuf.get();
1292 if (keyChar ==
'\0') {
1295 strBuilder.append(keyChar);
1299 key = strBuilder.toString();
1303 public CacheAddress getAddress() {
1307 public long getHash() {
1311 public CacheAddress getNextAddress() {
1315 public int getReuseCount() {
1319 public int getRefetchCount() {
1320 return refetchCount;
1323 public EntryStateEnum getState() {
1327 public long getCreationTime() {
1328 return creationTime;
1331 public long getFlags() {
1335 public String getKey() {
1347 public List<CacheData> getData() throws TskCoreException, IngestModuleException {
1349 if (dataList == null) {
1350 dataList =
new ArrayList<>();
1351 for (
int i = 0; i < 4; i++) {
1352 if (dataSizes[i] > 0) {
1353 CacheData cacheData =
new CacheData(dataAddresses[i], dataSizes[i],
true );
1355 cacheData.extract();
1356 dataList.add(cacheData);
1370 boolean hasHTTPHeaders() {
1371 if ((dataList == null) || dataList.isEmpty()) {
1374 return dataList.get(0).hasHTTPHeaders();
1383 String getHTTPHeader(String key) {
1384 if ((dataList == null) || dataList.isEmpty()) {
1388 return dataList.get(0).getHTTPHeader(key);
1396 String getHTTPHeaders() {
1397 if ((dataList == null) || dataList.isEmpty()) {
1401 return dataList.get(0).getHTTPHeaders();
1412 boolean isBrotliCompressed() {
1414 if (hasHTTPHeaders() ) {
1415 String encodingHeader = getHTTPHeader(
"content-encoding");
1416 if (encodingHeader!= null) {
1417 return encodingHeader.trim().equalsIgnoreCase(
"br");
1425 public String toString() {
1426 StringBuilder sb =
new StringBuilder();
1427 sb.append(String.format(
"Entry = Hash: %08x, State: %s, ReuseCount: %d, RefetchCount: %d",
1428 this.hash,
this.state.toString(), this.reuseCount, this.refetchCount ))
1429 .append(String.format(
"\n\tKey: %s, Keylen: %d",
1430 this.key,
this.keyLen,
this.reuseCount,
this.refetchCount ))
1431 .append(String.format(
"\n\tCreationTime: %s",
1432 TimeUtilities.epochToTime(
this.creationTime) ))
1433 .append(String.format(
"\n\tNext Address: %s",
1434 (nextAddress != null) ? nextAddress.toString() :
"None"));
1436 for (
int i = 0; i < 4; i++) {
1437 if (dataSizes[i] > 0) {
1438 sb.append(String.format(
"\n\tData %d: cache address = %s, Data = %s",
1439 i, dataAddresses[i].toString(),
1441 ? dataList.get(i).toString()
1442 :
"Data not retrived yet."));
1446 return sb.toString();