我认为使用LD_PRELOAD 替换dlopen() 的建议只是部分解决方案——您不会以这种方式捕获加载有dlopen() 的库的依赖项。
最后,如果不抓取动态链接器本身的内部状态,我看不到任何方法。它发现有一个从ld.so 导出的_rtld_global 符号包含该信息,但是您必须使用私有Glibc 标头来解释它。
以下是一个 Python sn-p,它将(假设我对 Glibc 源代码的阅读是正确的)按搜索顺序打印全局命名空间中的所有共享库。使用RTLD_LOCAL 加载的库将不会被打印。
它依赖于 Glibc 的实现细节这一事实意味着这种方法充满危险,但出于我的测试/审计目的,我认为它会做得很好。
import ctypes
# Abridged type declarations pillaged from Glibc. See:
# - https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/generic/ldsodefs.h
# - https://sourceware.org/git/?p=glibc.git;a=blob;f=include/link.h
class link_map(ctypes.Structure):
_fields_ = [
("l_addr", ctypes.c_size_t),
("l_name", ctypes.c_char_p),
]
class r_scope_elem(ctypes.Structure):
_fields_ = [
("r_list", ctypes.POINTER(ctypes.POINTER(link_map))),
("r_nlist", ctypes.c_uint),
]
class rtld_global(ctypes.Structure):
_fields_ = [
("_ns_loaded", ctypes.POINTER(link_map)),
("_ns_nloaded", ctypes.c_uint),
("_ns_main_searchlist", ctypes.POINTER(r_scope_elem)),
]
_rtld_global = rtld_global.in_dll(ctypes.CDLL(None), "_rtld_global")
searchlist = _rtld_global._ns_main_searchlist[0]
print [searchlist.r_list[n][0].l_name for n in xrange(searchlist.r_nlist)]
在我的 CentOS 7 系统上,打印如下:
['', '/lib64/libpython2.7.so.1.0', '/lib64/libpthread.so.0', '/lib64/libdl.so.2',
'/lib64/libutil.so.1', '/lib64/libm.so.6', '/lib64/libc.so.6',
'/lib64/ld-linux-x86-64.so.2']