diff --git a/src/core/operations/DisassembleX86.mjs b/src/core/operations/DisassembleX86.mjs index bdaf348aa1..1705967f41 100644 --- a/src/core/operations/DisassembleX86.mjs +++ b/src/core/operations/DisassembleX86.mjs @@ -8,6 +8,34 @@ import Operation from "../Operation.mjs"; import * as disassemble from "../vendor/DisassembleX86-64.mjs"; import OperationError from "../errors/OperationError.mjs"; +/** + * Parses a hexadecimal address value used by the disassembler. + * + * The underlying disassembler interprets both the code segment and the offset as + * hexadecimal, and renders them back as hexadecimal, so these arguments accept the + * common ways of writing a hex literal: bare (`ABC`), prefixed (`0xABC`) or suffixed + * (`ABCh`). + * + * @param {string} value + * @param {string} name - Argument name, used in the error message. + * @param {number} [padTo=0] - Left-pad the result with zeroes to this many digits. + * @returns {string} The bare hex digits, ready to be passed to SetBasePosition. + * + * @throws {OperationError} if the value is not a valid hexadecimal number. + */ +function parseHexAddress(value, name, padTo = 0) { + const trimmed = value.toString().trim(); + const hex = trimmed.replace(/^0x/i, "").replace(/h$/i, ""); + + if (hex === "" || !/^[\da-f]+$/i.test(hex)) { + throw new OperationError(`Invalid ${name}: '${trimmed}' is not a hexadecimal number.`); + } + + // SetBasePosition reads the code segment from the last four characters it is given, + // so shorter values must be padded to avoid losing their leading digits. + return hex.padStart(padTo, "0"); +} + /** * Disassemble x86 operation */ @@ -46,13 +74,15 @@ class DisassembleX86 extends Operation { }, { "name": "Code Segment (CS)", - "type": "number", - "value": 16 + "type": "string", + "value": "16", + "hint": "Hexadecimal, e.g. 16, 0xABC or ABCh" }, { "name": "Offset (IP)", - "type": "number", - "value": 0 + "type": "string", + "value": "0", + "hint": "Hexadecimal, e.g. 0, 0x1000 or 1000h" }, { "name": "Show instruction hex", @@ -122,7 +152,10 @@ class DisassembleX86 extends Operation { break; } - disassemble.SetBasePosition(codeSegment + ":" + offset); + disassemble.SetBasePosition( + parseHexAddress(codeSegment, "Code Segment (CS)", 4) + ":" + + parseHexAddress(offset, "Offset (IP)") + ); disassemble.setShowInstructionHex(showInstructionHex); disassemble.setShowInstructionPos(showInstructionPos); disassemble.LoadBinCode(input.replace(/\s/g, "")); diff --git a/tests/operations/tests/DisassembleX86.mjs b/tests/operations/tests/DisassembleX86.mjs new file mode 100644 index 0000000000..08414fa7de --- /dev/null +++ b/tests/operations/tests/DisassembleX86.mjs @@ -0,0 +1,89 @@ +/** + * Disassemble x86 tests. + * + * @author arjun2075 + * + * @copyright Crown Copyright 2026 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "Disassemble x86: default code segment is unchanged", + input: "90", + expectedMatch: /0016:0000\s+NOP/, + recipeConfig: [ + { + op: "Disassemble x86", + args: ["16", "Full x86 architecture", "16", "0", false, true], + }, + ], + }, + { + name: "Disassemble x86: hex code segment with 0x prefix", + input: "90", + expectedMatch: /0ABC:0000\s+NOP/, + recipeConfig: [ + { + op: "Disassemble x86", + args: ["16", "Full x86 architecture", "0xABC", "0", false, true], + }, + ], + }, + { + name: "Disassemble x86: hex code segment with h suffix", + input: "90", + expectedMatch: /0ABC:0000\s+NOP/, + recipeConfig: [ + { + op: "Disassemble x86", + args: ["16", "Full x86 architecture", "ABCh", "0", false, true], + }, + ], + }, + { + name: "Disassemble x86: bare hex code segment", + input: "90", + expectedMatch: /0ABC:0000\s+NOP/, + recipeConfig: [ + { + op: "Disassemble x86", + args: ["16", "Full x86 architecture", "ABC", "0", false, true], + }, + ], + }, + { + name: "Disassemble x86: hex offset with 0x prefix", + input: "90", + expectedMatch: /0016:1000\s+NOP/, + recipeConfig: [ + { + op: "Disassemble x86", + args: ["16", "Full x86 architecture", "16", "0x1000", false, true], + }, + ], + }, + { + name: "Disassemble x86: invalid code segment is rejected", + input: "90", + expectedOutput: "Invalid Code Segment (CS): 'wibble' is not a hexadecimal number.", + recipeConfig: [ + { + op: "Disassemble x86", + args: ["16", "Full x86 architecture", "wibble", "0", false, true], + }, + ], + }, + { + name: "Disassemble x86: invalid offset is rejected", + input: "90", + expectedOutput: "Invalid Offset (IP): 'wibble' is not a hexadecimal number.", + recipeConfig: [ + { + op: "Disassemble x86", + args: ["16", "Full x86 architecture", "16", "wibble", false, true], + }, + ], + }, +]);