Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions src/core/operations/DisassembleX86.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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, ""));
Expand Down
89 changes: 89 additions & 0 deletions tests/operations/tests/DisassembleX86.mjs
Original file line number Diff line number Diff line change
@@ -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],
},
],
},
]);