mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-02 22:21:01 +01:00
Began implementing GZIP/DEFLATE extraction. Unfinished.
This commit is contained in:
parent
19b7957523
commit
2a6db47aeb
@ -650,7 +650,7 @@ export const FILE_SIGNATURES = {
|
||||
56: 0x69,
|
||||
57: 0x70
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractZIP
|
||||
},
|
||||
],
|
||||
"Applications": [
|
||||
@ -790,7 +790,7 @@ export const FILE_SIGNATURES = {
|
||||
1: 0x8b,
|
||||
2: 0x8
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractGZIP
|
||||
},
|
||||
{
|
||||
name: "Bzip2",
|
||||
@ -1309,7 +1309,7 @@ export function extractFLV(bytes, offset) {
|
||||
let tagSize = -11; // Fake size of previous tag header
|
||||
while (stream.hasMore()) {
|
||||
const prevTagSize = stream.readInt(4, "be");
|
||||
const tagType = stream.readInt(1, "be");
|
||||
const tagType = stream.readInt(1);
|
||||
|
||||
if ([8, 9, 18].indexOf(tagType) < 0) {
|
||||
// This tag is not valid
|
||||
@ -1346,14 +1346,14 @@ export function extractRTF(bytes, offset) {
|
||||
|
||||
let openTags = 0;
|
||||
|
||||
if (stream.readInt(1, "be") !== 0x7b) { // {
|
||||
if (stream.readInt(1) !== 0x7b) { // {
|
||||
throw new Error("Not a valid RTF file");
|
||||
} else {
|
||||
openTags++;
|
||||
}
|
||||
|
||||
while (openTags > 0 && stream.hasMore()) {
|
||||
switch (stream.readInt(1, "be")) {
|
||||
switch (stream.readInt(1)) {
|
||||
case 0x7b: // {
|
||||
openTags++;
|
||||
break;
|
||||
@ -1372,3 +1372,102 @@ export function extractRTF(bytes, offset) {
|
||||
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GZIP extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractGZIP(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
/* HEADER */
|
||||
|
||||
// Skip over signature and compression method
|
||||
stream.moveForwardsBy(3);
|
||||
|
||||
// Read flags
|
||||
const flags = stream.readInt(1);
|
||||
|
||||
// Skip over last modification time
|
||||
stream.moveForwardsBy(4);
|
||||
|
||||
// Read compression flags
|
||||
const compressionFlags = stream.readInt(1);
|
||||
|
||||
// Skip over OS
|
||||
stream.moveForwardsBy(1);
|
||||
|
||||
|
||||
/* OPTIONAL HEADERS */
|
||||
|
||||
// Extra fields
|
||||
if (flags & 0x4) {
|
||||
console.log("Extra fields");
|
||||
const extraFieldsSize = stream.readInt(2, "le");
|
||||
stream.moveForwardsby(extraFieldsSize);
|
||||
}
|
||||
|
||||
// Original filename
|
||||
if (flags & 0x8) {
|
||||
console.log("Filename");
|
||||
stream.continueUntil(0x00);
|
||||
stream.moveForwardsBy(1);
|
||||
}
|
||||
|
||||
// Comment
|
||||
if (flags & 0x10) {
|
||||
console.log("Comment");
|
||||
stream.continueUntil(0x00);
|
||||
stream.moveForwardsBy(1);
|
||||
}
|
||||
|
||||
// Checksum
|
||||
if (flags & 0x2) {
|
||||
console.log("Checksum");
|
||||
stream.moveForwardsBy(2);
|
||||
}
|
||||
|
||||
|
||||
/* DEFLATE DATA */
|
||||
|
||||
let finalBlock = 0;
|
||||
|
||||
while (!finalBlock) {
|
||||
// Read header
|
||||
const blockHeader = stream.readBits(3);
|
||||
|
||||
finalBlock = blockHeader & 0x1;
|
||||
const blockType = blockHeader & 0x6;
|
||||
|
||||
if (blockType === 0) {
|
||||
// No compression
|
||||
stream.moveForwardsBy(1);
|
||||
const blockLength = stream.readInt(2, "le");
|
||||
console.log("No compression. Length: " + blockLength);
|
||||
stream.moveForwardsBy(2 + blockLength);
|
||||
} else if (blockType === 1) {
|
||||
// Fixed Huffman
|
||||
|
||||
} else if (blockType === 2) {
|
||||
// Dynamic Huffman
|
||||
|
||||
} else {
|
||||
throw new Error("Invalid block type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* FOOTER */
|
||||
|
||||
// Skip over checksum and size of original uncompressed input
|
||||
stream.moveForwardsBy(8);
|
||||
|
||||
console.log(stream.position);
|
||||
|
||||
return stream.carve();
|
||||
}
|
||||
|
@ -21,8 +21,9 @@ export default class Stream {
|
||||
*/
|
||||
constructor(input) {
|
||||
this.bytes = input;
|
||||
this.position = 0;
|
||||
this.length = this.bytes.length;
|
||||
this.position = 0;
|
||||
this.bitPos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,6 +38,7 @@ export default class Stream {
|
||||
const newPosition = this.position + numBytes;
|
||||
const bytes = this.bytes.slice(this.position, newPosition);
|
||||
this.position = newPosition;
|
||||
this.bitPos = 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@ -57,6 +59,7 @@ export default class Stream {
|
||||
result += String.fromCharCode(currentByte);
|
||||
}
|
||||
this.position += numBytes;
|
||||
this.bitPos = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -83,9 +86,59 @@ export default class Stream {
|
||||
}
|
||||
}
|
||||
this.position += numBytes;
|
||||
this.bitPos = 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a number of bits from the buffer.
|
||||
*
|
||||
* @TODO Add endianness
|
||||
*
|
||||
* @param {number} numBits
|
||||
* @returns {number}
|
||||
*/
|
||||
readBits(numBits) {
|
||||
if (this.position > this.length) return undefined;
|
||||
|
||||
let bitBuf = 0,
|
||||
bitBufLen = 0;
|
||||
|
||||
// Add remaining bits from current byte
|
||||
bitBuf = this.bytes[this.position++] & bitMask(this.bitPos);
|
||||
bitBufLen = 8 - this.bitPos;
|
||||
this.bitPos = 0;
|
||||
|
||||
// Not enough bits yet
|
||||
while (bitBufLen < numBits) {
|
||||
bitBuf |= this.bytes[this.position++] << bitBufLen;
|
||||
bitBufLen += 8;
|
||||
}
|
||||
|
||||
// Reverse back to numBits
|
||||
if (bitBufLen > numBits) {
|
||||
const excess = bitBufLen - numBits;
|
||||
bitBuf >>>= excess;
|
||||
bitBufLen -= excess;
|
||||
this.position--;
|
||||
this.bitPos = 8 - excess;
|
||||
}
|
||||
|
||||
return bitBuf;
|
||||
|
||||
/**
|
||||
* Calculates the bit mask based on the current bit position.
|
||||
*
|
||||
* @param {number} bitPos
|
||||
* @returns {number} The bit mask
|
||||
*/
|
||||
function bitMask(bitPos) {
|
||||
return (1 << (8 - bitPos)) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Consume the stream until we reach the specified byte or sequence of bytes.
|
||||
*
|
||||
@ -94,6 +147,8 @@ export default class Stream {
|
||||
continueUntil(val) {
|
||||
if (this.position > this.length) return;
|
||||
|
||||
this.bitPos = 0;
|
||||
|
||||
if (typeof val === "number") {
|
||||
while (++this.position < this.length && this.bytes[this.position] !== val) {
|
||||
continue;
|
||||
@ -121,8 +176,10 @@ export default class Stream {
|
||||
* @param {number} val
|
||||
*/
|
||||
consumeIf(val) {
|
||||
if (this.bytes[this.position] === val)
|
||||
if (this.bytes[this.position] === val) {
|
||||
this.position++;
|
||||
this.bitPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,6 +192,7 @@ export default class Stream {
|
||||
if (pos < 0 || pos > this.length)
|
||||
throw new Error("Cannot move to position " + pos + " in stream. Out of bounds.");
|
||||
this.position = pos;
|
||||
this.bitPos = 0;
|
||||
}
|
||||
|
||||
|
||||
@ -148,6 +206,7 @@ export default class Stream {
|
||||
if (pos < 0 || pos > this.length)
|
||||
throw new Error("Cannot move to position " + pos + " in stream. Out of bounds.");
|
||||
this.position = pos;
|
||||
this.bitPos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,6 +218,7 @@ export default class Stream {
|
||||
if (pos < 0 || pos > this.length)
|
||||
throw new Error("Cannot move to position " + pos + " in stream. Out of bounds.");
|
||||
this.position = pos;
|
||||
this.bitPos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,6 +236,7 @@ export default class Stream {
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
carve() {
|
||||
if (this.bitPos > 0) this.position++;
|
||||
return this.bytes.slice(0, this.position);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user