如何从glibc拦截函数并打印其参数的值?

时间:2016-06-18 04:28:51

标签: c glibc

在glibc中,函数_IO_new_fopen()由fopen()libcall调用。如果我正在运行以下代码,是否有任何方法允许我在调用它时拦截_IO_new_fopen()函数并打印出其参数的值?

对于内核函数,这可以通过Jprobe实现,我实际上正在为glibc中的函数寻找类似的机制。 LD_PRELOAD是glibc中的一个相关机制,它允许我们用自定义函数替换glibc函数,但它无助于实现我的目标。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main(void){

    char buff[100];
    int i, r1;
    FILE *f1 = fopen("text.txt", "wb");

    for(i = 0; i < 100; i++){
            buff[i] = 'b';
    }
    assert(f1);
    r1 = fwrite(buff, 1, 100, f1);
    printf("wrote %d elements\n", r1);
    fclose(f1);
}

1 个答案:

答案 0 :(得分:2)

glibc中,fopen是[公共]符号别名到[本地符号] _IO_new_fopen(即它们是相同的),因此,从技术上讲,fopen致电 _IO_new_fopen - 它 它。

如果您有兴趣拦截示例中显示的来电(例如来自 app ),那么使用LD_PRELOAD [以及dlopen,{{1} }等等,并定义自己的dlsym 工作。我以前用自己的代码完成了这个。

您可能只是缺少一个入口点(即您可能需要定义/拦截多个符号):fopenfopenfopen64_IO_file_fopen等。如果您对可执行文件执行_IO_fopen,则简单readelf -s可能会显示为其他内容。我猜你会看到fopen。此外,您可能需要考虑符号版本。

您将无法拦截fopen64glibc内的内部电话,因为它们绕过机制并直接进入。但是,这不太有用,因此可能需要您提供更多详细信息。

您还可以将fopen视为截取基础fopencookieread(2)等系统调用的方法。

<强>更新

  

具体来说,我正在尝试打印fread()/ fwrite()使用的缓冲区的地址和内容。

容易做到。详情如下......

  

我认为应该可以通过在某处添加“printk()”并重建libc.so.6来完成,但是有没有更方便的方法(即没有重建libc.so.6)?

无需重建write(2)libc将会处理它。请记住,LD_PRELOAD位于内核中。没必要去那里[它可能不会起作用]

您想要的地址是传递给printk而不是传递给fread的缓冲区的地址。传递给read(2)的缓冲区位于read(2)结构中,因此,它不会告诉您太多。否则,您可以在FILE下运行整个程序[或编写使用strace(1)]的自定义版本。

您可以截取,跟踪,断点等任意数量的函数。您只需创建自己的共享库(ptrace(2))并将.so设置为它。

这是一个示例拦截器函数[for LD_PRELOAD]:

fread

这是基本机制。你可以添加你想要的任何[邪恶的]东西。如果缓冲区在某个[坏]范围或有趣的缓冲区内容中,您可以获得幻想并添加一些陷阱的逻辑。也就是说,类似于// trapme -- put gdb breakpoint on this function __attribute__((__noinline__)) void trapme(void) { // prevent the function call to this from being optimized away __asm__ __volatile__ () ::: memory; } // dumpme -- dump out a buffer void dumpme(const void *buf,size_t xlen) { // dump data in whatever format you'd like ... } // fread -- intercept fread calls size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { static size_t (*fread_real)(void *ptr,size_t size,size_t nmemb,FILE *stream) = NULL; size_t xlen; printf("fread_fake: ENTER ptr=%p size=%lX nmemb=%ld stream=%p\n", ptr,size,nmemb,stream); // do trap on some suspicious activity ... #if 0 if (ptr == ...) trapme(); #endif // locate the real symbol in glibc if (fread_real == NULL) fread_real = dlsym(RTLD_NEXT,"fread"); // abort if fread_real is still null ... xlen = fread_real(ptr,size,nmemb,stream); // dump out the data if (xlen > 0) dumpme(ptr,xlen * size); // do trap on some suspicious activity ... #if 0 if (xlen == 372) trapme(); #endif printf("fread_fake: EXIT xlen=%ld\n",xlen); return xlen; } 断点的cond语句。因此,你可以使用它来触发并放入gdb,使用比单独使用gdb更复杂的测试[找到真正难以发现的错误]。

您还可以监控gdb并记住文件名等

让这个机制起作用并不是困难,但我的建议是编写fopen的拦截器并首先熟悉fopen技巧(vs.试图在循环中调试它。)

您可以根据需要创建这些功能。简单地开始(例如dlsymfopenfread)。然后,在覆盖范围内找到“间隙”时添加更多内容(例如,最终,您可能会发现截取fwrite会为您提供所需信息)

更新#2:

以下是一个示例fseek脚本,其中包含我要使用的选项:

strace

strace -ttt -i -f -etrace=all -o \ /home/me/log/foobar.spysys \ -eread=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 \ -ewrite=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 \ -x foobar -eread可以根据需要扩展到任意数量的单位