Przeglądaj źródła

Merge remote-tracking branch 'upstream/master' into dish-fix

n1073645 5 lat temu
rodzic
commit
78d1114869

+ 1 - 1
package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "cyberchef",
-  "version": "9.11.14",
+  "version": "9.11.17",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "cyberchef",
-  "version": "9.11.14",
+  "version": "9.11.17",
   "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
   "author": "n1474335 <n1474335@gmail.com>",
   "homepage": "https://gchq.github.io/CyberChef",

+ 142 - 4
src/core/lib/FileSignatures.mjs

@@ -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.
  *

+ 4 - 4
src/core/operations/Gunzip.mjs

@@ -5,9 +5,9 @@
  */
 
 import Operation from "../Operation.mjs";
-import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js";
+import gunzip from "zlibjs/bin/gunzip.min.js";
 
-const Zlib = zlibAndGzip.Zlib;
+const Zlib = gunzip.Zlib;
 
 /**
  * Gunzip operation
@@ -42,8 +42,8 @@ class Gunzip extends Operation {
      * @returns {File}
      */
     run(input, args) {
-        const gunzip = new Zlib.Gunzip(new Uint8Array(input));
-        return new Uint8Array(gunzip.decompress()).buffer;
+        const gzipObj = new Zlib.Gunzip(new Uint8Array(input));
+        return new Uint8Array(gzipObj.decompress()).buffer;
     }
 
 }

+ 9 - 6
src/core/operations/Gzip.mjs

@@ -6,9 +6,9 @@
 
 import Operation from "../Operation.mjs";
 import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib.mjs";
-import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js";
+import gzip from "zlibjs/bin/gzip.min.js";
 
-const Zlib = zlibAndGzip.Zlib;
+const Zlib = gzip.Zlib;
 
 /**
  * Gzip operation
@@ -73,12 +73,15 @@ class Gzip extends Operation {
             options.filename = filename;
         }
         if (comment.length) {
-            options.flags.fcommenct = true;
+            options.flags.comment = true;
             options.comment = comment;
         }
-
-        const gzip = new Zlib.Gzip(new Uint8Array(input), options);
-        return new Uint8Array(gzip.compress()).buffer;
+        const gzipObj = new Zlib.Gzip(new Uint8Array(input), options);
+        const compressed = new Uint8Array(gzipObj.compress());
+        if (options.flags.comment && !(compressed[3] & 0x10)) {
+            compressed[3] |= 0x10;
+        }
+        return compressed.buffer;
     }
 
 }

+ 15 - 0
src/web/waiters/RecipeWaiter.mjs

@@ -51,6 +51,7 @@ class RecipeWaiter {
                 }
             }.bind(this),
             onSort: function(evt) {
+                this.updateZIndices();
                 if (evt.from.id === "rec-list") {
                     document.dispatchEvent(this.manager.statechange);
                 }
@@ -149,6 +150,19 @@ class RecipeWaiter {
     }
 
 
+    /**
+     * Sets the z-index property on each operation to make sure that operations higher in the list
+     * have a higher index, meaning dropdowns are not hidden underneath subsequent operations.
+     */
+    updateZIndices() {
+        const operations = document.getElementById("rec-list").children;
+        for (let i = 0; i < operations.length; i++) {
+            const operation = operations[i];
+            operation.style.zIndex = 100 + operations.length - i;
+        }
+    }
+
+
     /**
      * Handler for favourite dragover events.
      * If the element being dragged is an operation, displays a visual cue so that the user knows it can
@@ -466,6 +480,7 @@ class RecipeWaiter {
         log.debug(`'${e.target.querySelector(".op-title").textContent}' added to recipe`);
 
         this.triggerArgEvents(e.target);
+        this.updateZIndices();
         window.dispatchEvent(this.manager.statechange);
     }
 

+ 2 - 0
tests/operations/index.mjs

@@ -41,6 +41,8 @@ import "./tests/DateTime.mjs";
 import "./tests/ExtractEmailAddresses.mjs";
 import "./tests/Fork.mjs";
 import "./tests/FromDecimal.mjs";
+import "./tests/Gzip.mjs";
+import "./tests/Gunzip.mjs";
 import "./tests/Hash.mjs";
 import "./tests/HaversineDistance.mjs";
 import "./tests/Hexdump.mjs";

+ 58 - 0
tests/operations/tests/Gunzip.mjs

@@ -0,0 +1,58 @@
+/**
+ * Gunzip Tests.
+ *
+ * @author n1073645 [n1073645@gmail.com]
+ *
+ * @copyright Crown Copyright 2019
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+    {
+        name: "Gunzip: No comment, no checksum and no filename",
+        input: "1f8b0800f7c8f85d00ff0dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
+        expectedOutput: "The quick brown fox jumped over the slow dog",
+        recipeConfig: [
+            {
+                op: "From Hex",
+                args: ["None"]
+            },
+            {
+                op: "Gunzip",
+                args: []
+            }
+        ]
+    },
+    {
+        name: "Gunzip: No comment, no checksum and filename",
+        input: "1f8b080843c9f85d00ff66696c656e616d65000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
+        expectedOutput: "The quick brown fox jumped over the slow dog",
+        recipeConfig: [
+            {
+                op: "From Hex",
+                args: ["None"]
+            },
+            {
+                op: "Gunzip",
+                args: []
+            }
+        ]
+    },
+    {
+        name: "Gunzip: Has a comment, no checksum and has a filename",
+        input: "1f8b08186fc9f85d00ff66696c656e616d6500636f6d6d656e74000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
+        expectedOutput: "The quick brown fox jumped over the slow dog",
+        recipeConfig: [
+            {
+                op: "From Hex",
+                args: ["None"]
+            },
+            {
+                op: "Gunzip",
+                args: []
+            }
+        ]
+    }
+]);

+ 89 - 0
tests/operations/tests/Gzip.mjs

@@ -0,0 +1,89 @@
+/**
+ * Gzip Tests.
+ *
+ * @author n1073645 [n1073645@gmail.com]
+ *
+ * @copyright Crown Copyright 2019
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+    {
+        name: "Gzip: No comment, no checksum and no filename",
+        input: "The quick brown fox jumped over the slow dog",
+        expectedOutput: "0dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
+        recipeConfig: [
+            {
+                op: "Gzip",
+                args: ["Dynamic Huffman Coding", "", "", false]
+            },
+            {
+                op: "Drop bytes",
+                args: [0, 10, false]
+            },
+            {
+                op: "To Hex",
+                args: ["None"]
+            }
+        ]
+    },
+    {
+        name: "Gzip: No comment, no checksum and has a filename",
+        input: "The quick brown fox jumped over the slow dog",
+        expectedOutput: "636f6d6d656e74000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
+        recipeConfig: [
+            {
+                op: "Gzip",
+                args: ["Dynamic Huffman Coding", "comment", "", false]
+            },
+            {
+                op: "Drop bytes",
+                args: [0, 10, false]
+            },
+            {
+                op: "To Hex",
+                args: ["None"]
+            }
+        ]
+    },
+    {
+        name: "Gzip: Has a comment, no checksum and no filename",
+        input: "The quick brown fox jumped over the slow dog",
+        expectedOutput: "636f6d6d656e74000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
+        recipeConfig: [
+            {
+                op: "Gzip",
+                args: ["Dynamic Huffman Coding", "", "comment", false]
+            },
+            {
+                op: "Drop bytes",
+                args: [0, 10, false]
+            },
+            {
+                op: "To Hex",
+                args: ["None"]
+            }
+        ]
+    },
+    {
+        name: "Gzip: Has a comment, no checksum and has a filename",
+        input: "The quick brown fox jumped over the slow dog",
+        expectedOutput: "66696c656e616d6500636f6d6d656e74000dc9dd0180200804e0556ea8262848fb3dc588c6a7e76faa8aeedb726036c68d951f76bf9a0af8aae1f97d9c0c084b02509cbf8c2c000000",
+        recipeConfig: [
+            {
+                op: "Gzip",
+                args: ["Dynamic Huffman Coding", "filename", "comment", false]
+            },
+            {
+                op: "Drop bytes",
+                args: [0, 10, false]
+            },
+            {
+                op: "To Hex",
+                args: ["None"]
+            }
+        ]
+    },
+]);