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  }