在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);
}
答案 0 :(得分:2)
在glibc
中,fopen
是[公共]符号别名到[本地符号] _IO_new_fopen
(即它们是相同的),因此,从技术上讲,fopen
不致电 _IO_new_fopen
- 它 它。
如果您有兴趣拦截示例中显示的来电(例如来自 app ),那么使用LD_PRELOAD
[以及dlopen
,{{1} }等等,并定义自己的dlsym
将工作。我以前用自己的代码完成了这个。
您可能只是缺少一个入口点(即您可能需要定义/拦截多个符号):fopen
,fopen
,fopen64
,_IO_file_fopen
等。如果您对可执行文件执行_IO_fopen
,则简单readelf -s
可能会显示为其他内容。我猜你会看到fopen
。此外,您可能需要考虑符号版本。
您将无法拦截fopen64
到glibc
内的内部电话,因为它们绕过机制并直接进入。但是,这不太有用,因此可能需要您提供更多详细信息。
您还可以将fopen
视为截取基础fopencookie
,read(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.试图在循环中调试它。)
您可以根据需要创建这些功能。简单地开始(例如dlsym
,fopen
,fread
)。然后,在覆盖范围内找到“间隙”时添加更多内容(例如,最终,您可能会发现截取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
可以根据需要扩展到任意数量的单位