mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 07:57:39 +00:00
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:
parent
42eb928054
commit
9fbd079560
16
build.py
16
build.py
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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') {
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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());
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
zygisk_inject_entry;
|
||||
unload_first_stage;
|
||||
NativeBridgeItf;
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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[]);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
35
native/src/zygisk/native_bridge.cpp
Normal file
35
native/src/zygisk/native_bridge.cpp
Normal 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,
|
||||
};
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user