golang 实现 LD_PRELOAD 拦截 libc

1,834 阅读1分钟
基于的工作:Hooking libc using Go shared libraries

先把 socket() 的 libc 调用拦截下来

package main

// #cgo LDFLAGS: -ldl
// #include <stddef.h>
// #include <netinet/in.h>
// #include "network_hook.h"
import "C"
import (
	"fmt"
	"syscall"
)

func init() {
	C.libc_hook_init()
}

//export socket
func socket(domain C.int, type_ C.int, protocol C.int) C.int {
	fmt.Println(fmt.Sprintf("open socket from thread id: %v", syscall.Gettid()))
	return C.orig_socket(domain, type_, protocol)
}

func main() {
}

把这个编译成 so

go build -buildmode=c-shared -o libmotrix.so main.go

然后调用

LD_PRELOAD=libmotrix.so curl http://www.baidu.com

就会把 socket 的 lib 调用给拦截下来

把 socket() 透明转交给 libc

network_hook.h

#ifndef __DLSYM_WRAPPER_H__
#define __DLSYM_WRAPPER_H__

void libc_hook_init();
int orig_socket(int, int, int);

#endif

network_hook.c

#include <dlfcn.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <math.h>
#include "network_hook.h"
#include "_cgo_export.h"

#define RTLD_NEXT	((void *) -1l)

#define HOOK_SYS_FUNC(name) if( !orig_##name##_func ) { orig_##name##_func = (name##_pfn_t)dlsym(RTLD_NEXT,#name); }

typedef int (*socket_pfn_t)(int, int, int);
static socket_pfn_t orig_socket_func;

void libc_hook_init() {
    HOOK_SYS_FUNC( socket );
}

int orig_socket(int domain, int type, int protocol) {
    return orig_socket_func(domain, type, protocol);
}

其中

  • orig_socket 是函数暴露给 golang 调用
  • orig_socket_func 是函数指针,指向了libc的原来的实现
  • socket_pfn_t 是socket()这个函数指针的类型定义

原来文章里的实现在我的机器上报错,原因未知。感觉这样用 cgo 包装一下更简单直接一些,还少了一次反射。