Magisk注入Zygisk的过程
magisk 版本 https://github.com/topjohnwu/Magisk/tree/366dd524197d207be76322cb4f45044f9b466e93
Magisk开启zygisk 最终会进入mount_zygisk
0x1 mount_zygisk
#define mount_zygisk(bit) \
if (access("/system/bin/app_process" #bit, F_OK) == 0) { \
app_process_##bit = xopen("/system/bin/app_process" #bit, O_RDONLY | O_CLOEXEC); \
string zbin = zygisk_bin + "/app_process" #bit; \
string mbin = MAGISKTMP + "/magisk" #bit; \
int src = xopen(mbin.data(), O_RDONLY | O_CLOEXEC); \
int out = xopen(zbin.data(), O_CREAT | O_WRONLY | O_CLOEXEC, 0); \
xsendfile(out, src, nullptr, INT_MAX); \
close(out); \
close(src); \
clone_attr("/system/bin/app_process" #bit, zbin.data()); \
bind_mount("zygisk", zbin.data(), "/system/bin/app_process" #bit); \
}
主要做了以下工作
- 保存应位数的/system/bin/app_process文件 ("/system/bin/app_process" #bit)
- 将 /sbin/magisk(32/64) 复制到 /sbin/.magisk/zygisk/app_process(32/64) (android 11 以下是 /sbin,11之上在/dev下随机创建一个文件夹)
- 将系统 /system/bin/app_process(32/64) 的属性复制给/sbin/.magisk/zygisk/app_process(32/64)
- 最后将 /sbin/.magisk/zygisk/app_process(32/64) 挂载到 /system/bin/app_process(32/64)
- 执行 /system/bin/app_process(32/64) 就是 执行 /sbin/.magisk/zygisk/app_process(32/64)
最终/system/bin 下的 app_process 就变成了 magisk ,而原先的 app_process 的 fd 被 magiskd持有。执行 app_process 的时候就是执行了 magisk 。
0x2 magisk 的 main
// native/src/core/applets.cpp
int main(int argc, char *argv[]) {
if (argc < 1)
return 1;
enable_selinux();
cmdline_logging();
init_argv0(argc, argv);
string_view argv0 = basename(argv[0]);
// app_process is actually not an applet
if (argv0.starts_with("app_process")) {
return app_process_main(argc, argv); // 入口
}
// ...
return 1;
}
2-1 app_process_main
// Magisk/native/src/zygisk/main.cpp
// Entrypoint for app_process overlay
int app_process_main(int argc, char *argv[]) {
android_logging();
char buf[PATH_MAX];
//...
if (int socket = zygisk_request(ZygiskRequest::SETUP); socket >= 0) {
// 和 magisk 进行通信 通信成功
do {
if (read_int(socket) != 0)
break;
// Send over zygisk loader
// zygisk_ld 在 native/out/generated/arm64-v8a_binaries.h
write_int(socket, sizeof(zygisk_ld));
xwrite(socket, zygisk_ld, sizeof(zygisk_ld)); // zygisk_ld 来自python 脚本组装的, 保存内容是 libzygisk-ld.so -》 zygisk/loader.c -> zygisk_inject_entry 注入入口
int app_proc_fd = recv_fd(socket); // 接收 系统的/system/bin/app_process(32/64) 的文件符
if (app_proc_fd < 0)
break;
string tmp = read_string(socket); // 获取 MAGISKTMP 路径
if (char *ld = getenv("LD_PRELOAD")) {
string env = ld;
env += ':';
env += HIJACK_BIN;
setenv("LD_PRELOAD", env.data(), 1); // 设置 LD_PRELOAD 是已经被magisk接收并挂载到HIJACK_BIN上的 zygisk_ld.so
} else {
setenv("LD_PRELOAD", HIJACK_BIN, 1); // 设置 LD_PRELOAD 预加载so 启动进程前设置LD_PRELOAD变量(https://blog.csdn.net/whatday/article/details/108890018)
}
setenv(MAGISKTMP_ENV, tmp.data(), 1);
close(socket);
// 执行一个新程序,同时确保在执行过程中关闭了不需要的文件描述符(FD_CLOEXEC)
fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC);
fexecve(app_proc_fd, argv, environ); // environ 是全局变量 设置了LD_PRELOAD 会预先脚在 环境变量设置的so
} while (false);
close(socket);
}
// If encountering any errors, unmount and execute the original app_process
xreadlink("/proc/self/exe", buf, sizeof(buf));
xumount2("/proc/self/exe", MNT_DETACH);
execve(buf, argv, environ);
return 1;
}
2-2 zygisk 的处理函数zygisk_handler(client, &cred)
zygisk_handler 有多个信号处理对应的函数
void zygisk_handler(int client, const sock_cred *cred) {
int code = read_int(client);
char buf[256];
switch (code) {
case ZygiskRequest::SETUP:
setup_files(client, cred); // 处理 SETUP 信号
break;
case ZygiskRequest::PASSTHROUGH:
magiskd_passthrough(client);
break;
case ZygiskRequest::GET_INFO:
get_process_info(client, cred);
break;
case ZygiskRequest::GET_LOG_PIPE:
send_log_pipe(client);
break;
case ZygiskRequest::CONNECT_COMPANION:
if (get_exe(cred->pid, buf, sizeof(buf))) {
connect_companion(client, str_ends(buf, "64"));
} else {
LOGW("zygisk: remote process %d probably died, abort\n", cred->pid);
}
break;
case ZygiskRequest::GET_MODDIR:
get_moddir(client);
break;
default:
// Unknown code
break;
}
close(client);
}
2-3 setup_files
// #define HIJACK_BIN64 "/system/bin/appwidget"
// #define HIJACK_BIN32 "/system/bin/bu"
// #define SEPOL_FILE_TYPE "magisk_file"
// native/src/zygisk/entry.cpp
static void setup_files(int client, const sock_cred *cred) {
LOGD("zygisk: setup files for pid=[%d]\n", cred->pid);
// ...
// Hijack some binary in /system/bin to host loader
const char *hbin;
string mbin;
int app_fd;
bool is_64_bit = str_ends(buf, "64");
if (is_64_bit) {
hbin = HIJACK_BIN64;
mbin = MAGISKTMP + "/" ZYGISKBIN "/loader64.so";
app_fd = app_process_64;
} else {
hbin = HIJACK_BIN32;
mbin = MAGISKTMP + "/" ZYGISKBIN "/loader32.so";
app_fd = app_process_32;
}
// ...
// Ack
write_int(client, 0); // 写入0 继续执行
// Receive and bind mount loader // 接受传过来的二进制(libzygisk-ld.so)
int ld_fd = xopen(mbin.data(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0755);
string ld_data = read_string(client); // 先读取大小再读取内容
xwrite(ld_fd, ld_data.data(), ld_data.size()); // 写入mbin文件(/sbin/.magisk/zygisk/loader64.so)内
close(ld_fd);
// 设置文件的安全上下文(SELinux 上下文)
setfilecon(mbin.data(), "u:object_r:" SEPOL_FILE_TYPE ":s0");
xmount(mbin.data(), hbin, nullptr, MS_BIND, nullptr); // mbin(/sbin/.magisk/zygisk/loader64.so) 文件挂在到hbin(/system/bin/appwidget)文件上
send_fd(client, app_fd); // 发送系统原来的 /system/bin/app_process(32/64) 文件符号
write_string(client, MAGISKTMP); // 写入 路径"/sbin"
}
完成在/system/bin/app_process zygote 上注入在 libzygisk-ld.so
0x3 注入zygisk流程图

接下来分析libzygisk-ld.so做了些什么
0x4 llibzygisk-ld.so 的初始化函数
根据编译文件定位文件loader.c
LOCAL_MODULE := zygisk-ld
LOCAL_SRC_FILES := zygisk/loader.c
// native/src/zygisk/loader.c
#if defined(__LP64__)
// Use symlink to workaround linker bug on old broken Android
// https://issuetracker.google.com/issues/36914295
#define SECOND_STAGE_PATH "/system/bin/app_process"
#else
#define SECOND_STAGE_PATH "/system/bin/app_process32"
#endif
__attribute__((constructor))
static void zygisk_loader() {
android_dlextinfo info = {
.flags = ANDROID_DLEXT_FORCE_LOAD
};
// Android 5.x doesn't support ANDROID_DLEXT_FORCE_LOAD
void *handle =
android_dlopen_ext(SECOND_STAGE_PATH, RTLD_LAZY, &info) ?:
dlopen(SECOND_STAGE_PATH, RTLD_LAZY);
if (handle) {
void(*entry)(void*) = dlsym(handle, "zygisk_inject_entry"); // 找到符号zygisk_inject_entry
if (entry) {
entry(handle); // 执行 zygisk_inject_entry 方法
}
}
}
加载/system/bin/app_process
(已经被替换为magisk)找到符号zygisk_inject_entr
并执行。
4-1 zygisk_inject_entr
// native/src/zygisk/entry.cpp
extern "C" void zygisk_inject_entry(void *handle) { // 注入入口
zygisk_logging();
ZLOGD("load success\n");
// 如果包含多个路径,代码会截取第一个路径,并将其设置回 LD_PRELOAD 环境变量中。如果只有一个路径或没有路径,它会将 LD_PRELOAD 环境变量删除。
char *ld = getenv("LD_PRELOAD");
if (char *c = strrchr(ld, ':')) {
*c = '\0';
setenv("LD_PRELOAD", ld, 1); // Restore original LD_PRELOAD
} else {
unsetenv("LD_PRELOAD");
}
MAGISKTMP = getenv(MAGISKTMP_ENV);
self_handle = handle;
unsetenv(MAGISKTMP_ENV);
sanitize_environ(); // 让环境变量对齐,避免检测到被删除的环境变量留下的空白
hook_functions(); // 核心函数
new_daemon_thread(&unload_first_stage, nullptr);
}
4-2 hook_functions
这部分才是 zygisk 的核心 plt hook fork unshare 等函数 androidSetCreateThreadFunc
(重点函数)
// native/src/zygisk/hook.cpp
hash_map<xstring, tree_map<xstring, tree_map<xstring, void *>>> *jni_method_map;
// template<class T>
// static inline void default_new(T *&p) { p = new T(); }
// template<class T>
// static inline void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
void hook_functions() {
default_new(plt_hook_list);
default_new(jni_hook_list);
default_new(jni_method_map);
ino_t android_runtime_inode = 0;
dev_t android_runtime_dev = 0;
for (auto &map : lsplt::MapInfo::Scan()) {
if (map.path.ends_with("libandroid_runtime.so")) {
android_runtime_inode = map.inode;
android_runtime_dev = map.dev;
break;
}
}
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, unshare);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, selinux_android_setcontext);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, androidSetCreateThreadFunc);
PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", android_log_close);
hook_commit();
// Remove unhooked methods
plt_hook_list->erase(
std::remove_if(plt_hook_list->begin(), plt_hook_list->end(),
[](auto &t) { return *std::get<3>(t) == nullptr;}),
plt_hook_list->end());
}
首先看几个定义的宏,最终通过lsplt::RegisterHook进行hook,发现 hook_register symbol 和 new_func 是拼接的,如androidSetCreateThreadFunc 该位置是
(void **) &old_androidSetCreateThreadFunc
(void*)new_androidSetCreateThreadFunc
// native/src/zygisk/hook.cpp
static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_func, void **old_func) {
if (!lsplt::RegisterHook(dev, inode, symbol, new_func, old_func)) {
ZLOGE("Failed to register plt_hook \"%s\"\n", symbol);
return;
}
plt_hook_list->emplace_back(dev, inode, symbol, old_func);
}
#define PLT_HOOK_REGISTER_SYM(DEV, INODE, SYM, NAME) \
hook_register(DEV, INODE, SYM, (void*) new_##NAME, (void **) &old_##NAME)
#define PLT_HOOK_REGISTER(DEV, INODE, NAME) \
PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME)
4-3 跟踪androidSetCreateThreadFunc
定位函数指针定义的位置,找到了一个DCL_HOOK_FUNC
宏是用于声明 hook 函数和备份函数的。
// native/src/zygisk/hook.cpp
// Current context
HookContext *g_ctx;
const JNINativeInterface *old_functions = nullptr;
JNINativeInterface *new_functions = nullptr;
} // namespace
#define DCL_HOOK_FUNC(ret, func, ...) \
ret (*old_##func)(__VA_ARGS__); \
ret new_##func(__VA_ARGS__)
jint env_RegisterNatives(
JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint numMethods) {
auto className = get_class_name(env, clazz);
ZLOGV("JNIEnv->RegisterNatives [%s]\n", className.data());
// 调用hookAndSaveJNIMethods
auto newMethods = hookAndSaveJNIMethods(className.data(), methods, numMethods);
// 调用原来的jni方法 有newMethods 就使用newMethods(替换后的函数)
return old_functions->RegisterNatives(env, clazz, newMethods.get() ?: methods, numMethods);
}
DCL_HOOK_FUNC(void, androidSetCreateThreadFunc, void *func) {
//new_androidSetCreateThreadFunc 函数体
ZLOGD("androidSetCreateThreadFunc\n");
using method_sig = jint(*)(JavaVM **, jsize, jsize *);
do {
auto get_created_vms = reinterpret_cast<method_sig>(
dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs")); // RTLD_DEFAULT 是一个特殊的句柄,用于指示 dlsym 在所有已加载的共享库中搜索符号
if (!get_created_vms) {
for (auto &map: lsplt::MapInfo::Scan()) {
if (!map.path.ends_with("/libnativehelper.so")) continue;
void *h = dlopen(map.path.data(), RTLD_LAZY);
if (!h) {
LOGW("cannot dlopen libnativehelper.so: %s\n", dlerror());
break;
}
// 从 libnativehelper.so 获取JNI_GetCreatedJavaVMs 方法
get_created_vms = reinterpret_cast<method_sig>(dlsym(h, "JNI_GetCreatedJavaVMs"));
dlclose(h);
break;
}
if (!get_created_vms) {
LOGW("JNI_GetCreatedJavaVMs not found\n");
break;
}
}
JavaVM *vm = nullptr;
jsize num = 0;
jint res = get_created_vms(&vm, 1, &num);
if (res != JNI_OK || vm == nullptr) break;
JNIEnv *env = nullptr;
res = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
if (res != JNI_OK || env == nullptr) break;
default_new(new_functions); // new一个JNINativeInterface空函数
memcpy(new_functions, env->functions, sizeof(*new_functions)); // 复制env->functions
new_functions->RegisterNatives = &env_RegisterNatives; // 替换RegisterNatives方法
// Replace the function table in JNIEnv to hook RegisterNatives
old_functions = env->functions; // 将原来的jni函数进行保存
env->functions = new_functions; // 替换env->functions 修改了RegisterNatives
} while (false);
old_androidSetCreateThreadFunc(func); // 执行原来的 androidSetCreateThreadFunc 函数
}
当系统调用androidSetCreateThreadFunc
方法时先替换jni
中的RegisterNatives
方法再执行原来的androidSetCreateThreadFunc
方法
执行当jni
执行RegisterNatives
会先执行hookAndSaveJNIMethods
再执行原的RegisterNatives
为什么选择hookRegisterNatives
,是因为zyogte
启动过程过程中会调用androidSetCreateThreadFunc->RegisterNatives
,时机比较早
4-4 hookAndSaveJNIMethods
- 只有 className 是 com/android/internal/os/Zygote才进行hook
// native/src/zygisk/jni_hooks.hpp
unique_ptr<JNINativeMethod[]> hookAndSaveJNIMethods(const char *className, const JNINativeMethod *methods, int numMethods) {
unique_ptr<JNINativeMethod[]> newMethods;
int clz_id = -1;
int hook_cnt = 0;
do {
if (className == "com/android/internal/os/Zygote"sv) {
// "com/android/internal/os/Zygote" 是一个 Android 系统中的类,它是 Android 启动过程中的第一个特定于 Android 的进程,它负责预加载所有系统资源和类,并为每个应用程序创建新的进程
clz_id = 0;
hook_cnt = 3;
break;
}
} while (false);
if (hook_cnt) {
newMethods = make_unique<JNINativeMethod[]>(numMethods);
memcpy(newMethods.get(), methods, sizeof(JNINativeMethod) * numMethods);
}
auto &class_map = (*jni_method_map)[className]; // 返回这个新创建的 tree_map 的引用
for (int i = 0; i < numMethods; ++i) {
if (hook_cnt && clz_id == 0) {// 重点hook的三个函数
HOOK_JNI(nativeForkAndSpecialize)
HOOK_JNI(nativeSpecializeAppProcess)
HOOK_JNI(nativeForkSystemServer)
}
class_map[methods[i].name][methods[i].signature] = methods[i].fnPtr;
}
return newMethods;
}
4-4 HOOK_JNI
// 用zygisk的方法(method##_methods[j])替换, 之后执行的方法就是zygisk中定义的函数
// method##_orig = methods[i].fnPtr; 保存原来的函数指针
// native/src/zygisk/hook.cpp
#define HOOK_JNI(method) \
if (methods[i].name == #method##sv) { \
int j = 0; \
for (; j < method##_methods_num; ++j) { \
if (strcmp(methods[i].signature, method##_methods[j].signature) == 0) { \
jni_hook_list->try_emplace(className).first->second.push_back(methods[i]); \
method##_orig = methods[i].fnPtr; \
newMethods[i] = method##_methods[j]; \
ZLOGI("replaced %s#" #method "\n", className); \
--hook_cnt; \
break; \
} \
} \
if (j == method##_methods_num) { \
ZLOGE("unknown signature of %s#" #method ": %s\n", className, methods[i].signature); \
} \
continue; \
}
三个函数的逻辑基本一致,这里挑选一个比较简短的函数进行跟踪
nativeForkSystemServer
// native/src/zygisk/jni_hooks.hpp
// 收集了android不同版本同一个函数不同的签名 比如 nativeForkSystemServer
// newMethods[i] = method##_methods[j]; => newMethods[i] = nativeForkSystemServer_methods[j]; 替换函数
const JNINativeMethod nativeForkSystemServer_methods[] = {
{
"nativeForkSystemServer",
"(II[II[[IJJ)I",
(void *) &nativeForkSystemServer_l
},
{
"nativeForkSystemServer",
"(II[IIII[[IJJ)I",
(void *) &nativeForkSystemServer_samsung_q
},
};
constexpr int nativeForkSystemServer_methods_num = std::size(nativeForkSystemServer_methods);
void *nativeForkSystemServer_orig = nullptr;
[[clang::no_stack_protector]] jint nativeForkSystemServer_l(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) {
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
ctx.nativeForkSystemServer_pre(); // nativeForkSystemServer 执行前
reinterpret_cast<decltype(&nativeForkSystemServer_l)>(nativeForkSystemServer_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities
); // 执行原函数
ctx.nativeForkSystemServer_post();// nativeForkSystemServer 执行后
return ctx.pid;
}
nativeForkSystemServer_pre
void HookContext::nativeForkSystemServer_pre() {
ZLOGV("pre forkSystemServer\n");
flags[SERVER_FORK_AND_SPECIALIZE] = true;
fork_pre();
if (pid != 0)
return;
vector<int> module_fds;
int fd = remote_get_info(1000, "system_server", &info_flags, module_fds);
if (fd >= 0) {
if (module_fds.empty()) {
write_int(fd, 0);
} else {
run_modules_pre(module_fds); // 运行modules插入的hook代码
// Send the bitset of module status back to magiskd from system_server
dynamic_bitset bits;
for (const auto &m : modules)
bits[m.getId()] = true;
write_int(fd, static_cast<int>(bits.slots()));
for (int i = 0; i < bits.slots(); ++i) {
auto l = bits.get_slot(i);
xwrite(fd, &l, sizeof(l));
}
}
close(fd);
}
sanitize_fds();
}
void HookContext::nativeForkSystemServer_post() {
if (pid == 0) {
ZLOGV("post forkSystemServer\n");
run_modules_post();
}
fork_post();
}
- Zygisk 加载是通过替换 app_process ,修改 LD_PRELOAD ,再执行原 app_process 实现
- LD_PRELOAD 执行的so,hook三个和创建进程开辟进程空间有关系的关键函数
参考文章:
https://gist.github.com/5ec1cff/bfe06429f5bf1da262c40d0145e9f190#file-zygisk-md
[完]