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

@@ -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;
}