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
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:
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.
5 api are exported from this library, the signatures are simple as illustrated following:
func Hook(target, replace, trampoline interface{}) error;
func UnHook(target interface{}) error;
func HookByIndirectJmp(target, replace, trampoline interface{});
func HookMethod(instance interface{}, method string, replace, trampoline interface{}) error;
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:
rdx is the context register used by compiler to access funcval.
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.