New way to unload zygisk

Co-authored-by: 残页 <a1364259@163.com>
This commit is contained in:
LoveSy 2023-03-23 13:31:32 +08:00 committed by John Wu
parent e48afff5e8
commit 3a4fe53f27
4 changed files with 48 additions and 8 deletions

View File

@ -1,3 +1,4 @@
{
zygisk_inject_entry;
unload_first_stage;
};

View File

@ -43,13 +43,10 @@ static void zygisk_cleanup_wait() {
}
}
static void *unload_first_stage(void *) {
// Wait 10us to make sure 1st stage is done
timespec ts = { .tv_sec = 0, .tv_nsec = 10000L };
nanosleep(&ts, nullptr);
extern "C" void unload_first_stage() {
ZLOGD("unloading first stage\n");
unmap_all(HIJACK_BIN);
xumount2(HIJACK_BIN, MNT_DETACH);
return nullptr;
}
extern "C" void zygisk_inject_entry(void *handle) {
@ -70,7 +67,6 @@ extern "C" void zygisk_inject_entry(void *handle) {
unsetenv(MAGISKTMP_ENV);
sanitize_environ();
hook_functions();
new_daemon_thread(&unload_first_stage, nullptr);
}
// The following code runs in zygote/app process

View File

@ -259,6 +259,29 @@ DCL_HOOK_FUNC(int, selinux_android_setcontext,
return old_selinux_android_setcontext(uid, isSystemServer, seinfo, pkgname);
}
dev_t art_dev = 0;
ino_t art_inode = 0;
pthread_t main_thread = 0;
DCL_HOOK_FUNC(int, pthread_attr_destroy, void* target) {
int res = old_pthread_attr_destroy((pthread_attr_t *)target);
if (main_thread != pthread_self()) {
return res;
}
ZLOGD("pthread_attr_destroy(%p)\n", target);
lsplt::RegisterHook(art_dev, art_inode, "pthread_attr_destroy", reinterpret_cast<void*>(*old_pthread_attr_destroy), nullptr);
lsplt::CommitHook();
// Directly call `dlclose` will crash because when `dlclose` returns, CPU will try to execute code at an
// unmapped address. Because both `pthread_attr_destroy` and `dlclose` have same
// parameter types (a pointer) and return type (an int), the way they treat arguments and results
// are the same. We can use `musttail` to let the compiler reuse our stack frame and thus
// dlclose will directly return to the caller of `pthread_attr_destroy`
[[clang::musttail]] return dlclose(self_handle);
}
#undef DCL_HOOK_FUNC
// -----------------------------------------------------------------
@ -646,7 +669,23 @@ void HookContext::unload_zygisk() {
m.clearApi();
}
new_daemon_thread(reinterpret_cast<thread_entry>(&dlclose), self_handle);
// We cannot directly `dlclose` ourselves here, otherwise when `dlclose` returns, it will return
// to our code which have been unmapped, causing a segment fault. Instead, we hook
// `pthread_attr_destroy` which will be called when VM daemon threads start and
// do some magic. Check comment in our replacement of the function for more info.
for (auto &map : lsplt::MapInfo::Scan()) {
if (map.path.ends_with("/libart.so")) {
ZLOGD("hook pthread_attr_destroy\n");
art_inode = map.inode;
art_dev = map.dev;
main_thread = pthread_self();
lsplt::RegisterHook(art_dev, art_inode, "pthread_attr_destroy",
reinterpret_cast<void *>(new_pthread_attr_destroy),
reinterpret_cast<void **>(&old_pthread_attr_destroy));
lsplt::CommitHook();
break;
}
}
}
}

View File

@ -10,7 +10,7 @@
#endif
__attribute__((constructor))
static void zygisk_loader() {
static void zygisk_loader(void) {
android_dlextinfo info = {
.flags = ANDROID_DLEXT_FORCE_LOAD
};
@ -20,5 +20,9 @@ static void zygisk_loader() {
if (entry) {
entry(handle);
}
void (*unload)(void) = dlsym(handle, "unload_first_stage");
if (unload) {
__attribute__((musttail)) return unload();
}
}
}