|
@@ -280,7 +280,7 @@ export const FILE_SIGNATURES = {
|
|
|
9: 0x0,
|
|
|
10: [0x0, 0x1]
|
|
|
},
|
|
|
- extractor: null
|
|
|
+ extractor: extractICO
|
|
|
},
|
|
|
{
|
|
|
name: "Radiance High Dynamic Range image",
|
|
@@ -2933,6 +2933,32 @@ export function extractBMP(bytes, offset) {
|
|
|
}
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * ICO extractor.
|
|
|
+ *
|
|
|
+ * @param {Uint8Array} bytes
|
|
|
+ * @param {number} offset
|
|
|
+ */
|
|
|
+export function extractICO(bytes, offset) {
|
|
|
+ const stream = new Stream(bytes.slice(offset));
|
|
|
+
|
|
|
+ // Move to number of files there are.
|
|
|
+ stream.moveTo(4);
|
|
|
+
|
|
|
+ // Read the number of files stored in the ICO
|
|
|
+ const numberFiles = stream.readInt(2, "le");
|
|
|
+
|
|
|
+ // Move forward to the last file header.
|
|
|
+ stream.moveForwardsBy(8 + ((numberFiles-1) * 16));
|
|
|
+ const fileSize = stream.readInt(4, "le");
|
|
|
+ const fileOffset = stream.readInt(4, "le");
|
|
|
+
|
|
|
+ // Move to the end of the last file.
|
|
|
+ stream.moveTo(fileOffset + fileSize);
|
|
|
+ return stream.carve();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* WAV extractor.
|
|
|
*
|
|
@@ -3075,15 +3101,127 @@ export function extractSQLITE(bytes, offset) {
|
|
|
export function extractPListXML(bytes, offset) {
|
|
|
const stream = new Stream(bytes.slice(offset));
|
|
|
|
|
|
- // Find closing tag (</plist>)
|
|
|
- stream.continueUntil([0x3c, 0x2f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x3e]);
|
|
|
- stream.moveForwardsBy(8);
|
|
|
+ let braceCount = 0;
|
|
|
+
|
|
|
+ // Continue to the first (<plist).
|
|
|
+ stream.continueUntil([0x3c, 0x70, 0x6c, 0x69, 0x73, 0x74]);
|
|
|
+ stream.moveForwardsBy(6);
|
|
|
+ braceCount++;
|
|
|
+
|
|
|
+ // While we have an unequal amount of braces.
|
|
|
+ while (braceCount > 0 && stream.hasMore()) {
|
|
|
+ if (stream.readInt(1) === 0x3c) {
|
|
|
+
|
|
|
+ // If we hit an <plist.
|
|
|
+ if (stream.getBytes(5).join("") === [0x70, 0x6c, 0x69, 0x73, 0x74].join("")) {
|
|
|
+ braceCount++;
|
|
|
+ } else {
|
|
|
+ stream.moveBackwardsBy(5);
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we hit an </plist>.
|
|
|
+ if (stream.getBytes(7).join("") === [0x2f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x3e].join("")) {
|
|
|
+ braceCount--;
|
|
|
+ } else {
|
|
|
+ stream.moveBackwardsBy(7);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
stream.consumeIf(0x0a);
|
|
|
|
|
|
return stream.carve();
|
|
|
}
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * OLE2 extractor.
|
|
|
+ *
|
|
|
+ * @param {Uint8Array} bytes
|
|
|
+ * @param {number} offset
|
|
|
+ * @returns {Uint8Array}
|
|
|
+ */
|
|
|
+export function extractOLE2(bytes, offset) {
|
|
|
+ const stream = new Stream(bytes.slice(offset));
|
|
|
+ const entries = [
|
|
|
+ [[0x52, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x20, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x72, 0x00, 0x79], 19, "Root Entry"],
|
|
|
+ [[0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6b], 15, "Workbook"],
|
|
|
+ [[0x43, 0x00, 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72], 23, "Current User"],
|
|
|
+ [[0x50, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x65, 0x00, 0x72, 0x00, 0x50, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74], 37, "PowerPoint Document"],
|
|
|
+ [[0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74], 23, "WordDocument"],
|
|
|
+ [[0x44, 0x00, 0x61, 0x00, 0x74, 0x00, 0x61], 7, "Data"],
|
|
|
+ [[0x50, 0x00, 0x69, 0x00, 0x63, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73], 15, "Pictures"],
|
|
|
+ [[0x31, 0x00, 0x54, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65], 11, "1Table"],
|
|
|
+ [[0x05, 0x00, 0x53, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e], 37, "SummaryInformation"],
|
|
|
+ [[0x05, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x53, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e], 53, "DocumentSummaryInformation"],
|
|
|
+ [[0x43, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x4f, 0x00, 0x62, 0x00, 0x6a], 13, "Comp Obj"],
|
|
|
+ [[0x01, 0x00], 2, "Entry"]
|
|
|
+ ];
|
|
|
+ let endianness = "le";
|
|
|
+
|
|
|
+ // Move to endianess field.
|
|
|
+ stream.moveForwardsBy(28);
|
|
|
+ if (stream.readInt(2, endianness) === 0xfffe)
|
|
|
+ endianness = "be";
|
|
|
+
|
|
|
+ // Calculate the size of the normal sectors.
|
|
|
+ const sizeOfSector = 2 ** stream.readInt(2, endianness);
|
|
|
+
|
|
|
+ // Move to root directory offset field.
|
|
|
+ stream.moveTo(48);
|
|
|
+
|
|
|
+ // Read root directory offset.
|
|
|
+ const rootStuff = stream.readInt(4, endianness);
|
|
|
+
|
|
|
+ // Calculate root directory offset.
|
|
|
+ let total = 512 + (rootStuff * sizeOfSector);
|
|
|
+ stream.moveTo(total);
|
|
|
+
|
|
|
+ // While valid directory entries.
|
|
|
+ let found = true;
|
|
|
+ while (found) {
|
|
|
+ found = false;
|
|
|
+
|
|
|
+ // Attempt to determine what directory entry it is.
|
|
|
+ for (const element of entries) {
|
|
|
+
|
|
|
+ // If the byte pattern matches.
|
|
|
+ if (stream.getBytes(element[1]).join("") === element[0].join("")) {
|
|
|
+ stream.moveBackwardsBy(element[1]);
|
|
|
+ found = true;
|
|
|
+
|
|
|
+ // Move forwards by the size of the comp obj.
|
|
|
+ if (element[2] === "Comp Obj") {
|
|
|
+
|
|
|
+ // The size of the Comp Obj entry - 128. Since we add 128 later.
|
|
|
+ total += 128 * 6;
|
|
|
+ stream.moveTo(total);
|
|
|
+ } else if (element[2] === "Entry") {
|
|
|
+
|
|
|
+ // If there is an entry move backwards by 126 to then move forwards by 128. Hence a total displacement of 2.
|
|
|
+ stream.moveBackwardsBy(126);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ stream.moveBackwardsBy(element[1]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we have found a valid entry, move forwards by 128.
|
|
|
+ if (found) {
|
|
|
+
|
|
|
+ // Every entry is at least 128 in size, some are bigger which is dealt with by the above if statement.
|
|
|
+ total += 128;
|
|
|
+ stream.moveForwardsBy(128);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Round up to a multiple of 512.
|
|
|
+ total = Math.ceil(total / 512) * 512;
|
|
|
+
|
|
|
+ stream.moveTo(total);
|
|
|
+ return stream.carve();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* GZIP extractor.
|
|
|
*
|