feat: add arm disassembler
This commit is contained in:
parent
2a1294f1c0
commit
2198a8d3d3
6
package-lock.json
generated
6
package-lock.json
generated
@ -10,6 +10,7 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@alexaltea/capstone-js": "^3.0.5",
|
||||||
"@astronautlabs/amf": "^0.0.6",
|
"@astronautlabs/amf": "^0.0.6",
|
||||||
"@babel/polyfill": "^7.12.1",
|
"@babel/polyfill": "^7.12.1",
|
||||||
"@blu3r4y/lzma": "^2.3.3",
|
"@blu3r4y/lzma": "^2.3.3",
|
||||||
@ -161,6 +162,11 @@
|
|||||||
"worker-loader": "^3.0.8"
|
"worker-loader": "^3.0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@alexaltea/capstone-js": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@alexaltea/capstone-js/-/capstone-js-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-HWa4d5vblYc3OEJ9MpcXFo0gV/oDLTI5iH7ng80Gs3/Wo3lcYvB14gDDwSr9So1F+fuwIET8meo6TxTezEyqTg=="
|
||||||
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||||
|
|||||||
@ -96,6 +96,7 @@
|
|||||||
"worker-loader": "^3.0.8"
|
"worker-loader": "^3.0.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@alexaltea/capstone-js": "^3.0.5",
|
||||||
"@astronautlabs/amf": "^0.0.6",
|
"@astronautlabs/amf": "^0.0.6",
|
||||||
"@babel/polyfill": "^7.12.1",
|
"@babel/polyfill": "^7.12.1",
|
||||||
"@blu3r4y/lzma": "^2.3.3",
|
"@blu3r4y/lzma": "^2.3.3",
|
||||||
|
|||||||
@ -548,6 +548,7 @@
|
|||||||
"Chi Square",
|
"Chi Square",
|
||||||
"P-list Viewer",
|
"P-list Viewer",
|
||||||
"Disassemble x86",
|
"Disassemble x86",
|
||||||
|
"Disassemble ARM",
|
||||||
"Pseudo-Random Number Generator",
|
"Pseudo-Random Number Generator",
|
||||||
"Generate De Bruijn Sequence",
|
"Generate De Bruijn Sequence",
|
||||||
"Generate UUID",
|
"Generate UUID",
|
||||||
|
|||||||
195
src/core/operations/DisassembleARM.mjs
Normal file
195
src/core/operations/DisassembleARM.mjs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/**
|
||||||
|
* @author MedjedThomasXM
|
||||||
|
* @copyright Crown Copyright 2024
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import OperationError from "../errors/OperationError.mjs";
|
||||||
|
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disassemble ARM operation
|
||||||
|
*/
|
||||||
|
class DisassembleARM extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DisassembleARM constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Disassemble ARM";
|
||||||
|
this.module = "Shellcode";
|
||||||
|
this.description = "Disassembles ARM machine code into assembly language.<br><br>Supports ARM (32-bit), Thumb, and ARM64 (AArch64) architectures using the Capstone disassembly framework.<br><br>Input should be in hexadecimal.";
|
||||||
|
this.infoURL = "https://wikipedia.org/wiki/ARM_architecture_family";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
"name": "Architecture",
|
||||||
|
"type": "option",
|
||||||
|
"value": ["ARM (32-bit)", "ARM64 (AArch64)"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mode",
|
||||||
|
"type": "option",
|
||||||
|
"value": ["ARM", "Thumb", "Thumb + Cortex-M", "ARMv8"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Endianness",
|
||||||
|
"type": "option",
|
||||||
|
"value": ["Little Endian", "Big Endian"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Starting address (hex)",
|
||||||
|
"type": "number",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Show instruction hex",
|
||||||
|
"type": "boolean",
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Show instruction position",
|
||||||
|
"type": "boolean",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
async run(input, args) {
|
||||||
|
const [
|
||||||
|
architecture,
|
||||||
|
mode,
|
||||||
|
endianness,
|
||||||
|
startAddress,
|
||||||
|
showHex,
|
||||||
|
showPosition
|
||||||
|
] = args;
|
||||||
|
|
||||||
|
// Remove whitespace from input
|
||||||
|
const hexInput = input.replace(/\s/g, "");
|
||||||
|
|
||||||
|
// Validate hex input
|
||||||
|
if (!/^[0-9a-fA-F]*$/.test(hexInput)) {
|
||||||
|
throw new OperationError("Invalid hexadecimal input. Please provide valid hex characters only.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hexInput.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hexInput.length % 2 !== 0) {
|
||||||
|
throw new OperationError("Invalid hexadecimal input. Length must be even.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert hex string to byte array
|
||||||
|
const bytes = [];
|
||||||
|
for (let i = 0; i < hexInput.length; i += 2) {
|
||||||
|
bytes.push(parseInt(hexInput.substr(i, 2), 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWorkerEnvironment()) {
|
||||||
|
self.sendStatusMessage("Loading Capstone disassembler...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamically import capstone to avoid loading the large library until needed
|
||||||
|
const cs = (await import("@alexaltea/capstone-js/dist/capstone.min.js")).default;
|
||||||
|
|
||||||
|
// Determine architecture constant
|
||||||
|
let arch;
|
||||||
|
if (architecture === "ARM64 (AArch64)") {
|
||||||
|
arch = cs.ARCH_ARM64;
|
||||||
|
} else {
|
||||||
|
arch = cs.ARCH_ARM;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine mode constant
|
||||||
|
let modeValue = cs.MODE_LITTLE_ENDIAN;
|
||||||
|
|
||||||
|
if (architecture === "ARM (32-bit)") {
|
||||||
|
switch (mode) {
|
||||||
|
case "ARM":
|
||||||
|
modeValue = cs.MODE_ARM;
|
||||||
|
break;
|
||||||
|
case "Thumb":
|
||||||
|
modeValue = cs.MODE_THUMB;
|
||||||
|
break;
|
||||||
|
case "Thumb + Cortex-M":
|
||||||
|
modeValue = cs.MODE_THUMB | cs.MODE_MCLASS;
|
||||||
|
break;
|
||||||
|
case "ARMv8":
|
||||||
|
modeValue = cs.MODE_ARM | cs.MODE_V8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
modeValue = cs.MODE_ARM;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ARM64 only has one mode (ARM mode is default for ARM64)
|
||||||
|
modeValue = cs.MODE_ARM;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add endianness
|
||||||
|
if (endianness === "Big Endian") {
|
||||||
|
modeValue |= cs.MODE_BIG_ENDIAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWorkerEnvironment()) {
|
||||||
|
self.sendStatusMessage("Disassembling...");
|
||||||
|
}
|
||||||
|
|
||||||
|
let disassembler;
|
||||||
|
try {
|
||||||
|
disassembler = new cs.Capstone(arch, modeValue);
|
||||||
|
} catch (e) {
|
||||||
|
throw new OperationError(`Failed to initialise Capstone disassembler: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let instructions;
|
||||||
|
try {
|
||||||
|
instructions = disassembler.disasm(bytes, startAddress);
|
||||||
|
} catch (e) {
|
||||||
|
disassembler.close();
|
||||||
|
throw new OperationError(`Disassembly failed: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format output
|
||||||
|
const output = [];
|
||||||
|
for (const insn of instructions) {
|
||||||
|
let line = "";
|
||||||
|
|
||||||
|
if (showPosition) {
|
||||||
|
// Format address as hex with 0x prefix
|
||||||
|
const addrHex = "0x" + insn.address.toString(16).padStart(8, "0");
|
||||||
|
line += addrHex + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showHex) {
|
||||||
|
// Format instruction bytes as hex
|
||||||
|
const bytesHex = insn.bytes.map(b => b.toString(16).padStart(2, "0")).join("");
|
||||||
|
line += bytesHex.padEnd(16, " ") + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
line += insn.mnemonic;
|
||||||
|
if (insn.op_str) {
|
||||||
|
line += " " + insn.op_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
disassembler.close();
|
||||||
|
|
||||||
|
return output.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DisassembleARM;
|
||||||
@ -60,6 +60,7 @@ import "./tests/Crypt.mjs";
|
|||||||
import "./tests/CSV.mjs";
|
import "./tests/CSV.mjs";
|
||||||
import "./tests/DateTime.mjs";
|
import "./tests/DateTime.mjs";
|
||||||
import "./tests/DefangIP.mjs";
|
import "./tests/DefangIP.mjs";
|
||||||
|
import "./tests/DisassembleARM.mjs";
|
||||||
import "./tests/DropNthBytes.mjs";
|
import "./tests/DropNthBytes.mjs";
|
||||||
import "./tests/ECDSA.mjs";
|
import "./tests/ECDSA.mjs";
|
||||||
import "./tests/ELFInfo.mjs";
|
import "./tests/ELFInfo.mjs";
|
||||||
|
|||||||
131
tests/operations/tests/DisassembleARM.mjs
Normal file
131
tests/operations/tests/DisassembleARM.mjs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* Disassemble ARM tests.
|
||||||
|
* @author MedjedThomasXM
|
||||||
|
* @copyright Crown Copyright 2024
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: ARM32 basic - MOV instruction",
|
||||||
|
input: "0000a0e3",
|
||||||
|
expectedOutput: "0x00000000 0000a0e3 mov r0, #0",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "ARM", "Little Endian", 0, true, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: ARM32 - Multiple instructions",
|
||||||
|
input: "04e02de5 00d04be2",
|
||||||
|
expectedOutput: "0x00000000 04e02de5 str lr, [sp, #-4]!\n0x00000004 00d04be2 sub sp, fp, #0",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "ARM", "Little Endian", 0, true, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: ARM32 Thumb mode",
|
||||||
|
input: "80b500af",
|
||||||
|
expectedOutput: "0x00000000 80b5 push {r7, lr}\n0x00000002 00af add r7, sp, #0",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "Thumb", "Little Endian", 0, true, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: ARM64 basic - MOV instruction",
|
||||||
|
input: "e0031faa",
|
||||||
|
expectedOutput: "0x00000000 e0031faa mov x0, xzr",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM64 (AArch64)", "ARM", "Little Endian", 0, true, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: ARM64 - Multiple instructions",
|
||||||
|
input: "fd7bbfa9 fd030091",
|
||||||
|
expectedOutput: "0x00000000 fd7bbfa9 stp x29, x30, [sp, #-0x10]!\n0x00000004 fd030091 mov x29, sp",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM64 (AArch64)", "ARM", "Little Endian", 0, true, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: Custom starting address",
|
||||||
|
input: "0000a0e3",
|
||||||
|
expectedOutput: "0x00001000 0000a0e3 mov r0, #0",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "ARM", "Little Endian", 4096, true, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: Hide instruction hex",
|
||||||
|
input: "0000a0e3",
|
||||||
|
expectedOutput: "0x00000000 mov r0, #0",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "ARM", "Little Endian", 0, false, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: Hide instruction position",
|
||||||
|
input: "0000a0e3",
|
||||||
|
expectedOutput: "0000a0e3 mov r0, #0",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "ARM", "Little Endian", 0, true, false]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: Hide both hex and position",
|
||||||
|
input: "0000a0e3",
|
||||||
|
expectedOutput: "mov r0, #0",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "ARM", "Little Endian", 0, false, false]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: Empty input",
|
||||||
|
input: "",
|
||||||
|
expectedOutput: "",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "ARM", "Little Endian", 0, true, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Disassemble ARM: Input with whitespace",
|
||||||
|
input: "00 00 a0 e3",
|
||||||
|
expectedOutput: "0x00000000 0000a0e3 mov r0, #0",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Disassemble ARM",
|
||||||
|
"args": ["ARM (32-bit)", "ARM", "Little Endian", 0, true, true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
Loading…
x
Reference in New Issue
Block a user