工作中遇到的问题 -- Go暴力拦截函数库gohook

Gohook

gohook有以下优点:

  • 跳转效率更高
  • 更安全可靠
  • 支持回调旧函数(最大优点)
  • 不依赖 runtime 内部实现

gohook 实现了对函数的暴力拦截,无论是普通函数,还是成员函数都可以强行拦截替换,并支持回调原来的旧函数。

gohook 有以下几个方法 :

  • func Hook(target, replace, trampoline interface{}) error;
  • func UnHook(target interface{}) error;
  • func HookMethod(instance interface{}, method string, replace, trampoline interface{}) error;
  • func UnHookMethod(instance interface{}, method string) error;
  • func HookByIndirectJmp(target, replace, trampoline interface{});

一般来说我们使用前面四种方法就已经可以满足日常需求了:

对于 Hook 方法,其接受三个参数,第一个参数是要 hook 的目标原函数,第二个参数是用来替换的函数,第三个参数用来支持回调旧函数。当 hook 完成后,会调用 trampoline,其相当于调用旧的目标函数(target),第三个参数可以传入 nil,此时表示不需要支持回调旧函数。

func originFunc() {
   str := "Hi, origin func"
   fmt.Println(str)
}

func monkeyFunc() {
   str := "Hi, monkey func"
   fmt.Println(str)
}

func main() {
   gohook.Hook(originFunc, monkeyFunc, nil)
   originFunc()
}

运行结果是:

Hi, monkey func

替换后回调原函数:

func originFunc() {
	str := "Hi, origin func"
	fmt.Println(str)
}

func monkeyFunc() {
	str := "Hi, monkey func"
	fmt.Println(str)
	trampolineFunc()
}

func trampolineFunc() {

}

func main() {
	gohook.Hook(originFunc, monkeyFunc, trampolineFunc)
	originFunc()
}

运行结果:

Hi, monkey func
Hi, origin func

这里的 trampoline 函数内的内容是什么并不重要,只是为了给原函数申请空间。

除了hook普通过程函数外,还可以使用 HookMethod方法 hook 成员函数。

一个实用的场景:比如我们想替换 time.Now() 函数,使其在每次调用时,返回一个固定时间:

func myTime() time.Time {
   return time.Date(2022, 1, 1, 0, 0, 0, 0, &time.Location{})
}

func main() {
   fmt.Println(time.Now())
   gohook.Hook(time.Now, myTime, nil)
   fmt.Println(time.Now())
}

运行结果:

2023-03-09 12:32:17.547981 +0800 CST m=+0.004697201
2022-01-01 00:00:00 +0000 UTC

注意事项

  • gohook 项目主要是用来辅助作测试,最好不要用于生产环境。
  • 过小的函数有可能会变成内联函数,在编译期间被优化掉,这样在运行时就无法 hook了。这也是上面例子中将fmt.Println(str)str := "Hi, origin func"拆开的原因。(编译时加上-gcflags='-m'选项可以查看哪些函数被 inline,另外也可以通过 // go:noline-gcflags=all='-l'来告诉编译器不要对其进行 inline)。
  • 跳转指令取决于硬件平台,该实现只支持 x86/x64 架构。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
当使用 Magic API 添加 URL 拦截器时,你可以通过以下示例来实现: ```python from magic import Magic, Response app = Magic() @app.interceptor(url="/api") def intercept_api(request): # 拦截以 '/api' 开头的 URL 请求 # 在这里可以进行一些处理,比如验证身份、修改请求参数等 # 如果需要终止请求并返回自定义响应,可以使用 Response 对象 # 示例:验证身份,如果未登录则返回 401 错误 if not check_authentication(request): return Response(status=401, body="Unauthorized") # 示例:修改请求参数,添加额外的头信息 request.headers["X-Extra-Header"] = "Example" # 继续处理请求,让其继续向下流动到下一个间件或路由处理函数 return None @app.route("/api/data") def handle_api(request): # 处理 '/api/data' 的请求逻辑,这里可以访问修改后的请求参数 # 示例:打印修改后的头信息 print(request.headers.get("X-Extra-Header")) # 返回响应 return Response(body="Hello, API!") if __name__ == "__main__": app.run() ``` 在上述示例,我们使用 Magic API 创建了一个应用,并添加了一个 URL 拦截器。拦截器使用 `@app.interceptor(url="/api")` 装饰器来指定拦截的 URL,这里是以 '/api' 开头的 URL。在拦截函数 `intercept_api` ,你可以根据需求进行一些处理,比如验证身份、修改请求参数等。如果需要终止请求并返回自定义响应,可以使用 `Response` 对象进行返回。 另外,在示例,我们还定义了一个处理 '/api/data' 请求的路由处理函数 `handle_api`,在这个函数可以访问到拦截器修改后的请求参数。 当你运行示例代码后,应用将监听本地的默认端口(5000),当请求 '/api/data' 时,拦截器会先执行,然后再执行对应的路由处理函数。 希望这个示例对你有所帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡桃姓胡,蝴蝶也姓胡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值