Skip to content

Commit

Permalink
Refactor zygisk to use native bridge to inject
Browse files Browse the repository at this point in the history
Co-authored-by: 残页 <a1364259@163.com>
  • Loading branch information
yujincheng08 and canyie committed Mar 4, 2023
1 parent 1aade8f commit 27a2700
Show file tree
Hide file tree
Showing 23 changed files with 391 additions and 765 deletions.
15 changes: 12 additions & 3 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def vprint(str):

cpu_count = multiprocessing.cpu_count()
archs = ['armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64']
archs32map = {'x86_64': 'x86', 'arm64-v8a': 'armeabi-v7a'}
triples = ['armv7a-linux-androideabi', 'i686-linux-android', 'aarch64-linux-android', 'x86_64-linux-android']
default_targets = ['magisk', 'magiskinit', 'magiskboot', 'magiskpolicy', 'busybox']
support_targets = default_targets + ['resetprop']
Expand Down Expand Up @@ -314,9 +315,17 @@ def dump_bin_header(args):
preload = op.join('native', 'out', arch, 'libinit-ld.so')
with open(preload, 'rb') as src:
text = binary_dump(src, 'init_ld_xz')
preload = op.join('native', 'out', arch, 'libzygisk-ld.so')
with open(preload, 'rb') as src:
text += binary_dump(src, 'zygisk_ld', compressor=lambda x: x)
if archs32map.get(arch) is not None:
preload = op.join('native', 'out', arch, 'libzygisk-ld.so')
with open(preload, 'rb') as src:
text += binary_dump(src, 'zygisk64_ld', compressor=lambda x: x)
preload = op.join('native', 'out', archs32map[arch], 'libzygisk-ld.so')
with open(preload, 'rb') as src:
text += binary_dump(src, 'zygisk32_ld', compressor=lambda x: x)
else:
preload = op.join('native', 'out', arch, 'libzygisk-ld.so')
with open(preload, 'rb') as src:
text += binary_dump(src, 'zygisk32_ld', compressor=lambda x: x)
write_if_diff(op.join(native_gen_path, f'{arch}_binaries.h'), text)


Expand Down
1 change: 0 additions & 1 deletion native/src/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ LOCAL_SRC_FILES := \
su/su_daemon.cpp \
zygisk/entry.cpp \
zygisk/main.cpp \
zygisk/utils.cpp \
zygisk/hook.cpp \
zygisk/memory.cpp \
zygisk/deny/cli.cpp \
Expand Down
5 changes: 0 additions & 5 deletions native/src/core/applets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ int main(int argc, char *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);
}

umask(0);

if (argv[0][0] == '\0') {
Expand Down
1 change: 1 addition & 0 deletions native/src/core/bootstages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum : int {
static int boot_state = FLAG_NONE;

bool zygisk_enabled = false;
std::string native_bridge = "";

/*********
* Setup *
Expand Down
4 changes: 2 additions & 2 deletions native/src/core/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,11 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
su_daemon_handler(client, &cred);
break;
case MainRequest::ZYGOTE_RESTART:
close(client);
LOGI("** zygote restarted\n");
pkg_xml_ino = 0;
prune_su_access();
remount_zygisk();
close(client);
break;
case MainRequest::SQLITE_CMD:
exec_sql(client);
Expand All @@ -160,7 +161,6 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
break;
}
case MainRequest::ZYGISK:
case MainRequest::ZYGISK_PASSTHROUGH:
zygisk_handler(client, &cred);
break;
default:
Expand Down
191 changes: 143 additions & 48 deletions native/src/core/module.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <sys/mount.h>
#include <map>
#include <utility>
#include <sys/vfs.h>

#include <base.hpp>
#include <magisk.hpp>
Expand All @@ -10,18 +11,40 @@

#include "core.hpp"
#include "node.hpp"
#include "embed.hpp"

using namespace std;

#define VLOGD(tag, from, to) LOGD("%-8s: %s <- %s\n", tag, to, from)

static int bind_mount(const char *reason, const char *from, const char *to) {
namespace {
int bind_mount(const char *reason, const char *from, const char *to) {
int ret = xmount(from, to, nullptr, MS_BIND | MS_REC, nullptr);
if (ret == 0)
VLOGD(reason, from, to);
return ret;
}

std::atomic_uint zygote_start_count{1};

void remount_zygisk(bool is64bit, bool rollback) {
auto lib = "/system/lib"s + (is64bit ? "64" : "32") + native_bridge;
if (struct statfs st{}; statfs(lib.data(), &st) == 0) {
if (!rollback && st.f_type != TMPFS_MAGIC) {
// Our native bridge hasn't been mounted yet or has been unmounted before
xmount((MAGISKTMP + "/" ZYGISKBIN "/libzygisk_loader" + (is64bit ? "64" : "32") +
".so").data(), lib.data(), nullptr,
MS_BIND, nullptr);
} else if (rollback && st.f_type == TMPFS_MAGIC && native_bridge != ZYGISKLDR) {
// We have mounted our native bridge which overrode the original one,
// and now we have been asked to revert our changes, unmount it
xumount2(lib.data(), MNT_DETACH);
}
}
}

}

string node_entry::module_mnt;
string node_entry::mirror_dir;

Expand Down Expand Up @@ -179,30 +202,75 @@ void tmpfs_node::mount() {
* Magisk Stuffs
****************/

class magisk_node : public node_entry {
public:
explicit magisk_node(const char *name) : node_entry(name, DT_REG, this) {}
void magisk_node::mount() {
const string src = MAGISKTMP + "/" + name();
if (access(src.data(), F_OK))
return;

void mount() override {
const string src = MAGISKTMP + "/" + name();
if (access(src.data(), F_OK))
return;
const string &dir_name = parent()->node_path();
if (name() == "magisk") {
for (int i = 0; applet_names[i]; ++i) {
string dest = dir_name + "/" + applet_names[i];
VLOGD("create", "./magisk", dest.data());
xsymlink("./magisk", dest.data());
}
} else if (name() == "magiskpolicy") {
string dest = dir_name + "/supolicy";
VLOGD("create", "./magiskpolicy", dest.data());
xsymlink("./magiskpolicy", dest.data());
}
create_and_mount("magisk", src);
}

const string &dir_name = parent()->node_path();
if (name() == "magisk") {
for (int i = 0; applet_names[i]; ++i) {
string dest = dir_name + "/" + applet_names[i];
VLOGD("create", "./magisk", dest.data());
xsymlink("./magisk", dest.data());
}
} else {
string dest = dir_name + "/supolicy";
VLOGD("create", "./magiskpolicy", dest.data());
xsymlink("./magiskpolicy", dest.data());
void zygisk_node::mount() {
// prepare zygisk loader
auto zygisk_bin = MAGISKTMP + "/" ZYGISKBIN;
xmkdir(zygisk_bin.data(), 0);
string src = zygisk_bin + "/libzygisk_loader" + (is64bit ? "64" : "32") + ".so";
int out = xopen(src.data(), O_WRONLY | O_CREAT | O_CLOEXEC, 0);
if (is64bit) {
#ifdef __LP64__
xwrite(out, zygisk64_ld, sizeof(zygisk64_ld));
#endif
} else {
xwrite(out, zygisk32_ld, sizeof(zygisk32_ld));
}
close(out);


const string &dest = node_path();
string mirror = mirror_path();
file_attr attr{};
if (getattr(mirror.data(), &attr) == 0) {
// file exists, mount upon it
if (attr.st.st_mode & S_IFLNK) {
// if it's a symlink, we need to resolve it
char buf[PATH_MAX] = {};
xrealpath(mirror.data(), buf, sizeof(buf));
mirror = mirror_dir + (buf[0] == '/' ? "" : "/") + buf;
// and get the real file attributes
getattr(mirror.data(), &attr);
} else if (!isa<tmpfs_node>(parent())) {
// if not symlink and parent is not a tmpfs, we can just bind mount
bind_mount("zygisk", src.data(), dest.data());
return;
}
create_and_mount("magisk", src);
VLOGD("cp", mirror.data(), dest.data());
// now, we need to create a copy of the original file
int s = xopen(mirror.data(), O_RDONLY | O_CLOEXEC, 0);
int d = xopen(dest.data(), O_WRONLY | O_CREAT | O_CLOEXEC, 0);
xsendfile(d, s, nullptr, attr.st.st_size);
close(s);
close(d);
// and set the attributes
setattr(dest.data(), &attr);
// finally, we can bind mount
bind_mount("zygisk", src.data(), dest.data());
} else {
// file does not exist, create a new one and mount upon it
create_and_mount("zygisk", src);
}
};
}

static void inject_magisk_bins(root_node *system) {
auto bin = system->get_child<inter_node>("bin");
Expand All @@ -213,6 +281,10 @@ static void inject_magisk_bins(root_node *system) {

// Insert binaries
bin->insert(new magisk_node("magisk"));
if (access("/system/bin/app_process32", F_OK) == 0)
bin->insert(new magisk_node("magisk32"));
if (access("/system/bin/app_process64", F_OK) == 0)
bin->insert(new magisk_node("magisk64"));
bin->insert(new magisk_node("magiskpolicy"));

// Also delete all applets to make sure no modules can override it
Expand All @@ -222,22 +294,6 @@ static void inject_magisk_bins(root_node *system) {
}

vector<module_info> *module_list;
int app_process_32 = -1;
int app_process_64 = -1;

#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); \
}

void load_modules() {
node_entry::mirror_dir = MAGISKTMP + "/" MIRRDIR;
Expand Down Expand Up @@ -276,9 +332,42 @@ void load_modules() {
system->collect_module_files(module, fd);
close(fd);
}
if (MAGISKTMP != "/sbin" || !str_contains(getenv("PATH") ?: "", "/sbin")) {
// Need to inject our binaries into /system/bin
inject_magisk_bins(system);

inject_magisk_bins(system);

if (zygisk_enabled) {
// If the system has built-in native bridge, we mount on top of it to avoid unnecessary property changes
// If not, we change the property to install our fake native bridge

native_bridge = getprop(NBPROP);
if (native_bridge.empty() || native_bridge == "0") {
native_bridge = ZYGISKLDR;
setprop(NBPROP, ZYGISKLDR, false);
}
if (access("/system/lib", F_OK) == 0) {
auto lib = system->get_child<inter_node>("lib");
if (!lib) {
lib = new inter_node("lib");
system->insert(lib);
}
lib->insert(new zygisk_node(false, native_bridge.data()));
}

if (access("/system/lib64", F_OK) == 0) {
auto lib64 = system->get_child<inter_node>("lib64");
if (!lib64) {
lib64 = new inter_node("lib64");
system->insert(lib64);
}
lib64->insert(new zygisk_node(true, native_bridge.data()));
}

// If Huawei's Maple compiler is enabled, the system server will be created from
// a special Zygote which ignores native bridge and is out of our control
// To avoid it, we simply disable the "Maple compiler"
if (getprop("ro.maple.enable") == "1") {
setprop("ro.maple.enable", "0", false);
}
}

if (!system->is_empty()) {
Expand All @@ -297,14 +386,6 @@ void load_modules() {
root->prepare();
root->mount();
}

// Mount on top of modules to enable zygisk
if (zygisk_enabled) {
string zygisk_bin = MAGISKTMP + "/" ZYGISKBIN;
mkdir(zygisk_bin.data(), 0);
mount_zygisk(32)
mount_zygisk(64)
}
}

/************************
Expand Down Expand Up @@ -482,3 +563,17 @@ void exec_module_scripts(const char *stage) {
[](const module_info &info) -> string_view { return info.name; });
exec_module_scripts(stage, module_names);
}

// Used to remount zygisk after soft reboot
void remount_zygisk() {
bool rollback = zygote_start_count.fetch_add(1) >= 5;
if (rollback) {
LOGW("zygote crashes too many times, rolling-back\n");
}
if (native_bridge == ZYGISKLDR) {
setprop(NBPROP, "0");
} else {
remount_zygisk(false, rollback);
remount_zygisk(true, rollback);
}
}
14 changes: 14 additions & 0 deletions native/src/core/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,20 @@ class tmpfs_node : public dir_node {
void mount() override;
};

class magisk_node : public node_entry {
public:
explicit magisk_node(const char *name) : node_entry(name, DT_REG, this) {}
void mount() override;
};

class zygisk_node : public magisk_node {
public:
explicit zygisk_node(bool is64bit, const char *name) : magisk_node(name), is64bit(is64bit) {}
void mount() override;
private:
bool is64bit;
};

template<class T>
static bool isa(node_entry *node) {
return node && (node->_node_type & type_id<T>());
Expand Down
4 changes: 2 additions & 2 deletions native/src/core/restorecon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void restore_tmpcon() {
setfilecon_at(dfd, entry->d_name, SYSTEM_CON);

if (SDK_INT >= 26) {
string magisk = MAGISKTMP + "/magisk";
setfilecon(magisk.data(), EXEC_CON);
setfilecon((MAGISKTMP + "/magisk32").data(), EXEC_CON);
setfilecon((MAGISKTMP + "/magisk64").data(), EXEC_CON);
}
}
7 changes: 3 additions & 4 deletions native/src/include/daemon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ enum : int {
SQLITE_CMD,
REMOVE_MODULES,
ZYGISK,
ZYGISK_PASSTHROUGH,

_STAGE_BARRIER_,

Expand Down Expand Up @@ -68,8 +67,7 @@ struct module_info {
};

extern bool zygisk_enabled;
extern int app_process_32;
extern int app_process_64;
extern std::string native_bridge;
extern std::vector<module_info> *module_list;

int connect_daemon(int req, bool create = false);
Expand Down Expand Up @@ -102,7 +100,8 @@ std::vector<bool> get_app_no_list();
int get_manager(int user_id = 0, std::string *pkg = nullptr, bool install = false);
void prune_su_access();

// Denylist
// Zygisk
void remount_zygisk();
extern std::atomic_flag skip_pkg_rescan;
void initialize_denylist();
int denylist_cli(int argc, char **argv);
Loading

0 comments on commit 27a2700

Please sign in to comment.