LD_PRELOAD hook 32位游戏fopen仅启动时调用原因及替代函数问询
问题描述
在Linux系统中使用LD_PRELOAD编写动态库hook fopen 调用,挂载到目标游戏时64位版本运行正常,但32位版本仅在启动阶段观测到65次fopen调用,后续运行过程中再也没有触发fopen调用。额外hook了freopen与fdopen函数排查,确认运行过程中这两个函数也未被调用。
使用的hook代码如下:
#include <cstdio> #include <cstring> #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <dlfcn.h> static auto originalFopen = reinterpret_cast<FILE * (*)(const char* path, const char* mode)>(dlsym(RTLD_NEXT, "fopen")); static auto originalFdopen = reinterpret_cast<FILE * (*)(int fd, const char* mode)>(dlsym(RTLD_NEXT, "fdopen")); static auto originalFreopen = reinterpret_cast<FILE * (*)(const char* path, const char* mode, FILE* stream)>(dlsym(RTLD_NEXT, "freopen")); FILE* f = originalFopen("TEST.txt", "w"); FILE* fopen(const char* path, const char* mode) { std::fwrite("idk1\n", 1, 5, f); return originalFopen(path, mode); } FILE* fdopen(int fd, const char* mode) { std::fwrite("idk2\n", 1, 5, f); return originalFdopen(fd, mode); } FILE* freopen(const char* path, const char* mode, FILE* stream) { std::fwrite(path, 1, std::strlen(path), f); std::fwrite("\n", 1, 1, f); return originalFreopen(path, mode, stream); }
目标游戏开源仓库地址:AmnesiaTheDarkDescent
核心原因
- 32位glibc存在大量历史兼容的内部符号别名,
fopen/freopen/fdopen只是对外暴露的弱符号,实际库内部所有stdio文件打开逻辑最终都会走到_IO_new_fopen(旧版本glibc为__fopen_internal)。32位程序编译时默认会绑定到内部强符号,直接跳过你hook的弱符号入口;64位glibc不存在这个兼容逻辑,所以hook可以正常生效。 - 32位版本游戏可能在启动完成后直接跳过stdio的
FILE*封装层,直接调用open/open64接口拿到文件描述符做IO操作,根本不会走fopen系列函数,自然不会触发你写的hook逻辑。 - 32位程序可能链接了带版本标记的旧版fopen符号,比如
fopen@GLIBC_2.0,你通过dlsym(RTLD_NEXT, "fopen")拿到的是当前系统默认版本的符号,和程序实际调用的符号版本不匹配,导致后续调用不会走到你的hook函数里。 - 你当前代码里的日志文件指针
f是全局变量,在动态库加载阶段就会调用原始fopen初始化,这个阶段存在符号解析时序问题,可能导致部分hook逻辑在初始化完成前就失效。
修复方案
- 先执行
objdump -T 你的32位游戏二进制文件 | grep -E 'fopen|open@',列出程序实际导入的所有文件打开相关符号,对照符号列表补全hook项,不要漏掉带版本标记的符号、fopen64、_IO_new_fopen、__fopen_internal这些内部/扩展符号。 - 额外hook libc的
open和open64函数,这两个是所有文件打开操作最终都会走到的posix层接口,不管上层是stdio封装还是直接走系统调用,只要是动态链接的程序都会经过这两个函数,可以覆盖所有文件打开场景。 - 把日志文件的初始化改成懒加载逻辑:第一次进入任意hook函数时再打开日志文件,不要在全局变量初始化阶段打开文件,避免动态库加载时序导致的异常。
- 如果需要hook版本化符号,可以用
dlsym(RTLD_NEXT, "fopen@GLIBC_2.0")这类写法明确指定要获取的符号版本,匹配程序实际调用的符号入口。
内容的提问来源于stack exchange,提问作者my_stack_exchange_account
相关产品推荐
相关产品推荐

