Always unload zygisk after specialize

This commit is contained in:
topjohnwu 2021-11-07 13:05:44 -08:00
parent 4dac9e40bd
commit b8c1588284
5 changed files with 51 additions and 35 deletions

View File

@ -152,6 +152,8 @@ enum Option : int {
DLCLOSE_MODULE_LIBRARY = 1, DLCLOSE_MODULE_LIBRARY = 1,
}; };
// All API functions will stop working after post[XXX]Specialize as Zygisk will be unloaded
// from the specialized process afterwards.
struct Api { struct Api {
// Connect to a root companion process and get a Unix domain socket for IPC. // Connect to a root companion process and get a Unix domain socket for IPC.
@ -274,22 +276,22 @@ void entry_impl(api_table *table, JNIEnv *env) {
} // namespace internal } // namespace internal
inline int Api::connectCompanion() { inline int Api::connectCompanion() {
return impl->connectCompanion(impl->_this); return impl->connectCompanion ? impl->connectCompanion(impl->_this) : -1;
} }
inline void Api::setOption(Option opt) { inline void Api::setOption(Option opt) {
impl->setOption(impl->_this, opt); if (impl->setOption) impl->setOption(impl->_this, opt);
} }
inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) {
impl->hookJniNativeMethods(env, className, methods, numMethods); if (impl->hookJniNativeMethods) impl->hookJniNativeMethods(env, className, methods, numMethods);
} }
inline void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) { inline void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) {
impl->pltHookRegister(regex, symbol, newFunc, oldFunc); if (impl->pltHookRegister) impl->pltHookRegister(regex, symbol, newFunc, oldFunc);
} }
inline void Api::pltHookExclude(const char *regex, const char *symbol) { inline void Api::pltHookExclude(const char *regex, const char *symbol) {
impl->pltHookExclude(regex, symbol); if (impl->pltHookExclude) impl->pltHookExclude(regex, symbol);
} }
inline bool Api::pltHookCommit() { inline bool Api::pltHookCommit() {
return impl->pltHookCommit(); return impl->pltHookCommit != nullptr && impl->pltHookCommit();
} }
} // namespace zygisk } // namespace zygisk

View File

@ -13,7 +13,7 @@
using namespace std; using namespace std;
static void *self_handle = nullptr; void *self_handle = nullptr;
static int zygisk_log(int prio, const char *fmt, va_list ap); static int zygisk_log(int prio, const char *fmt, va_list ap);
@ -26,14 +26,6 @@ static void zygisk_logging() {
log_cb.ex = nop_ex; log_cb.ex = nop_ex;
} }
void self_unload() {
ZLOGD("Request to self unload\n");
// If unhooking failed, do not unload or else it will cause SIGSEGV
if (!unhook_functions())
return;
new_daemon_thread(reinterpret_cast<thread_entry>(&dlclose), self_handle);
}
static char *first_stage_path = nullptr; static char *first_stage_path = nullptr;
void unload_first_stage() { void unload_first_stage() {
if (first_stage_path) { if (first_stage_path) {

View File

@ -21,6 +21,8 @@ using xstring = jni_hook::string;
//#define ZLOGV(...) ZLOGD(__VA_ARGS__) //#define ZLOGV(...) ZLOGD(__VA_ARGS__)
#define ZLOGV(...) #define ZLOGV(...)
static bool unhook_functions();
namespace { namespace {
enum { enum {
@ -28,6 +30,7 @@ enum {
FORK_AND_SPECIALIZE, FORK_AND_SPECIALIZE,
APP_SPECIALIZE, APP_SPECIALIZE,
SERVER_SPECIALIZE, SERVER_SPECIALIZE,
CAN_DLCLOSE,
FLAG_MAX FLAG_MAX
}; };
@ -51,6 +54,7 @@ struct HookContext {
HookContext() : pid(-1), info{} {} HookContext() : pid(-1), info{} {}
static void close_fds(); static void close_fds();
void unload_zygisk();
DCL_PRE_POST(fork) DCL_PRE_POST(fork)
void run_modules_pre(const vector<int> &fds); void run_modules_pre(const vector<int> &fds);
@ -170,6 +174,15 @@ DCL_HOOK_FUNC(void, android_log_close) {
old_android_log_close(); old_android_log_close();
} }
// Last point before process secontext changes
DCL_HOOK_FUNC(int, selinux_android_setcontext,
uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) {
if (g_ctx) {
g_ctx->flags[CAN_DLCLOSE] = unhook_functions();
}
return old_selinux_android_setcontext(uid, isSystemServer, seinfo, pkgname);
}
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// The original android::AppRuntime virtual table // The original android::AppRuntime virtual table
@ -327,6 +340,7 @@ void HookContext::run_modules_pre(const vector<int> &fds) {
modules.emplace_back(i, h); modules.emplace_back(i, h);
auto api = new ApiTable(&modules.back()); auto api = new ApiTable(&modules.back());
module_entry(api, env); module_entry(api, env);
modules.back().table = api;
} }
} }
close(fds[i]); close(fds[i]);
@ -357,6 +371,22 @@ void HookContext::close_fds() {
close(logd_fd.exchange(-1)); close(logd_fd.exchange(-1));
} }
void HookContext::unload_zygisk() {
if (flags[CAN_DLCLOSE]) {
// Do NOT call the destructor
operator delete(jni_method_map);
// Directly unmap the whole memory block
jni_hook::memory_block::release();
// Strip out all API function pointers
for (auto &m : modules) {
memset(m.table, 0, sizeof(*m.table));
}
new_daemon_thread(reinterpret_cast<thread_entry>(&dlclose), self_handle);
}
}
// ----------------------------------------------------------------- // -----------------------------------------------------------------
void HookContext::nativeSpecializeAppProcess_pre() { void HookContext::nativeSpecializeAppProcess_pre() {
@ -387,19 +417,14 @@ void HookContext::nativeSpecializeAppProcess_post() {
} }
env->ReleaseStringUTFChars(args->nice_name, process); env->ReleaseStringUTFChars(args->nice_name, process);
if (info.on_denylist) { run_modules_post();
self_unload(); if (info.is_magisk_app) {
} else { setenv("ZYGISK_ENABLED", "1", 1);
run_modules_post();
if (info.is_magisk_app) {
setenv("ZYGISK_ENABLED", "1", 1);
} else if (args->is_child_zygote && *args->is_child_zygote) {
// If we are in child zygote, unhook all zygisk hooks
// Modules still have their code loaded and can do whatever they want
unhook_functions();
}
} }
g_ctx = nullptr; g_ctx = nullptr;
if (!flags[FORK_AND_SPECIALIZE]) {
unload_zygisk();
}
} }
void HookContext::nativeForkSystemServer_pre() { void HookContext::nativeForkSystemServer_pre() {
@ -457,6 +482,7 @@ void HookContext::fork_pre() {
void HookContext::fork_post() { void HookContext::fork_post() {
sigmask(SIG_UNBLOCK, SIGCHLD); sigmask(SIG_UNBLOCK, SIGCHLD);
g_ctx = nullptr; g_ctx = nullptr;
unload_zygisk();
} }
} // namespace } // namespace
@ -464,7 +490,6 @@ void HookContext::fork_post() {
static bool hook_refresh() { static bool hook_refresh() {
if (xhook_refresh(0) == 0) { if (xhook_refresh(0) == 0) {
xhook_clear(); xhook_clear();
ZLOGI("xhook success\n");
return true; return true;
} else { } else {
ZLOGE("xhook failed\n"); ZLOGE("xhook failed\n");
@ -503,6 +528,7 @@ void hook_functions() {
XHOOK_REGISTER(ANDROID_RUNTIME, fork); XHOOK_REGISTER(ANDROID_RUNTIME, fork);
XHOOK_REGISTER(ANDROID_RUNTIME, unshare); XHOOK_REGISTER(ANDROID_RUNTIME, unshare);
XHOOK_REGISTER(ANDROID_RUNTIME, jniRegisterNativeMethods); XHOOK_REGISTER(ANDROID_RUNTIME, jniRegisterNativeMethods);
XHOOK_REGISTER(ANDROID_RUNTIME, selinux_android_setcontext);
XHOOK_REGISTER_SYM(ANDROID_RUNTIME, "__android_log_close", android_log_close); XHOOK_REGISTER_SYM(ANDROID_RUNTIME, "__android_log_close", android_log_close);
hook_refresh(); hook_refresh();
@ -526,7 +552,7 @@ void hook_functions() {
} }
} }
bool unhook_functions() { static bool unhook_functions() {
bool success = true; bool success = true;
// Restore JNIEnv // Restore JNIEnv
@ -539,11 +565,6 @@ bool unhook_functions() {
} }
} }
// Do NOT call the destructor
operator delete(jni_method_map);
// Directly unmap the whole memory block
jni_hook::memory_block::release();
// Unhook JNI methods // Unhook JNI methods
for (const auto &[clz, methods] : *jni_hook_list) { for (const auto &[clz, methods] : *jni_hook_list) {
if (!methods.empty() && old_jniRegisterNativeMethods( if (!methods.empty() && old_jniRegisterNativeMethods(

View File

@ -88,6 +88,7 @@ struct ZygiskModule {
ZygiskModule(int id, void *handle) : handle(handle), id(id) {} ZygiskModule(int id, void *handle) : handle(handle), id(id) {}
void * const handle; void * const handle;
ApiTable *table = nullptr;
bool unload = false; bool unload = false;
private: private:

View File

@ -41,9 +41,9 @@ struct AppInfo {
bool on_denylist; bool on_denylist;
}; };
extern void *self_handle;
void unload_first_stage(); void unload_first_stage();
void self_unload();
void hook_functions(); void hook_functions();
bool unhook_functions();
std::vector<int> remote_get_info(int uid, const char *process, AppInfo *info); std::vector<int> remote_get_info(int uid, const char *process, AppInfo *info);
int remote_request_unmount(); int remote_request_unmount();