通过分析dlopen的加载流程,do_dlopen会调用find_library来加载so,并且把该so的信息加载进并且返回一个soinfo结构体
soinfo结构体里面包含了so的所有信息,比如符号等等信息,
然后位于/bionic/linker/linker_main.cpp全局有几个变量,其中的solist是一个链表,指向已加载的共享库链表的头部,它是一个指向 soinfo
结构体的指针,表示链表的起始点,所以,可以通过这个solist拿到通过正常linker加载的所有so的信息
somain:指的是linker.so的soinfo的结构体指针linker.so
是一个特殊的共享库
sonext:每当使用 dlopen
加载一个共享库时,动态链接器会创建一个新的 soinfo
结构体,并通过 sonext
变量将这个结构体链接到共享库链表中。这样就形成了一个链表,其中包含了已加载的所有共享库的信息,每次调用dlopen加载so的时候,都会做这种操作,形成一个链表
newinfo->next = sonext;
sonext = newinfo;
刚好,在/bionic/linker/linker_main.cpp下就有一个方法,叫solist_get_head会返回这个solist,所以,只需要使用hook来hooklinker.so的__dl__Z15solist_get_headv这个符号即可得到这个方法的地址他是编译后的符号,调用它即可拿到该solist,因为是链表,每调用一次next指向的就是下一个soinfo的指针。
以下代码是一个,拿到soinfo后,遍历其中符号的示例:
void process_exported_symbols(soinfo* si) {
// 获取动态段的地址
ElfW(Dyn)* dynamic = si->dynamic;
// 遍历动态段,查找动态符号表的地址和大小
for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
if (d->d_tag == DT_SYMTAB) {
// d->d_un.d_ptr 是动态符号表的地址
ElfW(Sym)* symtab = reinterpret_cast<ElfW(Sym)*>(d->d_un.d_ptr);
// 获取符号表中的符号数量
size_t num_symbols = ...; // 计算符号数量的方法取决于符号表的结构
// 遍历动态符号表,处理每个符号
for (size_t i = 0; i < num_symbols; ++i) {
// 处理符号信息
ElfW(Sym)* symbol = &symtab[i];
const char* symbol_name = si->strtab + symbol->st_name;
// 处理符号名称、值等信息
// ...
}
}
}
}
// 调用上述函数
soinfo* my_so = ...; // 你的 soinfo 指针
process_exported_symbols(my_so);