github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/hooker/breakpoint_flow_runner.go (about) 1 package hooker 2 3 import ( 4 "time" 5 "unsafe" 6 7 "github.com/Rookout/GoSDK/pkg/augs" 8 "github.com/Rookout/GoSDK/pkg/logger" 9 "github.com/Rookout/GoSDK/pkg/rookoutErrors" 10 "github.com/Rookout/GoSDK/pkg/services/assembler" 11 "github.com/Rookout/GoSDK/pkg/services/callstack" 12 "github.com/Rookout/GoSDK/pkg/services/instrumentation/module" 13 "github.com/Rookout/GoSDK/pkg/services/safe_hook_installer" 14 "github.com/Rookout/GoSDK/pkg/services/safe_hook_validator" 15 "github.com/Rookout/GoSDK/pkg/services/suspender" 16 ) 17 18 const ( 19 InstallHookNumAttempts = 10 20 InstallHookAttemptDelayMS = 10 21 InstallHookWatchDogTimeoutMS = 60000 22 ) 23 24 type BreakpointFlowRunner interface { 25 GetAddressMapping() ([]module.AddressMapping, []module.AddressMapping, error) 26 ApplyBreakpointsState() error 27 IsUnhookedState() bool 28 ID() int 29 UnhookedFunctionStateID() int 30 } 31 32 type BreakpointFlowRunnerInitializationInfo struct { 33 Function *augs.Function 34 BPCallback uintptr 35 } 36 37 type breakpointFlowRunner struct { 38 installerCreator safe_hook_installer.SafeHookInstallerCreator 39 nativeAPI NativeHookerAPI 40 info BreakpointFlowRunnerInitializationInfo 41 stateID int 42 function *augs.Function 43 jumpDestination uintptr 44 } 45 46 func (c *breakpointFlowRunner) GetAddressMapping() ([]module.AddressMapping, []module.AddressMapping, error) { 47 return c.nativeAPI.GetInstructionMapping(c.function.Entry, c.function.End, c.stateID) 48 } 49 50 func (c *breakpointFlowRunner) installHook() (err error) { 51 var shi safe_hook_installer.SafeHookInstaller 52 var numGoroutines int 53 hookWriter, err := c.getHookWriter() 54 if err != nil { 55 return err 56 } 57 hookManager := safe_hook_installer.NewHookManager(hookWriter.HookAddr, hookWriter.Hook, c.nativeAPI.GetFunctionType) 58 59 err = hookWriter.AddWritePermission() 60 if err != nil { 61 return err 62 } 63 defer func() { 64 restoreErr := hookWriter.RestorePermissions() 65 if restoreErr != nil { 66 logger.Logger().WithError(restoreErr).Warning("Unable to restore memory section to original permissions") 67 } 68 69 70 if err == nil { 71 err = restoreErr 72 } 73 }() 74 75 for attempt := 1; attempt <= InstallHookNumAttempts; attempt++ { 76 if attempt > 1 { 77 78 time.Sleep(InstallHookAttemptDelayMS * time.Millisecond) 79 } 80 shi, err = c.installerCreator(c.function.Entry, c.function.End, c.stateID, hookWriter, hookManager, 81 suspender.GetSuspender(), callstack.NewStackTraceBuffer(), &safe_hook_validator.ValidatorFactoryImpl{}) 82 if err != nil { 83 84 break 85 } 86 err = c.nativeAPI.TriggerWatchDog(InstallHookWatchDogTimeoutMS) 87 if err != nil { 88 logger.Logger().Warningf("Failed to trigger the watchdog on attempt #%d. Reason: %v", attempt, err) 89 continue 90 } 91 numGoroutines, err = shi.InstallHook() 92 c.nativeAPI.DefuseWatchDog() 93 if err != nil { 94 logger.Logger().Warningf("Failed to install the hook on attempt #%d. Detected %d non-system goroutines. Reason: %v", attempt, numGoroutines, err) 95 continue 96 } 97 98 c.function.Hooked = !c.IsUnhookedState() 99 break 100 } 101 if err == nil { 102 103 notifyErr := c.nativeAPI.ApplyBreakpointsState(c.function.Entry, c.function.End, c.stateID) 104 if notifyErr != nil { 105 logger.Logger().Warningf("Failed to notify the native on installing the breakpoint (%v). However, the breakpoint was installed successfully.", notifyErr) 106 107 } 108 } 109 return err 110 } 111 112 func (c *breakpointFlowRunner) getHookWriter() (*safe_hook_installer.HookWriter, rookoutErrors.RookoutError) { 113 hookAddr, err := c.nativeAPI.GetHookAddress(c.function.Entry, c.function.End, c.stateID) 114 if err != nil { 115 return nil, err 116 } 117 hook, err := c.buildHook(hookAddr) 118 if err != nil { 119 return nil, err 120 } 121 return safe_hook_installer.NewHookWriter(hookAddr, hook), nil 122 } 123 124 func (c *breakpointFlowRunner) buildHook(hookAddr uintptr) ([]byte, rookoutErrors.RookoutError) { 125 if c.IsUnhookedState() { 126 return c.function.PatchedBytes, nil 127 } 128 129 hook, err := assembler.EncodeJmp(hookAddr, c.jumpDestination) 130 if err != nil { 131 return nil, err 132 } 133 if c.function.PatchedBytes == nil { 134 135 c.function.PatchedBytes = make([]byte, len(hook)) 136 for i := range hook { 137 c.function.PatchedBytes[i] = *((*byte)(unsafe.Pointer(hookAddr + uintptr(i)))) 138 } 139 } 140 return hook, nil 141 } 142 143 func (c *breakpointFlowRunner) IsUnhookedState() bool { 144 return c.stateID == c.UnhookedFunctionStateID() 145 } 146 147 func (c *breakpointFlowRunner) ID() int { 148 return c.stateID 149 } 150 151 func (c *breakpointFlowRunner) UnhookedFunctionStateID() int { 152 return 0 153 } 154 155 func NewFlowRunner(nativeAPI NativeHookerAPI, initInfo BreakpointFlowRunnerInitializationInfo, requiredBreakpoints []*augs.BreakpointInstance, installerFactory safe_hook_installer.SafeHookInstallerCreator) (*breakpointFlowRunner, error) { 156 stateID, err := nativeAPI.RegisterFunctionBreakpointsState(initInfo.Function.Entry, initInfo.Function.End, requiredBreakpoints, initInfo.BPCallback, initInfo.Function.Prologue, initInfo.Function.StackFrameSize > 0) 157 if err != nil { 158 return nil, err 159 } 160 161 runner := &breakpointFlowRunner{ 162 installerCreator: installerFactory, 163 info: initInfo, 164 stateID: stateID, 165 function: initInfo.Function, 166 nativeAPI: nativeAPI, 167 } 168 if runner.stateID == initInfo.Function.FunctionCopyStateID { 169 170 171 runner.stateID = runner.UnhookedFunctionStateID() 172 } 173 return runner, nil 174 }