Upgrade to Capstone V6 (#2766)

* Upgrade to Capstone V6

* Fix final bugs related to changes in metadata/shift encoding in Capstone v6. All tests pass

* rebase prep

* Update to Capstone v6 alpha 4

* lint

* Apply patch to fix nixos packaging
pull/2876/head^2
OBarronCS 8 months ago committed by GitHub
parent d815c06ff9
commit d29f8a74d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -162,37 +162,10 @@ let
}:
prev.capstone.overrideAttrs (
old:
lib.optionalAttrs ((isBuildSource old) && stdenv.hostPlatform.isDarwin) {
lib.optionalAttrs (isBuildSource old) {
nativeBuildInputs = old.nativeBuildInputs ++ [
cmake
fixDarwinDylibNames
];
preBuild = ''
sed -i 's/^IS_APPLE := .*$/IS_APPLE := 1/' ./src/Makefile
substituteInPlace ./setup.py \
--replace-fail "import sys" "import sys; sys.argv.extend(('--plat-name', 'any'))" || true
'';
# See: https://github.com/capstone-engine/capstone/issues/2621
postPatch = (
let
gitSrc = fetchFromGitHub {
owner = "capstone-engine";
repo = "capstone";
rev = old.version;
hash = "sha256-VGqqrixg7LaqRWTAEBzpC+gUTchncz3Oa2pSq8GLskI=";
};
in
''
cp ${gitSrc}/capstone.pc.in src/
cp ${gitSrc}/capstone-config.cmake.in src/
cp ${gitSrc}/cmake_uninstall.cmake.in src/
cp ${gitSrc}/CPackConfig.txt src/
cp ${gitSrc}/CPackConfig.cmake src/
''
);
}
)
) { };

@ -37,7 +37,7 @@ if pwndbg.dbg.is_gdblib_available():
CapstoneArch = {
"arm": CS_ARCH_ARM,
"armcm": CS_ARCH_ARM,
"aarch64": CS_ARCH_ARM64,
"aarch64": CS_ARCH_AARCH64,
"i386": CS_ARCH_X86,
"i8086": CS_ARCH_X86,
"x86-64": CS_ARCH_X86,

@ -5,7 +5,7 @@ from typing import Callable
from typing import Dict
from capstone import * # noqa: F403
from capstone.arm64 import * # noqa: F403
from capstone.aarch64 import * # noqa: F403
from typing_extensions import override
import pwndbg.aglib.arch
@ -28,105 +28,126 @@ if TYPE_CHECKING:
# Negative size indicates signed read
# None indicates the read size depends on the target register
AARCH64_SINGLE_LOAD_INSTRUCTIONS: Dict[int, int | None] = {
ARM64_INS_LDRB: 1,
ARM64_INS_LDURB: 1,
ARM64_INS_LDRSB: -1,
ARM64_INS_LDURSB: -1,
ARM64_INS_LDRH: 2,
ARM64_INS_LDURH: 2,
ARM64_INS_LDRSH: -2,
ARM64_INS_LDURSH: -2,
ARM64_INS_LDURSW: -4,
ARM64_INS_LDRSW: -4,
ARM64_INS_LDUR: None,
ARM64_INS_LDR: None,
ARM64_INS_LDTRB: 1,
ARM64_INS_LDTRSB: -1,
ARM64_INS_LDTRH: 2,
ARM64_INS_LDTRSH: -2,
ARM64_INS_LDTRSW: -4,
ARM64_INS_LDTR: None,
ARM64_INS_LDXRB: 1,
ARM64_INS_LDXRH: 2,
ARM64_INS_LDXR: None,
ARM64_INS_LDARB: 1,
ARM64_INS_LDARH: 2,
ARM64_INS_LDAR: None,
AARCH64_INS_LDRB: 1,
AARCH64_INS_ALIAS_LDRB: 1,
AARCH64_INS_LDURB: 1,
AARCH64_INS_ALIAS_LDURB: 1,
AARCH64_INS_LDRSB: -1,
AARCH64_INS_ALIAS_LDRSB: -1,
AARCH64_INS_LDURSB: -1,
AARCH64_INS_ALIAS_LDURSB: -1,
AARCH64_INS_LDRH: 2,
AARCH64_INS_ALIAS_LDRH: 2,
AARCH64_INS_LDURH: 2,
AARCH64_INS_ALIAS_LDURH: 2,
AARCH64_INS_LDRSH: -2,
AARCH64_INS_ALIAS_LDRSH: -2,
AARCH64_INS_LDURSH: -2,
AARCH64_INS_ALIAS_LDURSH: -2,
AARCH64_INS_LDURSW: -4,
AARCH64_INS_ALIAS_LDURSW: -4,
AARCH64_INS_LDRSW: -4,
AARCH64_INS_ALIAS_LDRSW: -4,
AARCH64_INS_LDUR: None,
AARCH64_INS_ALIAS_LDUR: None,
AARCH64_INS_LDR: None,
AARCH64_INS_ALIAS_LDR: None,
AARCH64_INS_LDTRB: 1,
AARCH64_INS_LDTRSB: -1,
AARCH64_INS_LDTRH: 2,
AARCH64_INS_LDTRSH: -2,
AARCH64_INS_LDTRSW: -4,
AARCH64_INS_LDTR: None,
AARCH64_INS_ALIAS_LDTR: None,
AARCH64_INS_LDXRB: 1,
AARCH64_INS_LDXRH: 2,
AARCH64_INS_LDXR: None,
AARCH64_INS_LDARB: 1,
AARCH64_INS_LDARH: 2,
AARCH64_INS_LDAR: None,
}
# None indicates that the write size depends on the source register
AARCH64_SINGLE_STORE_INSTRUCTIONS: Dict[int, int | None] = {
ARM64_INS_STRB: 1,
ARM64_INS_STURB: 1,
ARM64_INS_STRH: 2,
ARM64_INS_STURH: 2,
ARM64_INS_STUR: None,
ARM64_INS_STR: None,
AARCH64_INS_STRB: 1,
AARCH64_INS_ALIAS_STRB: 1,
AARCH64_INS_STURB: 1,
AARCH64_INS_ALIAS_STURB: 1,
AARCH64_INS_STRH: 2,
AARCH64_INS_ALIAS_STRH: 2,
AARCH64_INS_STURH: 2,
AARCH64_INS_ALIAS_STURH: 2,
AARCH64_INS_STUR: None,
AARCH64_INS_ALIAS_STUR: None,
AARCH64_INS_STR: None,
AARCH64_INS_ALIAS_STR: None,
# Store Register (unprivileged)
ARM64_INS_STTRB: 1,
ARM64_INS_STTRH: 2,
ARM64_INS_STTR: None,
AARCH64_INS_STTRB: 1,
AARCH64_INS_STTRH: 2,
AARCH64_INS_STTR: None,
# Store-Release
ARM64_INS_STLRB: 1,
ARM64_INS_STLRH: 2,
ARM64_INS_STLR: None,
AARCH64_INS_STLRB: 1,
AARCH64_INS_STLRH: 2,
AARCH64_INS_STLR: None,
}
# The first operand of these instructions gets the status result of the operation
AARCH64_EXCLUSIVE_STORE_INSTRUCTIONS = {
# Store Exclusive
ARM64_INS_STXRB: 1,
ARM64_INS_STXRH: 2,
ARM64_INS_STXR: None,
AARCH64_INS_STXRB: 1,
AARCH64_INS_STXRH: 2,
AARCH64_INS_STXR: None,
# Store-Release Exclusive
ARM64_INS_STLXRB: 1,
ARM64_INS_STLXRH: 2,
ARM64_INS_STLXR: None,
AARCH64_INS_STLXRB: 1,
AARCH64_INS_STLXRH: 2,
AARCH64_INS_STLXR: None,
}
CONDITIONAL_SELECT_INSTRUCTIONS = {
ARM64_INS_CSEL,
ARM64_INS_CSINC,
ARM64_INS_CSINV,
ARM64_INS_CSNEG,
ARM64_INS_CSET,
ARM64_INS_CSETM,
ARM64_INS_CINC,
ARM64_INS_CINV,
ARM64_INS_CNEG,
AARCH64_INS_CSEL,
AARCH64_INS_CSINC,
AARCH64_INS_CSINV,
AARCH64_INS_CSNEG,
AARCH64_INS_ALIAS_CSET,
AARCH64_INS_ALIAS_CSETM,
AARCH64_INS_ALIAS_CINC,
AARCH64_INS_ALIAS_CINV,
AARCH64_INS_ALIAS_CNEG,
}
AARCH64_EMULATED_ANNOTATIONS = CONDITIONAL_SELECT_INSTRUCTIONS | {
ARM64_INS_SXTB,
ARM64_INS_SXTH,
ARM64_INS_SXTW,
ARM64_INS_UXTB,
ARM64_INS_UXTH,
ARM64_INS_UXTW,
ARM64_INS_RBIT,
ARM64_INS_CLS,
ARM64_INS_CLZ,
ARM64_INS_BFXIL,
ARM64_INS_UBFIZ,
ARM64_INS_UBFM,
ARM64_INS_UBFX,
ARM64_INS_SBFIZ,
ARM64_INS_SBFM,
ARM64_INS_SBFX,
ARM64_INS_BFI,
ARM64_INS_NEG,
ARM64_INS_NEGS,
ARM64_INS_REV,
ARM64_INS_BIC,
ARM64_INS_BICS,
AARCH64_INS_SXTB,
AARCH64_INS_SXTH,
AARCH64_INS_SXTW,
AARCH64_INS_UXTB,
AARCH64_INS_UXTH,
AARCH64_INS_UXTW,
AARCH64_INS_RBIT,
AARCH64_INS_CLS,
AARCH64_INS_CLZ,
AARCH64_INS_ALIAS_BFXIL,
AARCH64_INS_ALIAS_UBFIZ,
AARCH64_INS_UBFM,
AARCH64_INS_ALIAS_UBFX,
AARCH64_INS_ALIAS_SBFIZ,
AARCH64_INS_SBFM,
AARCH64_INS_ALIAS_SBFX,
AARCH64_INS_ALIAS_BFI,
AARCH64_INS_NEG,
AARCH64_INS_ALIAS_NEGS,
AARCH64_INS_REV,
AARCH64_INS_BIC,
AARCH64_INS_BICS,
}
AARCH64_CONSTANT_SHIFTS = {AARCH64_SFT_LSL, AARCH64_SFT_LSR, AARCH64_SFT_ASR, AARCH64_SFT_ROR}
# Parameters to each function: (value, shift_amt, bit_width)
AARCH64_BIT_SHIFT_MAP: Dict[int, Callable[[int, int, int], int]] = {
ARM64_SFT_LSL: bit_math.logical_shift_left,
ARM64_SFT_LSR: bit_math.logical_shift_right,
ARM64_SFT_ASR: bit_math.arithmetic_shift_right,
ARM64_SFT_ROR: bit_math.rotate_right,
AARCH64_SFT_LSL: bit_math.logical_shift_left,
AARCH64_SFT_LSR: bit_math.logical_shift_right,
AARCH64_SFT_ASR: bit_math.arithmetic_shift_right,
AARCH64_SFT_ROR: bit_math.rotate_right,
}
@ -134,38 +155,54 @@ AARCH64_BIT_SHIFT_MAP: Dict[int, Callable[[int, int, int], int]] = {
# They take in a number, extract a byte, halfword, or word,
# and perform a zero- or sign-extend operation.
AARCH64_EXTEND_MAP: Dict[int, Callable[[int], int]] = {
ARM64_EXT_UXTB: lambda x: x & ((1 << 8) - 1),
ARM64_EXT_UXTH: lambda x: x & ((1 << 16) - 1),
ARM64_EXT_UXTW: lambda x: x & ((1 << 32) - 1),
ARM64_EXT_UXTX: lambda x: x, # UXTX has no effect. It extracts 64-bits from a 64-bit register.
ARM64_EXT_SXTB: lambda x: bit_math.to_signed(x, 8),
ARM64_EXT_SXTH: lambda x: bit_math.to_signed(x, 16),
ARM64_EXT_SXTW: lambda x: bit_math.to_signed(x, 32),
ARM64_EXT_SXTX: lambda x: bit_math.to_signed(x, 64),
AARCH64_EXT_UXTB: lambda x: x & ((1 << 8) - 1),
AARCH64_EXT_UXTH: lambda x: x & ((1 << 16) - 1),
AARCH64_EXT_UXTW: lambda x: x & ((1 << 32) - 1),
AARCH64_EXT_UXTX: lambda x: x, # UXTX has no effect. It extracts 64-bits from a 64-bit register.
AARCH64_EXT_SXTB: lambda x: bit_math.to_signed(x, 8),
AARCH64_EXT_SXTH: lambda x: bit_math.to_signed(x, 16),
AARCH64_EXT_SXTW: lambda x: bit_math.to_signed(x, 32),
AARCH64_EXT_SXTX: lambda x: bit_math.to_signed(x, 64),
}
AARCH64_MATH_INSTRUCTIONS = {
ARM64_INS_ADD: "+",
ARM64_INS_ADDS: "+",
ARM64_INS_SUB: "-",
ARM64_INS_SUBS: "-",
ARM64_INS_AND: "&",
ARM64_INS_ANDS: "&",
ARM64_INS_ORR: "&",
ARM64_INS_ASR: ">>s",
ARM64_INS_ASRV: ">>s",
ARM64_INS_EOR: "^",
ARM64_INS_LSL: "<<",
ARM64_INS_LSLV: "<<",
ARM64_INS_LSR: ">>",
ARM64_INS_LSRV: ">>",
ARM64_INS_UDIV: "/",
ARM64_INS_SDIV: "/",
ARM64_INS_SMULH: "*",
ARM64_INS_SMULL: "*",
ARM64_INS_UMULH: "*",
ARM64_INS_UMULL: "*",
ARM64_INS_MUL: "*",
AARCH64_INS_ADD: "+",
AARCH64_INS_ALIAS_ADD: "+",
AARCH64_INS_ADDS: "+",
AARCH64_INS_ALIAS_ADDS: "+",
AARCH64_INS_SUB: "-",
AARCH64_INS_ALIAS_SUB: "-",
AARCH64_INS_SUBS: "-",
AARCH64_INS_ALIAS_SUBS: "-",
AARCH64_INS_AND: "&",
AARCH64_INS_ALIAS_AND: "&",
AARCH64_INS_ANDS: "&",
AARCH64_INS_ALIAS_ANDS: "&",
AARCH64_INS_ORR: "|",
AARCH64_INS_ALIAS_ORR: "|",
AARCH64_INS_EOR: "^",
AARCH64_INS_ALIAS_EOR: "^",
AARCH64_INS_UDIV: "/",
AARCH64_INS_SDIV: "/",
AARCH64_INS_SMULH: "*",
AARCH64_INS_SMULL: "*",
AARCH64_INS_ALIAS_SMULL: "*",
AARCH64_INS_UMULH: "*",
AARCH64_INS_UMULL: "*",
AARCH64_INS_ALIAS_UMULL: "*",
AARCH64_INS_MUL: "*",
AARCH64_INS_ALIAS_MUL: "*",
}
AARCH64_SHIFT_INSTRUCTIONS = {
AARCH64_INS_LSL: "<<",
AARCH64_INS_ALIAS_LSL: "<<",
AARCH64_INS_LSR: ">>",
AARCH64_INS_ALIAS_LSR: ">>",
AARCH64_INS_ASR: ">>s",
AARCH64_INS_ALIAS_ASR: ">>s",
AARCH64_INS_ROR: ">>r",
AARCH64_INS_ALIAS_ROR: ">>r",
}
@ -182,23 +219,23 @@ def resolve_condition(condition: int, cpsr: int) -> InstructionCondition:
v = (cpsr >> 28) & 1
condition = {
ARM64_CC_INVALID: True, # Capstone uses this code for the 'B' instruction, the unconditional branch
ARM64_CC_EQ: z == 1,
ARM64_CC_NE: z == 0,
ARM64_CC_HS: c == 1,
ARM64_CC_LO: c == 0,
ARM64_CC_MI: n == 1,
ARM64_CC_PL: n == 0,
ARM64_CC_VS: v == 1,
ARM64_CC_VC: v == 0,
ARM64_CC_HI: c == 1 and z == 0,
ARM64_CC_LS: not (c == 1 and z == 0),
ARM64_CC_GE: n == v,
ARM64_CC_LT: n != v,
ARM64_CC_GT: z == 0 and n == v,
ARM64_CC_LE: not (z == 0 and n == v),
ARM64_CC_AL: True,
ARM64_CC_NV: True,
AArch64CC_EQ: z == 1,
AArch64CC_NE: z == 0,
AArch64CC_HS: c == 1,
AArch64CC_LO: c == 0,
AArch64CC_MI: n == 1,
AArch64CC_PL: n == 0,
AArch64CC_VS: v == 1,
AArch64CC_VC: v == 0,
AArch64CC_HI: c == 1 and z == 0,
AArch64CC_LS: not (c == 1 and z == 0),
AArch64CC_GE: n == v,
AArch64CC_LT: n != v,
AArch64CC_GT: z == 0 and n == v,
AArch64CC_LE: not (z == 0 and n == v),
AArch64CC_AL: True,
AArch64CC_NV: True,
AArch64CC_Invalid: True,
}.get(condition, False)
return InstructionCondition.TRUE if condition else InstructionCondition.FALSE
@ -210,23 +247,26 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
self.annotation_handlers: Dict[int, Callable[[PwndbgInstruction, Emulator], None]] = {
# MOV
ARM64_INS_MOV: self._common_move_annotator,
AARCH64_INS_MOV: self._common_move_annotator,
AARCH64_INS_ALIAS_MOV: self._common_move_annotator,
# MOVZ
AARCH64_INS_MOVZ: self._common_move_annotator,
# MOV WITH KEEP
ARM64_INS_MOVK: self._common_generic_register_destination,
AARCH64_INS_MOVK: self._common_generic_register_destination,
# ADR
ARM64_INS_ADR: self._common_generic_register_destination,
AARCH64_INS_ADR: self._common_generic_register_destination,
# ADRP
ARM64_INS_ADRP: self._handle_adrp,
AARCH64_INS_ADRP: self._handle_adrp,
# CMP
ARM64_INS_CMP: self._common_cmp_annotator_builder("cpsr", "-"),
AARCH64_INS_ALIAS_CMP: self._common_cmp_annotator_builder("cpsr", "-"),
# CMN
ARM64_INS_CMN: self._common_cmp_annotator_builder("cpsr", "+"),
AARCH64_INS_ALIAS_CMN: self._common_cmp_annotator_builder("cpsr", "+"),
# TST (bitwise "and")
ARM64_INS_TST: self._common_cmp_annotator_builder("cpsr", "&"),
AARCH64_INS_ALIAS_TST: self._common_cmp_annotator_builder("cpsr", "&"),
# CCMP (conditional compare)
ARM64_INS_CCMP: self._common_cmp_annotator_builder("cpsr", ""),
AARCH64_INS_CCMP: self._common_cmp_annotator_builder("cpsr", ""),
# CCMN
ARM64_INS_CCMN: self._common_cmp_annotator_builder("cpsr", ""),
AARCH64_INS_CCMN: self._common_cmp_annotator_builder("cpsr", ""),
}
@override
@ -273,6 +313,20 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
instruction.operands[-1].before_value,
AARCH64_MATH_INSTRUCTIONS[instruction.id],
)
elif instruction.id in AARCH64_SHIFT_INSTRUCTIONS:
# AArch64 encoding of shifts forces special attention: https://github.com/capstone-engine/capstone/issues/2631
if len(instruction.operands) == 2:
if instruction.operands[1].cs_op.shift.type in AARCH64_CONSTANT_SHIFTS:
self._common_binary_op_annotator(
instruction,
emu,
instruction.operands[0],
instruction.operands[1].before_value_no_modifiers,
instruction.operands[1].cs_op.shift.value,
AARCH64_SHIFT_INSTRUCTIONS[instruction.id],
)
else:
self._common_generic_register_destination(instruction, emu)
elif instruction.id in AARCH64_EMULATED_ANNOTATIONS:
self._common_generic_register_destination(instruction, emu)
else:
@ -291,30 +345,38 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
instruction.annotation = register_assign(result_operand.str, telescope)
@override
def _prepare(
self, instruction: PwndbgInstruction, emu: pwndbg.aglib.disasm.arch.Emulator
) -> None:
if CS_GRP_INT in instruction.groups:
# https://github.com/capstone-engine/capstone/issues/2630
instruction.groups.remove(CS_GRP_CALL)
@override
def _condition(
self, instruction: PwndbgInstruction, emu: Emulator
) -> pwndbg.aglib.disasm.arch.InstructionCondition:
# In ARM64, only branches have the conditional code in the instruction,
# as opposed to ARM32 which allows most instructions to be conditional
if instruction.id == ARM64_INS_B:
if instruction.id == AARCH64_INS_B:
# The B instruction can be made conditional by the condition codes
if instruction.cs_insn.cc in (ARM64_CC_INVALID, ARM64_CC_AL):
if instruction.cs_insn.cc in (AArch64CC_Invalid, AArch64CC_AL):
instruction.declare_conditional = False
else:
flags = super()._read_register_name(instruction, "cpsr", emu)
if flags is not None:
return resolve_condition(instruction.cs_insn.cc, flags)
elif instruction.id == ARM64_INS_CBNZ:
elif instruction.id == AARCH64_INS_CBNZ:
op_val = instruction.operands[0].before_value
return boolean_to_instruction_condition(op_val is not None and op_val != 0)
elif instruction.id == ARM64_INS_CBZ:
elif instruction.id == AARCH64_INS_CBZ:
op_val = instruction.operands[0].before_value
return boolean_to_instruction_condition(op_val is not None and op_val == 0)
elif instruction.id == ARM64_INS_TBNZ:
elif instruction.id == AARCH64_INS_TBNZ:
op_val, bit = (
instruction.operands[0].before_value,
instruction.operands[1].before_value,
@ -323,7 +385,7 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
if op_val is not None and bit is not None:
return boolean_to_instruction_condition(bool((op_val >> bit) & 1))
elif instruction.id == ARM64_INS_TBZ:
elif instruction.id == AARCH64_INS_TBZ:
op_val, bit = (
instruction.operands[0].before_value,
instruction.operands[1].before_value,
@ -351,7 +413,7 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
if (val := instruction.operands[-1].before_value) is not None:
return val & pwndbg.aglib.arch.ptrmask
return None
elif instruction.id == ARM64_INS_RET:
elif instruction.id in (AARCH64_INS_RET, AARCH64_INS_ALIAS_RET):
# If this is a ret WITHOUT an operand, it means we should read from the LR/x30 register
return super()._read_register_name(instruction, "lr", emu)
@ -383,11 +445,20 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
target = 0
# All memory operands have `base` defined
# Handle case of LDR instructions memory operands that uses immediate as memory location
# Example: ldr x1, 0x30
if op.mem.base == 0:
return op.mem.disp
base = self._read_register(instruction, op.mem.base, emu)
if base is None:
return None
target = base + op.mem.disp
target = base
if not instruction.cs_insn.post_index:
# Unlike in arm32, the sign of `disp` is encoded in the number already.
# There is no separate ".subtracted" value in Capstone
target += op.mem.disp
# If there is an index register
if op.mem.index != 0:
@ -448,6 +519,9 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
if target is None:
return None
# We need this to retain the value of the un-shifted register in some annotations, such as shifts
op.before_value_no_modifiers = target
# The shift and sign-extend operations depend on the target bit width.
# This is sometimes implicit in the target register size, which is always
# the first operand.
@ -461,10 +535,12 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
target = AARCH64_EXTEND_MAP[op.cs_op.ext](target) & ((1 << target_bit_width) - 1)
if op.cs_op.shift.type != 0:
print(target, op.cs_op.shift.type, op.cs_op.shift.value)
target = AARCH64_BIT_SHIFT_MAP[op.cs_op.shift.type](
target = AARCH64_BIT_SHIFT_MAP.get(op.cs_op.shift.type, lambda *a: None)(
target, op.cs_op.shift.value, target_bit_width
) & ((1 << target_bit_width) - 1)
)
if target is not None:
target = target & ((1 << target_bit_width) - 1)
return target

@ -214,6 +214,8 @@ class DisassemblyAssistant:
pwndbg.aglib.arch.name, generic_assistant
)
enhancer._prepare(instruction, emu)
# Don't disable emulation yet, as we can use it to read the syscall register
enhancer._enhance_syscall(instruction, emu)
@ -259,6 +261,10 @@ class DisassemblyAssistant:
print(enhancer.dump(instruction))
print("Done enhancing")
# This is run before enhancement - often used to handle edge case behavior
def _prepare(self, instruction: PwndbgInstruction, emu: Emulator) -> None:
return None
# Subclasses for specific architecture should override this
def _set_annotation_string(self, instruction: PwndbgInstruction, emu: Emulator) -> None:
"""
@ -317,7 +323,11 @@ class DisassemblyAssistant:
op.before_value, instruction, op, emu
)
if op.symbol and op.type == CS_OP_IMM and pwndbg.config.disasm_inline_symbols:
if (
op.symbol
and op.type in (CS_OP_IMM, CS_OP_MEM)
and pwndbg.config.disasm_inline_symbols
):
# Make an inline replacement, so `jmp 0x400122` becomes `jmp function_name`
instruction.asm_string = instruction.asm_string.replace(
hex(op.before_value), op.symbol

@ -78,31 +78,48 @@ ARM_MATH_INSTRUCTIONS = {
ARM_SHIFT_INSTRUCTIONS = {
ARM_INS_ASR: ">>s",
ARM_INS_ALIAS_ASR: ">>s",
ARM_INS_LSR: ">>",
ARM_INS_ALIAS_LSR: ">>",
ARM_INS_LSL: "<<",
ARM_INS_ALIAS_LSL: "<<",
ARM_INS_ROR: ">>r",
ARM_INS_ALIAS_ROR: ">>r",
}
def first_op_is_pc(i: PwndbgInstruction) -> bool:
return i.operands[0].reg == ARM_REG_PC
def ops_contain_pc(i: PwndbgInstruction) -> bool:
for op in i.operands:
if op.type == CS_OP_REG:
if op.reg == ARM_REG_PC:
return True
return False
ARM_CAN_WRITE_TO_PC: Dict[int, Callable[[PwndbgInstruction], bool]] = {
ARM_INS_ADD: first_op_is_pc,
ARM_INS_SUB: first_op_is_pc,
ARM_INS_SUBS: first_op_is_pc,
ARM_INS_MOV: first_op_is_pc,
ARM_INS_LDR: first_op_is_pc,
ARM_INS_POP: ops_contain_pc,
ARM_INS_LDM: ops_contain_pc,
# All of these instructions can write to the PC
# https://developer.arm.com/documentation/ddi0406/cb/Application-Level-Architecture/Application-Level-Programmers--Model/ARM-core-registers/Writing-to-the-PC?lang=en
# If they do write to PC, Capstone gives the instructions the `ARM_GRP_JUMP` group
# Note that we don't have the flag-setting variants - "ands", "subs" - because these generate an illegal instruction interrupt at runtime
ARM_CAN_WRITE_TO_PC_INSTRUCTIONS = {
ARM_INS_LDM,
ARM_INS_ALIAS_LDM,
ARM_INS_POP,
ARM_INS_ALIAS_POP,
ARM_INS_LDR,
ARM_INS_ADC,
ARM_INS_ADD,
ARM_INS_ADR,
ARM_INS_AND,
ARM_INS_ASR,
ARM_INS_ALIAS_ASR,
ARM_INS_BIC,
ARM_INS_EOR,
ARM_INS_LSL,
ARM_INS_ALIAS_LSL,
ARM_INS_LSR,
ARM_INS_ALIAS_LSR,
ARM_INS_MOV,
ARM_INS_MVN,
ARM_INS_ORR,
ARM_INS_ROR,
ARM_INS_ALIAS_ROR,
ARM_INS_RRX,
ARM_INS_ALIAS_RRX,
ARM_INS_RSB,
ARM_INS_RSC,
ARM_INS_SBC,
ARM_INS_SUB,
}
@ -178,23 +195,14 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
ARM_MATH_INSTRUCTIONS[instruction.id],
)
elif instruction.id in ARM_SHIFT_INSTRUCTIONS:
# If it's a constant shift
if len(instruction.operands) == 2:
# The encoding of shifts has changed between past Capstone versions: https://github.com/capstone-engine/capstone/pull/2638
# This check avoids a crash
if len(instruction.operands) == 3:
self._common_binary_op_annotator(
instruction,
emu,
instruction.operands[0],
instruction.operands[1].before_value_no_modifiers,
instruction.operands[1].cs_op.shift.value,
ARM_SHIFT_INSTRUCTIONS[instruction.id],
)
else:
# Register shift
self._common_binary_op_annotator(
instruction,
emu,
instruction.operands[0],
instruction.operands[1].before_value,
instruction.operands[2].before_value,
ARM_SHIFT_INSTRUCTIONS[instruction.id],
)
@ -202,13 +210,24 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
self.annotation_handlers.get(instruction.id, lambda *a: None)(instruction, emu)
@override
def _condition(self, instruction: PwndbgInstruction, emu: Emulator) -> InstructionCondition:
if instruction.id in ARM_CAN_WRITE_TO_PC:
instruction.declare_is_unconditional_jump = ARM_CAN_WRITE_TO_PC[instruction.id](
instruction
)
def _prepare(
self, instruction: PwndbgInstruction, emu: pwndbg.aglib.disasm.arch.Emulator
) -> None:
if CS_GRP_INT in instruction.groups:
# https://github.com/capstone-engine/capstone/issues/2630
instruction.groups.remove(CS_GRP_CALL)
if instruction.cs_insn.cc == ARM_CC_AL:
@override
def _condition(self, instruction: PwndbgInstruction, emu: Emulator) -> InstructionCondition:
if ARM_GRP_JUMP in instruction.groups:
if instruction.id in ARM_CAN_WRITE_TO_PC_INSTRUCTIONS:
# Since Capstone V6, instructions that write to the PC are given the jump group.
# However, in Pwndbg code, unless stated otherwise, jumps are assumed to be conditional, so we set this attribute
# to indicate that this is an unconditional branch.
instruction.declare_is_unconditional_jump = True
# These condition codes indicate unconditionally/condition is not relevant
if instruction.cs_insn.cc in (ARM_CC_AL, ARMCC_UNDEF):
if instruction.id in (ARM_INS_B, ARM_INS_BL, ARM_INS_BLX, ARM_INS_BX, ARM_INS_BXJ):
instruction.declare_conditional = False
return InstructionCondition.UNDETERMINED
@ -325,7 +344,11 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
# See "Operation" at the bottom of https://developer.arm.com/documentation/ddi0597/2024-03/Base-Instructions/LDR--literal---Load-Register--literal--
base = align_down(4, base)
target = base + op.mem.disp
target = base
# On post index, the base pointer is incremented after the memory dereference
if not instruction.cs_insn.post_index:
target += op.mem.disp * (-1 if op.cs_op.subtracted else 1)
# If there is an index register
if op.mem.index != 0:
@ -337,7 +360,7 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
if op.cs_op.shift.type != 0:
index = ARM_BIT_SHIFT_MAP[op.cs_op.shift.type](index, op.cs_op.shift.value, 32)
target += index * (-1 if op.cs_op.subtracted else 1)
target += index * op.mem.scale
return target

@ -13,19 +13,20 @@ from capstone import CS_AC
from capstone import CS_GRP
from capstone import CS_OP
from capstone import * # noqa: F403
from capstone.aarch64 import AARCH64_INS_BL
from capstone.aarch64 import AARCH64_INS_BLR
from capstone.aarch64 import AARCH64_INS_BR
from capstone.arm import ARM_INS_TBB
from capstone.arm import ARM_INS_TBH
# from capstone.arm64 import ARM64_INS_B
from capstone.arm64 import ARM64_INS_BL
from capstone.arm64 import ARM64_INS_BLR
from capstone.arm64 import ARM64_INS_BR
from capstone.mips import MIPS_INS_ALIAS_B
from capstone.mips import MIPS_INS_ALIAS_BAL
from capstone.mips import MIPS_INS_B
from capstone.mips import MIPS_INS_BAL
from capstone.mips import MIPS_INS_BLTZAL
from capstone.mips import MIPS_INS_J
from capstone.mips import MIPS_INS_JAL
from capstone.mips import MIPS_INS_JALR
from capstone.mips import MIPS_INS_JALR_HB
from capstone.mips import MIPS_INS_JR
from capstone.ppc import PPC_INS_B
from capstone.ppc import PPC_INS_BA
@ -54,13 +55,23 @@ from pwndbg.dbg import DisassembledInstruction
# so we don't need to manually specify those for each architecture
UNCONDITIONAL_JUMP_INSTRUCTIONS: Dict[int, Set[int]] = {
CS_ARCH_X86: {X86_INS_JMP},
CS_ARCH_MIPS: {MIPS_INS_J, MIPS_INS_JR, MIPS_INS_JAL, MIPS_INS_JALR, MIPS_INS_BAL, MIPS_INS_B},
CS_ARCH_MIPS: {
MIPS_INS_J,
MIPS_INS_JR,
MIPS_INS_JAL,
MIPS_INS_JALR,
MIPS_INS_JALR_HB,
MIPS_INS_BAL,
MIPS_INS_ALIAS_BAL,
MIPS_INS_B,
MIPS_INS_ALIAS_B,
},
CS_ARCH_SPARC: {SPARC_INS_JMP, SPARC_INS_JMPL},
CS_ARCH_ARM: {
ARM_INS_TBB,
ARM_INS_TBH,
},
CS_ARCH_ARM64: {ARM64_INS_BL, ARM64_INS_BLR, ARM64_INS_BR},
CS_ARCH_AARCH64: {AARCH64_INS_BL, AARCH64_INS_BLR, AARCH64_INS_BR},
CS_ARCH_RISCV: {
RISCV_INS_JAL,
RISCV_INS_JALR,
@ -115,7 +126,7 @@ class SplitType(Enum):
# Only use within the instruction.__repr__ to give a nice output
CAPSTONE_ARCH_MAPPING_STRING = {
CS_ARCH_ARM: "arm",
CS_ARCH_ARM64: "aarch64",
CS_ARCH_AARCH64: "aarch64",
CS_ARCH_X86: "x86",
CS_ARCH_PPC: "powerpc",
CS_ARCH_MIPS: "mips",
@ -212,9 +223,11 @@ class PwndbgInstructionImpl(PwndbgInstruction):
Groups that apply to all architectures: CS_GRP_INVALID | CS_GRP_JUMP | CS_GRP_CALL | CS_GRP_RET | CS_GRP_INT | CS_GRP_IRET | CS_GRP_PRIVILEGE | CS_GRP_BRANCH_RELATIVE
"""
self.id: int = cs_insn.id
self.id: int = cs_insn.alias_id if cs_insn.is_alias else cs_insn.id
"""
The underlying Capstone ID for the instruction
If it's an alias, use the id of the alias
Examples: X86_INS_JMP, X86_INS_CALL, RISCV_INS_C_JAL
"""
@ -293,8 +306,8 @@ class PwndbgInstructionImpl(PwndbgInstruction):
In most cases, we can determine this purely based on the instruction ID, and this field is irrelevent.
However, in some arches, like Arm, the same instruction can be made conditional by certain instruction attributes.
Ex:
Arm, `bls` instruction. This is encoded as a `b` (Capstone ID 11) under the code, with an additional condition code field.
In this case, sometimes a `b` instruction (ID 11) is unconditional (always branches), in other cases it is conditional.
Arm, `bls` instruction. This is encoded as a `b` under the code, with an additional condition code field.
In this case, sometimes a `b` instruction is unconditional (always branches), in other cases it is conditional.
We use this field to disambiguate these cases.
True if we manually determine this instruction is a conditional instruction
@ -305,9 +318,9 @@ class PwndbgInstructionImpl(PwndbgInstruction):
self.declare_is_unconditional_jump: bool = False
"""
This field is used to declare that this instruction is an unconditional jump.
Most of the type, we depend on Capstone groups to check for jump instructions,
but sometimes these are lacking, such as in the case of general-purpose instructions
where the PC is the destination register, such as Arm `add`, `sub`, `ldr`, and `pop` instructions.
Most of the time, we depend on Capstone groups to check for jump instructions.
However, some instructions become branches depending on the operands,
such as Arm `add`, `sub`, `ldr`, `pop`, where PC is the destination register
In these cases, we want to forcefully state that this instruction mutates the PC, so we set this attribute to True.
@ -417,6 +430,7 @@ class PwndbgInstructionImpl(PwndbgInstruction):
"""
return (
self.declare_conditional is not False
and self.declare_is_unconditional_jump is False
and bool(self.groups & GENERIC_JUMP_GROUPS)
and self.id not in UNCONDITIONAL_JUMP_INSTRUCTIONS[self.cs_insn._cs.arch]
)
@ -482,6 +496,7 @@ class PwndbgInstructionImpl(PwndbgInstruction):
info = f"""{self.mnemonic} {self.op_str} at {self.address:#x} (size={self.size}) (arch: {CAPSTONE_ARCH_MAPPING_STRING.get(self.cs_insn._cs.arch,None)})
ID: {self.id}, {self.cs_insn.insn_name()}
Capstone ID/Alias ID: {self.cs_insn.id} / {self.cs_insn.alias_id if self.cs_insn.is_alias else 'None'}
Raw asm: {'%-06s %s' % (self.mnemonic, self.op_str)}
New asm: {self.asm_string}
Next: {self.next:#x}
@ -492,7 +507,7 @@ class PwndbgInstructionImpl(PwndbgInstruction):
Operands: [{operands_str}]
Conditional jump: {self.is_conditional_jump}. Taken: {self.is_conditional_jump_taken}
Unconditional jump: {self.is_unconditional_jump}
Declare unconditional: {self.declare_conditional}
Declare conditional: {self.declare_conditional}
Declare unconditional jump: {self.declare_is_unconditional_jump}
Force jump target: {self.force_unconditional_jump_target}
Can change PC: {self.has_jump_target}

@ -33,12 +33,57 @@ from pwndbg.aglib.disasm.instruction import PwndbgInstruction
if TYPE_CHECKING:
from pwndbg.emu.emulator import Emulator
# These branch instructions do not have a branch delay
BRANCH_WITHOUT_DELAY_SLOT_INSTRUCTIONS = {
MIPS_INS_BC,
MIPS_INS_BALC,
MIPS_INS_JIALC,
MIPS_INS_JIC,
MIPS_INS_BLEZALC,
MIPS_INS_BGEZALC,
MIPS_INS_BGTZALC,
MIPS_INS_BLTZALC,
MIPS_INS_BEQZALC,
MIPS_INS_BNEZALC,
MIPS_INS_BLEZC,
MIPS_INS_BGEZC,
MIPS_INS_BGEUC,
MIPS_INS_BGEIC,
MIPS_INS_BGEUC,
MIPS_INS_BGEIUC,
MIPS_INS_BGTZC,
MIPS_INS_BLTZC,
MIPS_INS_BEQZC,
MIPS_INS_ALIAS_BEQZC,
MIPS_INS_BNEZC,
MIPS_INS_ALIAS_BNEZC,
MIPS_INS_BEQC,
MIPS_INS_ALIAS_BEQC,
MIPS_INS_BEQIC,
MIPS_INS_BNEC,
MIPS_INS_ALIAS_BNEC,
MIPS_INS_BNEIC,
MIPS_INS_BLTC,
MIPS_INS_BLTIC,
MIPS_INS_BLTUC,
MIPS_INS_BLTIUC,
MIPS_INS_BGEC,
MIPS_INS_BLTUC,
MIPS_INS_BNVC,
MIPS_INS_BOVC,
MIPS_INS_BRSC,
MIPS_INS_BALRSC,
MIPS_INS_BBEQZC,
MIPS_INS_BBNEZC,
}
# Instruction in branch delay slot is ignored if the branch it NOT taken
# Currently unused
BRANCH_LIKELY_INSTRUCTIONS = {
MIPS_INS_BC0TL,
MIPS_INS_BC1TL,
MIPS_INS_BC0FL,
MIPS_INS_BC1FL,
MIPS_INS_ALIAS_BC1FL,
MIPS_INS_BC1TL,
MIPS_INS_ALIAS_BC1TL,
MIPS_INS_BEQL,
MIPS_INS_BGEZALL,
MIPS_INS_BGEZL,
@ -47,9 +92,10 @@ BRANCH_LIKELY_INSTRUCTIONS = {
MIPS_INS_BLTZALL,
MIPS_INS_BLTZL,
MIPS_INS_BNEL,
MIPS_INS_ALIAS_BNEZL,
MIPS_INS_ALIAS_BEQZL,
}
CONDITION_RESOLVERS: Dict[int, Callable[[List[int]], bool]] = {
MIPS_INS_BEQZ: lambda ops: ops[0] == 0,
MIPS_INS_BNEZ: lambda ops: ops[0] != 0,
@ -63,6 +109,9 @@ CONDITION_RESOLVERS: Dict[int, Callable[[List[int]], bool]] = {
MIPS_INS_BLTZ: lambda ops: bit_math.to_signed(ops[0], pwndbg.aglib.arch.ptrsize * 8) < 0,
}
CONDITION_RESOLVERS[MIPS_INS_ALIAS_BEQZ] = CONDITION_RESOLVERS[MIPS_INS_BEQZ]
CONDITION_RESOLVERS[MIPS_INS_ALIAS_BNEZ] = CONDITION_RESOLVERS[MIPS_INS_BNEZ]
# These are instructions that have the first operand as the destination register.
# They all do some computation and set the register to the result.
# These were derived from "MIPS Architecture for Programmers Volume II: The MIPS64 Instruction Set Reference Manual"
@ -150,6 +199,7 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
self.annotation_handlers: Dict[int, Callable[[PwndbgInstruction, Emulator], None]] = {
# MOVE
MIPS_INS_MOVE: self._common_move_annotator,
MIPS_INS_ALIAS_MOVE: self._common_move_annotator,
# LI
MIPS_INS_LI: self._common_move_annotator,
# LUI
@ -207,12 +257,6 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
@override
def _condition(self, instruction: PwndbgInstruction, emu: Emulator) -> InstructionCondition:
if instruction.id == MIPS_INS_JAL:
# Capstone bug - JAL with an immediate (jal 0x1000000) has no jump group in the list, so we add it manually
# See: https://github.com/capstone-engine/capstone/issues/2448
# And: https://github.com/capstone-engine/capstone/issues/1680
instruction.groups.add(CS_GRP_CALL)
if len(instruction.operands) == 0:
return InstructionCondition.UNDETERMINED
@ -239,7 +283,7 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
@override
def _resolve_target(self, instruction: PwndbgInstruction, emu: Emulator | None):
if bool(instruction.groups & FORWARD_JUMP_GROUP) and not bool(
instruction.groups & BRANCH_LIKELY_INSTRUCTIONS
instruction.groups & BRANCH_WITHOUT_DELAY_SLOT_INSTRUCTIONS
):
instruction.causes_branch_delay = True

@ -29,23 +29,17 @@ RISCV_LOAD_INSTRUCTIONS = {
RISCV_INS_LHU: 2,
RISCV_INS_LWU: 4,
RISCV_INS_LD: 8,
RISCV_INS_C_LW: -4,
RISCV_INS_C_LWSP: -4,
RISCV_INS_C_LD: 8,
RISCV_INS_C_LDSP: 8,
}
# Due to a bug in Capstone, these instructions have incorrect operands to represent a memory address.
# So we temporarily separate them to handle them differently
# This will be fixed in Capstone 6 - https://github.com/capstone-engine/capstone/pull/2393
# TODO: remove this when updating to Capstone 6
RISCV_COMPRESSED_LOAD_INSTRUCTIONS = {RISCV_INS_C_LW: -4, RISCV_INS_C_LD: 8, RISCV_INS_C_LDSP: 8}
RISCV_STORE_INSTRUCTIONS = {
RISCV_INS_SB: 1,
RISCV_INS_SH: 2,
RISCV_INS_SW: 4,
RISCV_INS_SD: 8,
}
# TODO: remove this when updating to Capstone 6
RISCV_COMPRESSED_STORE_INSTRUCTIONS = {
RISCV_INS_C_SW: 4,
RISCV_INS_C_SWSP: 4,
RISCV_INS_C_SD: 8,
@ -150,26 +144,6 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
instruction.operands[0].str,
instruction.operands[1].str,
)
# TODO: remove this when updating to Capstone 6
elif instruction.id in RISCV_COMPRESSED_LOAD_INSTRUCTIONS:
# We need to manually resolve this now since Capstone doesn't properly represent
# memory operands for compressed instructions.
address = self._resolve_compressed_target_addr(instruction, emu)
if address is not None:
read_size = RISCV_COMPRESSED_LOAD_INSTRUCTIONS[instruction.id]
dest_str = f"[{MemoryColor.get_address_or_symbol(address)}]"
self._common_load_annotator(
instruction,
emu,
address,
abs(read_size),
read_size < 0,
pwndbg.aglib.arch.ptrsize,
instruction.operands[0].str,
dest_str,
)
elif instruction.id in RISCV_STORE_INSTRUCTIONS:
self._common_store_annotator(
instruction,
@ -179,21 +153,6 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
RISCV_STORE_INSTRUCTIONS[instruction.id],
instruction.operands[1].str,
)
elif instruction.id in RISCV_COMPRESSED_STORE_INSTRUCTIONS:
# TODO: remove this branch when updating to Capstone 6
address = self._resolve_compressed_target_addr(instruction, emu)
if address is not None:
dest_str = f"[{MemoryColor.get_address_or_symbol(address)}]"
self._common_store_annotator(
instruction,
emu,
address,
instruction.operands[0].before_value,
RISCV_COMPRESSED_STORE_INSTRUCTIONS[instruction.id],
dest_str,
)
elif instruction.id in RISCV_MATH_INSTRUCTIONS:
# We need this check, because some of these instructions can encoded as aliases
# Example: NOP is an alias of ADDI where target is x0. In Capstone, the ID will still be that of ADDI but with no operands
@ -233,22 +192,6 @@ class DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant):
result_operand.str, MemoryColor.get_address_and_symbol(address)
)
def _resolve_compressed_target_addr(
self, instruction: PwndbgInstruction, emu: Emulator
) -> int | None:
"""
Calculate the address used in a compressed load/store instruction.
None if address cannot be resolved.
TODO: remove this when updating to Capstone 6
"""
_, disp, reg = instruction.operands
if disp.before_value is None or reg.before_value is None:
return None
return disp.before_value + reg.before_value
def _is_condition_taken(
self, instruction: PwndbgInstruction, emu: Emulator | None
) -> InstructionCondition:

@ -14,7 +14,6 @@ from typing import Tuple
import capstone as C
import unicorn as U
import unicorn.riscv_const
import pwndbg.aglib.arch
import pwndbg.aglib.disasm
@ -180,7 +179,7 @@ arch_to_SYSCALL = {
U.UC_ARCH_MIPS: [C.mips_const.MIPS_INS_SYSCALL],
U.UC_ARCH_SPARC: [C.sparc_const.SPARC_INS_T],
U.UC_ARCH_ARM: [C.arm_const.ARM_INS_SVC],
U.UC_ARCH_ARM64: [C.arm64_const.ARM64_INS_SVC],
U.UC_ARCH_ARM64: [C.aarch64_const.AARCH64_INS_SVC],
U.UC_ARCH_PPC: [C.ppc_const.PPC_INS_SC],
U.UC_ARCH_RISCV: [C.riscv_const.RISCV_INS_ECALL],
}
@ -197,7 +196,7 @@ BANNED_INSTRUCTIONS = {
"mips": {C.mips.MIPS_INS_RDHWR},
"arm": ARM_BANNED_INSTRUCTIONS,
"armcm": ARM_BANNED_INSTRUCTIONS,
"aarch64": {C.arm64.ARM64_INS_MRS},
"aarch64": {C.aarch64.AARCH64_INS_MRS},
}
# https://github.com/unicorn-engine/unicorn/issues/550
@ -254,7 +253,8 @@ class Emulator:
self.last_single_step_result = InstructionExecutedResult(None, None)
# Initialize the register state
for reg in self.regs.emulated_regs_order:
for emu_reg in self.regs.emulated_regs_order:
reg = emu_reg.name
enum = self.get_reg_enum(reg)
if not reg:
@ -270,8 +270,9 @@ class Emulator:
debug(DEBUG_INIT, "# Could not set register %r", reg)
continue
# All registers are initialized to zero.
if value == 0:
# Most registers are initialized to zero.
# However, some registers (CPSR on AArch64) do not default to zero, so we must explicitly set them to 0
if not emu_reg.force_write and value == 0:
continue
name = f"U.x86_const.UC_X86_REG_{reg.upper()}"

@ -5,6 +5,7 @@ def to_signed(unsigned: int, bit_width: int):
"""
Returns the signed number associated with the two's-complement binary representation of `unsigned`
"""
unsigned = unsigned & ((1 << bit_width) - 1)
extract_bit = 1 << (bit_width - 1)
return unsigned - ((unsigned & extract_bit) << 1)

@ -5,6 +5,7 @@ standardized interface to registers like "sp" and "pc".
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict
from typing import Iterator
from typing import List
@ -18,6 +19,16 @@ from pwndbg.lib.arch import PWNDBG_SUPPORTED_ARCHITECTURES_TYPE
BitFlags = OrderedDict[str, Union[int, Tuple[int, int]]]
@dataclass
class EmulatedRegister:
"""
Represent a register to write to the Unicorn emulator.
"""
name: str
force_write: bool
class RegisterSet:
#: Program counter register
pc: str
@ -87,11 +98,12 @@ class RegisterSet:
# we must write the flags register after PC, and the stack pointer after the flags register.
# Otherwise, the values will be clobbered
# https://github.com/pwndbg/pwndbg/pull/2337
self.emulated_regs_order: List[str] = []
self.emulated_regs_order: List[EmulatedRegister] = []
for reg in [pc] + list(flags) + [stack, frame] + list(retaddr) + list(misc) + list(gpr):
if reg and reg not in self.emulated_regs_order:
self.emulated_regs_order.append(reg)
emu_reg = EmulatedRegister(reg, True if reg in flags else False)
self.emulated_regs_order.append(emu_reg)
self.all = set(misc) | set(flags) | set(extra_flags) | set(self.retaddr) | set(self.common)
self.all -= {None}

@ -6,7 +6,7 @@ authors = [{ name = "Dominik 'disconnect3d' Czarnota", email = "dominik.b.czarno
requires-python = "~=3.10"
readme = "README.md"
dependencies = [
"capstone>=5.0.3,<6",
"capstone==6.0.0a4",
"unicorn>=2.1.3,<3",
"pwntools>=4.14.0,<5",
"sortedcontainers>=2.4.0,<3",

@ -54,13 +54,13 @@ def test_aarch64_branch_enhancement(qemu_assembly_run):
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n"
" ► 0x10000000 <_start> bl #my_function <my_function>\n"
" ► 0x10000000 <_start> bl my_function <my_function>\n"
" x0: 0\n"
" x1: 0\n"
" x2: 0\n"
" x3: 0\n"
" \n"
" 0x10000004 <_start+4> b #end <end>\n"
" 0x10000004 <_start+4> b end <end>\n"
"\n"
" 0x1000000c <end> mov x0, #0 X0 => 0\n"
" 0x10000010 <end+4> mov x8, #0x5d X8 => 0x5d\n"
@ -77,7 +77,7 @@ def test_aarch64_branch_enhancement(qemu_assembly_run):
assert dis == expected
# Now, ensure the `b` instruction is set correctly.
gdb.execute("si")
gdb.execute("ni")
instruction = pwndbg.aglib.disasm.one_with_config()
assert not instruction.is_conditional_jump
@ -178,19 +178,19 @@ def test_aarch64_conditional_jump_output(qemu_assembly_run):
"─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n"
" ► 0x10000000 <_start> mov x2, #0xa X2 => 0xa\n"
" 0x10000004 <_start+4> mov x3, #0 X3 => 0\n"
" 0x10000008 <_start+8> ✔ cbz x3, #A <A>\n"
" 0x10000008 <_start+8> ✔ cbz x3, A <A>\n"
"\n"
" 0x10000010 <A> ✔ cbnz x2, #B <B>\n"
" 0x10000010 <A> ✔ cbnz x2, B <B>\n"
"\n"
" 0x10000018 <B> ✔ tbz w2, #0, #C <C>\n"
" 0x10000018 <B> ✔ tbz w2, #0, C <C>\n"
"\n"
" 0x10000020 <C> ✔ tbnz w2, #3, #D <D>\n"
" 0x10000020 <C> ✔ tbnz w2, #3, D <D>\n"
"\n"
" 0x10000028 <D> cmp x2, x3 0xa - 0x0 CPSR => 0x20000000 [ n z C v q pan il d a i f el:0 sp ]\n"
" 0x1000002c <D+4> b.eq #E <E>\n"
" 0x1000002c <D+4> b.eq E <E>\n"
" \n"
" 0x10000030 <D+8> nop \n"
" 0x10000034 <E> ✔ b.ne #F <F>\n"
" 0x10000034 <E> ✔ b.ne F <F>\n"
"\n"
" 0x1000003c <F> mov x0, #0 X0 => 0\n"
"────────────────────────────────────────────────────────────────────────────────\n"
@ -246,25 +246,23 @@ def test_conditional_jumps_no_emulate(qemu_assembly_run):
test_aarch64_conditional_jumps(qemu_assembly_run)
AARCH64_ASSEMBLY = """
AARCH64_BINARY_OPERATIONS = """
mov x0, 7
mov x1, 563
add x2, x0, x1
sub x3, x1, x0
adds x2, x0, x1
subs x3, x1, x0
and x4, x0, x1
orr x5, x0, x1
eor x6, x0, x1
lsl x7, x0, 2
lsr x8, x1, 2
mul x10, x0, x1
udiv x11, x1, x0
"""
def test_aarch64_binary_operations(qemu_assembly_run):
qemu_assembly_run(AARCH64_ASSEMBLY, "aarch64")
qemu_assembly_run(AARCH64_BINARY_OPERATIONS, "aarch64")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
@ -276,11 +274,11 @@ def test_aarch64_binary_operations(qemu_assembly_run):
" 0x10000004 <_start+4> mov x1, #0x233 X1 => 0x233\n"
" 0x10000008 <_start+8> add x2, x0, x1 X2 => 0x23a (0x7 + 0x233)\n"
" 0x1000000c <_start+12> sub x3, x1, x0 X3 => 0x22c (0x233 - 0x7)\n"
" 0x10000010 <_start+16> and x4, x0, x1 X4 => 3 (0x7 & 0x233)\n"
" 0x10000014 <_start+20> orr x5, x0, x1 X5 => 0x237 (0x7 & 0x233)\n"
" 0x10000018 <_start+24> eor x6, x0, x1 X6 => 0x234 (0x7 ^ 0x233)\n"
" 0x1000001c <_start+28> lsl x7, x0, #2 X7 => 28 (7 << 2)\n"
" 0x10000020 <_start+32> lsr x8, x1, #2 X8 => 140 (0x233 >> 0x2)\n"
" 0x10000010 <_start+16> adds x2, x0, x1 X2 => 0x23a (0x7 + 0x233)\n"
" 0x10000014 <_start+20> subs x3, x1, x0 X3 => 0x22c (0x233 - 0x7)\n"
" 0x10000018 <_start+24> and x4, x0, x1 X4 => 3 (0x7 & 0x233)\n"
" 0x1000001c <_start+28> orr x5, x0, x1 X5 => 0x237 (0x7 | 0x233)\n"
" 0x10000020 <_start+32> eor x6, x0, x1 X6 => 0x234 (0x7 ^ 0x233)\n"
" 0x10000024 <_start+36> mul x10, x0, x1 X10 => 0xf65 (0x7 * 0x233)\n"
" 0x10000028 <_start+40> udiv x11, x1, x0 X11 => 80 (0x233 / 0x7)\n"
"────────────────────────────────────────────────────────────────────────────────\n"
@ -342,16 +340,16 @@ def test_aarch64_store_instructions(qemu_assembly_run):
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n"
" ► 0x10000028 <stores> ldr x4, #stores+56 X4, 0x10000060 => 0x4010e8 (value1) ◂— 0\n"
" 0x1000002c <stores+4> strb w0, [x4] [value1] <= 0xf0\n"
" 0x10000030 <stores+8> ldr x5, #stores+64 X5, 0x10000068 => 0x4010e9 (value2) ◂— 0\n"
" 0x10000034 <stores+12> strh w0, [x5] [value2] <= 0xdef0\n"
" 0x10000038 <stores+16> ldr x6, #stores+72 X6, 0x10000070 => 0x4010eb (value4) ◂— 0\n"
" 0x1000003c <stores+20> str w0, [x6] [value4] <= 0x9abcdef0\n"
" 0x10000040 <stores+24> ldr x7, #stores+80 X7, 0x10000078 => 0x4010ef (value8) ◂— 0\n"
" 0x10000044 <stores+28> str x0, [x7] [value8] <= 0x123456789abcdef0\n"
" 0x10000048 <stores+32> mov x8, #0x5d X8 => 0x5d\n"
" 0x1000004c <stores+36> mov x0, #0 X0 => 0\n"
" ► 0x10000028 <stores> ldr x4, stores+56 X4, [stores+56] => 0x4010e8 (value1) ◂— 0\n"
" 0x1000002c <stores+4> strb w0, [x4] [value1] <= 0xf0\n"
" 0x10000030 <stores+8> ldr x5, stores+64 X5, [stores+64] => 0x4010e9 (value2) ◂— 0\n"
" 0x10000034 <stores+12> strh w0, [x5] [value2] <= 0xdef0\n"
" 0x10000038 <stores+16> ldr x6, stores+72 X6, [stores+72] => 0x4010eb (value4) ◂— 0\n"
" 0x1000003c <stores+20> str w0, [x6] [value4] <= 0x9abcdef0\n"
" 0x10000040 <stores+24> ldr x7, stores+80 X7, [stores+80] => 0x4010ef (value8) ◂— 0\n"
" 0x10000044 <stores+28> str x0, [x7] [value8] <= 0x123456789abcdef0\n"
" 0x10000048 <stores+32> mov x8, #0x5d X8 => 0x5d\n"
" 0x1000004c <stores+36> mov x0, #0 X0 => 0\n"
" 0x10000050 <stores+40> svc #0 <SYS_exit>\n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
@ -432,6 +430,252 @@ def test_aarch64_load_instructions(qemu_assembly_run):
assert dis == expected
CPSR_REGISTER_TEST = f"""
mov x19, #8
cmn x19, #8
b.ne exit
nop
nop
nop
nop
nop
end:
exit:
{AARCH64_GRACEFUL_EXIT}
"""
def test_aarch64_write_cpsr_when_zero(qemu_assembly_run):
"""
The purpose of this test is to ensure we writing our CPSR register to the Unicorn emulator always.
We have an optimization to not write registers with the value zero to the emulator. This conflicts with the flags register.
The CPSR register, by default, has the Z bit enabled, so the value is not 0. In this test, we do a comparison that sets the bit off,
making CPSR have the value of zero. If we don't write 0 to this register explicitly emulator, Unicorn will take the "default" value which has the Z bit enabled.
And therefore, the branch will be mispredicted.
"""
qemu_assembly_run(CPSR_REGISTER_TEST, "aarch64")
# Warm up the instruction cache so when we step forward, we remember the earlier instructions
gdb.execute("context disasm")
gdb.execute("si")
gdb.execute("si")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n"
" 0x10000000 <_start> mov x19, #8 X19 => 8\n"
" 0x10000004 <_start+4> cmn x19, #8 8 + 8 CPSR => 0x0 [ n z c v q pan il d a i f el:0 sp ]\n"
" ► 0x10000008 <_start+8> ✔ b.ne exit <exit>\n"
"\n"
" 0x10000020 <exit> mov x0, #0 X0 => 0\n"
" 0x10000024 <exit+4> mov x8, #0x5d X8 => 0x5d\n"
" 0x10000028 <exit+8> svc #0 <SYS_exit>\n"
" 0x1000002c udf #0\n"
" 0x10000030 udf #0\n"
" 0x10000034 udf #0\n"
" 0x10000038 udf #0\n"
" 0x1000003c udf #0\n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert dis == expected
AARCH64_MEMORY_OPERAND_TEST = rf"""
LDR X1, =msg
LDR W0, [X1], #4
LDR W0, [X1, #4]
MOV X3, #8
LDR W4, [X1, X3]
LDR W5, [X1, #-4]
MOV X6, #-4
LDR W7, [X1, X6]
exit:
{AARCH64_GRACEFUL_EXIT}
data:
.data
msg:
.asciz "ABCDEFGHIJKLMNOPQRSTUVWXYZ!"
"""
def test_aarch64_memory_operands(qemu_assembly_run):
"""
Testing LDR instructions with interseting operands:
- LDR a constant into a register
- Post-index offset
- Pre-index with constant/register
- Pre-index with negated constants/register
- Include shifts
"""
qemu_assembly_run(AARCH64_MEMORY_OPERAND_TEST, "aarch64")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n"
" ► 0x10000000 <_start> ldr x1, data+4 X1, [data+4] => 0x4010e8 (msg) ◂— 'ABCDEFGHIJKLMNOPQRSTUVWXYZ!'\n"
" 0x10000004 <_start+4> ldr w0, [x1], #4 W0, [msg] => 0x44434241\n"
" 0x10000008 <_start+8> ldr w0, [x1, #4] W0, [msg+8] => 0x4c4b4a49\n"
" 0x1000000c <_start+12> mov x3, #8 X3 => 8\n"
" 0x10000010 <_start+16> ldr w4, [x1, x3] W4, [msg+12] => 0x504f4e4d\n"
" 0x10000014 <_start+20> ldur w5, [x1, #-4] W5, [msg] => 0x44434241\n"
" 0x10000018 <_start+24> mov x6, #-4 X6 => 0xfffffffffffffffc\n"
" 0x1000001c <_start+28> ldr w7, [x1, x6] W7, [msg] => 0x44434241\n"
" 0x10000020 <exit> mov x0, #0 X0 => 0\n"
" 0x10000024 <exit+4> mov x8, #0x5d X8 => 0x5d\n"
" 0x10000028 <exit+8> svc #0 <SYS_exit>\n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert dis == expected
AARCH64_SHIFTS_AND_EXTENDS = r"""
mov X1, 1
mov X3, 8
ADD X0, X1, X1, LSL 2
ADD X0, X1, X3, LSR 2
MOV W2, 0xFFFFFFFF
ADD X0, X1, W2, SXTB
ADD X0, X1, W2, UXTB
ADD X0, X1, X2, ASR 2
MOV X0, X1, ROR 2
SXTB X2, w2
ADD X0, X1, X2, ASR 2
"""
def test_aarch64_shifts_and_extends(qemu_assembly_run):
"""
Ensure our logic in register shifts + extends are working correctly
"""
qemu_assembly_run(AARCH64_SHIFTS_AND_EXTENDS, "aarch64")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n"
" ► 0x10000000 <_start> mov x1, #1 X1 => 1\n"
" 0x10000004 <_start+4> mov x3, #8 X3 => 8\n"
" 0x10000008 <_start+8> add x0, x1, x1, lsl #2 X0 => 5 (1 + 4)\n"
" 0x1000000c <_start+12> add x0, x1, x3, lsr #2 X0 => 3 (1 + 2)\n"
" 0x10000010 <_start+16> mov w2, #-1 W2 => 0xffffffff\n"
" 0x10000014 <_start+20> add x0, x1, w2, sxtb X0 => 0 (0x1 + 0xffffffffffffffff)\n"
" 0x10000018 <_start+24> add x0, x1, w2, uxtb X0 => 0x100 (0x1 + 0xff)\n"
" 0x1000001c <_start+28> add x0, x1, x2, asr #2 X0 => 0x40000000 (0x1 + 0x3fffffff)\n"
" 0x10000020 <_start+32> orr x0, xzr, x1, ror #2 X0 => 0x4000000000000000 (0x0 | 0x4000000000000000)\n"
" 0x10000024 <_start+36> sxtb x2, w2\n"
" 0x10000028 <_start+40> add x0, x1, x2, asr #2 X0 => 0 (0x1 + 0xffffffffffffffff)\n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert dis == expected
AARCH64_MEMORY_OPERAND_SHIFT = r"""
LDR x2, =msg
ADD x2,x2,16
MOV w3, 0xffffffff
ldr x1, [x2, w3, sxtw]
ldr x1, [x2, w3, sxtw 3]
nop
.data
msg:
.asciz "ABCDEFGHIJKLMNOPQRSTUVWXYZ!"
"""
def test_aarch64_shifts_and_extends_in_memory_operands(qemu_assembly_run):
qemu_assembly_run(AARCH64_MEMORY_OPERAND_SHIFT, "aarch64")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n"
" ► 0x10000000 <_start> ldr x2, _start+24 X2, [_start+24] => 0x4010e8 (msg) ◂— 'ABCDEFGHIJKLMNOPQRSTUVWXYZ!'\n"
" 0x10000004 <_start+4> add x2, x2, #0x10 X2 => 0x4010f8 (msg+16) (0x4010e8 + 0x10)\n"
" 0x10000008 <_start+8> mov w3, #-1 W3 => 0xffffffff\n"
" 0x1000000c <_start+12> ldr x1, [x2, w3, sxtw] X1, [msg+15] => 0x5756555453525150 ('PQRSTUVW')\n"
" 0x10000010 <_start+16> ldr x1, [x2, w3, sxtw #3] X1, [msg+8] => 0x504f4e4d4c4b4a49 ('IJKLMNOP')\n"
" 0x10000014 <_start+20> nop \n"
"\n"
"\n"
"\n"
"\n"
"\n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert dis == expected
AARCH64_SHIFT_INSTRUCTIONS = """
MOV x0, #3
MOV x1, #0xF000
MOV x2, #0x1234
LSR x3, x1, #4
LSR x4, x1, x0
LSL x5, x4, #4
LSL x6, x4, x2
ASR x6, x4, #4
ASR x6, x4, x0
ROR x6, x4, #4
ROR x6, x4, x0
"""
def test_aarch64_shift_instructions(qemu_assembly_run):
"""
Test annotations for shift instructions - the format of these has changed between Capstone versions.
Special attention is paid to the shift-by-register amount
- https://github.com/capstone-engine/capstone/issues/2631
"""
qemu_assembly_run(AARCH64_SHIFT_INSTRUCTIONS, "aarch64")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n"
" ► 0x10000000 <_start> mov x0, #3 X0 => 3\n"
" 0x10000004 <_start+4> mov x1, #0xf000 X1 => 0xf000\n"
" 0x10000008 <_start+8> mov x2, #0x1234 X2 => 0x1234\n"
" 0x1000000c <_start+12> lsr x3, x1, #4 X3 => 0xf00 (0xf000 >> 0x4)\n"
" 0x10000010 <_start+16> lsr x4, x1, x0 X4 => 0x1e00\n"
" 0x10000014 <_start+20> lsl x5, x4, #4 X5 => 0x1e000 (0x1e00 << 0x4)\n"
" 0x10000018 <_start+24> lsl x6, x4, x2 X6 => 0xe000000000000000\n"
" 0x1000001c <_start+28> asr x6, x4, #4 X6 => 0x1e0 (0x1e00 >>s 0x4)\n"
" 0x10000020 <_start+32> asr x6, x4, x0 X6 => 0x3c0\n"
" 0x10000024 <_start+36> ror x6, x4, #4 X6 => 0x1e0 (0x1e00 >>r 0x4)\n"
" 0x10000028 <_start+40> ror x6, x4, x0 X6 => 0x3c0\n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert dis == expected
REFERENCE_BINARY = user.binaries.get("reference-binary.aarch64.out")

@ -48,14 +48,14 @@ def test_arm_simple_branch(qemu_assembly_run):
" ► 0x10000000 <_start> mov r2, #5 R2 => 5\n"
" 0x10000004 <_start+4> mov r1, #0xa R1 => 0xa\n"
" 0x10000008 <_start+8> cmp r0, r1 0x0 - 0xa CPSR => 0x80000010 [ N z c v q j t e a i f ]\n"
" 0x1000000c <_start+12> ✔ bne #not_equal <not_equal>\n"
" 0x1000000c <_start+12> ✔ bne not_equal <not_equal>\n"
"\n"
" 0x10000018 <not_equal> mov r3, #1 R3 => 1\n"
" 0x1000001c <not_equal+4> cmp r1, r3 0xa - 0x1 CPSR => 0x20000010 [ n z C v q j t e a i f ]\n"
" 0x10000020 <not_equal+8> ✔ bgt #greater <greater>\n"
" 0x10000020 <not_equal+8> ✔ bgt greater <greater>\n"
"\n"
" 0x1000002c <greater> cmp r3, r1 0x1 - 0xa CPSR => 0x80000010 [ N z c v q j t e a i f ]\n"
" 0x10000030 <greater+4> ✔ bls #end <end>\n"
" 0x10000030 <greater+4> ✔ bls end <end>\n"
"\n"
" 0x1000003c <end> mov r0, #0 R0 => 0\n"
" 0x10000040 <end+4> mov r7, #0xf8 R7 => 0xf8\n"
@ -71,14 +71,14 @@ def test_arm_simple_branch(qemu_assembly_run):
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"──────────────────[ DISASM / arm / arm mode / set emulate on ]──────────────────\n"
" 0x1000000c <_start+12> ✔ bne #not_equal <not_equal>\n"
" 0x1000000c <_start+12> ✔ bne not_equal <not_equal>\n"
"\n"
" 0x10000018 <not_equal> mov r3, #1 R3 => 1\n"
" 0x1000001c <not_equal+4> cmp r1, r3 0xa - 0x1 CPSR => 0x20000010 [ n z C v q j t e a i f ]\n"
" 0x10000020 <not_equal+8> ✔ bgt #greater <greater>\n"
" 0x10000020 <not_equal+8> ✔ bgt greater <greater>\n"
"\n"
" 0x1000002c <greater> cmp r3, r1 0x1 - 0xa CPSR => 0x80000010 [ N z c v q j t e a i f ]\n"
" ► 0x10000030 <greater+4> ✔ bls #end <end>\n"
" ► 0x10000030 <greater+4> ✔ bls end <end>\n"
"\n"
" 0x1000003c <end> mov r0, #0 R0 => 0\n"
" 0x10000040 <end+4> mov r7, #0xf8 R7 => 0xf8\n"
@ -419,7 +419,7 @@ def test_arm_cmp_instructions(qemu_assembly_run):
" ► 0x10000000 <_start> mov r0, #5 R0 => 5\n"
" 0x10000004 <_start+4> mov r1, #5 R1 => 5\n"
" 0x10000008 <_start+8> cmp r0, r1 5 - 5 CPSR => 0x60000010 [ n Z C v q j t e a i f ]\n"
" 0x1000000c <_start+12> ✔ beq #end <end>\n"
" 0x1000000c <_start+12> ✔ beq end <end>\n"
"\n"
" 0x1000001c <end> mov r0, #0 R0 => 0\n"
" 0x10000020 <end+4> mov r7, #0xf8 R7 => 0xf8\n"
@ -467,7 +467,7 @@ def test_arm_call_instructions(qemu_assembly_run):
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"──────────────────[ DISASM / arm / arm mode / set emulate on ]──────────────────\n"
" ► 0x10000000 <_start> nop \n"
" 0x10000004 <_start+4> bl #func <func>\n"
" 0x10000004 <_start+4> bl func <func>\n"
" \n"
" 0x10000008 <_start+8> nop \n"
" 0x1000000c <_start+12> nop \n"
@ -536,39 +536,144 @@ def test_arm_exclusive_store(qemu_assembly_run):
ARM_SHIFTS = """
MOV r0, #0xF000
LSR r1, r0, #4
MOV r2, #2
LSR r3, r0, r2
MOV r4, #0x1234
LSL r5, r4, #8
MOV r0, #3
MOV r1, #0xF000
MOV r2, #0x1234
LSR r3, r1, #4
LSR r4, r1, r0
LSL r5, r4, #4
LSL r6, r4, r2
ASR r6, r4, #4
ASR r6, r4, r0
ROR r6, r4, #4
ROR r6, r4, r0
"""
def test_arm_logical_shifts(qemu_assembly_run):
"""
Shifts have a different underlying Capstone representation if it's an constant or a register offset.
This test ensures we handle both cases.
"""
qemu_assembly_run(ARM_SHIFTS, "arm")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"──────────────────[ DISASM / arm / arm mode / set emulate on ]──────────────────\n"
" ► 0x10000000 <_start> mov r0, #3 R0 => 3\n"
" 0x10000004 <_start+4> mov r1, #0xf000 R1 => 0xf000\n"
" 0x10000008 <_start+8> movw r2, #0x1234 R2 => 0x1234\n"
" 0x1000000c <_start+12> lsr r3, r1, #4 R3 => 0xf00 (0xf000 >> 0x4)\n"
" 0x10000010 <_start+16> lsr r4, r1, r0 R4 => 0x1e00 (0xf000 >> 0x3)\n"
" 0x10000014 <_start+20> lsl r5, r4, #4 R5 => 0x1e000 (0x1e00 << 0x4)\n"
" 0x10000018 <_start+24> lsl r6, r4, r2 R6 => 0 (0x1e00 << 0x1234)\n"
" 0x1000001c <_start+28> asr r6, r4, #4 R6 => 0x1e0 (0x1e00 >>s 0x4)\n"
" 0x10000020 <_start+32> asr r6, r4, r0 R6 => 0x3c0 (0x1e00 >>s 0x3)\n"
" 0x10000024 <_start+36> ror r6, r4, #4 R6 => 0x1e0 (0x1e00 >>r 0x4)\n"
" 0x10000028 <_start+40> ror r6, r4, r0 R6 => 0x3c0 (0x1e00 >>r 0x3)\n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert dis == expected
NEGATIVE_DISPONENTS = r"""
LDR r1, =msg
ADD r1, 4
LDR r0, [r1, #-4]
nop
nop
nop
nop
nop
nop
nop
nop
.data
msg:
.asciz "ABCDEFGHIJKLMNOPQRSTUVWXYZ!"
"""
def test_logical_shifts(qemu_assembly_run):
def test_arm_negative_disponent(qemu_assembly_run):
"""
Shifts have a different underlying Capstone representation if it's an constant or a register offset.
This test ensures we handle both cases.
Negative disponents are now represented by a positive offset and a flag that indicates it should be subtracted.
This representation changed in CapstoneV6
"""
qemu_assembly_run(ARM_SHIFTS, "arm")
qemu_assembly_run(NEGATIVE_DISPONENTS, "arm")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"──────────────────[ DISASM / arm / arm mode / set emulate on ]──────────────────\n"
" ► 0x10000000 <_start> ldr r1, [pc, #0x24] R1, [_start+44] => 0x11094 (msg) ◂— 'ABCDEFGHIJKLMNOPQRSTUVWXYZ!'\n"
" 0x10000004 <_start+4> add r1, r1, #4 R1 => 0x11098 (msg+4) (0x11094 + 0x4)\n"
" 0x10000008 <_start+8> ldr r0, [r1, #-4] R0, [msg] => 0x44434241 ('ABCD')\n"
" 0x1000000c <_start+12> nop \n"
" 0x10000010 <_start+16> nop \n"
" 0x10000014 <_start+20> nop \n"
" 0x10000018 <_start+24> nop \n"
" 0x1000001c <_start+28> nop \n"
" 0x10000020 <_start+32> nop \n"
" 0x10000024 <_start+36> nop \n"
" 0x10000028 <_start+40> nop \n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert dis == expected
NEGATIVE_INDEX_REGISTER = r"""
LDR R1, =msg
ADD r1, 4
ADD r2, r1, 4
MOV R3, #4
MOV R4, #2
LDR R5, [R1, -R3]
LDR R6, [R2, -R4, LSL #2]
nop
nop
nop
nop
nop
nop
nop
.data
msg:
.asciz "ABCDEFGHIJKLMNOPQRSTUVWXYZ!"
"""
def test_arm_negative_index_register(qemu_assembly_run):
"""
In the second LDR instruction above, the index register, R2, is negated.
This has a specific encoding that has changed in Capstone in the past, so we test to make sure we are handling it correctly.
"""
qemu_assembly_run(NEGATIVE_INDEX_REGISTER, "arm")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"──────────────────[ DISASM / arm / arm mode / set emulate on ]──────────────────\n"
" ► 0x10000000 <_start> mov r0, #0xf000 R0 => 0xf000\n"
" 0x10000004 <_start+4> lsr r1, r0, #4 R1 => 0xf00 (0xf000 >> 0x4)\n"
" 0x10000008 <_start+8> mov r2, #2 R2 => 2\n"
" 0x1000000c <_start+12> lsr r3, r0, r2 R3 => 0x3c00 (0xf000 >> 0x2)\n"
" 0x10000010 <_start+16> movw r4, #0x1234 R4 => 0x1234\n"
" 0x10000014 <_start+20> lsl r5, r4, #8 R5 => 0x123400 (0x1234 << 0x8)\n"
" 0x10000018 <_start+24> lsl r6, r4, r2 R6 => 0x48d0 (0x1234 << 0x2)\n"
" ► 0x10000000 <_start> ldr r1, [pc, #0x30] R1, [_start+56] => 0x11094 (msg) ◂— 'ABCDEFGHIJKLMNOPQRSTUVWXYZ!'\n"
" 0x10000004 <_start+4> add r1, r1, #4 R1 => 0x11098 (msg+4) (0x11094 + 0x4)\n"
" 0x10000008 <_start+8> add r2, r1, #4 R2 => 0x1109c (msg+8) (0x11098 + 0x4)\n"
" 0x1000000c <_start+12> mov r3, #4 R3 => 4\n"
" 0x10000010 <_start+16> mov r4, #2 R4 => 2\n"
" 0x10000014 <_start+20> ldr r5, [r1, -r3] R5, [msg] => 0x44434241 ('ABCD')\n"
" 0x10000018 <_start+24> ldr r6, [r2, -r4, lsl #2] R6, [msg] => 0x44434241 ('ABCD')\n"
" 0x1000001c <_start+28> nop \n"
" 0x10000020 <_start+32> nop \n"
" 0x10000024 <_start+36> nop \n"

@ -100,10 +100,10 @@ def test_mips32_bnez_instruction(qemu_assembly_run):
"""
qemu_assembly_run(MIPS_BNEZ, "mips")
dis = gdb.execute("context disasm", to_string=True)
dis = pwndbg.color.strip(dis)
dis_1 = gdb.execute("context disasm", to_string=True)
dis_1 = pwndbg.color.strip(dis_1)
expected = (
expected_1 = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"───────────────────────[ DISASM / mips / set emulate on ]───────────────────────\n"
" ► 0x10000000 <_start> addiu $t0, $zero, 0xa T0 => 10 (0x0 + 0xa)\n"
@ -120,15 +120,15 @@ def test_mips32_bnez_instruction(qemu_assembly_run):
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert dis == expected
assert dis_1 == expected_1
gdb.execute("set emulate off")
no_emulate_dis = gdb.execute("context disasm", to_string=True)
no_emulate_dis = pwndbg.color.strip(no_emulate_dis)
no_emulate_dis_2 = gdb.execute("context disasm", to_string=True)
no_emulate_dis_2 = pwndbg.color.strip(no_emulate_dis_2)
# Without emulation, we cannot determine whether or not we take the branch yet
# So the disasm output should just contain the instructions linearly in memory
expected = (
expected_2 = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"──────────────────────[ DISASM / mips / set emulate off ]───────────────────────\n"
" ► 0x10000000 <_start> addiu $t0, $zero, 0xa T0 => 0x0 + 0xa\n"
@ -145,15 +145,15 @@ def test_mips32_bnez_instruction(qemu_assembly_run):
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert no_emulate_dis == expected
assert no_emulate_dis_2 == expected_2
# Once we are on the instruction, the branch target should be manually determined
gdb.execute("si")
no_emulate_dis = gdb.execute("context disasm", to_string=True)
no_emulate_dis = pwndbg.color.strip(no_emulate_dis)
no_emulate_dis_3 = gdb.execute("context disasm", to_string=True)
no_emulate_dis_3 = pwndbg.color.strip(no_emulate_dis_3)
expected = (
expected_3 = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"──────────────────────[ DISASM / mips / set emulate off ]───────────────────────\n"
" 0x10000000 <_start> addiu $t0, $zero, 0xa T0 => 0x0 + 0xa\n"
@ -170,7 +170,7 @@ def test_mips32_bnez_instruction(qemu_assembly_run):
"────────────────────────────────────────────────────────────────────────────────\n"
)
assert no_emulate_dis == expected
assert no_emulate_dis_3 == expected_3
MIPS_CALL = f"""
@ -282,13 +282,13 @@ def test_mips32_store_instruction(qemu_assembly_run):
" 0x10000004 <_start+4> ori $t0, $t0, 0x5678 T0 => 0x12345678 (0x12340000 | 0x5678)\n"
" 0x10000008 <_start+8> lui $s0, 0x40 S0 => 0x400000\n"
" 0x1000000c <_start+12> addiu $s0, $s0, 0x1130 S0 => 0x401130 (value1) (0x400000 + 0x1130)\n"
" 0x10000010 <_start+16> sw $t0, ($s0) [value1] <= 0x12345678\n"
" 0x10000010 <_start+16> sw $t0, 0($s0) [value1] <= 0x12345678\n"
" 0x10000014 <_start+20> lui $s1, 0x40 S1 => 0x400000\n"
" 0x10000018 <_start+24> addiu $s1, $s1, 0x1134 S1 => 0x401134 (value2) (0x400000 + 0x1134)\n"
" 0x1000001c <_start+28> sh $t0, ($s1) [value2] <= 0x5678\n"
" 0x1000001c <_start+28> sh $t0, 0($s1) [value2] <= 0x5678\n"
" 0x10000020 <_start+32> lui $s2, 0x40 S2 => 0x400000\n"
" 0x10000024 <_start+36> addiu $s2, $s2, 0x1136 S2 => 0x401136 (value3) (0x400000 + 0x1136)\n"
" 0x10000028 <_start+40> sb $t0, ($s2) [value3] <= 0x78\n"
" 0x10000028 <_start+40> sb $t0, 0($s2) [value3] <= 0x78\n"
"────────────────────────────────────────────────────────────────────────────────\n"
)
@ -347,12 +347,12 @@ def test_mips32_load_instructions(qemu_assembly_run):
expected = (
"LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n"
"───────────────────────[ DISASM / mips / set emulate on ]───────────────────────\n"
" ► 0x10000038 <loads> lw $t1, ($s0) T1, [value1] => 0xffffffff\n"
" 0x1000003c <loads+4> lhu $t2, ($s1) T2, [value2] => 0xffff\n"
" 0x10000040 <loads+8> lbu $t3, ($s2) T3, [value3] => 0xff\n"
" 0x10000044 <loads+12> lw $t4, ($s0) T4, [value1] => 0xffffffff\n"
" 0x10000048 <loads+16> lh $t5, ($s1) T5, [value2] => 0xffffffff\n"
" 0x1000004c <loads+20> lb $t6, ($s2) T6, [value3] => 0xffffffff\n"
" ► 0x10000038 <loads> lw $t1, 0($s0) T1, [value1] => 0xffffffff\n"
" 0x1000003c <loads+4> lhu $t2, 0($s1) T2, [value2] => 0xffff\n"
" 0x10000040 <loads+8> lbu $t3, 0($s2) T3, [value3] => 0xff\n"
" 0x10000044 <loads+12> lw $t4, 0($s0) T4, [value1] => 0xffffffff\n"
" 0x10000048 <loads+16> lh $t5, 0($s1) T5, [value2] => 0xffffffff\n"
" 0x1000004c <loads+20> lb $t6, 0($s2) T6, [value3] => 0xffffffff\n"
"\n"
"\n"
"\n"

@ -103,13 +103,9 @@ data:
def test_riscv64_compressed_loads(qemu_assembly_run):
"""
RISC-V support in Capstone is fairly new, and as of Capstone 5, there are some inconsistenties, and the underlying
metadata of the instructions can change between versions.
RISC-V support in Capstone is fairly new, and the underlying metadata of the instructions can change between versions.
Currently, compressed load and stores operations have a memory operand representation that is subject to change in Capstone v6.
If this crashes, it is we likely need to update the parser for compressed memory operands.
- Link: https://github.com/capstone-engine/capstone/issues/2351
This test ensures that we properly handle compressed load and stores instruction, as the data representation changed between v5 and v6.
"""
qemu_assembly_run(RISCV64_COMPRESSED_LOAD_STORE, "riscv64")

@ -15,6 +15,9 @@ def test_to_signed():
assert bit_math.to_signed(0xFFFF_FFFF, 32) == -1
assert bit_math.to_signed(0x8000_0000, 32) == -(2**31)
# Ignore higher bits
assert bit_math.to_signed(0xF_FF, 8) == -1
def test_lsl():
assert bit_math.logical_shift_left(0b1000_0000, 1, 8) == 0

@ -72,20 +72,46 @@ filecache = [
[[package]]
name = "capstone"
version = "5.0.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a4/23/759da7f13c2ce29ffe90ccb45eb61ffcd310b436bfb489f3dbd11fba8776/capstone-5.0.5.tar.gz", hash = "sha256:32346f6019d5351adaaf584ffc60c1e40db6b47d1d049eb924f903eb2b073e87", size = 2944153 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/4a/4eb99fa4daf4d3035cfd52790b2ae67810e5663f71797ee5025977464c13/capstone-5.0.5-py3-none-macosx_10_9_universal2.whl", hash = "sha256:24db89d74b571659fe6212e756795cd5d394378c50e19e41dbcfb6c087c2f87d", size = 2177052 },
{ url = "https://files.pythonhosted.org/packages/2f/cc/c7e6a8fef919b28fbfe8a5c8955b40bbe998f84f791699246d7e70f5cdb6/capstone-5.0.5-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:15f4b85df176999bbf7eb3f53f0cf2cee728254600c1be21442e2581189309e9", size = 1180188 },
{ url = "https://files.pythonhosted.org/packages/1a/5b/e7ee66ebec55da1e0ea841de62ba5e971fb8fe7b12a49215fdaa15d1b9f9/capstone-5.0.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5416621ac2d243d89b788f1309b143ea1f400da3eb5c47c6a87f1add99732a83", size = 1192762 },
{ url = "https://files.pythonhosted.org/packages/fb/42/a68064f021d55be5079c21da963f3347d0979476da268b61cb80a7527229/capstone-5.0.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ef47da78f44de1cdff1519b360186681fca0097e92046a7d7203d56364f99da", size = 1458295 },
{ url = "https://files.pythonhosted.org/packages/9b/b4/05263fe61b2a8d8211783f911283d1bd296d74bbbd6e9a1e48f6dadb76c9/capstone-5.0.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:754968f057d9e5d9c383f2918a1d56d455bfb274bbf307f219180b16e6d5aaeb", size = 1481602 },
{ url = "https://files.pythonhosted.org/packages/67/7b/4c21ee2001b110182326dd10b30f1a6f2c9227e28e9714145e473d397de8/capstone-5.0.5-py3-none-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:933797f7e2a257a77c3a699316deea92efa120a10d41e22725a96fc82f0a769e", size = 1480729 },
{ url = "https://files.pythonhosted.org/packages/45/8b/9ad9a8c337d2276b25d71aabc6096243a57246bb9f16431f52c25fa70597/capstone-5.0.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50b646f0c56b0cac5c993dde08b5e5eacf8b1f66031ec8d60154eae6e3c0645e", size = 1458038 },
{ url = "https://files.pythonhosted.org/packages/fd/a2/9fe3a14bb6c397d072ca8332b93b7b63c57bb9c0361c8fa3a8260efd5d90/capstone-5.0.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cd35b666739d7b79066fc69fd0c145d5ceb6a4131df3db1225ec6dcfa3fe322f", size = 1484181 },
{ url = "https://files.pythonhosted.org/packages/65/08/6a65de9ab23ce726b680c0e75a064406d4bd2738640fc47a2a6aa679f1d0/capstone-5.0.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:89dac65a1c84670ee30ccaf2ae688c4b27ad514d9dc8738a9826579051e29ecb", size = 1485340 },
{ url = "https://files.pythonhosted.org/packages/fd/77/be4185108d31137e8bb536d6dc3a9222dbc15bd1e84787a3bdec9108fcb9/capstone-5.0.5-py3-none-win_amd64.whl", hash = "sha256:a03b6b42b33bb0739b2436a555e699ac91cd1d1891134269b04e359b607e50e8", size = 1271239 },
version = "6.0.0a4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2e/e8/ed930d72d01d84705b85f49d28285eb0667af793d31ac29ca296f5b67b5f/capstone-6.0.0a4.tar.gz", hash = "sha256:62ca96f952e8d38913cf1e1edafbfc6cba533031b45c6dcf3854e8a68605e965", size = 4799040 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/63/94/a20c93bf0692f073545689a09febc3332ce99255212aa650ee7f953147c7/capstone-6.0.0a4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c043156a9b36208e73e3c4f700a97732bebc6be92ea391f78f83e9ed5b4e41c8", size = 3489947 },
{ url = "https://files.pythonhosted.org/packages/c6/0f/641267fb861383b8a1656de33b57337d5dd1723c0d595715ad6ac203e6de/capstone-6.0.0a4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99fefe28c7da22e817b13dfc59a06a7cd79ab12a4a2edd5d82114298e7f2109c", size = 1895902 },
{ url = "https://files.pythonhosted.org/packages/15/a2/1b685deb8e1cff3ffeb3e9122cf016ba71514bf63a9354bf76fd1bb5af4b/capstone-6.0.0a4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48bbfbcc4f5f22c2f07081db2bc1ad974d5c03fdc29d690d37e7e99047714b41", size = 1961921 },
{ url = "https://files.pythonhosted.org/packages/03/2f/aecf2347dffd9930ef0f771166297088900e5413205dd8e72bf5f1e780b7/capstone-6.0.0a4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6d8dddd9dc1d1cedef8186f02c0d2156f2c734e5ff40e7c0c7572df7f4be26b", size = 2279893 },
{ url = "https://files.pythonhosted.org/packages/42/ab/2e6c55b7166ce162dd8d764878caae2b0286beaf0a6a022b550f81e449f5/capstone-6.0.0a4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8351afe611a8ec50df753fe843be3abf740f186f5a535fcc6cf40392771a2f02", size = 2321918 },
{ url = "https://files.pythonhosted.org/packages/33/b0/17e6c6d68ffe33fe87593f50e5788749d3b5fed82f1e3c0685f73106d56c/capstone-6.0.0a4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d40ea5ca2404bc71815b73a5ef743c5240b484da28bd195d1f19526e66841e28", size = 2289663 },
{ url = "https://files.pythonhosted.org/packages/51/d1/618cd90822204c7b86edeb857c8beae27d24bb3c823a8cd5ff37b3523456/capstone-6.0.0a4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:999b9a85adcd389c0b71fec2e340f5445c392cf2826aa30b96793a11c24e90e0", size = 2312542 },
{ url = "https://files.pythonhosted.org/packages/fd/ce/fdf624f12da45dc9b314175d3454c49f33df7c6c6a1c81f14a3d7e9155be/capstone-6.0.0a4-cp310-cp310-win_amd64.whl", hash = "sha256:3a31629da8b276036badc7ac1cd9978d7de4205a0175306b48e74e1337cb4ac1", size = 2252118 },
{ url = "https://files.pythonhosted.org/packages/ab/11/1e820781491b60958a4393d2bfb4705374ebc5d2c034f8e7b7695811c181/capstone-6.0.0a4-cp310-cp310-win_arm64.whl", hash = "sha256:c95f411183ef714bf341073d6093642c6c00a0377b9e9fc581a3b77d9d9e0fe1", size = 2210983 },
{ url = "https://files.pythonhosted.org/packages/52/ea/55e7e726fc5692842fa0288c1be2f7397edd83c71aa3545176a6634728ad/capstone-6.0.0a4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:624db65efa6b6c76e561b8b678ca47b1bc98fdba8c0841d884ac23dad1df21e6", size = 3489946 },
{ url = "https://files.pythonhosted.org/packages/7a/68/0fe8b7472f7d0705ff733b73a627de40539e13fc24fabe0925d5168d5103/capstone-6.0.0a4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b907666508d244b947bb598334954e61f0749c9d7619d2ff5d189fd50facd55a", size = 1895902 },
{ url = "https://files.pythonhosted.org/packages/2a/58/361dd9c17aa6fe0c03a468c24482471ac4199860ee2cd77189a845e2603b/capstone-6.0.0a4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c132a9f5d024fdb5a45db2e78ace7bb8bdb92661a61262921693aa94234c22f", size = 1961922 },
{ url = "https://files.pythonhosted.org/packages/0d/2a/3d63fe56098e7de6ee8fb5ba538a340bc5c62c204b15880af1a786750932/capstone-6.0.0a4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4d5ef6beb87338e637b6d8b04adef463a82f2ea8b99926e9f08ed38df8a19bd", size = 2279891 },
{ url = "https://files.pythonhosted.org/packages/7f/17/b23a035203fe3f1f50951078fd7ab41d41581751badca21d4c321b05d291/capstone-6.0.0a4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac4f8ac1b9ca4337adffe12a2a5c2e82f546f864e3d102207269991e8a047691", size = 2321919 },
{ url = "https://files.pythonhosted.org/packages/de/e1/a20c3a0d85450ef7c9ce2bce8af12c52853869adf2a316ca589f9954b34e/capstone-6.0.0a4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fea6dde6f356a9d3eacda72e93107146265db3e02b06c9e3d82c6d13ce90310c", size = 2289664 },
{ url = "https://files.pythonhosted.org/packages/0c/0b/fa5d56d7095a9853b3702981e284e31fd3326951ab7315a8c6fe98dd87d9/capstone-6.0.0a4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b4dd5ede5c2ac679ed8d1e18ef72cf7bbaceaecbf27eb41a76556bfd1331d418", size = 2312539 },
{ url = "https://files.pythonhosted.org/packages/bc/b8/b836911c854ac93b4cdaef4f938ae9fd6d3e8df3960e40adb1d1e8a2ec18/capstone-6.0.0a4-cp311-cp311-win_amd64.whl", hash = "sha256:9f1cb304f7330ecfad18d94fe64169ca2d98c4188574b2e8bfd9515ae9a9c2b0", size = 2252118 },
{ url = "https://files.pythonhosted.org/packages/c3/81/fccc557cd0ff316a6fcc7c7e97cf3a05bcd07357372c14aa3542abf5c79a/capstone-6.0.0a4-cp311-cp311-win_arm64.whl", hash = "sha256:b23a9200fcf878879925fc674311d0431f9de5d07d8a442c353a62b6f3082b25", size = 2210984 },
{ url = "https://files.pythonhosted.org/packages/b0/47/ff0a5fd95060aaf450490a1145e7955fad029be0a063fe866b0306bb79d9/capstone-6.0.0a4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e3a9350abd4433f7a50a43dec9845f58709c14e7c4644d7dcd540a66b985ecc6", size = 3489948 },
{ url = "https://files.pythonhosted.org/packages/c9/54/2b0b473b58cf7fb181c05cb9b1b9f47fd1c2457029d6e5591a127d484c23/capstone-6.0.0a4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a39e8421a61e8e6324a3cdb18f9d599be4c1fdf9ca2d38bf3c2cc513a4331e6b", size = 1895904 },
{ url = "https://files.pythonhosted.org/packages/3a/51/310375f014eeb923ccde94ad0b3809cc2548029f558b7830b8a7766e1389/capstone-6.0.0a4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a8d72f398ead1a51115350b7e57415f7dbd118cecbb9193bc663c481a10baf0", size = 1961922 },
{ url = "https://files.pythonhosted.org/packages/ba/a0/fff69eaba09fd4f1c5fe95a4001bcbbd777dc451e245313dd16e1c7db1ae/capstone-6.0.0a4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:513e392bbaf5e9310a7409eba0699077767b44a0dd41ce70c30fd2132d431ce5", size = 2279894 },
{ url = "https://files.pythonhosted.org/packages/c0/ad/efaa0c8704548da3fa0f8801e4b8d9288e65c6e05fc0e3b861b302676664/capstone-6.0.0a4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e700366f05c291289c994329e00f6ba4899503d02ca77c768734f555841cb7", size = 2321918 },
{ url = "https://files.pythonhosted.org/packages/5c/7d/a2715b78e0342a18f5778e5a92eafd3a5c25494558054c39a89c9c92dcd5/capstone-6.0.0a4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3bc925fa3ad432010132eb8fdc4688cbc7e47aa3f5e2952307f414f25de486fd", size = 2289667 },
{ url = "https://files.pythonhosted.org/packages/7c/3c/482adad4a45f23fe183bbc66870e816101c208b8365bad5c81f131893ef8/capstone-6.0.0a4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:643e77dc7ae9ee5e8333303b66a7e783162d0d05fe7f2deeba094f5f2cfd68d1", size = 2312538 },
{ url = "https://files.pythonhosted.org/packages/d1/95/0eaf2857319fe44c1fde718a3f3bf0e07907951102863469aa2d16e6e71d/capstone-6.0.0a4-cp312-cp312-win_amd64.whl", hash = "sha256:bfb634ac0085a98031b42aaed64d12cdc46fc383a43cd224ef239bfdee22c9f0", size = 2252117 },
{ url = "https://files.pythonhosted.org/packages/db/32/d6caccaa374070903e520742d47a2e367f93aafd030e52bab753918df011/capstone-6.0.0a4-cp312-cp312-win_arm64.whl", hash = "sha256:9a88f020e01e1f451028cd2db609213ac5951c9c473457740fb4e368137464c3", size = 2210985 },
{ url = "https://files.pythonhosted.org/packages/ad/9d/2b84d5d465a44c0c4357279885f4c6de4feff8aef6320744a2650f9612c1/capstone-6.0.0a4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5db66c3c273c82f70ae68751d4b9c6241f7327bbeed9ee55ce4116ef5fcdbc6e", size = 3489946 },
{ url = "https://files.pythonhosted.org/packages/35/81/9366d6de1d7516e759e3110da1118c430db2c1ed33f27f9c67fad14d30cb/capstone-6.0.0a4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4041ff19810d5a81582f8e33c0e39c6db0704945188c21945234c92e0900c465", size = 1895904 },
{ url = "https://files.pythonhosted.org/packages/86/2a/e16249fd0ca2464ad6696d826598015f3e0ee1b8f9deec8b7076f34a8b85/capstone-6.0.0a4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:32fce2e387182a65c2a3bcf4d1e13a5410c45f56f73247777ba0bdea0b365b50", size = 1961922 },
{ url = "https://files.pythonhosted.org/packages/c2/f6/e2c85f0e37321bcc6156bc2ac91dbb9ce403d0832c6c4fca65122d9f1971/capstone-6.0.0a4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805b5a908525d17bb92922316bbeeaba571758f283085bd7d98219f4083761c4", size = 2279893 },
{ url = "https://files.pythonhosted.org/packages/12/13/0f8f7b88611a430e576142976a05d4a2581fbc64617cc3b2070b668ce1b0/capstone-6.0.0a4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f43a8d9d0c483a6b1540829acf47488cd1d2e0de7b5e6a92084234f37461932", size = 2321918 },
{ url = "https://files.pythonhosted.org/packages/00/25/b253ae59c03e5bc33c3cad30137216f0d9a482a80e68eb77492d1a6a2903/capstone-6.0.0a4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d8ebbc57e5437c5345d101c1076d5faff50e21b4c3c1caa0738c6372deaeaf2", size = 2289664 },
{ url = "https://files.pythonhosted.org/packages/b3/7f/2d8a702508d0b19e572bcd10a09a93b0c73caaecea106183b00b9cdcbb1b/capstone-6.0.0a4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a04bb61d769141afd7639b121ff47c9e0b20362f33780083c2f05f3446ee5b58", size = 2312542 },
{ url = "https://files.pythonhosted.org/packages/da/58/fc08a41ce1483b83434cbe9e739bfa1f765bc8a2e27ff4c651c3e4a63bc4/capstone-6.0.0a4-cp313-cp313-win_amd64.whl", hash = "sha256:8c38b86e7b4a9752643d470676ba8996bbdd8e2e7a22eca52485292c439ff93f", size = 2252117 },
{ url = "https://files.pythonhosted.org/packages/13/6d/e7cc611c59f33ab0114827e2c10a2f815a498e669ecaca0235f680c30e54/capstone-6.0.0a4-cp313-cp313-win_arm64.whl", hash = "sha256:8864fac943ac1eb18e051ce20f99ff614adb707ef606df591c3cfaf414dff99d", size = 2210983 },
]
[[package]]
@ -1226,7 +1252,7 @@ tests = [
[package.metadata]
requires-dist = [
{ name = "capstone", specifier = ">=5.0.3,<6" },
{ name = "capstone", specifier = "==6.0.0a4" },
{ name = "gnureadline", marker = "sys_platform != 'win32' and extra == 'lldb'", specifier = ">=8.2.10,<9" },
{ name = "ipython", specifier = ">=8.27.0,<9" },
{ name = "pt", git = "https://github.com/martinradev/gdb-pt-dump?rev=50227bda0b6332e94027f811a15879588de6d5cb" },

Loading…
Cancel
Save