mirror of
https://github.com/gchq/CyberChef
synced 2024-11-14 00:27:07 +00:00
Add support for LZNT1 decompression.
This commit is contained in:
parent
6ed9d4554a
commit
77042abc23
8 changed files with 161 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@ npm-debug.log
|
||||||
travis.log
|
travis.log
|
||||||
build
|
build
|
||||||
.vscode
|
.vscode
|
||||||
|
.idea
|
||||||
.*.swp
|
.*.swp
|
||||||
src/core/config/modules/*
|
src/core/config/modules/*
|
||||||
src/core/config/OperationConfig.json
|
src/core/config/OperationConfig.json
|
||||||
|
|
|
@ -351,7 +351,8 @@
|
||||||
"LZMA Decompress",
|
"LZMA Decompress",
|
||||||
"LZMA Compress",
|
"LZMA Compress",
|
||||||
"LZ4 Decompress",
|
"LZ4 Decompress",
|
||||||
"LZ4 Compress"
|
"LZ4 Compress",
|
||||||
|
"LZNT1 Decompress"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
88
src/core/lib/LZNT1.mjs
Normal file
88
src/core/lib/LZNT1.mjs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
41
src/core/operations/LZNT1Decompress.mjs
Normal file
41
src/core/operations/LZNT1Decompress.mjs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* @author 0xThiebaut [thiebaut.dev]
|
||||||
|
* @copyright Crown Copyright 2023
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import {decompress} from "../lib/LZNT1.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LZNT1 Decompress operation
|
||||||
|
*/
|
||||||
|
class LZNT1Decompress extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LZNT1 Decompress constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "LZNT1 Decompress";
|
||||||
|
this.module = "Compression";
|
||||||
|
this.description = "Decompresses data using the LZNT1 algorithm.<br><br>Similar to the Windows API <code>RtlDecompressBuffer</code>.";
|
||||||
|
this.infoURL = "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xca/5655f4a3-6ba4-489b-959f-e1f407c52f15";
|
||||||
|
this.inputType = "byteArray";
|
||||||
|
this.outputType = "byteArray";
|
||||||
|
this.args = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {byteArray} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {byteArray}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
return decompress(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LZNT1Decompress;
|
|
@ -62,7 +62,8 @@
|
||||||
"Training branch predictor...",
|
"Training branch predictor...",
|
||||||
"Timing cache hits...",
|
"Timing cache hits...",
|
||||||
"Speculatively executing recipes...",
|
"Speculatively executing recipes...",
|
||||||
"Adding LLM hallucinations..."
|
"Adding LLM hallucinations...",
|
||||||
|
"Decompressing malware..."
|
||||||
];
|
];
|
||||||
|
|
||||||
// Shuffle array using Durstenfeld algorithm
|
// Shuffle array using Durstenfeld algorithm
|
||||||
|
|
|
@ -635,6 +635,10 @@ WWFkYSBZYWRh\r
|
||||||
assert.strictEqual(chef.keccak("Flea Market").toString(), "c2a06880b19e453ee5440e8bd4c2024bedc15a6630096aa3f609acfd2b8f15f27cd293e1cc73933e81432269129ce954a6138889ce87831179d55dcff1cc7587");
|
assert.strictEqual(chef.keccak("Flea Market").toString(), "c2a06880b19e453ee5440e8bd4c2024bedc15a6630096aa3f609acfd2b8f15f27cd293e1cc73933e81432269129ce954a6138889ce87831179d55dcff1cc7587");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
it("LZNT1 Decompress", () => {
|
||||||
|
assert.strictEqual(chef.LZNT1Decompress("\x1a\xb0\x00compress\x00edtestda\x04ta\x07\x88alot").toString(), "compressedtestdatacompressedalot");
|
||||||
|
}),
|
||||||
|
|
||||||
it("MD6", () => {
|
it("MD6", () => {
|
||||||
assert.strictEqual(chef.MD6("Head Over Heels", {key: "arty"}).toString(), "d8f7fe4931fbaa37316f76283d5f615f50ddd54afdc794b61da522556aee99ad");
|
assert.strictEqual(chef.MD6("Head Over Heels", {key: "arty"}).toString(), "d8f7fe4931fbaa37316f76283d5f615f50ddd54afdc794b61da522556aee99ad");
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -62,6 +62,7 @@ import "./tests/JSONtoCSV.mjs";
|
||||||
import "./tests/JWTDecode.mjs";
|
import "./tests/JWTDecode.mjs";
|
||||||
import "./tests/JWTSign.mjs";
|
import "./tests/JWTSign.mjs";
|
||||||
import "./tests/JWTVerify.mjs";
|
import "./tests/JWTVerify.mjs";
|
||||||
|
import "./tests/LZNT1Decompress.mjs";
|
||||||
import "./tests/MS.mjs";
|
import "./tests/MS.mjs";
|
||||||
import "./tests/Magic.mjs";
|
import "./tests/Magic.mjs";
|
||||||
import "./tests/MorseCode.mjs";
|
import "./tests/MorseCode.mjs";
|
||||||
|
|
22
tests/operations/tests/LZNT1Decompress.mjs
Normal file
22
tests/operations/tests/LZNT1Decompress.mjs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* LZNT1 Decompress tests.
|
||||||
|
*
|
||||||
|
* @author 0xThiebaut [thiebaut.dev]
|
||||||
|
* @copyright Crown Copyright 2023
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "LZNT1 Decompress",
|
||||||
|
input: "\x1a\xb0\x00compress\x00edtestda\x04ta\x07\x88alot",
|
||||||
|
expectedOutput: "compressedtestdatacompressedalot",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "LZNT1 Decompress",
|
||||||
|
args: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]);
|
Loading…
Reference in a new issue