From 3a2fb20479178415cec01fce9aa256898ba3bd9f Mon Sep 17 00:00:00 2001 From: arjun2075 Date: Mon, 10 Aug 2026 08:04:46 -0700 Subject: [PATCH] fix: accept hexadecimal values for Disassemble x86 address arguments The 'Code Segment (CS)' and 'Offset (IP)' arguments were declared as 'number' ingredients, so they were parsed with parseFloat and rejected hexadecimal input such as '0xABC' with: Invalid ingredient value. Not a number: NaN However, SetBasePosition already parses both values with parseInt(x, 16) and GetPosition renders them back out with toString(16), so these fields have always been hexadecimal. The two conversions cancel out visually, which is why the mismatch went unnoticed. It is observable though: a code segment entered as '24' becomes 36 decimal internally, which crosses the documented 'CS >= 36 switches 32-bit output to SEG:OFFSET' threshold. Both arguments are now 'string' ingredients parsed by a dedicated helper that accepts bare ('ABC'), prefixed ('0xABC') and suffixed ('ABCh') hex, and rejects anything else with a named error instead of a NaN message. The code segment is left-padded to four digits because SetBasePosition reads it via slice(length - 4), which silently truncated shorter values ('ABC' was read as 'C'). Existing recipes are unaffected: the default is unchanged and numeric argument values from saved recipes are still handled. Fixes #2720 --- src/core/operations/DisassembleX86.mjs | 43 +++++++++-- tests/operations/tests/DisassembleX86.mjs | 89 +++++++++++++++++++++++ 2 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 tests/operations/tests/DisassembleX86.mjs 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], + }, + ], + }, +]);