Zygisk 开发入门
只能说这个玩意是真的不尽人意。。。。
即使之前 5ec1cff 的版本 也是几乎是没改啥玩意的,从官方的,三年前的模板到现在的版本,用的还是 Android.mk
所以当使用一些依赖于 CMake 的项目的时候,会非常非常麻烦。。。
所以这里我觉得还是找一些比较 modern 的 template 吧,起码不要使用 Android.mk 这种玩意了。。。
我使用的是这个 https://github.com/Perfare/Zygisk-Il2CppDumper
这个项目也是克隆下来打开就可以直接编译,但是不知道为什么的 Android studio 并没有识别出 gradle 任务,所以我要编译还是手动敲命令行
.\gradlew.bat assembleRelease
然后就是直接打开 main.cpp
继承于 zygisk::ModuleBase 的
如何在合适的时候加载 so
首先是如何放置这个 so
其实我本来想的是直接在模块里面使用文件读写将这个 so 读入
但是我尝试了一下,首先是在 preAppSpecialize 里面,先获取到 app 的 app_data_dir,然后再复制过去,但是试了几次好像会报出一个 selinux 错误,avc denied for search, 具体原因我没有深究,不过在多次尝试之后我决定这个不是个好方法。虽然理论上在 preSpecialize 的时候,我们是在以 zygote 的权限执行代码的,理论上是有权限去访问 app 的 data 目录的,不过实际测试下来好像并不可以,所以尝试别的方法将 so 放到 app 可以读取的目录里面
当然,更改 sepolicy 也是可以尝试的,不过秉承着 selinux 相关最好就是不要动的原则,我决定还是先试试别的方法,是在不行我们再去动 sepolicy
使用模块安装脚本进行复制
这个尝试成功了,不过只能说不尽人意吧。
模块安装的时候,Magisk 或 ksu 会去运行安装包里面的 customize.sh,所以我们只需要在这里面写上复制的逻辑就可以了
但是要复制到哪里去呢?
最好的肯定是复制到 app 的数据目录下面,但是我在查阅资料时发现了这样一篇 文章,是 zygisk il2cppdumper 的作者写的
这篇文章作者提出可以使用 memfd 来创建一个指向内存的文件
但是实际经过尝试,好像还是会出现 selinux 权限问题,看起来 Android 高版本不允许 dlopen 一个存在于 memfd 中的 so
但是如果使用模块复制,清除 app 数据目录的时候又会导致这个 so 也被清除掉,还需要重新安装一次模块,非常麻烦
所以,目前我是用的方法是我非常不想使用的方法。。。也就是从 /data/local/tmp 里面加载
实际上这是真的非常不好,因为已经证实部分 app 会直接通过 readlinkat 进行反查,查看自己的 fd 有没有存在于这个目录的,所以这实属是下下策,在前面的方法都不奏效的时候进行的。。。。
这里插一句,千万不要使用 Windows 的编辑器来写这些脚本,会导致最后的文件名中带有 \r!!!md 搞了半天没发现这个问题,结果是 linux 换行和 Windows 不一样导致根本没有复制到指定的目录。。。
修复权限
然后历经千辛万苦,终于是能够将我们的目标 so 复制到一个 app 可以访问的目录了
但是,当我们去 dlopen 这个 so 的时候,又出现了 selinux 错误。。。avc denied for execute
这个时候,先去 /data/local/tmp 下面查看下 selinux 上下文
ls -Z /data/local/tmp
结果可以发现,被复制过去的文件上下文自动变成了 u:object_r:shell_data_file:s0
这个上下文对于 untrusted_app 域来说是不支持 execute 的
所以我们需要 chcon 一下,然后再 chmod 一下
至于要改成什么,当然是和我们 app 自带的 lib 的 selinux 上下文一样即可,也就是 u:object_r:apk_data_file:s0
所以
cp $MODPATH/libr.so /data/local/tmp/libr.so
chcon u:object_r:apk_data_file:s0 /data/local/tmp/libr.so
chmod 777 /data/local/tmp/libr.so
然后就可以愉快的加载啦
4/21 UPDATE
千万不要忘了取消模板里面
api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
不然的话,如果有任何需要持久操作的东西都会炸!