如何使用LD_PRELOAD拦截dlsym调用?

我想拦截应用程序对dlsym的调用,我已经尝试在.so中声明我正在预加载dlsym,并使用dlsym本身来获取它的真实地址,但是由于非常明显的原因不起作用。

有没有比采用进程’内存映射更容易,并使用libelf在加载的libdl.so中找到dlsym的真实位置?

作为评论者,我偶然发现了与hdante的答案相同的问题:调用__libc_dlsym()直接崩溃了一个段错误。 在阅读了一些glibc源代码后,我提出了以下hack作为解决方法:

 extern void *_dl_sym(void *, const char *, void *); extern void *dlsym(void *handle, const char *name) { /* my target binary is even asking for dlsym() via dlsym()... */ if (!strcmp(name,"dlsym")) return (void*)dlsym; return _dl_sym(handle, name, dlsym); } 

注意这个“解决方案”的两件事:

  1. 这段代码绕过了由(__libc_)dlsym()内部完成的锁定,所以为了使这个线程安全,你应该添加一些锁定。
  2. _dl_sym()_dl_sym()参数是调用者的地址,glibc似乎通过堆栈展开来重构这个值,但我只是使用函数本身的地址。 调用者地址在内部用于查找调用者所在的链接映射以使RTLD_NEXT正确(并且,使用NULL作为RTLD_NEXT参数将使调用失败并在使用RTLD_NEXTRTLD_NEXT )。 但是,我还没有看过glibc的unwindindfunction,所以我不能100%确定上面的代码会做正确的事情,而且它可能只是偶然的机会……

到目前为止提出的解决方案有一些明显的缺点: _dl_sym() dlsym()在某些情况下与预期的dlsym()行为完全不同。 例如,尝试解析不存在的符号会退出程序而不是仅返回NULL。 要解决这个问题,可以使用_dl_sym()来获取指向原始dlsym()的指针,并将其用于其他所有内容(例如在“标准” LD_PRELOAD钩子approch中,而不需要连接dlsym ):

 extern void *_dl_sym(void *, const char *, void *); extern void *dlsym(void *handle, const char *name) { static void * (*real_dlsym)(void *, const char *)=NULL; if (real_dlsym == NULL) real_dlsym=_dl_sym(RTLD_NEXT, "dlsym", dlsym); /* my target binary is even asking for dlsym() via dlsym()... */ if (!strcmp(name,"dlsym")) return (void*)dlsym; return real_dlsym(handle,name); } 

http://www.linuxforu.com/2011/08/lets-hook-a-library-function/

从文字:

当需要在钩子中调用__libc_dlsym(句柄,符号)时,请注意自己调用dlsym()的函数。

 extern void *__libc_dlsym (void *, const char *); void *dlsym(void *handle, const char *symbol) { printf("Ha Ha...dlsym() Hooked\n"); void* result = __libc_dlsym(handle, symbol); /* now, this will call dlsym() library function */ return result; }