自动hook新加载的动态库可能没被hook的问题
我在线上使用 bhook,版本 v1.0.3,想 hook 一个动态下载的 so,发现会有 so 已经加载好,但是 bhook 没有自动去 hook 的问题,出现概率比较低。
我找到了一种复现场景,发现是 bhook 没有立即监听到动态库的加载,所以也没有 hook 了: 1.线程 1 去初始化 bhook,调用 Bytehook.init,并且调用 bytehook_hook_single; 2.线程 2 加载某一个 so;
复现的环境和代码我也贴出来:
bhook 版本:v1.0.3 机型:OnePlus6,Android 10
我改了一些代码,让问题复现:
void bh_task_manager_hook(bh_task_manager_t *self, bh_task_t *task)
{
if(bh_dl_monitor_is_initing())
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static bool oneshot_refreshed = false;
if(!oneshot_refreshed)
{
bool hooked = false;
pthread_mutex_lock(&lock);
if(!oneshot_refreshed)
{
BH_LOG_INFO("task_id:%u, hit init", task->id);
bh_dl_monitor_dlclose_rdlock();
bh_elf_manager_refresh(bh_core_global()->elf_mgr, false, NULL, NULL);
BH_LOG_INFO("sleep start..."); // 获取完 maps 停一会,然后 APP 另一个线程去加载新的 so,模拟动态 so 随机加载的场景
sleep(10);
BH_LOG_INFO("sleep end...");
bh_task_hook(task);
bh_dl_monitor_dlclose_unlock();
oneshot_refreshed = true;
hooked = true;
}
pthread_mutex_unlock(&lock);
if(hooked) return;
}
}
else
{
// start & check dl-monitor
if(0 != bh_task_manager_init_dl_monitor(self))
{
// For internal tasks in the DL monitor, this is not an error.
// But these internal tasks do not set callbacks, so there will be no side effects.
bh_task_hooked(task, BYTEHOOK_STATUS_CODE_INITERR_DLMTR, NULL, NULL);
return;
}
}
BH_LOG_INFO("task_id:%u start hook", task->id);
bh_dl_monitor_dlclose_rdlock();
bh_task_hook(task);
bh_dl_monitor_dlclose_unlock();
}
大致的流程是 bhook 会先 hook dlopen 系列函数,会先调用一遍 bh_elf_manager_refresh 把当前的 so 信息缓存起来,然后执行 hook dlopen 的任务;如果中途有别的 so 加载,bhook 会漏掉这些新的 so,相当于没监听到。 有个补救措施是 如果后续有新的 so 加载会调用 dlopen,就会触发 bh_elf_manager_refresh 再刷一遍缓存,之前漏掉的 so 也会加进来并且执行它们的 hook 任务,这样表现的情况就是 hook 被延后了。假如后续没有触发 bh_elf_manager_refresh,那么这次的 hook 就失败了
我感觉这种情况无法完全避免,如果对可靠性要求高的话,我可以每隔一段时间调用 refresh 吗?就像 xhook 的那种用法一样
还有个疑问是 dl_iterate_phdr 和 /proc/self/maps 可以拿到当前进程已经加载的所有共享对象,是不是也有可能不是最新的?比如我遍历 /proc/self/maps 的每一行,假如遍历的中途有个新的动态库加载,会出现遍历不到这个动态库的情况吗
嗯,确实存在这个问题,感谢指出。这个其实是“首次refresh”和“dlopen监听机制完成启动之前”的间隙。也许可以在bytehook初始化的最后阶段调用一下bh_task_manager_init_dl_monitor,然后再调用一次refresh,这样在初始化完成后就不会出现这个问题了。
bytehook比较早的内部版本是在初始化时就执行dlopen监听和refresh的,后来为了加快初始化速度,才这样调整的。后续版本我研究下怎么弥补这个问题。。。
还有个疑问是 dl_iterate_phdr 和 /proc/self/maps 可以拿到当前进程已经加载的所有共享对象,是不是也有可能不是最新的?比如我遍历 /proc/self/maps 的每一行,假如遍历的中途有个新的动态库加载,会出现遍历不到这个动态库的情况吗
是的,单纯遍历maps是有可能有这种问题。bytehook在dlopen监听机制启动后应该就不会有这个问题了。
感觉作者一直在吃老本啊,都是之前开源工程的那一套