前言
最近想研究一下go hook 的一些方式,发现有些函数都是没有方法体, 或者说是是一些私有方法
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)
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 的用法
- 官方解释
这个特殊的指令的作用域并不是紧跟的下一行代码,而是同一个包下生效。//go:linkname告诉 Go 的编译器
把本地的(私有)变量或者方法localname链接到指定的变量或者方法importpath.name。简单来说,
localname import.name指向的变量或者方法是同一个。因为这个指令破坏了类型系统和包的模块化原则,只
有在引入 unsafe 包的前提下才能使用这个指令。
- 这个时候我们发现这些没有方法体的方法,在
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 这类函数
需求在使用
byte数组转string时需要hookpackage 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可以看到能准确拿到函数执行的参数和返回值之类的
gohook 类似这样的
hook框架有很多,我们单独挑选一种来进行尝试,并能得到我们想要的结果
总结
如果我们需要hook 这类比较底层函数时没什么思路时,我们可以尝试使用这类方法进行尝试.但是也要注意官方推荐的少用,并严格按照标准格式进行实现