github.com/bytedance/mockey@v1.2.10/internal/monkey/inst/disasm_amd64.go (about) 1 /* 2 * Copyright 2022 ByteDance Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package inst 18 19 import ( 20 "reflect" 21 "unsafe" 22 23 "github.com/bytedance/mockey/internal/monkey/common" 24 "github.com/bytedance/mockey/internal/tool" 25 "golang.org/x/arch/x86/x86asm" 26 ) 27 28 func calcFnAddrRange(name string, fn func()) (uintptr, uintptr) { 29 v := reflect.ValueOf(fn) 30 var start, end uintptr 31 start = v.Pointer() 32 maxScan := 2000 33 code := common.BytesOf(v.Pointer(), 2000) 34 pos := 0 35 36 for pos < maxScan { 37 inst, err := x86asm.Decode(code[pos:], 64) 38 tool.Assert(err == nil, err) 39 40 args := []interface{}{name, inst.Op} 41 for i := range inst.Args { 42 args = append(args, inst.Args[i]) 43 } 44 tool.DebugPrintf("init: <%v>\t%v\t%v\t%v\t%v\t%v\t%v\n", args...) 45 46 if inst.Op == x86asm.RET { 47 end = start + uintptr(pos) 48 tool.DebugPrintf("init: %v(%v,%v)\n", name, start, end) 49 return start, end 50 } 51 52 pos += int(inst.Len) 53 } 54 tool.Assert(false, "%v ret not found", name) 55 return 0, 0 56 } 57 58 func Disassemble(code []byte, required int, checkLen bool) int { 59 var pos int 60 var err error 61 var inst x86asm.Inst 62 63 for pos < required { 64 inst, err = x86asm.Decode(code[pos:], 64) 65 tool.Assert(err == nil, err) 66 tool.DebugPrintf("Disassemble: inst: %v\n", inst) 67 tool.Assert(inst.Op != x86asm.RET || !checkLen, "function is too short to patch") 68 pos += inst.Len 69 } 70 return pos 71 } 72 73 func GetGenericJumpAddr(addr uintptr, maxScan uint64) uintptr { 74 code := common.BytesOf(addr, int(maxScan)) 75 var pos uint64 76 var err error 77 var inst x86asm.Inst 78 79 allAddrs := []uintptr{} 80 for pos < maxScan { 81 inst, err = x86asm.Decode(code[pos:], 64) 82 tool.Assert(err == nil, err) 83 84 args := []interface{}{inst.Op} 85 for i := range inst.Args { 86 args = append(args, inst.Args[i]) 87 } 88 tool.DebugPrintf("%v\t%v\t%v\t%v\t%v\t%v\n", args...) 89 90 if inst.Op == x86asm.RET { 91 break 92 } 93 94 if inst.Op == x86asm.CALL { 95 rel := int32(inst.Args[0].(x86asm.Rel)) 96 fnAddr := calcAddr(uintptr(unsafe.Pointer(&code[0]))+uintptr(pos+uint64(inst.Len)), rel) 97 isExtraCall, extraName := isGenericProxyCallExtra(fnAddr) 98 tool.DebugPrintf("found CALL, raw is: %x, rel: %v, raw is: %x, fnAddr: %v, isExtraCall: %v, extraName: %v\n", inst.String(), rel, fnAddr, isExtraCall, extraName) 99 if !isExtraCall { 100 allAddrs = append(allAddrs, fnAddr) 101 } 102 } 103 pos += uint64(inst.Len) 104 } 105 tool.Assert(len(allAddrs) == 1, "invalid callAddr: %v", allAddrs) 106 return allAddrs[0] 107 } 108 109 func calcAddr(from uintptr, rel int32) uintptr { 110 tool.DebugPrintf("calc CALL addr, from: %x(%v) CALL: %x\n", from, from, rel) 111 112 var dest uintptr 113 if rel < 0 { 114 dest = from - uintptr(uint32(-rel)) 115 } else { 116 dest = from + uintptr(rel) 117 } 118 119 tool.DebugPrintf("L->H:%v rel: %v from: %x(%v) dest: %x(%v), distance: %v\n", rel > 0, rel, from, from, dest, dest, from-dest) 120 return dest 121 }