├── LICENSE ├── README.md ├── capstr.go └── capstr_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Ryan Hileman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | capstr 2 | -------- 3 | 4 | Capstone Go bindings facilitating highly optimized printing of disassembly. 5 | 6 | Usage 7 | -------- 8 | 9 | ``` 10 | import "github.com/lunixbochs/capstr" 11 | 12 | engine, err := capstr.New(capstr.ARCH_X86, capstr.MODE_32) 13 | insns, err := engine.Dis(code, addr, insCount) 14 | for _, ins := range insns { 15 | fmt.Printf("%#x: %s %s\n", ins.Addr(), ins.Mnemonic(), ins.OpStr()) 16 | } 17 | ``` 18 | 19 | Benchmarks 20 | ------- 21 | ``` 22 | BenchmarkX86-4 200000 5532 ns/op 1032 B/op 22 allocs/op 23 | BenchmarkGapstone-4 200000 9900 ns/op 9704 B/op 54 allocs/op 24 | ``` 25 | -------------------------------------------------------------------------------- /capstr.go: -------------------------------------------------------------------------------- 1 | package capstr 2 | 3 | import ( 4 | "bytes" 5 | "unsafe" 6 | ) 7 | 8 | // #cgo LDFLAGS: -lcapstone 9 | // #cgo CFLAGS: -O3 -Wall -Werror 10 | // #include 11 | import "C" 12 | 13 | const ( 14 | ARCH_ARM = C.CS_ARCH_ARM 15 | ARCH_ARM64 = C.CS_ARCH_ARM64 16 | ARCH_MIPS = C.CS_ARCH_MIPS 17 | ARCH_X86 = C.CS_ARCH_X86 18 | ARCH_PPC = C.CS_ARCH_PPC 19 | ARCH_SPARC = C.CS_ARCH_SPARC 20 | ARCH_SYSZ = C.CS_ARCH_SYSZ 21 | ARCH_XCORE = C.CS_ARCH_XCORE 22 | ) 23 | 24 | const ( 25 | MODE_LITTLE_ENDIAN = C.CS_MODE_LITTLE_ENDIAN 26 | MODE_ARM = C.CS_MODE_ARM 27 | MODE_16 = C.CS_MODE_16 28 | MODE_32 = C.CS_MODE_32 29 | MODE_64 = C.CS_MODE_64 30 | MODE_THUMB = C.CS_MODE_THUMB 31 | MODE_MCLASS = C.CS_MODE_MCLASS 32 | MODE_V8 = C.CS_MODE_V8 33 | MODE_MICRO = C.CS_MODE_MICRO 34 | MODE_MIPS3 = C.CS_MODE_MIPS3 35 | MODE_MIPS32R6 = C.CS_MODE_MIPS32R6 36 | MODE_V9 = C.CS_MODE_V9 37 | MODE_BIG_ENDIAN = C.CS_MODE_BIG_ENDIAN 38 | MODE_MIPS32 = C.CS_MODE_MIPS32 39 | MODE_MIPS64 = C.CS_MODE_MIPS64 40 | ) 41 | 42 | const ( 43 | OPT_TYPE_INVALID = C.CS_OPT_INVALID 44 | OPT_TYPE_SYNTAX = C.CS_OPT_SYNTAX 45 | OPT_TYPE_DETAIL = C.CS_OPT_DETAIL 46 | OPT_TYPE_MODE = C.CS_OPT_MODE 47 | OPT_TYPE_MEM = C.CS_OPT_MEM 48 | OPT_TYPE_SKIPDATA = C.CS_OPT_SKIPDATA 49 | OPT_TYPE_SKIPDATA_SETUP = C.CS_OPT_SKIPDATA_SETUP 50 | ) 51 | 52 | const ( 53 | OPT_OFF = C.CS_OPT_OFF 54 | OPT_ON = C.CS_OPT_ON 55 | OPT_SYNTAX_DEFAULT = C.CS_OPT_SYNTAX_DEFAULT 56 | OPT_SYNTAX_INTEL = C.CS_OPT_SYNTAX_INTEL 57 | OPT_SYNTAX_ATT = C.CS_OPT_SYNTAX_ATT 58 | OPT_SYNTAX_NOREGNAME = C.CS_OPT_SYNTAX_NOREGNAME 59 | ) 60 | 61 | type Engine struct { 62 | handle C.csh 63 | } 64 | 65 | type CsError C.cs_err 66 | 67 | func (e CsError) Error() string { 68 | return C.GoString(C.cs_strerror(C.cs_err(e))) 69 | } 70 | 71 | func New(arch, mode int) (*Engine, error) { 72 | var handle C.csh 73 | cserr := C.cs_open(C.cs_arch(arch), C.cs_mode(mode), &handle) 74 | if cserr != C.CS_ERR_OK { 75 | return nil, CsError(cserr) 76 | } 77 | C.cs_option(handle, C.CS_OPT_DETAIL, C.CS_OPT_OFF) 78 | return &Engine{handle}, nil 79 | } 80 | 81 | func (e *Engine) Option(opt_type C.cs_opt_type, value C.size_t) error { 82 | if cserr := C.cs_option(e.handle, opt_type, value); cserr != C.CS_ERR_OK { 83 | return CsError(cserr) 84 | } 85 | return nil 86 | } 87 | 88 | func (e *Engine) Dis(code []byte, addr, count uint64) ([]Ins, error) { 89 | if len(code) == 0 { 90 | return nil, nil 91 | } 92 | ptr := (*C.uint8_t)(unsafe.Pointer(&code[0])) 93 | 94 | var disptr *C.cs_insn 95 | num := C.cs_disasm(e.handle, ptr, C.size_t(len(code)), C.uint64_t(addr), C.size_t(count), &disptr) 96 | if num > 0 { 97 | dis := (*[1 << 23]C.cs_insn)(unsafe.Pointer(disptr))[:num] 98 | ret := make([]Ins, num) 99 | for i, ins := range dis { 100 | outins := &ret[i] 101 | // byte array casts of cs_ins fields 102 | mnemonic := (*[32]byte)(unsafe.Pointer(&ins.mnemonic[0])) 103 | byteData := (*[16]byte)(unsafe.Pointer(&ins.bytes[0])) 104 | opstr := (*[160]byte)(unsafe.Pointer(&ins.op_str[0])) 105 | 106 | // populate the return ins fields 107 | outins.addr = uint64(ins.address) 108 | // this is faster than C.GoBytes() 109 | outins.dataSlice = outins.data[:ins.size] 110 | copy(outins.dataSlice, byteData[:]) 111 | 112 | // populate the strings 113 | if off := bytes.IndexByte(mnemonic[:], 0); off > 0 { 114 | outins.mnemonic = string(mnemonic[:off]) 115 | } 116 | if off := bytes.IndexByte(opstr[:], 0); off > 0 { 117 | outins.opstr = string(opstr[:off]) 118 | } 119 | } 120 | C.free(unsafe.Pointer(disptr)) 121 | return ret, nil 122 | } 123 | return nil, CsError(C.cs_errno(e.handle)) 124 | } 125 | 126 | func (e *Engine) Close() error { 127 | return CsError(C.cs_close(&e.handle)) 128 | } 129 | 130 | // conforms to usercorn/models.Ins interface 131 | type Ins struct { 132 | addr uint64 133 | dataSlice []byte 134 | mnemonic string 135 | opstr string 136 | data [16]byte 137 | } 138 | 139 | func (i Ins) Addr() uint64 { 140 | return i.addr 141 | } 142 | 143 | func (i Ins) Bytes() []byte { 144 | return i.dataSlice 145 | } 146 | 147 | func (i Ins) Mnemonic() string { 148 | return i.mnemonic 149 | } 150 | 151 | func (i Ins) OpStr() string { 152 | return i.opstr 153 | } 154 | -------------------------------------------------------------------------------- /capstr_test.go: -------------------------------------------------------------------------------- 1 | package capstr 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bnagy/gapstone" 7 | ) 8 | 9 | var code = []byte("\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34" + 10 | "\x12\x00\x00\x05\x23\x01\x00\x00\x36\x8b\x84\x91" + 11 | "\x23\x01\x00\x00\x41\x8d\x84\x39\x89\x67\x00\x00" + 12 | "\x8d\x87\x89\x67\x00\x00\xb4\xc6") 13 | 14 | func BenchmarkX86(b *testing.B) { 15 | engine, err := New(ARCH_X86, MODE_32) 16 | if err != nil { 17 | b.Fatal(err) 18 | } 19 | b.ResetTimer() 20 | for i := 0; i < b.N; i++ { 21 | _, err := engine.Dis(code, 0x10000, 0) 22 | if err != nil { 23 | b.Fatal(err) 24 | } 25 | } 26 | } 27 | 28 | func BenchmarkGapstone(b *testing.B) { 29 | engine, err := gapstone.New(ARCH_X86, MODE_32) 30 | if err != nil { 31 | b.Fatal(err) 32 | } 33 | engine.SetOption(gapstone.CS_OPT_DETAIL, gapstone.CS_OPT_OFF) 34 | b.ResetTimer() 35 | for i := 0; i < b.N; i++ { 36 | dis, err := engine.Disasm(code, 0x10000, 0) 37 | if err != nil { 38 | b.Fatal(err) 39 | } 40 | s := make([]string, len(dis)) 41 | for i, v := range dis { 42 | s[i] = v.Mnemonic + " " + v.OpStr 43 | } 44 | } 45 | } 46 | --------------------------------------------------------------------------------