我想拦截应用程序对dlsym的调用。我尝试在.so内部声明,以便预先加载dlsym,并使用dlsym本身获取其真实地址,但是出于相当明显的原因,此方法不起作用。
有没有比获取进程的内存映射和使用libelf查找dlsym在加载的libdl.so中的真实位置更简单的方法?
最佳答案
我偶然发现了与注解者一样的hdante答案问题:调用__libc_dlsym()
时直接由于段错误而崩溃。阅读了一些glibc资料后,我想到了以下解决方法:
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);
}
注意此“解决方案”的两件事:
(__libc_)dlsym()
在内部完成的锁定,因此,要使此线程安全,应添加一些锁定。 _dl_sym()
的第三个参数是调用者的地址,glibc似乎通过堆栈展开来重建此值,但我只是使用函数本身的地址。调用方地址在内部用于查找调用方所在的链接映射,以使RTLD_NEXT
之类的内容正确(并且,使用NULL作为thrid参数将使调用失败,并在使用RTLD_NEXT
时出错)。但是,我没有看过glibc的unwindind功能,因此我不确定100%以上代码是否正确,并且可能偶然碰巧可以正常工作... 到目前为止,提出的解决方案存在一些重大缺陷:在某些情况下,
_dl_sym()
的行为与预期的dlsym()
完全不同。例如,尝试解析不存在的符号会退出程序,而不仅仅是返回NULL。要解决此问题,可以使用_dl_sym()
来获取指向原始dlsym()
的指针,并将其用于其他所有内容(例如在“标准” LD_PRELOAD
钩子(Hook)方法中完全不钩住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);
}