You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
improve: mounting system, compatibility; remove: logging on release (#111)
This commit adds numerous improvements to the state of hidden'ility of ReZygisk, and also for compatibility. Recommended to check #111 for more information.
This commit is contained in:
@@ -63,19 +63,6 @@ namespace zygiskd {
|
||||
return true;
|
||||
}
|
||||
|
||||
int RequestLogcatFd() {
|
||||
int fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
PLOGE("RequestLogcatFd");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
socket_utils::write_u8(fd, (uint8_t) SocketAction::RequestLogcatFd);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint32_t GetProcessFlags(uid_t uid) {
|
||||
int fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
@@ -94,8 +81,8 @@ namespace zygiskd {
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Module> ReadModules() {
|
||||
std::vector<Module> modules;
|
||||
std::vector<ModuleInfo> ReadModules() {
|
||||
std::vector<ModuleInfo> modules;
|
||||
int fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
PLOGE("ReadModules");
|
||||
@@ -260,4 +247,34 @@ namespace zygiskd {
|
||||
close(fd);
|
||||
} else info->running = false;
|
||||
}
|
||||
|
||||
std::string UpdateMountNamespace(enum mount_namespace_state nms_state) {
|
||||
int fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
PLOGE("UpdateMountNamespace");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
socket_utils::write_u8(fd, (uint8_t) SocketAction::UpdateMountNamespace);
|
||||
socket_utils::write_u32(fd, getpid());
|
||||
socket_utils::write_u8(fd, (uint8_t)nms_state);
|
||||
|
||||
uint32_t target_pid = socket_utils::read_u32(fd);
|
||||
int target_fd = 0;
|
||||
|
||||
if (target_pid == 0) goto error;
|
||||
|
||||
target_fd = (int)socket_utils::read_u32(fd);
|
||||
if (target_fd == 0) goto error;
|
||||
|
||||
close(fd);
|
||||
|
||||
return "/proc/" + std::to_string(target_pid) + "/fd/" + std::to_string(target_fd);
|
||||
|
||||
error:
|
||||
close(fd);
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,14 @@ void* DlopenExt(const char* path, int flags) {
|
||||
}
|
||||
|
||||
void* DlopenMem(int fd, int flags) {
|
||||
auto info = android_dlextinfo{
|
||||
auto info = android_dlextinfo {
|
||||
.flags = ANDROID_DLEXT_USE_LIBRARY_FD,
|
||||
.library_fd = fd
|
||||
.reserved_addr = NULL,
|
||||
.reserved_size = 0,
|
||||
.relro_fd = 0,
|
||||
.library_fd = fd,
|
||||
.library_fd_offset = 0,
|
||||
.library_namespace = NULL
|
||||
};
|
||||
|
||||
/* INFO: We need to find the path of the fd since passing "" to android_dlopen_ext
|
||||
|
||||
@@ -187,7 +187,7 @@ ElfW(Addr) ElfImg::LinearLookup(std::string_view name) const {
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view ElfImg::LinearLookupByPrefix(std::string_view name) const {
|
||||
ElfW(Addr) ElfImg::LinearLookupByPrefix(std::string_view name) const {
|
||||
if (symtabs_.empty()) {
|
||||
symtabs_.reserve(symtab_count);
|
||||
if (symtab_start != nullptr && symstr_offset_for_symtab != 0) {
|
||||
@@ -207,11 +207,11 @@ std::string_view ElfImg::LinearLookupByPrefix(std::string_view name) const {
|
||||
if (symtab.first.size() < size) continue;
|
||||
|
||||
if (symtab.first.substr(0, size) == name) {
|
||||
return symtab.first;
|
||||
return symtab.second->st_value;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#include <android/log.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "socket_utils.h"
|
||||
|
||||
namespace logging {
|
||||
static int logfd = -1;
|
||||
|
||||
void setfd(int fd) {
|
||||
close(logfd);
|
||||
logfd = fd;
|
||||
}
|
||||
|
||||
int getfd() {
|
||||
return logfd;
|
||||
}
|
||||
|
||||
void log(int prio, const char* tag, const char* fmt, ...) {
|
||||
if (logfd == -1) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
__android_log_vprint(prio, tag, fmt, ap);
|
||||
va_end(ap);
|
||||
} else {
|
||||
char buf[BUFSIZ];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
socket_utils::write_u8(logfd, prio);
|
||||
socket_utils::write_string(logfd, tag);
|
||||
socket_utils::write_string(logfd, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,18 +61,23 @@ struct zygote_info {
|
||||
bool running;
|
||||
};
|
||||
|
||||
enum mount_namespace_state {
|
||||
Clean,
|
||||
Rooted,
|
||||
Module
|
||||
};
|
||||
|
||||
namespace zygiskd {
|
||||
|
||||
struct Module {
|
||||
struct ModuleInfo {
|
||||
std::string name;
|
||||
UniqueFd memfd;
|
||||
|
||||
inline explicit Module(std::string name, int memfd) : name(name), memfd(memfd) {}
|
||||
inline explicit ModuleInfo(std::string name, int memfd) : name(name), memfd(memfd) {}
|
||||
};
|
||||
|
||||
enum class SocketAction {
|
||||
PingHeartBeat,
|
||||
RequestLogcatFd,
|
||||
GetProcessFlags,
|
||||
GetInfo,
|
||||
ReadModules,
|
||||
@@ -80,6 +85,7 @@ namespace zygiskd {
|
||||
GetModuleDir,
|
||||
ZygoteRestart,
|
||||
SystemServerStarted,
|
||||
UpdateMountNamespace
|
||||
};
|
||||
|
||||
void Init(const char *path);
|
||||
@@ -88,9 +94,7 @@ namespace zygiskd {
|
||||
|
||||
bool PingHeartbeat();
|
||||
|
||||
int RequestLogcatFd();
|
||||
|
||||
std::vector<Module> ReadModules();
|
||||
std::vector<ModuleInfo> ReadModules();
|
||||
|
||||
uint32_t GetProcessFlags(uid_t uid);
|
||||
|
||||
@@ -103,4 +107,6 @@ namespace zygiskd {
|
||||
void SystemServerStarted();
|
||||
|
||||
void GetInfo(struct zygote_info *info);
|
||||
|
||||
std::string UpdateMountNamespace(enum mount_namespace_state mns_state);
|
||||
}
|
||||
|
||||
@@ -48,8 +48,13 @@ namespace SandHook {
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view findSymbolNameByPrefix(std::string_view prefix) const {
|
||||
return LinearLookupByPrefix(prefix);
|
||||
constexpr ElfW(Addr) getSymbAddressByPrefix(std::string_view prefix) const {
|
||||
ElfW(Addr) offset = LinearLookupByPrefix(prefix);
|
||||
if (offset > 0 && base != nullptr) {
|
||||
return static_cast<ElfW(Addr)>((uintptr_t) base + offset - bias);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -57,6 +62,11 @@ namespace SandHook {
|
||||
return reinterpret_cast<T>(getSymbAddress(name));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T getSymbAddressByPrefix(std::string_view prefix) const {
|
||||
return reinterpret_cast<T>(getSymbAddressByPrefix(prefix));
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return base != nullptr;
|
||||
}
|
||||
@@ -76,7 +86,7 @@ namespace SandHook {
|
||||
|
||||
ElfW(Addr) LinearLookup(std::string_view name) const;
|
||||
|
||||
std::string_view LinearLookupByPrefix(std::string_view name) const;
|
||||
ElfW(Addr) LinearLookupByPrefix(std::string_view name) const;
|
||||
|
||||
constexpr static uint32_t ElfHash(std::string_view name);
|
||||
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
#pragma once
|
||||
#ifndef LOGGING_H
|
||||
#define LOGGING_H
|
||||
|
||||
#include <android/log.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef LOG_TAG
|
||||
#if defined(__LP64__)
|
||||
# define LOG_TAG "zygisk-core64"
|
||||
#else
|
||||
# define LOG_TAG "zygisk-core32"
|
||||
#endif
|
||||
#ifdef __LP64__
|
||||
#define LOG_TAG "zygisk-core64"
|
||||
#else
|
||||
#define LOG_TAG "zygisk-core32"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define LOGD(...) logging::log(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGV(...) logging::log(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define LOGD(...)
|
||||
#define LOGV(...)
|
||||
#define LOGD(...)
|
||||
#define LOGV(...)
|
||||
#endif
|
||||
#define LOGI(...) logging::log(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGW(...) logging::log(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) logging::log(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGF(...) logging::log(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
|
||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
|
||||
|
||||
namespace logging {
|
||||
void setfd(int fd);
|
||||
|
||||
int getfd();
|
||||
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
void log(int prio, const char* tag, const char* fmt, ...);
|
||||
}
|
||||
#endif /* LOGGING_H */
|
||||
@@ -21,7 +21,6 @@ namespace SoList {
|
||||
#endif
|
||||
|
||||
inline static const char *(*get_realpath_sym)(SoInfo *) = NULL;
|
||||
inline static const char *(*get_soname_sym)(SoInfo *) = NULL;
|
||||
inline static void (*soinfo_free)(SoInfo *) = NULL;
|
||||
|
||||
inline SoInfo *get_next() {
|
||||
@@ -38,12 +37,6 @@ namespace SoList {
|
||||
return ((std::string *) ((uintptr_t) this + solist_realpath_offset))->c_str();
|
||||
}
|
||||
|
||||
inline const char *get_name() {
|
||||
if (get_soname_sym) return get_soname_sym(this);
|
||||
|
||||
return ((std::string *) ((uintptr_t) this + solist_realpath_offset - sizeof(void *)))->c_str();
|
||||
}
|
||||
|
||||
void set_next(SoInfo *si) {
|
||||
*(SoInfo **) ((uintptr_t) this + solist_next_offset) = si;
|
||||
}
|
||||
@@ -110,6 +103,13 @@ namespace SoList {
|
||||
return addr == NULL ? NULL : *addr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T *getStaticPointerByPrefix(const SandHook::ElfImg &linker, const char *name) {
|
||||
auto *addr = reinterpret_cast<T **>(linker.getSymbAddressByPrefix(name));
|
||||
|
||||
return addr == NULL ? NULL : *addr;
|
||||
}
|
||||
|
||||
static bool DropSoPath(const char* target_path) {
|
||||
bool path_found = false;
|
||||
if (solist == NULL && !Initialize()) {
|
||||
@@ -117,9 +117,9 @@ namespace SoList {
|
||||
return path_found;
|
||||
}
|
||||
for (auto iter = solist; iter; iter = iter->get_next()) {
|
||||
if (iter->get_name() && iter->get_path() && strstr(iter->get_path(), target_path)) {
|
||||
if (iter->get_path() && strstr(iter->get_path(), target_path)) {
|
||||
SoList::ProtectedDataGuard guard;
|
||||
LOGI("dropping solist record for %s loaded at %s with size %zu", iter->get_name(), iter->get_path(), iter->get_size());
|
||||
LOGV("dropping solist record loaded at %s with size %zu", iter->get_path(), iter->get_size());
|
||||
if (iter->get_size() > 0) {
|
||||
iter->set_size(0);
|
||||
SoInfo::soinfo_free(iter);
|
||||
@@ -136,7 +136,7 @@ namespace SoList {
|
||||
return;
|
||||
}
|
||||
if (g_module_load_counter == NULL || g_module_unload_counter == NULL) {
|
||||
LOGI("g_module counters not defined, skip reseting them");
|
||||
LOGD("g_module counters not defined, skip reseting them");
|
||||
return;
|
||||
}
|
||||
auto loaded_modules = *g_module_load_counter;
|
||||
@@ -163,57 +163,26 @@ namespace SoList {
|
||||
|
||||
See #63 for more information.
|
||||
*/
|
||||
|
||||
std::string_view solist_sym_name = linker.findSymbolNameByPrefix("__dl__ZL6solist");
|
||||
if (solist_sym_name.empty()) return false;
|
||||
LOGD("found symbol name %s", solist_sym_name.data());
|
||||
|
||||
std::string_view soinfo_free_name = linker.findSymbolNameByPrefix("__dl__ZL11soinfo_freeP6soinfo");
|
||||
if (soinfo_free_name.empty()) return false;
|
||||
LOGD("found symbol name %s", soinfo_free_name.data());
|
||||
|
||||
/* INFO: The size isn't a magic number, it's the size for the string: .llvm.7690929523238822858 */
|
||||
char llvm_sufix[25 + 1];
|
||||
|
||||
if (solist_sym_name.length() != strlen("__dl__ZL6solist")) {
|
||||
strncpy(llvm_sufix, solist_sym_name.data() + strlen("__dl__ZL6solist"), sizeof(llvm_sufix));
|
||||
} else {
|
||||
llvm_sufix[0] = '\0';
|
||||
}
|
||||
|
||||
solist = getStaticPointer<SoInfo>(linker, solist_sym_name.data());
|
||||
solist = getStaticPointerByPrefix<SoInfo>(linker, "__dl__ZL6solist");
|
||||
if (solist == NULL) return false;
|
||||
LOGD("found symbol solist");
|
||||
|
||||
char somain_sym_name[sizeof("__dl__ZL6somain") + sizeof(llvm_sufix)];
|
||||
snprintf(somain_sym_name, sizeof(somain_sym_name), "__dl__ZL6somain%s", llvm_sufix);
|
||||
|
||||
char sonext_sym_name[sizeof("__dl__ZL6sonext") + sizeof(llvm_sufix)];
|
||||
snprintf(sonext_sym_name, sizeof(somain_sym_name), "__dl__ZL6sonext%s", llvm_sufix);
|
||||
|
||||
char vdso_sym_name[sizeof("__dl__ZL4vdso") + sizeof(llvm_sufix)];
|
||||
snprintf(vdso_sym_name, sizeof(vdso_sym_name), "__dl__ZL4vdso%s", llvm_sufix);
|
||||
|
||||
somain = getStaticPointer<SoInfo>(linker, somain_sym_name);
|
||||
somain = getStaticPointerByPrefix<SoInfo>(linker, "__dl__ZL6somain");
|
||||
if (somain == NULL) return false;
|
||||
LOGD("found symbol somain");
|
||||
|
||||
sonext = linker.getSymbAddress<SoInfo **>(sonext_sym_name);
|
||||
sonext = linker.getSymbAddressByPrefix<SoInfo **>("__dl__ZL6sonext");
|
||||
if (sonext == NULL) return false;
|
||||
LOGD("found symbol sonext");
|
||||
|
||||
SoInfo *vdso = getStaticPointer<SoInfo>(linker, vdso_sym_name);
|
||||
SoInfo *vdso = getStaticPointerByPrefix<SoInfo>(linker, "__dl__ZL4vdso");
|
||||
if (vdso != NULL) LOGD("found symbol vdso");
|
||||
|
||||
SoInfo::get_realpath_sym = reinterpret_cast<decltype(SoInfo::get_realpath_sym)>(linker.getSymbAddress("__dl__ZNK6soinfo12get_realpathEv"));
|
||||
if (SoInfo::get_realpath_sym == NULL) return false;
|
||||
LOGD("found symbol get_realpath_sym");
|
||||
|
||||
SoInfo::get_soname_sym = reinterpret_cast<decltype(SoInfo::get_soname_sym)>(linker.getSymbAddress("__dl__ZNK6soinfo10get_sonameEv"));
|
||||
if (SoInfo::get_soname_sym == NULL) return false;
|
||||
LOGD("found symbol get_soname_sym");
|
||||
|
||||
SoInfo::soinfo_free = reinterpret_cast<decltype(SoInfo::soinfo_free)>(linker.getSymbAddress(soinfo_free_name));
|
||||
SoInfo::soinfo_free = reinterpret_cast<decltype(SoInfo::soinfo_free)>(linker.getSymbAddressByPrefix("__dl__ZL11soinfo_freeP6soinfo"));
|
||||
if (SoInfo::soinfo_free == NULL) return false;
|
||||
LOGD("found symbol soinfo_free");
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ size_t block_size = 0;
|
||||
|
||||
extern "C" [[gnu::visibility("default")]]
|
||||
void entry(void* addr, size_t size, const char* path) {
|
||||
LOGI("Zygisk library injected, version %s", ZKSU_VERSION);
|
||||
LOGD("Zygisk library injected, version %s", ZKSU_VERSION);
|
||||
start_addr = addr;
|
||||
block_size = size;
|
||||
zygiskd::Init(path);
|
||||
@@ -19,11 +19,7 @@ void entry(void* addr, size_t size, const char* path) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
logging::setfd(zygiskd::RequestLogcatFd());
|
||||
#endif
|
||||
|
||||
LOGI("start plt hooking");
|
||||
LOGD("start plt hooking");
|
||||
hook_functions();
|
||||
clean_trace(path, 1, 0, false);
|
||||
}
|
||||
|
||||
@@ -137,6 +137,36 @@ DCL_HOOK_FUNC(int, fork) {
|
||||
return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork();
|
||||
}
|
||||
|
||||
bool update_mnt_ns(enum mount_namespace_state mns_state, bool dry_run) {
|
||||
std::string ns_path = zygiskd::UpdateMountNamespace(mns_state);
|
||||
if (ns_path.empty()) {
|
||||
PLOGE("Failed to update mount namespace");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dry_run) return true;
|
||||
|
||||
int updated_ns = open(ns_path.data(), O_RDONLY);
|
||||
if (updated_ns == -1) {
|
||||
PLOGE("Failed to open mount namespace [%s]", ns_path.data());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("set mount namespace to [%s] fd=[%d]\n", ns_path.data(), updated_ns);
|
||||
if (setns(updated_ns, CLONE_NEWNS) == -1) {
|
||||
PLOGE("Failed to set mount namespace [%s]", ns_path.data());
|
||||
close(updated_ns);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
close(updated_ns);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unmount stuffs in the process's private mount namespace
|
||||
DCL_HOOK_FUNC(int, unshare, int flags) {
|
||||
int res = old_unshare(flags);
|
||||
@@ -144,37 +174,22 @@ DCL_HOOK_FUNC(int, unshare, int flags) {
|
||||
// For some unknown reason, unmounting app_process in SysUI can break.
|
||||
// This is reproducible on the official AVD running API 26 and 27.
|
||||
// Simply avoid doing any unmounts for SysUI to avoid potential issues.
|
||||
(g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) {
|
||||
if (g_ctx->flags[DO_REVERT_UNMOUNT]) {
|
||||
if (g_ctx->info_flags & PROCESS_ROOT_IS_KSU) {
|
||||
revert_unmount_ksu();
|
||||
} else if (g_ctx->info_flags & PROCESS_ROOT_IS_APATCH){
|
||||
revert_unmount_apatch();
|
||||
} else if (g_ctx->info_flags & PROCESS_ROOT_IS_MAGISK) {
|
||||
revert_unmount_magisk();
|
||||
}
|
||||
!g_ctx->flags[SERVER_FORK_AND_SPECIALIZE] && !(g_ctx->info_flags & PROCESS_IS_FIRST_STARTED)) {
|
||||
if (g_ctx->info_flags & (PROCESS_IS_MANAGER | PROCESS_GRANTED_ROOT)) {
|
||||
update_mnt_ns(Rooted, false);
|
||||
} else if (!(g_ctx->flags[DO_REVERT_UNMOUNT])) {
|
||||
update_mnt_ns(Module, false);
|
||||
}
|
||||
|
||||
/* Zygisksu changed: No umount app_process */
|
||||
|
||||
// Restore errno back to 0
|
||||
errno = 0;
|
||||
old_unshare(CLONE_NEWNS);
|
||||
}
|
||||
|
||||
/* INFO: To spoof the errno value */
|
||||
errno = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Close logd_fd if necessary to prevent crashing
|
||||
// For more info, check comments in zygisk_log_write
|
||||
DCL_HOOK_FUNC(void, android_log_close) {
|
||||
if (g_ctx == nullptr) {
|
||||
// Happens during un-managed fork like nativeForkApp, nativeForkUsap
|
||||
logging::setfd(-1);
|
||||
} else if (!g_ctx->flags[SKIP_FD_SANITIZATION]) {
|
||||
logging::setfd(-1);
|
||||
}
|
||||
old_android_log_close();
|
||||
}
|
||||
|
||||
// We cannot directly call `dlclose` to unload ourselves, otherwise when `dlclose` returns,
|
||||
// it will return to our code which has been unmapped, causing segmentation fault.
|
||||
// Instead, we hook `pthread_attr_setstacksize` which will be called when VM daemon threads start.
|
||||
@@ -189,11 +204,13 @@ DCL_HOOK_FUNC(int, pthread_attr_setstacksize, void *target, size_t size) {
|
||||
if (should_unmap_zygisk) {
|
||||
unhook_functions();
|
||||
cached_map_infos.clear();
|
||||
|
||||
if (should_unmap_zygisk) {
|
||||
// Because both `pthread_attr_setstacksize` and `dlclose` have the same function signature,
|
||||
// we can use `musttail` to let the compiler reuse our stack frame and thus
|
||||
// `dlclose` will directly return to the caller of `pthread_attr_setstacksize`.
|
||||
LOGI("unmap libzygisk.so loaded at %p with size %zu", start_addr, block_size);
|
||||
LOGD("unmap libzygisk.so loaded at %p with size %zu", start_addr, block_size);
|
||||
|
||||
[[clang::musttail]] return munmap(start_addr, block_size);
|
||||
}
|
||||
}
|
||||
@@ -598,14 +615,18 @@ void ZygiskContext::run_modules_post() {
|
||||
/* Zygisksu changed: Load module fds */
|
||||
void ZygiskContext::app_specialize_pre() {
|
||||
flags[APP_SPECIALIZE] = true;
|
||||
|
||||
info_flags = zygiskd::GetProcessFlags(g_ctx->args.app->uid);
|
||||
if (info_flags & PROCESS_IS_FIRST_STARTED) {
|
||||
update_mnt_ns(Clean, true);
|
||||
}
|
||||
|
||||
if ((info_flags & PROCESS_ON_DENYLIST) == PROCESS_ON_DENYLIST) {
|
||||
flags[DO_REVERT_UNMOUNT] = true;
|
||||
flags[DO_REVERT_UNMOUNT] = true;
|
||||
}
|
||||
|
||||
if ((info_flags & (PROCESS_IS_MANAGER | PROCESS_ROOT_IS_MAGISK)) == (PROCESS_IS_MANAGER | PROCESS_ROOT_IS_MAGISK)) {
|
||||
LOGI("Manager process detected. Notifying that Zygisk has been enabled.");
|
||||
LOGD("Manager process detected. Notifying that Zygisk has been enabled.");
|
||||
|
||||
setenv("ZYGISK_ENABLED", "1", 1);
|
||||
} else {
|
||||
@@ -620,7 +641,6 @@ void ZygiskContext::app_specialize_post() {
|
||||
// Cleanups
|
||||
env->ReleaseStringUTFChars(args.app->nice_name, process);
|
||||
g_ctx = nullptr;
|
||||
logging::setfd(-1);
|
||||
}
|
||||
|
||||
bool ZygiskContext::exempt_fd(int fd) {
|
||||
@@ -653,11 +673,10 @@ void ZygiskContext::nativeForkSystemServer_pre() {
|
||||
flags[SERVER_FORK_AND_SPECIALIZE] = true;
|
||||
|
||||
fork_pre();
|
||||
if (pid != 0)
|
||||
return;
|
||||
|
||||
run_modules_pre();
|
||||
zygiskd::SystemServerStarted();
|
||||
if (is_child()) {
|
||||
run_modules_pre();
|
||||
zygiskd::SystemServerStarted();
|
||||
}
|
||||
|
||||
sanitize_fds();
|
||||
}
|
||||
@@ -673,12 +692,9 @@ void ZygiskContext::nativeForkSystemServer_post() {
|
||||
void ZygiskContext::nativeForkAndSpecialize_pre() {
|
||||
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
||||
LOGV("pre forkAndSpecialize [%s]", process);
|
||||
|
||||
flags[APP_FORK_AND_SPECIALIZE] = true;
|
||||
/* Zygisksu changed: No args.app->fds_to_ignore check since we are Android 10+ */
|
||||
if (logging::getfd() != -1) {
|
||||
exempted_fds.push_back(logging::getfd());
|
||||
}
|
||||
|
||||
update_mnt_ns(Clean, false);
|
||||
|
||||
fork_pre();
|
||||
if (pid == 0) {
|
||||
@@ -805,7 +821,6 @@ 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();
|
||||
|
||||
// Remove unhooked methods
|
||||
|
||||
@@ -126,13 +126,13 @@ namespace {
|
||||
PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT,
|
||||
PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST,
|
||||
|
||||
PROCESS_IS_MANAGER = (1u << 28),
|
||||
PROCESS_ROOT_IS_APATCH = (1u << 27),
|
||||
PROCESS_IS_MANAGER = (1u << 27),
|
||||
PROCESS_ROOT_IS_APATCH = (1u << 28),
|
||||
PROCESS_ROOT_IS_KSU = (1u << 29),
|
||||
PROCESS_ROOT_IS_MAGISK = (1u << 30),
|
||||
PROCESS_IS_SYS_UI = (1u << 31),
|
||||
PROCESS_IS_FIRST_STARTED = (1u << 31),
|
||||
|
||||
PRIVATE_MASK = PROCESS_IS_SYS_UI
|
||||
PRIVATE_MASK = PROCESS_IS_FIRST_STARTED
|
||||
};
|
||||
|
||||
struct api_abi_base {
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
#include <mntent.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "files.hpp"
|
||||
#include "logging.h"
|
||||
#include "misc.hpp"
|
||||
#include "zygisk.hpp"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace {
|
||||
constexpr auto MODULE_DIR = "/data/adb/modules";
|
||||
constexpr auto KSU_OVERLAY_SOURCE = "KSU";
|
||||
constexpr auto AP_OVERLAY_SOURCE = "APatch";
|
||||
const std::vector<std::string> DEVICE_PARTITIONS{"/system", "/vendor", "/product", "/system_ext", "/odm", "/oem"};
|
||||
|
||||
void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1) {
|
||||
LOGD("Unmounted (%s)", mountpoint);
|
||||
} else {
|
||||
#ifndef NDEBUG
|
||||
PLOGE("Unmount (%s)", mountpoint);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void revert_unmount_ksu() {
|
||||
std::string ksu_loop;
|
||||
std::vector<std::string> targets;
|
||||
|
||||
// Unmount ksu module dir last
|
||||
targets.emplace_back(MODULE_DIR);
|
||||
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
if (info.target == MODULE_DIR) {
|
||||
ksu_loop = info.source;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (info.target.starts_with("/data/adb")) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (info.root.starts_with("/adb/modules")) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
|
||||
// Unmount ksu overlays
|
||||
if (info.type == "overlay"
|
||||
&& info.source == KSU_OVERLAY_SOURCE
|
||||
&& std::find(DEVICE_PARTITIONS.begin(), DEVICE_PARTITIONS.end(), info.target) != DEVICE_PARTITIONS.end()) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
|
||||
// Unmount temp dir
|
||||
if (info.type == "tmpfs" && info.source == KSU_OVERLAY_SOURCE) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
// Unmount everything from ksu loop except ksu module dir
|
||||
if (info.source == ksu_loop && info.target != MODULE_DIR) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
|
||||
// Do unmount
|
||||
for (auto& s: reversed(targets)) {
|
||||
lazy_unmount(s.data());
|
||||
}
|
||||
}
|
||||
|
||||
void revert_unmount_magisk() {
|
||||
std::vector<std::string> targets;
|
||||
|
||||
// Unmount dummy skeletons and MAGISKTMP
|
||||
// since mirror nodes are always mounted under skeleton, we don't have to specifically unmount
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
if (info.source == "magisk" || info.source == "worker" || // magisktmp tmpfs
|
||||
info.root.starts_with("/adb/modules")) { // bind mount from data partition
|
||||
targets.push_back(info.target);
|
||||
}
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (info.target.starts_with("/data/adb")) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& s: reversed(targets)) {
|
||||
lazy_unmount(s.data());
|
||||
}
|
||||
}
|
||||
|
||||
void revert_unmount_apatch() {
|
||||
std::string ap_loop;
|
||||
std::vector<std::string> targets;
|
||||
|
||||
// Unmount ksu module dir last
|
||||
targets.emplace_back(MODULE_DIR);
|
||||
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
if (info.target == MODULE_DIR) {
|
||||
ap_loop = info.source;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (info.target.starts_with("/data/adb")) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (info.root.starts_with("/adb/modules")) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
|
||||
// Unmount ksu overlays
|
||||
if (info.type == "overlay"
|
||||
&& info.source == AP_OVERLAY_SOURCE
|
||||
&& std::find(DEVICE_PARTITIONS.begin(), DEVICE_PARTITIONS.end(), info.target) != DEVICE_PARTITIONS.end()) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
|
||||
// Unmount temp dir
|
||||
if (info.type == "tmpfs" && info.source == AP_OVERLAY_SOURCE) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
// Unmount everything from ksu loop except ksu module dir
|
||||
if (info.source == ap_loop && info.target != MODULE_DIR) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
|
||||
// Do unmount
|
||||
for (auto& s: reversed(targets)) {
|
||||
lazy_unmount(s.data());
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,3 @@ extern size_t block_size;
|
||||
void hook_functions();
|
||||
|
||||
void clean_trace(const char* path, size_t load = 1, size_t unload = 0, bool spoof_maps = false);
|
||||
|
||||
void revert_unmount_ksu();
|
||||
|
||||
void revert_unmount_magisk();
|
||||
|
||||
void revert_unmount_apatch();
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
allow zygote tmpfs file *
|
||||
allow zygote appdomain_tmpfs file *
|
||||
type zygisk_file file_type
|
||||
typeattribute zygisk_file mlstrustedobject
|
||||
allow zygote zygisk_file sock_file {read write}
|
||||
|
||||
type magisk_file file_type
|
||||
typeattribute magisk_file mlstrustedobject
|
||||
|
||||
allow * magisk_file file *
|
||||
allow * magisk_file dir *
|
||||
allow * magisk_file fifo_file *
|
||||
allow * magisk_file chr_file *
|
||||
allow * magisk_file lnk_file *
|
||||
allow * magisk_file sock_file *
|
||||
|
||||
allow system_server system_server process execmem
|
||||
allow zygote zygote process execmem
|
||||
allow zygote magisk lnk_file read
|
||||
allow zygote unlabeled file {read open}
|
||||
allow zygote zygote capability sys_chroot
|
||||
allow zygote su dir search
|
||||
allow zygote su {lnk_file file} read
|
||||
|
||||
allow zygote adb_data_file dir search
|
||||
allow zygote mnt_vendor_file dir search
|
||||
allow zygote system_file dir mounton
|
||||
allow zygote labeledfs filesystem mount
|
||||
allow zygote fs_type filesystem unmount
|
||||
allow zygote zygote process execmem
|
||||
allow system_server system_server process execmem
|
||||
allow zygote tmpfs file *
|
||||
allow zygote appdomain_tmpfs file *
|
||||
|
||||
@@ -42,7 +42,7 @@ val CFlagsRelease = arrayOf(
|
||||
)
|
||||
|
||||
val CFlagsDebug = arrayOf(
|
||||
"-g", "-O0"
|
||||
"-g", "-O0", "-DDEBUG"
|
||||
)
|
||||
|
||||
val Files = arrayOf(
|
||||
|
||||
@@ -7,12 +7,6 @@
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#if DEBUG == false
|
||||
#define MAX_LOG_LEVEL ANDROID_LOG_VERBOSE
|
||||
#else
|
||||
#define MAX_LOG_LEVEL ANDROID_LOG_INFO
|
||||
#endif
|
||||
|
||||
#if (defined(__LP64__) || defined(_LP64))
|
||||
#define lp_select(a, b) b
|
||||
#else
|
||||
@@ -26,25 +20,24 @@
|
||||
|
||||
enum DaemonSocketAction {
|
||||
PingHeartbeat = 0,
|
||||
RequestLogcatFd = 1,
|
||||
GetProcessFlags = 2,
|
||||
GetInfo = 3,
|
||||
ReadModules = 4,
|
||||
RequestCompanionSocket = 5,
|
||||
GetModuleDir = 6,
|
||||
ZygoteRestart = 7,
|
||||
SystemServerStarted = 8
|
||||
GetProcessFlags = 1,
|
||||
GetInfo = 2,
|
||||
ReadModules = 3,
|
||||
RequestCompanionSocket = 4,
|
||||
GetModuleDir = 5,
|
||||
ZygoteRestart = 6,
|
||||
SystemServerStarted = 7,
|
||||
GetCleanNamespace = 8
|
||||
};
|
||||
|
||||
enum ProcessFlags: uint32_t {
|
||||
PROCESS_GRANTED_ROOT = (1u << 0),
|
||||
PROCESS_ON_DENYLIST = (1u << 1),
|
||||
PROCESS_IS_MANAGER = (1u << 28),
|
||||
PROCESS_ROOT_IS_APATCH = (1u << 27),
|
||||
PROCESS_IS_MANAGER = (1u << 27),
|
||||
PROCESS_ROOT_IS_APATCH = (1u << 28),
|
||||
PROCESS_ROOT_IS_KSU = (1u << 29),
|
||||
PROCESS_ROOT_IS_MAGISK = (1u << 30),
|
||||
PROCESS_IS_SYS_UI = (1u << 31),
|
||||
PROCESS_IS_SYSUI = (1u << 31)
|
||||
PROCESS_IS_FIRST_STARTED = (1u << 31)
|
||||
};
|
||||
|
||||
enum RootImplState {
|
||||
@@ -54,4 +47,10 @@ enum RootImplState {
|
||||
Abnormal
|
||||
};
|
||||
|
||||
enum MountNamespaceState {
|
||||
Clean,
|
||||
Rooted,
|
||||
Module
|
||||
};
|
||||
|
||||
#endif /* CONSTANTS_H */
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../utils.h"
|
||||
#include "kernelsu.h"
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <linux/limits.h>
|
||||
@@ -18,6 +20,11 @@
|
||||
|
||||
#include "utils.h"
|
||||
#include "root_impl/common.h"
|
||||
#include "root_impl/magisk.h"
|
||||
|
||||
int clean_namespace_fd = 0;
|
||||
int rooted_namespace_fd = 0;
|
||||
int module_namespace_fd = 0;
|
||||
|
||||
bool switch_mount_namespace(pid_t pid) {
|
||||
char path[PATH_MAX];
|
||||
@@ -184,7 +191,7 @@ int unix_listener_from_path(char *restrict path) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chcon(path, "u:object_r:magisk_file:s0") == -1) {
|
||||
if (chcon(path, "u:object_r:zygisk_file:s0") == -1) {
|
||||
LOGE("chcon: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
@@ -450,3 +457,381 @@ void stringify_root_impl_name(struct root_impl impl, char *restrict output) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct mountinfo {
|
||||
unsigned int id;
|
||||
unsigned int parent;
|
||||
dev_t device;
|
||||
const char *root;
|
||||
const char *target;
|
||||
const char *vfs_option;
|
||||
struct {
|
||||
unsigned int shared;
|
||||
unsigned int master;
|
||||
unsigned int propagate_from;
|
||||
} optional;
|
||||
const char *type;
|
||||
const char *source;
|
||||
const char *fs_option;
|
||||
};
|
||||
|
||||
struct mountinfos {
|
||||
struct mountinfo *mounts;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
char *strndup(const char *restrict str, size_t length) {
|
||||
char *restrict copy = malloc(length + 1);
|
||||
if (copy == NULL) return NULL;
|
||||
|
||||
memcpy(copy, str, length);
|
||||
copy[length] = '\0';
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void free_mounts(struct mountinfos *restrict mounts) {
|
||||
for (size_t i = 0; i < mounts->length; i++) {
|
||||
free((void *)mounts->mounts[i].root);
|
||||
free((void *)mounts->mounts[i].target);
|
||||
free((void *)mounts->mounts[i].vfs_option);
|
||||
free((void *)mounts->mounts[i].type);
|
||||
free((void *)mounts->mounts[i].source);
|
||||
free((void *)mounts->mounts[i].fs_option);
|
||||
}
|
||||
|
||||
free((void *)mounts->mounts);
|
||||
}
|
||||
|
||||
bool parse_mountinfo(const char *restrict pid, struct mountinfos *restrict mounts) {
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "/proc/%s/mountinfo", pid);
|
||||
|
||||
FILE *mountinfo = fopen(path, "r");
|
||||
if (mountinfo == NULL) {
|
||||
LOGE("fopen: %s\n", strerror(errno));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char line[PATH_MAX];
|
||||
size_t i = 0;
|
||||
|
||||
mounts->mounts = NULL;
|
||||
mounts->length = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), mountinfo) != NULL) {
|
||||
int root_start = 0, root_end = 0;
|
||||
int target_start = 0, target_end = 0;
|
||||
int vfs_option_start = 0, vfs_option_end = 0;
|
||||
int type_start = 0, type_end = 0;
|
||||
int source_start = 0, source_end = 0;
|
||||
int fs_option_start = 0, fs_option_end = 0;
|
||||
int optional_start = 0, optional_end = 0;
|
||||
unsigned int id, parent, maj, min;
|
||||
sscanf(line,
|
||||
"%u " // (1) id
|
||||
"%u " // (2) parent
|
||||
"%u:%u " // (3) maj:min
|
||||
"%n%*s%n " // (4) mountroot
|
||||
"%n%*s%n " // (5) target
|
||||
"%n%*s%n" // (6) vfs options (fs-independent)
|
||||
"%n%*[^-]%n - " // (7) optional fields
|
||||
"%n%*s%n " // (8) FS type
|
||||
"%n%*s%n " // (9) source
|
||||
"%n%*s%n", // (10) fs options (fs specific)
|
||||
&id, &parent, &maj, &min, &root_start, &root_end, &target_start,
|
||||
&target_end, &vfs_option_start, &vfs_option_end,
|
||||
&optional_start, &optional_end, &type_start, &type_end,
|
||||
&source_start, &source_end, &fs_option_start, &fs_option_end);
|
||||
|
||||
mounts->mounts = (struct mountinfo *)realloc(mounts->mounts, (i + 1) * sizeof(struct mountinfo));
|
||||
if (!mounts->mounts) {
|
||||
LOGE("Failed to allocate memory for mounts->mounts");
|
||||
|
||||
fclose(mountinfo);
|
||||
free_mounts(mounts);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int shared = 0;
|
||||
unsigned int master = 0;
|
||||
unsigned int propagate_from = 0;
|
||||
if (strstr(line + optional_start, "shared:")) {
|
||||
shared = (unsigned int)atoi(strstr(line + optional_start, "shared:") + 7);
|
||||
}
|
||||
|
||||
if (strstr(line + optional_start, "master:")) {
|
||||
master = (unsigned int)atoi(strstr(line + optional_start, "master:") + 7);
|
||||
}
|
||||
|
||||
if (strstr(line + optional_start, "propagate_from:")) {
|
||||
propagate_from = (unsigned int)atoi(strstr(line + optional_start, "propagate_from:") + 15);
|
||||
}
|
||||
|
||||
mounts->mounts[i].id = id;
|
||||
mounts->mounts[i].parent = parent;
|
||||
mounts->mounts[i].device = (dev_t)(makedev(maj, min));
|
||||
mounts->mounts[i].root = strndup(line + root_start, (size_t)(root_end - root_start));
|
||||
mounts->mounts[i].target = strndup(line + target_start, (size_t)(target_end - target_start));
|
||||
mounts->mounts[i].vfs_option = strndup(line + vfs_option_start, (size_t)(vfs_option_end - vfs_option_start));
|
||||
mounts->mounts[i].optional.shared = shared;
|
||||
mounts->mounts[i].optional.master = master;
|
||||
mounts->mounts[i].optional.propagate_from = propagate_from;
|
||||
mounts->mounts[i].type = strndup(line + type_start, (size_t)(type_end - type_start));
|
||||
mounts->mounts[i].source = strndup(line + source_start, (size_t)(source_end - source_start));
|
||||
mounts->mounts[i].fs_option = strndup(line + fs_option_start, (size_t)(fs_option_end - fs_option_start));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
fclose(mountinfo);
|
||||
|
||||
mounts->length = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum mns_umount_state {
|
||||
Complete,
|
||||
NotComplete,
|
||||
Error
|
||||
};
|
||||
|
||||
enum mns_umount_state unmount_root(bool modules_only, struct root_impl impl) {
|
||||
/* INFO: We are already in the target pid mount namespace, so actually,
|
||||
when we use self here, we meant its pid.
|
||||
*/
|
||||
struct mountinfos mounts;
|
||||
if (!parse_mountinfo("self", &mounts)) {
|
||||
LOGE("Failed to parse mountinfo\n");
|
||||
|
||||
return Error;
|
||||
}
|
||||
|
||||
/* INFO: Implementations like Magisk Kitsune will mount MagiskSU when boot is completed,
|
||||
so if we cache the clean mount done before the boot is completed, it will get
|
||||
it mounted later and hence it will leak mounts. To avoid that we will detect
|
||||
if implementation is Kitsune, and if so, see if /system/bin... is mounted,
|
||||
if not, it won't cache this namespace. */
|
||||
bool magiskSU_umounted = false;
|
||||
|
||||
switch (impl.impl) {
|
||||
case None: { break; }
|
||||
case Multiple: { break; }
|
||||
|
||||
case KernelSU:
|
||||
case APatch: {
|
||||
char source_name[LONGEST_ROOT_IMPL_NAME];
|
||||
if (impl.impl == KernelSU) strcpy(source_name, "KSU");
|
||||
else strcpy(source_name, "APatch");
|
||||
|
||||
const char **targets_to_unmount = NULL;
|
||||
size_t num_targets = 0;
|
||||
|
||||
for (size_t i = 0; i < mounts.length; i++) {
|
||||
struct mountinfo mount = mounts.mounts[i];
|
||||
|
||||
bool should_unmount = false;
|
||||
|
||||
if (modules_only) {
|
||||
if (strncmp(mount.target, "/debug_ramdisk", strlen("/debug_ramdisk")) == 0)
|
||||
should_unmount = true;
|
||||
} else {
|
||||
if (strcmp(mount.source, source_name) == 0) should_unmount = true;
|
||||
if (strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0) should_unmount = true;
|
||||
if (strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0) should_unmount = true;
|
||||
}
|
||||
|
||||
if (!should_unmount) continue;
|
||||
|
||||
num_targets++;
|
||||
targets_to_unmount = realloc(targets_to_unmount, num_targets * sizeof(char*));
|
||||
if (targets_to_unmount == NULL) {
|
||||
LOGE("[%s] Failed to allocate memory for targets_to_unmount\n", source_name);
|
||||
|
||||
free(targets_to_unmount);
|
||||
free_mounts(&mounts);
|
||||
|
||||
return Error;
|
||||
}
|
||||
|
||||
targets_to_unmount[num_targets - 1] = mount.target;
|
||||
}
|
||||
|
||||
for (size_t i = num_targets; i > 0; i--) {
|
||||
const char *target = targets_to_unmount[i - 1];
|
||||
|
||||
if (umount2(target, MNT_DETACH) == -1) {
|
||||
LOGE("[%s] Failed to unmount %s: %s\n", source_name, target, strerror(errno));
|
||||
} else {
|
||||
LOGI("[%s] Unmounted %s\n", source_name, target);
|
||||
}
|
||||
}
|
||||
free(targets_to_unmount);
|
||||
|
||||
break;
|
||||
}
|
||||
case Magisk: {
|
||||
LOGI("[Magisk] Unmounting root %s modules\n", modules_only ? "only" : "with");
|
||||
|
||||
const char **targets_to_unmount = NULL;
|
||||
size_t num_targets = 0;
|
||||
|
||||
for (size_t i = 0; i < mounts.length; i++) {
|
||||
struct mountinfo mount = mounts.mounts[i];
|
||||
|
||||
bool should_unmount = false;
|
||||
if (
|
||||
(
|
||||
modules_only &&
|
||||
(
|
||||
strcmp(mount.source, "magisk") == 0 ||
|
||||
strncmp(mount.target, "/debug_ramdisk", strlen("/debug_ramdisk")) == 0 ||
|
||||
strncmp(mount.target, "/system/bin", strlen("/system/bin")) == 0
|
||||
)
|
||||
) ||
|
||||
(
|
||||
!modules_only &&
|
||||
(
|
||||
strcmp(mount.source, "magisk") == 0 ||
|
||||
strncmp(mount.target, "/debug_ramdisk", strlen("/debug_ramdisk")) == 0 ||
|
||||
strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0 ||
|
||||
strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0 ||
|
||||
strncmp(mount.target, "/system/bin", strlen("/system/bin")) == 0
|
||||
)
|
||||
)
|
||||
) {
|
||||
should_unmount = true;
|
||||
}
|
||||
|
||||
if (!should_unmount) continue;
|
||||
|
||||
num_targets++;
|
||||
targets_to_unmount = realloc(targets_to_unmount, num_targets * sizeof(char*));
|
||||
if (targets_to_unmount == NULL) {
|
||||
LOGE("[Magisk] Failed to allocate memory for targets_to_unmount\n");
|
||||
|
||||
free(targets_to_unmount);
|
||||
free_mounts(&mounts);
|
||||
|
||||
return Error;
|
||||
}
|
||||
|
||||
targets_to_unmount[num_targets - 1] = mount.target;
|
||||
|
||||
if (impl.impl == Magisk && strncmp(mount.target, "/system/bin", strlen("/system/bin")) == 0)
|
||||
magiskSU_umounted = true;
|
||||
}
|
||||
|
||||
for (size_t i = num_targets; i > 0; i--) {
|
||||
const char *target = targets_to_unmount[i - 1];
|
||||
if (umount2(target, MNT_DETACH) == -1) {
|
||||
LOGE("[Magisk] Failed to unmount %s: %s\n", target, strerror(errno));
|
||||
} else {
|
||||
LOGI("[Magisk] Unmounted %s\n", target);
|
||||
}
|
||||
}
|
||||
free(targets_to_unmount);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free_mounts(&mounts);
|
||||
|
||||
return (impl.impl == Magisk && !magiskSU_umounted) ? NotComplete : Complete;
|
||||
}
|
||||
|
||||
int save_mns_fd(int pid, enum MountNamespaceState mns_state, struct root_impl impl) {
|
||||
if (mns_state == Clean && clean_namespace_fd != 0) return clean_namespace_fd;
|
||||
if (mns_state == Rooted && rooted_namespace_fd != 0) return rooted_namespace_fd;
|
||||
if (mns_state == Module && module_namespace_fd != 0) return module_namespace_fd;
|
||||
|
||||
int sockets[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
|
||||
LOGE("socketpair: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int reader = sockets[0];
|
||||
int writer = sockets[1];
|
||||
|
||||
pid_t fork_pid = fork();
|
||||
if (fork_pid == 0) {
|
||||
switch_mount_namespace(pid);
|
||||
|
||||
enum mns_umount_state umount_state = Complete;
|
||||
|
||||
if (mns_state != Rooted) {
|
||||
unshare(CLONE_NEWNS);
|
||||
umount_state = unmount_root(mns_state == Module, impl);
|
||||
if (umount_state == Error) {
|
||||
write_uint8_t(writer, (uint8_t)umount_state);
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mypid = 0;
|
||||
while (mypid != (uint32_t)getpid()) {
|
||||
write_uint8_t(writer, (uint8_t)umount_state);
|
||||
usleep(50);
|
||||
read_uint32_t(reader, &mypid);
|
||||
}
|
||||
|
||||
_exit(0);
|
||||
} else if (fork_pid > 0) {
|
||||
enum mns_umount_state umount_state = (enum mns_umount_state)0;
|
||||
read_uint8_t(reader, (uint8_t *)&umount_state);
|
||||
|
||||
if (umount_state == Error) {
|
||||
LOGE("Failed to unmount root\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char ns_path[PATH_MAX];
|
||||
snprintf(ns_path, PATH_MAX, "/proc/%d/ns/mnt", fork_pid);
|
||||
|
||||
int ns_fd = open(ns_path, O_RDONLY);
|
||||
if (ns_fd == -1) {
|
||||
LOGE("open: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_uint32_t(writer, (uint32_t)fork_pid);
|
||||
|
||||
if (close(reader) == -1) {
|
||||
LOGE("Failed to close reader: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (close(writer) == -1) {
|
||||
LOGE("Failed to close writer: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (waitpid(fork_pid, NULL, 0) == -1) {
|
||||
LOGE("waitpid: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mns_state == Rooted) return (rooted_namespace_fd = ns_fd);
|
||||
else if (mns_state == Clean && umount_state == Complete) return (clean_namespace_fd = ns_fd);
|
||||
else if (mns_state == Module && umount_state == Complete) return (module_namespace_fd = ns_fd);
|
||||
else return ns_fd;
|
||||
} else {
|
||||
LOGE("fork: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -107,4 +107,6 @@ int non_blocking_execv(const char *restrict file, char *const argv[]);
|
||||
|
||||
void stringify_root_impl_name(struct root_impl impl, char *restrict output);
|
||||
|
||||
int save_mns_fd(int pid, enum MountNamespaceState mns_state, struct root_impl impl);
|
||||
|
||||
#endif /* UTILS_H */
|
||||
|
||||
@@ -382,6 +382,7 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool first_process = true;
|
||||
while (1) {
|
||||
int client_fd = accept(socket_fd, NULL, NULL);
|
||||
if (client_fd == -1) {
|
||||
@@ -437,51 +438,26 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
|
||||
break;
|
||||
}
|
||||
/* TODO: Move to another thread and save client fds to an epoll list
|
||||
so that we can, in a single-thread, deal with multiple logcats */
|
||||
case RequestLogcatFd: {
|
||||
uint8_t level = 0;
|
||||
ssize_t ret = read_uint8_t(client_fd, &level);
|
||||
ASSURE_SIZE_READ_BREAK("RequestLogcatFd", "level", ret, sizeof(level));
|
||||
|
||||
char tag[128 + 1];
|
||||
ret = read_string(client_fd, tag, sizeof(tag));
|
||||
if (ret == -1) {
|
||||
LOGE("Failed reading logcat tag.\n");
|
||||
|
||||
close(client_fd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
char message[1024 + 1];
|
||||
ret = read_string(client_fd, message, sizeof(message));
|
||||
if (ret == -1) {
|
||||
LOGE("Failed reading logcat message.\n");
|
||||
|
||||
close(client_fd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
__android_log_print(level, tag, "%s", message);
|
||||
|
||||
break;
|
||||
}
|
||||
case GetProcessFlags: {
|
||||
uint32_t uid = 0;
|
||||
ssize_t ret = read_uint32_t(client_fd, &uid);
|
||||
ASSURE_SIZE_READ_BREAK("GetProcessFlags", "uid", ret, sizeof(uid));
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (uid_is_manager(uid)) {
|
||||
flags |= PROCESS_IS_MANAGER;
|
||||
if (first_process) {
|
||||
flags |= PROCESS_IS_FIRST_STARTED;
|
||||
|
||||
first_process = false;
|
||||
} else {
|
||||
if (uid_granted_root(uid)) {
|
||||
flags |= PROCESS_GRANTED_ROOT;
|
||||
}
|
||||
if (uid_should_umount(uid)) {
|
||||
flags |= PROCESS_ON_DENYLIST;
|
||||
if (uid_is_manager(uid)) {
|
||||
flags |= PROCESS_IS_MANAGER;
|
||||
} else {
|
||||
if (uid_granted_root(uid)) {
|
||||
flags |= PROCESS_GRANTED_ROOT;
|
||||
}
|
||||
if (uid_should_umount(uid)) {
|
||||
flags |= PROCESS_ON_DENYLIST;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,11 +647,35 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GetCleanNamespace: {
|
||||
pid_t pid = 0;
|
||||
ssize_t ret = read_uint32_t(client_fd, (uint32_t *)&pid);
|
||||
ASSURE_SIZE_READ_BREAK("GetCleanNamespace", "pid", ret, sizeof(pid));
|
||||
|
||||
uint8_t mns_state = 0;
|
||||
ret = read_uint8_t(client_fd, &mns_state);
|
||||
ASSURE_SIZE_READ_BREAK("GetCleanNamespace", "mns_state", ret, sizeof(mns_state));
|
||||
|
||||
uint32_t our_pid = (uint32_t)getpid();
|
||||
ret = write_uint32_t(client_fd, (uint32_t)our_pid);
|
||||
ASSURE_SIZE_WRITE_BREAK("GetCleanNamespace", "our_pid", ret, sizeof(our_pid));
|
||||
|
||||
if ((enum MountNamespaceState)mns_state == Clean) {
|
||||
save_mns_fd(pid, Rooted, impl);
|
||||
save_mns_fd(pid, Module, impl);
|
||||
}
|
||||
|
||||
uint32_t clean_namespace_fd = (uint32_t)save_mns_fd(pid, (enum MountNamespaceState)mns_state, impl);
|
||||
ret = write_uint32_t(client_fd, clean_namespace_fd);
|
||||
ASSURE_SIZE_WRITE_BREAK("GetCleanNamespace", "clean_namespace_fd", ret, sizeof(clean_namespace_fd));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (action != RequestCompanionSocket && action != RequestLogcatFd) close(client_fd);
|
||||
if (action != RequestCompanionSocket) close(client_fd);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user