zygisk是什么
来自面具作者的文章对于zygisk的介绍
Zygisk是Zygote的Magisk。这将在Zygote进程中运行Magisk的一部分,使Magisk模块更强大。这也是Magisk"退出舞台"理念中非常重要的一部分。当某一个进程位于上述拒绝列表中时,Magisk 将清理该过程的内存空间,以确保不会应用任何修改(P.S.1)。当然,Zygisk 仍然是 WIP(开发中)的,一旦实施准备好测试,会公布更多细节。
对于zygisk的理解
- zygisk依然是magisk,但是是更高级的版本,带了注入Zygote功能。
- zygisk的开启是可选的,在magisk设置。如不开启将和原来差不多。
- zygisk将提供超过之前magisk的功能,尤其是注入hook。所以可以基于zygisk开发出lsp(新的xp框架)而且不基于riru。
- riru和zygisk对Zygote的修改部分重复了,所以二者不能很好共存。
- zygisk没有hide,因为作者加入了谷歌之后不再做检测对抗了。
magisk实现原理
在magisk上有一行说明:systemless root
那么这个systemless是什么东西
在了解systemless之前我们还要了解什么是root以及如何获得root
root原理(来自网络)
下面是来自网络的root解释,年代已久不过我感觉还是有用的。
root权限就是root(管理员权限)用户的权限,该权限可以修改根目录下的文件
为了让我们的应用使用root权限
首先我们需要一个处理root请求的 su 进程
我们链接手机到电脑 输入adb shell
再输入 su 如果没root就会提示 su文件找不到
ok 既然没有这个文件那我们就push这个su执行文件到
/system/bin 下面吧
不行,因为你没有root权限所以不能向system传输文件
同样的因为你不能传输文件也就不能植入su 应用程序
也就是没法让你获得root权限 这就是死循环
之前的root方式
这个不细说了
systemless root
之前的root会对system目录做文件修改
而systemless root 则不会对system目录做任何修改
这是为了应对高版本的系统的检测
基于systemless的root就是 systemless root
magisk 是实现 systemless root 的一个方式,实际上在magisk之前就有systemless root。只不过magisk把这个发扬光大了。
magisk实现方式
在之前做过magisk的分析,我把答案抄过来。
magisk刷入过程
- 安装magisk apk
- 提取boot.img
- 制作修改的patch_boot.img
- 刷入patch_boot.img开机即可
不难得出核心在于针对boot.img文件的修改
那么修改的patch_boot.img具体做了什么是我们最关心的
启动日志分析
开机 打开面具的日志这里
可以看到这里在
/sbin/.magisk/mirror
下面建立了一个系统根目录的镜像目录
我们打开文件的时候就会发现 sbin 里面还拥有一个su执行文件和模块插件等其他文件
然后进行 bind mounted 操作,也就是让这个文件系统生效。
这时候ps -A其实就可以看到 su 进程,也就有了授权root的能力。
接下来就可以用magisk来管理root权限,请求root的都会出现在超级用户
这个列表里。
同时基于 bind mount 可以针对系统文件进行替换以及在这里执行sh脚本,这就是我们写的magisk模块的实现了。
非常多的细节可以在https://topjohnwu.github.io/Magisk/details.html 里面得到,或许我的理解会有误差。
zygisk实现原理
在了解zygisk之前,先了解magisk的功能实现原理。我这里分析zygisk关于注入Zygote的逻辑。
实现原理猜想
在分析之前我就在猜想他的实现原理了。
xposed注入Zygote原理是替换 app_process,而magisk可以做到替换系统文件。那么magisk是不是可以替换app_process来注入Zygote。
虽然zygisk是新一点东西 好在已经有人分析过了。可以踩在巨人的肩上了。
验证猜想
在magisk仓库里面搜索 app_process
果然找到了相关代码
打开main.cpp看到一行注释
// Entrypoint for app_process overlay
确认无疑了,对app_process进行了替换
源码分析
Zygisk 源码分析 实际上这篇文章已经写的非常详细了,我加上了一点自己的理解。算是拾人牙慧了。
太长不看版本
先总结一下整个流程
module.cpp
中判断是否开启zygisk_enabled
,是就进入
mount_zygisk进行 app_process
的 mount,将
app_process
替换成magisk
main.cpp
是magisk
执行文件,作用是替换app_process
工作(原理是用 LD_PRELOAD 注入)
entry.cpp
加载Zygisk,分为两个阶段加载。一阶段dlopen载入二阶段,二阶段进行hook。 其他的细节包括hide hook处理看源码分析。
注入阶段
native/jni/core/module.cpp
搜索 app_process
出现最多的就是这个module.cpp文件
打开文件看到bind_mount
就知道这个类对文件进行了挂载。
对app_process
进行处理的函数在magic_mount
中
mount是Linux下的一个命令,它可以将分区挂接到Linux的一个文件夹下,从而将分区和该目录联系起来,因此我们只要访问这个文件夹,就相当于访问该分区了。
新建文件夹后mount_zygisk
处理挂载文件夹
mount_zygisk 函数
两件事:文件移动和文件夹挂载
app_process 32/64 放到了zygisk下面
magisk 32/64 放到了 MAGISKTMP
下面
完成文件的app_process
到magisk
的替换
做完这些之后,我们重启手机启动运行的app_process
其实就是运行magisk
我们其实可以看到这些文件的
加载阶段
这部分核心研究跑起来的magisk跑起来之后具体做了什么
包括hook的实现逻辑
native/jni/zygisk/main.cpp
运行了magisk文件,现在进入他的函数分析他的逻辑。
Entrypoint for app_process overlay
对着这个注释往下看
app_process_main 函数
通过socker通信设置几个变量的值 LD_PRELOAD INJECT_ENV_1
MAGISKTMP_ENV
ZygiskRequest::SETUP
是通信的标志
这里非常核心的一个就是 LD_PRELOAD 变量的值设置,这将作用到后面的注入
LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。
native/jni/zygisk/entry.cpp
找到消息接收的地方
进入setup_files函数
buf 获取了可执行路径
sendfd 发送持有的真正的 app_process 文件 fd
write_string 发送 MAGISKTMP 路径
到这里我开始发现与参考文件不同的地方了
尤其是
根本对不上,zygisk下面也没有对应的so文件
那就是改版了,下面就自己分析新版zygisk的原理吧
native/jni/zygisk/entry.cpp
zyg_init函数
这里进行两阶段的加载操作
下面分析两个阶段的事情
first_stage_entry
一阶段:好在作者加了大段注释 ,我谷歌翻译一遍
在这里我发现 LD_PRELOAD 是个非常重要的变量
修改这个值就可以加载我们的注入的程序
这里一阶段就是来加载 LD_PRELOAD
的文件
这个文件就是之前 app_process_main
设置的
获取fd的值这里最终将这个值写入到 MAGISKFD_ENV
中
同时进行第一次的 android_dlopen_ext
second_stage_entry
获取MAGISKFD_ENV
的值进行 path
进入 android_dlopen_ext
再一次 dlopen 并立马关闭
恢复 变量 MAGISKTMP_ENV
MAGISKFD_ENV
在结束的时候,调用了
- sanitize_environ();
- hook_functions();
sanitize_environ
字面意思就是 消毒环境。就是用于对抗检测的,消灭证据。具体实现这里不是重点。
hook_functions
具体的hook实现部分
hook实现
我们已经将自己的文件注入进去,现在要实现hook
native/jni/zygisk/hook.cpp
核心的hook_functions 处于这个类
我们核心关注XHOOK_REGISTER(ANDROID_RUNTIME, jniRegisterNativeMethods);
这部分的逻辑
在之前的riru分析中,我们已经了解过这种hook的实现方法。基于xhook,对register方法进行hook后做个指针的切换。
几个重要的宏定义
XHOOK_REGISTER
宏
使用 xhook register
DCL_HOOK_FUNC
宏
对函数进行hook
DCL_PRE_POST
宏
定义函数的per和post两种
比如 fork定义了 fork_pre 和 fork_post
挑一对 pre和post来分析
nativeSpecializeAppProcess_pre/nativeSpecializeAppProcess_post
可以发现核心就是两个
也就是运行modules插入的hook代码
这个两个方法需要记得后面继续分析。
hookAndSaveJNIMethods
执行hook操作的方法 核心的hook逻辑
HOOK_JNI
三个函数的hook
- HOOK_JNI(nativeForkAndSpecialize)
- HOOK_JNI(nativeSpecializeAppProcess)
- HOOK_JNI(nativeForkSystemServer)
这三个函数是app进程启动的Zygote中三个相关函数,是应用进程或者系统服务进程被fork 出来的时候会调用的方法。
实现hook后引入
native/jni/zygisk/jni_hooks.hpp
实现具体hook的地方,在这里区分了版本和手机类型。将新旧方法做了保存和指针更换处理。
比如这个就是安卓Q版本的hook处理逻辑,这里有个重要的HookContext
结构体。
核心的三句
ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<decltype(&nativeSpecializeAppProcess_q)>(nativeSpecializeAppProcess_orig)( env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name,
is_child_zygote, instruction_set, app_data_dir);
ctx.nativeSpecializeAppProcess_post();
pre
和post
调用用于执行插入的hook code。
而中间那句就是执行原方法orgin
最后
分析到这里差不多,未完待续。后面还有关于如何zygisk屏蔽应用以及如何load moudules 代码的有时间再说。
参考
Zygisk 源码分析
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
最后于 2022-4-13 16:02
被小黄鸭爱学习编辑
,原因: 勘误