初步的答案是不能。
高级一点能行的方法在最后的链接里。
golang自己实现了相关的libc里面的函数,LD_PRELOAD环境变量其实是 libc里面的功能,可以在加载函数时,优先调用 LD_PRELOAD指定的so里面的函数。
可以用以下代码进行测试:
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <malloc.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <errno.h>
typedef int (*orig_socket_func_type)(int domain, int type, int protocol);
typedef int (*orig_accept_func_type)(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
typedef int (*orig_bind_func_type)(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
typedef int (*orig_listen_func_type)(int sockfd, int backlog);
const int DEFAULT_PRIORITY = 6;
int bind(int fd, const struct sockaddr *addr,
socklen_t addrlen)
{
int reuse = 1;
int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
if(ret<0)
{
printf("setsockopt SO_REUSEPORT failed. result:%d, err:%s\n", ret, strerror(errno));
}
printf("listen bind ok.\n");
orig_bind_func_type orig_func;
orig_func = (orig_bind_func_type)dlsym(RTLD_NEXT, "bind");
return orig_func(fd, addr, addrlen);
}
int listen(int fd, int backlog)
{
int reuse = 1;
int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
if(ret<0)
{
printf("setsockopt SO_REUSEPORT failed. result:%d, err:%s\n", ret, strerror(errno));
}
printf("listen hook ok.\n");
orig_listen_func_type orig_func;
orig_func = (orig_listen_func_type)dlsym(RTLD_NEXT, "listen");
return orig_func(fd, backlog);
}
Makefile
CFLAGS += -std=c99
default: prsocket.so
prsocket.so: prsocket.c
gcc $(CFLAGS) -shared -fPIC prsocket.c -o prsocket.so -ldl
clean:
rm *.so
运行nc ,可以看到,hook 是正常工作的。
$> LD_PRELOAD=./prsocket.so nc -l 9000
socket hook ok.
listen bind ok.
listen hook ok.
用golang 进行测试。
最简单的http服务。 http.go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello world")
})
server := http.Server{
Addr: ":9091",
}
fmt.Println("Starting server")
if err := server.ListenAndServe(); err != nil {
panic(err)
}
}
go build http.go
LD_PRELOAD=./prsocket.so ./http
完全没有hook上。
这个链接说可以用Rust写的程序实现对golang的hook,谁懂的可以参考一下。
文章链接:Hooking Go from Rust - Hitchhiker’s Guide to the Go-laxy MetalBear 🐻 - Tools for Backend EngineersHow did we hook Go functions from Rust to work with mirrord? A quick dive into the Go Runtime and switching from the Go stack to the system stack.https://metalbear.co/blog/hooking-go-from-rust-hitchhikers-guide-to-the-go-laxy/Golang doesn’t use libc on Linux, and instead calls syscalls directly. This is mostly harmless for the common developer - they don’t care about the assembly, syscalls, linkage, etc - they just want their binary to work. Therefore, being self-contained provides a very good user experience, as Go applications aren’t dependent on the local machine’s libc.
从作者的这个项目来看,他已经实现了对golang的hook,并且应用到了实际的应用中。
其他参考资料:
ubuntu - libfaketime doesn't work with golang - Stack Overflow
Go compiler produces dynamically linked binaries by default (unless instructed otherwise). Just do ldd
and you will see that the binary is linked to libc.so
(tested on Linux). The difference is that the go packages it depends on, are bundled in the binary. But not the system libraries.
– jimis