diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index de3ea8826a..2e38249903 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -308,6 +308,7 @@ "Expand alphabet range", "Drop bytes", "Take bytes", + "Insert bytes", "Pad lines", "Find / Replace", "Regular expression", diff --git a/src/core/operations/ChangeIPFormat.mjs b/src/core/operations/ChangeIPFormat.mjs index c9adc5d844..a7e72708cc 100644 --- a/src/core/operations/ChangeIPFormat.mjs +++ b/src/core/operations/ChangeIPFormat.mjs @@ -29,12 +29,12 @@ class ChangeIPFormat extends Operation { { "name": "Input format", "type": "option", - "value": ["Dotted Decimal", "Decimal", "Octal", "Hex"] + "value": ["Dotted Decimal", "Decimal", "Decimal (Little Endian)", "Octal", "Octal (Little Endian)", "Hex"] }, { "name": "Output format", "type": "option", - "value": ["Dotted Decimal", "Decimal", "Octal", "Hex"] + "value": ["Dotted Decimal", "Decimal", "Decimal (Little Endian)", "Octal", "Octal (Little Endian)", "Hex"] } ]; } @@ -71,9 +71,15 @@ class ChangeIPFormat extends Operation { case "Decimal": baIp = this.fromNumber(lines[i].toString(), 10); break; + case "Decimal (Little Endian)": + baIp = Utils.intToByteArray(parseInt(lines[i].toString(), 10), 4, "little"); + break; case "Octal": baIp = this.fromNumber(lines[i].toString(), 8); break; + case "Octal (Little Endian)": + baIp = Utils.intToByteArray(parseInt(lines[i].toString(), 8), 4, "little"); + break; case "Hex": baIp = fromHex(lines[i]); break; @@ -98,10 +104,18 @@ class ChangeIPFormat extends Operation { decIp = ((baIp[0] << 24) | (baIp[1] << 16) | (baIp[2] << 8) | baIp[3]) >>> 0; output += decIp.toString() + "\n"; break; + case "Decimal (Little Endian)": + decIp = Utils.byteArrayToInt(baIp, "little"); + output += decIp.toString() + "\n"; + break; case "Octal": decIp = ((baIp[0] << 24) | (baIp[1] << 16) | (baIp[2] << 8) | baIp[3]) >>> 0; output += "0" + decIp.toString(8) + "\n"; break; + case "Octal (Little Endian)": + decIp = Utils.byteArrayToInt(baIp, "little"); + output += "0" + decIp.toString(8) + "\n"; + break; case "Hex": hexIp = ""; for (j = 0; j < baIp.length; j++) { diff --git a/src/core/operations/InsertBytes.mjs b/src/core/operations/InsertBytes.mjs new file mode 100644 index 0000000000..ea07e9353a --- /dev/null +++ b/src/core/operations/InsertBytes.mjs @@ -0,0 +1,81 @@ +/** + * @author Didier Stevens [didier.stevens@gmail.com] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {BITWISE_OP_DELIMS} from "../lib/BitwiseOp.mjs"; +import Utils from "../Utils.mjs"; + +/** + * Insert bytes operation + */ +class InsertBytes extends Operation { + + /** + * InsertBytes constructor + */ + constructor() { + super(); + + this.name = "Insert bytes"; + this.module = "Default"; + this.description = "Insert bytes at arbitrary position. Options 'from end' and 'overwrite' available."; + this.infoURL = ""; + this.inputType = "byteArray"; + this.outputType = "byteArray"; + this.args = [ + { + "name": "Bytes", + "type": "toggleString", + "value": "", + "toggleValues": BITWISE_OP_DELIMS + }, + { + "name": "Start", + "type": "number", + "value": 0 + }, + { + "name": "From end", + "type": "boolean", + "value": false + }, + { + "name": "Overwrite", + "type": "boolean", + "value": false + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {byteArray} + */ + run(input, args) { + const value = Utils.convertToByteArray(args[0].string || "", args[0].option); + let start = args[1]; + const fromend = args[2]; + const overwrite = args[3]; + + if (start < 0) + throw new OperationError("Start must not be negative"); + if (start > input.length) + throw new OperationError("Start must not be bigger than input"); + if (fromend) + start = input.length - start; + const left = input.slice(0, start); + let right = input.slice(start); + if (overwrite) + right = right.slice(value.length); + + return left.concat(value, right); + } + +} + +export default InsertBytes; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index a82bc874c6..68fef73430 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -84,6 +84,7 @@ import "./tests/Hex.mjs"; import "./tests/Hexdump.mjs"; import "./tests/HKDF.mjs"; import "./tests/Image.mjs"; +import "./tests/InsertBytes.mjs"; import "./tests/IndexOfCoincidence.mjs"; import "./tests/JA3Fingerprint.mjs"; import "./tests/JA4.mjs"; diff --git a/tests/operations/tests/ChangeIPFormat.mjs b/tests/operations/tests/ChangeIPFormat.mjs index d92ffb7973..26c92124b1 100644 --- a/tests/operations/tests/ChangeIPFormat.mjs +++ b/tests/operations/tests/ChangeIPFormat.mjs @@ -48,5 +48,45 @@ TestRegister.addTests([ args: ["Octal", "Decimal"], }, ], + }, { + name: "Change IP format: Decimal (Little Endian) to Dotted Decimal", + input: "16885952", + expectedOutput: "192.168.1.1", + recipeConfig: [ + { + op: "Change IP format", + args: ["Decimal (Little Endian)", "Dotted Decimal"], + }, + ], + }, { + name: "Change IP format: Dotted Decimal to Decimal (Little Endian)", + input: "192.168.1.1", + expectedOutput: "16885952", + recipeConfig: [ + { + op: "Change IP format", + args: ["Dotted Decimal", "Decimal (Little Endian)"], + }, + ], + }, { + name: "Change IP format: Octal (Little Endian) to Dotted Decimal", + input: "0100324300", + expectedOutput: "192.168.1.1", + recipeConfig: [ + { + op: "Change IP format", + args: ["Octal (Little Endian)", "Dotted Decimal"], + }, + ], + }, { + name: "Change IP format: Dotted Decimal to Octal (Little Endian)", + input: "192.168.1.1", + expectedOutput: "0100324300", + recipeConfig: [ + { + op: "Change IP format", + args: ["Dotted Decimal", "Octal (Little Endian)"], + }, + ], }, ]); diff --git a/tests/operations/tests/InsertBytes.mjs b/tests/operations/tests/InsertBytes.mjs new file mode 100644 index 0000000000..8eca3973f2 --- /dev/null +++ b/tests/operations/tests/InsertBytes.mjs @@ -0,0 +1,78 @@ +/** + * InsertBytes test. + * + * @author Didier Stevens [didier.stevens@gmail.com] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "Insert bytes - test 1", + input: "This is a test", + expectedOutput: "This is a test", + recipeConfig: [ + { + op: "Insert bytes", + args: [{"string": "", "option": "Hex"}, 0, false, false], + }, + ], + }, + { + name: "Insert bytes - test 2", + input: "This is a test", + expectedOutput: "AThis is a test", + recipeConfig: [ + { + op: "Insert bytes", + args: [{"string": "41", "option": "Hex"}, 0, false, false], + }, + ], + }, + { + name: "Insert bytes - test 3", + input: "This is a test", + expectedOutput: "This is a testA", + recipeConfig: [ + { + op: "Insert bytes", + args: [{"string": "41", "option": "Hex"}, 0, true, false], + }, + ], + }, + { + name: "Insert bytes - test 4", + input: "This is a test", + expectedOutput: "Ahis is a test", + recipeConfig: [ + { + op: "Insert bytes", + args: [{"string": "41", "option": "Hex"}, 0, false, true], + }, + ], + }, + { + name: "Insert bytes - test 5", + input: "This is a test", + expectedOutput: "This is a tesA", + recipeConfig: [ + { + op: "Insert bytes", + args: [{"string": "41", "option": "Hex"}, 1, true, true], + }, + ], + }, + { + name: "Insert bytes - test 6", + input: "This is a test", + expectedOutput: "This is not a test", + recipeConfig: [ + { + op: "Insert bytes", + args: [{"string": "not ", "option": "Latin1"}, 8, false, false], + }, + ], + }, + +]);