摘要:上一篇博客写了老版本zygisk 使用dlclose 关闭so,所导致的问题,但是有些问题没有写清楚,所以有了第二篇
上一篇博客写了老版本zygisk 使用dlclose 关闭so,所导致的问题,但是有些问题没有写清楚,所以有了第二篇
libart加载之后注入libart.so 加载之后注入so,时机太早,是无法规避直接调用dlclose带来的soinfo空隙问题,上一篇博客写的确实有问题,当时我也有些疑问的,但是没有深入测试,而且在测试前期有些代码由bug导致从开始就造成了认知错误。
正确的时机和做法我在脚本中有写,_ZN3artL25ZygoteHooks_nativePreForkEP7_JNIEnvP7_jclass 这个时机是我开始测试的时机,是没有问题的,他会在nativeForkSystemServer这个函数的前面
所以正确的时机是在 _ZN3artL25ZygoteHooks_nativePreForkEP7_JNIEnvP7_jclass 这个函数执行的时候,注入zygisk.so文件,这样只需要稍微修改一下zygisk的代码便可直接使用
第二种绕过方式在我发博客以后,有兄弟直接进行了测试,后来还直接发给我解决这个问题的项目,这里很感谢。 参考了这个项目的代码(https://github.com/JingMatrix/NeoZygisk)。
在上一篇中,因为zygisk使用了dlopen加载so,导致了zygisk的soinfo加入到linker 的soinfo列表里,在应用进程启动的时候,调用dlclose 从soinfo的列表中删除zygisk的soinfo,因为so加载时机太早,zygisk 的soinfo位置太靠前,所以删除soinfo的时候导致出现了空隙。我采用了将libzygisk加载时机后延的方法。
其实防止检测的思想就是防止linker soinfo的列表出现空隙的。也可以说这个空隙是专门针对zygisk通过dlclose自卸载的检测。
所以我们在完成对抗的同时需要达到以下条件
zygisk 的soinfo必须从linker 的soinfo列表中删除
不能在soinfo中留下空隙
libzygisk.so 需要进行在用户进程启动以后完成自卸载
对抗思路和原理在libzygisk.so加载以后,直接删除linker 中zygisk.so的soinfo,并且不关闭libzygisk.so,同时更换自卸载函数,使用munmap完成自卸载。 这个思路从自卸载的角度来看,和dlclose大同小异,但是在操作上可控性更高。调用dlclose完成卸载,相当于同时删除soinfo,然后munmap删除已经加载到内存中libzygisk.so。但是由于so加载时机很早,卸载时机又很晚,导致linker的soinfo出现了空隙。现在将这个两个操作分开来,加载以后,直接删除soinfo,但是不释放已经加载到内存中的so,这样后续的so加载就会覆盖当前的位置,就不会出现空隙了,然后在应用进程启动以后在使用munmap 关闭soinfo。
代码分析替换自卸载函数
DCL_HOOK_FUNC(static int, pthread_attr_setstacksize, void *target, size_t size) { int res = old_pthread_attr_setstacksize((pthread_attr_t *) target, size); LOGV("pthread_attr_setstacksize called in [tid, pid]: %d, %d", gettid, getpid); // Only perform unloading on the main thread if (gettid != getpid) return res; if (g_hook->should_unmap) { g_hook->restore_plt_hook; void *start_addr = g_hook->start_addr; size_t block_size = g_hook->block_size; delete g_hook; // Because both `pthread_attr_setstacksize` and `munmap` have the same function // signature, we can use `musttail` to let the compiler reuse our stack frame and thus // `munmap` will directly return to the caller of `pthread_attr_setstacksize`. LOGD("unmap libzygisk.so loaded at %p with size %zu", start_addr, block_size); [[clang::musttail]] return munmap(start_addr, block_size); } } delete g_hook; return res;}删除soinfo
bool dropSoPath(const char *target_path) { bool path_found = false; if (solist == ptr && !initialize) { LOGE("failed to initialize solist"); return path_found; } for (auto *iter = solist; iter; iter = iter->getNext) { if (iter->getPath && strstr(iter->getPath, target_path)) { SoList::ProtectedDataGuard guard; LOGD("dropping solist record for %s with size %zu", iter->getPath, iter->getSize); if (iter->getSize > 0) { iter->setSize(0); SoInfo::soinfo_free(iter); path_found = true; } } } return path_found;}删除计数
void resetCounters(size_t load, size_t unload) { if (solist == ptr && !initialize) { LOGE("failed to initialize solist"); return; } if (g_module_load_counter == ptr || g_module_unload_counter == ptr) { LOGD("g_module counters not defined, skip reseting them"); return; } auto loaded_modules = *g_module_load_counter; auto unloaded_modules = *g_module_unload_counter; if (loaded_modules >= load) { *g_module_load_counter = loaded_modules - load; LOGD("reset g_module_load_counter to %zu", (size_t) *g_module_load_counter); } if (unloaded_modules >= unload) { *g_module_unload_counter = unloaded_modules - unload; LOGD("reset g_module_unload_counter to %zu", (size_t) *g_module_unload_counter); }}就写到这吧,感谢开源项目提供的解决方案,我也不知都那位大佬搞得这个方案,只能说思路很新颖,写的很好。 这是大佬项目地址 https://github.com/JingMatrix/NeoZygisk,我已经抄袭到我的项目上了 我的项目上一次已经放过地址了,这次就不放了
来源:寂寞的咖啡