init-sigstop-based ptrace zygisk

This commit is contained in:
5ec1cff
2023-10-31 17:18:56 +08:00
parent c249ebe22c
commit f78c217552
16 changed files with 795 additions and 492 deletions

View File

@@ -13,4 +13,9 @@ add_library(zygisk SHARED ${INJECTOR_SRC_LIST})
target_include_directories(zygisk PRIVATE include)
target_link_libraries(zygisk cxx::cxx log common lsplt_static phmap)
aux_source_directory(ptracer PTRACER_SRC_LIST)
add_executable(libptracer.so ${PTRACER_SRC_LIST})
target_include_directories(libptracer.so PRIVATE include)
target_link_libraries(libptracer.so cxx::cxx log common)
add_subdirectory(external)

View File

@@ -284,6 +284,14 @@ DCL_HOOK_FUNC(int, pthread_attr_destroy, void *target) {
return res;
}
DCL_HOOK_FUNC(char *, strdup, const char *s) {
if (s == "com.android.internal.os.ZygoteInit"sv) {
LOGD("strdup %s\n", s);
replace_jni_methods();
}
return old_strdup(s);
}
#undef DCL_HOOK_FUNC
// -----------------------------------------------------------------
@@ -762,6 +770,7 @@ void hook_functions() {
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, unshare);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, strdup);
PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", android_log_close);
hook_commit();
@@ -770,8 +779,6 @@ void hook_functions() {
std::remove_if(plt_hook_list->begin(), plt_hook_list->end(),
[](auto &t) { return *std::get<3>(t) == nullptr;}),
plt_hook_list->end());
replace_jni_methods();
}
static void hook_unloader() {

View File

@@ -0,0 +1,22 @@
#include <cstdio>
#include <cstdlib>
#include <string_view>
#include <unistd.h>
#include "main.hpp"
#include "utils.hpp"
using namespace std::string_view_literals;
int main(int argc, char **argv) {
if (argc >= 2 && argv[1] == "prop_monitor"sv) {
if (access("/system/lib" LP_SELECT("", "64"), R_OK) == 0) prop_monitor_main();
} else if (argc >= 3 && argv[1] == "trace-zygote"sv) {
auto pid = strtol(argv[2], nullptr, 0);
trace_zygote_main(pid);
} else {
if (argc >= 2) LOGE("unknown command %s", argv[1]);
else LOGE("no command specified");
}
return 0;
}

View File

@@ -0,0 +1,4 @@
#pragma once
void prop_monitor_main();
void trace_zygote_main(int pid);

View File

@@ -0,0 +1,37 @@
#include <sys/system_properties.h>
#include <unistd.h>
#include "main.hpp"
#include "utils.hpp"
void prop_monitor_main() {
LOGI("prop monitor started");
// if service is not running, pid = ""
auto name = "init.svc_debug_pid." LP_SELECT("zygote_secondary", "zygote"); // argv[1];
LOGI("start monitoring %s", name);
auto prop = __system_property_find(name);
if (prop == nullptr) {
__system_property_set(name, "");
prop = __system_property_find(name);
if (prop == nullptr) {
LOGE("failed to create prop");
exit(1);
}
}
char val[PROP_VALUE_MAX];
uint32_t new_serial = 0;
while (true) {
__system_property_wait(prop, new_serial, &new_serial, nullptr);
__system_property_get(name, val);
LOGD("%s(%u): %s\n", name, new_serial, val);
auto pid = strtol(val, nullptr, 0);
if (pid != 0) {
LOGD("start ptrace %ld", pid);
if (fork_dont_care() == 0) {
execl("/proc/self/exe", "zygisk-ptracer", "trace-zygote", val, nullptr);
PLOGE("failed to exec");
exit(1);
}
}
}
}

View File

@@ -0,0 +1,200 @@
#include <sys/ptrace.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/auxv.h>
#include <elf.h>
#include <link.h>
#include <vector>
#include <string>
#include <sys/mman.h>
#include <sys/wait.h>
#include <cstdlib>
#include <cstdio>
#include <dlfcn.h>
#include <signal.h>
#include <sys/system_properties.h>
#include <string>
#include "utils.hpp"
bool inject_on_main(int pid, const char *lib_path) {
// 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 = reinterpret_cast<uintptr_t *>(regs.REG_SP);
LOGD("kernel argument %p %s", arg, get_addr_mem_region(map, arg).c_str());
int argc;
auto argv = reinterpret_cast<char **>(reinterpret_cast<uintptr_t *>(arg) + 1);
LOGD("argv %p", argv);
read_proc(pid, arg, &argc, sizeof(argc));
LOGD("argc %d", argc);
auto envp = argv + argc + 1;
LOGD("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);
LOGD("auxv %p %s", auxv, get_addr_mem_region(map, auxv).c_str());
auto v = auxv;
void *entry_addr = nullptr;
void *addr_of_entry_addr = nullptr;
while (true) {
ElfW(auxv_t) buf;
read_proc(pid, (uintptr_t *) v, &buf, sizeof(buf));
if (buf.a_type == AT_ENTRY) {
entry_addr = reinterpret_cast<void *>(buf.a_un.a_val);
addr_of_entry_addr = reinterpret_cast<char *>(v) + offsetof(ElfW(auxv_t), a_un);
LOGD("entry address %p %s (v=%p, entry_addr=%p)", entry_addr,
get_addr_mem_region(map, entry_addr).c_str(), v, addr_of_entry_addr);
break;
}
if (buf.a_type == AT_NULL) break;
v++;
}
if (entry_addr == nullptr) {
LOGE("failed to get entry");
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;
if (waitpid(pid, &status, __WALL) == -1) {
PLOGE("wait");
return false;
}
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) {
if (!get_regs(pid, regs)) return false;
if (regs.REG_IP != break_addr) {
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));
map = MapInfo::Scan(std::to_string(pid));
auto local_map = MapInfo::Scan();
auto libc_base = find_module_base(map, "libc.so");
// 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_base, args);
LOGD("remote handle %p", (void *) remote_handle);
if (remote_handle == 0) {
LOGE("handle is null");
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_base, args);
LOGD("injector entry %p", (void*) injector_entry);
if (injector_entry == 0) {
LOGE("injector entry is null");
return false;
}
// call injector entry(handle)
args.clear();
args.push_back(remote_handle);
remote_call(pid, regs, injector_entry, (uintptr_t) libc_base, 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;
/*
ptrace(PTRACE_CONT, pid, 0, 0);
waitpid(pid, &status, __WALL);
if (WIFSTOPPED(status)) {
siginfo_t siginfo;
ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo);
LOGD("process stopped by signal %d %s si_code=%d si_addr=%p", WSTOPSIG(status),
strsignal(WSTOPSIG(status)), siginfo.si_code, siginfo.si_addr);
pause();
} else {
LOGD("other reason %d", status);
}*/
} else {
LOGE("stopped by other reason: %d", status);
}
return false;
}
#define STOPPED_WITH(sig, event) (WIFSTOPPED(status) && WSTOPSIG(status) == (sig) && (status >> 16) == (event))
void trace_zygote_main(int pid) {
int status;
LOGI("tracing %d (tracer %d)", pid, getpid());
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXEC) == -1) {
PLOGE("seize");
return;
}
wait_pid(pid, &status, __WALL);
if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) {
// if SIGSTOP is delivered before we seized it
LOGD("process is already stopped");
kill(pid, SIGCONT);
ptrace(PTRACE_CONT, pid, 0, 0);
wait_pid(pid, &status, __WALL);
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP)) {
ptrace(PTRACE_CONT, pid, 0, 0);
wait_pid(pid, &status, __WALL);
if (STOPPED_WITH(SIGCONT, 0)) {
LOGD("received SIGCONT");
ptrace(PTRACE_CONT, pid, 0, 0);
}
} else {
LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", parse_status(status).c_str());
}
} else if (STOPPED_WITH(SIGSTOP, 0)) {
// if SIGSTOP is delivered after we seized it
LOGD("process received SIGSTOP, suppress");
ptrace(PTRACE_CONT, pid, 0, 0);
} else {
LOGE("unknown state %s, neither EVENT_STOP nor SIGSTOP", parse_status(status).c_str());
exit(1);
}
wait_pid(pid, &status, __WALL);
// enter the app_process
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_EXEC)) {
LOGD("app_process exec-ed");
if (!inject_on_main(pid, "/dev/zygisk/lib" LP_SELECT("", "64") "/libzygisk.so")) {
LOGE("failed to inject");
exit(1);
}
} else {
LOGE("unknown status %d", status);
exit(1);
}
ptrace(PTRACE_DETACH, pid, 0, 0);
}

View File

@@ -0,0 +1,362 @@
#include <vector>
#include <sys/mman.h>
#include <sys/sysmacros.h>
#include <array>
#include <cinttypes>
#include <sys/ptrace.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/auxv.h>
#include <elf.h>
#include <link.h>
#include <vector>
#include <string>
#include <sys/mman.h>
#include <sys/wait.h>
#include <cstdlib>
#include <cstdio>
#include <dlfcn.h>
#include <signal.h>
#include <sstream>
#include <ios>
#include <cstring>
#include "utils.hpp"
#include "logging.h"
std::vector<MapInfo> MapInfo::Scan(const std::string& pid) {
constexpr static auto kPermLength = 5;
constexpr static auto kMapEntry = 7;
std::vector<MapInfo> info;
std::string file_name = std::string("/proc/") + pid + "/maps";
auto maps = std::unique_ptr<FILE, decltype(&fclose)>{fopen(file_name.c_str(), "r"), &fclose};
if (maps) {
char *line = nullptr;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, maps.get())) > 0) {
line[read - 1] = '\0';
uintptr_t start = 0;
uintptr_t end = 0;
uintptr_t off = 0;
ino_t inode = 0;
unsigned int dev_major = 0;
unsigned int dev_minor = 0;
std::array<char, kPermLength> perm{'\0'};
int path_off;
if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start,
&end, perm.data(), &off, &dev_major, &dev_minor, &inode,
&path_off) != kMapEntry) {
continue;
}
while (path_off < read && isspace(line[path_off])) path_off++;
auto &ref = info.emplace_back(MapInfo{start, end, 0, perm[3] == 'p', off,
static_cast<dev_t>(makedev(dev_major, dev_minor)),
inode, line + path_off});
if (perm[0] == 'r') ref.perms |= PROT_READ;
if (perm[1] == 'w') ref.perms |= PROT_WRITE;
if (perm[2] == 'x') ref.perms |= PROT_EXEC;
}
free(line);
}
return info;
}
ssize_t write_proc(int pid, uintptr_t *remote_addr, const void *buf, size_t len) {
LOGD("write to remote addr %p size %zu", remote_addr, len);
struct iovec local{
.iov_base = (void *) buf,
.iov_len = len
};
struct iovec remote{
.iov_base = (void *) remote_addr,
.iov_len = len
};
auto l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
if (l == -1) {
PLOGE("process_vm_writev");
} else if (l != len) {
LOGW("not fully written: %zu, excepted %zu", l, len);
}
return l;
}
ssize_t read_proc(int pid, uintptr_t *remote_addr, void *buf, size_t len) {
struct iovec local{
.iov_base = (void *) buf,
.iov_len = len
};
struct iovec remote{
.iov_base = (void *) remote_addr,
.iov_len = len
};
auto l = process_vm_readv(pid, &local, 1, &remote, 1, 0);
if (l == -1) {
PLOGE("process_vm_readv");
} else if (l != len) {
LOGW("not fully read: %zu, excepted %zu", l, len);
}
return l;
}
bool get_regs(int pid, struct user_regs_struct &regs) {
#if defined(__x86_64__) || defined(__i386__)
if (ptrace(PTRACE_GETREGS, pid, 0, &regs) == -1) {
PLOGE("getregs");
return false;
}
#elif defined(__aarch64__) || defined(__arm__)
struct iovec iov = {
.iov_base = &regs,
.iov_len = sizeof(struct user_regs_struct),
};
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
PLOGE("getregs");
return false;
}
#endif
return true;
}
bool set_regs(int pid, struct user_regs_struct &regs) {
#if defined(__x86_64__) || defined(__i386__)
if (ptrace(PTRACE_SETREGS, pid, 0, &regs) == -1) {
PLOGE("setregs");
return false;
}
#elif defined(__aarch64__) || defined(__arm__)
struct iovec iov = {
.iov_base = &regs,
.iov_len = sizeof(struct user_regs_struct),
};
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
PLOGE("setregs");
return false;
}
#endif
return true;
}
std::string get_addr_mem_region(std::vector<MapInfo> &info, void *addr) {
for (auto &map: info) {
if (map.start <= (uintptr_t) addr && map.end > (uintptr_t) addr) {
auto s = std::string(map.path);
s += ' ';
s += map.perms & PROT_READ ? 'r' : '-';
s += map.perms & PROT_WRITE ? 'w' : '-';
s += map.perms & PROT_EXEC ? 'x' : '-';
return s;
}
}
return "<unknown>";
}
void *find_module_base(std::vector<MapInfo> &info, std::string_view suffix) {
for (auto &map: info) {
if (map.offset == 0 && map.path.ends_with(suffix)) {
return (void *) map.start;
}
}
return nullptr;
}
void *find_func_addr(
std::vector<MapInfo> &local_info,
std::vector<MapInfo> &remote_info,
std::string_view module,
std::string_view func) {
auto lib = dlopen(module.data(), RTLD_NOW);
if (lib == nullptr) {
LOGE("failed to open lib %s: %s", module.data(), dlerror());
return nullptr;
}
auto sym = reinterpret_cast<uint8_t *>(dlsym(lib, func.data()));
if (sym == nullptr) {
LOGE("failed to find sym %s in %s: %s", func.data(), module.data(), dlerror());
dlclose(lib);
return nullptr;
}
LOGD("sym %s: %p", func.data(), sym);
dlclose(lib);
auto local_base = reinterpret_cast<uint8_t *>(find_module_base(local_info, module));
if (local_base == nullptr) {
LOGE("failed to find local base for module %s", module.data());
return nullptr;
}
auto remote_base = reinterpret_cast<uint8_t *>(find_module_base(remote_info, module));
if (remote_base == nullptr) {
LOGE("failed to find remote base for module %s", module.data());
return nullptr;
}
LOGD("found local base %p remote base %p", local_base, remote_base);
auto addr = (sym - local_base) + remote_base;
LOGD("addr %p", addr);
return addr;
}
void align_stack(struct user_regs_struct &regs, long preserve) {
regs.REG_SP = (regs.REG_SP - preserve) & ~0xf;
}
void *push_string(int pid, struct user_regs_struct &regs, const char *str) {
auto len = strlen(str) + 1;
regs.REG_SP -= len;
align_stack(regs);
auto addr = reinterpret_cast<uintptr_t *>(regs.REG_SP);
if (!write_proc(pid, addr, str, len)) {
LOGE("failed to write string %s", str);
}
LOGD("pushed string %p", addr);
return addr;
}
uintptr_t remote_call(int pid, struct user_regs_struct &regs, uintptr_t func_addr, uintptr_t return_addr,
std::vector<long> &args) {
align_stack(regs);
LOGD("call %d args", args.size());
for (auto &a: args) {
LOGD("arg %p", (void *) a);
}
#if defined(__x86_64__)
if (args.size() >= 1) {
regs.rdi = args[0];
}
if (args.size() >= 2) {
regs.rsi = args[1];
}
if (args.size() >= 3) {
regs.rdx = args[2];
}
if (args.size() >= 4) {
regs.rcx = args[3];
}
if (args.size() >= 5) {
regs.r8 = args[4];
}
if (args.size() >= 6) {
regs.r9 = args[5];
}
if (args.size() > 6) {
auto remain = (args.size() - 6) * sizeof(long);
align_stack(regs, remain);
if (!write_proc(pid, (uintptr_t *) regs.REG_SP, args.data(), remain)) {
LOGE("failed to push arguments");
}
}
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");
}
regs.REG_IP = func_addr;
#elif defined(__i386__)
if (args.size() > 0) {
auto remain = (args.size()) * sizeof(long);
align_stack(regs, remain);
if (!write_proc(pid, (uintptr_t *)regs.REG_SP, args.data(), remain)) {
LOGE("failed to push arguments");
}
}
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");
}
regs.REG_IP = func_addr;
#elif defined(__aarch64__)
for (int i = 0; i < args.size() && i < 8; i++) {
regs.regs[i] = args[i];
}
if (args.size() > 8) {
auto remain = (args.size() - 8) * sizeof(long);
align_stack(regs, remain);
write_proc(pid, (uintptr_t *)regs.REG_SP, args.data(), remain);
}
regs.regs[30] = return_addr;
regs.REG_IP = func_addr;
#elif defined(__arm__)
for (int i = 0; i < args.size() && i < 4; i++) {
regs.uregs[i] = args[i];
}
if (args.size() > 4) {
auto remain = (args.size() - 4) * sizeof(long);
align_stack(regs, remain);
write_proc(pid, (uintptr_t *)regs.REG_SP, args.data(), remain);
}
regs.uregs[14] = return_addr;
regs.REG_IP = func_addr;
constexpr auto CPSR_T_MASK = 1lu << 5;
if ((regs.REG_IP & 1) != 0) {
regs.REG_IP = regs.REG_IP & ~1;
regs.uregs[16] = regs.uregs[16] | CPSR_T_MASK;
} else {
regs.uregs[16] = regs.uregs[16] & ~CPSR_T_MASK;
}
#endif
if (!set_regs(pid, regs)) {
LOGE("failed to set regs");
return 0;
}
ptrace(PTRACE_CONT, pid, 0, 0);
int status;
if (waitpid(pid, &status, __WALL) == -1) {
PLOGE("wait");
}
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) {
if (!get_regs(pid, regs)) {
LOGE("failed to get regs after call");
return 0;
}
if (regs.REG_IP != return_addr) {
LOGE("wrong return addr %p", (void *) regs.REG_IP);
return 0;
}
return regs.REG_RET;
} else {
LOGE("stopped by other reason %d", status);
}
return 0;
}
int fork_dont_care() {
auto pid = fork();
if (pid < 0) {
PLOGE("fork 1");
} else if (pid == 0) {
pid = fork();
if (pid < 0) {
PLOGE("fork 2");
} else if (pid > 0) {
exit(0);
}
} else {
int status;
waitpid(pid, &status, __WALL);
}
return pid;
}
int wait_pid(int pid, int* status, int flags) {
while (true) {
auto result = waitpid(pid, status, flags);
if (result == -1 && errno == EINTR) continue;
return result;
}
}
std::string parse_status(int status) {
std::ostringstream os;
os << "status " << std::hex << status;
os << std::dec << " ";
if (WIFEXITED(status)) {
os << "exited with " << WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
os << "signaled with " << sigabbrev_np(WTERMSIG(status)) << "(" << WTERMSIG(status) << ")";
} else if (WIFSTOPPED(status)) {
os << "stopped by";
auto stop_sig = WSTOPSIG(status);
os << "signal " << sigabbrev_np(stop_sig) << "(" << stop_sig << "),";
os << "event=" << parse_ptrace_event(status);
} else {
os << "unknown";
}
return os.str();
}

View File

@@ -0,0 +1,114 @@
#pragma once
#include <string>
#include <sys/ptrace.h>
#include "daemon.h"
#ifdef __LP64__
#define LOG_TAG "zygisk-ptracer64"
#else
#define LOG_TAG "zygisk-ptracer32"
#endif
#include "logging.h"
struct MapInfo {
/// \brief The start address of the memory region.
uintptr_t start;
/// \brief The end address of the memory region.
uintptr_t end;
/// \brief The permissions of the memory region. This is a bit mask of the following values:
/// - PROT_READ
/// - PROT_WRITE
/// - PROT_EXEC
uint8_t perms;
/// \brief Whether the memory region is private.
bool is_private;
/// \brief The offset of the memory region.
uintptr_t offset;
/// \brief The device number of the memory region.
/// Major can be obtained by #major()
/// Minor can be obtained by #minor()
dev_t dev;
/// \brief The inode number of the memory region.
ino_t inode;
/// \brief The path of the memory region.
std::string path;
/// \brief Scans /proc/self/maps and returns a list of \ref MapInfo entries.
/// This is useful to find out the inode of the library to hook.
/// \return A list of \ref MapInfo entries.
[[maybe_unused, gnu::visibility("default")]] static std::vector<MapInfo> Scan(const std::string& pid = "self");
};
#if defined(__x86_64__)
#define REG_SP rsp
#define REG_IP rip
#define REG_RET rax
#elif defined(__i386__)
#define REG_SP esp
#define REG_IP eip
#define REG_RET eax
#elif defined(__aarch64__)
#define REG_SP sp
#define REG_IP pc
#define REG_RET regs[0]
#elif defined(__arm__)
#define REG_SP uregs[13]
#define REG_IP uregs[15]
#define REG_RET uregs[0]
#define user_regs_struct user_regs
#endif
ssize_t write_proc(int pid, uintptr_t *remote_addr, const void *buf, size_t len);
ssize_t read_proc(int pid, uintptr_t *remote_addr, void *buf, size_t len);
bool get_regs(int pid, struct user_regs_struct &regs);
bool set_regs(int pid, struct user_regs_struct &regs);
std::string get_addr_mem_region(std::vector<MapInfo> &info, void *addr);
void *find_module_base(std::vector<MapInfo> &info, std::string_view suffix);
void *find_func_addr(
std::vector<MapInfo> &local_info,
std::vector<MapInfo> &remote_info,
std::string_view module,
std::string_view func);
void align_stack(struct user_regs_struct &regs, long preserve = 0);
void *push_string(int pid, struct user_regs_struct &regs, const char *str);
uintptr_t remote_call(int pid, struct user_regs_struct &regs, uintptr_t func_addr, uintptr_t return_addr,
std::vector<long> &args);
int fork_dont_care();
int wait_pid(int pid, int* status, int flags);
std::string parse_status(int status);
#define CASE_CONST_RETURN(x) case x: return #x;
inline const char* parse_ptrace_event(int status) {
status = status >> 16;
switch (status) {
CASE_CONST_RETURN(PTRACE_EVENT_FORK)
CASE_CONST_RETURN(PTRACE_EVENT_VFORK)
CASE_CONST_RETURN(PTRACE_EVENT_CLONE)
CASE_CONST_RETURN(PTRACE_EVENT_EXEC)
CASE_CONST_RETURN(PTRACE_EVENT_VFORK_DONE)
CASE_CONST_RETURN(PTRACE_EVENT_EXIT)
CASE_CONST_RETURN(PTRACE_EVENT_SECCOMP)
CASE_CONST_RETURN(PTRACE_EVENT_STOP)
default:
return "(no event)";
}
}
inline const char* sigabbrev_np(int sig) {
if (sig > 0 && sig < NSIG) return sys_signame[sig];
return "(unknown)";
}