works now

This commit is contained in:
5ec1cff
2024-07-11 14:53:13 +08:00
parent f0eb0f2d92
commit d55dc13e87
31 changed files with 2084 additions and 23 deletions

View File

@@ -57,7 +57,7 @@ androidComponents.onVariants { variant ->
val prepareModuleFilesTask = task<Sync>("prepareModuleFiles$variantCapped") {
group = "module"
dependsOn("assemble$variantCapped")
dependsOn("assemble$variantCapped", ":service:assemble$variantCapped")
into(moduleDir)
from(rootProject.layout.projectDirectory.file("README.md"))
from(layout.projectDirectory.file("template")) {
@@ -83,7 +83,11 @@ androidComponents.onVariants { variant ->
filter<ReplaceTokens>("tokens" to tokens)
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
}
from(project(":service").layout.buildDirectory.file("outputs/apk/$variantLowered/service-$variantLowered.apk")) {
rename { "service.apk" }
}
from(layout.buildDirectory.file("intermediates/stripped_native_libs/$variantLowered/strip${variantCapped}DebugSymbols/out/lib")) {
exclude("**/libbinder.so", "**/libutils.so")
into("lib")
}

View File

@@ -35,5 +35,8 @@ add_library(binder SHARED binder/stub_binder.cpp)
target_include_directories(binder PUBLIC binder/include)
target_link_libraries(binder utils)
add_executable(libinject.so inject/main.cpp inject/utils.cpp)
target_link_libraries(libinject.so lspmparser my_logging)
add_library(${MODULE_NAME} SHARED binder_interceptor.cpp)
target_link_libraries(${MODULE_NAME} log binder utils dobby elf_util my_logging)

View File

@@ -62,7 +62,7 @@ CREATE_MEM_HOOK_STUB_ENTRY(
{
LOGD("transact: binder=%p code=%d", thiz, code);
if (IPCThreadState::self()->getCallingUid() == 0 && reply != nullptr &&
thiz != gBinderInterceptor) {
thiz != gBinderInterceptor) [[unlikely]] {
if (code == 0xdeadbeef) {
LOGD("request binder interceptor");
reply->writeStrongBinder(gBinderInterceptor);
@@ -128,6 +128,7 @@ BinderInterceptor::onTransact(uint32_t code, const android::Parcel &data, androi
items.erase(it);
return OK;
}
return BAD_VALUE;
}
}
return UNKNOWN_TRANSACTION;

View File

@@ -0,0 +1,298 @@
#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 <sys/socket.h>
#include <sys/un.h>
#include <android/dlext.h>
#include <fcntl.h>
#include <csignal>
#include <sys/system_properties.h>
#include <string>
#include <cinttypes>
#include "lsplt.hpp"
#include "logging.hpp"
#include "utils.hpp"
using namespace std::string_literals;
// zygote inject
bool inject_library(int pid, const char *lib_path, const char* entry_name) {
LOGI("injecting %s and calling %s in %d", lib_path, entry_name, pid);
struct user_regs_struct regs{}, backup{};
std::vector<lsplt::MapInfo> map;
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
PLOGE("attach");
}
int status;
{
wait_for_trace(pid, &status, __WALL);
}
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) {
if (!get_regs(pid, regs)) return false;
// The linker has been initialized now, we can do dlopen
LOGD("stopped at entry");
// backup registers
memcpy(&backup, &regs, sizeof(regs));
{
map = lsplt::MapInfo::Scan(std::to_string(pid));
}
auto local_map = lsplt::MapInfo::Scan();
auto libc_return_addr = find_module_return_addr(map, "libc.so");
LOGD("libc return addr %p", libc_return_addr);
std::vector<uintptr_t> args;
uintptr_t str, remote_handle, injector_entry;
auto close_addr = find_func_addr(local_map, map, "libc.so", "close");
if (!close_addr) return false;
int lib_fd = -1;
// prepare fd
{
set_sockcreate_con("u:object_r:system_file:s0");
UniqueFd local_socket = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (local_socket == -1) {
PLOGE("create local socket");
return false;
}
if (setfilecon(lib_path, "u:object_r:system_file:s0") == -1) {
PLOGE("set context of lib");
}
UniqueFd local_lib_fd = open(lib_path, O_RDONLY | O_CLOEXEC);
if (local_lib_fd == -1) {
PLOGE("open lib");
return false;
}
auto socket_addr = find_func_addr(local_map, map, "libc.so", "socket");
auto bind_addr = find_func_addr(local_map, map, "libc.so", "bind");
auto recvmsg_addr = find_func_addr(local_map, map, "libc.so", "recvmsg");
auto errno_addr = find_func_addr(local_map, map, "libc.so", "__errno");
auto get_remote_errno = [&]() -> int {
if (!errno_addr) {
LOGE("could not get errno!");
return 0;
}
auto addr = remote_call(pid, regs, (uintptr_t) errno_addr, 0, args);
int err = 0;
if (!read_proc(pid, addr, &err, sizeof(err))) return 0;
return err;
};
if (!socket_addr || !bind_addr || !recvmsg_addr) return false;
args.clear();
args.push_back(AF_UNIX);
args.push_back(SOCK_DGRAM | SOCK_CLOEXEC);
args.push_back(0);
int remote_fd = (int) remote_call(pid, regs, (uintptr_t) socket_addr, 0, args);
if (remote_fd == -1) {
errno = get_remote_errno();
PLOGE("remote socket");
return false;
}
auto close_remote = [&](int fd) -> void {
args.clear();
args.push_back(fd);
if (remote_call(pid, regs, (uintptr_t) close_addr, 0, args) != 0) {
LOGE("remote not closed: %d", fd);
}
};
auto magic = generateMagic(16);
struct sockaddr_un addr{
.sun_family = AF_UNIX,
.sun_path = {0}
};
LOGD("socket name %s", magic.c_str());
memcpy(addr.sun_path + 1, magic.c_str(), magic.size());
socklen_t len = sizeof(addr.sun_family) + 1 + magic.size();
auto remote_addr = push_memory(pid, regs, &addr, sizeof(addr));
args.clear();
args.push_back(remote_fd);
args.push_back(remote_addr);
args.push_back(len);
auto bind_result = remote_call(pid, regs, (uintptr_t) bind_addr, 0, args);
if (bind_result == (uintptr_t) -1) {
errno = get_remote_errno();
PLOGE("remote bind");
close_remote(remote_fd);
return false;
}
char cmsgbuf[CMSG_SPACE(sizeof(int))] = {0};
auto remote_cmsgbuf = push_memory(pid, regs, &cmsgbuf, sizeof(cmsgbuf));
struct msghdr hdr{};
hdr.msg_control = (void*) remote_cmsgbuf;
hdr.msg_controllen = sizeof(cmsgbuf);
auto remote_hdr = push_memory(pid, regs, &hdr, sizeof(hdr));
args.clear();
args.push_back(remote_fd);
args.push_back(remote_hdr);
args.push_back(MSG_WAITALL);
if (!remote_pre_call(pid, regs, (uintptr_t) recvmsg_addr, 0, args)) {
// we can't do anything more
LOGE("pre call remote recvmsg");
return false;
}
hdr.msg_control = &cmsgbuf;
hdr.msg_name = &addr;
hdr.msg_namelen = len;
{
cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = AF_UNIX;
*(int *) CMSG_DATA(cmsg) = local_lib_fd;
}
if (sendmsg(local_socket, &hdr, 0) == -1) {
PLOGE("send to remote");
close_remote(remote_fd);
return false;
}
auto recvmsg_result = (ssize_t) remote_post_call(pid, regs, 0);
if (recvmsg_result == -1) {
errno = get_remote_errno();
PLOGE("post call recvmsg");
close_remote(remote_fd);
return false;
}
if (read_proc(pid, remote_cmsgbuf, &cmsgbuf, sizeof(cmsgbuf)) != sizeof(cmsgbuf)) {
LOGE("failed to read proc");
close_remote(remote_fd);
return false;
}
cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
if (cmsg == nullptr || cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
LOGE("remote recv fd failed!");
close_remote(remote_fd);
return false;
}
lib_fd = *(int*) CMSG_DATA(cmsg);
LOGD("remote lib fd: %d", lib_fd);
close_remote(remote_fd);
}
// call dlopen
{
auto dlopen_addr = find_func_addr(local_map, map, "libdl.so", "android_dlopen_ext");
if (dlopen_addr == nullptr) return false;
android_dlextinfo info{};
info.flags = ANDROID_DLEXT_USE_LIBRARY_FD;
info.library_fd = lib_fd;
uintptr_t remote_info = push_memory(pid, regs, &info, sizeof(info));
str = push_string(pid, regs, lib_path);
args.clear();
args.push_back((long) str);
args.push_back((long) RTLD_NOW);
args.push_back(remote_info);
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;
}
args.clear();
args.push_back(lib_fd);
if (remote_call(pid, regs, (uintptr_t) close_addr, 0, args) != 0) {
LOGE("remote lib not closed: %d", lib_fd);
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);
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)
{
args.clear();
args.push_back(remote_handle);
remote_call(pid, regs, injector_entry, (uintptr_t) libc_return_addr, args);
}
LOGD("restore context");
// restore registers
if (!set_regs(pid, backup)) return false;
if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
PLOGE("failed to detach");
return false;
}
return true;
} else {
LOGE("stopped by other reason: %s", parse_status(status).c_str());
}
return false;
}
int main(int argc, char **argv) {
logging::setPrintEnabled(true);
auto pid = strtol(argv[1], nullptr, 0);
char buf[4096];
realpath(argv[2], buf);
return !inject_library(pid, buf, argv[3]);
}

View File

@@ -0,0 +1,696 @@
#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 <csignal>
#include <cstring>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <cinttypes>
#include <sys/xattr.h>
#include <random>
#include "utils.hpp"
#include "logging.hpp"
#include <sched.h>
#include <fcntl.h>
bool switch_mnt_ns(int pid, int *fd) {
int nsfd, old_nsfd = -1;
std::string path;
if (pid == 0) {
if (fd != nullptr) {
nsfd = *fd;
*fd = -1;
} else return false;
path = "/proc/self/fd/";
path += std::to_string(nsfd);
} else {
if (fd != nullptr) {
old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
if (old_nsfd == -1) {
PLOGE("get old nsfd");
return false;
}
*fd = old_nsfd;
}
path = std::string("/proc/") + std::to_string(pid) + "/ns/mnt";
nsfd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (nsfd == -1) {
PLOGE("open nsfd %s", path.c_str());
close(old_nsfd);
return false;
}
}
if (setns(nsfd, CLONE_NEWNS) == -1) {
PLOGE("set ns to %s", path.c_str());
close(nsfd);
close(old_nsfd);
return false;
}
close(nsfd);
return true;
}
ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len, bool use_proc_mem) {
LOGV("write to %d addr %" PRIxPTR " size %zu use_proc_mem=%d", pid, remote_addr, len, use_proc_mem);
if (use_proc_mem) {
char path[64];
snprintf(path, sizeof(path), "/proc/%d/mem", pid);
auto fd = open(path, O_WRONLY | O_CLOEXEC);
if (fd == -1) {
PLOGE("open proc mem");
return -1;
}
auto l = pwrite(fd, buf, len, remote_addr);
if (l == -1) {
PLOGE("pwrite");
} else if (static_cast<size_t>(l) != len) {
LOGW("not fully written: %zu, excepted %zu", l, len);
}
return l;
} else {
struct iovec local{
.iov_base = (void *) buf,
.iov_len = len
};
struct iovec remote{
.iov_base = (void *) remote_addr,
.iov_len = len
};
auto l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
if (l == -1) {
PLOGE("process_vm_writev");
} else if (static_cast<size_t>(l) != len) {
LOGW("not fully written: %zu, excepted %zu", l, len);
}
return l;
}
}
ssize_t read_proc(int pid, uintptr_t remote_addr, void *buf, size_t len) {
struct iovec local{
.iov_base = (void *) buf,
.iov_len = len
};
struct iovec remote{
.iov_base = (void *) remote_addr,
.iov_len = len
};
auto l = process_vm_readv(pid, &local, 1, &remote, 1, 0);
if (l == -1) {
PLOGE("process_vm_readv");
} else if (static_cast<size_t>(l) != len) {
LOGW("not fully read: %zu, excepted %zu", l, len);
}
return l;
}
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<lsplt::MapInfo> &info, uintptr_t addr) {
for (auto &map: info) {
if (map.start <= addr && map.end > addr) {
auto s = std::string(map.path);
s += ' ';
s += map.perms & PROT_READ ? 'r' : '-';
s += map.perms & PROT_WRITE ? 'w' : '-';
s += map.perms & PROT_EXEC ? 'x' : '-';
return s;
}
}
return "<unknown>";
}
void *find_module_return_addr(std::vector<lsplt::MapInfo> &info, std::string_view suffix) {
for (auto &map: info) {
if ((map.perms & PROT_EXEC) == 0 && map.path.ends_with(suffix)) {
return (void *) map.start;
}
}
return nullptr;
}
void *find_module_base(std::vector<lsplt::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<lsplt::MapInfo> &local_info,
std::vector<lsplt::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, uintptr_t preserve) {
regs.REG_SP = (regs.REG_SP - preserve) & ~0xf;
}
uintptr_t push_memory(int pid, struct user_regs_struct &regs, void* local_addr, size_t len) {
regs.REG_SP -= len;
align_stack(regs);
auto addr = static_cast<uintptr_t>(regs.REG_SP);
if (!write_proc(pid, addr, local_addr, len)) {
LOGE("failed to write mem %p+%zu", local_addr, len);
}
LOGD("pushed mem %" PRIxPTR, addr);
return addr;
}
uintptr_t push_string(int pid, struct user_regs_struct &regs, const char *str) {
auto len = strlen(str) + 1;
regs.REG_SP -= len;
align_stack(regs);
auto addr = static_cast<uintptr_t>(regs.REG_SP);
if (!write_proc(pid, addr, str, len)) {
LOGE("failed to write string %s", str);
}
LOGD("pushed string %" PRIxPTR, addr);
return addr;
}
bool remote_pre_call(int pid, struct user_regs_struct &regs, uintptr_t func_addr, uintptr_t return_addr,
std::vector<uintptr_t> &args) {
align_stack(regs);
LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size());
for (auto &a: args) {
LOGV("arg %p", (void *) a);
}
#if defined(__x86_64__)
if (args.size() >= 1) {
regs.rdi = args[0];
}
if (args.size() >= 2) {
regs.rsi = args[1];
}
if (args.size() >= 3) {
regs.rdx = args[2];
}
if (args.size() >= 4) {
regs.rcx = args[3];
}
if (args.size() >= 5) {
regs.r8 = args[4];
}
if (args.size() >= 6) {
regs.r9 = args[5];
}
if (args.size() > 6) {
auto remain = (args.size() - 6) * sizeof(uintptr_t);
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(uintptr_t);
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(uintptr_t);
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(uintptr_t);
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 (size_t i = 0; i < args.size() && i < 8; i++) {
regs.regs[i] = args[i];
}
if (args.size() > 8) {
auto remain = (args.size() - 8) * sizeof(uintptr_t);
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 (size_t i = 0; i < args.size() && i < 4; i++) {
regs.uregs[i] = args[i];
}
if (args.size() > 4) {
auto remain = (args.size() - 4) * sizeof(uintptr_t);
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 false;
}
return ptrace(PTRACE_CONT, pid, 0, 0) == 0;
}
uintptr_t remote_post_call(int pid, struct user_regs_struct &regs, uintptr_t return_addr) {
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);
siginfo_t siginfo;
if (ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo) == -1) {
PLOGE("failed to get siginfo");
} else {
LOGE("si_code=%d si_addr=%p", siginfo.si_code, siginfo.si_addr);
}
return 0;
}
return regs.REG_RET;
} else {
LOGE("stopped by other reason %s at addr %p", parse_status(status).c_str(), (void*) regs.REG_IP);
}
return 0;
}
uintptr_t remote_call(int pid, struct user_regs_struct &regs, uintptr_t func_addr, uintptr_t return_addr,
std::vector<uintptr_t> &args) {
if (!remote_pre_call(pid, regs, func_addr, return_addr, args)) return 0;
return remote_post_call(pid, regs, return_addr);
}
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;
}
bool wait_for_trace(int pid, int* status, int flags) {
while (true) {
auto result = waitpid(pid, status, flags);
if (result == -1) {
if (errno == EINTR) {
continue;
} else {
PLOGE("wait %d failed", pid);
return false;
}
}
if (!WIFSTOPPED(*status)) {
LOGE("process %d not stopped for trace: %s, exit", pid, parse_status(*status).c_str());
return false;
}
return true;
}
}
std::string parse_status(int status) {
char buf[64];
if (WIFEXITED(status)) {
snprintf(buf, sizeof(buf), "0x%x exited with %d", status, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
snprintf(buf, sizeof(buf), "0x%x signaled with %s(%d)", status, sigabbrev_np(WTERMSIG(status)), WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
auto stop_sig = WSTOPSIG(status);
snprintf(buf, sizeof(buf), "0x%x stopped by signal=%s(%d),event=%s", status, sigabbrev_np(stop_sig), stop_sig, parse_ptrace_event(status));
} else {
snprintf(buf, sizeof(buf), "0x%x unknown", status);
}
return buf;
}
std::string get_program(int pid) {
std::string path = "/proc/";
path += std::to_string(pid);
path += "/exe";
constexpr const auto SIZE = 256;
char buf[SIZE + 1];
auto sz = readlink(path.c_str(), buf, SIZE);
if (sz == -1) {
PLOGE("readlink /proc/%d/exe", pid);
return "";
}
buf[sz] = 0;
return buf;
}
std::string parse_exec(int pid) {
struct user_regs_struct regs;
if (!get_regs(pid, regs)) return "";
auto nr = regs.REG_NR;
if (nr != SYS_execve) {
// LOGI("syscall %ld != %d", nr, SYS_execve);
return "";
}
auto file_name_ptr = regs.REG_SYS_ARG0;
char buf[128];
auto sz = read_proc(pid, file_name_ptr, buf, sizeof(buf));
if (sz < 0) return "";
for (auto i = 0; i < sz; i++) {
if (buf[i] == 0) {
LOGD("exec len %d prog %s", i, buf);
return buf;
}
}
// too long
return "";
}
bool skip_syscall(int pid) {
struct user_regs_struct regs;
if (!get_regs(pid, regs)) return false;
#if defined(__aarch64__)
// https://stackoverflow.com/questions/63620203/ptrace-change-syscall-number-arm64
int syscallno = 0xffff;
struct iovec iov = {
.iov_base = &syscallno,
.iov_len = sizeof (int),
};
if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL, &iov) == -1) {
PLOGE("failed to set syscall");
}
#elif defined(__arm__)
if (ptrace(PTRACE_SET_SYSCALL, pid, 0, 0xffff) == -1) {
PLOGE("failed to set syscall");
}
#else
auto orig_nr = regs.REG_NR;
regs.REG_NR = 0xffff;
if (!set_regs(pid, regs)) return false;
regs.REG_NR = orig_nr;
#endif
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) {
PLOGE("failed to singlestep");
return false;
}
int status;
waitpid(pid, &status, __WALL);
#if defined(__x86_64__)
regs.REG_IP -= 2;
regs.rax = orig_nr;
#elif defined(__i386__)
regs.REG_IP -= 2;
regs.eax = orig_nr;
#elif defined(__aarch64__)
regs.REG_IP -= 4;
#elif defined(__arm__)
regs.REG_IP -= (regs.REG_IP % 2) ? 2 : 4;
#endif
return set_regs(pid, regs);
}
void wait_for_syscall(int pid) {
int status;
for (;;) {
if (!wait_for_trace(pid, &status, __WALL)) {
LOGE("could not wait for trace");
exit(1);
}
if (WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) {
LOGV("!! stopped at syscall");
break;
}
LOGV("! not syscall: %s", parse_status(status).c_str());
if (ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status)) == -1) {
PLOGE("failed to cont");
exit(1);
}
}
}
bool do_syscall(int pid, uintptr_t &ret, int nr, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5) {
#if defined(__i386__)
LOGE("do_syscall is unsupported on i386!");
return false;
#else
struct user_regs_struct regs, backup_regs;
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) {
PLOGE("failed to singlestep");
return false;
}
wait_for_syscall(pid);
if (!get_regs(pid, regs)) return false;
memcpy(&backup_regs, &regs, sizeof(struct user_regs_struct));
// set syscall nr and args
#if defined(__aarch64__)
// https://stackoverflow.com/questions/63620203/ptrace-change-syscall-number-arm64
int syscallno = nr;
struct iovec iov = {
.iov_base = &syscallno,
.iov_len = sizeof (int),
};
if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL, &iov) == -1) {
PLOGE("failed to set syscall");
return false;
}
regs.regs[0] = arg0;
regs.regs[1] = arg1;
regs.regs[2] = arg2;
regs.regs[3] = arg3;
regs.regs[4] = arg4;
regs.regs[5] = arg5;
#elif defined(__arm__)
if (ptrace(PTRACE_SET_SYSCALL, pid, 0, nr) == -1) {
PLOGE("failed to set syscall");
return false;
}
regs.uregs[0] = arg0;
regs.uregs[1] = arg1;
regs.uregs[2] = arg2;
regs.uregs[3] = arg3;
regs.uregs[4] = arg4;
regs.uregs[5] = arg5;
#elif defined(__x86_64__)
auto orig_nr = regs.REG_NR;
regs.REG_NR = nr;
regs.rdi = arg0;
regs.rsi = arg1;
regs.rdx = arg2;
regs.r10 = arg3;
regs.r8 = arg4;
regs.r9 = arg5;
#endif
if (!set_regs(pid, regs)) return false;
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) {
PLOGE("failed to singlestep");
return false;
}
wait_for_syscall(pid);
if (!get_regs(pid, regs)) return false;
// go back to last instruction
// on x86, we should fill the nr register with orig_nr
#if defined(__x86_64__)
ret = regs.rax;
backup_regs.REG_IP -= 2;
backup_regs.rax = orig_nr;
#elif defined(__aarch64__)
ret = regs.regs[0];
backup_regs.REG_IP -= 4;
#elif defined(__arm__)
ret = regs.uregs[0];
backup_regs.REG_IP -= (regs.REG_IP % 2) ? 2 : 4;
#endif
return set_regs(pid, backup_regs);
#endif // i386
}
uintptr_t remote_syscall(int pid, uintptr_t &ret, int nr, uintptr_t arg0 = 0, uintptr_t arg1 = 0, uintptr_t arg2 = 0, uintptr_t arg3 = 0, uintptr_t arg4 = 0, uintptr_t arg5 = 0) {
if (!do_syscall(pid, ret, nr, arg0, arg1, arg2, arg3, arg4, arg5)) {
LOGE("do remote syscall %d failed", nr);
return -1;
}
if (SYSCALL_IS_ERR(ret)) {
LOGE("do remote syscall %d return error %d %s", nr, SYSCALL_ERR(ret), strerror(SYSCALL_ERR(ret)));
return -1;
}
return ret;
}
uintptr_t remote_mmap(int pid, uintptr_t addr, size_t size, int prot, int flags, int fd, off_t offset) {
uintptr_t result;
LOGD("remote mmap %" PRIxPTR " size %zu fd %d off %lu", addr, size, fd, offset);
if (!remote_syscall(pid, result, SYS_mmap, addr, size, prot, flags, fd, offset)) {
LOGE("do remote mmap failed");
return -1;
}
LOGD("remote mmap get %" PRIxPTR, result);
return result;
}
bool remote_munmap(int pid, uintptr_t addr, size_t size) {
uintptr_t result = 0;
if (remote_syscall(pid, result, SYS_munmap, addr, size)) {
LOGE("do remote munmap failed");
return false;
}
return true;
}
int remote_open(int pid, uintptr_t path_addr, int flags) {
uintptr_t result;
if (!remote_syscall(pid, result, SYS_openat, AT_FDCWD, path_addr, flags)) {
LOGE("remote open failed");
return -1;
}
return result;
}
bool remote_close(int pid, int fd) {
uintptr_t result = 0;
if (remote_syscall(pid, result, SYS_close, fd)) {
LOGE("remote close failed");
return false;
}
return true;
}
int wait_for_child(int pid) {
int status;
for (;;) {
if (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR) continue;
PLOGE("waitpid %d", pid);
return -1;
} else {
return status;
}
}
}
std::string generateMagic(size_t len) {
constexpr const char chrs[] = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::mt19937 rg{std::random_device{}()};
std::uniform_int_distribution<std::string::size_type> pick(0, sizeof(chrs) - 2);
std::string s;
s.reserve(len);
while(len--) s += chrs[pick(rg)];
return s;
}
int setfilecon(const char* path, const char* con) {
return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, con, strlen(con) + 1, 0);
}
bool set_sockcreate_con(const char* con) {
auto sz = static_cast<ssize_t>(strlen(con) + 1);
UniqueFd fd = open("/proc/thread-self/attr/sockcreate", O_WRONLY | O_CLOEXEC);
if (fd == -1 || write(fd, con, sz) != sz) {
PLOGE("set socket con");
char buf[128];
snprintf(buf, sizeof(buf), "/proc/%d/attr/sockcreate", gettid());
fd = open(buf, O_WRONLY | O_CLOEXEC);
if (fd == -1 || write(fd, con, sz) != sz) {
PLOGE("set socket con fallback");
return false;
}
}
return true;
}

View File

@@ -0,0 +1,166 @@
#pragma once
#include <string>
#include <sys/ptrace.h>
#include <map>
#include "lsplt.hpp"
#define LOG_TAG "TrickyStore"
#define SYSCALL_IS_ERR(e) (((unsigned long) e) > -4096UL)
#define SYSCALL_ERR(e) (-(int)(e))
#if defined(__x86_64__)
#define REG_SP rsp
#define REG_IP rip
#define REG_RET rax
#define REG_NR orig_rax
#define REG_SYS_ARG0 rdi
#elif defined(__i386__)
#define REG_SP esp
#define REG_IP eip
#define REG_RET eax
#define REG_NR orig_eax
#define REG_SYS_ARG0 ebx
#elif defined(__aarch64__)
#define REG_SP sp
#define REG_IP pc
#define REG_RET regs[0]
#define REG_NR regs[8]
#define REG_SYS_ARG0 regs[0]
#elif defined(__arm__)
#define REG_SP uregs[13]
#define REG_IP uregs[15]
#define REG_RET uregs[0]
#define REG_NR uregs[7]
#define REG_SYS_ARG0 uregs[0]
#define user_regs_struct user_regs
#define SYS_mmap SYS_mmap2
#endif
ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len, bool use_proc_mem = false);
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<lsplt::MapInfo> &info, uintptr_t addr);
void *find_module_base(std::vector<lsplt::MapInfo> &info, std::string_view suffix);
void *find_func_addr(
std::vector<lsplt::MapInfo> &local_info,
std::vector<lsplt::MapInfo> &remote_info,
std::string_view module,
std::string_view func);
void align_stack(struct user_regs_struct &regs, uintptr_t preserve = 0);
uintptr_t push_memory(int pid, struct user_regs_struct &regs, void* local_addr, size_t len);
uintptr_t 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<uintptr_t> &args);
int fork_dont_care();
bool wait_for_trace(int pid, int* status, int flags);
std::string parse_status(int status);
#define WPTEVENT(x) (x >> 16)
#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)";
}
std::string get_program(int pid);
void *find_module_return_addr(std::vector<lsplt::MapInfo> &info, std::string_view suffix);
// pid = 0, fd != nullptr -> set to fd
// pid != 0, fd != nullptr -> set to pid ns, give orig ns in fd
bool switch_mnt_ns(int pid, int *fd);
std::vector<std::string> get_cmdline(int pid);
std::string parse_exec(int pid);
bool skip_syscall(int pid);
bool do_syscall(int pid, uintptr_t &ret, int nr, uintptr_t arg0 = 0, uintptr_t arg1 = 0, uintptr_t arg2 = 0, uintptr_t arg3 = 0, uintptr_t arg4 = 0, uintptr_t arg5 = 0);
uintptr_t remote_mmap(int pid, uintptr_t addr, size_t size, int prot, int flags, int fd, off_t offset);
bool remote_munmap(int pid, uintptr_t addr, size_t size);
int remote_open(int pid, uintptr_t path_addr, int flags);
bool remote_close(int pid, int fd);
int wait_for_child(int pid);
int get_elf_class(std::string_view path);
bool remote_pre_call(int pid, struct user_regs_struct &regs, uintptr_t func_addr, uintptr_t return_addr,
std::vector<uintptr_t> &args);
uintptr_t remote_post_call(int pid, struct user_regs_struct &regs, uintptr_t return_addr);
// magic.h
constexpr const auto kMainMagicLength = 16;
std::string generateMagic(size_t len);
// files.hpp
int setfilecon(const char* path, const char* con);
// utils.hpp
class UniqueFd {
using Fd = int;
public:
UniqueFd() = default;
UniqueFd(Fd fd) : fd_(fd) {}
~UniqueFd() { if (fd_ >= 0) close(fd_); }
// Disallow copy
UniqueFd(const UniqueFd&) = delete;
UniqueFd& operator=(const UniqueFd&) = delete;
// Allow move
UniqueFd(UniqueFd&& other) { std::swap(fd_, other.fd_); }
UniqueFd& operator=(UniqueFd&& other) {
std::swap(fd_, other.fd_);
return *this;
}
// Implict cast to Fd
operator const Fd&() const { return fd_; }
private:
Fd fd_ = -1;
};
// socket_utils.hpp
bool set_sockcreate_con(const char* con);

View File

@@ -57,29 +57,18 @@ ui_print "- Extracting module files"
extract "$ZIPFILE" 'module.prop' "$MODPATH"
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
extract "$ZIPFILE" 'service.sh' "$MODPATH"
extract "$ZIPFILE" 'service.apk' "$MODPATH"
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
HAS32BIT=false && [ $(getprop ro.product.cpu.abilist32) ] && HAS32BIT=true
mkdir "$MODPATH/zygisk"
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
if [ "$HAS32BIT" = true ]; then
ui_print "- Extracting x86 libraries"
extract "$ZIPFILE" "lib/x86/lib$SONAME.so" "$MODPATH/zygisk/" true
mv "$MODPATH/zygisk/lib$SONAME.so" "$MODPATH/zygisk/x86.so"
fi
if [ "$ARCH" = "x64" ]; then
ui_print "- Extracting x64 libraries"
extract "$ZIPFILE" "lib/x86_64/lib$SONAME.so" "$MODPATH/zygisk" true
mv "$MODPATH/zygisk/lib$SONAME.so" "$MODPATH/zygisk/x86_64.so"
extract "$ZIPFILE" "lib/x86_64/lib$SONAME.so" "$MODPATH" true
extract "$ZIPFILE" "lib/x86_64/libinject.so" "$MODPATH" true
else
if [ "$HAS32BIT" = true ]; then
extract "$ZIPFILE" "lib/armeabi-v7a/lib$SONAME.so" "$MODPATH/zygisk" true
mv "$MODPATH/zygisk/lib$SONAME.so" "$MODPATH/zygisk/armeabi-v7a.so"
fi
ui_print "- Extracting arm64 libraries"
extract "$ZIPFILE" "lib/arm64-v8a/lib$SONAME.so" "$MODPATH/zygisk" true
mv "$MODPATH/zygisk/lib$SONAME.so" "$MODPATH/zygisk/arm64-v8a.so"
extract "$ZIPFILE" "lib/arm64-v8a/lib$SONAME.so" "$MODPATH" true
extract "$ZIPFILE" "lib/arm64-v8a/libinject.so" "$MODPATH" true
fi
mv "$MODPATH/libinject.so" "$MODPATH/inject"
chmod 755 "$MODPATH/inject"

View File

@@ -0,0 +1,4 @@
allow keystore keystore process execmem
allow keystore system_file unix_dgram_socket *
allow system_file keystore unix_dgram_socket *
allow keystore system_file file *

View File

@@ -1,3 +1,14 @@
DEBUG=@DEBUG@
MODDIR=${0%/*}
cd $MODDIR
(
while [ true ]; do
/system/bin/app_process -Djava.class.path=./service.apk / --nice-name=TrickyStore io.github.a13e300.tricky_store.MainKt
if [ $? -ne 0 ]; then
exit 1
fi
done
) &