Refactor zygisk to use native bridge to inject

Co-authored-by: vvb2060 <vvb2060@gmail.com>
Co-authored-by: topjohnwu <topjohnwu@gmail.com>
This commit is contained in:
LoveSy 2023-11-06 17:19:12 -08:00 committed by topjohnwu
parent 42eb928054
commit 9fbd079560
17 changed files with 183 additions and 347 deletions

View File

@ -246,7 +246,7 @@ def run_ndk_build(flags):
error("Build binary failed!")
os.chdir("..")
for arch in archs:
for tgt in support_targets + ["libinit-ld.so", "libzygisk-ld.so"]:
for tgt in support_targets + ["libinit-ld.so"]:
source = op.join("native", "libs", arch, tgt)
target = op.join("native", "out", arch, tgt)
mv(source, target)
@ -342,9 +342,6 @@ 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)
write_if_diff(op.join(native_gen_path, f"{arch}_binaries.h"), text)
@ -395,8 +392,9 @@ def build_binary(args):
flag = ""
clean = False
if "magisk" in args.target or "magiskinit" in args.target:
flag += " B_PRELOAD=1"
if "magisk" in args.target:
flag += " B_MAGISK=1"
clean = True
if "magiskpolicy" in args.target:
flag += " B_POLICY=1"
@ -417,14 +415,10 @@ def build_binary(args):
if flag:
run_ndk_build(flag)
# magiskinit and magisk embeds preload.so
# magiskinit embeds preload.so
flag = ""
if "magisk" in args.target:
flag += " B_MAGISK=1"
clean = True
if "magiskinit" in args.target:
flag += " B_INIT=1"

View File

@ -38,6 +38,7 @@ LOCAL_SRC_FILES := \
zygisk/utils.cpp \
zygisk/hook.cpp \
zygisk/memory.cpp \
zygisk/native_bridge.cpp \
zygisk/deny/cli.cpp \
zygisk/deny/utils.cpp \
zygisk/deny/revert.cpp
@ -57,12 +58,6 @@ LOCAL_SRC_FILES := init/preload.c
LOCAL_STRIP_MODE := --strip-all
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := zygisk-ld
LOCAL_SRC_FILES := zygisk/loader.c
LOCAL_STRIP_MODE := --strip-all
include $(BUILD_SHARED_LIBRARY)
endif
ifdef B_INIT

View File

@ -31,11 +31,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') {

View File

@ -416,6 +416,8 @@ static void boot_complete() {
// Ensure manager exists
check_pkg_refresh();
get_manager(0, nullptr, true);
reset_zygisk(true);
}
void boot_stage_handler(int client, int code) {

View File

@ -144,6 +144,7 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
LOGI("** zygote restarted\n");
pkg_xml_ino = 0;
prune_su_access();
reset_zygisk(false);
close(client);
break;
case MainRequest::SQLITE_CMD:
@ -158,7 +159,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:

View File

@ -15,6 +15,8 @@ using namespace std;
#define VLOGD(tag, from, to) LOGD("%-8s: %s <- %s\n", tag, to, from)
static string native_bridge = "0";
static 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)
@ -22,9 +24,6 @@ static int bind_mount(const char *reason, const char *from, const char *to) {
return ret;
}
string node_entry::module_mnt;
string node_entry::mirror_dir;
/*************************
* Node Tree Construction
*************************/
@ -211,6 +210,21 @@ public:
}
};
class zygisk_node : public node_entry {
public:
explicit zygisk_node(const char *name, bool is64bit) : node_entry(name, DT_REG, this),
is64bit(is64bit) {}
void mount() override {
const string src = get_magisk_tmp() + "/magisk"s + (is64bit ? "64" : "32");
create_and_mount("zygisk", src);
xmount(nullptr, node_path().data(), nullptr, MS_REMOUNT | MS_BIND | MS_RDONLY, nullptr);
}
private:
bool is64bit;
};
static void inject_magisk_bins(root_node *system) {
auto bin = system->get_child<inter_node>("bin");
if (!bin) {
@ -228,24 +242,28 @@ static void inject_magisk_bins(root_node *system) {
delete bin->extract("supolicy");
}
vector<module_info> *module_list;
int app_process_32 = -1;
int app_process_64 = -1;
static void inject_zygisk_libs(root_node *system) {
if (access("/system/bin/linker", 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(native_bridge.data(), false));
}
#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 = get_magisk_tmp() + "/magisk"s #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); \
if (access("/system/bin/linker64", 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(native_bridge.data(), true));
}
}
vector<module_info> *module_list;
void load_modules() {
node_entry::mirror_dir = get_magisk_tmp() + "/"s MIRRDIR;
node_entry::module_mnt = get_magisk_tmp() + "/"s MODULEMNT "/";
@ -289,6 +307,22 @@ void load_modules() {
inject_magisk_bins(system);
}
if (zygisk_enabled) {
string native_bridge_orig = get_prop(NBPROP);
if (native_bridge_orig.empty()) {
native_bridge_orig = "0";
}
native_bridge = native_bridge_orig != "0" ? ZYGISKLDR + native_bridge_orig : ZYGISKLDR;
set_prop(NBPROP, native_bridge.data(), true);
// Weather Huawei's Maple compiler is enabled.
// If so, system server will be created by a special Zygote which ignores the native bridge
// and make system server out of our control. Avoid it by disabling.
if (get_prop("ro.maple.enable") == "1") {
set_prop("ro.maple.enable", "0", true);
}
inject_zygisk_libs(system);
}
if (!system->is_empty()) {
// Handle special read-only partitions
for (const char *part : { "/vendor", "/product", "/system_ext" }) {
@ -304,14 +338,6 @@ void load_modules() {
root->mount();
}
// Mount on top of modules to enable zygisk
if (zygisk_enabled) {
string zygisk_bin = get_magisk_tmp() + "/"s ZYGISKBIN;
mkdir(zygisk_bin.data(), 0);
mount_zygisk(32)
mount_zygisk(64)
}
ssprintf(buf, sizeof(buf), "%s/" WORKERDIR, get_magisk_tmp());
xmount(nullptr, buf, nullptr, MS_REMOUNT | MS_RDONLY, nullptr);
}
@ -491,3 +517,23 @@ void exec_module_scripts(const char *stage) {
[](const module_info &info) -> string_view { return info.name; });
exec_module_scripts(stage, module_names);
}
void reset_zygisk(bool restore) {
if (!zygisk_enabled) return;
static atomic_uint zygote_start_count{1};
if (restore) {
zygote_start_count = 1;
} else if (zygote_start_count.fetch_add(1) > 3) {
LOGW("zygote crashes too many times, rolling-back\n");
restore = true;
}
if (restore) {
string native_bridge_orig = "0";
if (native_bridge.length() > strlen(ZYGISKLDR)) {
native_bridge_orig = native_bridge.substr(strlen(ZYGISKLDR));
}
set_prop(NBPROP, native_bridge_orig.data(), true);
} else {
set_prop(NBPROP, native_bridge.data(), true);
}
}

View File

@ -49,8 +49,8 @@ public:
virtual void mount() = 0;
static string module_mnt;
static string mirror_dir;
inline static string module_mnt;
inline static string mirror_dir;
protected:
template<class T>

View File

@ -10,11 +10,11 @@
using namespace std;
#define CALL_PROVIDER \
exe, "/system/bin", "com.android.commands.content.Content", \
"/system/bin/app_process", "/system/bin", "com.android.commands.content.Content", \
"call", "--uri", target, "--user", user, "--method", action
#define START_ACTIVITY \
exe, "/system/bin", "com.android.commands.am.Am", \
"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \
"start", "-p", target, "--user", user, "-a", "android.intent.action.VIEW", \
"-f", "0x58800020", "--es", "action", action
@ -130,21 +130,10 @@ static bool check_no_error(int fd) {
static void exec_cmd(const char *action, vector<Extra> &data,
const shared_ptr<su_info> &info, bool provider = true) {
char exe[128];
char target[128];
char user[4];
ssprintf(user, sizeof(user), "%d", to_user_id(info->eval_uid));
if (zygisk_enabled) {
#if defined(__LP64__)
ssprintf(exe, sizeof(exe), "/proc/self/fd/%d", app_process_64);
#else
ssprintf(exe, sizeof(exe), "/proc/self/fd/%d", app_process_32);
#endif
} else {
strscpy(exe, "/system/bin/app_process", sizeof(exe));
}
// First try content provider call method
if (provider) {
ssprintf(target, sizeof(target), "content://%s.provider", info->mgr_pkg.data());

View File

@ -1,4 +1,4 @@
{
zygisk_inject_entry;
unload_first_stage;
NativeBridgeItf;
};

View File

@ -35,7 +35,6 @@ enum : int {
SQLITE_CMD,
REMOVE_MODULES,
ZYGISK,
ZYGISK_PASSTHROUGH,
_STAGE_BARRIER_,
@ -67,9 +66,8 @@ struct module_info {
};
extern bool zygisk_enabled;
extern int app_process_32;
extern int app_process_64;
extern std::vector<module_info> *module_list;
void reset_zygisk(bool restore);
extern "C" const char *get_magisk_tmp();
int connect_daemon(int req, bool create = false);

View File

@ -3,6 +3,8 @@
#include <string>
#define JAVA_PACKAGE_NAME "com.topjohnwu.magisk"
#define ZYGISKLDR "libzygisk.so"
#define NBPROP "ro.dalvik.vm.native.bridge"
#define SECURE_DIR "/data/adb"
#define MODULEROOT SECURE_DIR "/modules"
#define MODULEUPGRADE SECURE_DIR "/modules_update"
@ -49,5 +51,4 @@ extern int SDK_INT;
int magisk_main(int argc, char *argv[]);
int su_client_main(int argc, char *argv[]);
int resetprop_main(int argc, char *argv[]);
int app_process_main(int argc, char *argv[]);
int zygisk_main(int argc, char *argv[]);

View File

@ -6,9 +6,7 @@
#include <android/dlext.h>
#include <base.hpp>
#include <daemon.hpp>
#include <magisk.hpp>
#include <selinux.hpp>
#include "zygisk.hpp"
#include "module.hpp"
@ -18,43 +16,11 @@ using namespace std;
void *self_handle = nullptr;
// Make sure /proc/self/environ is sanitized
// Filter env and reset MM_ENV_END
static void sanitize_environ() {
char *cur = environ[0];
for (int i = 0; environ[i]; ++i) {
// Copy all env onto the original stack
size_t len = strlen(environ[i]);
memmove(cur, environ[i], len + 1);
environ[i] = cur;
cur += len + 1;
}
prctl(PR_SET_MM, PR_SET_MM_ENV_END, cur, 0, 0);
}
extern "C" void unload_first_stage() {
ZLOGD("unloading first stage\n");
unmap_all(HIJACK_BIN);
xumount2(HIJACK_BIN, MNT_DETACH);
}
extern "C" void zygisk_inject_entry(void *handle) {
zygisk_logging();
ZLOGD("load success\n");
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");
}
extern "C" [[maybe_unused]] void zygisk_inject_entry(void *handle) {
self_handle = handle;
sanitize_environ();
zygisk_logging();
hook_functions();
ZLOGD("load success\n");
}
// The following code runs in zygote/app process
@ -146,88 +112,6 @@ static void connect_companion(int client, bool is_64_bit) {
send_fd(zygiskd_socket, client);
}
static timespec last_zygote_start;
static int zygote_start_counts[] = { 0, 0 };
#define zygote_start_count zygote_start_counts[is_64_bit]
#define zygote_started (zygote_start_counts[0] + zygote_start_counts[1])
#define zygote_start_reset(val) { zygote_start_counts[0] = val; zygote_start_counts[1] = val; }
static void setup_files(int client, const sock_cred *cred) {
LOGD("zygisk: setup files for pid=[%d]\n", cred->pid);
char buf[4096];
if (!get_exe(cred->pid, buf, sizeof(buf))) {
write_int(client, 1);
return;
}
// 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 = get_magisk_tmp() + "/"s ZYGISKBIN "/loader64.so";
app_fd = app_process_64;
} else {
hbin = HIJACK_BIN32;
mbin = get_magisk_tmp() + "/"s ZYGISKBIN "/loader32.so";
app_fd = app_process_32;
}
if (!zygote_started) {
// First zygote launch, record time
clock_gettime(CLOCK_MONOTONIC, &last_zygote_start);
}
if (zygote_start_count) {
// This zygote ABI had started before, kill existing zygiskd
close(zygiskd_sockets[0]);
close(zygiskd_sockets[1]);
zygiskd_sockets[0] = -1;
zygiskd_sockets[1] = -1;
xumount2(hbin, MNT_DETACH);
}
++zygote_start_count;
if (zygote_start_count >= 5) {
// Bootloop prevention
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
if (ts.tv_sec - last_zygote_start.tv_sec > 60) {
// This is very likely manual soft reboot
memcpy(&last_zygote_start, &ts, sizeof(ts));
zygote_start_reset(1);
} else {
// If any zygote relaunched more than 5 times within a minute,
// don't do any setups further to prevent bootloop.
zygote_start_reset(999);
write_int(client, 1);
return;
}
}
// Ack
write_int(client, 0);
// Receive and bind mount loader
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());
close(ld_fd);
setfilecon(mbin.data(), MAGISK_FILE_CON);
xmount(mbin.data(), hbin, nullptr, MS_BIND, nullptr);
send_fd(client, app_fd);
}
static void magiskd_passthrough(int client) {
bool is_64_bit = read_int(client);
write_int(client, 0);
send_fd(client, is_64_bit ? app_process_64 : app_process_32);
}
extern bool uid_granted_root(int uid);
static void get_process_info(int client, const sock_cred *cred) {
int uid = read_int(client);
@ -301,12 +185,6 @@ 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);
break;
case ZygiskRequest::PASSTHROUGH:
magiskd_passthrough(client);
break;
case ZygiskRequest::GET_INFO:
get_process_info(client, cred);
break;

View File

@ -2,14 +2,14 @@
#include <sys/mount.h>
#include <dlfcn.h>
#include <regex.h>
#include <unwind.h>
#include <bitset>
#include <list>
#include <lsplt.hpp>
#include <base.hpp>
#include <flags.h>
#include <daemon.hpp>
#include <magisk.hpp>
#include "zygisk.hpp"
#include "memory.hpp"
@ -29,6 +29,7 @@ static void hook_unloader();
static void unhook_functions();
static void hook_jni_env();
static void restore_jni_env(JNIEnv *env);
static void ReloadNativeBridge(const string &nb);
namespace {
@ -157,9 +158,6 @@ DCL_HOOK_FUNC(int, unshare, int flags) {
(g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) {
if (g_ctx->flags[DO_REVERT_UNMOUNT]) {
revert_unmount();
} else {
umount2("/system/bin/app_process64", MNT_DETACH);
umount2("/system/bin/app_process32", MNT_DETACH);
}
// Restore errno back to 0
errno = 0;
@ -209,6 +207,21 @@ DCL_HOOK_FUNC(int, pthread_attr_destroy, void *target) {
return res;
}
// it should be safe to assume all dlclose's in libnativebridge are for zygisk_loader
DCL_HOOK_FUNC(int, dlclose, void *handle) {
static bool kDone = false;
if (!kDone) {
ZLOGV("dlclose zygisk_loader\n");
kDone = true;
string nb = get_prop(NBPROP);
if (nb != ZYGISKLDR) {
auto orig_nb_path = nb.substr(sizeof(ZYGISKLDR) - 1);
ReloadNativeBridge(orig_nb_path);
}
}
[[clang::musttail]] return old_dlclose(handle);
}
#undef DCL_HOOK_FUNC
// -----------------------------------------------------------------
@ -714,6 +727,37 @@ void HookContext::nativeForkAndSpecialize_post() {
// -----------------------------------------------------------------
inline void *unwind_get_region_start(_Unwind_Context *ctx) {
auto fp = _Unwind_GetRegionStart(ctx);
#if defined(__arm__)
auto pc = _Unwind_GetGR(ctx, 15); // r15 is pc
if (pc & 1) {
// Thumb mode
fp |= 1;
}
#endif
return reinterpret_cast<void *>(fp);
}
static void ReloadNativeBridge(const string &nb) {
// Use unwind to find the address of LoadNativeBridge
// and call it to get NativeBridgeRuntimeCallbacks
void *load_native_bridge = nullptr;
_Unwind_Backtrace(+[](struct _Unwind_Context *ctx, void *arg) -> _Unwind_Reason_Code {
void *fp = unwind_get_region_start(ctx);
Dl_info info{};
dladdr(fp, &info);
ZLOGV("backtrace: %p %s\n", fp, info.dli_fname ? info.dli_fname : "???");
if (info.dli_fname && std::string_view(info.dli_fname).ends_with("/libart.so")) {
ZLOGV("LoadNativeBridge: %p\n", fp);
*reinterpret_cast<void **>(arg) = fp;
return _URC_END_OF_STACK;
}
return _URC_NO_REASON;
}, &load_native_bridge);
reinterpret_cast<bool (*)(const string &)>(load_native_bridge)(nb);
}
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);
@ -741,15 +785,20 @@ void hook_functions() {
ino_t android_runtime_inode = 0;
dev_t android_runtime_dev = 0;
ino_t native_bridge_inode = 0;
dev_t native_bridge_dev = 0;
for (auto &map : lsplt::MapInfo::Scan()) {
if (map.path.ends_with("libandroid_runtime.so")) {
if (map.path.ends_with("/libandroid_runtime.so")) {
android_runtime_inode = map.inode;
android_runtime_dev = map.dev;
break;
} else if (map.path.ends_with("/libnativebridge.so")) {
native_bridge_inode = map.inode;
native_bridge_dev = map.dev;
}
}
PLT_HOOK_REGISTER(native_bridge_dev, native_bridge_inode, dlclose);
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, androidSetCreateThreadFunc);

View File

@ -1,28 +0,0 @@
#include <dlfcn.h>
#include <android/dlext.h>
#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(void) {
android_dlextinfo info = {
.flags = ANDROID_DLEXT_FORCE_LOAD
};
void *handle = android_dlopen_ext(SECOND_STAGE_PATH, RTLD_LAZY, &info);
if (handle) {
void(*entry)(void*) = dlsym(handle, "zygisk_inject_entry");
if (entry) {
entry(handle);
}
void (*unload)(void) = dlsym(handle, "unload_first_stage");
if (unload) {
__attribute__((musttail)) return unload();
}
}
}

View File

@ -7,98 +7,11 @@
#include <socket.hpp>
#include <daemon.hpp>
#include <selinux.hpp>
#include <embed.hpp>
#include "zygisk.hpp"
using namespace std;
// Entrypoint for app_process overlay
int app_process_main(int argc, char *argv[]) {
android_logging();
char buf[PATH_MAX];
bool zygote = false;
if (auto fp = open_file("/proc/self/attr/current", "r")) {
fscanf(fp.get(), "%s", buf);
zygote = (buf == "u:r:zygote:s0"sv);
}
if (!zygote) {
// For the non zygote case, we need to get real app_process via passthrough
// We have to connect magiskd via exec-ing magisk due to SELinux restrictions
// This is actually only relevant for calling app_process via ADB shell
// because zygisk shall already have the app_process overlays unmounted
// during app process specialization within its private mount namespace.
int fds[2];
socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
if (fork_dont_care() == 0) {
// This fd has to survive exec
fcntl(fds[1], F_SETFD, 0);
ssprintf(buf, sizeof(buf), "%d", fds[1]);
#if defined(__LP64__)
execlp("magisk", "", "zygisk", "passthrough", buf, "1", (char *) nullptr);
#else
execlp("magisk", "", "zygisk", "passthrough", buf, "0", (char *) nullptr);
#endif
exit(-1);
}
close(fds[1]);
if (read_int(fds[0]) != 0) {
fprintf(stderr, "Failed to connect magiskd, try umount %s or reboot.\n", argv[0]);
return 1;
}
int app_proc_fd = recv_fd(fds[0]);
if (app_proc_fd < 0)
return 1;
close(fds[0]);
fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC);
fexecve(app_proc_fd, argv, environ);
return 1;
}
if (int socket = zygisk_request(ZygiskRequest::SETUP); socket >= 0) {
do {
if (read_int(socket) != 0)
break;
// Send over zygisk loader
write_int(socket, sizeof(zygisk_ld));
xwrite(socket, zygisk_ld, sizeof(zygisk_ld));
int app_proc_fd = recv_fd(socket);
if (app_proc_fd < 0)
break;
if (char *ld = getenv("LD_PRELOAD")) {
string env = ld;
env += ':';
env += HIJACK_BIN;
setenv("LD_PRELOAD", env.data(), 1);
} else {
setenv("LD_PRELOAD", HIJACK_BIN, 1);
}
close(socket);
fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC);
fexecve(app_proc_fd, argv, environ);
} 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;
}
static void zygiskd(int socket) {
if (getuid() != 0 || fcntl(socket, F_GETFD) < 0)
exit(-1);
@ -177,30 +90,8 @@ static void zygiskd(int socket) {
// This should only ever be called internally
int zygisk_main(int argc, char *argv[]) {
android_logging();
if (argc == 3 && argv[1] == "companion"sv) {
zygiskd(parse_int(argv[2]));
} else if (argc == 4 && argv[1] == "passthrough"sv) {
int client = parse_int(argv[2]);
int is_64_bit = parse_int(argv[3]);
if (fcntl(client, F_GETFD) < 0)
return 1;
if (int magiskd = connect_daemon(MainRequest::ZYGISK_PASSTHROUGH); magiskd >= 0) {
write_int(magiskd, ZygiskRequest::PASSTHROUGH);
write_int(magiskd, is_64_bit);
if (read_int(magiskd) != 0) {
write_int(client, 1);
return 0;
}
write_int(client, 0);
int real_app_fd = recv_fd(magiskd);
send_fd(client, real_app_fd);
} else {
write_int(client, 1);
return 0;
}
}
return 0;
}

View File

@ -0,0 +1,35 @@
#include <android/dlext.h>
#include <dlfcn.h>
#include <unwind.h>
#include <magisk.hpp>
#include "../core/core.hpp"
// The reference layout of this struct
// https://cs.android.com/android/platform/superproject/main/+/main:art/libnativebridge/include/nativebridge/native_bridge.h
struct NativeBridgeCallbacks {
uint32_t version;
void *padding[5];
bool (*isCompatibleWith)(uint32_t);
};
static bool is_compatible_with(uint32_t) {
auto name = get_prop(NBPROP);
android_dlextinfo info = {
.flags = ANDROID_DLEXT_FORCE_LOAD
};
void *handle = android_dlopen_ext(name.data(), RTLD_LAZY, &info);
if (handle) {
auto entry = reinterpret_cast<void (*)(void *)>(dlsym(handle, "zygisk_inject_entry"));
if (entry) {
entry(handle);
}
}
return false;
}
extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf{
.version = 2,
.padding = {},
.isCompatibleWith = &is_compatible_with,
};

View File

@ -6,18 +6,11 @@
#include <vector>
#include <daemon.hpp>
#define MAGISKTMP_ENV "MAGISKTMP"
#define HIJACK_BIN64 "/system/bin/appwidget"
#define HIJACK_BIN32 "/system/bin/bu"
namespace ZygiskRequest {
enum : int {
SETUP,
GET_INFO,
CONNECT_COMPANION,
GET_MODDIR,
PASSTHROUGH,
END
};
}
@ -27,13 +20,11 @@ enum : int {
#define ZLOGE(...) LOGE("zygisk64: " __VA_ARGS__)
#define ZLOGI(...) LOGI("zygisk64: " __VA_ARGS__)
#define ZLOGW(...) LOGW("zygisk64: " __VA_ARGS__)
#define HIJACK_BIN HIJACK_BIN64
#else
#define ZLOGD(...) LOGD("zygisk32: " __VA_ARGS__)
#define ZLOGE(...) LOGE("zygisk32: " __VA_ARGS__)
#define ZLOGI(...) LOGI("zygisk32: " __VA_ARGS__)
#define ZLOGW(...) LOGW("zygisk32: " __VA_ARGS__)
#define HIJACK_BIN HIJACK_BIN32
#endif
// Unmap all pages matching the name