You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 07:12:52