You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
WIP
This commit is contained in:
@@ -93,7 +93,7 @@ namespace zygiskd {
|
||||
}
|
||||
|
||||
int GetModuleDir(size_t index) {
|
||||
int fd = Connect(1);
|
||||
UniqueFd fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
PLOGE("GetModuleDir");
|
||||
return -1;
|
||||
@@ -102,4 +102,13 @@ namespace zygiskd {
|
||||
socket_utils::write_usize(fd, index);
|
||||
return socket_utils::recv_fd(fd);
|
||||
}
|
||||
|
||||
void ZygoteRestart() {
|
||||
UniqueFd fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
PLOGE("Could not notify ZygoteRestart");
|
||||
return;
|
||||
}
|
||||
socket_utils::write_u8(fd, (uint8_t) SocketAction::ZygoteRestart);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace zygiskd {
|
||||
ReadModules,
|
||||
RequestCompanionSocket,
|
||||
GetModuleDir,
|
||||
ZygoteRestart,
|
||||
};
|
||||
|
||||
bool PingHeartbeat();
|
||||
@@ -71,4 +72,6 @@ namespace zygiskd {
|
||||
int ConnectCompanion(size_t index);
|
||||
|
||||
int GetModuleDir(size_t index);
|
||||
|
||||
void ZygoteRestart();
|
||||
}
|
||||
|
||||
@@ -2,21 +2,61 @@
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#include "main.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "daemon.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc >= 2 && argv[1] == "prop_monitor"sv) {
|
||||
if (access("/system/lib" LP_SELECT("", "64"), R_OK) == 0) prop_monitor_main();
|
||||
} else if (argc >= 3 && argv[1] == "trace-zygote"sv) {
|
||||
auto pid = strtol(argv[2], nullptr, 0);
|
||||
trace_zygote_main(pid);
|
||||
} else {
|
||||
if (argc >= 2) LOGE("unknown command %s", argv[1]);
|
||||
else LOGE("no command specified");
|
||||
if (access("/system/lib" LP_SELECT("", "64"), R_OK) != 0) return 1;
|
||||
auto lock_fd = open("/dev/zygisk/lock" LP_SELECT("32", "64"), O_CLOEXEC | O_CREAT, O_RDONLY);
|
||||
if (lock_fd == -1) {
|
||||
PLOGE("create lock file");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
struct flock lock{
|
||||
.l_type = F_RDLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = 0,
|
||||
.l_len = 0
|
||||
};
|
||||
if (fcntl(lock_fd, F_SETLK, &lock) == -1) {
|
||||
PLOGE("set file lock");
|
||||
close(lock_fd);
|
||||
return 1;
|
||||
}
|
||||
LOGI("zygote monitor started");
|
||||
struct timespec last_launch_time { .tv_sec = 0, .tv_nsec = 0 }, ts;
|
||||
int launch_count = 0;
|
||||
bool first = true;
|
||||
while (true) {
|
||||
auto pid = wait_for_zygote();
|
||||
if (pid == -1) break;
|
||||
LOGI("inject zygote %d", pid);
|
||||
if (first) first = false;
|
||||
else {
|
||||
LOGI("notify zygisk companion restart");
|
||||
zygiskd::ZygoteRestart();
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
auto delta = ts.tv_sec - last_launch_time.tv_sec;
|
||||
if (delta > 30) launch_count++;
|
||||
else launch_count = 0;
|
||||
if (launch_count >= 5) {
|
||||
LOGE("zygote crash too much times, stop");
|
||||
break;
|
||||
}
|
||||
memcpy(&last_launch_time, &ts, sizeof(struct timespec));
|
||||
if (!trace_zygote(pid)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
__system_property_set("ctl.sigstop_off", "zygote");
|
||||
__system_property_set("ctl.sigstop_off", "zygote_secondary");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void prop_monitor_main();
|
||||
void trace_zygote_main(int pid);
|
||||
int wait_for_zygote();
|
||||
bool trace_zygote(int pid);
|
||||
int find_zygote();
|
||||
|
||||
@@ -1,37 +1,86 @@
|
||||
#include <sys/system_properties.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "main.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "files.hpp"
|
||||
#include "misc.hpp"
|
||||
|
||||
void prop_monitor_main() {
|
||||
LOGI("prop monitor started");
|
||||
// if service is not running, pid = ""
|
||||
auto name = "init.svc_debug_pid." LP_SELECT("zygote_secondary", "zygote"); // argv[1];
|
||||
LOGI("start monitoring %s", name);
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
int find_zygote() {
|
||||
LOGD("find zygote");
|
||||
auto sockets = ScanUnixSockets();
|
||||
auto dir = xopen_dir("/proc");
|
||||
for (dirent *entry; (entry = readdir(dir.get()));) {
|
||||
auto pid = parse_int(entry->d_name);
|
||||
char comm[18];
|
||||
char state;
|
||||
if (pid == -1 || pid == 1) continue;
|
||||
auto stat_file = xopen_file((std::string("/proc/") + std::to_string(pid) + "/stat").c_str(), "r");
|
||||
if (stat_file == nullptr) continue;
|
||||
if (fscanf(stat_file.get(), "%*d %17s %c", comm, &state) != 2
|
||||
|| comm != "(init)"sv
|
||||
|| state != 'T') {
|
||||
continue;
|
||||
}
|
||||
LOGD("%d is stopped init", pid);
|
||||
auto fd_dir = xopen_dir((std::string("/proc/") + std::to_string(pid) + "/fd").c_str());
|
||||
if (fd_dir == nullptr) continue;
|
||||
for (dirent *fd_entry; (fd_entry = readdir(fd_dir.get()));) {
|
||||
if (fd_entry->d_name == "."sv || fd_entry->d_name == ".."sv) continue;
|
||||
struct stat st{};
|
||||
if (stat((std::string("/proc/") + std::to_string(pid) + "/fd/" + fd_entry->d_name).c_str(), &st) == -1) {
|
||||
PLOGE("stat /proc/%d/fd/%s", pid, fd_entry->d_name);
|
||||
continue;
|
||||
}
|
||||
if ((st.st_mode & S_IFSOCK) == 0) continue;
|
||||
auto it = sockets.find(st.st_ino);
|
||||
if (it != sockets.end() && it->second == LP_SELECT("/dev/socket/zygote_secondary", "/dev/socket/zygote")) {
|
||||
LOGD("%d is zygote", pid);
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int wait_for_zygote() {
|
||||
auto name = "init.svc." LP_SELECT("zygote_secondary", "zygote");
|
||||
auto prop = __system_property_find(name);
|
||||
if (prop == nullptr) {
|
||||
__system_property_set(name, "");
|
||||
__system_property_set(name, "stopped");
|
||||
prop = __system_property_find(name);
|
||||
if (prop == nullptr) {
|
||||
LOGE("failed to create prop");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
std::string last_state = "running";
|
||||
char val[PROP_VALUE_MAX];
|
||||
uint32_t new_serial = 0;
|
||||
while (true) {
|
||||
__system_property_wait(prop, new_serial, &new_serial, nullptr);
|
||||
__system_property_get(name, val);
|
||||
LOGD("%s(%u): %s\n", name, new_serial, val);
|
||||
auto pid = strtol(val, nullptr, 0);
|
||||
if (pid != 0) {
|
||||
LOGD("start ptrace %ld", pid);
|
||||
if (fork_dont_care() == 0) {
|
||||
execl("/proc/self/exe", "zygisk-ptracer", "trace-zygote", val, nullptr);
|
||||
PLOGE("failed to exec");
|
||||
if (val != last_state && val == "running"sv) {
|
||||
LOGI("zygote is running, find zygote");
|
||||
int pid = -1;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
pid = find_zygote();
|
||||
if (pid != -1) break;
|
||||
else {
|
||||
LOGW("could not find zygote, wait 1s");
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
if (pid == -1) {
|
||||
LOGE("failed to find zygote");
|
||||
exit(1);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
last_state = val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,49 +152,65 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
|
||||
#define STOPPED_WITH(sig, event) (WIFSTOPPED(status) && WSTOPSIG(status) == (sig) && (status >> 16) == (event))
|
||||
|
||||
void trace_zygote_main(int pid) {
|
||||
bool trace_zygote(int pid) {
|
||||
#define WAIT_OR_DIE if (wait_pid(pid, &status, __WALL) != pid) return false;
|
||||
#define CONT_OR_DIE \
|
||||
if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) { \
|
||||
PLOGE("cont"); \
|
||||
return false; \
|
||||
}
|
||||
int status;
|
||||
LOGI("tracing %d (tracer %d)", pid, getpid());
|
||||
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXEC) == -1) {
|
||||
PLOGE("seize");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
wait_pid(pid, &status, __WALL);
|
||||
struct finally {
|
||||
int pid;
|
||||
~finally() {
|
||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
||||
}
|
||||
} _{pid};
|
||||
WAIT_OR_DIE
|
||||
if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) {
|
||||
// if SIGSTOP is delivered before we seized it
|
||||
LOGD("process is already stopped");
|
||||
kill(pid, SIGCONT);
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
wait_pid(pid, &status, __WALL);
|
||||
if (kill(pid, SIGCONT)) {
|
||||
PLOGE("kill");
|
||||
return false;
|
||||
}
|
||||
CONT_OR_DIE
|
||||
WAIT_OR_DIE
|
||||
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP)) {
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
wait_pid(pid, &status, __WALL);
|
||||
CONT_OR_DIE
|
||||
WAIT_OR_DIE
|
||||
if (STOPPED_WITH(SIGCONT, 0)) {
|
||||
LOGD("received SIGCONT");
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
}
|
||||
} else {
|
||||
LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", parse_status(status).c_str());
|
||||
return false;
|
||||
}
|
||||
} else if (STOPPED_WITH(SIGSTOP, 0)) {
|
||||
// if SIGSTOP is delivered after we seized it
|
||||
LOGD("process received SIGSTOP, suppress");
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
CONT_OR_DIE
|
||||
} else {
|
||||
LOGE("unknown state %s, neither EVENT_STOP nor SIGSTOP", parse_status(status).c_str());
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
wait_pid(pid, &status, __WALL);
|
||||
WAIT_OR_DIE
|
||||
// enter the app_process
|
||||
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_EXEC)) {
|
||||
LOGD("app_process exec-ed");
|
||||
LOGI("app_process exec-ed");
|
||||
if (!inject_on_main(pid, "/dev/zygisk/lib" LP_SELECT("", "64") "/libzygisk.so")) {
|
||||
LOGE("failed to inject");
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOGE("unknown status %d", status);
|
||||
exit(1);
|
||||
LOGE("unknown status %s", parse_status(status).c_str());
|
||||
return false;
|
||||
}
|
||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,37 @@ std::vector<MapInfo> MapInfo::Scan(const std::string& pid) {
|
||||
return info;
|
||||
}
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/main/+/main:external/toybox/toys/net/netstat.c;l=200;drc=657f94698c7fc7d4f9838cbcf3b4b78e38939d5c
|
||||
std::map<ino_t, std::string> ScanUnixSockets() {
|
||||
constexpr static auto kSocketEntry = 1;
|
||||
LOGD("scanning unix sockets");
|
||||
std::map<ino_t, std::string> info;
|
||||
auto sockets = std::unique_ptr<FILE, decltype(&fclose)>{fopen("/proc/net/unix", "r"), &fclose};
|
||||
char *line = nullptr;
|
||||
size_t len = 0;
|
||||
// skip header
|
||||
getline(&line, &len, sockets.get());
|
||||
if (sockets) {
|
||||
ssize_t read;
|
||||
while ((read = getline(&line, &len, sockets.get())) > 0) {
|
||||
line[read - 1] = '\0';
|
||||
ino_t ino;
|
||||
char *path = nullptr;
|
||||
// Num RefCount Protocol Flags Type St Inode Path
|
||||
if (sscanf(line, "%*p: %*lx %*lx %*lx %*lx %*lx %lu%m[^\n]", &ino, &path) < kSocketEntry) {
|
||||
continue;
|
||||
}
|
||||
if (path != nullptr) {
|
||||
LOGD("%ld -> %s", ino, path + 1);
|
||||
info.emplace(ino, path + 1);
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
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);
|
||||
struct iovec local{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <sys/ptrace.h>
|
||||
#include <map>
|
||||
|
||||
#include "daemon.h"
|
||||
|
||||
@@ -37,7 +38,7 @@ struct MapInfo {
|
||||
/// \brief Scans /proc/self/maps and returns a list of \ref MapInfo entries.
|
||||
/// This is useful to find out the inode of the library to hook.
|
||||
/// \return A list of \ref MapInfo entries.
|
||||
[[maybe_unused, gnu::visibility("default")]] static std::vector<MapInfo> Scan(const std::string& pid = "self");
|
||||
static std::vector<MapInfo> Scan(const std::string& pid = "self");
|
||||
};
|
||||
|
||||
#if defined(__x86_64__)
|
||||
@@ -112,3 +113,6 @@ inline const char* sigabbrev_np(int sig) {
|
||||
if (sig > 0 && sig < NSIG) return sys_signame[sig];
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
std::map<ino_t, std::string> ScanUnixSockets();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user