1 /// Idiomatic lifting of $(LINK2 http://www.capstone-engine.org, Capstone)'s C API to D 2 module capstone.api; 3 4 import std.typecons: Tuple, BitFlags, Yes, Nullable; 5 import std.exception: enforce, assertThrown; 6 import std.format; 7 import std.conv; 8 import std..string; 9 import std.array; 10 import std.range: isInputRange; 11 import std.algorithm: canFind; 12 import std.traits: EnumMembers; 13 14 import capstone; 15 16 // Aliases 17 alias CapstoneArm = CapstoneImpl!(Arch.arm); 18 alias InstructionArm = InstructionImpl!(Arch.arm); 19 alias CapstoneArm64 = CapstoneImpl!(Arch.arm64); 20 alias InstructionArm64 = InstructionImpl!(Arch.arm64); 21 alias CapstoneMips = CapstoneImpl!(Arch.mips); 22 alias InstructionMips = InstructionImpl!(Arch.mips); 23 alias CapstonePpc = CapstoneImpl!(Arch.ppc); 24 alias InstructionPpc = InstructionImpl!(Arch.ppc); 25 alias CapstoneSparc = CapstoneImpl!(Arch.sparc); 26 alias InstructionSparc = InstructionImpl!(Arch.sparc); 27 alias CapstoneSysz = CapstoneImpl!(Arch.sysz); 28 alias InstructionSysz = InstructionImpl!(Arch.sysz); 29 alias CapstoneX86 = CapstoneImpl!(Arch.x86); 30 alias InstructionX86 = InstructionImpl!(Arch.x86); 31 alias CapstoneXCore = CapstoneImpl!(Arch.xcore); 32 alias InstructionXCore = InstructionImpl!(Arch.xcore); 33 34 /// Architecture type 35 enum Arch{ 36 arm = 0, /// ARM architecture (including Thumb, Thumb-2) 37 arm64, /// ARM-64 (also called AArch64) 38 mips, /// Mips architecture 39 x86, /// X86 architecture (including x86 & x86-64) 40 ppc, /// Support for PowerPC architecture 41 sparc, /// Support for Sparc architecture 42 sysz, /// Support for SystemZ architecture 43 xcore /// Support for XCore architecture 44 } 45 46 /// The support options that Capstone can be compiled with 47 enum SupportQuery { 48 arm = 0, /// Support for ARM architecture (including Thumb, Thumb-2) 49 arm64, /// Support for ARM-64 (also called AArch64) 50 mips, /// Support for Mips architecture 51 x86, /// Support for X86 architecture (including x86 & x86-64) 52 powerPc, /// Support for PowerPC architecture 53 sparc, /// Support for Sparc architecture 54 systemZ, /// Support for SystemZ architecture 55 xCore, /// Support for XCore architecture 56 all = 0xFFFF, /// Supports all architectures 57 diet, /// Compiled in diet mode, i.e. missing less relevant data fields 58 x86Reduce /// Compiled in X86-reduce mode, i.e. missing less relevant data fields and exotic X86 instruction sets 59 } 60 61 /// Mode type 62 enum Mode { 63 littleEndian = 0, /// Little-endian mode (default mode) 64 arm = 0, /// 32-bit ARM 65 bit16 = 1 << 1, /// 16-bit mode (X86) 66 bit32 = 1 << 2, /// 32-bit mode (X86) 67 bit64 = 1 << 3, /// 64-bit mode (X86, PPC) 68 armThumb = 1 << 4, /// ARM's Thumb mode, including Thumb-2 69 armCortexM = 1 << 5,/// ARM's Cortex-M series 70 armV8 = 1 << 6, /// ARMv8 A32 encodings for ARM 71 mipsMicro = 1 << 4, /// MicroMips mode (MIPS) 72 mips3 = 1 << 5, /// Mips III ISA 73 mips32r6 = 1 << 6, /// Mips32r6 ISA 74 mipsGp64 = 1 << 7, /// General Purpose Registers are 64-bit wide (MIPS) 75 bigEndian = 1 << 31,/// SparcV9 mode (Sparc) 76 sparcV9 = 1 << 4, /// Big-endian mode 77 mips32 = bit32, /// Mips32 ISA (Mips) 78 mips64 = bit64 /// Mips64 ISA (Mips) 79 } 80 /// Type for combination of several modes 81 alias ModeFlags = BitFlags!(Mode, Yes.unsafe); 82 83 /// Disassembly syntax variants 84 enum Syntax { 85 systemDefault = 0, /// System's default syntax 86 intel, /// X86 Intel syntax - default on X86 87 att, /// X86 AT&T syntax 88 noregname /// Prints register name with only number 89 } 90 91 // Auxiliary templates to derive the types to use as InstructionId, Register, InstructionGroup and InstructionDetail for a given architecture 92 private{ 93 import std.meta: AliasSeq; 94 template ArchSpec(Arch arch){ 95 static if(arch == Arch.arm) 96 alias ArchSpec = AliasSeq!(ArmInstructionId, ArmRegister, ArmInstructionGroup, ArmInstructionDetail); 97 else static if(arch == Arch.arm64) 98 alias ArchSpec = AliasSeq!(Arm64InstructionId, Arm64Register, Arm64InstructionGroup, Arm64InstructionDetail); 99 else static if(arch == Arch.mips) 100 alias ArchSpec = AliasSeq!(MipsInstructionId, MipsRegister, MipsInstructionGroup, MipsInstructionDetail); 101 else static if(arch == Arch.ppc) 102 alias ArchSpec = AliasSeq!(PpcInstructionId, PpcRegister, PpcInstructionGroup, PpcInstructionDetail); 103 else static if(arch == Arch.sparc) 104 alias ArchSpec = AliasSeq!(SparcInstructionId, SparcRegister, SparcInstructionGroup, SparcInstructionDetail); 105 else static if(arch == Arch.sysz) 106 alias ArchSpec = AliasSeq!(SyszInstructionId, SyszRegister, SyszInstructionGroup, SyszInstructionDetail); 107 else static if(arch == Arch.x86) 108 alias ArchSpec = AliasSeq!(X86InstructionId, X86Register, X86InstructionGroup, X86InstructionDetail); 109 else static if(arch == Arch.xcore) 110 alias ArchSpec = AliasSeq!(XCoreInstructionId, XCoreRegister, XCoreInstructionGroup, XCoreInstructionDetail); 111 else static assert(false); 112 } 113 alias InstructionId(Arch arch) = ArchSpec!(arch)[0]; 114 alias Register(Arch arch) = ArchSpec!(arch)[1]; 115 alias InstructionGroup(Arch arch) = ArchSpec!(arch)[2]; 116 alias InstructionDetail(Arch arch) = ArchSpec!(arch)[3]; 117 } 118 119 /// Instruction detail 120 struct Detail(Arch arch) { 121 Register!arch[] regsRead; /// Registers implicitly read by this instruction 122 Register!arch[] regsWrite; /// Registers implicitly modified by this instruction 123 InstructionGroup!arch[] groups; /// The groups this instruction belongs to 124 125 /// Architecture-specific instruction detail 126 InstructionDetail!arch archSpecific; 127 /// Convenience-alias making `archSpecific`'s members directly accessible from this 128 alias archSpecific this; 129 130 private this(cs_detail internal){ 131 regsRead = internal.regs_read[0..internal.regs_read_count].to!(Register!arch[]); 132 regsWrite = internal.regs_write[0..internal.regs_write_count].to!(Register!arch[]); 133 groups = internal.groups[0..internal.groups_count].to!(InstructionGroup!arch[]); 134 archSpecific = InstructionDetail!arch(internal.arch_detail); 135 } 136 } 137 138 /// Architecture-independent instruction 139 abstract class Instruction { 140 ulong address; /// Address (EIP) of this instruction 141 ubyte[] bytes; /// Machine bytes of this instruction 142 string mnemonic; /// Ascii text of instruction mnemonic 143 string opStr; /// Ascii text of instruction operands 144 145 private this(cs_insn internal){ 146 address = internal.address; 147 bytes = internal.bytes[0..internal.size].dup; 148 mnemonic = internal.mnemonic.ptr.to!string; 149 opStr = internal.op_str.ptr.to!string; 150 } 151 } 152 153 /// Architecture-specific instruction 154 class InstructionImpl(Arch arch) : Instruction { 155 InstructionId!arch id; /// Instruction ID (basically a numeric ID for the instruction mnemonic) 156 private Nullable!(Detail!arch) _detail; 157 158 private this(cs_insn internal, bool detail, bool skipData){ 159 super(internal); 160 id = internal.id.to!(InstructionId!arch); 161 162 if(detail && !skipData) 163 _detail = Detail!arch(*internal.detail); 164 } 165 /** More details about the instruction 166 167 Note that this is only available if both requirements are met: 168 $(OL 169 $(LI details are enabled) 170 $(LI the engine is not in Skipdata mode)) 171 */ 172 @property detail() const { 173 if(_detail.isNull) 174 throw new CapstoneException("Trying to access unavailable instruction detail", ErrorCode.UnavailableInstructionDetail); 175 return _detail; 176 } 177 178 /** Checks whether the instruction belongs to the instruction group `group` 179 180 Convenience method for searching through `detail.groups`. 181 */ 182 bool isInGroup(InstructionGroup!arch group) const { 183 return detail.groups.canFind(group); 184 } 185 186 /** Checks if the instruction IMPLICITLY uses a particular register 187 188 Convenience method for searching through `detail.readRegs`. 189 */ 190 bool readsReg(Register!arch reg) const { 191 return detail.regsRead.canFind(reg); 192 } 193 194 /** Checks if the instruction IMPLICITLY modifies a particular register 195 196 Convenience method for searching through `detail.writeRegs`. 197 */ 198 bool writesReg(Register!arch reg) const { 199 return detail.regsWrite.canFind(reg); 200 } 201 } 202 203 /** User-defined callback function type for SKIPDATA mode of operation 204 205 The first parameter is the input buffer containing code to be disassembled, 206 while the second one holds the position of the currently-examined byte in this buffer. 207 208 Returns: The number of bytes to skip, or 0 to immediately stop disassembling 209 210 Example: 211 --- 212 size_t callback(in ubyte[] code, size_t offset) { 213 return 2; // Always skip 2 bytes when encountering uninterpretable instructions 214 } 215 --- 216 See `setupSkipdata` documentation for full sample code demonstrating this functionality. 217 */ 218 alias Callback = size_t delegate(in ubyte[] code, size_t offset) nothrow @nogc; 219 220 // This trampoline is the ugly C-lang callback (calling D in turn) 221 private extern(C) size_t cCallback(const(ubyte)* code, size_t code_size, size_t offset, void* userData) nothrow @nogc{ 222 auto slice = code[0..code_size]; 223 224 // Call the nice d-lang callback 225 auto dCallback = *cast(Callback*)userData; 226 auto res = dCallback(slice, offset); 227 return res; 228 } 229 230 /// Version consisting of major and minor numbers 231 struct Version{ 232 int major; /// Major version number 233 int minor; /// Minor version number 234 235 /// Textual representation 236 string toString() const { 237 return "%s.%s".format(major, minor); 238 } 239 } 240 241 /// Determines the `Version` supported by these bindings 242 auto versionOfBindings() { 243 return Version(CS_API_MAJOR, CS_API_MINOR); 244 } 245 246 /// Determines the `Version` supported by the installed library 247 auto versionOfLibrary() { 248 int major, minor; 249 cs_version(&major, &minor); 250 return Version(major, minor); 251 } 252 /// 253 unittest{ 254 const libVer = versionOfLibrary; 255 const bindVer = versionOfBindings; 256 assert(libVer == bindVer, "API version mismatch between library (%s) and bindings (%s)".format(libVer, bindVer)); 257 } 258 259 /** Indicates whether the installed library was compiled in $(LINK2 http://www.capstone-engine.org/diet.html, diet mode) 260 261 Convenience functionality which is also available via `supports`. 262 */ 263 auto diet(){ 264 return supports(SupportQuery.diet); 265 } 266 267 /** Indicates whether an architecture or particular option is supported by the installed Capstone library 268 269 Params: 270 query = The `SupportQuery` to issue to the library 271 272 Returns: True if the requested option is supported 273 274 Example: 275 --- 276 // Query installed Capstone library for supported options 277 foreach(query; EnumMembers!SupportQuery) 278 writefln!"%-10s: %s"(query, supports(query)); 279 --- 280 */ 281 auto supports(in SupportQuery query){ 282 return cs_support(query); 283 } 284 285 /** Encapsulates an instance of the Capstone dissassembly engine 286 287 This class encapsulates the core functionality of the Capstone disassembly engine, providing 288 access to runtime options for 289 $(UL 290 $(LI changing the `Mode` of interpretation) 291 $(LI changing the `Syntax` of the disassembly) 292 $(LI choosing whether `Instruction`'s should be disassembled in detail, i.e. filling `Instruction.detail`) 293 $(LI defining manual handling of broken instructions through the $(LINK2 http://www.capstone-engine.org/skipdata.html, SKIPDATA) mode of operation (optionally via a `Callback`)) 294 ) 295 296 Note that, since the architecture is chosen at runtime, this base class only gives access to the architecture-indepentent aspects, 297 but can be cast to the `CapstoneImpl` of corresponding architecture. 298 */ 299 abstract class Capstone{ 300 private{ 301 alias Handle = size_t; 302 Handle handle; 303 304 ModeFlags _mode; 305 Syntax _syntax; 306 bool _detail; 307 bool _skipData; 308 309 string mnemonic; 310 Callback callback; 311 } 312 const Arch arch; 313 314 /** Constructs an instance of the disassembly engine 315 316 Params: 317 modeFlags = A combination of flags to further specify how bytes will be interpreted, e.g. in little-endian. 318 */ 319 private this(in Arch arch, in ModeFlags modeFlags){ 320 const libVer = versionOfLibrary; 321 const bindVer = versionOfBindings; 322 if(libVer != bindVer) 323 throw new CapstoneException("API version mismatch between library (%s) and bindings (%s)".format(libVer, bindVer), ErrorCode.UnsupportedVersion); 324 325 // Create Capstone engine instance 326 this.arch = arch; 327 this._mode = modeFlags; 328 cs_open(arch, modeFlags.to!uint, &handle).checkErrno; 329 330 // Sync members with library's default values 331 // Note: not really necessary at the time of writing as they happen to match 332 syntax = _syntax; 333 detail = _detail; 334 skipData = _skipData; 335 } 336 337 ~this(){ 338 if(handle) 339 cs_close(&handle).checkErrno; 340 } 341 342 /** Creates a Capstone instance for disassembling code of a specific architecture 343 344 Params: 345 arch = The architecture to interpret the bytestream for 346 modeFlags = The mode of interpretation 347 */ 348 static Capstone create(Arch arch, ModeFlags modeFlags){ 349 final switch(arch){ 350 case Arch.arm: 351 return new CapstoneImpl!(Arch.arm)(modeFlags); 352 case Arch.arm64: 353 return new CapstoneImpl!(Arch.arm64)(modeFlags); 354 case Arch.mips: 355 return new CapstoneImpl!(Arch.mips)(modeFlags); 356 case Arch.ppc: 357 return new CapstoneImpl!(Arch.ppc)(modeFlags); 358 case Arch.sparc: 359 return new CapstoneImpl!(Arch.sparc)(modeFlags); 360 case Arch.sysz: 361 return new CapstoneImpl!(Arch.sysz)(modeFlags); 362 case Arch.x86: 363 return new CapstoneImpl!(Arch.x86)(modeFlags); 364 case Arch.xcore: 365 return new CapstoneImpl!(Arch.xcore)(modeFlags); 366 } 367 } 368 369 /// Gets the mode of interpretation 370 @property auto mode() const {return _mode;} 371 /// Sets the mode of interpretation 372 @property void mode(in ModeFlags modeFlags){ 373 _mode = modeFlags; 374 cs_option(handle, cs_opt_type.CS_OPT_MODE, modeFlags.to!uint).checkErrno; 375 } 376 377 /// Gets the disassembly syntax variant 378 @property auto syntax() const {return _syntax;} 379 /// Sets the disassembly syntax variant 380 @property void syntax(in Syntax option){ 381 _syntax = option; 382 cs_option(handle, cs_opt_type.CS_OPT_SYNTAX, option).checkErrno; 383 } 384 385 /// Indicates whether instructions will be disassembled in detail 386 @property auto detail() const {return _detail;} 387 /// Sets whether instructions will be disassembled in detail 388 @property void detail(in bool enable){ 389 _detail = enable; 390 auto option = (enable ? cs_opt_value.CS_OPT_ON : cs_opt_value.CS_OPT_OFF); 391 cs_option(handle, cs_opt_type.CS_OPT_DETAIL, option).checkErrno; 392 } 393 394 /// Indicates whether SKIPDATA mode of operation is in use 395 @property auto skipData() const {return _skipData;} 396 /// Sets whether to use SKIPDATA mode of operation 397 @property void skipData(in bool enable){ 398 _skipData = enable; 399 auto option = (enable ? cs_opt_value.CS_OPT_ON : cs_opt_value.CS_OPT_OFF); 400 cs_option(handle, cs_opt_type.CS_OPT_SKIPDATA, option).checkErrno; 401 } 402 403 /** Customises behaviour in SKIPDATA mode of operation 404 405 By default, disassembling will stop when it encounters a broken instruction. 406 Most of the time, the reason is that this is data mixed inside the input. 407 408 When in SKIPDATA mode, some (unknown) amount of data until the next interpretable instruction will be skipped. 409 Capstone considers the skipped data a special instruction with ID 0x00 and a `mnemonic` that defaults to `".byte"`. 410 The operand string is a hex-code of the sequence of bytes it skipped. 411 412 By default, for each iteration, Capstone skips 1 byte on X86 architecture, 2 bytes on Thumb mode on Arm 413 architecture, and 4 bytes for the rest. The reason while Capstone skips 1 byte on X86 is that X86 puts no 414 restriction on instruction alignment, but other architectures enforce some requirements on this aspect. 415 416 To customise how many bytes to skip when encountering data, a `Callback` delegate can optonally be setup 417 to return the corresponding number. 418 419 Params: 420 mnemonic = The mnemonic to use for representing skipped data 421 callback = The optional callback to use for handling bytes that cannot be interpreted as an instruction. 422 423 Example: 424 --- 425 // Custom data that can be referred to in a callback delegate 426 struct CallbackData{ 427 int bytesToSkip; 428 } 429 auto myData = CallbackData(1); 430 size_t myCallback(in ubyte[] code, size_t offset) { 431 return myData.bytesToSkip++; // Always skip one more byte when encountering data 432 } 433 cs.skipData = true; // Enable skipdata mode 434 cs.setupSkipdata("db", &myCallback); // Use custom callback, and "db" as custom mnemonic for data 435 --- 436 */ 437 void setupSkipdata(in string mnemonic = ".byte", Callback callback = null){ 438 if(!mnemonic) 439 throw new CapstoneException("Invalid mnemonic", ErrorCode.InvalidOption); 440 this.mnemonic = mnemonic; 441 this.callback = callback; 442 443 auto setup = cs_opt_skipdata(this.mnemonic.ptr, this.callback ? &cCallback : null, &this.callback); 444 cs_option(handle, cs_opt_type.CS_OPT_SKIPDATA_SETUP, cast(size_t)&setup).checkErrno; 445 } 446 447 /** Disassemble binary code, given the code buffer, start address and number of instructions to be decoded 448 449 For systems with scarce memory, the API `disasmIter` might be a better choice than `disasm` 450 Params: 451 code = Buffer containing raw binary code to be disassembled 452 address = Address of the first instruction in given raw code buffer 453 count = Number of instructions to be disassembled, or 0 to get all of them 454 Returns: The successfully disassembled instructions 455 456 Example: 457 --- 458 auto CODE = cast(ubyte[])"\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92"; 459 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); // Initialise x86 32bit engine 460 auto res = cs.disasm(CODE, 0x1000); // Disassemble, offsetting addresses by 0x1000 461 assert("%s %s".format(res[0].mnemonic, res[0].opStr) == "lea ecx, dword ptr [edx + esi + 8]"); 462 assert("%s %s".format(res[1].mnemonic, res[1].opStr) == "add eax, ebx"); 463 assert("%s %s".format(res[2].mnemonic, res[2].opStr) == "add esi, 0x1234"); 464 --- 465 */ 466 abstract const(Instruction)[] disasm(in ubyte[] code, in ulong address, in size_t count = 0); 467 468 /** Provides a range to iteratively disassemble binary code - one instruction at a time 469 470 Fast API to disassemble binary code, given the code buffer and start address. 471 Provides access to only one disassembled instruction at a time, resulting in a smaller memory footprint. 472 Params: 473 code = Buffer containing raw binary code to be disassembled 474 address = Address of the first instruction in given raw code buffer 475 Returns: An input range over the disassembled instructions 476 477 Example: 478 --- 479 auto CODE = cast(ubyte[])"\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92"; 480 481 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); // Initialise x86 32bit engine 482 auto range = cs.disasmIter(CODE, 0x1000); // Disassemble one instruction at a time, offsetting addresses by 0x1000 483 assert("%s %s".format(range.front.mnemonic, range.front.opStr) == "lea ecx, dword ptr [edx + esi + 8]"); 484 range.popFront; 485 assert("%s %s".format(range.front.mnemonic, range.front.opStr) == "add eax, ebx"); 486 range.popFront; 487 assert("%s %s".format(range.front.mnemonic, range.front.opStr) == "add esi, 0x1234"); 488 range.popFront; 489 assert(range.empty); 490 --- 491 */ 492 abstract InstructionRange disasmIter(in ubyte[] code, in ulong address); 493 } 494 495 /** Encapsulates an architecture-specific instance of the Capstone dissassembly engine 496 497 Note that, in contrast to the base class, the architecture is chosen at compile-time. 498 499 Params: 500 archParam = The architecture this Capstone instance is tailored for 501 */ 502 class CapstoneImpl(Arch archParam) : Capstone { // Actually parametrised by Registers, InstructionId, InstructionDetail and InstructionGroup but those are uniquely implied by the architecture 503 /** Creates an architecture-specific instance with a given mode of interpretation 504 505 Params: 506 modeFlags = The (initial) mode of interpretation, which can still be changed later on 507 */ 508 this(in ModeFlags modeFlags){ 509 super(archParam, modeFlags); 510 } 511 512 override InstructionImpl!archParam[] disasm(in ubyte[] code, in ulong address, in size_t count = 0){ 513 cs_insn* internalInstrs; 514 auto actualCount = cs_disasm(handle, code.ptr, code.length, address, count, &internalInstrs); 515 scope(exit){if(internalInstrs){cs_free(internalInstrs, actualCount);}} 516 cs_errno(handle).checkErrno; 517 518 auto instrAppnd = appender!(InstructionImpl!archParam[]); 519 instrAppnd.reserve(actualCount); 520 foreach(instr; internalInstrs[0..actualCount]) 521 instrAppnd.put(new InstructionImpl!archParam(instr, detail, skipData)); 522 return instrAppnd.data; 523 } 524 525 override InstructionImplRange!archParam disasmIter(in ubyte[] code, in ulong address){ 526 return new InstructionImplRange!archParam(this, code, address); 527 } 528 529 // TODO: Really needed? Almost identical to regular `toString` 530 /** Determines friendly name of a register 531 532 When in diet mode, this API is irrelevant because engine does not store register names 533 Param: 534 regId = Register id 535 Returns: Friendly string representation of the register's name 536 */ 537 string regName(Register!archParam regId) const { 538 if(diet) 539 throw new CapstoneException("Register names are not stored when running Capstone in diet mode", 540 ErrorCode.IrrelevantDataAccessInDietEngine); 541 return cs_reg_name(handle, regId).to!string; 542 } 543 544 // TODO: Really needed? Almost identical to regular `toString` 545 /** Determines friendly name of an instruction 546 547 When in diet mode, this API is irrelevant because engine does not store instruction names 548 Param: 549 instrId = Instruction id 550 Returns: Friendly string representation of the instruction's name 551 */ 552 string instrName(InstructionId!archParam instrId) const { 553 if(diet) 554 throw new CapstoneException("Instruction names are not stored when running Capstone in diet mode", 555 ErrorCode.IrrelevantDataAccessInDietEngine); 556 return cs_insn_name(handle, instrId).to!string; 557 } 558 559 // TODO: Really needed? Almost identical to regular `toString` 560 /** Determines friendly name of a group id (that an instruction can belong to) 561 562 When in diet mode, this API is irrelevant because engine does not store group names 563 Param: 564 groupId = Group id 565 Returns: Friendly string representation of the group's name, or null if `groupId` is invalid 566 */ 567 string groupName(InstructionGroup!archParam groupId) const { 568 if(diet) 569 throw new CapstoneException("Group names are not stored when running Capstone in diet mode", 570 ErrorCode.IrrelevantDataAccessInDietEngine); 571 return cs_group_name(handle, groupId).to!string; 572 } 573 } 574 575 unittest{ 576 const code = cast(ubyte[])"\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92"; 577 auto cs = new CapstoneX86(ModeFlags(Mode.bit16)); 578 cs.disasm(code, 0x1000); 579 cs.mode = ModeFlags(Mode.bit32); 580 cs.disasm(code, 0x1000); 581 } 582 583 // TODO: Try switching to InputRange!Instruction (more restrictive than isInputRange, though) 584 /// An input range that provides access to one disassembled `Instruction` at a time 585 abstract class InstructionRange { 586 @property Instruction front(); 587 @property bool empty(); 588 void popFront(); 589 } 590 static assert(isInputRange!InstructionRange); 591 592 /// An extended `InstructionRange` that provides architecture-specific instructions 593 class InstructionImplRange(Arch arch) : InstructionRange { 594 import core.exception: RangeError; 595 private{ 596 CapstoneImpl!arch cs; 597 const ubyte[] code; // Keep ref, s.t. it cannot be deallocated externally 598 const(ubyte)* pCode; 599 ulong codeLength; 600 ulong address; 601 602 InstructionImpl!arch instr; 603 cs_insn* pInsn; 604 605 bool hasFront; 606 } 607 608 private this(CapstoneImpl!arch cs, in ubyte[] code, in ulong address){ 609 this.cs = cs; 610 this.code = code; 611 this.pCode = code.ptr; 612 this.codeLength = code.length; 613 this.address = address; 614 this.hasFront = true; 615 616 pInsn = cs_malloc(cs.handle); 617 if(!pInsn) 618 throw new CapstoneException("Insufficient memory to allocate an instruction", ErrorCode.OutOfMemory); 619 popFront; 620 } 621 622 ~this(){ 623 if(pInsn) 624 cs_free(pInsn, 1); 625 } 626 627 /// True if no disassemblable instructions remain 628 @property override bool empty() const {return !hasFront;} 629 630 /** The latest disassembled instruction 631 632 Throws if called on an `empty` range. 633 */ 634 @property override InstructionImpl!arch front() { 635 enforce!RangeError(!empty, "Trying to access an empty range (%s)".format(typeof(this).stringof)); 636 return instr; 637 } 638 639 /** Advances the range, disassembling the next instruction 640 641 Throws if called on an `empty` range. 642 */ 643 override void popFront(){ 644 enforce!RangeError(!empty, "Trying to access an empty range (%s)".format(typeof(this).stringof)); 645 hasFront = cs_disasm_iter(cs.handle, &pCode, &codeLength, &address, pInsn); 646 if(hasFront) 647 instr = new InstructionImpl!arch(*pInsn, cs.detail, cs.skipData); 648 else 649 cs_errno(cs.handle).checkErrno; 650 } 651 } 652 static assert(isInputRange!(InstructionRange)); 653 654 unittest{ 655 auto CODE = cast(ubyte[])"\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92"; 656 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); 657 auto instrs = cs.disasm(CODE, 0x1000); 658 assert(instrs.length == 3); // With skipdata disabled, disassembling will halt when encountering data 659 cs.skipData = true; 660 instrs = cs.disasm(CODE, 0x1000); 661 assert(instrs.length == 6); 662 } 663 664 unittest{ 665 auto CODE = cast(ubyte[])"\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92"; 666 // Custom data that can be referred to in a callback delegate 667 struct CallbackData{ 668 int bytesToSkip; 669 } 670 auto myData = CallbackData(1); 671 size_t myCallback(in ubyte[] code, size_t offset) { 672 return myData.bytesToSkip++; // Always skip one more byte when encountering data 673 } 674 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); 675 cs.skipData = true; // Enable skipdata mode 676 cs.setupSkipdata("db", &myCallback); // Use custom callback, and "db" as custom mnemonic for data 677 const instrs = cs.disasm(CODE, 0x1000); // Disassemble (offsetting addresses by 0x1000) 678 assert(instrs.length == 6); 679 } 680 681 unittest{ 682 auto CODE = cast(ubyte[])"\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92"; 683 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); // Initialise x86 32bit engine 684 auto res = cs.disasm(CODE, 0x1000); // Disassemble, offsetting addresses by 0x1000 685 assert("%s %s".format(res[0].mnemonic, res[0].opStr) == "lea ecx, dword ptr [edx + esi + 8]"); 686 assert("%s %s".format(res[1].mnemonic, res[1].opStr) == "add eax, ebx"); 687 assert("%s %s".format(res[2].mnemonic, res[2].opStr) == "add esi, 0x1234"); 688 } 689 690 unittest{ 691 auto CODE = cast(ubyte[])"\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92"; 692 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); // Initialise x86 32bit engine 693 auto range = cs.disasmIter(CODE, 0x1000); // Disassemble one instruction at a time, offsetting addresses by 0x1000 694 assert("%s %s".format(range.front.mnemonic, range.front.opStr) == "lea ecx, dword ptr [edx + esi + 8]"); 695 range.popFront; 696 assert("%s %s".format(range.front.mnemonic, range.front.opStr) == "add eax, ebx"); 697 range.popFront; 698 assert("%s %s".format(range.front.mnemonic, range.front.opStr) == "add esi, 0x1234"); 699 range.popFront; 700 assert(range.empty); 701 import core.exception: RangeError; // Once empty, both `front` and `popFront` cannot be accessed 702 assertThrown!RangeError(range.front); 703 assertThrown!RangeError(range.popFront); 704 } 705 706 unittest{ 707 auto CODE = cast(ubyte[])"\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92"; 708 709 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); 710 assert(cs.disasmIter(CODE, 0x1000).array.length == 3); // With skipdata disabled, disassembling will halt when encountering data 711 cs.skipData = true; 712 assert(cs.disasmIter(CODE, 0x1000).array.length == 6); 713 } 714 715 unittest{ 716 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); 717 assert(cs.regName(X86Register.eip) == X86Register.eip.to!string); // Mostly same output as `to!string` 718 assert(cs.regName(X86Register.st7) == "st(7)"); // Differs sometimes though 719 } 720 721 unittest{ 722 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); 723 assert(cs.instrName(X86InstructionId.add) == X86InstructionId.add.to!string); // Mostly same as `to!string` 724 } 725 726 unittest{ 727 auto cs = new CapstoneX86(ModeFlags(Mode.bit32)); 728 assert(cs.groupName(X86InstructionGroup.sse1) == X86InstructionGroup.sse1.to!string); // Mostly same as `to!string` 729 } 730 731 unittest{ 732 enum code = cast(ubyte[])"\x55"; 733 auto cs = new CapstoneX86(ModeFlags(Mode.bit64)); 734 cs.detail = true; 735 736 auto range = cs.disasmIter(code, 0x1000); 737 auto pushInstr = range.front; // 0x55 disassembles to "push" 738 assert(pushInstr.mnemonic == "push"); 739 assert(pushInstr.isInGroup(X86InstructionGroup.mode64)); // "push" is part of the mode64 instructions 740 assert(pushInstr.readsReg(X86Register.rsp)); // "push" accesses rsp 741 assert(pushInstr.writesReg(X86Register.rsp)); // "push" modifies rsp 742 }