You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
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:
@@ -18,10 +18,11 @@ namespace zygiskd {
|
|||||||
|
|
||||||
int Connect(uint8_t retry) {
|
int Connect(uint8_t retry) {
|
||||||
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||||
struct sockaddr_un addr{
|
struct sockaddr_un addr = {
|
||||||
.sun_family = AF_UNIX,
|
.sun_family = AF_UNIX,
|
||||||
.sun_path={0},
|
.sun_path = { 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
auto socket_path = TMP_PATH + kCPSocketName;
|
auto socket_path = TMP_PATH + kCPSocketName;
|
||||||
strcpy(addr.sun_path, socket_path.c_str());
|
strcpy(addr.sun_path, socket_path.c_str());
|
||||||
socklen_t socklen = sizeof(addr);
|
socklen_t socklen = sizeof(addr);
|
||||||
@@ -67,6 +68,7 @@ namespace zygiskd {
|
|||||||
}
|
}
|
||||||
socket_utils::write_u8(fd, (uint8_t) SocketAction::GetProcessFlags);
|
socket_utils::write_u8(fd, (uint8_t) SocketAction::GetProcessFlags);
|
||||||
socket_utils::write_u32(fd, uid);
|
socket_utils::write_u32(fd, uid);
|
||||||
|
|
||||||
return socket_utils::read_u32(fd);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,18 @@ private:
|
|||||||
Fd fd_ = -1;
|
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 {
|
namespace zygiskd {
|
||||||
|
|
||||||
struct Module {
|
struct Module {
|
||||||
@@ -55,6 +67,7 @@ namespace zygiskd {
|
|||||||
PingHeartBeat,
|
PingHeartBeat,
|
||||||
RequestLogcatFd,
|
RequestLogcatFd,
|
||||||
GetProcessFlags,
|
GetProcessFlags,
|
||||||
|
GetInfo,
|
||||||
ReadModules,
|
ReadModules,
|
||||||
RequestCompanionSocket,
|
RequestCompanionSocket,
|
||||||
GetModuleDir,
|
GetModuleDir,
|
||||||
@@ -81,4 +94,6 @@ namespace zygiskd {
|
|||||||
void ZygoteRestart();
|
void ZygoteRestart();
|
||||||
|
|
||||||
void SystemServerStarted();
|
void SystemServerStarted();
|
||||||
|
|
||||||
|
void GetInfo(struct zygote_info *info);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,60 @@
|
|||||||
#include "main.h"
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "monitor.h"
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
#include "daemon.h"
|
#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) {
|
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) {
|
if (argc >= 2 && strcmp(argv[1], "monitor") == 0) {
|
||||||
init_monitor();
|
init_monitor();
|
||||||
|
|
||||||
|
printf("[ReZygisk]: Started monitoring...\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argc >= 3 && strcmp(argv[1], "trace") == 0) {
|
} else if (argc >= 3 && strcmp(argv[1], "trace") == 0) {
|
||||||
if (argc >= 4 && strcmp(argv[3], "--restart") == 0) zygiskd::ZygoteRestart();
|
if (argc >= 4 && strcmp(argv[3], "--restart") == 0) zygiskd::ZygoteRestart();
|
||||||
@@ -18,35 +66,75 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("[ReZygisk]: Tracing %ld...\n", pid);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argc >= 2 && strcmp(argv[1], "ctl") == 0) {
|
} else if (argc >= 2 && strcmp(argv[1], "ctl") == 0) {
|
||||||
if (argc == 3) {
|
enum Command command;
|
||||||
if (strcmp(argv[2], "start") == 0) {
|
|
||||||
send_control_command(START);
|
|
||||||
|
|
||||||
return 0;
|
if (strcmp(argv[2], "start") == 0) command = START;
|
||||||
} else if (strcmp(argv[2], "stop") == 0) {
|
else if (strcmp(argv[2], "stop") == 0) command = STOP;
|
||||||
send_control_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;
|
return 1;
|
||||||
} else if (strcmp(argv[2], "exit") == 0) {
|
|
||||||
send_control_command(EXIT);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("ReZygisk Tracer %s\n", ZKSU_VERSION);
|
if (send_control_command(command) == -1) {
|
||||||
printf("Usage: %s ctl start|stop|exit\n", argv[0]);
|
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) {
|
} else if (argc >= 2 && strcmp(argv[1], "version") == 0) {
|
||||||
printf("ReZygisk Tracer %s\n", ZKSU_VERSION);
|
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;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
printf("ReZygisk Tracer %s\n", ZKSU_VERSION);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "main.h"
|
#include "monitor.h"
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
#include "files.hpp"
|
#include "files.hpp"
|
||||||
#include "misc.hpp"
|
#include "misc.hpp"
|
||||||
@@ -41,81 +41,103 @@ struct EventHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct EventLoop {
|
struct EventLoop {
|
||||||
private:
|
private:
|
||||||
int epoll_fd_;
|
int epoll_fd_;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
public:
|
public:
|
||||||
bool Init() {
|
bool Init() {
|
||||||
epoll_fd_ = epoll_create(1);
|
epoll_fd_ = epoll_create(1);
|
||||||
if (epoll_fd_ == -1) {
|
if (epoll_fd_ == -1) {
|
||||||
PLOGE("failed to create");
|
PLOGE("failed to create");
|
||||||
return false;
|
|
||||||
}
|
return false;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stop() {
|
void Stop() {
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loop() {
|
void Loop() {
|
||||||
running = true;
|
running = true;
|
||||||
constexpr auto MAX_EVENTS = 2;
|
|
||||||
struct epoll_event events[MAX_EVENTS];
|
constexpr auto MAX_EVENTS = 2;
|
||||||
while (running) {
|
struct epoll_event events[MAX_EVENTS];
|
||||||
int nfds = epoll_wait(epoll_fd_, events, MAX_EVENTS, -1);
|
|
||||||
if (nfds == -1) {
|
while (running) {
|
||||||
if (errno != EINTR)
|
int nfds = epoll_wait(epoll_fd_, events, MAX_EVENTS, -1);
|
||||||
PLOGE("epoll_wait");
|
if (nfds == -1) {
|
||||||
continue;
|
if (errno != EINTR) PLOGE("epoll_wait");
|
||||||
}
|
|
||||||
for (int i = 0; i < nfds; i++) {
|
continue;
|
||||||
reinterpret_cast<EventHandler *>(events[i].data.ptr)->HandleEvent(*this,
|
|
||||||
events[i].events);
|
|
||||||
if (!running) break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
bool RegisterHandler(EventHandler &handler, uint32_t events) {
|
||||||
struct epoll_event ev{};
|
struct epoll_event ev{};
|
||||||
ev.events = events;
|
ev.events = events;
|
||||||
ev.data.ptr = &handler;
|
ev.data.ptr = &handler;
|
||||||
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, handler.GetFd(), &ev) == -1) {
|
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, handler.GetFd(), &ev) == -1) {
|
||||||
PLOGE("failed to add event handler");
|
PLOGE("failed to add event handler");
|
||||||
return false;
|
|
||||||
}
|
return false;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] bool UnregisterHandler(EventHandler &handler) {
|
bool UnregisterHandler(EventHandler &handler) {
|
||||||
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), NULL) == -1) {
|
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), NULL) == -1) {
|
||||||
PLOGE("failed to del event handler");
|
PLOGE("failed to del event handler");
|
||||||
return false;
|
|
||||||
}
|
return false;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
~EventLoop() {
|
~EventLoop() {
|
||||||
if (epoll_fd_ >= 0) close(epoll_fd_);
|
if (epoll_fd_ >= 0) close(epoll_fd_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static TracingState tracing_state = TRACING;
|
static TracingState tracing_state = TRACING;
|
||||||
static char prop_path[PATH_MAX];
|
static char prop_path[PATH_MAX];
|
||||||
|
|
||||||
|
|
||||||
struct Status {
|
struct Status {
|
||||||
bool supported = false;
|
bool supported = false;
|
||||||
bool zygote_injected = false;
|
bool zygote_injected = false;
|
||||||
bool daemon_running = false;
|
bool daemon_running = false;
|
||||||
pid_t daemon_pid = -1;
|
pid_t daemon_pid = -1;
|
||||||
char *daemon_info;
|
char *daemon_info;
|
||||||
char *daemon_error_info;
|
char *daemon_error_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Status status64;
|
static Status status64 = {
|
||||||
static Status status32;
|
.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 {
|
struct SocketHandler : public EventHandler {
|
||||||
int sock_fd_;
|
int sock_fd_;
|
||||||
@@ -157,155 +179,157 @@ struct SocketHandler : public EventHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
std::vector<uint8_t> buf;
|
std::vector<uint8_t> buf;
|
||||||
buf.resize(sizeof(MsgHead), 0);
|
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 real_size;
|
||||||
ssize_t nread = recv(sock_fd_, &msg, sizeof(msg), MSG_PEEK);
|
ssize_t nread = recv(sock_fd_, &msg, sizeof(msg), MSG_PEEK);
|
||||||
if (nread == -1) {
|
if (nread == -1) {
|
||||||
if (errno == EAGAIN) break;
|
if (errno == EAGAIN) break;
|
||||||
|
|
||||||
PLOGE("read socket");
|
PLOGE("read socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<size_t>(nread) < sizeof(Command)) {
|
if (static_cast<size_t>(nread) < sizeof(Command)) {
|
||||||
LOGE("read %zu < %zu", nread, sizeof(Command));
|
LOGE("read %zu < %zu", nread, sizeof(Command));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.cmd >= Command::DAEMON64_SET_INFO && msg.cmd != Command::SYSTEM_SERVER_STARTED) {
|
if (msg.cmd >= Command::DAEMON64_SET_INFO && msg.cmd != Command::SYSTEM_SERVER_STARTED) {
|
||||||
if (nread != sizeof(msg)) {
|
if (nread != sizeof(msg)) {
|
||||||
LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(MsgHead));
|
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);
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (msg.cmd) {
|
real_size = sizeof(MsgHead) + msg.length;
|
||||||
case START: {
|
} else {
|
||||||
if (tracing_state == STOPPING) tracing_state = TRACING;
|
if (nread != sizeof(Command)) {
|
||||||
else if (tracing_state == STOPPED) {
|
LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(Command));
|
||||||
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
|
|
||||||
|
|
||||||
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;
|
updateStatus();
|
||||||
memcpy(monitor_stop_reason, "user requested", sizeof("user requested"));
|
|
||||||
|
|
||||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0);
|
break;
|
||||||
updateStatus();
|
}
|
||||||
}
|
case STOP: {
|
||||||
|
if (tracing_state == TRACING) {
|
||||||
|
LOGI("stop tracing requested");
|
||||||
|
|
||||||
break;
|
tracing_state = STOPPING;
|
||||||
}
|
|
||||||
case EXIT: {
|
|
||||||
LOGI("prepare for exit ...");
|
|
||||||
|
|
||||||
tracing_state = EXITING;
|
|
||||||
memcpy(monitor_stop_reason, "user requested", sizeof("user requested"));
|
memcpy(monitor_stop_reason, "user requested", sizeof("user requested"));
|
||||||
|
|
||||||
|
ptrace(PTRACE_INTERRUPT, 1, 0, 0);
|
||||||
updateStatus();
|
updateStatus();
|
||||||
loop.Stop();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case ZYGOTE64_INJECTED: {
|
|
||||||
status64.zygote_injected = true;
|
|
||||||
|
|
||||||
updateStatus();
|
break;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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;
|
constexpr int MAX_RETRY_COUNT = 5;
|
||||||
|
|
||||||
#define CREATE_ZYGOTE_START_COUNTER(abi) \
|
#define CREATE_ZYGOTE_START_COUNTER(abi) \
|
||||||
struct timespec last_zygote##abi{.tv_sec = 0, .tv_nsec = 0}; \
|
struct timespec last_zygote##abi = { \
|
||||||
int count_zygote##abi = 0; \
|
.tv_sec = 0, \
|
||||||
bool should_stop_inject##abi() { \
|
.tv_nsec = 0 \
|
||||||
struct timespec now{}; \
|
}; \
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now); \
|
\
|
||||||
if (now.tv_sec - last_zygote##abi.tv_sec < 30) { \
|
int count_zygote ## abi = 0; \
|
||||||
count_zygote##abi++; \
|
bool should_stop_inject ## abi() { \
|
||||||
} else { \
|
struct timespec now = {}; \
|
||||||
count_zygote##abi = 0; \
|
clock_gettime(CLOCK_MONOTONIC, &now); \
|
||||||
} \
|
if (now.tv_sec - last_zygote ## abi.tv_sec < 30) \
|
||||||
last_zygote##abi = now; \
|
count_zygote ## abi++; \
|
||||||
return count_zygote##abi >= MAX_RETRY_COUNT; \
|
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(64)
|
||||||
CREATE_ZYGOTE_START_COUNTER(32)
|
CREATE_ZYGOTE_START_COUNTER(32)
|
||||||
@@ -360,6 +389,8 @@ static bool ensure_daemon_created(bool is_64bit) {
|
|||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
|
LOGI("daemon%s started with pid %d", is_64bit ? "64" : "32", pid);
|
||||||
|
|
||||||
status.supported = true;
|
status.supported = true;
|
||||||
status.daemon_pid = pid;
|
status.daemon_pid = pid;
|
||||||
status.daemon_running = true;
|
status.daemon_running = true;
|
||||||
@@ -367,23 +398,25 @@ static bool ensure_daemon_created(bool is_64bit) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
LOGI("daemon%s already started with pid %d", is_64bit ? "64" : "32", status.daemon_pid);
|
||||||
|
|
||||||
return status.daemon_running;
|
return status.daemon_running;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_DAEMON_EXIT(abi) \
|
#define CHECK_DAEMON_EXIT(abi) \
|
||||||
if (status##abi.supported && pid == status64.daemon_pid) { \
|
if (status##abi.supported && pid == status64.daemon_pid) { \
|
||||||
char status_str[64]; \
|
char status_str[64]; \
|
||||||
parse_status(status, status_str, sizeof(status_str)); \
|
parse_status(status, status_str, sizeof(status_str)); \
|
||||||
\
|
\
|
||||||
LOGW("daemon" #abi "pid %d exited: %s", pid, status_str); \
|
LOGW("daemon" #abi "pid %d exited: %s", pid, status_str); \
|
||||||
status##abi.daemon_running = false; \
|
status##abi.daemon_running = false; \
|
||||||
\
|
\
|
||||||
if (status##abi.daemon_error_info[0] == '\0') \
|
if (status##abi.daemon_error_info[0] == '\0') \
|
||||||
memcpy(status##abi.daemon_error_info, status_str, strlen(status_str)); \
|
memcpy(status##abi.daemon_error_info, status_str, strlen(status_str)); \
|
||||||
\
|
\
|
||||||
updateStatus(); \
|
updateStatus(); \
|
||||||
continue; \
|
continue; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PRE_INJECT(abi, is_64) \
|
#define PRE_INJECT(abi, is_64) \
|
||||||
@@ -605,7 +638,9 @@ struct SigChldHandler : public EventHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static char pre_section[1024];
|
static char pre_section[1024];
|
||||||
|
static int pre_section_len = 0;
|
||||||
static char post_section[1024];
|
static char post_section[1024];
|
||||||
|
static int post_section_len = 0;
|
||||||
|
|
||||||
#define WRITE_STATUS_ABI(suffix) \
|
#define WRITE_STATUS_ABI(suffix) \
|
||||||
if (status ## suffix.supported) { \
|
if (status ## suffix.supported) { \
|
||||||
@@ -616,7 +651,7 @@ static char post_section[1024];
|
|||||||
else strcat(status_text, "❌ not injected,"); \
|
else strcat(status_text, "❌ not injected,"); \
|
||||||
\
|
\
|
||||||
strcat(status_text, " daemon" #suffix ":"); \
|
strcat(status_text, " daemon" #suffix ":"); \
|
||||||
if (status ## suffix .daemon_running) { \
|
if (status ## suffix.daemon_running) { \
|
||||||
strcat(status_text, "😋 running"); \
|
strcat(status_text, "😋 running"); \
|
||||||
\
|
\
|
||||||
if (status ## suffix.daemon_info[0] != '\0') { \
|
if (status ## suffix.daemon_info[0] != '\0') { \
|
||||||
@@ -637,7 +672,7 @@ static char post_section[1024];
|
|||||||
|
|
||||||
static void updateStatus() {
|
static void updateStatus() {
|
||||||
FILE *prop = fopen(prop_path, "w");
|
FILE *prop = fopen(prop_path, "w");
|
||||||
char status_text[64] = "monitor: ";
|
char status_text[256] = "monitor: ";
|
||||||
|
|
||||||
switch (tracing_state) {
|
switch (tracing_state) {
|
||||||
case TRACING: {
|
case TRACING: {
|
||||||
@@ -671,6 +706,8 @@ static void updateStatus() {
|
|||||||
WRITE_STATUS_ABI(32)
|
WRITE_STATUS_ABI(32)
|
||||||
|
|
||||||
fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section);
|
fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section);
|
||||||
|
|
||||||
|
fclose(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool prepare_environment() {
|
static bool prepare_environment() {
|
||||||
@@ -686,19 +723,65 @@ static bool prepare_environment() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char line[1024];
|
const char field_name[] = "description=";
|
||||||
ssize_t len = fread(line, 1, sizeof(line), orig_prop);
|
|
||||||
/* TODO: Check if this part translation is correct.
|
/* TODO: improve this code */
|
||||||
Old approach may be a callback approach that gets called
|
int i = 1;
|
||||||
every new line
|
while (1) {
|
||||||
*/
|
int int_char = fgetc(orig_prop);
|
||||||
if ((unsigned long)len > (sizeof("description") - 1) && strncmp(line, "description=", sizeof("description") - 1) == 0) {
|
if (int_char == EOF) break;
|
||||||
strcat(post_section, "description=");
|
|
||||||
strcat(post_section, line + sizeof("description"));
|
pre_section[pre_section_len] = (char)int_char;
|
||||||
} else {
|
pre_section[pre_section_len + 1] = '\0';
|
||||||
strcat(pre_section, line);
|
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();
|
updateStatus();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -724,9 +807,9 @@ void init_monitor() {
|
|||||||
LOGI("exit");
|
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);
|
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 = {
|
struct sockaddr_un addr = {
|
||||||
.sun_family = AF_UNIX,
|
.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;
|
socklen_t socklen = sizeof(sa_family_t) + sun_path_len;
|
||||||
|
|
||||||
ssize_t nsend = sendto(sockfd, (void *)&cmd, sizeof(cmd), 0, (sockaddr *)&addr, socklen);
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
void init_monitor();
|
void init_monitor();
|
||||||
|
|
||||||
bool trace_zygote(int pid);
|
bool trace_zygote(int pid);
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
@@ -21,6 +22,6 @@ enum Command {
|
|||||||
SYSTEM_SERVER_STARTED = 10
|
SYSTEM_SERVER_STARTED = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
void send_control_command(enum Command cmd);
|
int send_control_command(enum Command cmd);
|
||||||
|
|
||||||
#endif /* MAIN_HPP */
|
#endif /* MAIN_HPP */
|
||||||
@@ -19,209 +19,297 @@
|
|||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
|
|
||||||
bool inject_on_main(int pid, const char *lib_path) {
|
bool inject_on_main(int pid, const char *lib_path) {
|
||||||
LOGI("injecting %s to zygote %d", lib_path, pid);
|
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{};
|
parsing KernelArgumentBlock
|
||||||
auto map = MapInfo::Scan(std::to_string(pid));
|
|
||||||
if (!get_regs(pid, regs)) return false;
|
https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/private/KernelArgumentBlock.h;l=30;drc=6d1ee77ee32220e4202c3066f7e1f69572967ad8
|
||||||
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;
|
struct user_regs_struct regs {},
|
||||||
auto argv = reinterpret_cast<char **>(reinterpret_cast<uintptr_t *>(arg) + 1);
|
backup {};
|
||||||
LOGV("argv %p", argv);
|
|
||||||
read_proc(pid, arg, &argc, sizeof(argc));
|
/* WARNING: C++ keyword */
|
||||||
LOGV("argc %d", argc);
|
std::vector<MapInfo> map = MapInfo::Scan(std::to_string(pid));
|
||||||
auto envp = argv + argc + 1;
|
if (!get_regs(pid, regs)) return false;
|
||||||
LOGV("envp %p", envp);
|
|
||||||
auto p = envp;
|
uintptr_t arg = (uintptr_t)regs.REG_SP;
|
||||||
while (true) {
|
|
||||||
uintptr_t *buf;
|
LOGV("kernel argument %" PRIxPTR " %s", arg, get_addr_mem_region(map, arg).c_str());
|
||||||
read_proc(pid, (uintptr_t) p, &buf, sizeof(buf));
|
|
||||||
if (buf != nullptr) ++p;
|
int argc;
|
||||||
else break;
|
char **argv = (char **)((uintptr_t *)arg + 1);
|
||||||
}
|
LOGV("argv %p", (void *)argv);
|
||||||
++p;
|
|
||||||
auto auxv = reinterpret_cast<ElfW(auxv_t) *>(p);
|
read_proc(pid, arg, &argc, sizeof(argc));
|
||||||
LOGV("auxv %p %s", auxv, get_addr_mem_region(map, (uintptr_t) auxv).c_str());
|
LOGV("argc %d", argc);
|
||||||
auto v = auxv;
|
|
||||||
uintptr_t entry_addr = 0;
|
/* WARNING: C++ keyword */
|
||||||
uintptr_t addr_of_entry_addr = 0;
|
auto envp = argv + argc + 1;
|
||||||
while (true) {
|
LOGV("envp %p", (void *)envp);
|
||||||
ElfW(auxv_t) buf;
|
|
||||||
read_proc(pid, (uintptr_t) v, &buf, sizeof(buf));
|
/* WARNING: C++ keyword */
|
||||||
if (buf.a_type == AT_ENTRY) {
|
auto p = envp;
|
||||||
entry_addr = (uintptr_t) buf.a_un.a_val;
|
while (1) {
|
||||||
addr_of_entry_addr = (uintptr_t) v + offsetof(ElfW(auxv_t), a_un);
|
uintptr_t *buf;
|
||||||
LOGV("entry address %" PRIxPTR " %s (entry=%" PRIxPTR ", entry_addr=%" PRIxPTR ")", entry_addr,
|
read_proc(pid, (uintptr_t)p, &buf, sizeof(buf));
|
||||||
get_addr_mem_region(map, entry_addr).c_str(), (uintptr_t) v, addr_of_entry_addr);
|
|
||||||
break;
|
if (buf == NULL) break;
|
||||||
}
|
|
||||||
if (buf.a_type == AT_NULL) break;
|
/* TODO: Why ++p? */
|
||||||
v++;
|
p++;
|
||||||
}
|
}
|
||||||
if (entry_addr == 0) {
|
|
||||||
LOGE("failed to get entry");
|
/* TODO: Why ++p? */
|
||||||
return false;
|
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
|
if (buf.a_type == AT_NULL) break;
|
||||||
// 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
|
v++;
|
||||||
if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &entry_addr, sizeof(entry_addr))) return false;
|
}
|
||||||
|
|
||||||
// backup registers
|
if (entry_addr == 0) {
|
||||||
memcpy(&backup, ®s, sizeof(regs));
|
LOGE("failed to get entry");
|
||||||
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);
|
|
||||||
|
|
||||||
// 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;
|
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, ®s, 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 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) {
|
bool trace_zygote(int pid) {
|
||||||
LOGI("start tracing %d", pid);
|
LOGI("start tracing %d (tracer %d)", pid, getpid());
|
||||||
#define WAIT_OR_DIE wait_for_trace(pid, &status, __WALL);
|
|
||||||
#define CONT_OR_DIE \
|
int status;
|
||||||
if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) { \
|
|
||||||
PLOGE("cont"); \
|
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL) == -1) {
|
||||||
return false; \
|
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());
|
LOGD("inject done, continue process");
|
||||||
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL) == -1) {
|
if (kill(pid, SIGCONT)) {
|
||||||
PLOGE("seize");
|
PLOGE("kill");
|
||||||
return false;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONT_OR_DIE
|
||||||
WAIT_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);
|
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP)) {
|
||||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
CONT_OR_DIE
|
||||||
return false;
|
WAIT_OR_DIE
|
||||||
}
|
|
||||||
|
if (STOPPED_WITH(SIGCONT, 0)) {
|
||||||
|
LOGD("received SIGCONT");
|
||||||
|
|
||||||
|
ptrace(PTRACE_DETACH, pid, 0, SIGCONT);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
char status_str[64];
|
char status_str[64];
|
||||||
parse_status(status, status_str, sizeof(status_str));
|
parse_status(status, status_str, sizeof(status_str));
|
||||||
|
|
||||||
LOGE("unknown state %s, not SIGSTOP + EVENT_STOP", status_str);
|
LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", status_str);
|
||||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
@@ -22,400 +24,481 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "utils.hpp"
|
|
||||||
#include "logging.h"
|
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
bool switch_mnt_ns(int pid, int *fd) {
|
bool switch_mnt_ns(int pid, int *fd) {
|
||||||
int nsfd, old_nsfd = -1;
|
int nsfd, old_nsfd = -1;
|
||||||
std::string path;
|
|
||||||
if (pid == 0) {
|
/* WARNING: C++ keyword */
|
||||||
if (fd != nullptr) {
|
char path[PATH_MAX];
|
||||||
nsfd = *fd;
|
if (pid == 0) {
|
||||||
*fd = -1;
|
if (fd != NULL) {
|
||||||
} else return false;
|
nsfd = *fd;
|
||||||
path = "/proc/self/fd/";
|
*fd = -1;
|
||||||
path += std::to_string(nsfd);
|
} else return false;
|
||||||
} else {
|
|
||||||
if (fd != nullptr) {
|
snprintf(path, sizeof(path), "/proc/self/fd/%d", nsfd);
|
||||||
old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
|
} else {
|
||||||
if (old_nsfd == -1) {
|
if (fd != NULL) {
|
||||||
PLOGE("get old nsfd");
|
old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
|
||||||
return false;
|
if (old_nsfd == -1) {
|
||||||
}
|
PLOGE("get old nsfd");
|
||||||
*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);
|
|
||||||
return false;
|
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);
|
close(nsfd);
|
||||||
return true;
|
close(old_nsfd);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(nsfd);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<MapInfo> MapInfo::Scan(const std::string& pid) {
|
/* WARNING: C++ keyword */
|
||||||
constexpr static auto kPermLength = 5;
|
std::vector<MapInfo> MapInfo::Scan(const std::string &pid) {
|
||||||
constexpr static auto kMapEntry = 7;
|
constexpr static auto kPermLength = 5;
|
||||||
std::vector<MapInfo> info;
|
constexpr static auto kMapEntry = 7;
|
||||||
std::string file_name = std::string("/proc/") + pid + "/maps";
|
|
||||||
auto maps = std::unique_ptr<FILE, decltype(&fclose)>{fopen(file_name.c_str(), "r"), &fclose};
|
/* WARNING: C++ keyword */
|
||||||
if (maps) {
|
std::vector<MapInfo> info;
|
||||||
char *line = nullptr;
|
char file_name[NAME_MAX];
|
||||||
size_t len = 0;
|
snprintf(file_name, sizeof(file_name), "/proc/%s/maps", pid.c_str());
|
||||||
ssize_t read;
|
|
||||||
while ((read = getline(&line, &len, maps.get())) > 0) {
|
/* WARNING: C++ keyword */
|
||||||
line[read - 1] = '\0';
|
auto maps = std::unique_ptr<FILE, decltype(&fclose)>{fopen(file_name, "r"), &fclose};
|
||||||
uintptr_t start = 0;
|
if (maps) {
|
||||||
uintptr_t end = 0;
|
char *line = NULL;
|
||||||
uintptr_t off = 0;
|
size_t len = 0;
|
||||||
ino_t inode = 0;
|
ssize_t read;
|
||||||
unsigned int dev_major = 0;
|
|
||||||
unsigned int dev_minor = 0;
|
/* WARNING: C++ keyword */
|
||||||
std::array<char, kPermLength> perm{'\0'};
|
while ((read = getline(&line, &len, maps.get())) > 0) {
|
||||||
int path_off;
|
line[read - 1] = '\0';
|
||||||
if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start,
|
|
||||||
&end, perm.data(), &off, &dev_major, &dev_minor, &inode,
|
uintptr_t start = 0;
|
||||||
&path_off) != kMapEntry) {
|
uintptr_t end = 0;
|
||||||
continue;
|
uintptr_t off = 0;
|
||||||
}
|
ino_t inode = 0;
|
||||||
while (path_off < read && isspace(line[path_off])) path_off++;
|
unsigned int dev_major = 0;
|
||||||
auto &ref = info.emplace_back(MapInfo{start, end, 0, perm[3] == 'p', off,
|
unsigned int dev_minor = 0;
|
||||||
static_cast<dev_t>(makedev(dev_major, dev_minor)),
|
|
||||||
inode, line + path_off});
|
/* WARNING: C++ keyword */
|
||||||
if (perm[0] == 'r') ref.perms |= PROT_READ;
|
std::array<char, kPermLength> perm {'\0'};
|
||||||
if (perm[1] == 'w') ref.perms |= PROT_WRITE;
|
|
||||||
if (perm[2] == 'x') ref.perms |= PROT_EXEC;
|
int path_off;
|
||||||
}
|
if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start,
|
||||||
free(line);
|
&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) {
|
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);
|
LOGV("write to remote addr %" PRIxPTR " size %zu", remote_addr, len);
|
||||||
struct iovec local{
|
|
||||||
.iov_base = (void *) buf,
|
struct iovec local = {
|
||||||
.iov_len = len
|
.iov_base = (void *)buf,
|
||||||
};
|
.iov_len = len
|
||||||
struct iovec remote{
|
};
|
||||||
.iov_base = (void *) remote_addr,
|
|
||||||
.iov_len = len
|
struct iovec remote = {
|
||||||
};
|
.iov_base = (void *)remote_addr,
|
||||||
auto l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
|
.iov_len = len
|
||||||
if (l == -1) {
|
};
|
||||||
PLOGE("process_vm_writev");
|
|
||||||
} else if (static_cast<size_t>(l) != len) {
|
ssize_t l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
|
||||||
LOGW("not fully written: %zu, excepted %zu", l, len);
|
if (l == -1) PLOGE("process_vm_writev");
|
||||||
}
|
else if ((size_t)l != len) LOGW("not fully written: %zu, excepted %zu", l, len);
|
||||||
return l;
|
|
||||||
|
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{
|
struct iovec local = {
|
||||||
.iov_base = (void *) buf,
|
.iov_base = (void *)buf,
|
||||||
.iov_len = len
|
.iov_len = len
|
||||||
};
|
};
|
||||||
struct iovec remote{
|
|
||||||
.iov_base = (void *) remote_addr,
|
struct iovec remote = {
|
||||||
.iov_len = len
|
.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");
|
ssize_t l = process_vm_readv(pid, &local, 1, &remote, 1, 0);
|
||||||
} else if (static_cast<size_t>(l) != len) {
|
if (l == -1) PLOGE("process_vm_readv");
|
||||||
LOGW("not fully read: %zu, excepted %zu", l, len);
|
else if ((size_t)l != len) LOGW("not fully read: %zu, excepted %zu", l, len);
|
||||||
}
|
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_regs(int pid, struct user_regs_struct ®s) {
|
bool get_regs(int pid, struct user_regs_struct ®s) {
|
||||||
#if defined(__x86_64__) || defined(__i386__)
|
#if defined(__x86_64__) || defined(__i386__)
|
||||||
if (ptrace(PTRACE_GETREGS, pid, 0, ®s) == -1) {
|
if (ptrace(PTRACE_GETREGS, pid, 0, ®s) == -1) {
|
||||||
PLOGE("getregs");
|
PLOGE("getregs");
|
||||||
return false;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#elif defined(__aarch64__) || defined(__arm__)
|
#elif defined(__aarch64__) || defined(__arm__)
|
||||||
struct iovec iov = {
|
struct iovec iov = {
|
||||||
.iov_base = ®s,
|
.iov_base = ®s,
|
||||||
.iov_len = sizeof(struct user_regs_struct),
|
.iov_len = sizeof(struct user_regs_struct),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
|
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
|
||||||
PLOGE("getregs");
|
PLOGE("getregs");
|
||||||
return false;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return true;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set_regs(int pid, struct user_regs_struct ®s) {
|
bool set_regs(int pid, struct user_regs_struct ®s) {
|
||||||
#if defined(__x86_64__) || defined(__i386__)
|
#if defined(__x86_64__) || defined(__i386__)
|
||||||
if (ptrace(PTRACE_SETREGS, pid, 0, ®s) == -1) {
|
if (ptrace(PTRACE_SETREGS, pid, 0, ®s) == -1) {
|
||||||
PLOGE("setregs");
|
PLOGE("setregs");
|
||||||
return false;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#elif defined(__aarch64__) || defined(__arm__)
|
#elif defined(__aarch64__) || defined(__arm__)
|
||||||
struct iovec iov = {
|
struct iovec iov = {
|
||||||
.iov_base = ®s,
|
.iov_base = ®s,
|
||||||
.iov_len = sizeof(struct user_regs_struct),
|
.iov_len = sizeof(struct user_regs_struct),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
|
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
|
||||||
PLOGE("setregs");
|
PLOGE("setregs");
|
||||||
return false;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return true;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* WARNING: C++ keyword */
|
||||||
std::string get_addr_mem_region(std::vector<MapInfo> &info, uintptr_t addr) {
|
std::string get_addr_mem_region(std::vector<MapInfo> &info, uintptr_t addr) {
|
||||||
for (auto &map: info) {
|
/* WARNING: C++ keyword */
|
||||||
if (map.start <= addr && map.end > addr) {
|
for (auto &map: info) {
|
||||||
auto s = std::string(map.path);
|
if (map.start <= addr && map.end > addr) {
|
||||||
s += ' ';
|
/* WARNING: C++ keyword */
|
||||||
s += map.perms & PROT_READ ? 'r' : '-';
|
auto s = std::string(map.path);
|
||||||
s += map.perms & PROT_WRITE ? 'w' : '-';
|
|
||||||
s += map.perms & PROT_EXEC ? 'x' : '-';
|
s += ' ';
|
||||||
return 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) {
|
void *find_module_return_addr(std::vector<MapInfo> &info, std::string_view suffix) {
|
||||||
for (auto &map: info) {
|
/* WARNING: C++ keyword */
|
||||||
if ((map.perms & PROT_EXEC) == 0 && map.path.ends_with(suffix)) {
|
for (auto &map: info) {
|
||||||
return (void *) map.start;
|
/* WARNING: C++ keyword */
|
||||||
}
|
if ((map.perms & PROT_EXEC) == 0 && map.path.ends_with(suffix)) return (void *)map.start;
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* WARNING: C++ keyword */
|
||||||
void *find_module_base(std::vector<MapInfo> &info, std::string_view suffix) {
|
void *find_module_base(std::vector<MapInfo> &info, std::string_view suffix) {
|
||||||
for (auto &map: info) {
|
/* WARNING: C++ keyword */
|
||||||
if (map.offset == 0 && map.path.ends_with(suffix)) {
|
for (auto &map: info) {
|
||||||
return (void *) map.start;
|
/* WARNING: C++ keyword */
|
||||||
}
|
if (map.offset == 0 && map.path.ends_with(suffix)) return (void *)map.start;
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *find_func_addr(
|
/* WARNING: C++ keyword */
|
||||||
std::vector<MapInfo> &local_info,
|
void *find_func_addr(std::vector<MapInfo> &local_info, std::vector<MapInfo> &remote_info, std::string_view module, std::string_view func) {
|
||||||
std::vector<MapInfo> &remote_info,
|
void *lib = dlopen(module.data(), RTLD_NOW);
|
||||||
std::string_view module,
|
if (lib == NULL) {
|
||||||
std::string_view func) {
|
LOGE("failed to open lib %s: %s", module.data(), dlerror());
|
||||||
auto lib = dlopen(module.data(), RTLD_NOW);
|
|
||||||
if (lib == nullptr) {
|
return NULL;
|
||||||
LOGE("failed to open lib %s: %s", module.data(), dlerror());
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
uint8_t *sym = (uint8_t *)dlsym(lib, func.data());
|
||||||
auto sym = reinterpret_cast<uint8_t *>(dlsym(lib, func.data()));
|
if (sym == NULL) {
|
||||||
if (sym == nullptr) {
|
LOGE("failed to find sym %s in %s: %s", func.data(), module.data(), dlerror());
|
||||||
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);
|
|
||||||
dlclose(lib);
|
dlclose(lib);
|
||||||
auto local_base = reinterpret_cast<uint8_t *>(find_module_base(local_info, module));
|
|
||||||
if (local_base == nullptr) {
|
return NULL;
|
||||||
LOGE("failed to find local base for module %s", module.data());
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
LOGD("sym %s: %p", func.data(), sym);
|
||||||
auto remote_base = reinterpret_cast<uint8_t *>(find_module_base(remote_info, module));
|
|
||||||
if (remote_base == nullptr) {
|
dlclose(lib);
|
||||||
LOGE("failed to find remote base for module %s", module.data());
|
|
||||||
return nullptr;
|
uint8_t *local_base = (uint8_t *)find_module_base(local_info, module);
|
||||||
}
|
if (local_base == NULL) {
|
||||||
LOGD("found local base %p remote base %p", local_base, remote_base);
|
LOGE("failed to find local base for module %s", module.data());
|
||||||
auto addr = (sym - local_base) + remote_base;
|
|
||||||
LOGD("addr %p", addr);
|
return NULL;
|
||||||
return addr;
|
}
|
||||||
|
|
||||||
|
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 ®s, long preserve) {
|
void align_stack(struct user_regs_struct ®s, 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 ®s, const char *str) {
|
uintptr_t push_string(int pid, struct user_regs_struct ®s, const char *str) {
|
||||||
auto len = strlen(str) + 1;
|
size_t len = strlen(str) + 1;
|
||||||
regs.REG_SP -= len;
|
|
||||||
align_stack(regs);
|
regs.REG_SP -= len;
|
||||||
auto addr = static_cast<uintptr_t>(regs.REG_SP);
|
|
||||||
if (!write_proc(pid, addr, str, len)) {
|
align_stack(regs);
|
||||||
LOGE("failed to write string %s", str);
|
|
||||||
}
|
uintptr_t addr = (uintptr_t)regs.REG_SP;
|
||||||
LOGD("pushed string %" PRIxPTR, addr);
|
if (!write_proc(pid, addr, str, len)) LOGE("failed to write string %s", str);
|
||||||
return 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,
|
/* WARNING: C++ keyword */
|
||||||
std::vector<long> &args) {
|
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);
|
align_stack(regs);
|
||||||
LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size());
|
|
||||||
for (auto &a: args) {
|
/* WARNING: C++ keyword */
|
||||||
LOGV("arg %p", (void *) a);
|
LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size());
|
||||||
}
|
|
||||||
#if defined(__x86_64__)
|
/* WARNING: C++ keyword */
|
||||||
if (args.size() >= 1) {
|
for (auto &a: args) {
|
||||||
regs.rdi = args[0];
|
LOGV("arg %p", (void *) a);
|
||||||
}
|
}
|
||||||
if (args.size() >= 2) {
|
|
||||||
regs.rsi = args[1];
|
#if defined(__x86_64__)
|
||||||
}
|
if (args.size() >= 1) regs.rdi = args[0];
|
||||||
if (args.size() >= 3) {
|
if (args.size() >= 2) regs.rsi = args[1];
|
||||||
regs.rdx = args[2];
|
if (args.size() >= 3) regs.rdx = args[2];
|
||||||
}
|
if (args.size() >= 4) regs.rcx = args[3];
|
||||||
if (args.size() >= 4) {
|
if (args.size() >= 5) regs.r8 = args[4];
|
||||||
regs.rcx = args[3];
|
if (args.size() >= 6) regs.r9 = args[5];
|
||||||
}
|
|
||||||
if (args.size() >= 5) {
|
|
||||||
regs.r8 = args[4];
|
|
||||||
}
|
|
||||||
if (args.size() >= 6) {
|
|
||||||
regs.r9 = args[5];
|
|
||||||
}
|
|
||||||
if (args.size() > 6) {
|
if (args.size() > 6) {
|
||||||
auto remain = (args.size() - 6) * sizeof(long);
|
long remain = (args.size() - 6L) * sizeof(long);
|
||||||
align_stack(regs, remain);
|
align_stack(regs, remain);
|
||||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) {
|
|
||||||
LOGE("failed to push arguments");
|
if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) LOGE("failed to push arguments");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.REG_SP -= sizeof(long);
|
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;
|
regs.REG_IP = func_addr;
|
||||||
#elif defined(__i386__)
|
#elif defined(__i386__)
|
||||||
if (args.size() > 0) {
|
if (args.size() > 0) {
|
||||||
auto remain = (args.size()) * sizeof(long);
|
long remain = (args.size()) * sizeof(long);
|
||||||
align_stack(regs, remain);
|
align_stack(regs, remain);
|
||||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) {
|
|
||||||
LOGE("failed to push arguments");
|
if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) LOGE("failed to push arguments");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.REG_SP -= sizeof(long);
|
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;
|
regs.REG_IP = func_addr;
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
for (size_t i = 0; i < args.size() && i < 8; i++) {
|
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) {
|
if (args.size() > 8) {
|
||||||
auto remain = (args.size() - 8) * sizeof(long);
|
long remain = (args.size() - 8) * sizeof(long);
|
||||||
align_stack(regs, remain);
|
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.regs[30] = return_addr;
|
||||||
regs.REG_IP = func_addr;
|
regs.REG_IP = func_addr;
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
for (size_t i = 0; i < args.size() && i < 4; i++) {
|
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) {
|
if (args.size() > 4) {
|
||||||
auto remain = (args.size() - 4) * sizeof(long);
|
long remain = (args.size() - 4) * sizeof(long);
|
||||||
align_stack(regs, remain);
|
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.uregs[14] = return_addr;
|
||||||
regs.REG_IP = func_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;
|
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() {
|
int fork_dont_care() {
|
||||||
auto pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid < 0) {
|
|
||||||
PLOGE("fork 1");
|
if (pid < 0) PLOGE("fork 1");
|
||||||
} else if (pid == 0) {
|
else if (pid == 0) {
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) PLOGE("fork 2");
|
||||||
PLOGE("fork 2");
|
else if (pid > 0) exit(0);
|
||||||
} else if (pid > 0) {
|
} else {
|
||||||
exit(0);
|
int status;
|
||||||
}
|
waitpid(pid, &status, __WALL);
|
||||||
} else {
|
}
|
||||||
int status;
|
|
||||||
waitpid(pid, &status, __WALL);
|
return pid;
|
||||||
}
|
|
||||||
return pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_for_trace(int pid, int* status, int flags) {
|
void wait_for_trace(int pid, int *status, int flags) {
|
||||||
while (true) {
|
while (1) {
|
||||||
auto result = waitpid(pid, status, flags);
|
pid_t result = waitpid(pid, status, flags);
|
||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) continue;
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
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);
|
PLOGE("wait %d failed", pid);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
void parse_status(int status, char *buf, size_t len) {
|
||||||
snprintf(buf, len, "0x%x ", status);
|
snprintf(buf, len, "0x%x ", status);
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
if (WIFEXITED(status)) {
|
||||||
snprintf(buf + strlen(buf), len - strlen(buf), "exited with %d", WEXITSTATUS(status));
|
snprintf(buf + strlen(buf), len - strlen(buf), "exited with %d", WEXITSTATUS(status));
|
||||||
} else if (WIFSIGNALED(status)) {
|
} else if (WIFSIGNALED(status)) {
|
||||||
snprintf(buf + strlen(buf), len - strlen(buf), "signaled with %s(%d)", sigabbrev_np(WTERMSIG(status)), WTERMSIG(status));
|
snprintf(buf + strlen(buf), len - strlen(buf), "signaled with %s(%d)", sigabbrev_np(WTERMSIG(status)), WTERMSIG(status));
|
||||||
} else if (WIFSTOPPED(status)) {
|
} else if (WIFSTOPPED(status)) {
|
||||||
snprintf(buf + strlen(buf), len - strlen(buf), "stopped by ");
|
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), "signal=%s(%d),", sigabbrev_np(stop_sig), stop_sig);
|
||||||
snprintf(buf + strlen(buf), len - strlen(buf), "event=%s", parse_ptrace_event(status));
|
snprintf(buf + strlen(buf), len - strlen(buf), "event=%s", parse_ptrace_event(status));
|
||||||
} else {
|
} 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) {
|
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);
|
snprintf(path, sizeof(path), "/proc/%d/exe", pid);
|
||||||
|
|
||||||
ssize_t sz = readlink(path, buf, size);
|
ssize_t sz = readlink(path, buf, size);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#else
|
#else
|
||||||
#define LOG_TAG "zygisk-ptrace32"
|
#define LOG_TAG "zygisk-ptrace32"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
struct MapInfo {
|
struct MapInfo {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ androidComponents.onVariants { variant ->
|
|||||||
into(moduleDir)
|
into(moduleDir)
|
||||||
from("${rootProject.projectDir}/README.md")
|
from("${rootProject.projectDir}/README.md")
|
||||||
from("$projectDir/src") {
|
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"))
|
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||||
}
|
}
|
||||||
from("$projectDir/src") {
|
from("$projectDir/src") {
|
||||||
@@ -66,7 +66,7 @@ androidComponents.onVariants { variant ->
|
|||||||
}
|
}
|
||||||
from("$projectDir/src/mazoku")
|
from("$projectDir/src/mazoku")
|
||||||
from("$projectDir/src") {
|
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(
|
val tokens = mapOf(
|
||||||
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false",
|
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false",
|
||||||
"MIN_KSU_VERSION" to "$minKsuVersion",
|
"MIN_KSU_VERSION" to "$minKsuVersion",
|
||||||
@@ -158,12 +158,6 @@ androidComponents.onVariants { variant ->
|
|||||||
root.file("bin/$abi64/zygiskd").asFile
|
root.file("bin/$abi64/zygiskd").asFile
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
set.add(
|
|
||||||
Pair(
|
|
||||||
root.file("bin/zygisk-ctl").asFile,
|
|
||||||
root.file("zygisk-ctl.sh").asFile
|
|
||||||
)
|
|
||||||
)
|
|
||||||
sig.initSign(privKey)
|
sig.initSign(privKey)
|
||||||
set.forEach { it.first.sha(it.second) }
|
set.forEach { it.first.sha(it.second) }
|
||||||
val signFile = root.file(name).asFile
|
val signFile = root.file(name).asFile
|
||||||
|
|||||||
@@ -94,14 +94,12 @@ ui_print "- Extracting module files"
|
|||||||
extract "$ZIPFILE" 'module.prop' "$MODPATH"
|
extract "$ZIPFILE" 'module.prop' "$MODPATH"
|
||||||
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
|
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
|
||||||
extract "$ZIPFILE" 'service.sh' "$MODPATH"
|
extract "$ZIPFILE" 'service.sh' "$MODPATH"
|
||||||
extract "$ZIPFILE" 'zygisk-ctl.sh' "$MODPATH"
|
|
||||||
extract "$ZIPFILE" 'mazoku' "$MODPATH"
|
extract "$ZIPFILE" 'mazoku' "$MODPATH"
|
||||||
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
|
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
|
||||||
|
|
||||||
mkdir "$MODPATH/bin"
|
mkdir "$MODPATH/bin"
|
||||||
mkdir "$MODPATH/lib"
|
mkdir "$MODPATH/lib"
|
||||||
mkdir "$MODPATH/lib64"
|
mkdir "$MODPATH/lib64"
|
||||||
mv "$MODPATH/zygisk-ctl.sh" "$MODPATH/bin/zygisk-ctl"
|
|
||||||
|
|
||||||
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
|
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
|
||||||
ui_print "- Extracting x86 libraries"
|
ui_print "- Extracting x86 libraries"
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
MODDIR=${0%/*}/..
|
|
||||||
|
|
||||||
export TMP_PATH=/sbin
|
|
||||||
[ -d /sbin ] || export TMP_PATH=/debug_ramdisk
|
|
||||||
|
|
||||||
exec $MODDIR/bin/zygisk-ptrace64 ctl $*
|
|
||||||
@@ -27,6 +27,7 @@ pub enum DaemonSocketAction {
|
|||||||
PingHeartbeat,
|
PingHeartbeat,
|
||||||
RequestLogcatFd,
|
RequestLogcatFd,
|
||||||
GetProcessFlags,
|
GetProcessFlags,
|
||||||
|
GetInfo,
|
||||||
ReadModules,
|
ReadModules,
|
||||||
RequestCompanionSocket,
|
RequestCompanionSocket,
|
||||||
GetModuleDir,
|
GetModuleDir,
|
||||||
|
|||||||
@@ -268,6 +268,20 @@ fn handle_daemon_action(
|
|||||||
);
|
);
|
||||||
stream.write_u32(flags.bits())?;
|
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 => {
|
DaemonSocketAction::ReadModules => {
|
||||||
stream.write_usize(context.modules.len())?;
|
stream.write_usize(context.modules.len())?;
|
||||||
for module in context.modules.iter() {
|
for module in context.modules.iter() {
|
||||||
|
|||||||
Reference in New Issue
Block a user