您现在的位置是:首页 >其他 >golang程序能不能用LD_PRELOAD进行hook?网站首页其他

golang程序能不能用LD_PRELOAD进行hook?

路边闲人2 2024-06-26 14:23:58
简介golang程序能不能用LD_PRELOAD进行hook?

答案是不能。

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
", ret, strerror(errno));
    }
    printf("listen bind ok.
");
    

    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
", ret, strerror(errno));
    }
    printf("listen hook ok.
");
    

    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上。

Hooking Go from Rust - Hitchhiker’s Guide to the Go-laxy MetalBear ? - Tools for Backend Engineers

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.

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

 Oct 31, 2019 at 14:27

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。