You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
Compare commits
11 Commits
v4-0.9.0
...
v4-0.9.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
698e6e6624 | ||
|
|
fbb792ef13 | ||
|
|
381abc99d5 | ||
|
|
fd603982e8 | ||
|
|
2d384685e7 | ||
|
|
675febfd14 | ||
|
|
ad32c4efb0 | ||
|
|
331b01b0f4 | ||
|
|
8079123e43 | ||
|
|
9a95377d7b | ||
|
|
043cfd93d6 |
@@ -11,7 +11,7 @@ Standalone implementation of Zygisk, providing Zygisk API support for KernelSU a
|
||||
### KernelSU
|
||||
|
||||
+ Minimal KernelSU version: 10940
|
||||
+ Minimal ksud version: 11412
|
||||
+ Minimal ksud version: 11424
|
||||
+ Kernel has full SELinux patch support
|
||||
|
||||
### Magisk
|
||||
|
||||
@@ -20,11 +20,11 @@ val gitCommitHash = "git rev-parse --verify --short HEAD".execute()
|
||||
|
||||
val moduleId by extra("zygisksu")
|
||||
val moduleName by extra("Zygisk Next")
|
||||
val verName by extra("v4-0.9.0")
|
||||
val verName by extra("v4-0.9.1.1")
|
||||
val verCode by extra(gitCommitCount)
|
||||
val commitHash by extra(gitCommitHash)
|
||||
val minKsuVersion by extra(10940)
|
||||
val minKsudVersion by extra(11412)
|
||||
val minKsudVersion by extra(11425)
|
||||
val maxKsuVersion by extra(20000)
|
||||
val minMagiskVersion by extra(26402)
|
||||
|
||||
|
||||
@@ -1,23 +1 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
android.useAndroidX=false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[versions]
|
||||
agp = "8.1.2"
|
||||
kotlin = "1.9.10"
|
||||
agp = "8.2.0"
|
||||
kotlin = "1.9.22"
|
||||
|
||||
[plugins]
|
||||
agp-lib = { id = "com.android.library", version.ref = "agp" }
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
#include "socket_utils.h"
|
||||
|
||||
namespace zygiskd {
|
||||
static std::string zygisk_path;
|
||||
static std::string TMP_PATH;
|
||||
void Init(const char *path) {
|
||||
LOGI("zygisk path set to %s", path);
|
||||
zygisk_path = path;
|
||||
TMP_PATH = path;
|
||||
}
|
||||
|
||||
std::string GetTmpPath() {
|
||||
return TMP_PATH;
|
||||
}
|
||||
|
||||
int Connect(uint8_t retry) {
|
||||
@@ -19,7 +22,7 @@ namespace zygiskd {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path={0},
|
||||
};
|
||||
auto socket_path = zygisk_path + kCPSocketName;
|
||||
auto socket_path = TMP_PATH + kCPSocketName;
|
||||
strcpy(addr.sun_path, socket_path.c_str());
|
||||
socklen_t socklen = sizeof(addr);
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#endif
|
||||
|
||||
constexpr auto kCPSocketName = "/" LP_SELECT("cp32", "cp64") ".sock";
|
||||
#define TMP_PATH "/debug_ramdisk/zygisksu"
|
||||
|
||||
class UniqueFd {
|
||||
using Fd = int;
|
||||
@@ -63,7 +62,9 @@ namespace zygiskd {
|
||||
SystemServerStarted,
|
||||
};
|
||||
|
||||
void Init(const char *path = TMP_PATH);
|
||||
void Init(const char *path);
|
||||
|
||||
std::string GetTmpPath();
|
||||
|
||||
bool PingHeartbeat();
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ using namespace std;
|
||||
void *self_handle = nullptr;
|
||||
|
||||
extern "C" [[gnu::visibility("default")]]
|
||||
void entry(void* handle) {
|
||||
void entry(void* handle, const char* path) {
|
||||
LOGI("Zygisk library injected, version %s", ZKSU_VERSION);
|
||||
self_handle = handle;
|
||||
zygiskd::Init(path);
|
||||
|
||||
zygiskd::Init();
|
||||
if (!zygiskd::PingHeartbeat()) {
|
||||
LOGE("Zygisk daemon is not running");
|
||||
return;
|
||||
|
||||
@@ -46,7 +46,7 @@ void revert_unmount_ksu() {
|
||||
&& std::find(KSU_PARTITIONS.begin(), KSU_PARTITIONS.end(), info.target) != KSU_PARTITIONS.end()) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
// Unmount /debug_ramdisk
|
||||
// Unmount temp dir
|
||||
if (info.type == "tmpfs" && info.source == KSU_OVERLAY_SOURCE) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
zygiskd::Init(getenv("TMP_PATH"));
|
||||
if (argc >= 2 && argv[1] == "monitor"sv) {
|
||||
init_monitor();
|
||||
return 0;
|
||||
} else if (argc >= 3 && argv[1] == "trace"sv) {
|
||||
if (argc >= 4 && argv[3] == "--restart"sv) {
|
||||
zygiskd::Init();
|
||||
zygiskd::ZygoteRestart();
|
||||
}
|
||||
auto pid = strtol(argv[2], 0, 0);
|
||||
|
||||
@@ -37,7 +37,7 @@ enum TracingState {
|
||||
|
||||
std::string monitor_stop_reason;
|
||||
|
||||
constexpr char SOCKET_NAME[] = TMP_PATH "/init_monitor";
|
||||
constexpr char SOCKET_NAME[] = "init_monitor";
|
||||
|
||||
struct EventLoop;
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnregisterHandler(EventHandler &handler) {
|
||||
[[maybe_unused]] bool UnregisterHandler(EventHandler &handler) {
|
||||
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), nullptr) == -1) {
|
||||
PLOGE("failed to del event handler");
|
||||
return false;
|
||||
@@ -136,7 +136,7 @@ struct SocketHandler : public EventHandler {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path={0},
|
||||
};
|
||||
strcpy(addr.sun_path, SOCKET_NAME);
|
||||
sprintf(addr.sun_path, "%s/%s", zygiskd::GetTmpPath().c_str(), SOCKET_NAME);
|
||||
socklen_t socklen = sizeof(sa_family_t) + strlen(addr.sun_path);
|
||||
if (bind(sock_fd_, (struct sockaddr *) &addr, socklen) == -1) {
|
||||
PLOGE("bind socket");
|
||||
@@ -149,7 +149,7 @@ struct SocketHandler : public EventHandler {
|
||||
return sock_fd_;
|
||||
}
|
||||
|
||||
void HandleEvent(EventLoop &loop, uint32_t event) override {
|
||||
void HandleEvent(EventLoop &loop, uint32_t) override {
|
||||
struct [[gnu::packed]] MsgHead {
|
||||
Command cmd;
|
||||
int length;
|
||||
@@ -346,7 +346,7 @@ public:
|
||||
return signal_fd_;
|
||||
}
|
||||
|
||||
void HandleEvent(EventLoop &loop, uint32_t event) override {
|
||||
void HandleEvent(EventLoop &, uint32_t) override {
|
||||
for (;;) {
|
||||
ssize_t s = read(signal_fd_, &fdsi, sizeof(fdsi));
|
||||
if (s == -1) {
|
||||
@@ -472,7 +472,7 @@ public:
|
||||
} while (false);
|
||||
updateStatus();
|
||||
} else {
|
||||
LOGE("process %d received unknown status %s", pid,
|
||||
LOGW("process %d received unknown status %s", pid,
|
||||
parse_status(status).c_str());
|
||||
}
|
||||
process.erase(state);
|
||||
@@ -544,7 +544,7 @@ static void updateStatus() {
|
||||
}
|
||||
|
||||
static bool prepare_environment() {
|
||||
prop_path = TMP_PATH "/module.prop";
|
||||
prop_path = zygiskd::GetTmpPath() + "/module.prop";
|
||||
close(open(prop_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644));
|
||||
auto orig_prop = xopen_file("./module.prop", "r");
|
||||
if (orig_prop == nullptr) {
|
||||
@@ -595,13 +595,13 @@ void send_control_command(Command cmd) {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path={0},
|
||||
};
|
||||
strcpy(addr.sun_path, SOCKET_NAME);
|
||||
sprintf(addr.sun_path, "%s/%s", zygiskd::GetTmpPath().c_str(), SOCKET_NAME);
|
||||
socklen_t socklen = sizeof(sa_family_t) + strlen(addr.sun_path);
|
||||
auto nsend = sendto(sockfd, (void *) &cmd, sizeof(cmd), 0, (sockaddr *) &addr, socklen);
|
||||
if (nsend == -1) {
|
||||
err(EXIT_FAILURE, "send");
|
||||
} else if (nsend != sizeof(cmd)) {
|
||||
printf("send %ld != %ld\n", nsend, sizeof(cmd));
|
||||
printf("send %zu != %zu\n", nsend, sizeof(cmd));
|
||||
exit(1);
|
||||
}
|
||||
printf("command sent\n");
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <signal.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include <string>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
bool inject_on_main(int pid, const char *lib_path) {
|
||||
@@ -23,42 +25,42 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
struct user_regs_struct regs{}, backup{};
|
||||
auto map = MapInfo::Scan(std::to_string(pid));
|
||||
if (!get_regs(pid, regs)) return false;
|
||||
auto arg = reinterpret_cast<uintptr_t *>(regs.REG_SP);
|
||||
LOGD("kernel argument %p %s", arg, get_addr_mem_region(map, arg).c_str());
|
||||
auto arg = static_cast<uintptr_t>(regs.REG_SP);
|
||||
LOGV("kernel argument %" PRIxPTR " %s", arg, get_addr_mem_region(map, arg).c_str());
|
||||
int argc;
|
||||
auto argv = reinterpret_cast<char **>(reinterpret_cast<uintptr_t *>(arg) + 1);
|
||||
LOGD("argv %p", argv);
|
||||
LOGV("argv %p", argv);
|
||||
read_proc(pid, arg, &argc, sizeof(argc));
|
||||
LOGD("argc %d", argc);
|
||||
LOGV("argc %d", argc);
|
||||
auto envp = argv + argc + 1;
|
||||
LOGD("envp %p", envp);
|
||||
LOGV("envp %p", envp);
|
||||
auto p = envp;
|
||||
while (true) {
|
||||
uintptr_t *buf;
|
||||
read_proc(pid, (uintptr_t *) p, &buf, sizeof(buf));
|
||||
read_proc(pid, (uintptr_t) p, &buf, sizeof(buf));
|
||||
if (buf != nullptr) ++p;
|
||||
else break;
|
||||
}
|
||||
++p;
|
||||
auto auxv = reinterpret_cast<ElfW(auxv_t) *>(p);
|
||||
LOGD("auxv %p %s", auxv, get_addr_mem_region(map, auxv).c_str());
|
||||
LOGV("auxv %p %s", auxv, get_addr_mem_region(map, (uintptr_t) auxv).c_str());
|
||||
auto v = auxv;
|
||||
void *entry_addr = nullptr;
|
||||
void *addr_of_entry_addr = nullptr;
|
||||
uintptr_t entry_addr = 0;
|
||||
uintptr_t addr_of_entry_addr = 0;
|
||||
while (true) {
|
||||
ElfW(auxv_t) buf;
|
||||
read_proc(pid, (uintptr_t *) v, &buf, sizeof(buf));
|
||||
read_proc(pid, (uintptr_t) v, &buf, sizeof(buf));
|
||||
if (buf.a_type == AT_ENTRY) {
|
||||
entry_addr = reinterpret_cast<void *>(buf.a_un.a_val);
|
||||
addr_of_entry_addr = reinterpret_cast<char *>(v) + offsetof(ElfW(auxv_t), a_un);
|
||||
LOGD("entry address %p %s (v=%p, entry_addr=%p)", entry_addr,
|
||||
get_addr_mem_region(map, entry_addr).c_str(), v, addr_of_entry_addr);
|
||||
entry_addr = (uintptr_t) buf.a_un.a_val;
|
||||
addr_of_entry_addr = (uintptr_t) v + offsetof(ElfW(auxv_t), a_un);
|
||||
LOGV("entry address %" PRIxPTR " %s (entry=%" PRIxPTR ", entry_addr=%" PRIxPTR ")", entry_addr,
|
||||
get_addr_mem_region(map, entry_addr).c_str(), (uintptr_t) v, addr_of_entry_addr);
|
||||
break;
|
||||
}
|
||||
if (buf.a_type == AT_NULL) break;
|
||||
v++;
|
||||
}
|
||||
if (entry_addr == nullptr) {
|
||||
if (entry_addr == 0) {
|
||||
LOGE("failed to get entry");
|
||||
return false;
|
||||
}
|
||||
@@ -66,13 +68,13 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
// Replace the program entry with an invalid address
|
||||
// For arm32 compatibility, we set the last bit to the same as the entry address
|
||||
uintptr_t break_addr = (-0x05ec1cff & ~1) | ((uintptr_t) entry_addr & 1);
|
||||
if (!write_proc(pid, (uintptr_t *) addr_of_entry_addr, &break_addr, sizeof(break_addr))) return false;
|
||||
if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &break_addr, sizeof(break_addr))) return false;
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
int status;
|
||||
wait_for_trace(pid, &status, __WALL);
|
||||
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) {
|
||||
if (!get_regs(pid, regs)) return false;
|
||||
if ((regs.REG_IP & ~1) != (break_addr & ~1)) {
|
||||
if (static_cast<uintptr_t>(regs.REG_IP & ~1) != (break_addr & ~1)) {
|
||||
LOGE("stopped at unknown addr %p", (void *) regs.REG_IP);
|
||||
return false;
|
||||
}
|
||||
@@ -80,7 +82,7 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
LOGD("stopped at entry");
|
||||
|
||||
// restore entry address
|
||||
if (!write_proc(pid, (uintptr_t *) addr_of_entry_addr, &entry_addr, sizeof(entry_addr))) return false;
|
||||
if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &entry_addr, sizeof(entry_addr))) return false;
|
||||
|
||||
// backup registers
|
||||
memcpy(&backup, ®s, sizeof(regs));
|
||||
@@ -119,11 +121,13 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
args.clear();
|
||||
args.push_back(dlerror_str_addr);
|
||||
auto dlerror_len = remote_call(pid, regs, (uintptr_t) strlen_addr, (uintptr_t) libc_return_addr, args);
|
||||
LOGD("dlerror len %ld", dlerror_len);
|
||||
if (dlerror_len <= 0) return false;
|
||||
if (dlerror_len <= 0) {
|
||||
LOGE("dlerror len <= 0");
|
||||
return false;
|
||||
}
|
||||
std::string err;
|
||||
err.resize(dlerror_len + 1, 0);
|
||||
read_proc(pid, (uintptr_t*) dlerror_str_addr, err.data(), dlerror_len);
|
||||
read_proc(pid, (uintptr_t) dlerror_str_addr, err.data(), dlerror_len);
|
||||
LOGE("dlerror info %s", err.c_str());
|
||||
return false;
|
||||
}
|
||||
@@ -142,9 +146,11 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// call injector entry(handle, magic)
|
||||
// call injector entry(handle, path)
|
||||
args.clear();
|
||||
args.push_back(remote_handle);
|
||||
str = push_string(pid, regs, zygiskd::GetTmpPath().c_str());
|
||||
args.push_back((long) str);
|
||||
remote_call(pid, regs, injector_entry, (uintptr_t) libc_return_addr, args);
|
||||
|
||||
// reset pc to entry
|
||||
@@ -178,7 +184,7 @@ bool trace_zygote(int pid) {
|
||||
}
|
||||
WAIT_OR_DIE
|
||||
if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) {
|
||||
std::string lib_path = TMP_PATH;
|
||||
std::string lib_path = zygiskd::GetTmpPath();
|
||||
lib_path += "/lib" LP_SELECT("", "64") "/libzygisk.so";
|
||||
if (!inject_on_main(pid, lib_path.c_str())) {
|
||||
LOGE("failed to inject");
|
||||
|
||||
@@ -102,8 +102,8 @@ std::vector<MapInfo> MapInfo::Scan(const std::string& pid) {
|
||||
return info;
|
||||
}
|
||||
|
||||
ssize_t write_proc(int pid, uintptr_t *remote_addr, const void *buf, size_t len) {
|
||||
LOGD("write to remote addr %p size %zu", remote_addr, len);
|
||||
ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len) {
|
||||
LOGV("write to remote addr %" PRIxPTR " size %zu", remote_addr, len);
|
||||
struct iovec local{
|
||||
.iov_base = (void *) buf,
|
||||
.iov_len = len
|
||||
@@ -115,13 +115,13 @@ ssize_t write_proc(int pid, uintptr_t *remote_addr, const void *buf, size_t len)
|
||||
auto l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
|
||||
if (l == -1) {
|
||||
PLOGE("process_vm_writev");
|
||||
} else if (l != len) {
|
||||
} else if (static_cast<size_t>(l) != len) {
|
||||
LOGW("not fully written: %zu, excepted %zu", l, len);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
ssize_t read_proc(int pid, uintptr_t *remote_addr, void *buf, size_t len) {
|
||||
ssize_t read_proc(int pid, uintptr_t remote_addr, void *buf, size_t len) {
|
||||
struct iovec local{
|
||||
.iov_base = (void *) buf,
|
||||
.iov_len = len
|
||||
@@ -133,7 +133,7 @@ ssize_t read_proc(int pid, uintptr_t *remote_addr, void *buf, size_t len) {
|
||||
auto l = process_vm_readv(pid, &local, 1, &remote, 1, 0);
|
||||
if (l == -1) {
|
||||
PLOGE("process_vm_readv");
|
||||
} else if (l != len) {
|
||||
} else if (static_cast<size_t>(l) != len) {
|
||||
LOGW("not fully read: %zu, excepted %zu", l, len);
|
||||
}
|
||||
return l;
|
||||
@@ -177,9 +177,9 @@ bool set_regs(int pid, struct user_regs_struct ®s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string get_addr_mem_region(std::vector<MapInfo> &info, void *addr) {
|
||||
std::string get_addr_mem_region(std::vector<MapInfo> &info, uintptr_t addr) {
|
||||
for (auto &map: info) {
|
||||
if (map.start <= (uintptr_t) addr && map.end > (uintptr_t) addr) {
|
||||
if (map.start <= addr && map.end > addr) {
|
||||
auto s = std::string(map.path);
|
||||
s += ' ';
|
||||
s += map.perms & PROT_READ ? 'r' : '-';
|
||||
@@ -248,24 +248,24 @@ void align_stack(struct user_regs_struct ®s, long preserve) {
|
||||
regs.REG_SP = (regs.REG_SP - preserve) & ~0xf;
|
||||
}
|
||||
|
||||
void *push_string(int pid, struct user_regs_struct ®s, const char *str) {
|
||||
uintptr_t push_string(int pid, struct user_regs_struct ®s, const char *str) {
|
||||
auto len = strlen(str) + 1;
|
||||
regs.REG_SP -= len;
|
||||
align_stack(regs);
|
||||
auto addr = reinterpret_cast<uintptr_t *>(regs.REG_SP);
|
||||
auto addr = static_cast<uintptr_t>(regs.REG_SP);
|
||||
if (!write_proc(pid, addr, str, len)) {
|
||||
LOGE("failed to write string %s", str);
|
||||
}
|
||||
LOGD("pushed string %p", addr);
|
||||
LOGD("pushed string %" PRIxPTR, addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_addr, uintptr_t return_addr,
|
||||
std::vector<long> &args) {
|
||||
align_stack(regs);
|
||||
LOGD("call %d args", args.size());
|
||||
LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size());
|
||||
for (auto &a: args) {
|
||||
LOGD("arg %p", (void *) a);
|
||||
LOGV("arg %p", (void *) a);
|
||||
}
|
||||
#if defined(__x86_64__)
|
||||
if (args.size() >= 1) {
|
||||
@@ -289,12 +289,12 @@ uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_add
|
||||
if (args.size() > 6) {
|
||||
auto remain = (args.size() - 6) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
if (!write_proc(pid, (uintptr_t *) regs.REG_SP, args.data(), remain)) {
|
||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) {
|
||||
LOGE("failed to push arguments");
|
||||
}
|
||||
}
|
||||
regs.REG_SP -= sizeof(long);
|
||||
if (!write_proc(pid, (uintptr_t *) regs.REG_SP, &return_addr, sizeof(return_addr))) {
|
||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, &return_addr, sizeof(return_addr))) {
|
||||
LOGE("failed to write return addr");
|
||||
}
|
||||
regs.REG_IP = func_addr;
|
||||
@@ -302,34 +302,34 @@ uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_add
|
||||
if (args.size() > 0) {
|
||||
auto remain = (args.size()) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
if (!write_proc(pid, (uintptr_t *)regs.REG_SP, args.data(), remain)) {
|
||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) {
|
||||
LOGE("failed to push arguments");
|
||||
}
|
||||
}
|
||||
regs.REG_SP -= sizeof(long);
|
||||
if (!write_proc(pid, (uintptr_t*) regs.REG_SP, &return_addr, sizeof(return_addr))) {
|
||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, &return_addr, sizeof(return_addr))) {
|
||||
LOGE("failed to write return addr");
|
||||
}
|
||||
regs.REG_IP = func_addr;
|
||||
#elif defined(__aarch64__)
|
||||
for (int i = 0; i < args.size() && i < 8; i++) {
|
||||
for (size_t i = 0; i < args.size() && i < 8; i++) {
|
||||
regs.regs[i] = args[i];
|
||||
}
|
||||
if (args.size() > 8) {
|
||||
auto remain = (args.size() - 8) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
write_proc(pid, (uintptr_t *)regs.REG_SP, args.data(), remain);
|
||||
write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain);
|
||||
}
|
||||
regs.regs[30] = return_addr;
|
||||
regs.REG_IP = func_addr;
|
||||
#elif defined(__arm__)
|
||||
for (int i = 0; i < args.size() && i < 4; i++) {
|
||||
for (size_t i = 0; i < args.size() && i < 4; i++) {
|
||||
regs.uregs[i] = args[i];
|
||||
}
|
||||
if (args.size() > 4) {
|
||||
auto remain = (args.size() - 4) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
write_proc(pid, (uintptr_t *)regs.REG_SP, args.data(), remain);
|
||||
write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain);
|
||||
}
|
||||
regs.uregs[14] = return_addr;
|
||||
regs.REG_IP = func_addr;
|
||||
@@ -353,7 +353,7 @@ uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_add
|
||||
return 0;
|
||||
}
|
||||
if (WSTOPSIG(status) == SIGSEGV) {
|
||||
if (regs.REG_IP != return_addr) {
|
||||
if (static_cast<uintptr_t>(regs.REG_IP) != return_addr) {
|
||||
LOGE("wrong return addr %p", (void *) regs.REG_IP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -60,15 +60,15 @@ struct MapInfo {
|
||||
#define user_regs_struct user_regs
|
||||
#endif
|
||||
|
||||
ssize_t write_proc(int pid, uintptr_t *remote_addr, const void *buf, size_t len);
|
||||
ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len);
|
||||
|
||||
ssize_t read_proc(int pid, uintptr_t *remote_addr, void *buf, size_t len);
|
||||
ssize_t read_proc(int pid, uintptr_t remote_addr, void *buf, size_t len);
|
||||
|
||||
bool get_regs(int pid, struct user_regs_struct ®s);
|
||||
|
||||
bool set_regs(int pid, struct user_regs_struct ®s);
|
||||
|
||||
std::string get_addr_mem_region(std::vector<MapInfo> &info, void *addr);
|
||||
std::string get_addr_mem_region(std::vector<MapInfo> &info, uintptr_t addr);
|
||||
|
||||
void *find_module_base(std::vector<MapInfo> &info, std::string_view suffix);
|
||||
|
||||
@@ -80,7 +80,7 @@ void *find_func_addr(
|
||||
|
||||
void align_stack(struct user_regs_struct ®s, long preserve = 0);
|
||||
|
||||
void *push_string(int pid, struct user_regs_struct ®s, const char *str);
|
||||
uintptr_t push_string(int pid, struct user_regs_struct ®s, const char *str);
|
||||
|
||||
uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_addr, uintptr_t return_addr,
|
||||
std::vector<long> &args);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import android.databinding.tool.ext.capitalizeUS
|
||||
import java.security.MessageDigest
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
@@ -35,9 +36,9 @@ android.buildFeatures {
|
||||
}
|
||||
|
||||
androidComponents.onVariants { variant ->
|
||||
val variantLowered = variant.name.toLowerCase()
|
||||
val variantCapped = variant.name.capitalize()
|
||||
val buildTypeLowered = variant.buildType?.toLowerCase()
|
||||
val variantLowered = variant.name.lowercase()
|
||||
val variantCapped = variant.name.capitalizeUS()
|
||||
val buildTypeLowered = variant.buildType?.lowercase()
|
||||
|
||||
val moduleDir = layout.buildDirectory.dir("outputs/module/$variantLowered")
|
||||
val zipFileName = "$moduleName-$verName-$verCode-$commitHash-$buildTypeLowered.zip".replace(' ', '-')
|
||||
@@ -77,11 +78,11 @@ androidComponents.onVariants { variant ->
|
||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
into("bin") {
|
||||
from(project(":zygiskd").buildDir.path + "/rustJniLibs/android")
|
||||
from(project(":zygiskd").layout.buildDirectory.file("rustJniLibs/android"))
|
||||
include("**/zygiskd")
|
||||
}
|
||||
into("lib") {
|
||||
from("${project(":loader").buildDir}/intermediates/stripped_native_libs/$variantLowered/out/lib")
|
||||
from(project(":loader").layout.buildDirectory.file("intermediates/stripped_native_libs/$variantLowered/out/lib"))
|
||||
}
|
||||
|
||||
val root = moduleDir.get()
|
||||
@@ -106,16 +107,16 @@ androidComponents.onVariants { variant ->
|
||||
.putLong(real.length())
|
||||
.array()
|
||||
sig.update(buffer)
|
||||
println("sha $path ${real.length()}")
|
||||
real.forEachBlock { bytes, size ->
|
||||
sig.update(bytes, 0, size)
|
||||
}
|
||||
}
|
||||
|
||||
fun getSign(name: String, abi32: String, abi64: String) {
|
||||
println("getSign for $name $abi32 $abi64")
|
||||
val set =
|
||||
TreeSet<Pair<File, File?>> { o1, o2 -> o1.first.path.replace("\\", "/").compareTo(o2.first.path.replace("\\", "/")) }
|
||||
val set = TreeSet<Pair<File, File?>> { o1, o2 ->
|
||||
o1.first.path.replace("\\", "/")
|
||||
.compareTo(o2.first.path.replace("\\", "/"))
|
||||
}
|
||||
set.add(Pair(root.file("module.prop").asFile, null))
|
||||
set.add(Pair(root.file("sepolicy.rule").asFile, null))
|
||||
set.add(Pair(root.file("post-fs-data.sh").asFile, null))
|
||||
@@ -173,6 +174,7 @@ androidComponents.onVariants { variant ->
|
||||
getSign("machikado.arm", "armeabi-v7a", "arm64-v8a")
|
||||
getSign("machikado.x86", "x86", "x86_64")
|
||||
} else {
|
||||
println("no private_key found, this build will not be signed")
|
||||
root.file("machikado.arm").asFile.createNewFile()
|
||||
root.file("machikado.x86").asFile.createNewFile()
|
||||
}
|
||||
@@ -192,7 +194,7 @@ androidComponents.onVariants { variant ->
|
||||
group = "module"
|
||||
dependsOn(prepareModuleFilesTask)
|
||||
archiveFileName.set(zipFileName)
|
||||
destinationDirectory.set(file("$buildDir/outputs/release"))
|
||||
destinationDirectory.set(layout.buildDirectory.file("outputs/release").get().asFile)
|
||||
from(moduleDir)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ create_sys_perm() {
|
||||
chcon u:object_r:system_file:s0 $1
|
||||
}
|
||||
|
||||
TMP_PATH="/debug_ramdisk/zygisksu"
|
||||
export TMP_PATH=/sbin
|
||||
[ -d /sbin ] || export TMP_PATH=/debug_ramdisk
|
||||
|
||||
create_sys_perm $TMP_PATH
|
||||
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
MODDIR=${0%/*}/..
|
||||
|
||||
export TMP_PATH=/sbin
|
||||
[ -d /sbin ] || export TMP_PATH=/debug_ramdisk
|
||||
|
||||
exec $MODDIR/bin/zygisk-ptrace64 ctl $*
|
||||
|
||||
@@ -64,4 +64,4 @@ afterEvaluate {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use crate::dl;
|
||||
use crate::utils::{check_unix_socket, UnixStreamExt};
|
||||
use anyhow::Result;
|
||||
use passfd::FdPassingExt;
|
||||
use rustix::fs::fstat;
|
||||
use std::ffi::c_void;
|
||||
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::thread;
|
||||
use anyhow::Result;
|
||||
use passfd::FdPassingExt;
|
||||
use rustix::fs::fstat;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use crate::utils::{check_unix_socket, UnixStreamExt};
|
||||
use crate::dl;
|
||||
|
||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||
|
||||
@@ -28,7 +27,7 @@ pub fn entry(fd: i32) {
|
||||
None => {
|
||||
log::debug!("No companion entry for `{name}`");
|
||||
stream.write_u8(0).expect("reply 0");
|
||||
return ();
|
||||
std::process::exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,7 +42,9 @@ pub fn entry(fd: i32) {
|
||||
stream.write_u8(1).expect("reply success");
|
||||
thread::spawn(move || {
|
||||
let st0 = fstat(&stream).expect("failed to stat stream");
|
||||
unsafe { entry(stream.as_raw_fd()); }
|
||||
unsafe {
|
||||
entry(stream.as_raw_fd());
|
||||
}
|
||||
// Only close client if it is the same file so we don't
|
||||
// accidentally close a re-used file descriptor.
|
||||
// This check is required because the module companion
|
||||
|
||||
@@ -1,34 +1,25 @@
|
||||
use crate::lp_select;
|
||||
use bitflags::bitflags;
|
||||
use const_format::concatcp;
|
||||
use konst::primitive::parse_i32;
|
||||
use konst::unwrap_ctx;
|
||||
use log::LevelFilter;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use crate::lp_select;
|
||||
|
||||
pub const MIN_KSU_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MIN_KSU_VERSION")));
|
||||
pub const MAX_KSU_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MAX_KSU_VERSION")));
|
||||
pub const MIN_MAGISK_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MIN_MAGISK_VERSION")));
|
||||
pub const ZKSU_VERSION: &'static str = env!("ZKSU_VERSION");
|
||||
pub const ZKSU_VERSION: &str = env!("ZKSU_VERSION");
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
|
||||
|
||||
|
||||
|
||||
pub const PATH_MODULES_DIR: &str = "..";
|
||||
pub const PATH_MODULE_PROP: &str = "module.prop";
|
||||
pub const ZYGOTE_INJECTED: i32 = lp_select!(5, 4);
|
||||
pub const DAEMON_SET_INFO: i32 = lp_select!(7, 6);
|
||||
pub const DAEMON_SET_ERROR_INFO: i32 = lp_select!(9, 8);
|
||||
pub const SYSTEM_SERVER_STARTED: i32 = 10;
|
||||
pub const TMP_DIR: &str = "/debug_ramdisk/zygisksu";
|
||||
pub const CONTROLLER_SOCKET: &str = concatcp!(TMP_DIR, "/init_monitor");
|
||||
pub const PATH_CP_NAME: &str = concatcp!(TMP_DIR, lp_select!("/cp32.sock", "/cp64.sock"));
|
||||
|
||||
pub const MAX_RESTART_COUNT: i32 = 5;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
|
||||
@@ -30,13 +30,13 @@ extern "C" {
|
||||
}
|
||||
|
||||
type AndroidCreateNamespaceFn = unsafe extern "C" fn(
|
||||
*const c_char, // name
|
||||
*const c_char, // ld_library_path
|
||||
*const c_char, // default_library_path
|
||||
u64, // type
|
||||
*const c_char, // permitted_when_isolated_path
|
||||
*mut AndroidNamespace, // parent
|
||||
*const c_void, // caller_addr
|
||||
*const c_char, // name
|
||||
*const c_char, // ld_library_path
|
||||
*const c_char, // default_library_path
|
||||
u64, // type
|
||||
*const c_char, // permitted_when_isolated_path
|
||||
*mut AndroidNamespace, // parent
|
||||
*const c_void, // caller_addr
|
||||
) -> *mut AndroidNamespace;
|
||||
|
||||
pub unsafe fn dlopen(path: &str, flags: i32) -> Result<*mut c_void> {
|
||||
@@ -57,11 +57,15 @@ pub unsafe fn dlopen(path: &str, flags: i32) -> Result<*mut c_void> {
|
||||
libc::RTLD_DEFAULT,
|
||||
std::ffi::CString::new("__loader_android_create_namespace")?.as_ptr(),
|
||||
);
|
||||
let android_create_namespace_fn: AndroidCreateNamespaceFn = std::mem::transmute(android_create_namespace_fn);
|
||||
let android_create_namespace_fn: AndroidCreateNamespaceFn =
|
||||
std::mem::transmute(android_create_namespace_fn);
|
||||
let ns = android_create_namespace_fn(
|
||||
filename, dir, std::ptr::null(),
|
||||
filename,
|
||||
dir,
|
||||
std::ptr::null(),
|
||||
ANDROID_NAMESPACE_TYPE_SHARED,
|
||||
std::ptr::null(), std::ptr::null_mut(),
|
||||
std::ptr::null(),
|
||||
std::ptr::null_mut(),
|
||||
&dlopen as *const _ as *const c_void,
|
||||
);
|
||||
if ns != std::ptr::null_mut() {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
mod companion;
|
||||
mod constants;
|
||||
mod dl;
|
||||
mod root_impl;
|
||||
mod utils;
|
||||
mod zygiskd;
|
||||
mod companion;
|
||||
|
||||
use std::future::Future;
|
||||
use crate::constants::ZKSU_VERSION;
|
||||
|
||||
fn init_android_logger(tag: &str) {
|
||||
|
||||
@@ -71,7 +71,7 @@ pub fn uid_should_umount(uid: i32) -> bool {
|
||||
// TODO: signature
|
||||
pub fn uid_is_manager(uid: i32) -> bool {
|
||||
if let Ok(s) = rustix::fs::stat("/data/user_de/0/me.weishu.kernelsu") {
|
||||
return s.st_uid == uid as u32
|
||||
return s.st_uid == uid as u32;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
@@ -1,44 +1,78 @@
|
||||
use std::process::{Command, Stdio};
|
||||
use std::fs;
|
||||
use std::os::android::fs::MetadataExt;
|
||||
use crate::constants::MIN_MAGISK_VERSION;
|
||||
use std::process::{Command, Stdio};
|
||||
use log::info;
|
||||
use crate::utils::LateInit;
|
||||
|
||||
const MAGISK_OFFICIAL: &str = "com.topjohnwu.magisk";
|
||||
const MAGISK_THIRD_PARTIES: &[(&str, &str)] = &[
|
||||
("alpha", "io.github.vvb2060.magisk"),
|
||||
("kitsune", "io.github.huskydg.magisk"),
|
||||
];
|
||||
|
||||
pub enum Version {
|
||||
Supported,
|
||||
TooOld,
|
||||
}
|
||||
|
||||
static VARIANT: LateInit<&str> = LateInit::new();
|
||||
|
||||
pub fn get_magisk() -> Option<Version> {
|
||||
let version: Option<i32> = Command::new("magisk")
|
||||
if !VARIANT.initiated() {
|
||||
Command::new("magisk")
|
||||
.arg("-v")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.map(|version| {
|
||||
let third_party = MAGISK_THIRD_PARTIES.iter().find_map(|v| {
|
||||
version.contains(v.0).then_some(v.1)
|
||||
});
|
||||
VARIANT.init(third_party.unwrap_or(MAGISK_OFFICIAL));
|
||||
info!("Magisk variant: {}", *VARIANT);
|
||||
});
|
||||
}
|
||||
Command::new("magisk")
|
||||
.arg("-V")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.spawn()
|
||||
.ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.and_then(|output| output.trim().parse().ok());
|
||||
version.map(|version| {
|
||||
if version >= MIN_MAGISK_VERSION {
|
||||
Version::Supported
|
||||
} else {
|
||||
Version::TooOld
|
||||
}
|
||||
})
|
||||
.and_then(|output| output.trim().parse::<i32>().ok())
|
||||
.map(|version| {
|
||||
if version >= MIN_MAGISK_VERSION {
|
||||
Version::Supported
|
||||
} else {
|
||||
Version::TooOld
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn uid_granted_root(uid: i32) -> bool {
|
||||
Command::new("magisk")
|
||||
.arg("--sqlite")
|
||||
.arg(format!("select 1 from policies where uid={uid} and policy=2 limit 1"))
|
||||
.arg(format!(
|
||||
"select 1 from policies where uid={uid} and policy=2 limit 1"
|
||||
))
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.spawn()
|
||||
.ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.map(|output| output.is_empty()) == Some(false)
|
||||
.map(|output| output.is_empty())
|
||||
== Some(false)
|
||||
}
|
||||
|
||||
pub fn uid_should_umount(uid: i32) -> bool {
|
||||
let output = Command::new("pm")
|
||||
.args(["list", "packages", "--uid", &uid.to_string()])
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.spawn()
|
||||
.ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok());
|
||||
let line = match output {
|
||||
@@ -54,43 +88,37 @@ pub fn uid_should_umount(uid: i32) -> bool {
|
||||
};
|
||||
Command::new("magisk")
|
||||
.arg("--sqlite")
|
||||
.arg(format!("select 1 from denylist where package_name=\"{pkg}\" limit 1"))
|
||||
.arg(format!(
|
||||
"select 1 from denylist where package_name=\"{pkg}\" limit 1"
|
||||
))
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.spawn()
|
||||
.ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.map(|output| output.is_empty()) == Some(false)
|
||||
.map(|output| output.is_empty())
|
||||
== Some(false)
|
||||
}
|
||||
|
||||
// TODO: signature
|
||||
// TODO: magisk random package name
|
||||
pub fn uid_is_manager(uid: i32) -> bool {
|
||||
let output = Command::new("magisk")
|
||||
.arg("--sqlite")
|
||||
.arg(format!("select value from strings where key=\"requester\" limit 1"))
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.spawn()
|
||||
.ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.map(|output| output.trim().to_string());
|
||||
if let Some(output) = output {
|
||||
if let Some(manager) = output.strip_prefix("value=") {
|
||||
if let Ok(s) = rustix::fs::stat(format!("/data/user_de/0/{}", manager)) {
|
||||
return s.st_uid == uid as u32;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return fs::metadata(format!("/data/user_de/0/{}", manager))
|
||||
.map(|s| s.st_uid() == uid as u32)
|
||||
.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
if let Ok(s) = rustix::fs::stat("/data/user_de/0/com.topjohnwu.magisk") {
|
||||
if s.st_uid == uid as u32 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Ok(s) = rustix::fs::stat("/data/user_de/0/io.github.vvb2060.magisk") {
|
||||
if s.st_uid == uid as u32 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
fs::metadata(format!("/data/user_de/0/{}", *VARIANT))
|
||||
.map(|s| s.st_uid() == uid as u32)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
@@ -20,21 +20,19 @@ pub fn setup() {
|
||||
let impl_ = match (ksu_version, magisk_version) {
|
||||
(None, None) => RootImpl::None,
|
||||
(Some(_), Some(_)) => RootImpl::Multiple,
|
||||
(Some(ksu_version), None) => {
|
||||
match ksu_version {
|
||||
kernelsu::Version::Supported => RootImpl::KernelSU,
|
||||
kernelsu::Version::TooOld => RootImpl::TooOld,
|
||||
kernelsu::Version::Abnormal => RootImpl::Abnormal,
|
||||
}
|
||||
}
|
||||
(None, Some(magisk_version)) => {
|
||||
match magisk_version {
|
||||
magisk::Version::Supported => RootImpl::Magisk,
|
||||
magisk::Version::TooOld => RootImpl::TooOld,
|
||||
}
|
||||
}
|
||||
(Some(ksu_version), None) => match ksu_version {
|
||||
kernelsu::Version::Supported => RootImpl::KernelSU,
|
||||
kernelsu::Version::TooOld => RootImpl::TooOld,
|
||||
kernelsu::Version::Abnormal => RootImpl::Abnormal,
|
||||
},
|
||||
(None, Some(magisk_version)) => match magisk_version {
|
||||
magisk::Version::Supported => RootImpl::Magisk,
|
||||
magisk::Version::TooOld => RootImpl::TooOld,
|
||||
},
|
||||
};
|
||||
unsafe { ROOT_IMPL = impl_; }
|
||||
unsafe {
|
||||
ROOT_IMPL = impl_;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_impl() -> &'static RootImpl {
|
||||
|
||||
@@ -1,35 +1,49 @@
|
||||
use anyhow::Result;
|
||||
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream};
|
||||
use std::ffi::{c_char, c_void, CStr, CString};
|
||||
use std::os::fd::{AsFd, AsRawFd};
|
||||
use std::os::unix::net::{UnixDatagram, UnixListener};
|
||||
use std::process::Command;
|
||||
use std::sync::OnceLock;
|
||||
use bitflags::Flags;
|
||||
use rustix::net::{AddressFamily, bind_unix, connect_unix, listen, SendFlags, sendto_unix, socket, SocketAddrUnix, SocketType};
|
||||
use rustix::net::{
|
||||
bind_unix, connect_unix, listen, sendto_unix, socket, AddressFamily, SendFlags, SocketAddrUnix,
|
||||
SocketType,
|
||||
};
|
||||
use rustix::path::Arg;
|
||||
use rustix::thread::gettid;
|
||||
use std::ffi::{c_char, c_void, CStr, CString};
|
||||
use std::os::fd::{AsFd, AsRawFd};
|
||||
use std::os::unix::net::{UnixListener};
|
||||
use std::process::Command;
|
||||
use std::sync::OnceLock;
|
||||
use std::{
|
||||
fs,
|
||||
io::{Read, Write},
|
||||
os::unix::net::UnixStream,
|
||||
};
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[macro_export]
|
||||
macro_rules! lp_select {
|
||||
($lp32:expr, $lp64:expr) => { $lp64 };
|
||||
($lp32:expr, $lp64:expr) => {
|
||||
$lp64
|
||||
};
|
||||
}
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[macro_export]
|
||||
macro_rules! lp_select {
|
||||
($lp32:expr, $lp64:expr) => { $lp32 };
|
||||
($lp32:expr, $lp64:expr) => {
|
||||
$lp32
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[macro_export]
|
||||
macro_rules! debug_select {
|
||||
($debug:expr, $release:expr) => { $debug };
|
||||
($debug:expr, $release:expr) => {
|
||||
$debug
|
||||
};
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[macro_export]
|
||||
macro_rules! debug_select {
|
||||
($debug:expr, $release:expr) => { $release };
|
||||
($debug:expr, $release:expr) => {
|
||||
$release
|
||||
};
|
||||
}
|
||||
|
||||
pub struct LateInit<T> {
|
||||
@@ -38,12 +52,18 @@ pub struct LateInit<T> {
|
||||
|
||||
impl<T> LateInit<T> {
|
||||
pub const fn new() -> Self {
|
||||
LateInit { cell: OnceLock::new() }
|
||||
LateInit {
|
||||
cell: OnceLock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&self, value: T) {
|
||||
assert!(self.cell.set(value).is_ok())
|
||||
}
|
||||
|
||||
pub fn initiated(&self) -> bool {
|
||||
self.cell.get().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for LateInit<T> {
|
||||
@@ -58,7 +78,10 @@ pub fn set_socket_create_context(context: &str) -> Result<()> {
|
||||
match fs::write(path, context) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => {
|
||||
let path = format!("/proc/self/task/{}/attr/sockcreate", gettid().as_raw_nonzero());
|
||||
let path = format!(
|
||||
"/proc/self/task/{}/attr/sockcreate",
|
||||
gettid().as_raw_nonzero()
|
||||
);
|
||||
fs::write(path, context)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -94,6 +117,7 @@ pub fn get_property(name: &str) -> Result<String> {
|
||||
Ok(prop.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_property(name: &str, value: &str) -> Result<()> {
|
||||
let name = CString::new(name)?;
|
||||
let value = CString::new(value)?;
|
||||
@@ -103,11 +127,10 @@ pub fn set_property(name: &str, value: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn wait_property(name: &str, serial: u32) -> Result<u32> {
|
||||
let name = CString::new(name)?;
|
||||
let info = unsafe {
|
||||
__system_property_find(name.as_ptr())
|
||||
};
|
||||
let info = unsafe { __system_property_find(name.as_ptr()) };
|
||||
let mut serial = serial;
|
||||
unsafe {
|
||||
__system_property_wait(info, serial, &mut serial, std::ptr::null());
|
||||
@@ -115,14 +138,11 @@ pub fn wait_property(name: &str, serial: u32) -> Result<u32> {
|
||||
Ok(serial)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_property_serial(name: &str) -> Result<u32> {
|
||||
let name = CString::new(name)?;
|
||||
let info = unsafe {
|
||||
__system_property_find(name.as_ptr())
|
||||
};
|
||||
Ok(unsafe {
|
||||
__system_property_serial(info)
|
||||
})
|
||||
let info = unsafe { __system_property_find(name.as_ptr()) };
|
||||
Ok(unsafe { __system_property_serial(info) })
|
||||
}
|
||||
|
||||
pub fn switch_mount_namespace(pid: i32) -> Result<()> {
|
||||
@@ -234,6 +254,11 @@ extern "C" {
|
||||
fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32;
|
||||
fn __system_property_set(name: *const c_char, value: *const c_char) -> u32;
|
||||
fn __system_property_find(name: *const c_char) -> *const c_void;
|
||||
fn __system_property_wait(info: *const c_void, old_serial: u32, new_serial: *mut u32, timeout: *const libc::timespec) -> bool;
|
||||
fn __system_property_wait(
|
||||
info: *const c_void,
|
||||
old_serial: u32,
|
||||
new_serial: *mut u32,
|
||||
timeout: *const libc::timespec,
|
||||
) -> bool;
|
||||
fn __system_property_serial(info: *const c_void) -> u32;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
use crate::constants::{DaemonSocketAction, ProcessFlags};
|
||||
use crate::utils::{check_unix_socket, UnixStreamExt};
|
||||
use crate::{constants, dl, lp_select, root_impl, utils};
|
||||
use crate::utils::{check_unix_socket, LateInit, UnixStreamExt};
|
||||
use crate::{constants, lp_select, root_impl, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use passfd::FdPassingExt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use rustix::fs::{fcntl_setfd, FdFlags};
|
||||
use std::fs;
|
||||
use std::io::Error;
|
||||
use std::os::fd::{OwnedFd, RawFd};
|
||||
use std::ops::Deref;
|
||||
use std::os::fd::{AsFd, OwnedFd, RawFd};
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::os::unix::{
|
||||
net::{UnixListener, UnixStream},
|
||||
prelude::AsRawFd,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, exit};
|
||||
use log::info;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use bitflags::Flags;
|
||||
|
||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||
use std::process::{exit, Command};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
struct Module {
|
||||
name: String,
|
||||
@@ -30,11 +29,23 @@ struct Context {
|
||||
modules: Vec<Module>,
|
||||
}
|
||||
|
||||
static TMP_PATH: LateInit<String> = LateInit::new();
|
||||
static CONTROLLER_SOCKET: LateInit<String> = LateInit::new();
|
||||
static PATH_CP_NAME: LateInit<String> = LateInit::new();
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
log::info!("Welcome to Zygisk Next ({}) !", constants::ZKSU_VERSION);
|
||||
info!("Welcome to Zygisk Next ({}) !", constants::ZKSU_VERSION);
|
||||
|
||||
TMP_PATH.init(std::env::var("TMP_PATH")?);
|
||||
CONTROLLER_SOCKET.init(format!("{}/init_monitor", TMP_PATH.deref()));
|
||||
PATH_CP_NAME.init(format!(
|
||||
"{}/{}",
|
||||
TMP_PATH.deref(),
|
||||
lp_select!("/cp32.sock", "/cp64.sock")
|
||||
));
|
||||
|
||||
let arch = get_arch()?;
|
||||
log::debug!("Daemon architecture: {arch}");
|
||||
debug!("Daemon architecture: {arch}");
|
||||
let modules = load_modules(arch)?;
|
||||
|
||||
{
|
||||
@@ -42,9 +53,13 @@ pub fn main() -> Result<()> {
|
||||
let info = match root_impl::get_impl() {
|
||||
root_impl::RootImpl::KernelSU | root_impl::RootImpl::Magisk => {
|
||||
msg.extend_from_slice(&constants::DAEMON_SET_INFO.to_le_bytes());
|
||||
let module_names: Vec<_> = modules.iter()
|
||||
.map(|m| m.name.as_str()).collect();
|
||||
format!("Root: {:?},module({}): {}", root_impl::get_impl(), modules.len(), module_names.join(","))
|
||||
let module_names: Vec<_> = modules.iter().map(|m| m.name.as_str()).collect();
|
||||
format!(
|
||||
"Root: {:?},module({}): {}",
|
||||
root_impl::get_impl(),
|
||||
modules.len(),
|
||||
module_names.join(",")
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
msg.extend_from_slice(&constants::DAEMON_SET_ERROR_INFO.to_le_bytes());
|
||||
@@ -54,12 +69,11 @@ pub fn main() -> Result<()> {
|
||||
msg.extend_from_slice(&(info.len() as u32 + 1).to_le_bytes());
|
||||
msg.extend_from_slice(info.as_bytes());
|
||||
msg.extend_from_slice(&[0u8]);
|
||||
utils::unix_datagram_sendto(constants::CONTROLLER_SOCKET, msg.as_slice()).expect("failed to send info");
|
||||
utils::unix_datagram_sendto(&CONTROLLER_SOCKET, msg.as_slice())
|
||||
.expect("failed to send info");
|
||||
}
|
||||
|
||||
let context = Context {
|
||||
modules,
|
||||
};
|
||||
let context = Context { modules };
|
||||
let context = Arc::new(context);
|
||||
let listener = create_daemon_socket()?;
|
||||
for stream in listener.incoming() {
|
||||
@@ -67,11 +81,11 @@ pub fn main() -> Result<()> {
|
||||
let context = Arc::clone(&context);
|
||||
let action = stream.read_u8()?;
|
||||
let action = DaemonSocketAction::try_from(action)?;
|
||||
log::trace!("New daemon action {:?}", action);
|
||||
trace!("New daemon action {:?}", action);
|
||||
match action {
|
||||
DaemonSocketAction::PingHeartbeat => {
|
||||
let value = constants::ZYGOTE_INJECTED;
|
||||
utils::unix_datagram_sendto(constants::CONTROLLER_SOCKET, &value.to_le_bytes())?;
|
||||
utils::unix_datagram_sendto(&CONTROLLER_SOCKET, &value.to_le_bytes())?;
|
||||
}
|
||||
DaemonSocketAction::ZygoteRestart => {
|
||||
info!("Zygote restarted, clean up companions");
|
||||
@@ -82,12 +96,12 @@ pub fn main() -> Result<()> {
|
||||
}
|
||||
DaemonSocketAction::SystemServerStarted => {
|
||||
let value = constants::SYSTEM_SERVER_STARTED;
|
||||
utils::unix_datagram_sendto(constants::CONTROLLER_SOCKET, &value.to_le_bytes())?;
|
||||
utils::unix_datagram_sendto(&CONTROLLER_SOCKET, &value.to_le_bytes())?;
|
||||
}
|
||||
_ => {
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = handle_daemon_action(action, stream, &context) {
|
||||
log::warn!("Error handling daemon action: {}\n{}", e, e.backtrace());
|
||||
warn!("Error handling daemon action: {}\n{}", e, e.backtrace());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -113,7 +127,7 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
||||
let dir = match fs::read_dir(constants::PATH_MODULES_DIR) {
|
||||
Ok(dir) => dir,
|
||||
Err(e) => {
|
||||
log::warn!("Failed reading modules directory: {}", e);
|
||||
warn!("Failed reading modules directory: {}", e);
|
||||
return Ok(modules);
|
||||
}
|
||||
};
|
||||
@@ -125,16 +139,20 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
||||
if !so_path.exists() || disabled.exists() {
|
||||
continue;
|
||||
}
|
||||
log::info!(" Loading module `{name}`...");
|
||||
info!(" Loading module `{name}`...");
|
||||
let lib_fd = match create_library_fd(&so_path) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
log::warn!(" Failed to create memfd for `{name}`: {e}");
|
||||
warn!(" Failed to create memfd for `{name}`: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let companion = Mutex::new(None);
|
||||
let module = Module { name, lib_fd, companion };
|
||||
let module = Module {
|
||||
name,
|
||||
lib_fd,
|
||||
companion,
|
||||
};
|
||||
modules.push(module);
|
||||
}
|
||||
|
||||
@@ -161,7 +179,7 @@ fn create_library_fd(so_path: &PathBuf) -> Result<OwnedFd> {
|
||||
|
||||
fn create_daemon_socket() -> Result<UnixListener> {
|
||||
utils::set_socket_create_context("u:r:zygote:s0")?;
|
||||
let listener = utils::unix_listener_from_path(constants::PATH_CP_NAME)?;
|
||||
let listener = utils::unix_listener_from_path(&PATH_CP_NAME)?;
|
||||
Ok(listener)
|
||||
}
|
||||
|
||||
@@ -187,13 +205,13 @@ fn spawn_companion(name: &str, lib_fd: RawFd) -> Result<Option<UnixStream>> {
|
||||
0 => Ok(None),
|
||||
1 => Ok(Some(daemon)),
|
||||
_ => bail!("Invalid companion response"),
|
||||
}
|
||||
};
|
||||
} else {
|
||||
bail!("exited with status {}", status);
|
||||
}
|
||||
} else {
|
||||
// Remove FD_CLOEXEC flag
|
||||
unsafe { libc::fcntl(companion.as_raw_fd() as libc::c_int, libc::F_SETFD, 0i32); };
|
||||
fcntl_setfd(companion.as_fd(), FdFlags::empty())?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,19 +223,21 @@ fn spawn_companion(name: &str, lib_fd: RawFd) -> Result<Option<UnixStream>> {
|
||||
exit(0)
|
||||
}
|
||||
|
||||
fn handle_daemon_action(action: DaemonSocketAction, mut stream: UnixStream, context: &Context) -> Result<()> {
|
||||
fn handle_daemon_action(
|
||||
action: DaemonSocketAction,
|
||||
mut stream: UnixStream,
|
||||
context: &Context,
|
||||
) -> Result<()> {
|
||||
match action {
|
||||
DaemonSocketAction::RequestLogcatFd => {
|
||||
loop {
|
||||
let level = match stream.read_u8() {
|
||||
Ok(level) => level,
|
||||
Err(_) => break,
|
||||
};
|
||||
let tag = stream.read_string()?;
|
||||
let message = stream.read_string()?;
|
||||
utils::log_raw(level as i32, &tag, &message)?;
|
||||
}
|
||||
}
|
||||
DaemonSocketAction::RequestLogcatFd => loop {
|
||||
let level = match stream.read_u8() {
|
||||
Ok(level) => level,
|
||||
Err(_) => break,
|
||||
};
|
||||
let tag = stream.read_string()?;
|
||||
let message = stream.read_string()?;
|
||||
utils::log_raw(level as i32, &tag, &message)?;
|
||||
},
|
||||
DaemonSocketAction::GetProcessFlags => {
|
||||
let uid = stream.read_u32()? as i32;
|
||||
let mut flags = ProcessFlags::empty();
|
||||
@@ -236,8 +256,16 @@ fn handle_daemon_action(action: DaemonSocketAction, mut stream: UnixStream, cont
|
||||
root_impl::RootImpl::Magisk => flags |= ProcessFlags::PROCESS_ROOT_IS_MAGISK,
|
||||
_ => panic!("wrong root impl: {:?}", root_impl::get_impl()),
|
||||
}
|
||||
log::trace!("Uid {} granted root: {}", uid, flags.contains(ProcessFlags::PROCESS_GRANTED_ROOT));
|
||||
log::trace!("Uid {} on denylist: {}", uid, flags.contains(ProcessFlags::PROCESS_ON_DENYLIST));
|
||||
trace!(
|
||||
"Uid {} granted root: {}",
|
||||
uid,
|
||||
flags.contains(ProcessFlags::PROCESS_GRANTED_ROOT)
|
||||
);
|
||||
trace!(
|
||||
"Uid {} on denylist: {}",
|
||||
uid,
|
||||
flags.contains(ProcessFlags::PROCESS_ON_DENYLIST)
|
||||
);
|
||||
stream.write_u32(flags.bits())?;
|
||||
}
|
||||
DaemonSocketAction::ReadModules => {
|
||||
@@ -253,7 +281,7 @@ fn handle_daemon_action(action: DaemonSocketAction, mut stream: UnixStream, cont
|
||||
let mut companion = module.companion.lock().unwrap();
|
||||
if let Some(Some(sock)) = companion.as_ref() {
|
||||
if !check_unix_socket(sock, false) {
|
||||
log::error!("Poll companion for module `{}` crashed", module.name);
|
||||
error!("Poll companion for module `{}` crashed", module.name);
|
||||
companion.take();
|
||||
}
|
||||
}
|
||||
@@ -261,21 +289,27 @@ fn handle_daemon_action(action: DaemonSocketAction, mut stream: UnixStream, cont
|
||||
match spawn_companion(&module.name, module.lib_fd.as_raw_fd()) {
|
||||
Ok(c) => {
|
||||
if c.is_some() {
|
||||
log::trace!(" Spawned companion for `{}`", module.name);
|
||||
trace!(" Spawned companion for `{}`", module.name);
|
||||
} else {
|
||||
log::trace!(" No companion spawned for `{}` because it has not entry", module.name);
|
||||
trace!(
|
||||
" No companion spawned for `{}` because it has not entry",
|
||||
module.name
|
||||
);
|
||||
}
|
||||
*companion = Some(c);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!(" Failed to spawn companion for `{}`: {}", module.name, e);
|
||||
warn!(" Failed to spawn companion for `{}`: {}", module.name, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
match companion.as_ref() {
|
||||
Some(Some(sock)) => {
|
||||
if let Err(e) = sock.send_fd(stream.as_raw_fd()) {
|
||||
log::error!("Failed to send companion fd socket of module `{}`: {}", module.name, e);
|
||||
error!(
|
||||
"Failed to send companion fd socket of module `{}`: {}",
|
||||
module.name, e
|
||||
);
|
||||
stream.write_u8(0)?;
|
||||
}
|
||||
// Ok: Send by companion
|
||||
|
||||
Reference in New Issue
Block a user