1 /// Object-oriented wrapper of disassembled instructions 2 module capstone.instruction; 3 4 import std.conv: to; 5 import std.typecons: Nullable; 6 import std.algorithm: canFind; 7 8 import capstone.api; 9 import capstone.capstone; 10 import capstone.detail; 11 import capstone.error; 12 import capstone.internal; 13 import capstone.instructiongroup; 14 import capstone.register; 15 16 /// Architecture-independent instruction base class 17 abstract class Instruction { 18 private: 19 const Capstone cs; 20 cs_insn* internal; // Have to keep it around for cs_regs_access 21 22 public: 23 /// Address (EIP) of this instruction 24 auto address() const {return internal.address;} 25 /// Machine bytes of this instruction 26 auto bytes() const {return internal.bytes[0..internal.size];} 27 /// Ascii text of instruction mnemonic 28 auto mnemonic() const {return internal.mnemonic.ptr.to!string;} 29 /// Ascii text of instruction operands 30 auto opStr() const {return internal.op_str.ptr.to!string;} 31 32 private this(in Capstone cs, cs_insn* internal){ 33 this.cs = cs; 34 this.internal = internal; 35 } 36 37 ~this(){ 38 assert(internal); 39 cs_free(internal, 1); 40 } 41 42 /// Retrieves instruction's id as plain integer 43 auto idAsInt() const {return internal.id;} 44 45 /** Returns friendly string representation of an instruction's name 46 47 When in diet mode, this API is irrelevant because engine does not store instruction names. 48 */ 49 string name() const { 50 if(diet) 51 throw new CapstoneException("Instruction names are not stored when running Capstone in diet mode", 52 ErrorCode.IrrelevantDataAccessInDietEngine); 53 return cs_insn_name(cs.handle, internal.id).to!string; // TODO: Error handling 54 } 55 56 /** More details about the instruction 57 58 Note that this is only available if both requirements are met: 59 $(OL 60 $(LI details are enabled) 61 $(LI the engine is not in Skipdata mode)) 62 */ 63 const(Detail) detail() const; 64 65 /// Checks whether the instruction belongs to the instruction group `group` 66 bool isInGroup(in InstructionGroup group) const { 67 return cs_insn_group(cs.handle, internal, group._id); // TODO: Error handling 68 } 69 70 /// Checks if the instruction IMPLICITLY uses a particular register 71 bool reads(in Register reg) const { 72 return cs_reg_read(cs.handle, internal, reg._id); // TODO: Error handling 73 } 74 75 /// Checks if the instruction IMPLICITLY modifies a particular register 76 bool writes(in Register reg) const { 77 return cs_reg_write(cs.handle, internal, reg._id); // TODO: Error handling 78 } 79 80 /// Retrieves both the implicitly and explicitly written registers 81 const(Register)[] writes() const; 82 83 /// Retrieves both the implicitly and explicitly read registers 84 const(Register)[] reads() const; 85 } 86 87 /** Class template for architecture-specific instructions 88 89 Note that all architecture-specific instances, like `X86Instruction`, instantiate and derive from this one. 90 */ 91 abstract class InstructionImpl(TId, TRegister, TDetail) : Instruction if(is(TId == enum)) { // TODO: isRegister, isDetail 92 private Nullable!TDetail _detail; 93 94 package this(in Capstone cs, cs_insn* internal){ 95 super(cs, internal); 96 if(cs.detail && !cs.skipData) 97 _detail = new TDetail(cs, internal.detail); 98 } 99 100 /// Retrieves instruction's id 101 auto id() const {return internal.id.to!TId;} 102 103 override const(TDetail) detail() const { 104 if(_detail.isNull) 105 throw new CapstoneException("Trying to access unavailable instruction detail", ErrorCode.UnavailableInstructionDetail); 106 return _detail.get; 107 } 108 109 private const(TRegister)[] accessedRegs(in bool writeAccess) const { 110 import std.algorithm: map; 111 import std.array: array; 112 113 if(diet) 114 throw new CapstoneException("Registers accessed by an instruction are not stored when running Capstone in diet mode", 115 ErrorCode.IrrelevantDataAccessInDietEngine); 116 cs_regs read, write; 117 ubyte numRead, numWrite; 118 cs_regs_access(cs.handle, internal, &read, &numRead, &write, &numWrite).checkErrno; 119 auto regs = writeAccess ? write[0..numWrite] : read[0..numRead]; 120 return regs.map!(reg => new TRegister(cs, reg)).array; 121 } 122 123 override const(TRegister)[] writes() const {return accessedRegs(true);} 124 override const(TRegister)[] reads() const {return accessedRegs(false);} 125 }