gohook

package module
v1.1.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 24, 2022 License: MIT Imports: 12 Imported by: 0

README

Build Status

Gohook

A funny library to hook golang function dynamically at runtime, enabling functionality like patching in dynamic language.

The most significant feature this library provided that makes it distinguished from others is that it supports calling back to the original function.

Read following blogpost for further explanation of the implementation detail: 1,2

How it works

The general idea of this library is that gohook will find out the address of a go function and then insert a few jump instructions to redirect execution flow to the new function.

there are a few steps to perform a hook:

  1. find out the address of a function, this can be accomplished by standard reflect library.
  2. inject jump code into target function, with carefully crafted binary instruction.
  3. implement trampoline function to enable calling back to the original function.

It may seem risky and dangerous to perform operations like these at first glance, but this is actually common practice in c/c++ though, you can google it, search for "hot patching" something like that for more information.

Using gohook

5 api are exported from this library, the signatures are simple as illustrated following:

  1. func Hook(target, replace, trampoline interface{}) error;
  2. func UnHook(target interface{}) error;
  3. func HookByIndirectJmp(target, replace, trampoline interface{});
  4. func HookMethod(instance interface{}, method string, replace, trampoline interface{}) error;
  5. func UnHookMethod(instance interface{}, method string) error;

The first 3 functions are used to hook/unhook regular functions, the rest are for instance method, as the naming implies(essentially, HookMethod(obj,x,y,z) is the same as Hook(ObjType.x,y,z)).

Basically, you can just call gohook.Hook(fmt.Printf, myPrintf, myPrintfTramp) to hook the fmt.Printf in the standard library.

Trampolines here serves as a shadow function after the target function is hooked, think of it as a copy of the original target function.

In situation where calling back to the original function is not needed, trampoline can be passed a nil value.

HookByIndirectJmp() differs from Hook() in that it uses rdx to perform an indirect jump from a funcval, and:

  1. rdx is the context register used by compiler to access funcval.
  2. funcval contains extra information for a closure, which is used by compiler and runtime.

this makes it possible to hook closure function and function created by reflect.MakeFunc(), in a less compatible way, since the implementaion of this hook has to guess the memory layout of a reflect.Value object, which may vary from different version of runtime.

package main

import (
	"fmt"
	"github.com/brahma-adshonor/gohook"
	"os"
)

func myPrintln(a ...interface{}) (n int, err error) {
    fmt.Fprintln(os.Stdout, "before real Printfln")
    return myPrintlnTramp(a...)
}

func myPrintlnTramp(a ...interface{}) (n int, err error) {
    // a dummy function to make room for a shadow copy of the original function.
    // it doesn't matter what we do here, just to create an addressable function with adequate size.
    myPrintlnTramp(a...)
    myPrintlnTramp(a...)
    myPrintlnTramp(a...)

    for {
        fmt.Printf("hello")
    }

    return 0, nil
}

func main() {
	gohook.Hook(fmt.Println, myPrintln, myPrintlnTramp)
	fmt.Println("hello world!")
}

For more usage example, please refer to the example folder.

Notes

  1. 32 bit mode may not work, far jump is not handled.
  2. trampoline is used to make room for the original function, it will be overwrited.
  3. in case of small function which may be inlined, gohook may fail:
    • disable inlining by passig -gcflags=-l to build cmd.
  4. this library is created for integrated testing, and not fully tested in production(yet), user discretion is advised.
  5. escape analysis may be influenced:
    • deep copy arguments if you need to copy argument from replacement function(see func_stack_test.go).
    • escape those arguments from trampoline(by passing it to a goroutine or to other function that can escape it) if that argument is allocated from the replacement function.

Documentation

Index

Constants

View Source
const (
	FT_CondJmp  = 1
	FT_JMP      = 2
	FT_CALL     = 3
	FT_RET      = 4
	FT_OTHER    = 5
	FT_INVALID  = 6
	FT_SKIP     = 7
	FT_OVERFLOW = 8
)

Variables

This section is empty.

Functions

func CopyFunction

func CopyFunction(allowCall bool, from, to interface{}, info *CodeInfo) ([]byte, error)

func CopyInstruction

func CopyInstruction(location uintptr, data []byte)

func FixOneInstruction

func FixOneInstruction(mode int, fix_recursive_call bool, startAddr, curAddr uintptr, code []byte, to uintptr, to_sz int) (int, int, []byte)

func GetArchMode

func GetArchMode() int

func GetFuncAddr

func GetFuncAddr(f interface{}) uintptr

func GetFuncInstSize

func GetFuncInstSize(f interface{}) uint32

func GetFuncSizeByGuess

func GetFuncSizeByGuess(mode int, start uintptr, minimal bool) (uint32, error)

func GetInsLenGreaterThan

func GetInsLenGreaterThan(mode int, data []byte, least int) int

func Hook

func Hook(target, replacement, trampoline interface{}) error

func HookByIndirectJmp

func HookByIndirectJmp(target, replacement, trampoline interface{}) error

func HookMethod

func HookMethod(instance interface{}, method string, replacement, trampoline interface{}) error

func ResetFuncPrologue

func ResetFuncPrologue()

func SetFuncPrologue

func SetFuncPrologue(mode int, data []byte)

func SetMinJmpCodeSize

func SetMinJmpCodeSize(sz int)

func ShowDebugInfo

func ShowDebugInfo() string

func UnHook

func UnHook(target interface{}) error

func UnHookMethod

func UnHookMethod(instance interface{}, methodName string) error

Types

type CodeFix

type CodeFix struct {
	Code    []byte
	Addr    uintptr
	Foreign bool
}

func FixTargetFuncCode

func FixTargetFuncCode(mode int, start uintptr, funcSz uint32, to uintptr, move_sz int) ([]CodeFix, error)

FixTargetFuncCode fix function code starting at address [start] parameter 'funcSz' may not specify, in which case, we need to find out the end by scanning next prologue or finding invalid instruction. 'to' specifys a new location, to which 'move_sz' bytes instruction will be copied since move_sz byte instructions will be copied, those relative jump instruction need to be fixed.

type CodeInfo

type CodeInfo struct {
	How            string
	Origin         []byte
	Fix            []CodeFix
	TrampolineOrig []byte
}

func HookFunction

func HookFunction(rdxIndirect bool, target, replace, trampoline uintptr) (*CodeInfo, error)

type ElfInfo

type ElfInfo struct {
	CurFile string
	Symbol  SymbolSlice
}

func NewElfInfo

func NewElfInfo() (*ElfInfo, error)

func (*ElfInfo) GetFuncSize

func (ei *ElfInfo) GetFuncSize(addr uintptr) (uint32, error)

type HookInfo

type HookInfo struct {
	Mode        int
	Info        *CodeInfo
	Target      reflect.Value
	Replacement reflect.Value
	Trampoline  reflect.Value
}

type SymbolSlice

type SymbolSlice []elf.Symbol

func (SymbolSlice) Len

func (a SymbolSlice) Len() int

func (SymbolSlice) Less

func (a SymbolSlice) Less(i, j int) bool

func (SymbolSlice) Swap

func (a SymbolSlice) Swap(i, j int)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL