Hook技术之dlsym RTLD_NEXT

dlsym 是一个用于动态链接库(Dynamic Shared Object,简称DSO)操作的函数,它允许程序在运行时获取符号(如函数或变量)的地址。RTLD_NEXT 是一个特殊的句柄,用于在共享库中进行符号解析时,表示查找下一个共享库。

dlsym 函数

dlsym 函数的原型如下:

void *dlsym(void *handle, const char *symbol);
  • handle: 指向已打开的共享库的句柄,通常由 dlopen 返回。如果使用特殊句柄 RTLD_NEXT,表示查找下一个共享库。
  • symbol: 需要查找的符号名称。

RTLD_NEXT

RTLD_NEXT 是一个特殊的句柄,表示在共享库的查找链中,查找下一个共享库。这通常用于实现函数的“拦截”或“钩子(hook)”操作,即在自己的实现中调用被替换的原始实现。

例如,假设你有一个共享库A,它定义了一个函数 foo,你希望在另一个共享库B中拦截 foo 的调用,但仍然希望在某些情况下调用原始的 foo。这时可以使用 RTLD_NEXT

示例代码

以下是一个示例,展示了如何使用 dlsymRTLD_NEXT 来实现函数拦截:

原始库(liboriginal.so)

首先,定义一个原始库(liboriginal.so),其包含一个名为 foo 的函数:

// original.c
#include <stdio.h>

void foo() {
    printf("Original foo\n");
}

编译这个文件生成共享库:

gcc -shared -fPIC -o liboriginal.so original.c
拦截库(libhook.so)

接下来,定义一个拦截库(libhook.so),其拦截 foo 函数的调用:

// hook.c
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>

// 拦截的 foo 函数
void foo() {
    printf("Hooked foo\n");

    // 使用 RTLD_NEXT 查找下一个 foo 实现
    void (*original_foo)() = (void (*)())dlsym(RTLD_NEXT, "foo");
    if (original_foo) {
        original_foo(); // 调用原始的 foo 实现
    }
}

编译这个文件生成共享库:

gcc -shared -fPIC -o libhook.so hook.c -ldl
测试程序

最后,编写一个测试程序来使用这些库:

// test.c
#include <stdio.h>

void foo();

int main() {
    foo();
    return 0;
}

编译测试程序:

gcc -o test test.c -L. -loriginal

运行时使用拦截库:

LD_PRELOAD=./libhook.so ./test

预期输出

Hooked foo
Original foo

在这个示例中,libhook.so 中的 foo 函数首先被调用,之后它使用 dlsym(RTLD_NEXT, "foo") 查找并调用原始的 foo 实现。

总结

  • dlsym 用于在动态链接库中查找符号(函数或变量)。
  • RTLD_NEXT 是一个特殊的句柄,表示在共享库查找链中查找下一个共享库。
  • 通过使用 RTLD_NEXT,可以实现函数拦截,同时保留对原始实现的访问。

这种技术在调试、性能监控、日志记录等场景中非常有用,通过拦截函数调用,可以在不修改原始代码的情况下添加额外的功能。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #define _GNU_SOURCE #include <dlfcn.h> #include <sys/stat.h> #include <fcntl.h> #include <syscall.h> typedef void* (*hook_alloc)(size_t size); typedef void* (*hook_free)(void* ptr); typedef void (*hook_start)(); typedef void (*hook_exit)(int exit_code); typedef int (*hook_main)(int argc, char *argv[]); static char logname[1024] = "./hook.log"; static int fd = 0; void *malloc(size_t size){ static hook_alloc pmalloc = NULL; if(pmalloc == NULL){ pmalloc = (hook_alloc)dlsym(RTLD_NEXT, "malloc"); } static char buffer[128] = {0}; void *caller_addr = __builtin_return_address(0); void *ptr = pmalloc(size); sprintf(buffer, "%p: malloc:%p,%ld\n", caller_addr, ptr, size); write(1, buffer, 128); return ptr; } void free(void *ptr){ static hook_free pfree = NULL; if(pfree == NULL){ pfree = (hook_free)dlsym(RTLD_NEXT, "free"); } static char buffer[128] = {0}; void *caller_addr = __builtin_return_address(0); sprintf(buffer, "%p: free:%p\n", caller_addr, ptr); write(1, buffer, 128); pfree(ptr); } void _start(){ static hook_start pstart = NULL; const char msg[] = "\n!!! _START FUNCTION HIJACKED VIA LD_PRELOAD !!!\n"; syscall(SYS_write, STDOUT_FILENO, msg, sizeof(msg)-1); if(pstart == NULL){ pstart = (hook_start)dlsym(RTLD_NEXT, "_start"); if(pstart == NULL){ fprintf(stderr, "Failed to get original _start function\n"); _exit(1); } } fd = open(logname, O_RDWR | O_CREAT | O_APPEND, 0644); if(fd < 0){ fprintf(stderr, "Failed to open log file %s\n", logname); _exit(1); } printf("program start : open log file: %s\n", logname); pstart(); } static hook_exit pexit = NULL; __attribute__((noreturn)) void _exit(int exit_code){ if(pexit == NULL){ pexit = (hook_exit)dlsym(RTLD_NEXT, "_exit"); } printf("program exit : close log file: %s\n", logname); close(fd); pexit(exit_code); __builtin_unreachable(); // return; } // __attribute__((constructor)) void init() { // pexit = dlsym(RTLD_NEXT, "_exit"); // } // __attribute__((visibility("default"))) void _start(){} int main(int argc, char *argv[]){ static hook_main pmain = NULL; if(pmain == NULL){ pmain = (hook_main)dlsym(RTLD_NEXT, "main"); } fd = open(logname, O_RDWR | O_CREAT | O_APPEND, 0644); if(fd < 0){ fprintf(stderr, "Failed to open log file %s\n", logname); _exit(1); } printf("program start : open log file: %s\n", logname); return pmain(argc, argv); } 劫持main函数失败
07-03
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/mman.h> #define _GNU_SOURCE #include <dlfcn.h> typedef void* (*hook_mmap)(void *addr, size_t length, int prot, int flags, int fd, off_t offset); typedef void* (*hook_munmap)(void *addr, size_t length); void* fun(void *ptr){ while(1){ } } void *__GI___mmap64(void *addr, size_t length, int prot, int flags, int fd, off_t offset){ static hook_mmap pmmap = NULL; if(pmmap == NULL){ pmmap = (hook_mmap)dlsym(RTLD_NEXT, "__GI___mmap64"); } static char buffer[128] = {0}; void *caller_addr = __builtin_return_address(0); void *ptr = pmmap(addr, length, prot, flags, fd, offset); int ret = sprintf(buffer, "%p: __mmap:%p,%ld\n", caller_addr, ptr, length); write(1, buffer, ret); write(fd, buffer, ret); fsync(fd); return ptr; } void* __wrap___mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { printf("拦截__mmap调用! size=%zu\n", length); // 调用原始__mmap函数 static hook_mmap pmmap = NULL; if(pmmap == NULL){ pmmap = (hook_mmap)dlsym(RTLD_NEXT, "mmap64"); } static char buffer[128] = {0}; void *caller_addr = __builtin_return_address(0); void *ptr = pmmap(addr, length, prot, flags, fd, offset); int ret = sprintf(buffer, "%p: __mmap:%p,%ld\n", caller_addr, ptr, length); write(1, buffer, ret); write(fd, buffer, ret); fsync(fd); printf("分配地址: %p\n", ptr); return ptr; } int main(){ void *paddr = malloc(10); printf("Allocated memory at %p\n", paddr); pthread_t tid = 0; pthread_create(&tid, NULL, fun, NULL); void *ptr = mmap(NULL, 1024, 0, 0, -1, 0); while(1){ } free(paddr); return 0; } gcc memtest.c -o memtest -Wl,--wrap=__mmap
07-06
### dlsym Hook 实现原理 `dlsym` 是 Linux 系统中用于动态链接库(shared library)符号解析的函数,它可以根据符号名称(如函数名或变量名)查找其在共享库中的地址。在 Hook 技术中,`dlsym` 常用于获取原始函数的地址,从而实现对目标函数的替换或拦截。 Hook 的基本原理是通过 `dlsym(RTLD_NEXT, "function_name")` 获取当前函数的下一个定义地址(即原始库中的地址),并将其保存到全局函数指针变量中。随后,将目标函数(如 `read` 或 `write`)替换为自定义的 Hook 函数,从而实现对函数调用的拦截和修改行为。 Hook 技术的核心步骤包括: - **初始化 Hook 环境**:通过 `dlsym` 获取原始函数地址。 - **定义 Hook 函数**:实现自定义逻辑,例如记录调用信息、修改参数或返回值。 - **替换函数指针**:通过函数指针重定向,将原始函数调用指向 Hook 函数。 ### 示例代码 以下是一个使用 `dlsym` Hook `read` 和 `write` 函数的示例: ```c #include <stdio.h> #include <dlfcn.h> #include <unistd.h> #include <stdarg.h> // 定义函数指针类型 typedef ssize_t (*read_hook)(int fd, void *buf, size_t count); typedef ssize_t (*write_hook)(int fd, const void *buf, size_t count); // 全局变量,保存原始函数地址 read_hook old_read = NULL; write_hook old_write = NULL; // 初始化 Hook 环境 int hook_init() { if (!old_read) { old_read = (read_hook)dlsym(RTLD_NEXT, "read"); } if (!old_write) { old_write = (write_hook)dlsym(RTLD_NEXT, "write"); } return (old_read && old_write) ? 0 : -1; } // Hook read 函数 ssize_t read(int fd, void *buf, size_t count) { if (!old_read) { hook_init(); } printf("Hook read: fd=%d, count=%zu\n", fd, count); return old_read(fd, buf, count); // 调用原始 read 函数 } // Hook write 函数 ssize_t write(int fd, const void *buf, size_t count) { if (!old_write) { hook_init(); } printf("Hook write: fd=%d, count=%zu\n", fd, count); return old_write(fd, buf, count); // 调用原始 write 函数 } ``` ### 使用方式 1. **编译动态链接库**: ```bash gcc -fPIC -shared hook.c -o libhook.so -ldl ``` 2. **使用 LD_PRELOAD 加载 Hook 库**: ```bash LD_PRELOAD=./libhook.so ./your_program ``` 3. **运行结果**: 当程序调用 `read` 或 `write` 时,会输出拦截信息,并调用原始函数。 ### Hook 技术的应用场景 - **性能监控**:记录函数调用时间、参数等信息。 - **安全检测**:拦截敏感函数调用,检查输入合法性。 - **功能增强**:在不修改源码的情况下扩展函数行为。 ### 注意事项 - Hook 技术可能影响程序稳定性,需谨慎使用。 - 动态链接库的加载顺序会影响 `dlsym(RTLD_NEXT, ...)` 的解析结果。 - Hook 函数需与原始函数签名保持一致,否则可能导致崩溃。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值