挂钩fopen()函数抛出分段错误

时间:2014-07-25 03:30:32

标签: c linux linux-kernel shared-libraries

我试图通过挂钩fopen()函数并使用LD_PRELOAD来记录对特定目录的访问。

  1. 我的第一个问题是:挂起fopen()是否足以记录打开文件的操作?
  2. 我的代码抛出了分段错误。特别是,代码是这样的(忽略错误检查): FILE* (my_fopen)(const char filename, const char* mode); void* libc_handle;
    void __attribute__ ((constructor)) init(void){ libc_handle = dlopen("libc.so.6", RTLD_LAZY); *(void**)(&my_fopen) = dlsym(libc_handle,"fopen"); } FILE* fopen(const char* filename, const char* mode){ printf("Hello world\n"); return my_fopen(filename, mode); }
  3. 在LD_PRELOAD中编译并指定新库后,我运行了

    ls

    它会抛出Segmenation Fault。知道为什么会这样吗?我甚至试图删除 printf(),但没有帮助。

1 个答案:

答案 0 :(得分:4)

您的代码中存在一些问题,这些问题已在下面的示例中修复(我还添加了相关标题并提供了main来提供完整的程序):

#include <stdio.h>
#include <dlfcn.h>

FILE* (*my_fopen)(const char *filename, const char* mode);
void* libc_handle;

void __attribute__ ((constructor)) init(void){
   libc_handle = dlopen("libc.so.6", RTLD_LAZY);
   my_fopen = dlsym(libc_handle,"fopen");
}
FILE* fopen(const char* filename, const char* mode){
   printf("Hello, Pax\n");
   return my_fopen(filename, mode);
}

int main (void) {
   FILE *fout = fopen ("xyzzy.txt","w");
   fclose (fout);
   return 0;
}

您提供的更改如下:

  • my_fopen函数指针应该是指针。我怀疑你可能认为FILE*是这样做的,但实际上并不正确。要指定返回FILE指针的ffunction指针,您需要FILE * (*fn)(blah, blah)

  • 同样,该函数的第一个参数必须是const char *,换句话说,指针。您只需const char

  • 您实际上并不需要那个复杂的表达式来设置my_fopen指针(转换,取地址,取消引用)。你可以使用更简单的my_fopen = ...。事实上,我认为演员可能实际上是阻止gcc在这种情况下报告错误的原因,因为它假设,如果你演员,你知道你在做什么

  • 您可能还应该检查dlopen的返回值。我在这段代码中没有做到这一点,但是,如果你因某些原因找不到(或者无法加载)库,那么之后的那一行可能会让你感到悲伤。

当我在Red Hat Enterprise Linux Workstation release 6.4 (Santiago)上编译并运行此程序时,我得到了Hello, Pax 的输出,并且创建了文件xyzzy.txt


而且,除此之外,还有其他功能可用于访问文件系统,例如openopendirfreopen,{{1 }},creat(我认为)。

根据您的需要,您可能还需要做一些额外的工作。


想要考虑的一件事是mkfifo甚至可能使用 ls。它实际上只能使用fopenopendir/readdir构建。

因此,让我们使用我们知道调用stat的程序。输入以下程序fopen

qqtest.c

并使用#include <stdio.h> int main (void) { FILE *fh = fopen ("xyzzy.txt", "w"); fclose (fh); return 0; } 进行编译,然后运行它。您应该看不到输出,但应创建文件gcc -o qqtest qqtest.c。确认后,删除xyzzy.txt文件,然后输入以下程序xyzzy.txt

qq.c

使用#include <stdio.h> #include <dlfcn.h> FILE* (*my_fopen)(const char *filename, const char* mode); void* libc_handle; void __attribute__ ((constructor)) init(void){ libc_handle = dlopen("libc.so.6", RTLD_LAZY); my_fopen = dlsym(libc_handle,"fopen"); } FILE* fopen(const char* filename, const char* mode){ printf("Hello, Pax\n"); return my_fopen(filename, mode); } 进行编译,然后运行gcc -shared -o qq.so qq.c -ldl程序(当然,将共享对象路径更改为您自己的目录):

qqtest

这一次,您应该在LD_PRELOAD=/home/pax/qq.so ./qqtest 文件创建之前看到Hello, Pax字符串输出,证明它正在调用您的包装函数,而后者又会调用原始xyzzy.txt }。


现在,这一切都非常好,但是,即使一旦你开始工作,你也必须截取几个不同的电话,以确保你能抓住所有的变化。

这将花费您很长时间才能完成,正如Chris Stratton在评论中指出的那样,Linux内核已经具有报告文件系统更改的能力你。

如果您的目标是仅跟踪文件系统更改而不是自行了解如何完成更改,请查看inotify以了解如何执行此操作,无需发明轮子。