802798ce3c
git-subtree-dir: external/capstone git-subtree-split: e46f64fadb351e9ecd05264fab26f2772feb0994
1833 lines
57 KiB
Python
Executable File
1833 lines
57 KiB
Python
Executable File
# Capstone Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
|
|
import os
|
|
import sys
|
|
|
|
__all__ = [
|
|
"Cs",
|
|
"CsInsn",
|
|
"cs_disasm_quick",
|
|
"cs_disasm_lite",
|
|
"cs_version",
|
|
"cs_support",
|
|
"version_bind",
|
|
"debug",
|
|
"CS_API_MAJOR",
|
|
"CS_API_MINOR",
|
|
"CS_VERSION_MAJOR",
|
|
"CS_VERSION_MINOR",
|
|
"CS_VERSION_EXTRA",
|
|
"CS_ARCH_ARM",
|
|
"CS_ARCH_AARCH64",
|
|
"CS_ARCH_MIPS",
|
|
"CS_ARCH_X86",
|
|
"CS_ARCH_PPC",
|
|
"CS_ARCH_SPARC",
|
|
"CS_ARCH_SYSTEMZ",
|
|
"CS_ARCH_XCORE",
|
|
"CS_ARCH_M68K",
|
|
"CS_ARCH_TMS320C64X",
|
|
"CS_ARCH_M680X",
|
|
"CS_ARCH_EVM",
|
|
"CS_ARCH_MOS65XX",
|
|
"CS_ARCH_WASM",
|
|
"CS_ARCH_BPF",
|
|
"CS_ARCH_RISCV",
|
|
"CS_ARCH_SH",
|
|
"CS_ARCH_TRICORE",
|
|
"CS_ARCH_ALPHA",
|
|
"CS_ARCH_HPPA",
|
|
"CS_ARCH_LOONGARCH",
|
|
"CS_ARCH_XTENSA",
|
|
"CS_ARCH_ARC",
|
|
"CS_ARCH_ALL",
|
|
"CS_MODE_LITTLE_ENDIAN",
|
|
"CS_MODE_BIG_ENDIAN",
|
|
"CS_MODE_16",
|
|
"CS_MODE_32",
|
|
"CS_MODE_64",
|
|
"CS_MODE_ARM",
|
|
"CS_MODE_THUMB",
|
|
"CS_MODE_MCLASS",
|
|
"CS_MODE_V8",
|
|
"CS_MODE_APPLE_PROPRIETARY",
|
|
"CS_MODE_V9",
|
|
"CS_MODE_QPX",
|
|
"CS_MODE_SPE",
|
|
"CS_MODE_BOOKE",
|
|
"CS_MODE_PS",
|
|
"CS_MODE_MIPS16",
|
|
"CS_MODE_MIPS32",
|
|
"CS_MODE_MIPS64",
|
|
"CS_MODE_MICRO",
|
|
"CS_MODE_MIPS1",
|
|
"CS_MODE_MIPS2",
|
|
"CS_MODE_MIPS32R2",
|
|
"CS_MODE_MIPS32R3",
|
|
"CS_MODE_MIPS32R5",
|
|
"CS_MODE_MIPS32R6",
|
|
"CS_MODE_MIPS3",
|
|
"CS_MODE_MIPS4",
|
|
"CS_MODE_MIPS5",
|
|
"CS_MODE_MIPS64R2",
|
|
"CS_MODE_MIPS64R3",
|
|
"CS_MODE_MIPS64R5",
|
|
"CS_MODE_MIPS64R6",
|
|
"CS_MODE_OCTEON",
|
|
"CS_MODE_OCTEONP",
|
|
"CS_MODE_NANOMIPS",
|
|
"CS_MODE_NMS1",
|
|
"CS_MODE_I7200",
|
|
"CS_MODE_MIPS_NOFLOAT",
|
|
"CS_MODE_MIPS_PTR64",
|
|
"CS_MODE_MICRO32R3",
|
|
"CS_MODE_MICRO32R6",
|
|
"CS_MODE_M68K_000",
|
|
"CS_MODE_M68K_010",
|
|
"CS_MODE_M68K_020",
|
|
"CS_MODE_M68K_030",
|
|
"CS_MODE_M68K_040",
|
|
"CS_MODE_M68K_060",
|
|
"CS_MODE_M68K_CPU32",
|
|
"CS_MODE_M680X_6301",
|
|
"CS_MODE_M680X_6309",
|
|
"CS_MODE_M680X_6800",
|
|
"CS_MODE_M680X_6801",
|
|
"CS_MODE_M680X_6805",
|
|
"CS_MODE_M680X_6808",
|
|
"CS_MODE_M680X_6809",
|
|
"CS_MODE_M680X_6811",
|
|
"CS_MODE_M680X_CPU12",
|
|
"CS_MODE_M680X_HCS08",
|
|
"CS_MODE_M680X_RS08",
|
|
"CS_MODE_M680X_HCS12X",
|
|
"CS_MODE_BPF_CLASSIC",
|
|
"CS_MODE_BPF_EXTENDED",
|
|
"CS_MODE_RISCV32",
|
|
"CS_MODE_RISCV64",
|
|
"CS_MODE_RISCV_C",
|
|
"CS_MODE_RISCV_FD",
|
|
"CS_MODE_RISCV_F",
|
|
"CS_MODE_RISCV_D",
|
|
"CS_MODE_RISCV_V",
|
|
"CS_MODE_RISCV_ZFINX",
|
|
"CS_MODE_RISCV_ZDINX",
|
|
"CS_MODE_RISCV_ZHINX",
|
|
"CS_MODE_RISCV_ZHINXMIN",
|
|
"CS_MODE_RISCV_ZCMP_ZCMT_ZCE",
|
|
"CS_MODE_RISCV_ZCE",
|
|
"CS_MODE_RISCV_ZCMP",
|
|
"CS_MODE_RISCV_ZCMT",
|
|
"CS_MODE_RISCV_ZICFISS",
|
|
"CS_MODE_RISCV_EXPERIMENTAL_ZICFISS",
|
|
"CS_MODE_RISCV_E",
|
|
"CS_MODE_RISCV_A",
|
|
"CS_MODE_RISCV_COREV",
|
|
"CS_MODE_RISCV_XCVALU",
|
|
"CS_MODE_RISCV_XCVBI",
|
|
"CS_MODE_RISCV_XCVBITMANIP",
|
|
"CS_MODE_RISCV_XCVELW",
|
|
"CS_MODE_RISCV_XCVMAC",
|
|
"CS_MODE_RISCV_XCVMEM",
|
|
"CS_MODE_RISCV_XCVSIMD",
|
|
"CS_MODE_RISCV_THEAD",
|
|
"CS_MODE_RISCV_XTHEADBA",
|
|
"CS_MODE_RISCV_XTHEADBS",
|
|
"CS_MODE_RISCV_XTHEADCMO",
|
|
"CS_MODE_RISCV_XTHEADCONDMOV",
|
|
"CS_MODE_RISCV_XTHEADFMEMIDX",
|
|
"CS_MODE_RISCV_XTHEADMAC",
|
|
"CS_MODE_RISCV_XTHEADMEMIDX",
|
|
"CS_MODE_RISCV_XTHEADMEMPAIR",
|
|
"CS_MODE_RISCV_XTHEADSYNC",
|
|
"CS_MODE_RISCV_XTHEADVDOT",
|
|
"CS_MODE_RISCV_SIFIVE",
|
|
"CS_MODE_RISCV_XSFVCP",
|
|
"CS_MODE_RISCV_XSFVFNRCLIPXFQF",
|
|
"CS_MODE_RISCV_XSFVFWMACCQQQ",
|
|
"CS_MODE_RISCV_XSFVQMACCDOD",
|
|
"CS_MODE_RISCV_XSFVQMACCQOQ",
|
|
"CS_MODE_RISCV_BITMANIP",
|
|
"CS_MODE_RISCV_ZBA",
|
|
"CS_MODE_RISCV_ZBB",
|
|
"CS_MODE_RISCV_ZBC",
|
|
"CS_MODE_RISCV_ZBKB",
|
|
"CS_MODE_RISCV_ZBKC",
|
|
"CS_MODE_RISCV_ZBKX",
|
|
"CS_MODE_RISCV_ZBS",
|
|
"CS_MODE_MOS65XX_6502",
|
|
"CS_MODE_MOS65XX_65C02",
|
|
"CS_MODE_MOS65XX_W65C02",
|
|
"CS_MODE_MOS65XX_65816",
|
|
"CS_MODE_MOS65XX_65816_LONG_M",
|
|
"CS_MODE_MOS65XX_65816_LONG_X",
|
|
"CS_MODE_MOS65XX_65816_LONG_MX",
|
|
"CS_MODE_SH2",
|
|
"CS_MODE_SH2A",
|
|
"CS_MODE_SH3",
|
|
"CS_MODE_SH4",
|
|
"CS_MODE_SH4A",
|
|
"CS_MODE_SHFPU",
|
|
"CS_MODE_SHDSP",
|
|
"CS_MODE_TRICORE_110",
|
|
"CS_MODE_TRICORE_120",
|
|
"CS_MODE_TRICORE_130",
|
|
"CS_MODE_TRICORE_131",
|
|
"CS_MODE_TRICORE_160",
|
|
"CS_MODE_TRICORE_161",
|
|
"CS_MODE_TRICORE_162",
|
|
"CS_MODE_TRICORE_180",
|
|
"CS_MODE_HPPA_11",
|
|
"CS_MODE_HPPA_20",
|
|
"CS_MODE_HPPA_20W",
|
|
"CS_MODE_LOONGARCH32",
|
|
"CS_MODE_LOONGARCH64",
|
|
"CS_MODE_SYSTEMZ_ARCH8",
|
|
"CS_MODE_SYSTEMZ_ARCH9",
|
|
"CS_MODE_SYSTEMZ_ARCH10",
|
|
"CS_MODE_SYSTEMZ_ARCH11",
|
|
"CS_MODE_SYSTEMZ_ARCH12",
|
|
"CS_MODE_SYSTEMZ_ARCH13",
|
|
"CS_MODE_SYSTEMZ_ARCH14",
|
|
"CS_MODE_SYSTEMZ_Z10",
|
|
"CS_MODE_SYSTEMZ_Z196",
|
|
"CS_MODE_SYSTEMZ_ZEC12",
|
|
"CS_MODE_SYSTEMZ_Z13",
|
|
"CS_MODE_SYSTEMZ_Z14",
|
|
"CS_MODE_SYSTEMZ_Z15",
|
|
"CS_MODE_SYSTEMZ_Z16",
|
|
"CS_MODE_SYSTEMZ_GENERIC",
|
|
"CS_OPT_SYNTAX",
|
|
"CS_OPT_SYNTAX_DEFAULT",
|
|
"CS_OPT_SYNTAX_INTEL",
|
|
"CS_OPT_SYNTAX_ATT",
|
|
"CS_OPT_SYNTAX_NOREGNAME",
|
|
"CS_OPT_SYNTAX_MASM",
|
|
"CS_OPT_SYNTAX_MOTOROLA",
|
|
"CS_OPT_SYNTAX_CS_REG_ALIAS",
|
|
"CS_OPT_SYNTAX_NO_DOLLAR",
|
|
"CS_OPT_SYNTAX_NO_ALIAS_TEXT",
|
|
"CS_OPT_SYNTAX_NO_ALIAS_TEXT_COMPRESSED",
|
|
"CS_OPT_DETAIL",
|
|
"CS_OPT_DETAIL_REAL",
|
|
"CS_OPT_MODE",
|
|
"CS_OPT_ON",
|
|
"CS_OPT_OFF",
|
|
"CS_OPT_INVALID",
|
|
"CS_OPT_MEM",
|
|
"CS_OPT_SKIPDATA",
|
|
"CS_OPT_SKIPDATA_SETUP",
|
|
"CS_OPT_MNEMONIC",
|
|
"CS_OPT_UNSIGNED",
|
|
"CS_OPT_ONLY_OFFSET_BRANCH",
|
|
"CS_ERR_OK",
|
|
"CS_ERR_MEM",
|
|
"CS_ERR_ARCH",
|
|
"CS_ERR_HANDLE",
|
|
"CS_ERR_CSH",
|
|
"CS_ERR_MODE",
|
|
"CS_ERR_OPTION",
|
|
"CS_ERR_DETAIL",
|
|
"CS_ERR_VERSION",
|
|
"CS_ERR_MEMSETUP",
|
|
"CS_ERR_DIET",
|
|
"CS_ERR_SKIPDATA",
|
|
"CS_ERR_X86_ATT",
|
|
"CS_ERR_X86_INTEL",
|
|
"CS_ERR_X86_MASM",
|
|
"CS_SUPPORT_DIET",
|
|
"CS_SUPPORT_X86_REDUCE",
|
|
"CS_SKIPDATA_CALLBACK",
|
|
"CS_OP_INVALID",
|
|
"CS_OP_REG",
|
|
"CS_OP_IMM",
|
|
"CS_OP_FP",
|
|
"CS_OP_PRED",
|
|
"CS_OP_RESERVED_5",
|
|
"CS_OP_RESERVED_6",
|
|
"CS_OP_RESERVED_7",
|
|
"CS_OP_RESERVED_8",
|
|
"CS_OP_RESERVED_9",
|
|
"CS_OP_RESERVED_10",
|
|
"CS_OP_RESERVED_11",
|
|
"CS_OP_RESERVED_12",
|
|
"CS_OP_RESERVED_13",
|
|
"CS_OP_RESERVED_14",
|
|
"CS_OP_RESERVED_15",
|
|
"CS_OP_SPECIAL",
|
|
"CS_OP_MEM",
|
|
"CS_OP_MEM_REG",
|
|
"CS_OP_MEM_IMM",
|
|
"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",
|
|
"CS_AC_INVALID",
|
|
"CS_AC_READ",
|
|
"CS_AC_WRITE",
|
|
"CsError",
|
|
"__version__",
|
|
]
|
|
|
|
UINT8_MAX = 0xFF
|
|
UINT16_MAX = 0xFFFF
|
|
|
|
# Capstone C interface
|
|
|
|
# API version
|
|
CS_API_MAJOR = 6
|
|
CS_API_MINOR = 0
|
|
|
|
# Package version
|
|
CS_VERSION_MAJOR = CS_API_MAJOR
|
|
CS_VERSION_MINOR = CS_API_MINOR
|
|
CS_VERSION_EXTRA = 0
|
|
|
|
__version__ = "%u.%u.%u" % (CS_VERSION_MAJOR, CS_VERSION_MINOR, CS_VERSION_EXTRA)
|
|
|
|
# architectures
|
|
CS_ARCH_ARM = 0
|
|
CS_ARCH_AARCH64 = 1
|
|
CS_ARCH_SYSTEMZ = 2
|
|
CS_ARCH_MIPS = 3
|
|
CS_ARCH_X86 = 4
|
|
CS_ARCH_PPC = 5
|
|
CS_ARCH_SPARC = 6
|
|
CS_ARCH_XCORE = 7
|
|
CS_ARCH_M68K = 8
|
|
CS_ARCH_TMS320C64X = 9
|
|
CS_ARCH_M680X = 10
|
|
CS_ARCH_EVM = 11
|
|
CS_ARCH_MOS65XX = 12
|
|
CS_ARCH_WASM = 13
|
|
CS_ARCH_BPF = 14
|
|
CS_ARCH_RISCV = 15
|
|
CS_ARCH_SH = 16
|
|
CS_ARCH_TRICORE = 17
|
|
CS_ARCH_ALPHA = 18
|
|
CS_ARCH_HPPA = 19
|
|
CS_ARCH_LOONGARCH = 20
|
|
CS_ARCH_XTENSA = 21
|
|
CS_ARCH_ARC = 22
|
|
CS_ARCH_MAX = 22
|
|
CS_ARCH_ALL = 0xFFFF
|
|
|
|
CS_MODE_AARCH64_ISA_BITS = 0x00FFFFF8
|
|
CS_MODE_VENDOR_AARCH64_BIT0 = 30
|
|
|
|
# disasm mode
|
|
CS_MODE_LITTLE_ENDIAN = 0 # little-endian mode (default mode)
|
|
CS_MODE_ARM = 0 # ARM mode
|
|
CS_MODE_16 = 1 << 1 # 16-bit mode (for X86)
|
|
CS_MODE_32 = 1 << 2 # 32-bit mode (for X86)
|
|
CS_MODE_64 = 1 << 3 # 64-bit mode (for X86, PPC)
|
|
CS_MODE_THUMB = 1 << 4 # ARM's Thumb mode, including Thumb-2
|
|
CS_MODE_MCLASS = 1 << 5 # ARM's Cortex-M series
|
|
CS_MODE_V8 = 1 << 6 # ARMv8 A32 encodings for ARM
|
|
CS_MODE_APPLE_PROPRIETARY = (
|
|
1 << CS_MODE_VENDOR_AARCH64_BIT0
|
|
) # Apple proprietary AArch64 instructions like AMX, MUL53, and others.
|
|
CS_MODE_MICRO = 1 << 4 # MicroMips mode (MIPS architecture)
|
|
CS_MODE_MIPS3 = 1 << 5 # Mips III ISA
|
|
CS_MODE_MIPS32R6 = 1 << 6 # Mips32r6 ISA
|
|
CS_MODE_MIPS2 = 1 << 7 # Mips II ISA
|
|
CS_MODE_V9 = 1 << 4 # Sparc V9 mode (for Sparc)
|
|
CS_MODE_QPX = 1 << 4 # Quad Processing eXtensions mode (PPC)
|
|
CS_MODE_SPE = 1 << 5 # Signal Processing Engine mode (PPC)
|
|
CS_MODE_BOOKE = 1 << 6 # Book-E mode (PPC)
|
|
CS_MODE_PS = 1 << 7 # Paired-singles mode (PPC)
|
|
CS_MODE_AIX_OS = 1 << 8 # PowerPC AIX-OS
|
|
CS_MODE_PWR7 = 1 << 9 # Power 7
|
|
CS_MODE_PWR8 = 1 << 10 # Power 8
|
|
CS_MODE_PWR9 = 1 << 11 # Power 9
|
|
CS_MODE_PWR10 = 1 << 12 # Power 10
|
|
CS_MODE_PPC_ISA_FUTURE = 1 << 13 # Power ISA Future
|
|
CS_MODE_MODERN_AIX_AS = 1 << 14 # PowerPC AIX-OS with modern assembly
|
|
CS_MODE_MSYNC = (
|
|
1 << 15
|
|
) # PowerPC Has only the msync instruction instead of sync. Implies BOOKE
|
|
CS_MODE_M68K_000 = 1 << 1 # M68K 68000 mode
|
|
CS_MODE_M68K_010 = 1 << 2 # M68K 68010 mode
|
|
CS_MODE_M68K_020 = 1 << 3 # M68K 68020 mode
|
|
CS_MODE_M68K_030 = 1 << 4 # M68K 68030 mode
|
|
CS_MODE_M68K_040 = 1 << 5 # M68K 68040 mode
|
|
CS_MODE_M68K_060 = 1 << 6 # M68K 68060 mode
|
|
CS_MODE_M68K_CPU32 = 1 << 7
|
|
CS_MODE_BIG_ENDIAN = 1 << 31 # big-endian mode
|
|
CS_MODE_MIPS16 = CS_MODE_16 # Generic mips16
|
|
CS_MODE_MIPS32 = CS_MODE_32 # Generic mips32
|
|
CS_MODE_MIPS64 = CS_MODE_64 # Generic mips64
|
|
CS_MODE_MICRO = 1 << 4 # microMips
|
|
CS_MODE_MIPS1 = 1 << 5 # Mips I ISA Support
|
|
CS_MODE_MIPS2 = 1 << 6 # Mips II ISA Support
|
|
CS_MODE_MIPS32R2 = 1 << 7 # Mips32r2 ISA Support
|
|
CS_MODE_MIPS32R3 = 1 << 8 # Mips32r3 ISA Support
|
|
CS_MODE_MIPS32R5 = 1 << 9 # Mips32r5 ISA Support
|
|
CS_MODE_MIPS32R6 = 1 << 10 # Mips32r6 ISA Support
|
|
CS_MODE_MIPS3 = 1 << 11 # MIPS III ISA Support
|
|
CS_MODE_MIPS4 = 1 << 12 # MIPS IV ISA Support
|
|
CS_MODE_MIPS5 = 1 << 13 # MIPS V ISA Support
|
|
CS_MODE_MIPS64R2 = 1 << 14 # Mips64r2 ISA Support
|
|
CS_MODE_MIPS64R3 = 1 << 15 # Mips64r3 ISA Support
|
|
CS_MODE_MIPS64R5 = 1 << 16 # Mips64r5 ISA Support
|
|
CS_MODE_MIPS64R6 = 1 << 17 # Mips64r6 ISA Support
|
|
CS_MODE_OCTEON = 1 << 18 # Octeon cnMIPS Support
|
|
CS_MODE_OCTEONP = 1 << 19 # Octeon+ cnMIPS Support
|
|
CS_MODE_NANOMIPS = 1 << 20 # Generic nanomips
|
|
CS_MODE_NMS1 = (1 << 21) | CS_MODE_NANOMIPS # nanoMips NMS1
|
|
CS_MODE_I7200 = (1 << 22) | CS_MODE_NANOMIPS # nanoMips I7200
|
|
CS_MODE_MIPS_NOFLOAT = 1 << 23 # Disable floating points ops
|
|
CS_MODE_MIPS_PTR64 = 1 << 24 # Mips pointers are 64-bit
|
|
CS_MODE_MICRO32R3 = CS_MODE_MICRO | CS_MODE_MIPS32R3 # microMips32r3
|
|
CS_MODE_MICRO32R6 = CS_MODE_MICRO | CS_MODE_MIPS32R6 # microMips32r6
|
|
CS_MODE_M680X_6301 = 1 << 1 # M680X HD6301/3 mode
|
|
CS_MODE_M680X_6309 = 1 << 2 # M680X HD6309 mode
|
|
CS_MODE_M680X_6800 = 1 << 3 # M680X M6800/2 mode
|
|
CS_MODE_M680X_6801 = 1 << 4 # M680X M6801/3 mode
|
|
CS_MODE_M680X_6805 = 1 << 5 # M680X M6805 mode
|
|
CS_MODE_M680X_6808 = 1 << 6 # M680X M68HC08 mode
|
|
CS_MODE_M680X_6809 = 1 << 7 # M680X M6809 mode
|
|
CS_MODE_M680X_6811 = 1 << 8 # M680X M68HC11 mode
|
|
CS_MODE_M680X_CPU12 = 1 << 9 # M680X CPU12 mode
|
|
CS_MODE_M680X_HCS08 = 1 << 10 # M680X HCS08 mode
|
|
CS_MODE_M680X_RS08 = 1 << 11 # M680X RS08 mode
|
|
CS_MODE_M680X_HCS12X = 1 << 12 # M680X HCS12X mode
|
|
CS_MODE_BPF_CLASSIC = 0 # Classic BPF mode (default)
|
|
CS_MODE_BPF_EXTENDED = 1 << 0 # Extended BPF mode
|
|
CS_MODE_RISCV32 = 1 << 0 # RISCV32 mode
|
|
CS_MODE_RISCV64 = 1 << 1 # RISCV64 mode
|
|
CS_MODE_RISCV_C = 1 << 2 # RISCV compressed instructure mode
|
|
CS_MODE_RISCV_FD = 1 << 3
|
|
CS_MODE_RISCV_F = CS_MODE_RISCV_FD
|
|
CS_MODE_RISCV_D = CS_MODE_RISCV_FD
|
|
CS_MODE_RISCV_V = 1 << 4
|
|
CS_MODE_RISCV_ZFINX = 1 << 5
|
|
CS_MODE_RISCV_ZDINX = CS_MODE_RISCV_ZFINX
|
|
CS_MODE_RISCV_ZHINX = CS_MODE_RISCV_ZFINX
|
|
CS_MODE_RISCV_ZHINXMIN = CS_MODE_RISCV_ZFINX
|
|
CS_MODE_RISCV_ZCMP_ZCMT_ZCE = 1 << 6
|
|
CS_MODE_RISCV_ZCE = CS_MODE_RISCV_ZCMP_ZCMT_ZCE
|
|
CS_MODE_RISCV_ZCMP = CS_MODE_RISCV_ZCMP_ZCMT_ZCE
|
|
CS_MODE_RISCV_ZCMT = CS_MODE_RISCV_ZCMP_ZCMT_ZCE
|
|
CS_MODE_RISCV_ZICFISS = 1 << 7
|
|
CS_MODE_RISCV_EXPERIMENTAL_ZICFISS = CS_MODE_RISCV_ZICFISS
|
|
CS_MODE_RISCV_E = 1 << 8
|
|
CS_MODE_RISCV_A = 1 << 9
|
|
CS_MODE_RISCV_COREV = 1 << 10
|
|
CS_MODE_RISCV_XCVALU = CS_MODE_RISCV_COREV
|
|
CS_MODE_RISCV_XCVBI = CS_MODE_RISCV_COREV
|
|
CS_MODE_RISCV_XCVBITMANIP = CS_MODE_RISCV_COREV
|
|
CS_MODE_RISCV_XCVELW = CS_MODE_RISCV_COREV
|
|
CS_MODE_RISCV_XCVMAC = CS_MODE_RISCV_COREV
|
|
CS_MODE_RISCV_XCVMEM = CS_MODE_RISCV_COREV
|
|
CS_MODE_RISCV_XCVSIMD = CS_MODE_RISCV_COREV
|
|
CS_MODE_RISCV_THEAD = 1 << 11
|
|
CS_MODE_RISCV_XTHEADBA = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADBS = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADCMO = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADCONDMOV = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADFMEMIDX = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADMAC = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADMEMIDX = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADMEMPAIR = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADSYNC = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_XTHEADVDOT = CS_MODE_RISCV_THEAD
|
|
CS_MODE_RISCV_SIFIVE = 1 << 12
|
|
CS_MODE_RISCV_XSFVCP = CS_MODE_RISCV_SIFIVE
|
|
CS_MODE_RISCV_XSFVFNRCLIPXFQF = CS_MODE_RISCV_SIFIVE
|
|
CS_MODE_RISCV_XSFVFWMACCQQQ = CS_MODE_RISCV_SIFIVE
|
|
CS_MODE_RISCV_XSFVQMACCDOD = CS_MODE_RISCV_SIFIVE
|
|
CS_MODE_RISCV_XSFVQMACCQOQ = CS_MODE_RISCV_SIFIVE
|
|
CS_MODE_RISCV_BITMANIP = 1 << 13
|
|
CS_MODE_RISCV_ZBA = 1 << 14
|
|
CS_MODE_RISCV_ZBB = 1 << 15
|
|
CS_MODE_RISCV_ZBC = 1 << 16
|
|
CS_MODE_RISCV_ZBKB = 1 << 17
|
|
CS_MODE_RISCV_ZBKC = 1 << 18
|
|
CS_MODE_RISCV_ZBKX = 1 << 19
|
|
CS_MODE_RISCV_ZBS = 1 << 20
|
|
CS_MODE_MOS65XX_6502 = 1 << 1 # MOS65XXX MOS 6502
|
|
CS_MODE_MOS65XX_65C02 = 1 << 2 # MOS65XXX WDC 65c02
|
|
CS_MODE_MOS65XX_W65C02 = 1 << 3 # MOS65XXX WDC W65c02
|
|
CS_MODE_MOS65XX_65816 = 1 << 4 # MOS65XXX WDC 65816, 8-bit m/x
|
|
CS_MODE_MOS65XX_65816_LONG_M = 1 << 5 # MOS65XXX WDC 65816, 16-bit m, 8-bit x
|
|
CS_MODE_MOS65XX_65816_LONG_X = 1 << 6 # MOS65XXX WDC 65816, 8-bit m, 16-bit x
|
|
CS_MODE_MOS65XX_65816_LONG_MX = (
|
|
CS_MODE_MOS65XX_65816_LONG_M | CS_MODE_MOS65XX_65816_LONG_X
|
|
)
|
|
CS_MODE_SH2 = 1 << 1 # SH2
|
|
CS_MODE_SH2A = 1 << 2 # SH2A
|
|
CS_MODE_SH3 = 1 << 3 # SH3
|
|
CS_MODE_SH4 = 1 << 4 # SH4
|
|
CS_MODE_SH4A = 1 << 5 # SH4A
|
|
CS_MODE_SHFPU = 1 << 6 # w/ FPU
|
|
CS_MODE_SHDSP = 1 << 7 # w/ DSP
|
|
CS_MODE_TRICORE_110 = 1 << 1 # Tricore 1.1
|
|
CS_MODE_TRICORE_120 = 1 << 2 # Tricore 1.2
|
|
CS_MODE_TRICORE_130 = 1 << 3 # Tricore 1.3
|
|
CS_MODE_TRICORE_131 = 1 << 4 # Tricore 1.3.1
|
|
CS_MODE_TRICORE_160 = 1 << 5 # Tricore 1.6
|
|
CS_MODE_TRICORE_161 = 1 << 6 # Tricore 1.6.1
|
|
CS_MODE_TRICORE_162 = 1 << 7 # Tricore 1.6.2
|
|
CS_MODE_TRICORE_180 = 1 << 8 # Tricore 1.8.0
|
|
CS_MODE_HPPA_11 = 1 << 1 # HPPA 1.1
|
|
CS_MODE_HPPA_20 = 1 << 2 # HPPA 2.0
|
|
CS_MODE_HPPA_20W = CS_MODE_HPPA_20 | (1 << 3) # HPPA 2.0 wide
|
|
CS_MODE_LOONGARCH32 = 1 << 0
|
|
CS_MODE_LOONGARCH64 = 1 << 1
|
|
CS_MODE_SYSTEMZ_ARCH8 = 1 << 1
|
|
CS_MODE_SYSTEMZ_ARCH9 = 1 << 2
|
|
CS_MODE_SYSTEMZ_ARCH10 = 1 << 3
|
|
CS_MODE_SYSTEMZ_ARCH11 = 1 << 4
|
|
CS_MODE_SYSTEMZ_ARCH12 = 1 << 5
|
|
CS_MODE_SYSTEMZ_ARCH13 = 1 << 6
|
|
CS_MODE_SYSTEMZ_ARCH14 = 1 << 7
|
|
CS_MODE_SYSTEMZ_Z10 = 1 << 8
|
|
CS_MODE_SYSTEMZ_Z196 = 1 << 9
|
|
CS_MODE_SYSTEMZ_ZEC12 = 1 << 10
|
|
CS_MODE_SYSTEMZ_Z13 = 1 << 11
|
|
CS_MODE_SYSTEMZ_Z14 = 1 << 12
|
|
CS_MODE_SYSTEMZ_Z15 = 1 << 13
|
|
CS_MODE_SYSTEMZ_Z16 = 1 << 14
|
|
CS_MODE_SYSTEMZ_GENERIC = 1 << 15
|
|
|
|
# Capstone option type
|
|
CS_OPT_INVALID = 0 # No option specified
|
|
CS_OPT_SYNTAX = 1 # Intel X86 asm syntax (CS_ARCH_X86 arch)
|
|
CS_OPT_DETAIL = 2 # Break down instruction structure into details
|
|
CS_OPT_MODE = 3 # Change engine's mode at run-time
|
|
CS_OPT_MEM = 4 # Change engine's mode at run-time
|
|
CS_OPT_SKIPDATA = 5 # Skip data when disassembling
|
|
CS_OPT_SKIPDATA_SETUP = 6 # Setup user-defined function for SKIPDATA option
|
|
CS_OPT_MNEMONIC = 7 # Customize instruction mnemonic
|
|
CS_OPT_UNSIGNED = 8 # Print immediate in unsigned form
|
|
CS_OPT_ONLY_OFFSET_BRANCH = 9 # ARM, prints branch immediates without offset.
|
|
|
|
# Capstone option value
|
|
CS_OPT_OFF = 0 # Turn OFF an option - default option of CS_OPT_DETAIL
|
|
CS_OPT_ON = 1 << 0 # Turn ON an option (CS_OPT_DETAIL)
|
|
|
|
# Common instruction operand types - to be consistent across all architectures.
|
|
CS_OP_INVALID = 0 # uninitialized/invalid operand.
|
|
CS_OP_REG = 1 # Register operand.
|
|
CS_OP_IMM = 2 # Immediate operand.
|
|
CS_OP_FP = 3 # Floating-Point operand.
|
|
CS_OP_PRED = 4 # Predicate operand.
|
|
CS_OP_RESERVED_5 = 5
|
|
CS_OP_RESERVED_6 = 6
|
|
CS_OP_RESERVED_7 = 7
|
|
CS_OP_RESERVED_8 = 8
|
|
CS_OP_RESERVED_9 = 9
|
|
CS_OP_RESERVED_10 = 10
|
|
CS_OP_RESERVED_11 = 11
|
|
CS_OP_RESERVED_12 = 12
|
|
CS_OP_RESERVED_13 = 13
|
|
CS_OP_RESERVED_14 = 14
|
|
CS_OP_RESERVED_15 = 15
|
|
CS_OP_SPECIAL = 0x10 # Special operands from archs
|
|
CS_OP_MEM = 0x80 # Memory operand. Can be ORed with another operand type.
|
|
CS_OP_MEM_REG = CS_OP_MEM | CS_OP_REG # Memory referencing register operand.
|
|
CS_OP_MEM_IMM = CS_OP_MEM | CS_OP_IMM # Memory referencing immediate operand.
|
|
|
|
# Common instruction groups - to be consistent across all architectures.
|
|
CS_GRP_INVALID = 0 # uninitialized/invalid group.
|
|
CS_GRP_JUMP = 1 # all jump instructions (conditional+direct+indirect jumps)
|
|
CS_GRP_CALL = 2 # all call instructions
|
|
CS_GRP_RET = 3 # all return instructions
|
|
CS_GRP_INT = 4 # all interrupt instructions (int+syscall)
|
|
CS_GRP_IRET = 5 # all interrupt return instructions
|
|
CS_GRP_PRIVILEGE = 6 # all privileged instructions
|
|
CS_GRP_BRANCH_RELATIVE = 7 # all relative branching instructions
|
|
|
|
# Access types for instruction operands.
|
|
CS_AC_INVALID = 0 # Invalid/uninitialized access type.
|
|
CS_AC_READ = 1 << 0 # Operand that is read from.
|
|
CS_AC_WRITE = 1 << 1 # Operand that is written to.
|
|
CS_AC_READ_WRITE = CS_AC_READ | CS_AC_WRITE
|
|
|
|
# Capstone syntax value
|
|
CS_OPT_SYNTAX_DEFAULT = (
|
|
1 << 1
|
|
) # Default assembly syntax of all platforms (CS_OPT_SYNTAX)
|
|
CS_OPT_SYNTAX_INTEL = (
|
|
1 << 2
|
|
) # Intel X86 asm syntax - default syntax on X86 (CS_OPT_SYNTAX, CS_ARCH_X86)
|
|
CS_OPT_SYNTAX_ATT = 1 << 3 # ATT asm syntax (CS_OPT_SYNTAX, CS_ARCH_X86)
|
|
CS_OPT_SYNTAX_NOREGNAME = (
|
|
1 << 4
|
|
) # Asm syntax prints register name with only number - (CS_OPT_SYNTAX, CS_ARCH_PPC, CS_ARCH_ARM)
|
|
CS_OPT_SYNTAX_MASM = 1 << 5 # MASM syntax (CS_OPT_SYNTAX, CS_ARCH_X86)
|
|
CS_OPT_SYNTAX_MOTOROLA = 1 << 6 # MOS65XX use $ as hex prefix
|
|
CS_OPT_SYNTAX_CS_REG_ALIAS = (
|
|
1 << 7
|
|
) # Prints common register alias which are not defined in LLVM (ARM: r9 = sb etc.)
|
|
CS_OPT_SYNTAX_PERCENT = 1 << 8 # Prints the % in front of PPC registers.
|
|
CS_OPT_SYNTAX_NO_DOLLAR = (
|
|
1 << 9
|
|
) # Does not print the $ in front of Mips, LoongArch registers.
|
|
CS_OPT_SYNTAX_NO_ALIAS_TEXT = (
|
|
1 << 10
|
|
) # Does not print an instruction's alias test if the instruction is an alias
|
|
CS_OPT_SYNTAX_NO_ALIAS_TEXT_COMPRESSED = (
|
|
1 << 11
|
|
) # Like the one above it, but only supresses compressed instruction aliases
|
|
CS_OPT_DETAIL_REAL = (
|
|
1 << 1
|
|
) # If enabled, always sets the real instruction detail.Even if the instruction is an alias.
|
|
|
|
# Capstone error type
|
|
CS_ERR_OK = 0 # No error: everything was fine
|
|
CS_ERR_MEM = 1 # Out-Of-Memory error: cs_open(), cs_disasm()
|
|
CS_ERR_ARCH = 2 # Unsupported architecture: cs_open()
|
|
CS_ERR_HANDLE = 3 # Invalid handle: cs_op_count(), cs_op_index()
|
|
CS_ERR_CSH = 4 # Invalid csh argument: cs_close(), cs_errno(), cs_option()
|
|
CS_ERR_MODE = 5 # Invalid/unsupported mode: cs_open()
|
|
CS_ERR_OPTION = 6 # Invalid/unsupported option: cs_option()
|
|
CS_ERR_DETAIL = 7 # Invalid/unsupported option: cs_option()
|
|
CS_ERR_MEMSETUP = 8
|
|
CS_ERR_VERSION = 9 # Unsupported version (bindings)
|
|
CS_ERR_DIET = 10 # Information irrelevant in diet engine
|
|
CS_ERR_SKIPDATA = 11 # Access irrelevant data for "data" instruction in SKIPDATA mode
|
|
CS_ERR_X86_ATT = 12 # X86 AT&T syntax is unsupported (opt-out at compile time)
|
|
CS_ERR_X86_INTEL = 13 # X86 Intel syntax is unsupported (opt-out at compile time)
|
|
CS_ERR_X86_MASM = 14 # X86 Intel syntax is unsupported (opt-out at compile time)
|
|
|
|
# query id for cs_support()
|
|
CS_SUPPORT_DIET = CS_ARCH_ALL + 1
|
|
CS_SUPPORT_X86_REDUCE = CS_ARCH_ALL + 2
|
|
|
|
# Capstone reverse lookup
|
|
CS_AC = {v: k for k, v in locals().items() if k.startswith("CS_AC_")}
|
|
CS_ARCH = {v: k for k, v in locals().items() if k.startswith("CS_ARCH_")}
|
|
CS_ERR = {v: k for k, v in locals().items() if k.startswith("CS_ERR_")}
|
|
CS_GRP = {v: k for k, v in locals().items() if k.startswith("CS_GRP_")}
|
|
CS_MODE = {v: k for k, v in locals().items() if k.startswith("CS_MODE_")}
|
|
CS_OP = {v: k for k, v in locals().items() if k.startswith("CS_OP_")}
|
|
CS_OPT = {v: k for k, v in locals().items() if k.startswith("CS_OPT_")}
|
|
|
|
import ctypes, ctypes.util
|
|
from os.path import split, join, dirname
|
|
import sysconfig
|
|
from pathlib import PurePath
|
|
|
|
import inspect
|
|
|
|
if sys.version_info >= (3, 9):
|
|
import importlib.resources as resources
|
|
else:
|
|
import importlib_resources as resources
|
|
|
|
if not hasattr(sys.modules[__name__], "__file__"):
|
|
__file__ = inspect.getfile(inspect.currentframe())
|
|
|
|
mode = 0
|
|
if sys.platform == "darwin":
|
|
_lib = "libcapstone.dylib"
|
|
elif sys.platform in ("win32", "cygwin"):
|
|
_lib = "capstone.dll"
|
|
else:
|
|
_lib = "libcapstone.so"
|
|
mode = getattr(os, 'RTLD_DEEPBIND', 0)
|
|
|
|
_found = False
|
|
|
|
|
|
def _load_lib(path):
|
|
lib_file = join(path, _lib)
|
|
if os.path.exists(lib_file):
|
|
return ctypes.CDLL(lib_file, mode=mode)
|
|
else:
|
|
# if we're on linux, try again with .so.5 extension
|
|
if lib_file.endswith(".so"):
|
|
if os.path.exists(lib_file + ".{}".format(CS_VERSION_MAJOR)):
|
|
return ctypes.CDLL(
|
|
lib_file + ".{}".format(CS_VERSION_MAJOR),
|
|
mode=mode
|
|
)
|
|
return None
|
|
|
|
|
|
_cs = None
|
|
|
|
# Loading attempts, in order
|
|
# - user-provided environment variable
|
|
# - importlib.resources can get us the path to the local libraries
|
|
# - we can get the path to the local libraries by parsing our filename
|
|
# - global load
|
|
# - python's lib directory
|
|
# - last-gasp attempt at some hardcoded paths on darwin and linux
|
|
|
|
_path_list = [
|
|
os.getenv("LIBCAPSTONE_PATH", None),
|
|
str(resources.files(__name__) / "lib"),
|
|
join(split(__file__)[0], "lib"),
|
|
"",
|
|
sysconfig.get_path("platlib"),
|
|
"/usr/local/lib/" if sys.platform == "darwin" else "/usr/lib64",
|
|
]
|
|
|
|
for _path in _path_list:
|
|
if _path is None:
|
|
continue
|
|
_cs = _load_lib(_path)
|
|
if _cs is not None:
|
|
break
|
|
else:
|
|
raise ImportError("ERROR: fail to load the dynamic library.")
|
|
|
|
|
|
# low-level structure for C code
|
|
|
|
|
|
def copy_ctypes(src):
|
|
"""Returns a new ctypes object which is a bitwise copy of an existing one"""
|
|
dst = type(src)()
|
|
ctypes.memmove(ctypes.byref(dst), ctypes.byref(src), ctypes.sizeof(type(src)))
|
|
return dst
|
|
|
|
|
|
def copy_ctypes_list(src):
|
|
return [copy_ctypes(n) for n in src]
|
|
|
|
|
|
# Weird import placement because these modules are needed by the below code but need the above functions
|
|
from . import (
|
|
arm,
|
|
aarch64,
|
|
m68k,
|
|
mips,
|
|
ppc,
|
|
sparc,
|
|
systemz,
|
|
x86,
|
|
xcore,
|
|
tms320c64x,
|
|
m680x,
|
|
evm,
|
|
mos65xx,
|
|
wasm,
|
|
bpf,
|
|
riscv,
|
|
sh,
|
|
tricore,
|
|
alpha,
|
|
hppa,
|
|
loongarch,
|
|
arc,
|
|
xtensa,
|
|
)
|
|
|
|
|
|
class _cs_arch(ctypes.Union):
|
|
_fields_ = (
|
|
("aarch64", aarch64.CsAArch64),
|
|
("arm", arm.CsArm),
|
|
("m68k", m68k.CsM68K),
|
|
("mips", mips.CsMips),
|
|
("x86", x86.CsX86),
|
|
("ppc", ppc.CsPpc),
|
|
("sparc", sparc.CsSparc),
|
|
("systemz", systemz.CsSystemZ),
|
|
("xcore", xcore.CsXcore),
|
|
("tms320c64x", tms320c64x.CsTMS320C64x),
|
|
("m680x", m680x.CsM680x),
|
|
("evm", evm.CsEvm),
|
|
("mos65xx", mos65xx.CsMOS65xx),
|
|
("wasm", wasm.CsWasm),
|
|
("bpf", bpf.CsBPF),
|
|
("riscv", riscv.CsRISCV),
|
|
("sh", sh.CsSH),
|
|
("tricore", tricore.CsTriCore),
|
|
("alpha", alpha.CsAlpha),
|
|
("hppa", hppa.CsHPPA),
|
|
("loongarch", loongarch.CsLoongArch),
|
|
("xtensa", xtensa.CsXtensa),
|
|
("arc", arc.CsARC),
|
|
)
|
|
|
|
|
|
class _cs_detail(ctypes.Structure):
|
|
_fields_ = (
|
|
("regs_read", ctypes.c_uint16 * 20),
|
|
("regs_read_count", ctypes.c_ubyte),
|
|
("regs_write", ctypes.c_uint16 * 47),
|
|
("regs_write_count", ctypes.c_ubyte),
|
|
("groups", ctypes.c_ubyte * 16),
|
|
("groups_count", ctypes.c_ubyte),
|
|
("writeback", ctypes.c_bool),
|
|
("arch", _cs_arch),
|
|
)
|
|
|
|
|
|
class _cs_insn(ctypes.Structure):
|
|
_fields_ = (
|
|
("id", ctypes.c_uint),
|
|
("alias_id", ctypes.c_uint64),
|
|
("address", ctypes.c_uint64),
|
|
("size", ctypes.c_uint16),
|
|
("bytes", ctypes.c_ubyte * 24),
|
|
("mnemonic", ctypes.c_char * 32),
|
|
("op_str", ctypes.c_char * 160),
|
|
("is_alias", ctypes.c_bool),
|
|
("usesAliasDetails", ctypes.c_bool),
|
|
("illegal", ctypes.c_bool),
|
|
("detail", ctypes.POINTER(_cs_detail)),
|
|
)
|
|
|
|
|
|
# callback for SKIPDATA option
|
|
CS_SKIPDATA_CALLBACK = ctypes.CFUNCTYPE(
|
|
ctypes.c_size_t,
|
|
ctypes.POINTER(ctypes.c_char),
|
|
ctypes.c_size_t,
|
|
ctypes.c_size_t,
|
|
ctypes.c_void_p,
|
|
)
|
|
|
|
|
|
class _cs_opt_skipdata(ctypes.Structure):
|
|
_fields_ = (
|
|
("mnemonic", ctypes.c_char_p),
|
|
("callback", CS_SKIPDATA_CALLBACK),
|
|
("user_data", ctypes.c_void_p),
|
|
)
|
|
|
|
|
|
class _cs_opt_mnem(ctypes.Structure):
|
|
_fields_ = (
|
|
("id", ctypes.c_uint),
|
|
("mnemonic", ctypes.c_char_p),
|
|
)
|
|
|
|
|
|
# setup all the function prototype
|
|
def _setup_prototype(lib, fname, restype, *argtypes):
|
|
getattr(lib, fname).restype = restype
|
|
getattr(lib, fname).argtypes = argtypes
|
|
|
|
|
|
_setup_prototype(
|
|
_cs,
|
|
"cs_open",
|
|
ctypes.c_int,
|
|
ctypes.c_uint,
|
|
ctypes.c_uint,
|
|
ctypes.POINTER(ctypes.c_size_t),
|
|
)
|
|
_setup_prototype(
|
|
_cs,
|
|
"cs_disasm",
|
|
ctypes.c_size_t,
|
|
ctypes.c_size_t,
|
|
ctypes.POINTER(ctypes.c_char),
|
|
ctypes.c_size_t,
|
|
ctypes.c_uint64,
|
|
ctypes.c_size_t,
|
|
ctypes.POINTER(ctypes.POINTER(_cs_insn)),
|
|
)
|
|
_setup_prototype(
|
|
_cs,
|
|
"cs_disasm_iter",
|
|
ctypes.c_bool,
|
|
ctypes.c_size_t,
|
|
ctypes.POINTER(ctypes.POINTER(ctypes.c_char)),
|
|
ctypes.POINTER(ctypes.c_size_t),
|
|
ctypes.POINTER(ctypes.c_uint64),
|
|
ctypes.POINTER(_cs_insn),
|
|
)
|
|
_setup_prototype(_cs, "cs_free", None, ctypes.c_void_p, ctypes.c_size_t)
|
|
_setup_prototype(_cs, "cs_close", ctypes.c_int, ctypes.POINTER(ctypes.c_size_t))
|
|
_setup_prototype(_cs, "cs_reg_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
|
|
_setup_prototype(_cs, "cs_insn_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
|
|
_setup_prototype(_cs, "cs_group_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
|
|
_setup_prototype(
|
|
_cs,
|
|
"cs_op_count",
|
|
ctypes.c_int,
|
|
ctypes.c_size_t,
|
|
ctypes.POINTER(_cs_insn),
|
|
ctypes.c_uint,
|
|
)
|
|
_setup_prototype(
|
|
_cs,
|
|
"cs_op_index",
|
|
ctypes.c_int,
|
|
ctypes.c_size_t,
|
|
ctypes.POINTER(_cs_insn),
|
|
ctypes.c_uint,
|
|
ctypes.c_uint,
|
|
)
|
|
_setup_prototype(_cs, "cs_errno", ctypes.c_int, ctypes.c_size_t)
|
|
_setup_prototype(
|
|
_cs, "cs_option", ctypes.c_int, ctypes.c_size_t, ctypes.c_int, ctypes.c_void_p
|
|
)
|
|
_setup_prototype(
|
|
_cs,
|
|
"cs_version",
|
|
ctypes.c_int,
|
|
ctypes.POINTER(ctypes.c_int),
|
|
ctypes.POINTER(ctypes.c_int),
|
|
)
|
|
_setup_prototype(_cs, "cs_support", ctypes.c_bool, ctypes.c_int)
|
|
_setup_prototype(_cs, "cs_strerror", ctypes.c_char_p, ctypes.c_int)
|
|
_setup_prototype(
|
|
_cs,
|
|
"cs_regs_access",
|
|
ctypes.c_int,
|
|
ctypes.c_size_t,
|
|
ctypes.POINTER(_cs_insn),
|
|
ctypes.POINTER(ctypes.c_uint16 * 64),
|
|
ctypes.POINTER(ctypes.c_uint8),
|
|
ctypes.POINTER(ctypes.c_uint16 * 64),
|
|
ctypes.POINTER(ctypes.c_uint8),
|
|
)
|
|
|
|
|
|
# access to error code via @errno of CsError
|
|
class CsError(Exception):
|
|
def __init__(self, errno):
|
|
self.errno = errno
|
|
|
|
def __str__(self):
|
|
return _cs.cs_strerror(self.errno).decode()
|
|
|
|
|
|
# return the core's version
|
|
def cs_version():
|
|
major = ctypes.c_int()
|
|
minor = ctypes.c_int()
|
|
combined = _cs.cs_version(ctypes.byref(major), ctypes.byref(minor))
|
|
return (major.value, minor.value, combined)
|
|
|
|
|
|
# return the binding's version
|
|
def version_bind():
|
|
return (CS_API_MAJOR, CS_API_MINOR, (CS_API_MAJOR << 8) + CS_API_MINOR)
|
|
|
|
|
|
def cs_support(query):
|
|
return _cs.cs_support(query)
|
|
|
|
|
|
# dummy class resembling Cs class, just for cs_disasm_quick()
|
|
# this class only need to be referenced to via 2 fields: @csh & @arch
|
|
class _dummy_cs(object):
|
|
def __init__(self, csh, arch):
|
|
self.csh = csh
|
|
self.arch = arch
|
|
self._detail = False
|
|
|
|
|
|
# Quick & dirty Python function to disasm raw binary code
|
|
# This function return CsInsn objects
|
|
# NOTE: you might want to use more efficient Cs class & its methods.
|
|
def cs_disasm_quick(arch, mode, code, offset, count=0):
|
|
# verify version compatibility with the core before doing anything
|
|
(major, minor, _combined) = cs_version()
|
|
if major != CS_API_MAJOR or minor != CS_API_MINOR:
|
|
# our binding version is different from the core's API version
|
|
raise CsError(CS_ERR_VERSION)
|
|
|
|
csh = ctypes.c_size_t()
|
|
status = _cs.cs_open(arch, mode, ctypes.byref(csh))
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
|
|
all_insn = ctypes.POINTER(_cs_insn)()
|
|
res = _cs.cs_disasm(csh, code, len(code), offset, count, ctypes.byref(all_insn))
|
|
if res > 0:
|
|
try:
|
|
for i in range(res):
|
|
yield CsInsn(_dummy_cs(csh, arch), all_insn[i])
|
|
finally:
|
|
_cs.cs_free(all_insn, res)
|
|
else:
|
|
status = _cs.cs_errno(csh)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
return
|
|
yield
|
|
|
|
status = _cs.cs_close(ctypes.byref(csh))
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
|
|
|
|
# Another quick, but lighter function to disasm raw binary code.
|
|
# This function is faster than cs_disasm_quick() around 20% because
|
|
# cs_disasm_lite() only return tuples of (address, size, mnemonic, op_str),
|
|
# rather than CsInsn objects.
|
|
# NOTE: you might want to use more efficient Cs class & its methods.
|
|
def cs_disasm_lite(arch, mode, code, offset, count=0):
|
|
# verify version compatibility with the core before doing anything
|
|
(major, minor, _combined) = cs_version()
|
|
if major != CS_API_MAJOR or minor != CS_API_MINOR:
|
|
# our binding version is different from the core's API version
|
|
raise CsError(CS_ERR_VERSION)
|
|
|
|
if cs_support(CS_SUPPORT_DIET):
|
|
# Diet engine cannot provide @mnemonic & @op_str
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
csh = ctypes.c_size_t()
|
|
status = _cs.cs_open(arch, mode, ctypes.byref(csh))
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
|
|
all_insn = ctypes.POINTER(_cs_insn)()
|
|
res = _cs.cs_disasm(csh, code, len(code), offset, count, ctypes.byref(all_insn))
|
|
if res > 0:
|
|
try:
|
|
for i in range(res):
|
|
insn = all_insn[i]
|
|
yield (
|
|
insn.address,
|
|
insn.size,
|
|
insn.mnemonic.decode("ascii"),
|
|
insn.op_str.decode("ascii"),
|
|
)
|
|
finally:
|
|
_cs.cs_free(all_insn, res)
|
|
else:
|
|
status = _cs.cs_errno(csh)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
return
|
|
yield
|
|
|
|
status = _cs.cs_close(ctypes.byref(csh))
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
|
|
|
|
def _ascii_name_or_default(name, default):
|
|
return default if name is None else name.decode("ascii")
|
|
|
|
|
|
# Python-style class to disasm code
|
|
class CsInsn(object):
|
|
def __init__(self, cs, all_info):
|
|
self._raw = copy_ctypes(all_info)
|
|
self._cs = cs
|
|
if self._cs._detail and not self.is_invalid_insn():
|
|
# save detail
|
|
self._raw.detail = ctypes.pointer(all_info.detail._type_())
|
|
ctypes.memmove(
|
|
ctypes.byref(self._raw.detail[0]),
|
|
ctypes.byref(all_info.detail[0]),
|
|
ctypes.sizeof(type(all_info.detail[0])),
|
|
)
|
|
|
|
def __repr__(self):
|
|
return "<CsInsn 0x%x [%s]: %s %s>" % (
|
|
self.address,
|
|
self.bytes.hex(),
|
|
self.mnemonic,
|
|
self.op_str,
|
|
)
|
|
|
|
# return if the instruction is invalid
|
|
def is_invalid_insn(self):
|
|
arch = self._cs.arch
|
|
if arch == CS_ARCH_EVM:
|
|
return self.id == evm.EVM_INS_INVALID
|
|
else:
|
|
return self.id == 0
|
|
|
|
# return instruction's ID.
|
|
@property
|
|
def id(self):
|
|
return self._raw.id
|
|
|
|
# return instruction's address.
|
|
@property
|
|
def address(self):
|
|
return self._raw.address
|
|
|
|
# return instruction's size.
|
|
@property
|
|
def size(self):
|
|
return self._raw.size
|
|
|
|
# return instruction's is_alias flag
|
|
@property
|
|
def is_alias(self):
|
|
return self._raw.is_alias
|
|
|
|
# return instruction's illegal flag
|
|
# Set if instruction can be decoded but is invalid
|
|
# due to context or illegal operands.
|
|
@property
|
|
def illegal(self):
|
|
return self._raw.illegal
|
|
|
|
# return instruction's alias_id
|
|
@property
|
|
def alias_id(self):
|
|
return self._raw.alias_id
|
|
|
|
# return instruction's flag if it uses alias details
|
|
@property
|
|
def uses_alias_details(self):
|
|
return self._raw.usesAliasDetails
|
|
|
|
# return instruction's machine bytes (which should have @size bytes).
|
|
@property
|
|
def bytes(self):
|
|
return bytearray(self._raw.bytes)[: self._raw.size]
|
|
|
|
# return instruction's mnemonic.
|
|
@property
|
|
def mnemonic(self):
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide @mnemonic.
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return self._raw.mnemonic.decode("ascii")
|
|
|
|
# return instruction's operands (in string).
|
|
@property
|
|
def op_str(self):
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide @op_str.
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return self._raw.op_str.decode("ascii")
|
|
|
|
# return list of all implicit registers being read.
|
|
@property
|
|
def regs_read(self):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide @regs_read.
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
if self._cs._detail:
|
|
return self._raw.detail.contents.regs_read[
|
|
: self._raw.detail.contents.regs_read_count
|
|
]
|
|
|
|
raise CsError(CS_ERR_DETAIL)
|
|
|
|
# return list of all implicit registers being modified
|
|
@property
|
|
def regs_write(self):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide @regs_write
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
if self._cs._detail:
|
|
return self._raw.detail.contents.regs_write[
|
|
: self._raw.detail.contents.regs_write_count
|
|
]
|
|
|
|
raise CsError(CS_ERR_DETAIL)
|
|
|
|
# return list of semantic groups this instruction belongs to.
|
|
@property
|
|
def groups(self):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide @groups
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
if self._cs._detail:
|
|
return self._raw.detail.contents.groups[
|
|
: self._raw.detail.contents.groups_count
|
|
]
|
|
|
|
raise CsError(CS_ERR_DETAIL)
|
|
|
|
# return whether instruction has writeback operands.
|
|
@property
|
|
def writeback(self):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide @writeback.
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
if self._cs._detail:
|
|
return self._raw.detail.contents.writeback
|
|
|
|
raise CsError(CS_ERR_DETAIL)
|
|
|
|
def __gen_detail(self):
|
|
if self.is_invalid_insn():
|
|
# do nothing in skipdata mode
|
|
return
|
|
|
|
arch = self._cs.arch
|
|
if arch == CS_ARCH_ARM:
|
|
(
|
|
self.usermode,
|
|
self.vector_size,
|
|
self.vector_data,
|
|
self.cps_mode,
|
|
self.cps_flag,
|
|
self.cc,
|
|
self.vcc,
|
|
self.update_flags,
|
|
self.post_index,
|
|
self.mem_barrier,
|
|
self.pred_mask,
|
|
self.operands,
|
|
) = arm.get_arch_info(self._raw.detail.contents.arch.arm)
|
|
elif arch == CS_ARCH_AARCH64:
|
|
(self.cc, self.update_flags, self.post_index, self.operands) = (
|
|
aarch64.get_arch_info(self._raw.detail.contents.arch.aarch64)
|
|
)
|
|
elif arch == CS_ARCH_X86:
|
|
(
|
|
self.prefix,
|
|
self.opcode,
|
|
self.rex,
|
|
self.addr_size,
|
|
self.modrm,
|
|
self.sib,
|
|
self.disp,
|
|
self.sib_index,
|
|
self.sib_scale,
|
|
self.sib_base,
|
|
self.xop_cc,
|
|
self.sse_cc,
|
|
self.avx_cc,
|
|
self.avx_sae,
|
|
self.avx_rm,
|
|
self.eflags,
|
|
self.fpu_flags,
|
|
self.encoding,
|
|
self.modrm_offset,
|
|
self.disp_offset,
|
|
self.disp_size,
|
|
self.imm_offset,
|
|
self.imm_size,
|
|
self.operands,
|
|
) = x86.get_arch_info(self._raw.detail.contents.arch.x86)
|
|
elif arch == CS_ARCH_M68K:
|
|
(self.operands, self.op_size) = m68k.get_arch_info(
|
|
self._raw.detail.contents.arch.m68k
|
|
)
|
|
elif arch == CS_ARCH_MIPS:
|
|
self.operands = mips.get_arch_info(self._raw.detail.contents.arch.mips)
|
|
elif arch == CS_ARCH_PPC:
|
|
(self.bc, self.update_cr0, self.format, self.operands) = ppc.get_arch_info(
|
|
self._raw.detail.contents.arch.ppc
|
|
)
|
|
elif arch == CS_ARCH_SPARC:
|
|
(self.cc, self.cc_field, self.hint, self.format, self.operands) = (
|
|
sparc.get_arch_info(self._raw.detail.contents.arch.sparc)
|
|
)
|
|
elif arch == CS_ARCH_SYSTEMZ:
|
|
(self.cc, self.format, self.operands) = systemz.get_arch_info(
|
|
self._raw.detail.contents.arch.systemz
|
|
)
|
|
elif arch == CS_ARCH_XCORE:
|
|
(self.operands) = xcore.get_arch_info(self._raw.detail.contents.arch.xcore)
|
|
elif arch == CS_ARCH_TMS320C64X:
|
|
(self.condition, self.funit, self.parallel, self.operands) = (
|
|
tms320c64x.get_arch_info(self._raw.detail.contents.arch.tms320c64x)
|
|
)
|
|
elif arch == CS_ARCH_M680X:
|
|
(self.flags, self.operands) = m680x.get_arch_info(
|
|
self._raw.detail.contents.arch.m680x
|
|
)
|
|
elif arch == CS_ARCH_EVM:
|
|
(self.pop, self.push, self.fee) = evm.get_arch_info(
|
|
self._raw.detail.contents.arch.evm
|
|
)
|
|
elif arch == CS_ARCH_MOS65XX:
|
|
(self.am, self.modifies_flags, self.operands) = mos65xx.get_arch_info(
|
|
self._raw.detail.contents.arch.mos65xx
|
|
)
|
|
elif arch == CS_ARCH_WASM:
|
|
(self.operands) = wasm.get_arch_info(self._raw.detail.contents.arch.wasm)
|
|
elif arch == CS_ARCH_BPF:
|
|
(self.operands) = bpf.get_arch_info(self._raw.detail.contents.arch.bpf)
|
|
elif arch == CS_ARCH_RISCV:
|
|
(self.need_effective_addr, self.operands) = riscv.get_arch_info(
|
|
self._raw.detail.contents.arch.riscv
|
|
)
|
|
elif arch == CS_ARCH_SH:
|
|
(self.sh_insn, self.sh_size, self.operands) = sh.get_arch_info(
|
|
self._raw.detail.contents.arch.sh
|
|
)
|
|
elif arch == CS_ARCH_TRICORE:
|
|
(self.update_flags, self.operands) = tricore.get_arch_info(
|
|
self._raw.detail.contents.arch.tricore
|
|
)
|
|
elif arch == CS_ARCH_ALPHA:
|
|
(self.operands) = alpha.get_arch_info(self._raw.detail.contents.arch.alpha)
|
|
elif arch == CS_ARCH_HPPA:
|
|
(self.operands) = hppa.get_arch_info(self._raw.detail.contents.arch.hppa)
|
|
elif arch == CS_ARCH_LOONGARCH:
|
|
(self.format, self.operands) = loongarch.get_arch_info(
|
|
self._raw.detail.contents.arch.loongarch
|
|
)
|
|
elif arch == CS_ARCH_ARC:
|
|
(self.operands) = arc.get_arch_info(self._raw.detail.contents.arch.arc)
|
|
elif arch == CS_ARCH_XTENSA:
|
|
(self.operands) = xtensa.get_arch_info(
|
|
self._raw.detail.contents.arch.xtensa
|
|
)
|
|
|
|
def __getattr__(self, name):
|
|
if not self._cs._detail:
|
|
raise CsError(CS_ERR_DETAIL)
|
|
|
|
attr = object.__getattribute__
|
|
if not attr(self, "_cs")._detail:
|
|
raise AttributeError(f"'CsInsn' has no attribute '{name}'")
|
|
_dict = attr(self, "__dict__")
|
|
if "operands" not in _dict:
|
|
self.__gen_detail()
|
|
if name not in _dict:
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
raise AttributeError(f"'CsInsn' has no attribute '{name}'")
|
|
return _dict[name]
|
|
|
|
# get the last error code
|
|
def errno(self):
|
|
return _cs.cs_errno(self._cs.csh)
|
|
|
|
# get the register name, given the register ID
|
|
def reg_name(self, reg_id, default=None):
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide register name
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return _ascii_name_or_default(_cs.cs_reg_name(self._cs.csh, reg_id), default)
|
|
|
|
# get the instruction name
|
|
def insn_name(self, default=None):
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide instruction name
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
if self.is_invalid_insn():
|
|
return default
|
|
|
|
return _ascii_name_or_default(_cs.cs_insn_name(self._cs.csh, self.id), default)
|
|
|
|
# get the group name
|
|
def group_name(self, group_id, default=None):
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide group name
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return _ascii_name_or_default(
|
|
_cs.cs_group_name(self._cs.csh, group_id), default
|
|
)
|
|
|
|
# verify if this insn belong to group with id as @group_id
|
|
def group(self, group_id):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide group information
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return group_id in self.groups
|
|
|
|
# verify if this instruction implicitly read register @reg_id
|
|
def reg_read(self, reg_id):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide regs_read information
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return reg_id in self.regs_read
|
|
|
|
# verify if this instruction implicitly modified register @reg_id
|
|
def reg_write(self, reg_id):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
if self._cs._diet:
|
|
# Diet engine cannot provide regs_write information
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return reg_id in self.regs_write
|
|
|
|
# return number of operands having same operand type @op_type
|
|
def op_count(self, op_type):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
c = 0
|
|
for op in self.operands:
|
|
if op.type == op_type:
|
|
c += 1
|
|
return c
|
|
|
|
# get the operand at position @position of all operands having the same type @op_type
|
|
def op_find(self, op_type, position):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
c = 0
|
|
for op in self.operands:
|
|
if op.type == op_type:
|
|
c += 1
|
|
if c == position:
|
|
return op
|
|
|
|
# Return (list-of-registers-read, list-of-registers-modified) by this instructions.
|
|
# This includes all the implicit & explicit registers.
|
|
def regs_access(self):
|
|
if self.is_invalid_insn():
|
|
raise CsError(CS_ERR_SKIPDATA)
|
|
|
|
regs_read = (ctypes.c_uint16 * 64)()
|
|
regs_read_count = ctypes.c_uint8()
|
|
regs_write = (ctypes.c_uint16 * 64)()
|
|
regs_write_count = ctypes.c_uint8()
|
|
|
|
status = _cs.cs_regs_access(
|
|
self._cs.csh,
|
|
self._raw,
|
|
ctypes.byref(regs_read),
|
|
ctypes.byref(regs_read_count),
|
|
ctypes.byref(regs_write),
|
|
ctypes.byref(regs_write_count),
|
|
)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
|
|
if regs_read_count.value > 0:
|
|
regs_read = regs_read[: regs_read_count.value]
|
|
else:
|
|
regs_read = []
|
|
|
|
if regs_write_count.value > 0:
|
|
regs_write = regs_write[: regs_write_count.value]
|
|
else:
|
|
regs_write = []
|
|
|
|
return (regs_read, regs_write)
|
|
|
|
|
|
class Cs(object):
|
|
def __init__(self, arch, mode):
|
|
# verify version compatibility with the core before doing anything
|
|
(major, minor, _combined) = cs_version()
|
|
if major != CS_API_MAJOR or minor != CS_API_MINOR:
|
|
self.csh = None
|
|
# our binding version is different from the core's API version
|
|
raise CsError(CS_ERR_VERSION)
|
|
|
|
self.arch, self._mode = arch, mode
|
|
self.csh = ctypes.c_size_t()
|
|
status = _cs.cs_open(arch, mode, ctypes.byref(self.csh))
|
|
if status != CS_ERR_OK:
|
|
self.csh = None
|
|
raise CsError(status)
|
|
|
|
try:
|
|
from . import ccapstone
|
|
|
|
# rewire disasm to use the faster version
|
|
self.disasm = ccapstone.Cs(self).disasm
|
|
except:
|
|
pass
|
|
|
|
if arch == CS_ARCH_X86:
|
|
# Intel syntax is default for X86
|
|
self._syntax = CS_OPT_SYNTAX_INTEL
|
|
else:
|
|
self._syntax = None
|
|
|
|
self._detail = False # by default, do not produce instruction details
|
|
self._imm_unsigned = (
|
|
False # by default, print immediate operands as signed numbers
|
|
)
|
|
self._diet = cs_support(CS_SUPPORT_DIET)
|
|
self._x86reduce = cs_support(CS_SUPPORT_X86_REDUCE)
|
|
|
|
# default mnemonic for SKIPDATA
|
|
self._skipdata_mnem = ".byte"
|
|
self._skipdata_cb = (None, None)
|
|
# store reference to option object to avoid it being freed
|
|
# because C code uses it by reference
|
|
self._skipdata_opt = _cs_opt_skipdata()
|
|
self._skipdata = False
|
|
|
|
# destructor to be called automatically when object is destroyed.
|
|
def __del__(self):
|
|
if self.csh:
|
|
try:
|
|
status = _cs.cs_close(ctypes.byref(self.csh))
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
except: # _cs might be pulled from under our feet
|
|
pass
|
|
|
|
def option(self, opt_type, opt_value):
|
|
status = _cs.cs_option(self.csh, opt_type, opt_value)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
if opt_type == CS_OPT_DETAIL or opt_type == CS_OPT_DETAIL_REAL:
|
|
self._detail = (opt_value & CS_OPT_ON) != 0
|
|
elif opt_type == CS_OPT_SKIPDATA:
|
|
self._skipdata = opt_value == CS_OPT_ON
|
|
elif opt_type == CS_OPT_UNSIGNED:
|
|
self._imm_unsigned = opt_value == CS_OPT_ON
|
|
|
|
# is this a diet engine?
|
|
@property
|
|
def diet(self):
|
|
return self._diet
|
|
|
|
# is this engine compiled with X86-reduce option?
|
|
@property
|
|
def x86_reduce(self):
|
|
return self._x86reduce
|
|
|
|
# return assembly syntax.
|
|
@property
|
|
def syntax(self):
|
|
return self._syntax
|
|
|
|
# syntax setter: modify assembly syntax.
|
|
@syntax.setter
|
|
def syntax(self, style):
|
|
status = _cs.cs_option(self.csh, CS_OPT_SYNTAX, style)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
# save syntax
|
|
self._syntax = style
|
|
|
|
# return current skipdata status
|
|
@property
|
|
def skipdata(self):
|
|
return self._skipdata
|
|
|
|
# setter: modify skipdata status
|
|
@skipdata.setter
|
|
def skipdata(self, opt):
|
|
if opt == False:
|
|
status = _cs.cs_option(self.csh, CS_OPT_SKIPDATA, CS_OPT_OFF)
|
|
else:
|
|
status = _cs.cs_option(self.csh, CS_OPT_SKIPDATA, CS_OPT_ON)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
|
|
# save this option
|
|
self._skipdata = opt
|
|
|
|
@property
|
|
def skipdata_setup(self):
|
|
return (self._skipdata_mnem,) + self._skipdata_cb
|
|
|
|
@skipdata_setup.setter
|
|
def skipdata_setup(self, opt):
|
|
_mnem, _cb, _ud = opt
|
|
self._skipdata_opt.mnemonic = _mnem.encode()
|
|
self._skipdata_opt.callback = CS_SKIPDATA_CALLBACK(_cb or 0)
|
|
self._skipdata_opt.user_data = ctypes.cast(_ud, ctypes.c_void_p)
|
|
status = _cs.cs_option(
|
|
self.csh,
|
|
CS_OPT_SKIPDATA_SETUP,
|
|
ctypes.cast(ctypes.byref(self._skipdata_opt), ctypes.c_void_p),
|
|
)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
|
|
self._skipdata_mnem = _mnem
|
|
self._skipdata_cb = (_cb, _ud)
|
|
|
|
@property
|
|
def skipdata_mnem(self):
|
|
return self._skipdata_mnem
|
|
|
|
@skipdata_mnem.setter
|
|
def skipdata_mnem(self, mnem):
|
|
self.skipdata_setup = (mnem,) + self._skipdata_cb
|
|
|
|
@property
|
|
def skipdata_callback(self):
|
|
return self._skipdata_cb
|
|
|
|
@skipdata_callback.setter
|
|
def skipdata_callback(self, val):
|
|
if not isinstance(val, tuple):
|
|
val = (val, None)
|
|
func, data = val
|
|
self.skipdata_setup = (self._skipdata_mnem, func, data)
|
|
|
|
# customize instruction mnemonic
|
|
def mnemonic_setup(self, id, mnem):
|
|
_mnem_opt = _cs_opt_mnem()
|
|
_mnem_opt.id = id
|
|
if mnem:
|
|
_mnem_opt.mnemonic = mnem.encode()
|
|
else:
|
|
_mnem_opt.mnemonic = mnem
|
|
status = _cs.cs_option(
|
|
self.csh,
|
|
CS_OPT_MNEMONIC,
|
|
ctypes.cast(ctypes.byref(_mnem_opt), ctypes.c_void_p),
|
|
)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
|
|
# check to see if this engine supports a particular arch,
|
|
# or diet mode (depending on @query).
|
|
def support(self, query):
|
|
return cs_support(query)
|
|
|
|
# is detail mode enable?
|
|
@property
|
|
def detail(self):
|
|
return self._detail
|
|
|
|
# modify detail mode.
|
|
@detail.setter
|
|
def detail(self, opt): # opt is boolean type, so must be either 'True' or 'False'
|
|
if opt == False:
|
|
status = _cs.cs_option(self.csh, CS_OPT_DETAIL, CS_OPT_OFF)
|
|
else:
|
|
status = _cs.cs_option(self.csh, CS_OPT_DETAIL, CS_OPT_ON)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
# save detail
|
|
self._detail = opt
|
|
|
|
# is detail mode enable?
|
|
@property
|
|
def imm_unsigned(self):
|
|
return self._imm_unsigned
|
|
|
|
# modify detail mode.
|
|
@imm_unsigned.setter
|
|
def imm_unsigned(
|
|
self, opt
|
|
): # opt is boolean type, so must be either 'True' or 'False'
|
|
if opt == False:
|
|
status = _cs.cs_option(self.csh, CS_OPT_UNSIGNED, CS_OPT_OFF)
|
|
else:
|
|
status = _cs.cs_option(self.csh, CS_OPT_UNSIGNED, CS_OPT_ON)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
# save detail
|
|
self._imm_unsigned = opt
|
|
|
|
# return disassembly mode of this engine.
|
|
@property
|
|
def mode(self):
|
|
return self._mode
|
|
|
|
# modify engine's mode at run-time.
|
|
@mode.setter
|
|
def mode(self, opt): # opt is new disasm mode, of int type
|
|
status = _cs.cs_option(self.csh, CS_OPT_MODE, opt)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
# save mode
|
|
self._mode = opt
|
|
|
|
# get the last error code
|
|
def errno(self):
|
|
return _cs.cs_errno(self.csh)
|
|
|
|
# get the register name, given the register ID
|
|
def reg_name(self, reg_id, default=None):
|
|
if self._diet:
|
|
# Diet engine cannot provide register name
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return _ascii_name_or_default(_cs.cs_reg_name(self.csh, reg_id), default)
|
|
|
|
# get the instruction name, given the instruction ID
|
|
def insn_name(self, insn_id, default=None):
|
|
if self._diet:
|
|
# Diet engine cannot provide instruction name
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return _ascii_name_or_default(_cs.cs_insn_name(self.csh, insn_id), default)
|
|
|
|
# get the group name
|
|
def group_name(self, group_id, default=None):
|
|
if self._diet:
|
|
# Diet engine cannot provide group name
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
return _ascii_name_or_default(_cs.cs_group_name(self.csh, group_id), default)
|
|
|
|
# Disassemble binary & return disassembled instructions in CsInsn objects
|
|
def disasm(self, code, offset, count=0):
|
|
all_insn = ctypes.POINTER(_cs_insn)()
|
|
# Pass a bytearray by reference
|
|
size = len(code)
|
|
view = memoryview(code)
|
|
if not view.readonly:
|
|
code = ctypes.byref(ctypes.c_char.from_buffer(view))
|
|
elif not isinstance(code, bytes):
|
|
code = view.tobytes()
|
|
res = _cs.cs_disasm(self.csh, code, size, offset, count, ctypes.byref(all_insn))
|
|
if res > 0:
|
|
try:
|
|
for i in range(res):
|
|
yield CsInsn(self, all_insn[i])
|
|
finally:
|
|
_cs.cs_free(all_insn, res)
|
|
else:
|
|
status = _cs.cs_errno(self.csh)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
return
|
|
yield
|
|
|
|
# This function matches the cs_disasm_iter implementation which
|
|
# *should* be much faster via the C API due to pre-allocating
|
|
# memory (https://www.capstone-engine.org/iteration.html).
|
|
# Note: It is unclear whether this function via the Python
|
|
# binding provides the same speedup like cs_disasm_lite.
|
|
def disasm_iter(self, code, offset):
|
|
if self._diet:
|
|
# Diet engine cannot provide @mnemonic & @op_str
|
|
raise CsError(CS_ERR_DIET)
|
|
insn = _cs_insn()
|
|
size = ctypes.c_size_t(len(code))
|
|
|
|
# Pass a bytearray by reference
|
|
view = memoryview(code)
|
|
code = ctypes.pointer(ctypes.c_char.from_buffer_copy(view))
|
|
if view.readonly:
|
|
code = (ctypes.c_char * len(view)).from_buffer_copy(view)
|
|
else:
|
|
code = ctypes.pointer(ctypes.c_char.from_buffer(view))
|
|
|
|
# since we are taking a pointer to a pointer, ctypes does not do
|
|
# the typical auto conversion, so we have to cast it here.
|
|
code = ctypes.cast(code, ctypes.POINTER(ctypes.c_char))
|
|
address = ctypes.c_uint64(offset)
|
|
while _cs.cs_disasm_iter(
|
|
self.csh,
|
|
ctypes.byref(code),
|
|
ctypes.byref(size),
|
|
ctypes.byref(address),
|
|
ctypes.byref(insn),
|
|
):
|
|
yield (
|
|
insn.address,
|
|
insn.size,
|
|
insn.mnemonic.decode("ascii"),
|
|
insn.op_str.decode("ascii"),
|
|
)
|
|
|
|
# Light function to disassemble binary. This is about 20% faster than disasm() because
|
|
# unlike disasm(), disasm_lite() only return tuples of (address, size, mnemonic, op_str),
|
|
# rather than CsInsn objects.
|
|
def disasm_lite(self, code, offset, count=0):
|
|
if self._diet:
|
|
# Diet engine cannot provide @mnemonic & @op_str
|
|
raise CsError(CS_ERR_DIET)
|
|
|
|
all_insn = ctypes.POINTER(_cs_insn)()
|
|
size = len(code)
|
|
# Pass a bytearray by reference
|
|
view = memoryview(code)
|
|
if not view.readonly:
|
|
code = ctypes.byref(ctypes.c_char.from_buffer(view))
|
|
elif not isinstance(code, bytes):
|
|
code = view.tobytes()
|
|
res = _cs.cs_disasm(self.csh, code, size, offset, count, ctypes.byref(all_insn))
|
|
if res > 0:
|
|
try:
|
|
for i in range(res):
|
|
insn = all_insn[i]
|
|
yield (
|
|
insn.address,
|
|
insn.size,
|
|
insn.mnemonic.decode("ascii"),
|
|
insn.op_str.decode("ascii"),
|
|
)
|
|
finally:
|
|
_cs.cs_free(all_insn, res)
|
|
else:
|
|
status = _cs.cs_errno(self.csh)
|
|
if status != CS_ERR_OK:
|
|
raise CsError(status)
|
|
return
|
|
yield
|
|
|
|
|
|
# print out debugging info
|
|
def debug():
|
|
# is Cython there?
|
|
try:
|
|
from . import ccapstone
|
|
|
|
return ccapstone.debug()
|
|
except:
|
|
# no Cython, fallback to Python code below
|
|
pass
|
|
|
|
if cs_support(CS_SUPPORT_DIET):
|
|
diet = "diet"
|
|
else:
|
|
diet = "standard"
|
|
|
|
archs = {
|
|
"arm": CS_ARCH_ARM,
|
|
"aarch64": CS_ARCH_AARCH64,
|
|
"m68k": CS_ARCH_M68K,
|
|
"mips": CS_ARCH_MIPS,
|
|
"ppc": CS_ARCH_PPC,
|
|
"sparc": CS_ARCH_SPARC,
|
|
"systemz": CS_ARCH_SYSTEMZ,
|
|
"xcore": CS_ARCH_XCORE,
|
|
"tms320c64x": CS_ARCH_TMS320C64X,
|
|
"m680x": CS_ARCH_M680X,
|
|
"evm": CS_ARCH_EVM,
|
|
"mos65xx": CS_ARCH_MOS65XX,
|
|
"bpf": CS_ARCH_BPF,
|
|
"riscv": CS_ARCH_RISCV,
|
|
"tricore": CS_ARCH_TRICORE,
|
|
"wasm": CS_ARCH_WASM,
|
|
"sh": CS_ARCH_SH,
|
|
"alpha": CS_ARCH_ALPHA,
|
|
"hppa": CS_ARCH_HPPA,
|
|
"loongarch": CS_ARCH_LOONGARCH,
|
|
"xtensa": CS_ARCH_XTENSA,
|
|
"arc": CS_ARCH_ARC,
|
|
}
|
|
|
|
all_archs = ""
|
|
keys = archs.keys()
|
|
for k in sorted(keys):
|
|
if cs_support(archs[k]):
|
|
all_archs += "-%s" % k
|
|
|
|
if cs_support(CS_ARCH_X86):
|
|
all_archs += "-x86"
|
|
if cs_support(CS_SUPPORT_X86_REDUCE):
|
|
all_archs += "_reduce"
|
|
|
|
(major, minor, _combined) = cs_version()
|
|
|
|
return "python-%s%s-c%u.%u-b%u.%u" % (
|
|
diet,
|
|
all_archs,
|
|
major,
|
|
minor,
|
|
CS_API_MAJOR,
|
|
CS_API_MINOR,
|
|
)
|