mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-21 15:26:16 -04:00
88 lines
2.8 KiB
JavaScript
88 lines
2.8 KiB
JavaScript
/**
|
|
*
|
|
* LZNT1 Decompress.
|
|
*
|
|
* @author 0xThiebaut [thiebaut.dev]
|
|
* @copyright Crown Copyright 2023
|
|
* @license Apache-2.0
|
|
*
|
|
* https://github.com/Velocidex/go-ntfs/blob/master/parser%2Flznt1.go
|
|
*/
|
|
|
|
import Utils from "../Utils.mjs";
|
|
import OperationError from "../errors/OperationError.mjs";
|
|
|
|
const COMPRESSED_MASK = 1 << 15,
|
|
SIZE_MASK = (1 << 12) - 1;
|
|
|
|
/**
|
|
* @param {number} offset
|
|
* @returns {number}
|
|
*/
|
|
function getDisplacement(offset) {
|
|
let result = 0;
|
|
while (offset >= 0x10) {
|
|
offset >>= 1;
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @param {byteArray} compressed
|
|
* @returns {byteArray}
|
|
*/
|
|
export function decompress(compressed) {
|
|
const decompressed = Array();
|
|
let coffset = 0;
|
|
|
|
while (coffset + 2 <= compressed.length) {
|
|
const doffset = decompressed.length;
|
|
|
|
const blockHeader = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), "little");
|
|
coffset += 2;
|
|
|
|
const size = blockHeader & SIZE_MASK;
|
|
const blockEnd = coffset + size + 1;
|
|
|
|
if (size === 0) {
|
|
break;
|
|
} else if (compressed.length < coffset + size) {
|
|
throw new OperationError("Malformed LZNT1 stream: Block too small! Has the stream been truncated?");
|
|
}
|
|
|
|
if ((blockHeader & COMPRESSED_MASK) !== 0) {
|
|
while (coffset < blockEnd) {
|
|
let header = compressed[coffset++];
|
|
|
|
for (let i = 0; i < 8 && coffset < blockEnd; i++) {
|
|
if ((header & 1) === 0) {
|
|
decompressed.push(compressed[coffset++]);
|
|
} else {
|
|
const pointer = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), "little");
|
|
coffset += 2;
|
|
|
|
const displacement = getDisplacement(decompressed.length - doffset - 1);
|
|
const symbolOffset = (pointer >> (12 - displacement)) + 1;
|
|
const symbolLength = (pointer & (0xFFF >> displacement)) + 2;
|
|
const shiftOffset = decompressed.length - symbolOffset;
|
|
|
|
for (let shiftDelta = 0; shiftDelta < symbolLength + 1; shiftDelta++) {
|
|
const shift = shiftOffset + shiftDelta;
|
|
if (shift < 0 || decompressed.length <= shift) {
|
|
throw new OperationError("Malformed LZNT1 stream: Invalid shift!");
|
|
}
|
|
decompressed.push(decompressed[shift]);
|
|
}
|
|
}
|
|
header >>= 1;
|
|
}
|
|
}
|
|
} else {
|
|
decompressed.push(...compressed.slice(coffset, coffset + size + 1));
|
|
coffset += size + 1;
|
|
}
|
|
}
|
|
|
|
return decompressed;
|
|
}
|