mirror of
https://github.com/gchq/CyberChef
synced 2025-01-01 07:18:47 +00:00
Added 'Move to input' button to output file list. Improved zlib extraction efficiency.
This commit is contained in:
parent
9fa7edffbf
commit
84d31c1d59
9 changed files with 94 additions and 28 deletions
|
@ -102,6 +102,7 @@
|
|||
"$": false,
|
||||
"jQuery": false,
|
||||
"log": false,
|
||||
"app": false,
|
||||
|
||||
"COMPILE_TIME": false,
|
||||
"COMPILE_MSG": false,
|
||||
|
|
|
@ -832,8 +832,9 @@ class Utils {
|
|||
const buff = await Utils.readFile(file);
|
||||
const blob = new Blob(
|
||||
[buff],
|
||||
{type: "octet/stream"}
|
||||
{type: file.type || "octet/stream"}
|
||||
);
|
||||
const blobURL = URL.createObjectURL(blob);
|
||||
|
||||
const html = `<div class='card' style='white-space: normal;'>
|
||||
<div class='card-header' id='heading${i}'>
|
||||
|
@ -848,10 +849,19 @@ class Utils {
|
|||
<span class='float-right' style="margin-top: -3px">
|
||||
${file.size.toLocaleString()} bytes
|
||||
<a title="Download ${Utils.escapeHtml(file.name)}"
|
||||
href='${URL.createObjectURL(blob)}'
|
||||
download='${Utils.escapeHtml(file.name)}'>
|
||||
href="${blobURL}"
|
||||
download="${Utils.escapeHtml(file.name)}"
|
||||
data-toggle="tooltip">
|
||||
<i class="material-icons" style="vertical-align: bottom">save</i>
|
||||
</a>
|
||||
<a title="Move to input"
|
||||
href="#"
|
||||
blob-url="${blobURL}"
|
||||
file-name="${Utils.escapeHtml(file.name)}"
|
||||
class="extract-file"
|
||||
data-toggle="tooltip">
|
||||
<i class="material-icons" style="vertical-align: bottom">open_in_browser</i>
|
||||
</a>
|
||||
</span>
|
||||
</h6>
|
||||
</div>
|
||||
|
@ -1163,6 +1173,21 @@ String.prototype.count = function(chr) {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper for self.sendStatusMessage to handle different environments.
|
||||
*
|
||||
* @param {string} msg
|
||||
*/
|
||||
export function sendStatusMessage(msg) {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage(msg);
|
||||
else if (ENVIRONMENT_IS_WEB())
|
||||
app.alert(msg, 10000);
|
||||
else if (ENVIRONMENT_IS_NODE())
|
||||
log.debug(msg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Polyfills
|
||||
*/
|
||||
|
|
|
@ -1518,14 +1518,8 @@ export function extractELF(bytes, offset) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Steps through a DEFLATE stream
|
||||
*
|
||||
* @param {Stream} stream
|
||||
*/
|
||||
function parseDEFLATE(stream) {
|
||||
// Construct required Huffman Tables
|
||||
const fixedLiteralTableLengths = new Uint8Array(288);
|
||||
const fixedLiteralTableLengths = new Array(288);
|
||||
for (let i = 0; i < fixedLiteralTableLengths.length; i++) {
|
||||
fixedLiteralTableLengths[i] =
|
||||
(i <= 143) ? 8 :
|
||||
|
@ -1534,10 +1528,16 @@ function parseDEFLATE(stream) {
|
|||
8;
|
||||
}
|
||||
const fixedLiteralTable = buildHuffmanTable(fixedLiteralTableLengths);
|
||||
const fixedDistanceTableLengths = new Uint8Array(30).fill(5);
|
||||
const fixedDistanceTableLengths = new Array(30).fill(5);
|
||||
const fixedDistanceTable = buildHuffmanTable(fixedDistanceTableLengths);
|
||||
const huffmanOrder = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
||||
const huffmanOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
||||
|
||||
/**
|
||||
* Steps through a DEFLATE stream
|
||||
*
|
||||
* @param {Stream} stream
|
||||
*/
|
||||
function parseDEFLATE(stream) {
|
||||
// Parse DEFLATE data
|
||||
let finalBlock = 0;
|
||||
|
||||
|
@ -1619,6 +1619,14 @@ function parseDEFLATE(stream) {
|
|||
}
|
||||
|
||||
|
||||
// Static length tables
|
||||
const lengthExtraTable = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0
|
||||
];
|
||||
const distanceExtraTable = [
|
||||
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
|
||||
];
|
||||
|
||||
/**
|
||||
* Parses a Huffman Block given the literal and distance tables
|
||||
*
|
||||
|
@ -1627,20 +1635,18 @@ function parseDEFLATE(stream) {
|
|||
* @param {Uint32Array} distTab
|
||||
*/
|
||||
function parseHuffmanBlock(stream, litTab, distTab) {
|
||||
const lengthExtraTable = new Uint8Array([
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0
|
||||
]);
|
||||
const distanceExtraTable = new Uint8Array([
|
||||
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
|
||||
]);
|
||||
|
||||
let code;
|
||||
let loops = 0;
|
||||
while ((code = readHuffmanCode(stream, litTab))) {
|
||||
// console.log("Code: " + code + " (" + Utils.chr(code) + ") " + Utils.bin(code));
|
||||
|
||||
// End of block
|
||||
if (code === 256) break;
|
||||
|
||||
// Detect probably infinite loops
|
||||
if (++loops > 10000)
|
||||
throw new Error("Caught in probable infinite loop while parsing Huffman Block");
|
||||
|
||||
// Literal
|
||||
if (code < 256) continue;
|
||||
|
||||
|
@ -1657,7 +1663,7 @@ function parseHuffmanBlock(stream, litTab, distTab) {
|
|||
/**
|
||||
* Builds a Huffman table given the relevant code lengths
|
||||
*
|
||||
* @param {Uint8Array} lengths
|
||||
* @param {Array} lengths
|
||||
* @returns {Array} result
|
||||
* @returns {Uint32Array} result.table
|
||||
* @returns {number} result.maxCodeLength
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*
|
||||
*/
|
||||
import {FILE_SIGNATURES} from "./FileSignatures";
|
||||
import {sendStatusMessage} from "../Utils";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -148,6 +149,7 @@ export function scanForFileTypes(buf, categories=Object.keys(FILE_SIGNATURES)) {
|
|||
let pos = 0;
|
||||
while ((pos = locatePotentialSig(buf, sig, pos)) >= 0) {
|
||||
if (bytesMatch(sig, buf, pos)) {
|
||||
sendStatusMessage(`Found potential signature for ${filetype.name} at pos ${pos}`);
|
||||
foundFiles.push({
|
||||
offset: pos,
|
||||
fileDetails: filetype
|
||||
|
@ -249,9 +251,12 @@ export function isImage(buf) {
|
|||
*/
|
||||
export function extractFile(bytes, fileDetail, offset) {
|
||||
if (fileDetail.extractor) {
|
||||
sendStatusMessage(`Attempting to extract ${fileDetail.name} at pos ${offset}...`);
|
||||
const fileData = fileDetail.extractor(bytes, offset);
|
||||
const ext = fileDetail.extension.split(",")[0];
|
||||
return new File([fileData], `extracted_at_0x${offset.toString(16)}.${ext}`);
|
||||
return new File([fileData], `extracted_at_0x${offset.toString(16)}.${ext}`, {
|
||||
type: fileDetail.mime
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error(`No extraction algorithm available for "${fileDetail.mime}" files`);
|
||||
|
|
|
@ -62,12 +62,13 @@ class ExtractFiles extends Operation {
|
|||
|
||||
// Extract each file that we support
|
||||
const files = [];
|
||||
const errors = [];
|
||||
detectedFiles.forEach(detectedFile => {
|
||||
try {
|
||||
files.push(extractFile(bytes, detectedFile.fileDetails, detectedFile.offset));
|
||||
} catch (err) {
|
||||
if (!ignoreFailedExtractions && err.message.indexOf("No extraction algorithm available") < 0) {
|
||||
throw new OperationError(
|
||||
errors.push(
|
||||
`Error while attempting to extract ${detectedFile.fileDetails.name} ` +
|
||||
`at offset ${detectedFile.offset}:\n` +
|
||||
`${err.message}`
|
||||
|
@ -76,9 +77,14 @@ class ExtractFiles extends Operation {
|
|||
}
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
throw new OperationError(errors.join("\n\n"));
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays the files in HTML for web apps.
|
||||
*
|
||||
|
|
|
@ -173,6 +173,7 @@ class Manager {
|
|||
this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output);
|
||||
this.addDynamicListener("#output-file-slice i", "click", this.output.displayFileSlice, this.output);
|
||||
document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output));
|
||||
this.addDynamicListener(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output);
|
||||
|
||||
// Options
|
||||
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));
|
||||
|
|
|
@ -494,6 +494,24 @@ class OutputWaiter {
|
|||
magicButton.setAttribute("data-original-title", "Magic!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for extract file events.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
async extractFileClick(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const el = e.target.nodeName === "I" ? e.target.parentNode : e.target;
|
||||
const blobURL = el.getAttribute("blob-url");
|
||||
const fileName = el.getAttribute("file-name");
|
||||
|
||||
const blob = await fetch(blobURL).then(r => r.blob());
|
||||
this.manager.input.loadFile(new File([blob], fileName, {type: blob.type}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default OutputWaiter;
|
||||
|
|
|
@ -271,7 +271,7 @@
|
|||
<i class="material-icons">content_copy</i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary bmd-btn-icon" id="switch" data-toggle="tooltip" title="Move output to input">
|
||||
<i class="material-icons">loop</i>
|
||||
<i class="material-icons">open_in_browser</i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary bmd-btn-icon" id="undo-switch" data-toggle="tooltip" title="Undo" disabled="disabled">
|
||||
<i class="material-icons">undo</i>
|
||||
|
|
|
@ -91,3 +91,7 @@
|
|||
padding-right: 6px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
#files .card-header .float-right a:hover {
|
||||
text-decoration: none;
|
||||
}
|
Loading…
Reference in a new issue