go hook 的一些方式


前言

最近想研究一下go hook 的一些方式,发现有些函数都是没有方法体, 或者说是是一些私有方法

  1. time.Now, Now(), time.Sleep,reflet.markchan
// example
// Provided by package runtime.
func now() (sec int64, nsec int32, mono int64)

func Sleep(d Duration)

func makechan(typ *rtype, size int) (ch unsafe.Pointer)

  1. runtime->string-->slicebytetostring
func slicebytetostring(buf *tmpBuf, ptr *byte, n int) (str string) {
	if n == 0 {
		// Turns out to be a relatively common case.
		// Consider that you want to parse out data between parens in "foo()bar",
		// you find the indices and convert the subslice to string.
		return ""
	}
	if raceenabled {
		racereadrangepc(unsafe.Pointer(ptr),
			uintptr(n),
			getcallerpc(),
			funcPC(slicebytetostring))
	}
	if msanenabled {
		msanread(unsafe.Pointer(ptr), uintptr(n))
	}
	if n == 1 {
		p := unsafe.Pointer(&staticuint64s[*ptr])
		if sys.BigEndian {
			p = add(p, 7)
		}
		stringStructOf(&str).str = p
		stringStructOf(&str).len = 1
		return
	}

	var p unsafe.Pointer
	if buf != nil && n <= len(buf) {
		p = unsafe.Pointer(buf)
	} else {
		p = mallocgc(uintptr(n), nil, false)
	}
	stringStructOf(&str).str = p
	stringStructOf(&str).len = n
	memmove(p, unsafe.Pointer(ptr), uintptr(n))
	return
}

go:linkname 的用法

  1. 官方解释
这个特殊的指令的作用域并不是紧跟的下一行代码,而是同一个包下生效。//go:linkname告诉 Go 的编译器

把本地的(私有)变量或者方法localname链接到指定的变量或者方法importpath.name。简单来说,

localname import.name指向的变量或者方法是同一个。因为这个指令破坏了类型系统和包的模块化原则,只

有在引入 unsafe 包的前提下才能使用这个指令。
  1. 这个时候我们发现这些没有方法体的方法,在src/runtime
//go:linkname time_now time.now
func time_now() (sec int64, nsec int32, mono int64) {
    sec, nsec = walltime()
    return sec, nsec, nanotime()
}

原来使用go:linkname这种用法, 这种有点像重写方法,这样就可以运行到某个方法体时自动从定向到我们重写的方法,

(自我理解)

我们实战一下

实战项目

运行发现会进入到我们自己自己重写的方法中去。

Hook 这类函数

  1. 需求在使用byte数组转string时需要hook

    package main
    
    import (
    	"fmt"
    	"github.com/brahma-adshonor/gohook"
    	_ "unsafe"
    )
    
    
    const tmpStringBufSize = 32
    
    type tmpBuf [tmpStringBufSize]byte
    
    //go:linkname slicebytetostring runtime.slicebytetostring
    func slicebytetostring(buf *tmpBuf, ptr *byte, n int) (str string)
    
    func slicebytetostringR(buf *tmpBuf, ptr *byte, n int) (str string) {
    	e := slicebytetostringT(buf, ptr, n)
    	fmt.Println("str", e)
    	return e
    }
    
    func slicebytetostringT(buf *tmpBuf, ptr *byte, n int) (str string) {
    	return ""
    }
    
    
    func main()  {
    	gohook.Hook(slicebytetostring, slicebytetostringR, slicebytetostringT)
    	s := string([]byte{65, 66, 67,65, 66, 67})
    	fmt.Println(s) // ABC
    }
    // str ABCABC
    // ABCABC
  2. 可以看到能准确拿到函数执行的参数和返回值之类的

  3. gohook 类似这样的hook框架有很多,我们单独挑选一种来进行尝试,并能得到我们想要的结果

总结

如果我们需要hook 这类比较底层函数时没什么思路时,我们可以尝试使用这类方法进行尝试.但是也要注意官方推荐的少用,并严格按照标准格式进行实现


文章作者: jusk9527
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 jusk9527 !
  目录