improve: CLI and code (#3)

This commit improves the CLI of ReZygisk, allowing the use of important information like PID of the daemons. Also improves the code of the loaders ptracer.
This commit is contained in:
Pedro.js
2024-06-20 22:53:13 -03:00
committed by GitHub
parent 39788a9ec2
commit 678d886343
13 changed files with 1132 additions and 748 deletions

View File

@@ -18,10 +18,11 @@ namespace zygiskd {
int Connect(uint8_t retry) {
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
struct sockaddr_un addr{
.sun_family = AF_UNIX,
.sun_path={0},
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
.sun_path = { 0 },
};
auto socket_path = TMP_PATH + kCPSocketName;
strcpy(addr.sun_path, socket_path.c_str());
socklen_t socklen = sizeof(addr);
@@ -67,6 +68,7 @@ namespace zygiskd {
}
socket_utils::write_u8(fd, (uint8_t) SocketAction::GetProcessFlags);
socket_utils::write_u32(fd, uid);
return socket_utils::read_u32(fd);
}
@@ -139,4 +141,29 @@ namespace zygiskd {
}
}
}
void GetInfo(struct zygote_info *info) {
/* TODO: Optimize and avoid re-connect twice here */
int fd = Connect(1);
if (fd != -1) {
info->running = true;
socket_utils::write_u8(fd, (uint8_t) SocketAction::GetInfo);
int flags = socket_utils::read_u32(fd);
if (flags & (1 << 29)) {
info->root_impl = ZYGOTE_ROOT_IMPL_KERNELSU;
} else if (flags & (1 << 30)) {
info->root_impl = ZYGOTE_ROOT_IMPL_MAGISK;
} else {
info->root_impl = ZYGOTE_ROOT_IMPL_NONE;
}
info->pid = socket_utils::read_u32(fd);
close(fd);
} else info->running = false;
}
}

View File

@@ -42,6 +42,18 @@ private:
Fd fd_ = -1;
};
enum zygote_root_impl {
ZYGOTE_ROOT_IMPL_NONE,
ZYGOTE_ROOT_IMPL_KERNELSU,
ZYGOTE_ROOT_IMPL_MAGISK
};
struct zygote_info {
enum zygote_root_impl root_impl;
pid_t pid;
bool running;
};
namespace zygiskd {
struct Module {
@@ -55,6 +67,7 @@ namespace zygiskd {
PingHeartBeat,
RequestLogcatFd,
GetProcessFlags,
GetInfo,
ReadModules,
RequestCompanionSocket,
GetModuleDir,
@@ -81,4 +94,6 @@ namespace zygiskd {
void ZygoteRestart();
void SystemServerStarted();
void GetInfo(struct zygote_info *info);
}

View File

@@ -1,12 +1,60 @@
#include "main.h"
#include <stdio.h>
#include "monitor.h"
#include "utils.hpp"
#include "daemon.h"
#define CUSTOM_TMP_PATH 0
#define SBIN_AS_TMP_PATH 1
#define DEBUG_RAMDISK_AS_TMP_PATH 2
int main(int argc, char **argv) {
zygiskd::Init(getenv("TMP_PATH"));
int tmp_path_type = CUSTOM_TMP_PATH;
if (getenv("TMP_PATH") == NULL) {
tmp_path_type = SBIN_AS_TMP_PATH;
FILE *fp = fopen("/sbin", "r");
if (fp == NULL) {
tmp_path_type = DEBUG_RAMDISK_AS_TMP_PATH;
fp = fopen("/debug_ramdisk", "r");
if (fp == NULL) {
printf("Cannot find TMP_PATH. You should make an issue about that.\n");
return 1;
} else fclose(fp);
} else fclose(fp);
} else {
tmp_path_type = CUSTOM_TMP_PATH;
}
switch (tmp_path_type) {
case CUSTOM_TMP_PATH: {
zygiskd::Init(getenv("TMP_PATH"));
break;
}
case SBIN_AS_TMP_PATH: {
zygiskd::Init("/sbin");
break;
}
case DEBUG_RAMDISK_AS_TMP_PATH: {
zygiskd::Init("/debug_ramdisk");
break;
}
}
printf("The ReZygisk Tracer %s\n\n", ZKSU_VERSION);
if (argc >= 2 && strcmp(argv[1], "monitor") == 0) {
init_monitor();
printf("[ReZygisk]: Started monitoring...\n");
return 0;
} else if (argc >= 3 && strcmp(argv[1], "trace") == 0) {
if (argc >= 4 && strcmp(argv[3], "--restart") == 0) zygiskd::ZygoteRestart();
@@ -18,35 +66,75 @@ int main(int argc, char **argv) {
return 1;
}
printf("[ReZygisk]: Tracing %ld...\n", pid);
return 0;
} else if (argc >= 2 && strcmp(argv[1], "ctl") == 0) {
if (argc == 3) {
if (strcmp(argv[2], "start") == 0) {
send_control_command(START);
enum Command command;
return 0;
} else if (strcmp(argv[2], "stop") == 0) {
send_control_command(STOP);
if (strcmp(argv[2], "start") == 0) command = START;
else if (strcmp(argv[2], "stop") == 0) command = STOP;
else if (strcmp(argv[2], "exit") == 0) command = EXIT;
else {
printf("[ReZygisk]: Usage: %s ctl <start|stop|exit>\n", argv[0]);
return 0;
} else if (strcmp(argv[2], "exit") == 0) {
send_control_command(EXIT);
return 0;
}
return 1;
}
printf("ReZygisk Tracer %s\n", ZKSU_VERSION);
printf("Usage: %s ctl start|stop|exit\n", argv[0]);
if (send_control_command(command) == -1) {
printf("[ReZygisk]: Failed to send the command, is the daemon running?\n");
return 1;
return 1;
}
printf("[ReZygisk]: command sent\n");
return 0;
} else if (argc >= 2 && strcmp(argv[1], "version") == 0) {
printf("ReZygisk Tracer %s\n", ZKSU_VERSION);
return 0;
} else if (argc >= 2 && strcmp(argv[1], "info") == 0) {
printf("ReZygisk Tracer %s\n", ZKSU_VERSION);
struct zygote_info info;
zygiskd::GetInfo(&info);
printf("Daemon process PID: %d\n", info.pid);
switch (info.root_impl) {
case ZYGOTE_ROOT_IMPL_NONE: {
printf("Root implementation: none\n");
break;
}
case ZYGOTE_ROOT_IMPL_KERNELSU: {
printf("Root implementation: KernelSU\n");
break;
}
case ZYGOTE_ROOT_IMPL_MAGISK: {
printf("Root implementation: Magisk\n");
break;
}
}
printf("Is the daemon running: %s\n", info.running ? "yes" : "no");
return 0;
} else {
printf("ReZygisk Tracer %s\n", ZKSU_VERSION);
printf("usage: %s monitor | trace <pid> | ctl <start|stop|exit> | version\n", argv[0]);
printf(
"Available commands:\n"
" - monitor\n"
" - trace <pid> [--restart]\n"
" - ctl <start|stop|exit>\n"
" - version: Shows the version of ReZygisk.\n"
" - info: Shows information about the created daemon/injection.\n"
"\n"
"<...>: Obligatory\n"
"[...]: Optional\n");
return 1;
}

View File

@@ -13,7 +13,7 @@
#include <sys/mount.h>
#include <fcntl.h>
#include "main.h"
#include "monitor.h"
#include "utils.hpp"
#include "files.hpp"
#include "misc.hpp"
@@ -41,81 +41,103 @@ struct EventHandler {
};
struct EventLoop {
private:
private:
int epoll_fd_;
bool running = false;
public:
public:
bool Init() {
epoll_fd_ = epoll_create(1);
if (epoll_fd_ == -1) {
PLOGE("failed to create");
return false;
}
return true;
epoll_fd_ = epoll_create(1);
if (epoll_fd_ == -1) {
PLOGE("failed to create");
return false;
}
return true;
}
void Stop() {
running = false;
running = false;
}
void Loop() {
running = true;
constexpr auto MAX_EVENTS = 2;
struct epoll_event events[MAX_EVENTS];
while (running) {
int nfds = epoll_wait(epoll_fd_, events, MAX_EVENTS, -1);
if (nfds == -1) {
if (errno != EINTR)
PLOGE("epoll_wait");
continue;
}
for (int i = 0; i < nfds; i++) {
reinterpret_cast<EventHandler *>(events[i].data.ptr)->HandleEvent(*this,
events[i].events);
if (!running) break;
}
running = true;
constexpr auto MAX_EVENTS = 2;
struct epoll_event events[MAX_EVENTS];
while (running) {
int nfds = epoll_wait(epoll_fd_, events, MAX_EVENTS, -1);
if (nfds == -1) {
if (errno != EINTR) PLOGE("epoll_wait");
continue;
}
for (int i = 0; i < nfds; i++) {
reinterpret_cast<EventHandler *>(events[i].data.ptr)->HandleEvent(*this,
events[i].events);
if (!running) break;
}
}
}
bool RegisterHandler(EventHandler &handler, uint32_t events) {
struct epoll_event ev{};
ev.events = events;
ev.data.ptr = &handler;
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, handler.GetFd(), &ev) == -1) {
PLOGE("failed to add event handler");
return false;
}
return true;
struct epoll_event ev{};
ev.events = events;
ev.data.ptr = &handler;
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, handler.GetFd(), &ev) == -1) {
PLOGE("failed to add event handler");
return false;
}
return true;
}
[[maybe_unused]] bool UnregisterHandler(EventHandler &handler) {
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), NULL) == -1) {
PLOGE("failed to del event handler");
return false;
}
return true;
bool UnregisterHandler(EventHandler &handler) {
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), NULL) == -1) {
PLOGE("failed to del event handler");
return false;
}
return true;
}
~EventLoop() {
if (epoll_fd_ >= 0) close(epoll_fd_);
if (epoll_fd_ >= 0) close(epoll_fd_);
}
};
static TracingState tracing_state = TRACING;
static char prop_path[PATH_MAX];
struct Status {
bool supported = false;
bool zygote_injected = false;
bool daemon_running = false;
pid_t daemon_pid = -1;
char *daemon_info;
char *daemon_error_info;
bool supported = false;
bool zygote_injected = false;
bool daemon_running = false;
pid_t daemon_pid = -1;
char *daemon_info;
char *daemon_error_info;
};
static Status status64;
static Status status32;
static Status status64 = {
.supported = false,
.zygote_injected = false,
.daemon_running = false,
.daemon_pid = -1,
.daemon_info = NULL,
.daemon_error_info = NULL
};
static Status status32 = {
.supported = false,
.zygote_injected = false,
.daemon_running = false,
.daemon_pid = -1,
.daemon_info = NULL,
.daemon_error_info = NULL
};
struct SocketHandler : public EventHandler {
int sock_fd_;
@@ -157,155 +179,157 @@ struct SocketHandler : public EventHandler {
};
while (1) {
std::vector<uint8_t> buf;
buf.resize(sizeof(MsgHead), 0);
std::vector<uint8_t> buf;
buf.resize(sizeof(MsgHead), 0);
MsgHead &msg = *reinterpret_cast<MsgHead*>(buf.data());
MsgHead &msg = *reinterpret_cast<MsgHead*>(buf.data());
ssize_t real_size;
ssize_t nread = recv(sock_fd_, &msg, sizeof(msg), MSG_PEEK);
if (nread == -1) {
if (errno == EAGAIN) break;
ssize_t real_size;
ssize_t nread = recv(sock_fd_, &msg, sizeof(msg), MSG_PEEK);
if (nread == -1) {
if (errno == EAGAIN) break;
PLOGE("read socket");
}
PLOGE("read socket");
}
if (static_cast<size_t>(nread) < sizeof(Command)) {
LOGE("read %zu < %zu", nread, sizeof(Command));
continue;
}
if (static_cast<size_t>(nread) < sizeof(Command)) {
LOGE("read %zu < %zu", nread, sizeof(Command));
continue;
}
if (msg.cmd >= Command::DAEMON64_SET_INFO && msg.cmd != Command::SYSTEM_SERVER_STARTED) {
if (nread != sizeof(msg)) {
LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(MsgHead));
continue;
}
real_size = sizeof(MsgHead) + msg.length;
} else {
if (nread != sizeof(Command)) {
LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(Command));
continue;
}
real_size = sizeof(Command);
}
buf.resize(real_size);
nread = recv(sock_fd_, &msg, real_size, 0);
if (nread == -1) {
if (errno == EAGAIN) break;
PLOGE("recv");
continue;
}
if (nread != real_size) {
LOGE("real size %zu != %zu", real_size, nread);
if (msg.cmd >= Command::DAEMON64_SET_INFO && msg.cmd != Command::SYSTEM_SERVER_STARTED) {
if (nread != sizeof(msg)) {
LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(MsgHead));
continue;
}
switch (msg.cmd) {
case START: {
if (tracing_state == STOPPING) tracing_state = TRACING;
else if (tracing_state == STOPPED) {
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
real_size = sizeof(MsgHead) + msg.length;
} else {
if (nread != sizeof(Command)) {
LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(Command));
LOGI("start tracing init");
continue;
}
tracing_state = TRACING;
}
real_size = sizeof(Command);
}
updateStatus();
buf.resize(real_size);
nread = recv(sock_fd_, &msg, real_size, 0);
break;
if (nread == -1) {
if (errno == EAGAIN) break;
PLOGE("recv");
continue;
}
if (nread != real_size) {
LOGE("real size %zu != %zu", real_size, nread);
continue;
}
switch (msg.cmd) {
case START: {
if (tracing_state == STOPPING) tracing_state = TRACING;
else if (tracing_state == STOPPED) {
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
LOGI("start tracing init");
tracing_state = TRACING;
}
case STOP: {
if (tracing_state == TRACING) {
LOGI("stop tracing requested");
tracing_state = STOPPING;
memcpy(monitor_stop_reason, "user requested", sizeof("user requested"));
updateStatus();
ptrace(PTRACE_INTERRUPT, 1, 0, 0);
updateStatus();
}
break;
}
case STOP: {
if (tracing_state == TRACING) {
LOGI("stop tracing requested");
break;
}
case EXIT: {
LOGI("prepare for exit ...");
tracing_state = EXITING;
tracing_state = STOPPING;
memcpy(monitor_stop_reason, "user requested", sizeof("user requested"));
ptrace(PTRACE_INTERRUPT, 1, 0, 0);
updateStatus();
loop.Stop();
break;
}
case ZYGOTE64_INJECTED: {
status64.zygote_injected = true;
updateStatus();
break;
}
case ZYGOTE32_INJECTED: {
status32.zygote_injected = true;
updateStatus();
break;
}
case DAEMON64_SET_INFO: {
LOGD("received daemon64 info %s", msg.data);
status64.daemon_info = msg.data;
updateStatus();
break;
}
case DAEMON32_SET_INFO: {
LOGD("received daemon32 info %s", msg.data);
status32.daemon_info = msg.data;
updateStatus();
break;
}
case DAEMON64_SET_ERROR_INFO: {
LOGD("received daemon64 error info %s", msg.data);
status64.daemon_running = false;
status64.daemon_error_info = msg.data;
updateStatus();
break;
}
case DAEMON32_SET_ERROR_INFO: {
LOGD("received daemon32 error info %s", msg.data);
status32.daemon_running = false;
status32.daemon_error_info = msg.data;
updateStatus();
break;
}
case SYSTEM_SERVER_STARTED: {
LOGD("system server started, mounting prop");
if (mount(prop_path, "/data/adb/modules/zygisksu/module.prop", NULL, MS_BIND, NULL) == -1) {
PLOGE("failed to mount prop");
}
break;
}
break;
}
case EXIT: {
LOGI("prepare for exit ...");
tracing_state = EXITING;
memcpy(monitor_stop_reason, "user requested", sizeof("user requested"));
updateStatus();
loop.Stop();
break;
}
case ZYGOTE64_INJECTED: {
status64.zygote_injected = true;
updateStatus();
break;
}
case ZYGOTE32_INJECTED: {
status32.zygote_injected = true;
updateStatus();
break;
}
case DAEMON64_SET_INFO: {
LOGD("received daemon64 info %s", msg.data);
status64.daemon_info = msg.data;
updateStatus();
break;
}
case DAEMON32_SET_INFO: {
LOGD("received daemon32 info %s", msg.data);
status32.daemon_info = msg.data;
updateStatus();
break;
}
case DAEMON64_SET_ERROR_INFO: {
LOGD("received daemon64 error info %s", msg.data);
status64.daemon_running = false;
status64.daemon_error_info = msg.data;
updateStatus();
break;
}
case DAEMON32_SET_ERROR_INFO: {
LOGD("received daemon32 error info %s", msg.data);
status32.daemon_running = false;
status32.daemon_error_info = msg.data;
updateStatus();
break;
}
case SYSTEM_SERVER_STARTED: {
LOGD("system server started, mounting prop");
if (mount(prop_path, "/data/adb/modules/zygisksu/module.prop", NULL, MS_BIND, NULL) == -1) {
PLOGE("failed to mount prop");
}
umount2(prop_path, MNT_DETACH);
break;
}
}
}
}
@@ -316,20 +340,25 @@ struct SocketHandler : public EventHandler {
constexpr int MAX_RETRY_COUNT = 5;
#define CREATE_ZYGOTE_START_COUNTER(abi) \
struct timespec last_zygote##abi{.tv_sec = 0, .tv_nsec = 0}; \
int count_zygote##abi = 0; \
bool should_stop_inject##abi() { \
struct timespec now{}; \
clock_gettime(CLOCK_MONOTONIC, &now); \
if (now.tv_sec - last_zygote##abi.tv_sec < 30) { \
count_zygote##abi++; \
} else { \
count_zygote##abi = 0; \
} \
last_zygote##abi = now; \
return count_zygote##abi >= MAX_RETRY_COUNT; \
}
#define CREATE_ZYGOTE_START_COUNTER(abi) \
struct timespec last_zygote##abi = { \
.tv_sec = 0, \
.tv_nsec = 0 \
}; \
\
int count_zygote ## abi = 0; \
bool should_stop_inject ## abi() { \
struct timespec now = {}; \
clock_gettime(CLOCK_MONOTONIC, &now); \
if (now.tv_sec - last_zygote ## abi.tv_sec < 30) \
count_zygote ## abi++; \
else \
count_zygote ## abi = 0; \
\
last_zygote##abi = now; \
\
return count_zygote##abi >= MAX_RETRY_COUNT; \
}
CREATE_ZYGOTE_START_COUNTER(64)
CREATE_ZYGOTE_START_COUNTER(32)
@@ -360,6 +389,8 @@ static bool ensure_daemon_created(bool is_64bit) {
exit(1);
} else {
LOGI("daemon%s started with pid %d", is_64bit ? "64" : "32", pid);
status.supported = true;
status.daemon_pid = pid;
status.daemon_running = true;
@@ -367,23 +398,25 @@ static bool ensure_daemon_created(bool is_64bit) {
return true;
}
} else {
LOGI("daemon%s already started with pid %d", is_64bit ? "64" : "32", status.daemon_pid);
return status.daemon_running;
}
}
#define CHECK_DAEMON_EXIT(abi) \
if (status##abi.supported && pid == status64.daemon_pid) { \
char status_str[64]; \
parse_status(status, status_str, sizeof(status_str)); \
\
LOGW("daemon" #abi "pid %d exited: %s", pid, status_str); \
status##abi.daemon_running = false; \
\
if (status##abi.daemon_error_info[0] == '\0') \
memcpy(status##abi.daemon_error_info, status_str, strlen(status_str)); \
\
updateStatus(); \
continue; \
#define CHECK_DAEMON_EXIT(abi) \
if (status##abi.supported && pid == status64.daemon_pid) { \
char status_str[64]; \
parse_status(status, status_str, sizeof(status_str)); \
\
LOGW("daemon" #abi "pid %d exited: %s", pid, status_str); \
status##abi.daemon_running = false; \
\
if (status##abi.daemon_error_info[0] == '\0') \
memcpy(status##abi.daemon_error_info, status_str, strlen(status_str)); \
\
updateStatus(); \
continue; \
}
#define PRE_INJECT(abi, is_64) \
@@ -605,7 +638,9 @@ struct SigChldHandler : public EventHandler {
};
static char pre_section[1024];
static int pre_section_len = 0;
static char post_section[1024];
static int post_section_len = 0;
#define WRITE_STATUS_ABI(suffix) \
if (status ## suffix.supported) { \
@@ -616,7 +651,7 @@ static char post_section[1024];
else strcat(status_text, "❌ not injected,"); \
\
strcat(status_text, " daemon" #suffix ":"); \
if (status ## suffix .daemon_running) { \
if (status ## suffix.daemon_running) { \
strcat(status_text, "😋 running"); \
\
if (status ## suffix.daemon_info[0] != '\0') { \
@@ -637,7 +672,7 @@ static char post_section[1024];
static void updateStatus() {
FILE *prop = fopen(prop_path, "w");
char status_text[64] = "monitor: ";
char status_text[256] = "monitor: ";
switch (tracing_state) {
case TRACING: {
@@ -671,6 +706,8 @@ static void updateStatus() {
WRITE_STATUS_ABI(32)
fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section);
fclose(prop);
}
static bool prepare_environment() {
@@ -686,19 +723,65 @@ static bool prepare_environment() {
return false;
}
char line[1024];
ssize_t len = fread(line, 1, sizeof(line), orig_prop);
/* TODO: Check if this part translation is correct.
Old approach may be a callback approach that gets called
every new line
*/
if ((unsigned long)len > (sizeof("description") - 1) && strncmp(line, "description=", sizeof("description") - 1) == 0) {
strcat(post_section, "description=");
strcat(post_section, line + sizeof("description"));
} else {
strcat(pre_section, line);
const char field_name[] = "description=";
/* TODO: improve this code */
int i = 1;
while (1) {
int int_char = fgetc(orig_prop);
if (int_char == EOF) break;
pre_section[pre_section_len] = (char)int_char;
pre_section[pre_section_len + 1] = '\0';
pre_section_len++;
if ((char)int_char != field_name[0]) continue;
while (1) {
int int_char2 = fgetc(orig_prop);
if (int_char2 == EOF) break;
if ((char)int_char2 == field_name[i]) {
i++;
if (i == (int)(sizeof(field_name) - 1)) {
pre_section[pre_section_len] = (char)int_char2;
pre_section[pre_section_len + 1] = '\0';
pre_section_len++;
while (1) {
int int_char3 = fgetc(orig_prop);
if (int_char3 == EOF) break;
post_section[post_section_len] = (char)int_char3;
post_section[post_section_len + 1] = '\0';
post_section_len++;
i++;
}
break;
} else {
pre_section[pre_section_len] = (char)int_char2;
pre_section[pre_section_len + 1] = '\0';
pre_section_len++;
continue;
}
} else {
pre_section[pre_section_len] = (char)int_char2;
pre_section[pre_section_len + 1] = '\0';
pre_section_len++;
i = 1;
break;
}
}
}
fclose(orig_prop);
updateStatus();
return true;
@@ -724,9 +807,9 @@ void init_monitor() {
LOGI("exit");
}
void send_control_command(enum Command cmd) {
int send_control_command(enum Command cmd) {
int sockfd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sockfd == -1) err(EXIT_FAILURE, "socket");
if (sockfd == -1) return -1;
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
@@ -737,12 +820,9 @@ void send_control_command(enum Command cmd) {
socklen_t socklen = sizeof(sa_family_t) + sun_path_len;
ssize_t nsend = sendto(sockfd, (void *)&cmd, sizeof(cmd), 0, (sockaddr *)&addr, socklen);
if (nsend == -1) err(EXIT_FAILURE, "send");
else if (nsend != sizeof(cmd)) {
printf("[ReZygisk]: Failed to send data. Tried to send %zu bytes but only %zu were sent.\n", sizeof(cmd), nsend);
exit(1);
}
/* TODO: Should we close even when it fails? */
close(sockfd);
printf("[ReZygisk]: command sent\n");
return nsend != sizeof(cmd) ? -1 : 0;
}

View File

@@ -4,6 +4,7 @@
#include <stdbool.h>
void init_monitor();
bool trace_zygote(int pid);
enum Command {
@@ -21,6 +22,6 @@ enum Command {
SYSTEM_SERVER_STARTED = 10
};
void send_control_command(enum Command cmd);
int send_control_command(enum Command cmd);
#endif /* MAIN_HPP */

View File

@@ -19,209 +19,297 @@
#include "utils.hpp"
bool inject_on_main(int pid, const char *lib_path) {
LOGI("injecting %s to zygote %d", lib_path, pid);
// parsing KernelArgumentBlock
// https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/private/KernelArgumentBlock.h;l=30;drc=6d1ee77ee32220e4202c3066f7e1f69572967ad8
struct user_regs_struct regs{}, backup{};
auto map = MapInfo::Scan(std::to_string(pid));
if (!get_regs(pid, regs)) return false;
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);
LOGV("argv %p", argv);
read_proc(pid, arg, &argc, sizeof(argc));
LOGV("argc %d", argc);
auto envp = argv + argc + 1;
LOGV("envp %p", envp);
auto p = envp;
while (true) {
uintptr_t *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);
LOGV("auxv %p %s", auxv, get_addr_mem_region(map, (uintptr_t) auxv).c_str());
auto v = auxv;
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));
if (buf.a_type == AT_ENTRY) {
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 == 0) {
LOGE("failed to get entry");
return false;
LOGI("injecting %s to zygote %d", lib_path, pid);
/*
parsing KernelArgumentBlock
https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/private/KernelArgumentBlock.h;l=30;drc=6d1ee77ee32220e4202c3066f7e1f69572967ad8
*/
struct user_regs_struct regs {},
backup {};
/* WARNING: C++ keyword */
std::vector<MapInfo> map = MapInfo::Scan(std::to_string(pid));
if (!get_regs(pid, regs)) return false;
uintptr_t arg = (uintptr_t)regs.REG_SP;
LOGV("kernel argument %" PRIxPTR " %s", arg, get_addr_mem_region(map, arg).c_str());
int argc;
char **argv = (char **)((uintptr_t *)arg + 1);
LOGV("argv %p", (void *)argv);
read_proc(pid, arg, &argc, sizeof(argc));
LOGV("argc %d", argc);
/* WARNING: C++ keyword */
auto envp = argv + argc + 1;
LOGV("envp %p", (void *)envp);
/* WARNING: C++ keyword */
auto p = envp;
while (1) {
uintptr_t *buf;
read_proc(pid, (uintptr_t)p, &buf, sizeof(buf));
if (buf == NULL) break;
/* TODO: Why ++p? */
p++;
}
/* TODO: Why ++p? */
p++;
ElfW(auxv_t) *auxv = (ElfW(auxv_t) *)p;
LOGV("auxv %p %s", auxv, get_addr_mem_region(map, (uintptr_t) auxv).c_str());
ElfW(auxv_t) *v = auxv;
uintptr_t entry_addr = 0;
uintptr_t addr_of_entry_addr = 0;
while (1) {
ElfW(auxv_t) buf;
read_proc(pid, (uintptr_t)v, &buf, sizeof(buf));
if (buf.a_type == AT_ENTRY) {
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;
}
// 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;
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 (static_cast<uintptr_t>(regs.REG_IP & ~1) != (break_addr & ~1)) {
LOGE("stopped at unknown addr %p", (void *) regs.REG_IP);
return false;
}
// The linker has been initialized now, we can do dlopen
LOGD("stopped at entry");
if (buf.a_type == AT_NULL) break;
// restore entry address
if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &entry_addr, sizeof(entry_addr))) return false;
v++;
}
// backup registers
memcpy(&backup, &regs, sizeof(regs));
map = MapInfo::Scan(std::to_string(pid));
auto local_map = MapInfo::Scan();
auto libc_return_addr = find_module_return_addr(map, "libc.so");
LOGD("libc return addr %p", libc_return_addr);
if (entry_addr == 0) {
LOGE("failed to get entry");
// call dlopen
auto dlopen_addr = find_func_addr(local_map, map, "libdl.so", "dlopen");
if (dlopen_addr == nullptr) return false;
std::vector<long> args;
auto str = push_string(pid, regs, lib_path);
args.clear();
args.push_back((long) str);
args.push_back((long) RTLD_NOW);
auto remote_handle = remote_call(pid, regs, (uintptr_t) dlopen_addr, (uintptr_t) libc_return_addr, args);
LOGD("remote handle %p", (void *) remote_handle);
if (remote_handle == 0) {
LOGE("handle is null");
// call dlerror
auto dlerror_addr = find_func_addr(local_map, map, "libdl.so", "dlerror");
if (dlerror_addr == nullptr) {
LOGE("find dlerror");
return false;
}
args.clear();
auto dlerror_str_addr = remote_call(pid, regs, (uintptr_t) dlerror_addr, (uintptr_t) libc_return_addr, args);
LOGD("dlerror str %p", (void*) dlerror_str_addr);
if (dlerror_str_addr == 0) return false;
auto strlen_addr = find_func_addr(local_map, map, "libc.so", "strlen");
if (strlen_addr == nullptr) {
LOGE("find strlen");
return false;
}
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);
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);
LOGE("dlerror info %s", err.c_str());
return false;
}
// call dlsym(handle, "entry")
auto dlsym_addr = find_func_addr(local_map, map, "libdl.so", "dlsym");
if (dlsym_addr == nullptr) return false;
args.clear();
str = push_string(pid, regs, "entry");
args.push_back(remote_handle);
args.push_back((long) str);
auto injector_entry = remote_call(pid, regs, (uintptr_t) dlsym_addr, (uintptr_t) libc_return_addr, args);
LOGD("injector entry %p", (void*) injector_entry);
if (injector_entry == 0) {
LOGE("injector entry is null");
return false;
}
// 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
backup.REG_IP = (long) entry_addr;
LOGD("invoke entry");
// restore registers
if (!set_regs(pid, backup)) return false;
return true;
} else {
char status_str[64];
parse_status(status, status_str, sizeof(status_str));
LOGE("stopped by other reason: %s", status_str);
}
return false;
}
/*
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;
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 (static_cast<uintptr_t>(regs.REG_IP & ~1) != (break_addr & ~1)) {
LOGE("stopped at unknown addr %p", (void *) regs.REG_IP);
return false;
}
/* The linker has been initialized now, we can do dlopen */
LOGD("stopped at entry");
/* restore entry address */
if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &entry_addr, sizeof(entry_addr))) return false;
/* backup registers */
memcpy(&backup, &regs, sizeof(regs));
/* WARNING: C++ keyword */
map = MapInfo::Scan(std::to_string(pid));
/* WARNING: C++ keyword */
std::vector<MapInfo> local_map = MapInfo::Scan();
void *libc_return_addr = find_module_return_addr(map, "libc.so");
LOGD("libc return addr %p", libc_return_addr);
/* call dlopen */
void *dlopen_addr = find_func_addr(local_map, map, "libdl.so", "dlopen");
if (dlopen_addr == NULL) return false;
/* WARNING: C++ keyword */
std::vector<long> args;
/* WARNING: C++ keyword */
uintptr_t str = push_string(pid, regs, lib_path);
args.clear();
args.push_back((long) str);
args.push_back((long) RTLD_NOW);
uintptr_t remote_handle = remote_call(pid, regs, (uintptr_t)dlopen_addr, (uintptr_t)libc_return_addr, args);
LOGD("remote handle %p", (void *)remote_handle);
if (remote_handle == 0) {
LOGE("handle is null");
/* call dlerror */
void *dlerror_addr = find_func_addr(local_map, map, "libdl.so", "dlerror");
if (dlerror_addr == NULL) {
LOGE("find dlerror");
return false;
}
args.clear();
uintptr_t dlerror_str_addr = remote_call(pid, regs, (uintptr_t)dlerror_addr, (uintptr_t)libc_return_addr, args);
LOGD("dlerror str %p", (void*) dlerror_str_addr);
if (dlerror_str_addr == 0) return false;
void *strlen_addr = find_func_addr(local_map, map, "libc.so", "strlen");
if (strlen_addr == NULL) {
LOGE("find strlen");
return false;
}
args.clear();
args.push_back(dlerror_str_addr);
uintptr_t dlerror_len = remote_call(pid, regs, (uintptr_t)strlen_addr, (uintptr_t)libc_return_addr, args);
if (dlerror_len <= 0) {
LOGE("dlerror len <= 0");
return false;
}
/* NOTICE: C++ -> C */
char *err = (char *)malloc(dlerror_len + 1);
read_proc(pid, (uintptr_t) dlerror_str_addr, err, dlerror_len);
LOGE("dlerror info %s", err);
return false;
}
/* call dlsym(handle, "entry") */
void *dlsym_addr = find_func_addr(local_map, map, "libdl.so", "dlsym");
if (dlsym_addr == NULL) return false;
args.clear();
str = push_string(pid, regs, "entry");
args.push_back(remote_handle);
args.push_back((long) str);
uintptr_t injector_entry = remote_call(pid, regs, (uintptr_t)dlsym_addr, (uintptr_t)libc_return_addr, args);
LOGD("injector entry %p", (void *)injector_entry);
if (injector_entry == 0) {
LOGE("injector entry is null");
return false;
}
/* 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 */
backup.REG_IP = (long) entry_addr;
LOGD("invoke entry");
/* restore registers */
if (!set_regs(pid, backup)) return false;
return true;
} else {
char status_str[64];
parse_status(status, status_str, sizeof(status_str));
LOGE("stopped by other reason: %s", status_str);
}
return false;
}
#define STOPPED_WITH(sig, event) (WIFSTOPPED(status) && WSTOPSIG(status) == (sig) && (status >> 16) == (event))
#define WAIT_OR_DIE wait_for_trace(pid, &status, __WALL);
#define CONT_OR_DIE \
if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) { \
PLOGE("cont"); \
\
return false; \
}
bool trace_zygote(int pid) {
LOGI("start tracing %d", pid);
#define WAIT_OR_DIE wait_for_trace(pid, &status, __WALL);
#define CONT_OR_DIE \
if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) { \
PLOGE("cont"); \
return false; \
LOGI("start tracing %d (tracer %d)", pid, getpid());
int status;
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL) == -1) {
PLOGE("seize");
return false;
}
WAIT_OR_DIE
if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) {
/* WARNING: C++ keyword */
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");
return false;
}
int status;
LOGI("tracing %d (tracer %d)", pid, getpid());
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL) == -1) {
PLOGE("seize");
return false;
LOGD("inject done, continue process");
if (kill(pid, SIGCONT)) {
PLOGE("kill");
return false;
}
CONT_OR_DIE
WAIT_OR_DIE
if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) {
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");
return false;
}
LOGD("inject done, continue process");
if (kill(pid, SIGCONT)) {
PLOGE("kill");
return false;
}
CONT_OR_DIE
WAIT_OR_DIE
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP)) {
CONT_OR_DIE
WAIT_OR_DIE
if (STOPPED_WITH(SIGCONT, 0)) {
LOGD("received SIGCONT");
ptrace(PTRACE_DETACH, pid, 0, SIGCONT);
}
} else {
char status_str[64];
parse_status(status, status_str, sizeof(status_str));
LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", status_str);
ptrace(PTRACE_DETACH, pid, 0, 0);
return false;
}
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP)) {
CONT_OR_DIE
WAIT_OR_DIE
if (STOPPED_WITH(SIGCONT, 0)) {
LOGD("received SIGCONT");
ptrace(PTRACE_DETACH, pid, 0, SIGCONT);
}
} else {
char status_str[64];
parse_status(status, status_str, sizeof(status_str));
char status_str[64];
parse_status(status, status_str, sizeof(status_str));
LOGE("unknown state %s, not SIGSTOP + EVENT_STOP", status_str);
ptrace(PTRACE_DETACH, pid, 0, 0);
return false;
LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", status_str);
ptrace(PTRACE_DETACH, pid, 0, 0);
return false;
}
return true;
} else {
char status_str[64];
parse_status(status, status_str, sizeof(status_str));
LOGE("unknown state %s, not SIGSTOP + EVENT_STOP", status_str);
ptrace(PTRACE_DETACH, pid, 0, 0);
return false;
}
return true;
}

View File

@@ -1,3 +1,5 @@
#include <linux/limits.h>
#include <vector>
#include <sys/mman.h>
#include <sys/sysmacros.h>
@@ -22,400 +24,481 @@
#include <cstring>
#include <sys/stat.h>
#include "utils.hpp"
#include "logging.h"
#include <sched.h>
#include <fcntl.h>
#include "utils.hpp"
#include "logging.h"
bool switch_mnt_ns(int pid, int *fd) {
int nsfd, old_nsfd = -1;
std::string path;
if (pid == 0) {
if (fd != nullptr) {
nsfd = *fd;
*fd = -1;
} else return false;
path = "/proc/self/fd/";
path += std::to_string(nsfd);
} else {
if (fd != nullptr) {
old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
if (old_nsfd == -1) {
PLOGE("get old nsfd");
return false;
}
*fd = old_nsfd;
}
path = std::string("/proc/") + std::to_string(pid) + "/ns/mnt";
nsfd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (nsfd == -1) {
PLOGE("open nsfd %s", path.c_str());
close(old_nsfd);
return false;
}
}
if (setns(nsfd, CLONE_NEWNS) == -1) {
PLOGE("set ns to %s", path.c_str());
close(nsfd);
close(old_nsfd);
int nsfd, old_nsfd = -1;
/* WARNING: C++ keyword */
char path[PATH_MAX];
if (pid == 0) {
if (fd != NULL) {
nsfd = *fd;
*fd = -1;
} else return false;
snprintf(path, sizeof(path), "/proc/self/fd/%d", nsfd);
} else {
if (fd != NULL) {
old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
if (old_nsfd == -1) {
PLOGE("get old nsfd");
return false;
}
*fd = old_nsfd;
}
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
nsfd = open(path, O_RDONLY | O_CLOEXEC);
if (nsfd == -1) {
PLOGE("open nsfd %s", path);
close(old_nsfd);
return false;
}
}
if (setns(nsfd, CLONE_NEWNS) == -1) {
PLOGE("set ns to %s", path);
close(nsfd);
return true;
close(old_nsfd);
return false;
}
close(nsfd);
return true;
}
std::vector<MapInfo> MapInfo::Scan(const std::string& pid) {
constexpr static auto kPermLength = 5;
constexpr static auto kMapEntry = 7;
std::vector<MapInfo> info;
std::string file_name = std::string("/proc/") + pid + "/maps";
auto maps = std::unique_ptr<FILE, decltype(&fclose)>{fopen(file_name.c_str(), "r"), &fclose};
if (maps) {
char *line = nullptr;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, maps.get())) > 0) {
line[read - 1] = '\0';
uintptr_t start = 0;
uintptr_t end = 0;
uintptr_t off = 0;
ino_t inode = 0;
unsigned int dev_major = 0;
unsigned int dev_minor = 0;
std::array<char, kPermLength> perm{'\0'};
int path_off;
if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start,
&end, perm.data(), &off, &dev_major, &dev_minor, &inode,
&path_off) != kMapEntry) {
continue;
}
while (path_off < read && isspace(line[path_off])) path_off++;
auto &ref = info.emplace_back(MapInfo{start, end, 0, perm[3] == 'p', off,
static_cast<dev_t>(makedev(dev_major, dev_minor)),
inode, line + path_off});
if (perm[0] == 'r') ref.perms |= PROT_READ;
if (perm[1] == 'w') ref.perms |= PROT_WRITE;
if (perm[2] == 'x') ref.perms |= PROT_EXEC;
}
free(line);
/* WARNING: C++ keyword */
std::vector<MapInfo> MapInfo::Scan(const std::string &pid) {
constexpr static auto kPermLength = 5;
constexpr static auto kMapEntry = 7;
/* WARNING: C++ keyword */
std::vector<MapInfo> info;
char file_name[NAME_MAX];
snprintf(file_name, sizeof(file_name), "/proc/%s/maps", pid.c_str());
/* WARNING: C++ keyword */
auto maps = std::unique_ptr<FILE, decltype(&fclose)>{fopen(file_name, "r"), &fclose};
if (maps) {
char *line = NULL;
size_t len = 0;
ssize_t read;
/* WARNING: C++ keyword */
while ((read = getline(&line, &len, maps.get())) > 0) {
line[read - 1] = '\0';
uintptr_t start = 0;
uintptr_t end = 0;
uintptr_t off = 0;
ino_t inode = 0;
unsigned int dev_major = 0;
unsigned int dev_minor = 0;
/* WARNING: C++ keyword */
std::array<char, kPermLength> perm {'\0'};
int path_off;
if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start,
&end, perm.data(), &off, &dev_major, &dev_minor, &inode,
&path_off) != kMapEntry) continue;
while (path_off < read && isspace(line[path_off])) path_off++;
/* WARNING: C++ keyword */
MapInfo &ref = info.emplace_back(MapInfo{
start,
end,
0,
perm[3] == 'p',
off,
static_cast<dev_t>(makedev(dev_major, dev_minor)),
inode,
line + path_off
});
if (perm[0] == 'r') ref.perms |= PROT_READ;
if (perm[1] == 'w') ref.perms |= PROT_WRITE;
if (perm[2] == 'x') ref.perms |= PROT_EXEC;
}
return info;
free(line);
}
return info;
}
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
};
struct iovec remote{
.iov_base = (void *) remote_addr,
.iov_len = len
};
auto l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
if (l == -1) {
PLOGE("process_vm_writev");
} else if (static_cast<size_t>(l) != len) {
LOGW("not fully written: %zu, excepted %zu", l, len);
}
return l;
LOGV("write to remote addr %" PRIxPTR " size %zu", remote_addr, len);
struct iovec local = {
.iov_base = (void *)buf,
.iov_len = len
};
struct iovec remote = {
.iov_base = (void *)remote_addr,
.iov_len = len
};
ssize_t l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
if (l == -1) PLOGE("process_vm_writev");
else if ((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) {
struct iovec local{
.iov_base = (void *) buf,
.iov_len = len
};
struct iovec remote{
.iov_base = (void *) remote_addr,
.iov_len = len
};
auto l = process_vm_readv(pid, &local, 1, &remote, 1, 0);
if (l == -1) {
PLOGE("process_vm_readv");
} else if (static_cast<size_t>(l) != len) {
LOGW("not fully read: %zu, excepted %zu", l, len);
}
return l;
struct iovec local = {
.iov_base = (void *)buf,
.iov_len = len
};
struct iovec remote = {
.iov_base = (void *)remote_addr,
.iov_len = len
};
ssize_t l = process_vm_readv(pid, &local, 1, &remote, 1, 0);
if (l == -1) PLOGE("process_vm_readv");
else if ((size_t)l != len) LOGW("not fully read: %zu, excepted %zu", l, len);
return l;
}
bool get_regs(int pid, struct user_regs_struct &regs) {
#if defined(__x86_64__) || defined(__i386__)
#if defined(__x86_64__) || defined(__i386__)
if (ptrace(PTRACE_GETREGS, pid, 0, &regs) == -1) {
PLOGE("getregs");
return false;
PLOGE("getregs");
return false;
}
#elif defined(__aarch64__) || defined(__arm__)
#elif defined(__aarch64__) || defined(__arm__)
struct iovec iov = {
.iov_base = &regs,
.iov_len = sizeof(struct user_regs_struct),
.iov_base = &regs,
.iov_len = sizeof(struct user_regs_struct),
};
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
PLOGE("getregs");
return false;
PLOGE("getregs");
return false;
}
#endif
return true;
#endif
return true;
}
bool set_regs(int pid, struct user_regs_struct &regs) {
#if defined(__x86_64__) || defined(__i386__)
#if defined(__x86_64__) || defined(__i386__)
if (ptrace(PTRACE_SETREGS, pid, 0, &regs) == -1) {
PLOGE("setregs");
return false;
PLOGE("setregs");
return false;
}
#elif defined(__aarch64__) || defined(__arm__)
#elif defined(__aarch64__) || defined(__arm__)
struct iovec iov = {
.iov_base = &regs,
.iov_len = sizeof(struct user_regs_struct),
.iov_base = &regs,
.iov_len = sizeof(struct user_regs_struct),
};
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
PLOGE("setregs");
return false;
PLOGE("setregs");
return false;
}
#endif
return true;
#endif
return true;
}
/* WARNING: C++ keyword */
std::string get_addr_mem_region(std::vector<MapInfo> &info, uintptr_t addr) {
for (auto &map: info) {
if (map.start <= addr && map.end > addr) {
auto s = std::string(map.path);
s += ' ';
s += map.perms & PROT_READ ? 'r' : '-';
s += map.perms & PROT_WRITE ? 'w' : '-';
s += map.perms & PROT_EXEC ? 'x' : '-';
return s;
}
/* WARNING: C++ keyword */
for (auto &map: info) {
if (map.start <= addr && map.end > addr) {
/* WARNING: C++ keyword */
auto s = std::string(map.path);
s += ' ';
s += map.perms & PROT_READ ? 'r' : '-';
s += map.perms & PROT_WRITE ? 'w' : '-';
s += map.perms & PROT_EXEC ? 'x' : '-';
return s;
}
return "<unknown>";
}
return "<unknown>";
}
/* WARNING: C++ keyword */
void *find_module_return_addr(std::vector<MapInfo> &info, std::string_view suffix) {
for (auto &map: info) {
if ((map.perms & PROT_EXEC) == 0 && map.path.ends_with(suffix)) {
return (void *) map.start;
}
}
return nullptr;
/* WARNING: C++ keyword */
for (auto &map: info) {
/* WARNING: C++ keyword */
if ((map.perms & PROT_EXEC) == 0 && map.path.ends_with(suffix)) return (void *)map.start;
}
return NULL;
}
/* WARNING: C++ keyword */
void *find_module_base(std::vector<MapInfo> &info, std::string_view suffix) {
for (auto &map: info) {
if (map.offset == 0 && map.path.ends_with(suffix)) {
return (void *) map.start;
}
}
return nullptr;
/* WARNING: C++ keyword */
for (auto &map: info) {
/* WARNING: C++ keyword */
if (map.offset == 0 && map.path.ends_with(suffix)) return (void *)map.start;
}
return NULL;
}
void *find_func_addr(
std::vector<MapInfo> &local_info,
std::vector<MapInfo> &remote_info,
std::string_view module,
std::string_view func) {
auto lib = dlopen(module.data(), RTLD_NOW);
if (lib == nullptr) {
LOGE("failed to open lib %s: %s", module.data(), dlerror());
return nullptr;
}
auto sym = reinterpret_cast<uint8_t *>(dlsym(lib, func.data()));
if (sym == nullptr) {
LOGE("failed to find sym %s in %s: %s", func.data(), module.data(), dlerror());
dlclose(lib);
return nullptr;
}
LOGD("sym %s: %p", func.data(), sym);
/* WARNING: C++ keyword */
void *find_func_addr(std::vector<MapInfo> &local_info, std::vector<MapInfo> &remote_info, std::string_view module, std::string_view func) {
void *lib = dlopen(module.data(), RTLD_NOW);
if (lib == NULL) {
LOGE("failed to open lib %s: %s", module.data(), dlerror());
return NULL;
}
uint8_t *sym = (uint8_t *)dlsym(lib, func.data());
if (sym == NULL) {
LOGE("failed to find sym %s in %s: %s", func.data(), module.data(), dlerror());
dlclose(lib);
auto local_base = reinterpret_cast<uint8_t *>(find_module_base(local_info, module));
if (local_base == nullptr) {
LOGE("failed to find local base for module %s", module.data());
return nullptr;
}
auto remote_base = reinterpret_cast<uint8_t *>(find_module_base(remote_info, module));
if (remote_base == nullptr) {
LOGE("failed to find remote base for module %s", module.data());
return nullptr;
}
LOGD("found local base %p remote base %p", local_base, remote_base);
auto addr = (sym - local_base) + remote_base;
LOGD("addr %p", addr);
return addr;
return NULL;
}
LOGD("sym %s: %p", func.data(), sym);
dlclose(lib);
uint8_t *local_base = (uint8_t *)find_module_base(local_info, module);
if (local_base == NULL) {
LOGE("failed to find local base for module %s", module.data());
return NULL;
}
uint8_t *remote_base = (uint8_t *)find_module_base(remote_info, module);
if (remote_base == NULL) {
LOGE("failed to find remote base for module %s", module.data());
return NULL;
}
LOGD("found local base %p remote base %p", local_base, remote_base);
uint8_t *addr = (sym - local_base) + remote_base;
LOGD("addr %p", addr);
return addr;
}
/* WARNING: C++ keyword */
void align_stack(struct user_regs_struct &regs, long preserve) {
regs.REG_SP = (regs.REG_SP - preserve) & ~0xf;
regs.REG_SP = (regs.REG_SP - preserve) & ~0xf;
}
/* WARNING: C++ keyword */
uintptr_t push_string(int pid, struct user_regs_struct &regs, const char *str) {
auto len = strlen(str) + 1;
regs.REG_SP -= len;
align_stack(regs);
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 %" PRIxPTR, addr);
return addr;
size_t len = strlen(str) + 1;
regs.REG_SP -= len;
align_stack(regs);
uintptr_t addr = (uintptr_t)regs.REG_SP;
if (!write_proc(pid, addr, str, len)) LOGE("failed to write string %s", str);
LOGD("pushed string %" PRIxPTR, addr);
return addr;
}
uintptr_t remote_call(int pid, struct user_regs_struct &regs, uintptr_t func_addr, uintptr_t return_addr,
std::vector<long> &args) {
align_stack(regs);
LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size());
for (auto &a: args) {
LOGV("arg %p", (void *) a);
}
#if defined(__x86_64__)
if (args.size() >= 1) {
regs.rdi = args[0];
}
if (args.size() >= 2) {
regs.rsi = args[1];
}
if (args.size() >= 3) {
regs.rdx = args[2];
}
if (args.size() >= 4) {
regs.rcx = args[3];
}
if (args.size() >= 5) {
regs.r8 = args[4];
}
if (args.size() >= 6) {
regs.r9 = args[5];
}
/* WARNING: C++ keyword */
uintptr_t remote_call(int pid, struct user_regs_struct &regs, uintptr_t func_addr, uintptr_t return_addr, std::vector<long> &args) {
align_stack(regs);
/* WARNING: C++ keyword */
LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size());
/* WARNING: C++ keyword */
for (auto &a: args) {
LOGV("arg %p", (void *) a);
}
#if defined(__x86_64__)
if (args.size() >= 1) regs.rdi = args[0];
if (args.size() >= 2) regs.rsi = args[1];
if (args.size() >= 3) regs.rdx = args[2];
if (args.size() >= 4) regs.rcx = args[3];
if (args.size() >= 5) regs.r8 = args[4];
if (args.size() >= 6) regs.r9 = args[5];
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)) {
LOGE("failed to push arguments");
}
long remain = (args.size() - 6L) * sizeof(long);
align_stack(regs, 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))) {
LOGE("failed to write 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(__i386__)
#elif defined(__i386__)
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)) {
LOGE("failed to push arguments");
}
long remain = (args.size()) * sizeof(long);
align_stack(regs, 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))) {
LOGE("failed to write 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__)
#elif defined(__aarch64__)
for (size_t i = 0; i < args.size() && i < 8; i++) {
regs.regs[i] = args[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);
long remain = (args.size() - 8) * sizeof(long);
align_stack(regs, 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__)
#elif defined(__arm__)
for (size_t i = 0; i < args.size() && i < 4; i++) {
regs.uregs[i] = args[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);
long remain = (args.size() - 4) * sizeof(long);
align_stack(regs, remain);
write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain);
}
regs.uregs[14] = return_addr;
regs.REG_IP = func_addr;
constexpr auto CPSR_T_MASK = 1lu << 5;
if ((regs.REG_IP & 1) != 0) {
regs.REG_IP = regs.REG_IP & ~1;
regs.uregs[16] = regs.uregs[16] | CPSR_T_MASK;
} else {
regs.uregs[16] = regs.uregs[16] & ~CPSR_T_MASK;
}
#endif
if (!set_regs(pid, regs)) {
LOGE("failed to set regs");
return 0;
}
ptrace(PTRACE_CONT, pid, 0, 0);
int status;
wait_for_trace(pid, &status, __WALL);
if (!get_regs(pid, regs)) {
LOGE("failed to get regs after call");
return 0;
}
if (WSTOPSIG(status) == SIGSEGV) {
if (static_cast<uintptr_t>(regs.REG_IP) != return_addr) {
LOGE("wrong return addr %p", (void *) regs.REG_IP);
return 0;
}
return regs.REG_RET;
} else {
char status_str[64];
parse_status(status, status_str, sizeof(status_str));
LOGE("stopped by other reason %s at addr %p", status_str, (void*) regs.REG_IP);
constexpr auto CPSR_T_MASK = 1lu << 5;
if ((regs.REG_IP & 1) != 0) {
regs.REG_IP = regs.REG_IP & ~1;
regs.uregs[16] = regs.uregs[16] | CPSR_T_MASK;
} else {
regs.uregs[16] = regs.uregs[16] & ~CPSR_T_MASK;
}
#endif
if (!set_regs(pid, regs)) {
LOGE("failed to set regs");
return 0;
}
ptrace(PTRACE_CONT, pid, 0, 0);
int status;
wait_for_trace(pid, &status, __WALL);
if (!get_regs(pid, regs)) {
LOGE("failed to get regs after call");
return 0;
}
if (WSTOPSIG(status) == SIGSEGV) {
if ((uintptr_t)regs.REG_IP != return_addr) {
LOGE("wrong return addr %p", (void *) regs.REG_IP);
return 0;
}
return regs.REG_RET;
} else {
char status_str[64];
parse_status(status, status_str, sizeof(status_str));
LOGE("stopped by other reason %s at addr %p", status_str, (void *)regs.REG_IP);
}
return 0;
}
int fork_dont_care() {
auto pid = fork();
if (pid < 0) {
PLOGE("fork 1");
} else if (pid == 0) {
pid = fork();
if (pid < 0) {
PLOGE("fork 2");
} else if (pid > 0) {
exit(0);
}
} else {
int status;
waitpid(pid, &status, __WALL);
}
return pid;
pid_t pid = fork();
if (pid < 0) PLOGE("fork 1");
else if (pid == 0) {
pid = fork();
if (pid < 0) PLOGE("fork 2");
else if (pid > 0) exit(0);
} else {
int status;
waitpid(pid, &status, __WALL);
}
return pid;
}
void wait_for_trace(int pid, int* status, int flags) {
while (true) {
auto result = waitpid(pid, status, flags);
if (result == -1) {
if (errno == EINTR) {
continue;
} else {
PLOGE("wait %d failed", pid);
exit(1);
}
}
if (!WIFSTOPPED(*status)) {
char status_str[64];
parse_status(*status, status_str, sizeof(status_str));
void wait_for_trace(int pid, int *status, int flags) {
while (1) {
pid_t result = waitpid(pid, status, flags);
if (result == -1) {
if (errno == EINTR) continue;
LOGE("process %d not stopped for trace: %s, exit", pid, status_str);
exit(1);
}
return;
PLOGE("wait %d failed", pid);
exit(1);
}
if (!WIFSTOPPED(*status)) {
char status_str[64];
parse_status(*status, status_str, sizeof(status_str));
LOGE("process %d not stopped for trace: %s, exit", pid, status_str);
exit(1);
}
return;
}
}
void parse_status(int status, char *buf, size_t len) {
snprintf(buf, len, "0x%x ", status);
if (WIFEXITED(status)) {
snprintf(buf + strlen(buf), len - strlen(buf), "exited with %d", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
snprintf(buf + strlen(buf), len - strlen(buf), "signaled with %s(%d)", sigabbrev_np(WTERMSIG(status)), WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
snprintf(buf + strlen(buf), len - strlen(buf), "stopped by ");
auto stop_sig = WSTOPSIG(status);
int stop_sig = WSTOPSIG(status);
snprintf(buf + strlen(buf), len - strlen(buf), "signal=%s(%d),", sigabbrev_np(stop_sig), stop_sig);
snprintf(buf + strlen(buf), len - strlen(buf), "event=%s", parse_ptrace_event(status));
} else {
@@ -424,7 +507,7 @@ void parse_status(int status, char *buf, size_t len) {
}
int get_program(int pid, char *buf, size_t size) {
char path[64];
char path[PATH_MAX];
snprintf(path, sizeof(path), "/proc/%d/exe", pid);
ssize_t sz = readlink(path, buf, size);

View File

@@ -10,6 +10,7 @@
#else
#define LOG_TAG "zygisk-ptrace32"
#endif
#include "logging.h"
struct MapInfo {

View File

@@ -52,7 +52,7 @@ androidComponents.onVariants { variant ->
into(moduleDir)
from("${rootProject.projectDir}/README.md")
from("$projectDir/src") {
exclude("module.prop", "customize.sh", "post-fs-data.sh", "service.sh", "zygisk-ctl.sh", "mazoku")
exclude("module.prop", "customize.sh", "post-fs-data.sh", "service.sh", "mazoku")
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
}
from("$projectDir/src") {
@@ -66,7 +66,7 @@ androidComponents.onVariants { variant ->
}
from("$projectDir/src/mazoku")
from("$projectDir/src") {
include("customize.sh", "post-fs-data.sh", "service.sh", "zygisk-ctl.sh")
include("customize.sh", "post-fs-data.sh", "service.sh")
val tokens = mapOf(
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false",
"MIN_KSU_VERSION" to "$minKsuVersion",
@@ -158,12 +158,6 @@ androidComponents.onVariants { variant ->
root.file("bin/$abi64/zygiskd").asFile
)
)
set.add(
Pair(
root.file("bin/zygisk-ctl").asFile,
root.file("zygisk-ctl.sh").asFile
)
)
sig.initSign(privKey)
set.forEach { it.first.sha(it.second) }
val signFile = root.file(name).asFile

View File

@@ -94,14 +94,12 @@ ui_print "- Extracting module files"
extract "$ZIPFILE" 'module.prop' "$MODPATH"
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
extract "$ZIPFILE" 'service.sh' "$MODPATH"
extract "$ZIPFILE" 'zygisk-ctl.sh' "$MODPATH"
extract "$ZIPFILE" 'mazoku' "$MODPATH"
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
mkdir "$MODPATH/bin"
mkdir "$MODPATH/lib"
mkdir "$MODPATH/lib64"
mv "$MODPATH/zygisk-ctl.sh" "$MODPATH/bin/zygisk-ctl"
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
ui_print "- Extracting x86 libraries"

View File

@@ -1,6 +0,0 @@
MODDIR=${0%/*}/..
export TMP_PATH=/sbin
[ -d /sbin ] || export TMP_PATH=/debug_ramdisk
exec $MODDIR/bin/zygisk-ptrace64 ctl $*

View File

@@ -27,6 +27,7 @@ pub enum DaemonSocketAction {
PingHeartbeat,
RequestLogcatFd,
GetProcessFlags,
GetInfo,
ReadModules,
RequestCompanionSocket,
GetModuleDir,

View File

@@ -268,6 +268,20 @@ fn handle_daemon_action(
);
stream.write_u32(flags.bits())?;
}
DaemonSocketAction::GetInfo => {
let mut flags = ProcessFlags::empty();
match root_impl::get_impl() {
root_impl::RootImpl::KernelSU => flags |= ProcessFlags::PROCESS_ROOT_IS_KSU,
root_impl::RootImpl::Magisk => flags |= ProcessFlags::PROCESS_ROOT_IS_MAGISK,
_ => panic!("wrong root impl: {:?}", root_impl::get_impl()),
}
stream.write_u32(flags.bits())?;
let pid = unsafe { libc::getpid() };
stream.write_u32(pid as u32)?;
}
DaemonSocketAction::ReadModules => {
stream.write_usize(context.modules.len())?;
for module in context.modules.iter() {