diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 89938834..d465734d 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -326,7 +326,8 @@ "Unescape string", "Pseudo-Random Number Generator", "Sleep", - "File Tree" + "File Tree", + "Drop nth bytes" ] }, { diff --git a/src/core/operations/DropNthBytes.mjs b/src/core/operations/DropNthBytes.mjs new file mode 100644 index 00000000..e6bac1cd --- /dev/null +++ b/src/core/operations/DropNthBytes.mjs @@ -0,0 +1,79 @@ +/** + * @author Oshawk [oshawk@protonmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Drop nth bytes operation + */ +class DropNthBytes extends Operation { + + /** + * DropNthBytes constructor + */ + constructor() { + super(); + + this.name = "Drop nth bytes"; + this.module = "Default"; + this.description = "Drops every nth byte starting with a given byte."; + this.infoURL = ""; + this.inputType = "byteArray"; + this.outputType = "byteArray"; + this.args = [ + { + name: "Drop every", + type: "number", + value: 4 + }, + { + name: "Starting at", + type: "number", + value: 0 + }, + { + name: "Apply to each line", + type: "boolean", + value: false + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {byteArray} + */ + run(input, args) { + const n = args[0]; + const start = args[1]; + const eachLine = args[2]; + + if (parseInt(n, 10) !== n || n <= 0) { + throw new OperationError("'Drop every' must be a positive integer."); + } + if (parseInt(start, 10) !== start || start < 0) { + throw new OperationError("'Starting at' must be a positive or zero integer."); + } + + let offset = 0; + const output = []; + for (let i = 0; i < input.length; i++) { + if (eachLine && input[i] === 0x0a) { + output.push(0x0a); + offset = i + 1; + } else if (i - offset < start || (i - (start + offset)) % n !== 0) { + output.push(input[i]); + } + } + + return output; + } + +} + +export default DropNthBytes; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index acac5f09..1755d86c 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -59,6 +59,7 @@ import "./tests/Crypt.mjs"; import "./tests/CSV.mjs"; import "./tests/DateTime.mjs"; import "./tests/DefangIP.mjs"; +import "./tests/DropNthBytes.mjs"; import "./tests/ECDSA.mjs"; import "./tests/ELFInfo.mjs"; import "./tests/Enigma.mjs"; diff --git a/tests/operations/tests/DropNthBytes.mjs b/tests/operations/tests/DropNthBytes.mjs new file mode 100644 index 00000000..00d4e0ab --- /dev/null +++ b/tests/operations/tests/DropNthBytes.mjs @@ -0,0 +1,123 @@ +/** + * @author Oshawk [oshawk@protonmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import TestRegister from "../../lib/TestRegister.mjs"; + +/** + * Drop nth bytes tests + */ +TestRegister.addTests([ + { + name: "Drop nth bytes: Nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Drop nth bytes: Nothing (apply to each line)", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Drop nth bytes: Basic single line", + input: "0123456789", + expectedOutput: "1235679", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Drop nth bytes: Basic single line (apply to each line)", + input: "0123456789", + expectedOutput: "1235679", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Drop nth bytes: Complex single line", + input: "0123456789", + expectedOutput: "01234678", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 5, false], + }, + ], + }, + { + name: "Drop nth bytes: Complex single line (apply to each line)", + input: "0123456789", + expectedOutput: "01234678", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 5, true], + }, + ], + }, + { + name: "Drop nth bytes: Basic multi line", + input: "01234\n56789", + expectedOutput: "123\n5689", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Drop nth bytes: Basic multi line (apply to each line)", + input: "01234\n56789", + expectedOutput: "123\n678", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Drop nth bytes: Complex multi line", + input: "01234\n56789", + expectedOutput: "012345679", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 5, false], + }, + ], + }, + { + name: "Drop nth bytes: Complex multi line (apply to each line)", + input: "012345\n6789ab", + expectedOutput: "01234\n6789a", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 5, true], + }, + ], + } +]);