You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
Compare commits
33 Commits
rewrite/we
...
v1.0.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc1b757bb8 | ||
|
|
a0a54f2153 | ||
|
|
295a62b649 | ||
|
|
b6f02b39b3 | ||
|
|
e036b1f40a | ||
|
|
9a3b2f4a79 | ||
|
|
9810eb3974 | ||
|
|
823623a96f | ||
|
|
a75b2fe2b8 | ||
|
|
48238521df | ||
|
|
fa9adcf3b5 | ||
|
|
6c05527ffa | ||
|
|
aff2ad8d3c | ||
|
|
b7fe7b3dbe | ||
|
|
f432550f07 | ||
|
|
a0ab02cedc | ||
|
|
f9a23a2882 | ||
|
|
d111a2dfc5 | ||
|
|
cd4784376e | ||
|
|
c786790b0f | ||
|
|
4f35e06ac4 | ||
|
|
57f985292e | ||
|
|
34643c794f | ||
|
|
ec705fb260 | ||
|
|
c023da0fd6 | ||
|
|
63f29f0771 | ||
|
|
c975722795 | ||
|
|
2f589d0eda | ||
|
|
70697be9a5 | ||
|
|
6261466e44 | ||
|
|
d455117c49 | ||
|
|
6272e0a2ac | ||
|
|
62481ca2b6 |
2
.github/ISSUE_TEMPLATE/issue_template.yml
vendored
2
.github/ISSUE_TEMPLATE/issue_template.yml
vendored
@@ -59,7 +59,7 @@ body:
|
||||
id: code_of_conduct
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/PerformanC/voice/blob/main/CODE_OF_CONDUCT.md)
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/PerformanC/contributing/blob/main/CODE_OF_CONDUCT.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/pull_request.yml
vendored
2
.github/ISSUE_TEMPLATE/pull_request.yml
vendored
@@ -35,7 +35,7 @@ body:
|
||||
id: code_of_conduct
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/PerformanC/voice/blob/main/CODE_OF_CONDUCT.md)
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/PerformanC/contributing/blob/main/CODE_OF_CONDUCT.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
java-version: "17"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4.2.1
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
|
||||
10
.github/workflows/trusted_ci.yml
vendored
10
.github/workflows/trusted_ci.yml
vendored
@@ -21,15 +21,19 @@ jobs:
|
||||
java-version: "17"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4.2.1
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Setup keys
|
||||
env:
|
||||
private_key: ${{ secrets.ORG_PRIVATE_KEY }}
|
||||
public_key: ${{ secrets.ORG_PUBLIC_KEY }}
|
||||
run: |
|
||||
echo "$private_key" | base64 -d > module/private_key
|
||||
echo "$public_key" | base64 -d > module/public_key
|
||||
if [ -z "$private_key" ] || [ -z "$public_key" ]; then
|
||||
echo "Private or public key is not set."
|
||||
else
|
||||
echo "$private_key" | base64 -d > module/private_key
|
||||
echo "$public_key" | base64 -d > module/public_key
|
||||
fi
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "loader/src/external/lsplt"]
|
||||
[submodule "LSPlt"]
|
||||
path = loader/src/external/lsplt
|
||||
url = https://github.com/JingMatrix/LSPlt
|
||||
url = https://github.com/PerformanC/LSPlt
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
- **es_ES** by [@LuchoModzzz](https://github.com/Lxchoooo)
|
||||
- **es_MX** by [@LuchoModzzz](https://github.com/Lxchoooo)
|
||||
- **fr_FR** by [@GhostFRR](https://github.com/GhostFRR)
|
||||
- **ja_JP** by [@Fyphen1223](https://github.com/Fyphen1223)
|
||||
- **ja_JP** by [@Fyphen1223](https://github.com/Fyphen1223) & [@reindex-ot](https://github.com/reindex-ot)
|
||||
- **id_ID** by [@bpanca05](https://github.com/bpanca05) & [@LuckyKiddos](https://github.com/GuitarHeroStyles)
|
||||
- **it_IT** by [@thasave14](https://github.com/thasave14)
|
||||
- **pt_BR** by [@ThePedroo](https://github.com/ThePedroo)
|
||||
- **ro_RO** by [@ExtremeXT](https://github.com/ExtremeXT)
|
||||
- **ru_RU** by [@Emulond](https://github.com/Emulond) & [@AJleKcAHgP68](https://github.com/AJleKcAHgP68)
|
||||
- **tr_TR** by [@dyingwillow](https://github.com/dyingwillow)
|
||||
- **tr_TR** by [@witchfuneral](https://github.com/witchfuneral)
|
||||
- **uk_UA** by [@Kittyskj](https://github.com/Kittyskj)
|
||||
- **vi_VN** by [@RainyXeon](https://github.com/RainyXeon)
|
||||
- **zh_CN** by [@Meltartica](https://github.com/Meltartica) & [@SheepChef](https://github.com/SheepChef)
|
||||
|
||||
@@ -38,19 +38,18 @@ val androidSourceCompatibility by extra(JavaVersion.VERSION_11)
|
||||
val androidTargetCompatibility by extra(JavaVersion.VERSION_11)
|
||||
|
||||
tasks.register("Delete", Delete::class) {
|
||||
delete(rootProject.buildDir)
|
||||
delete(layout.buildDirectory.get())
|
||||
}
|
||||
|
||||
fun Project.configureBaseExtension() {
|
||||
extensions.findByType(LibraryExtension::class)?.run {
|
||||
namespace = "icu.nullptr.zygisk.next"
|
||||
namespace = "com.performanc.org.rezygisk"
|
||||
compileSdk = androidCompileSdkVersion
|
||||
ndkVersion = androidCompileNdkVersion
|
||||
buildToolsVersion = androidBuildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdk = androidMinSdkVersion
|
||||
targetSdk = androidTargetSdkVersion
|
||||
}
|
||||
|
||||
lint {
|
||||
|
||||
@@ -535,8 +535,10 @@ ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
|
||||
((uintptr_t)1 << ((hash >> img->gnu_shift2_) % bloom_mask_bits));
|
||||
|
||||
if ((mask & bloom_word) != mask) {
|
||||
LOGE("Symbol '%s' (hash %u) filtered out by GNU Bloom Filter (idx %zu, mask 0x%lx, word 0x%lx)",
|
||||
name, hash, bloom_idx, (unsigned long)mask, (unsigned long)bloom_word);
|
||||
/* INFO: Very loggy -- generates too much noise. GNU is rarely used for Zygisk context. */
|
||||
/* LOGW("Symbol '%s' (hash %u) filtered out by GNU Bloom Filter (idx %zu, mask 0x%lx, word 0x%lx)",
|
||||
name, hash, bloom_idx, (unsigned long)mask, (unsigned long)bloom_word);
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
2
loader/src/external/lsplt
vendored
2
loader/src/external/lsplt
vendored
Submodule loader/src/external/lsplt updated: 984804293e...dc62fbe05e
@@ -4,6 +4,9 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define IS_ISOLATED_SERVICE(uid) \
|
||||
((uid) >= 90000 && (uid) < 1000000)
|
||||
|
||||
/*
|
||||
* Bionic's atoi runs through strtol().
|
||||
* Use our own implementation for faster conversion.
|
||||
|
||||
97
loader/src/injector/clear.c
Normal file
97
loader/src/injector/clear.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/audit.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
static bool seccomp_filters_visible() {
|
||||
FILE *status_file = fopen("/proc/self/status", "r");
|
||||
if (!status_file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *needle = "Seccomp_filters:";
|
||||
char line[256];
|
||||
|
||||
while (fgets(line, sizeof(line), status_file)) {
|
||||
if (strncmp(line, needle, strlen(needle)) == 0) {
|
||||
fclose(status_file);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(status_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
void send_seccomp_event() {
|
||||
if (seccomp_filters_visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
__u32 args[4] = {0};
|
||||
|
||||
int rnd_fd = open("/dev/urandom", O_RDONLY);
|
||||
if (rnd_fd == -1) {
|
||||
PLOGE("send_seccomp_event: open(/dev/urandom)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (read(rnd_fd, &args, sizeof(args)) != sizeof(args)) {
|
||||
PLOGE("send_seccomp_event: read(rnd_fd)");
|
||||
close(rnd_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
close(rnd_fd);
|
||||
|
||||
args[0] |= 0x10000;
|
||||
|
||||
struct sock_filter filter[] = {
|
||||
/* INFO: Check syscall number */
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_exit_group, 0, 9),
|
||||
|
||||
/* INFO: Load and check arg0 (lower 32 bits) */
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, args[0], 0, 7),
|
||||
|
||||
/* INFO: Load and check arg1 (lower 32 bits) */
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[1])),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, args[1], 0, 5),
|
||||
|
||||
/* INFO: Load and check arg2 (lower 32 bits) */
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[2])),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, args[2], 0, 3),
|
||||
|
||||
/* INFO: Load and check arg3 (lower 32 bits) */
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[3])),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, args[3], 0, 1),
|
||||
|
||||
/* INFO: All match: return TRACE => will trigger PTRACE_EVENT_SECCOMP */
|
||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE),
|
||||
|
||||
/* INFO: Default: allow */
|
||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
|
||||
};
|
||||
|
||||
struct sock_fprog prog = {
|
||||
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
|
||||
.filter = filter,
|
||||
};
|
||||
|
||||
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
|
||||
PLOGE("send_seccomp_event: prctl(SECCOMP)");
|
||||
return;
|
||||
}
|
||||
|
||||
/* INFO: This will trigger a ptrace event, syscall will not execute due to tracee_skip_syscall */
|
||||
syscall(__NR_exit_group, args[0], args[1], args[2], args[3]);
|
||||
}
|
||||
@@ -22,5 +22,8 @@ void entry(void* addr, size_t size, const char* path) {
|
||||
|
||||
LOGD("start plt hooking");
|
||||
hook_functions();
|
||||
clean_trace(path, 1, 0, false);
|
||||
|
||||
void *module_addrs[1] = { addr };
|
||||
clean_trace(path, module_addrs, 1, 1, 0, false);
|
||||
send_seccomp_event();
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ struct ZygiskContext {
|
||||
~ZygiskContext();
|
||||
|
||||
/* Zygisksu changed: Load module fds */
|
||||
bool load_modules_only();
|
||||
void run_modules_pre();
|
||||
void run_modules_post();
|
||||
DCL_PRE_POST(fork)
|
||||
@@ -124,6 +125,8 @@ struct ZygiskContext {
|
||||
vector<tuple<dev_t, ino_t, const char *, void **>> *plt_hook_list;
|
||||
map<string, vector<JNINativeMethod>> *jni_hook_list;
|
||||
bool should_unmap_zygisk = false;
|
||||
bool enable_unloader = false;
|
||||
bool hooked_unloader = false;
|
||||
std::vector<lsplt::MapInfo> cached_map_infos = {};
|
||||
|
||||
} // namespace
|
||||
@@ -215,6 +218,9 @@ DCL_HOOK_FUNC(int, pthread_attr_setstacksize, void *target, size_t size) {
|
||||
int res = old_pthread_attr_setstacksize((pthread_attr_t *)target, size);
|
||||
LOGV("Call pthread_attr_setstacksize in [tid, pid]: %d, %d", gettid(), getpid());
|
||||
|
||||
if (!enable_unloader)
|
||||
return res;
|
||||
|
||||
// Only perform unloading on the main thread
|
||||
if (gettid() != getpid())
|
||||
return res;
|
||||
@@ -249,6 +255,27 @@ DCL_HOOK_FUNC(char *, strdup, const char *s) {
|
||||
return old_strdup(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* INFO: Our goal is to get called after libart.so is loaded, but before ART actually starts running.
|
||||
* If we are too early, we won't find libart.so in maps, and if we are too late, we could make other
|
||||
* threads crash if they try to use the PLT while we are in the process of hooking it.
|
||||
* For this task, hooking property_get was chosen as there are lots of calls to this, so it's
|
||||
* relatively unlikely to break.
|
||||
*
|
||||
* The line where libart.so is loaded is:
|
||||
* https://github.com/aosp-mirror/platform_frameworks_base/blob/1cdfff555f4a21f71ccc978290e2e212e2f8b168/core/jni/AndroidRuntime.cpp#L1266
|
||||
*
|
||||
* And shortly after that, in the startVm method that is called right after, there are many calls to property_get:
|
||||
* https://github.com/aosp-mirror/platform_frameworks_base/blob/1cdfff555f4a21f71ccc978290e2e212e2f8b168/core/jni/AndroidRuntime.cpp#L791
|
||||
*
|
||||
* After we succeed in getting called at a point where libart.so is already loaded, we will ignore
|
||||
* the rest of the property_get calls.
|
||||
*/
|
||||
DCL_HOOK_FUNC(int, property_get, const char *key, char *value, const char *default_value) {
|
||||
hook_unloader();
|
||||
return old_property_get(key, value, default_value);
|
||||
}
|
||||
|
||||
#undef DCL_HOOK_FUNC
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@@ -601,9 +628,11 @@ void ZygiskContext::sanitize_fds() {
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir))) {
|
||||
int fd = parse_int(entry->d_name);
|
||||
if (fd < 0 || fd < MAX_FD_SIZE || fd == dfd || allowed_fds[fd]) continue;
|
||||
if (fd < 0 || fd > MAX_FD_SIZE || fd == dfd || allowed_fds[fd]) continue;
|
||||
|
||||
close(fd);
|
||||
|
||||
LOGW("Closed leaked fd: %d", fd);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
@@ -615,13 +644,12 @@ void ZygiskContext::fork_post() {
|
||||
g_ctx = nullptr;
|
||||
}
|
||||
|
||||
/* Zygisksu changed: Load module fds */
|
||||
void ZygiskContext::run_modules_pre() {
|
||||
bool ZygiskContext::load_modules_only() {
|
||||
struct zygisk_modules ms;
|
||||
if (rezygiskd_read_modules(&ms) == false) {
|
||||
LOGE("Failed to read modules from zygiskd");
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ms.modules_count; i++) {
|
||||
@@ -648,6 +676,11 @@ void ZygiskContext::run_modules_pre() {
|
||||
|
||||
free_modules(&ms);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Zygisksu changed: Load module fds */
|
||||
void ZygiskContext::run_modules_pre() {
|
||||
for (auto &m : modules) {
|
||||
m.onLoad(env);
|
||||
|
||||
@@ -669,7 +702,19 @@ void ZygiskContext::run_modules_post() {
|
||||
|
||||
if (modules.size() > 0) {
|
||||
LOGD("modules unloaded: %zu/%zu", modules_unloaded, modules.size());
|
||||
clean_trace("/data/adb", modules.size(), modules_unloaded, true);
|
||||
|
||||
/* INFO: While Variable Length Arrays (VLAs) aren't usually
|
||||
recommended due to the ease of using too much of the
|
||||
stack, this should be fine since it should not be
|
||||
possible to exhaust the stack with only a few addresses. */
|
||||
void *module_addrs[modules.size() * sizeof(void *)];
|
||||
|
||||
size_t i = 0;
|
||||
for (const auto &m : modules) {
|
||||
module_addrs[i++] = m.getEntry();
|
||||
}
|
||||
|
||||
clean_trace("/data/adb", module_addrs, modules.size(), modules.size(), modules_unloaded, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +722,48 @@ void ZygiskContext::run_modules_post() {
|
||||
void ZygiskContext::app_specialize_pre() {
|
||||
flags[APP_SPECIALIZE] = true;
|
||||
|
||||
info_flags = rezygiskd_get_process_flags(g_ctx->args.app->uid, (const char *const)process);
|
||||
/* INFO: Isolated services have different UIDs than the main apps. Because
|
||||
numerous root implementations base themselves in the UID of the
|
||||
app, we need to ensure that the UID sent to ReZygiskd to search
|
||||
is the app's and not the isolated service, or else it will be
|
||||
able to bypass DenyList.
|
||||
|
||||
All apps, and isolated processes, of *third-party* applications will
|
||||
have their app_data_dir set. The system applications might not have
|
||||
one, however it is unlikely they will create an isolated process,
|
||||
and even if so, it should not impact in detections, performance or
|
||||
any area.
|
||||
*/
|
||||
uid_t uid = args.app->uid;
|
||||
if (IS_ISOLATED_SERVICE(uid) && args.app->app_data_dir) {
|
||||
/* INFO: If the app is an isolated service, we use the UID of the
|
||||
app's process data directory, which is the UID of the
|
||||
app itself, which root implementations actually use.
|
||||
*/
|
||||
const char *data_dir = env->GetStringUTFChars(args.app->app_data_dir, NULL);
|
||||
if (!data_dir) {
|
||||
LOGE("Failed to get app data directory");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(data_dir, &st) == -1) {
|
||||
PLOGE("Failed to stat app data directory [%s]", data_dir);
|
||||
|
||||
env->ReleaseStringUTFChars(args.app->app_data_dir, data_dir);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uid = st.st_uid;
|
||||
|
||||
LOGD("Isolated service being related to UID %d, app data dir: %s", uid, data_dir);
|
||||
|
||||
env->ReleaseStringUTFChars(args.app->app_data_dir, data_dir);
|
||||
}
|
||||
|
||||
info_flags = rezygiskd_get_process_flags(uid, (const char *const)process);
|
||||
if (info_flags & PROCESS_IS_FIRST_STARTED) {
|
||||
/* INFO: To ensure we are really using a clean mount namespace, we use
|
||||
the first process it as reference for clean mount namespace,
|
||||
@@ -687,33 +773,64 @@ void ZygiskContext::app_specialize_pre() {
|
||||
update_mnt_ns(Clean, true);
|
||||
}
|
||||
|
||||
if ((info_flags & (PROCESS_IS_MANAGER | PROCESS_ROOT_IS_MAGISK)) == (PROCESS_IS_MANAGER | PROCESS_ROOT_IS_MAGISK)) {
|
||||
if ((info_flags & PROCESS_IS_MANAGER) == PROCESS_IS_MANAGER) {
|
||||
LOGD("Manager process detected. Notifying that Zygisk has been enabled.");
|
||||
|
||||
/* INFO: This environment variable is related to Magisk Zygisk/Manager. It
|
||||
it used by Magisk's Zygisk to communicate to Magisk Manager whether
|
||||
Zygisk is working or not.
|
||||
Zygisk is working or not, allowing Zygisk modules to both work properly
|
||||
and for the manager to mark Zygisk as enabled.
|
||||
|
||||
To allow Zygisk modules to both work properly and for the manager to
|
||||
identify Zygisk, being it not built-in, as working, we also set it. */
|
||||
However, to enhance capabilities of root managers, it is also set for
|
||||
any other supported manager, so that, if they wish, they can recognize
|
||||
if Zygisk is enabled.
|
||||
*/
|
||||
setenv("ZYGISK_ENABLED", "1", 1);
|
||||
} else {
|
||||
/* INFO: Modules only have two "start off" points from Zygisk, preSpecialize and
|
||||
postSpecialize. While preSpecialie in fact runs with Zygote (not superuser)
|
||||
privileges, in postSpecialize it will now be with lower permission, in
|
||||
the app's sandbox and therefore can move to a clean mount namespace after
|
||||
executing the modules preSpecialize.
|
||||
/* INFO: Because we load directly from the file, we need to do it before we umount
|
||||
the mounts, or else it won't have access to /data/adb anymore.
|
||||
*/
|
||||
if ((info_flags & PROCESS_ON_DENYLIST) == PROCESS_ON_DENYLIST) {
|
||||
flags[DO_REVERT_UNMOUNT] = true;
|
||||
if (!load_modules_only()) {
|
||||
LOGE("Failed to load modules");
|
||||
|
||||
update_mnt_ns(Clean, false);
|
||||
return;
|
||||
}
|
||||
|
||||
/* INFO: Modules only have two "start off" points from Zygisk, preSpecialize and
|
||||
postSpecialize. In preSpecialize, the process still has privileged
|
||||
permissions, and therefore can execute mount/umount/setns functions.
|
||||
If we update the mount namespace AFTER executing them, any mounts made
|
||||
will be lost, and the process will not have access to them anymore.
|
||||
|
||||
In postSpecialize, while still could have its mounts modified with the
|
||||
assistance of a Zygisk companion, it will already have the mount
|
||||
namespace switched by then, so there won't be issues.
|
||||
|
||||
Knowing this, we update the mns before execution, so that they can still
|
||||
make changes to mounts in DenyListed processes without being reverted.
|
||||
*/
|
||||
bool in_denylist = (info_flags & PROCESS_ON_DENYLIST) == PROCESS_ON_DENYLIST;
|
||||
if (in_denylist) {
|
||||
flags[DO_REVERT_UNMOUNT] = true;
|
||||
|
||||
update_mnt_ns(Clean, false);
|
||||
}
|
||||
|
||||
/* INFO: Executed after setns to ensure a module can update the mounts of an
|
||||
application without worrying about it being overwritten by setns.
|
||||
*/
|
||||
run_modules_pre();
|
||||
|
||||
/* INFO: The modules may request that although the process is NOT in
|
||||
the DenyList, it has its mount namespace switched to the clean
|
||||
one.
|
||||
|
||||
So to ensure this behavior happens, we must also check after the
|
||||
modules are loaded and executed, so that the modules can have
|
||||
the chance to request it.
|
||||
*/
|
||||
if (!in_denylist && flags[DO_REVERT_UNMOUNT])
|
||||
update_mnt_ns(Clean, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,6 +876,7 @@ void ZygiskContext::nativeForkSystemServer_pre() {
|
||||
if (!is_child())
|
||||
return;
|
||||
|
||||
load_modules_only();
|
||||
run_modules_pre();
|
||||
rezygiskd_system_server_started();
|
||||
|
||||
@@ -821,7 +939,7 @@ ZygiskContext::~ZygiskContext() {
|
||||
m.clearApi();
|
||||
}
|
||||
|
||||
hook_unloader();
|
||||
enable_unloader = true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -849,19 +967,35 @@ static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_
|
||||
#define PLT_HOOK_REGISTER(DEV, INODE, NAME) \
|
||||
PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME)
|
||||
|
||||
void clean_trace(const char* path, size_t load, size_t unload, bool spoof_maps) {
|
||||
/* INFO: module_addrs_length is always the same as "load" */
|
||||
void clean_trace(const char *path, void **module_addrs, size_t module_addrs_length, size_t load, size_t unload, bool spoof_maps) {
|
||||
LOGD("cleaning trace for path %s", path);
|
||||
|
||||
if (load > 0 || unload > 0) solist_reset_counters(load, unload);
|
||||
|
||||
LOGD("Dropping solist record for %s", path);
|
||||
|
||||
bool path_found = solist_drop_so_path(path);
|
||||
if (!path_found || !spoof_maps) return;
|
||||
bool any_dropped = false;
|
||||
for (size_t i = 0; i < module_addrs_length; i++) {
|
||||
bool local_any_dropped = solist_drop_so_path(module_addrs[i]);
|
||||
if (!local_any_dropped) continue;
|
||||
|
||||
any_dropped = true;
|
||||
|
||||
LOGD("Dropped solist record for %p", module_addrs[i]);
|
||||
}
|
||||
|
||||
if (!any_dropped || !spoof_maps) return;
|
||||
|
||||
LOGD("spoofing virtual maps for %s", path);
|
||||
// spoofing map names is futile in Android, we do it simply
|
||||
// to avoid Zygisk detections based on string comparison
|
||||
|
||||
/* INFO: Spoofing maps names is futile, after all it will
|
||||
still show up in /proc/self/(s)maps but with a
|
||||
different name, however still detectable by
|
||||
checking the permissions. This, however, avoids
|
||||
just checking for "zygisk". */
|
||||
|
||||
/* TODO: Use SoList to map through libraries to avoid open /proc/self/maps here */
|
||||
for (auto &map : lsplt::MapInfo::Scan()) {
|
||||
if (strstr(map.path.c_str(), path) && strstr(map.path.c_str(), "libzygisk") == 0)
|
||||
{
|
||||
@@ -877,8 +1011,8 @@ void clean_trace(const char* path, size_t load, size_t unload, bool spoof_maps)
|
||||
mprotect(addr, size, PROT_READ);
|
||||
}
|
||||
memcpy(copy, addr, size);
|
||||
mprotect(copy, size, map.perms);
|
||||
mremap(copy, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, addr);
|
||||
mprotect(addr, size, map.perms);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -903,6 +1037,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(android_runtime_dev, android_runtime_inode, property_get);
|
||||
hook_commit();
|
||||
|
||||
// Remove unhooked methods
|
||||
@@ -913,9 +1048,13 @@ void hook_functions() {
|
||||
}
|
||||
|
||||
static void hook_unloader() {
|
||||
if (hooked_unloader) return;
|
||||
hooked_unloader = true;
|
||||
|
||||
ino_t art_inode = 0;
|
||||
dev_t art_dev = 0;
|
||||
|
||||
cached_map_infos = lsplt::MapInfo::Scan();
|
||||
for (auto &map : cached_map_infos) {
|
||||
if (map.path.ends_with("/libart.so")) {
|
||||
art_inode = map.inode;
|
||||
@@ -925,7 +1064,16 @@ static void hook_unloader() {
|
||||
}
|
||||
|
||||
if (art_dev == 0 || art_inode == 0) {
|
||||
/*
|
||||
* INFO: If we are here, it means we are too early and libart.so hasn't loaded yet when
|
||||
* property_get was called. This doesn't normally happen, but we try again next time
|
||||
* just to be safe.
|
||||
*/
|
||||
|
||||
LOGE("virtual map for libart.so is not cached");
|
||||
|
||||
hooked_unloader = false;
|
||||
|
||||
return;
|
||||
} else {
|
||||
LOGD("hook_unloader called with libart.so [%zu:%lu]", art_dev, art_inode);
|
||||
|
||||
@@ -213,6 +213,7 @@ case 5: \
|
||||
bool tryUnload() const { return unload && dlclose(handle) == 0; };
|
||||
void clearApi() { memset(&api, 0, sizeof(api)); }
|
||||
int getId() const { return id; }
|
||||
void *getEntry() const { return entry.ptr; }
|
||||
|
||||
ZygiskModule(int id, void *handle, void *entry);
|
||||
|
||||
|
||||
@@ -2,27 +2,28 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <android/dlext.h>
|
||||
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "elf_util.h"
|
||||
#include "logging.h"
|
||||
|
||||
#include "solist.h"
|
||||
|
||||
/* TODO: Is offset for realpath necessary? It seems to have the function
|
||||
available anywhere. */
|
||||
#ifdef __LP64__
|
||||
size_t solist_size_offset = 0x18;
|
||||
size_t solist_next_offset = 0x30;
|
||||
size_t solist_realpath_offset = 0x1a8;
|
||||
#else
|
||||
size_t solist_size_offset = 0x90;
|
||||
size_t solist_next_offset = 0xa4;
|
||||
size_t solist_realpath_offset = 0x174;
|
||||
#endif
|
||||
|
||||
static const char *(*get_realpath_sym)(SoInfo *) = NULL;
|
||||
static void (*soinfo_free)(SoInfo *) = NULL;
|
||||
|
||||
static inline SoInfo *get_next(SoInfo *self) {
|
||||
return *(SoInfo **)((uintptr_t)self + solist_next_offset);
|
||||
}
|
||||
static SoInfo *(*find_containing_library)(const void *p) = NULL;
|
||||
|
||||
static inline const char *get_path(SoInfo *self) {
|
||||
if (get_realpath_sym)
|
||||
@@ -35,11 +36,7 @@ static inline void set_size(SoInfo *self, size_t size) {
|
||||
*(size_t *) ((uintptr_t)self + solist_size_offset) = size;
|
||||
}
|
||||
|
||||
static inline size_t get_size(SoInfo *self) {
|
||||
return *(size_t *) ((uintptr_t)self + solist_size_offset);
|
||||
}
|
||||
|
||||
struct pdg ppdg;
|
||||
struct pdg ppdg = { 0 };
|
||||
|
||||
static bool pdg_setup(ElfImg *img) {
|
||||
ppdg.ctor = (void *(*)())getSymbAddress(img, "__dl__ZN18ProtectedDataGuardC2Ev");
|
||||
@@ -48,22 +45,20 @@ static bool pdg_setup(ElfImg *img) {
|
||||
return ppdg.ctor != NULL && ppdg.dtor != NULL;
|
||||
}
|
||||
|
||||
static void pdg_protect() {
|
||||
if (ppdg.ctor != NULL)
|
||||
(*(ppdg.ctor))();
|
||||
}
|
||||
|
||||
/* INFO: Allow data to be written to the areas. */
|
||||
static void pdg_unprotect() {
|
||||
if (ppdg.dtor != NULL)
|
||||
(*(ppdg.dtor))();
|
||||
(*ppdg.ctor)();
|
||||
}
|
||||
|
||||
static SoInfo *solist = NULL;
|
||||
static SoInfo *somain = NULL;
|
||||
static SoInfo **sonext = NULL;
|
||||
/* INFO: Block write and only allow read access to the areas. */
|
||||
static void pdg_protect() {
|
||||
(*ppdg.dtor)();
|
||||
}
|
||||
|
||||
static uint64_t *g_module_load_counter = NULL;
|
||||
static uint64_t *g_module_unload_counter = NULL;
|
||||
static SoInfo *somain = NULL;
|
||||
|
||||
static size_t *g_module_load_counter = NULL;
|
||||
static size_t *g_module_unload_counter = NULL;
|
||||
|
||||
static bool solist_init() {
|
||||
#ifdef __LP64__
|
||||
@@ -77,10 +72,6 @@ static bool solist_init() {
|
||||
return false;
|
||||
}
|
||||
|
||||
ppdg = (struct pdg) {
|
||||
.ctor = NULL,
|
||||
.dtor = NULL
|
||||
};
|
||||
if (!pdg_setup(linker)) {
|
||||
LOGE("Failed to setup pdg");
|
||||
|
||||
@@ -96,17 +87,6 @@ static bool solist_init() {
|
||||
|
||||
See #63 for more information.
|
||||
*/
|
||||
solist = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL6solist");
|
||||
if ((void *)solist == NULL) {
|
||||
LOGE("Failed to find solist __dl__ZL6solist*");
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("%p is solist", (void *)solist);
|
||||
|
||||
somain = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL6somain");
|
||||
if (somain == NULL) {
|
||||
LOGE("Failed to find somain __dl__ZL6somain*");
|
||||
@@ -118,20 +98,6 @@ static bool solist_init() {
|
||||
|
||||
LOGD("%p is somain", (void *)somain);
|
||||
|
||||
sonext = (SoInfo **)getSymbAddressByPrefix(linker, "__dl__ZL6sonext");
|
||||
if (sonext == NULL) {
|
||||
LOGE("Failed to find sonext __dl__ZL6sonext*");
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("%p is sonext", (void *)sonext);
|
||||
|
||||
SoInfo *vdso = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL4vdso");
|
||||
if (vdso != NULL) LOGD("%p is vdso", (void *)vdso);
|
||||
|
||||
get_realpath_sym = (const char *(*)(SoInfo *))getSymbAddress(linker, "__dl__ZNK6soinfo12get_realpathEv");
|
||||
if (get_realpath_sym == NULL) {
|
||||
LOGE("Failed to find get_realpath __dl__ZNK6soinfo12get_realpathEv");
|
||||
@@ -154,25 +120,28 @@ static bool solist_init() {
|
||||
|
||||
LOGD("%p is soinfo_free", (void *)soinfo_free);
|
||||
|
||||
g_module_load_counter = (uint64_t *)getSymbAddress(linker, "__dl__ZL21g_module_load_counter");
|
||||
find_containing_library = (SoInfo *(*)(const void *))getSymbAddress(linker, "__dl__Z23find_containing_libraryPKv");
|
||||
if (find_containing_library == NULL) {
|
||||
LOGE("Failed to find find_containing_library __dl__Z23find_containing_libraryPKv");
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
g_module_load_counter = (size_t *)getSymbAddress(linker, "__dl__ZL21g_module_load_counter");
|
||||
if (g_module_load_counter != NULL) LOGD("found symbol g_module_load_counter");
|
||||
|
||||
g_module_unload_counter = (uint64_t *)getSymbAddress(linker, "__dl__ZL23g_module_unload_counter");
|
||||
g_module_unload_counter = (size_t *)getSymbAddress(linker, "__dl__ZL23g_module_unload_counter");
|
||||
if (g_module_unload_counter != NULL) LOGD("found symbol g_module_unload_counter");
|
||||
|
||||
for (size_t i = 0; i < 1024 / sizeof(void *); i++) {
|
||||
uintptr_t possible_field = (uintptr_t)solist + i * sizeof(void *);
|
||||
size_t possible_size_of_somain = *(size_t *)((uintptr_t)somain + i * sizeof(void *));
|
||||
|
||||
if (possible_size_of_somain < 0x100000 && possible_size_of_somain > 0x100) {
|
||||
solist_size_offset = i * sizeof(void *);
|
||||
|
||||
LOGD("solist_size_offset is %zu * %zu = %p", i, sizeof(void *), (void *)solist_size_offset);
|
||||
}
|
||||
|
||||
if (*(void **)possible_field == somain || (vdso != NULL && *(void **)possible_field == vdso)) {
|
||||
solist_next_offset = i * sizeof(void *);
|
||||
LOGD("solist_next_offset is %zu * %zu = %p", i, sizeof(void *), (void *)solist_next_offset);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -183,36 +152,49 @@ static bool solist_init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solist_drop_so_path(const char *target_path) {
|
||||
if (solist == NULL && !solist_init()) {
|
||||
/* INFO: find_containing_library returns the SoInfo for the library that contains
|
||||
that memory inside its limits, hence why named "lib_memory" in ReZygisk. */
|
||||
bool solist_drop_so_path(void *lib_memory) {
|
||||
if (somain == NULL && !solist_init()) {
|
||||
LOGE("Failed to initialize solist");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (SoInfo *iter = solist; iter; iter = get_next(iter)) {
|
||||
if (get_path(iter) && strstr(get_path(iter), target_path)) {
|
||||
pdg_protect();
|
||||
SoInfo *found = (*find_containing_library)(lib_memory);
|
||||
if (found == NULL) {
|
||||
LOGD("Could not find containing library for %p", lib_memory);
|
||||
|
||||
LOGV("dropping solist record loaded at %s with size %zu", get_path(iter), get_size(iter));
|
||||
if (get_size(iter) > 0) {
|
||||
set_size(iter, 0);
|
||||
soinfo_free(iter);
|
||||
|
||||
pdg_unprotect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pdg_unprotect();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
LOGD("Found so path for %p: %s", lib_memory, get_path(found));
|
||||
|
||||
char path[PATH_MAX];
|
||||
if (get_path(found) == NULL) {
|
||||
LOGE("Failed to get path for %p", found);
|
||||
|
||||
return false;
|
||||
}
|
||||
strcpy(path, get_path(found));
|
||||
|
||||
pdg_unprotect();
|
||||
|
||||
set_size(found, 0);
|
||||
soinfo_free(found);
|
||||
|
||||
pdg_protect();
|
||||
|
||||
LOGD("Successfully dropped so path for: %s", path);
|
||||
|
||||
/* INFO: Let's avoid trouble regarding detections */
|
||||
memset(path, strlen(path), 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void solist_reset_counters(size_t load, size_t unload) {
|
||||
if (solist == NULL && !solist_init()) {
|
||||
if (somain == NULL && !solist_init()) {
|
||||
LOGE("Failed to initialize solist");
|
||||
|
||||
return;
|
||||
@@ -224,18 +206,18 @@ void solist_reset_counters(size_t load, size_t unload) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t loaded_modules = *g_module_load_counter;
|
||||
uint64_t unloaded_modules = *g_module_unload_counter;
|
||||
size_t loaded_modules = *g_module_load_counter;
|
||||
size_t unloaded_modules = *g_module_unload_counter;
|
||||
|
||||
if (loaded_modules >= load) {
|
||||
*g_module_load_counter = loaded_modules - load;
|
||||
*g_module_load_counter -= load;
|
||||
|
||||
LOGD("reset g_module_load_counter to %zu", (size_t) *g_module_load_counter);
|
||||
LOGD("reset g_module_load_counter to %zu", *g_module_load_counter);
|
||||
}
|
||||
|
||||
if (unloaded_modules >= unload) {
|
||||
*g_module_unload_counter = unloaded_modules - unload;
|
||||
*g_module_unload_counter -= unload;
|
||||
|
||||
LOGD("reset g_module_unload_counter to %zu", (size_t) *g_module_unload_counter);
|
||||
LOGD("reset g_module_unload_counter to %zu", *g_module_unload_counter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ struct pdg {
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#1712
|
||||
*/
|
||||
bool solist_drop_so_path(const char *target_path);
|
||||
bool solist_drop_so_path(void *lib_memory);
|
||||
|
||||
/*
|
||||
INFO: When dlopen'ing a library, the system will increment 1 to a global
|
||||
|
||||
@@ -7,4 +7,6 @@ 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 clean_trace(const char *path, void **module_addrs, size_t module_addrs_length, size_t load, size_t unload, bool spoof_maps);
|
||||
|
||||
extern "C" void send_seccomp_event();
|
||||
|
||||
@@ -159,17 +159,43 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
void *libc_return_addr = find_module_return_addr(map, "libc.so");
|
||||
LOGD("libc return addr %p", libc_return_addr);
|
||||
|
||||
const char *libdl_path = NULL;
|
||||
const char *libc_path = NULL;
|
||||
for (size_t i = 0; i < local_map->size; i++) {
|
||||
if (local_map->maps[i].path == NULL) continue;
|
||||
|
||||
const char *filename = position_after(local_map->maps[i].path, '/');
|
||||
|
||||
if (strcmp(filename, "libdl.so") == 0) {
|
||||
libdl_path = local_map->maps[i].path;
|
||||
|
||||
/* INFO: If we had found libc.so too, no need to continue searching */
|
||||
if (libc_path) break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(filename, "libc.so") == 0) {
|
||||
libc_path = local_map->maps[i].path;
|
||||
|
||||
/* INFO: If we had found libdl.so too, no need to continue searching */
|
||||
if (libdl_path) break;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* call dlopen */
|
||||
#ifdef __LP64__
|
||||
void *dlopen_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlopen");
|
||||
#else
|
||||
void *dlopen_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlopen");
|
||||
#endif
|
||||
if (dlopen_addr == NULL) {
|
||||
void *dlopen_addr = NULL;
|
||||
if (!libdl_path || (dlopen_addr = find_func_addr(local_map, map, libdl_path, "dlopen")) == NULL) {
|
||||
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */
|
||||
LOGW("Failed to find dlopen from libdl.so, will load from linker");
|
||||
|
||||
dlopen_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlopen");
|
||||
#ifdef __LP64__
|
||||
dlopen_addr = find_func_addr(local_map, map, "/system/bin/linker64", "__dl_dlopen");
|
||||
#else
|
||||
dlopen_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlopen");
|
||||
#endif
|
||||
if (dlopen_addr == NULL) {
|
||||
PLOGE("Find __dl_dlopen");
|
||||
|
||||
@@ -198,16 +224,16 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
LOGE("handle is null");
|
||||
|
||||
/* call dlerror */
|
||||
#ifdef __LP64__
|
||||
void *dlerror_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlerror");
|
||||
#else
|
||||
void *dlerror_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlerror");
|
||||
#endif
|
||||
if (dlerror_addr == NULL) {
|
||||
void *dlerror_addr = NULL;
|
||||
if (!libdl_path || (dlerror_addr = find_func_addr(local_map, map, libdl_path, "dlerror")) == NULL) {
|
||||
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */
|
||||
LOGW("Failed to find dlerror from libdl.so, will load from linker");
|
||||
|
||||
dlerror_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlerror");
|
||||
#ifdef __LP64__
|
||||
dlerror_addr = find_func_addr(local_map, map, "/system/bin/linker64", "__dl_dlerror");
|
||||
#else
|
||||
dlerror_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlerror");
|
||||
#endif
|
||||
if (dlerror_addr == NULL) {
|
||||
LOGE("Find __dl_dlerror");
|
||||
|
||||
@@ -225,19 +251,19 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
LOGE("dlerror str is null");
|
||||
|
||||
free(args);
|
||||
free_maps(local_map);
|
||||
free_maps(map);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __LP64__
|
||||
void *strlen_addr = find_func_addr(local_map, map, "/system/lib64/libc.so", "strlen");
|
||||
#else
|
||||
void *strlen_addr = find_func_addr(local_map, map, "/system/lib/libc.so", "strlen");
|
||||
#endif
|
||||
void *strlen_addr = find_func_addr(local_map, map, libc_path, "strlen");
|
||||
if (strlen_addr == NULL) {
|
||||
LOGE("find strlen");
|
||||
|
||||
free(args);
|
||||
free_maps(local_map);
|
||||
free_maps(map);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -249,6 +275,8 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
LOGE("dlerror len <= 0");
|
||||
|
||||
free(args);
|
||||
free_maps(local_map);
|
||||
free_maps(map);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -258,6 +286,8 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
LOGE("malloc err");
|
||||
|
||||
free(args);
|
||||
free_maps(local_map);
|
||||
free_maps(map);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -269,20 +299,23 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
free(err);
|
||||
free(args);
|
||||
|
||||
free_maps(local_map);
|
||||
free_maps(map);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* call dlsym(handle, "entry") */
|
||||
#ifdef __LP64__
|
||||
void *dlsym_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlsym");
|
||||
#else
|
||||
void *dlsym_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlsym");
|
||||
#endif
|
||||
if (dlsym_addr == NULL) {
|
||||
void *dlsym_addr = NULL;
|
||||
if (!libdl_path || (dlsym_addr = find_func_addr(local_map, map, libdl_path, "dlsym")) == NULL) {
|
||||
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */
|
||||
LOGW("Failed to find dlsym from libdl.so, will load from linker");
|
||||
|
||||
dlsym_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlsym");
|
||||
#ifdef __LP64__
|
||||
dlsym_addr = find_func_addr(local_map, map, "/system/bin/linker64", "__dl_dlsym");
|
||||
#else
|
||||
dlsym_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlsym");
|
||||
#endif
|
||||
if (dlsym_addr == NULL) {
|
||||
LOGE("find __dl_dlsym");
|
||||
|
||||
@@ -370,7 +403,7 @@ bool trace_zygote(int pid) {
|
||||
|
||||
int status;
|
||||
|
||||
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL) == -1) {
|
||||
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACESECCOMP) == -1) {
|
||||
PLOGE("seize");
|
||||
|
||||
return false;
|
||||
@@ -405,6 +438,16 @@ bool trace_zygote(int pid) {
|
||||
if (STOPPED_WITH(SIGCONT, 0)) {
|
||||
LOGD("received SIGCONT");
|
||||
|
||||
/* INFO: Due to kernel bugs, fixed in 5.16+, ptrace_message (msg of
|
||||
PTRACE_GETEVENTMSG) may not represent the current state of
|
||||
the process. Because we set some options, which alters the
|
||||
ptrace_message, we need to call PTRACE_SYSCALL to reset the
|
||||
ptrace_message to 0, the default/normal state.
|
||||
*/
|
||||
ptrace(PTRACE_SYSCALL, pid, 0, 0);
|
||||
|
||||
WAIT_OR_DIE
|
||||
|
||||
ptrace(PTRACE_DETACH, pid, 0, SIGCONT);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -313,20 +313,12 @@ void *find_module_return_addr(struct maps *map, const char *suffix) {
|
||||
}
|
||||
|
||||
void *find_module_base(struct maps *map, const char *file) {
|
||||
const char *suffix = position_after(file, '/');
|
||||
if (!suffix) {
|
||||
LOGE("failed to find suffix in %s", file);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < map->size; i++) {
|
||||
if (map->maps[i].path == NULL) continue;
|
||||
|
||||
const char *file_name = position_after(map->maps[i].path, '/');
|
||||
if (!file_name) continue;
|
||||
const char *file_path = map->maps[i].path;
|
||||
|
||||
if (strlen(file_name) < strlen(suffix) || map->maps[i].offset != 0 || strncmp(file_name, suffix, strlen(suffix)) != 0) continue;
|
||||
if (strlen(file_path) != strlen(file) || map->maps[i].offset != 0 || strncmp(file_path, file, strlen(file)) != 0) continue;
|
||||
|
||||
return (void *)map->maps[i].start;
|
||||
}
|
||||
@@ -530,6 +522,32 @@ int fork_dont_care() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
void tracee_skip_syscall(int pid) {
|
||||
struct user_regs_struct regs;
|
||||
if (!get_regs(pid, ®s)) {
|
||||
LOGE("failed to get seccomp regs");
|
||||
exit(1);
|
||||
}
|
||||
regs.REG_SYSNR = -1;
|
||||
if (!set_regs(pid, ®s)) {
|
||||
LOGE("failed to set seccomp regs");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* INFO: It might not work, don't check for error */
|
||||
#if defined(__aarch64__)
|
||||
int sysnr = -1;
|
||||
struct iovec iov = {
|
||||
.iov_base = &sysnr,
|
||||
.iov_len = sizeof (int),
|
||||
};
|
||||
ptrace(PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL, &iov);
|
||||
#elif defined(__arm__)
|
||||
ptrace(PTRACE_SET_SYSCALL, pid, 0, (void*) -1);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void wait_for_trace(int pid, int *status, int flags) {
|
||||
while (1) {
|
||||
pid_t result = waitpid(pid, status, flags);
|
||||
@@ -540,7 +558,13 @@ void wait_for_trace(int pid, int *status, int flags) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(*status)) {
|
||||
if (*status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
|
||||
tracee_skip_syscall(pid);
|
||||
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
|
||||
continue;
|
||||
} else if (!WIFSTOPPED(*status)) {
|
||||
char status_str[64];
|
||||
parse_status(*status, status_str, sizeof(status_str));
|
||||
|
||||
|
||||
@@ -37,18 +37,22 @@ void free_maps(struct maps *maps);
|
||||
#define REG_SP rsp
|
||||
#define REG_IP rip
|
||||
#define REG_RET rax
|
||||
#define REG_SYSNR orig_rax
|
||||
#elif defined(__i386__)
|
||||
#define REG_SP esp
|
||||
#define REG_IP eip
|
||||
#define REG_RET eax
|
||||
#define REG_SYSNR orig_eax
|
||||
#elif defined(__aarch64__)
|
||||
#define REG_SP sp
|
||||
#define REG_IP pc
|
||||
#define REG_RET regs[0]
|
||||
#define REG_SYSNR regs[8]
|
||||
#elif defined(__arm__)
|
||||
#define REG_SP uregs[13]
|
||||
#define REG_IP uregs[15]
|
||||
#define REG_RET uregs[0]
|
||||
#define REG_SYSNR uregs[7]
|
||||
#define user_regs_struct user_regs
|
||||
#endif
|
||||
|
||||
@@ -62,6 +66,8 @@ bool set_regs(int pid, struct user_regs_struct *regs);
|
||||
|
||||
void get_addr_mem_region(struct maps *map, uintptr_t addr, char *buf, size_t buf_size);
|
||||
|
||||
const char *position_after(const char *str, const char needle);
|
||||
|
||||
void *find_module_return_addr(struct maps *map, const char *suffix);
|
||||
|
||||
void *find_func_addr(struct maps *local_info, struct maps *remote_info, const char *module, const char *func);
|
||||
|
||||
93
webroot/lang/ja_JP.json
Normal file
93
webroot/lang/ja_JP.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "日本語",
|
||||
"global": {
|
||||
"unknown": "不明"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "言語を選択してください"
|
||||
},
|
||||
"theme": {
|
||||
"header": "テーマを選択してください",
|
||||
"dark": "ダーク",
|
||||
"light": "ライト",
|
||||
"system": "システムベース"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "コピー",
|
||||
"clear": "すべてのログを消去"
|
||||
},
|
||||
"header": "エラーの履歴",
|
||||
"placeholder": "エラーログは記録されていません!"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "ホーム",
|
||||
"status": {
|
||||
"notWorking": "動作していません",
|
||||
"ok": "動作中",
|
||||
"partially": "部分的に動作中"
|
||||
},
|
||||
"info": {
|
||||
"version": "バージョン",
|
||||
"root": "Root の実装",
|
||||
"zygote": {
|
||||
"injected": "インジェクト済み",
|
||||
"notInjected": "未インジェクト",
|
||||
"unknown": "不明"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "モジュール",
|
||||
"notAvaliable": "Zygisk を使用するモジュールはありません。",
|
||||
"arch": "アーキテクチャ: "
|
||||
},
|
||||
"actions": {
|
||||
"header": "アクション",
|
||||
"monitorButton": {
|
||||
"start": "開始",
|
||||
"stop": "停止",
|
||||
"pause": "一時停止"
|
||||
},
|
||||
"monitor": "監視",
|
||||
"status": {
|
||||
"tracing": "トレース中",
|
||||
"stopping": "停止中",
|
||||
"stopped": "停止済み",
|
||||
"exiting": "終了中",
|
||||
"unknown": "不明"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"header": "設定",
|
||||
"font": {
|
||||
"header": "システムフォントを有効化",
|
||||
"description": "現在の WebUI でシステムフォントの使用を有効化します。注意: FlipFont との互換性がない場合があります"
|
||||
},
|
||||
"theme": {
|
||||
"header": "システムテーマ",
|
||||
"description": "WebUI のシステムテーマを変更します"
|
||||
},
|
||||
"language": {
|
||||
"header": "言語の変更",
|
||||
"description": "言語を変更します"
|
||||
},
|
||||
"logs": {
|
||||
"header": "エラーの履歴",
|
||||
"description": "すべてのエラーログを表示します"
|
||||
},
|
||||
"credits": {
|
||||
"module": "モジュールの開発者",
|
||||
"original": "オリジナルの開発者",
|
||||
"web": "WebUI の開発者"
|
||||
},
|
||||
"license": {
|
||||
"module": "モジュールのライセンス",
|
||||
"web": "WebUI のライセンス"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,11 +121,7 @@ void companion_entry(int fd) {
|
||||
ASSURE_SIZE_WRITE("ZygiskdCompanion", "module_entry", ret, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = SIG_IGN;
|
||||
struct sigaction sa = { .sa_handler = SIG_IGN };
|
||||
sigaction(SIGPIPE, &sa, NULL);
|
||||
|
||||
while (1) {
|
||||
|
||||
@@ -62,6 +62,7 @@ void apatch_get_existence(struct root_impl_state *state) {
|
||||
}
|
||||
|
||||
struct package_config {
|
||||
char *process;
|
||||
uid_t uid;
|
||||
bool root_granted;
|
||||
bool umount_needed;
|
||||
@@ -104,7 +105,7 @@ bool _apatch_get_package_config(struct packages_config *restrict config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
strtok(line, ",");
|
||||
config->configs[config->size].process = strdup(strtok(line, ","));
|
||||
|
||||
char *exclude_str = strtok(NULL, ",");
|
||||
if (exclude_str == NULL) continue;
|
||||
@@ -128,6 +129,10 @@ bool _apatch_get_package_config(struct packages_config *restrict config) {
|
||||
}
|
||||
|
||||
void _apatch_free_package_config(struct packages_config *restrict config) {
|
||||
for (size_t i = 0; i < config->size; i++) {
|
||||
free(config->configs[i].process);
|
||||
}
|
||||
|
||||
free(config->configs);
|
||||
}
|
||||
|
||||
@@ -155,7 +160,7 @@ bool apatch_uid_granted_root(uid_t uid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool apatch_uid_should_umount(uid_t uid) {
|
||||
bool apatch_uid_should_umount(uid_t uid, const char *const process) {
|
||||
struct packages_config config;
|
||||
if (!_apatch_get_package_config(&config)) {
|
||||
_apatch_free_package_config(&config);
|
||||
@@ -174,6 +179,29 @@ bool apatch_uid_should_umount(uid_t uid) {
|
||||
return umount_needed;
|
||||
}
|
||||
|
||||
/* INFO: Isolated services have different UIDs than the main app, and
|
||||
while libzygisk.so has code to send the UID of the app related
|
||||
to the isolated service, we add this so that in case it fails,
|
||||
this should avoid it pass through as Mounted.
|
||||
*/
|
||||
if (IS_ISOLATED_SERVICE(uid)) {
|
||||
size_t targeted_process_length = strlen(process);
|
||||
|
||||
for (size_t i = 0; i < config.size; i++) {
|
||||
size_t config_process_length = strlen(config.configs[i].process);
|
||||
size_t smallest_process_length = targeted_process_length < config_process_length ? targeted_process_length : config_process_length;
|
||||
|
||||
if (strncmp(config.configs[i].process, process, smallest_process_length) != 0) continue;
|
||||
|
||||
/* INFO: This allow us to copy the information to avoid use-after-free */
|
||||
bool umount_needed = config.configs[i].umount_needed;
|
||||
|
||||
_apatch_free_package_config(&config);
|
||||
|
||||
return umount_needed;
|
||||
}
|
||||
}
|
||||
|
||||
_apatch_free_package_config(&config);
|
||||
|
||||
return false;
|
||||
|
||||
@@ -7,7 +7,7 @@ void apatch_get_existence(struct root_impl_state *state);
|
||||
|
||||
bool apatch_uid_granted_root(uid_t uid);
|
||||
|
||||
bool apatch_uid_should_umount(uid_t uid);
|
||||
bool apatch_uid_should_umount(uid_t uid, const char *const process);
|
||||
|
||||
bool apatch_uid_is_manager(uid_t uid);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ void root_impls_setup(void) {
|
||||
impl.impl = Multiple;
|
||||
} else if (state_ksu.state == Supported) {
|
||||
impl.impl = KernelSU;
|
||||
impl.variant = state_ksu.variant;
|
||||
} else if (state_apatch.state == Supported) {
|
||||
impl.impl = APatch;
|
||||
} else if (state_magisk.state == Supported) {
|
||||
@@ -100,7 +101,7 @@ bool uid_should_umount(uid_t uid, const char *const process) {
|
||||
return ksu_uid_should_umount(uid);
|
||||
}
|
||||
case APatch: {
|
||||
return apatch_uid_should_umount(uid);
|
||||
return apatch_uid_should_umount(uid, process);
|
||||
}
|
||||
case Magisk: {
|
||||
return magisk_uid_should_umount(process);
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
#define CMD_GET_VERSION 2
|
||||
#define CMD_UID_GRANTED_ROOT 12
|
||||
#define CMD_UID_SHOULD_UMOUNT 13
|
||||
#define CMD_GET_MANAGER_UID 16
|
||||
#define CMD_HOOK_MODE 0xC0DEAD1A
|
||||
|
||||
static enum kernelsu_variants variant = KOfficial;
|
||||
|
||||
static bool supports_manager_uid_retrieval = false;
|
||||
|
||||
void ksu_get_existence(struct root_impl_state *state) {
|
||||
int version = 0;
|
||||
@@ -42,6 +48,26 @@ void ksu_get_existence(struct root_impl_state *state) {
|
||||
}
|
||||
|
||||
state->state = Supported;
|
||||
|
||||
char mode[16] = { 0 };
|
||||
prctl((signed int)KERNEL_SU_OPTION, CMD_HOOK_MODE, mode, 0, 0);
|
||||
|
||||
if (mode[0] != '\0') state->variant = KNext;
|
||||
else state->variant = KOfficial;
|
||||
|
||||
variant = state->variant;
|
||||
|
||||
/* INFO: CMD_GET_MANAGER_UID is a KernelSU Next feature, however we won't
|
||||
limit to KernelSU Next only in case other forks wish to implement
|
||||
it. */
|
||||
int reply_ok = 0;
|
||||
prctl((signed int)KERNEL_SU_OPTION, CMD_GET_MANAGER_UID, 0, 0, &reply_ok);
|
||||
|
||||
if (reply_ok == KERNEL_SU_OPTION) {
|
||||
LOGI("KernelSU implementation supports CMD_GET_MANAGER_UID.\n");
|
||||
|
||||
supports_manager_uid_retrieval = true;
|
||||
}
|
||||
}
|
||||
else if (version >= 1 && version <= MIN_KSU_VERSION - 1) state->state = TooOld;
|
||||
else state->state = Abnormal;
|
||||
@@ -68,8 +94,24 @@ bool ksu_uid_should_umount(uid_t uid) {
|
||||
}
|
||||
|
||||
bool ksu_uid_is_manager(uid_t uid) {
|
||||
/* INFO: If the manager UID is set, we can use it to check if the UID
|
||||
is the manager UID, which is more reliable than checking
|
||||
the KSU manager data directory, as spoofed builds of
|
||||
KernelSU Next have different package names.
|
||||
*/
|
||||
if (supports_manager_uid_retrieval) {
|
||||
uid_t manager_uid = 0;
|
||||
prctl(KERNEL_SU_OPTION, CMD_GET_MANAGER_UID, &manager_uid, NULL, NULL);
|
||||
|
||||
return uid == manager_uid;
|
||||
}
|
||||
|
||||
const char *manager_path = NULL;
|
||||
if (variant == KOfficial) manager_path = "/data/user_de/0/me.weishu.kernelsu";
|
||||
else if (variant == KNext) manager_path = "/data/user_de/0/com.rifsxd.ksunext";
|
||||
|
||||
struct stat s;
|
||||
if (stat("/data/user_de/0/me.weishu.kernelsu", &s) == -1) {
|
||||
if (stat(manager_path, &s) == -1) {
|
||||
if (errno != ENOENT) {
|
||||
LOGE("Failed to stat KSU manager data directory: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
|
||||
#include "../constants.h"
|
||||
|
||||
enum kernelsu_variants {
|
||||
KOfficial,
|
||||
KNext
|
||||
};
|
||||
|
||||
void ksu_get_existence(struct root_impl_state *state);
|
||||
|
||||
bool ksu_uid_granted_root(uid_t uid);
|
||||
|
||||
@@ -28,7 +28,7 @@ char *magisk_managers[] = {
|
||||
#define DEBUG_RAMDISK_MAGISK lp_select("/debug_ramdisk/magisk32", "/debug_ramdisk/magisk64")
|
||||
#define BITLESS_DEBUG_RAMDISK_MAGISK "/debug_ramdisk/magisk"
|
||||
|
||||
enum magisk_variants variant = Official;
|
||||
static enum magisk_variants variant = MOfficial;
|
||||
/* INFO: Longest path */
|
||||
static char path_to_magisk[sizeof(DEBUG_RAMDISK_MAGISK)] = { 0 };
|
||||
bool is_using_sulist = false;
|
||||
@@ -74,7 +74,7 @@ void magisk_get_existence(struct root_impl_state *state) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->variant = (uint8_t)Official;
|
||||
state->variant = (uint8_t)MOfficial;
|
||||
|
||||
for (unsigned long i = 0; i < sizeof(supported_variants) / sizeof(supported_variants[0]); i++) {
|
||||
if (strstr(magisk_info, supported_variants[i])) {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "../constants.h"
|
||||
|
||||
enum magisk_variants {
|
||||
Official,
|
||||
Kitsune
|
||||
MOfficial,
|
||||
MKitsune
|
||||
};
|
||||
|
||||
void magisk_get_existence(struct root_impl_state *state);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "utils.h"
|
||||
#include "root_impl/common.h"
|
||||
#include "root_impl/kernelsu.h"
|
||||
#include "root_impl/magisk.h"
|
||||
|
||||
int clean_namespace_fd = 0;
|
||||
@@ -436,7 +437,8 @@ void stringify_root_impl_name(struct root_impl impl, char *restrict output) {
|
||||
break;
|
||||
}
|
||||
case KernelSU: {
|
||||
strcpy(output, "KernelSU");
|
||||
if (impl.variant == KOfficial) strcpy(output, "KernelSU");
|
||||
else strcpy(output, "KernelSU Next");
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -446,11 +448,8 @@ void stringify_root_impl_name(struct root_impl impl, char *restrict output) {
|
||||
break;
|
||||
}
|
||||
case Magisk: {
|
||||
if (impl.variant == 0) {
|
||||
strcpy(output, "Magisk Official");
|
||||
} else {
|
||||
strcpy(output, "Magisk Kitsune");
|
||||
}
|
||||
if (impl.variant == MOfficial) strcpy(output, "Magisk Official");
|
||||
else strcpy(output, "Magisk Kitsune");
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -603,115 +602,57 @@ bool umount_root(struct root_impl impl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (impl.impl) {
|
||||
case None: { break; }
|
||||
case Multiple: { break; }
|
||||
/* INFO: "Magisk" is the longest word that will ever be put in source_name */
|
||||
char source_name[sizeof("magisk")];
|
||||
if (impl.impl == KernelSU) strcpy(source_name, "KSU");
|
||||
else if (impl.impl == APatch) strcpy(source_name, "APatch");
|
||||
else strcpy(source_name, "magisk");
|
||||
|
||||
case KernelSU:
|
||||
case APatch: {
|
||||
char source_name[LONGEST_ROOT_IMPL_NAME];
|
||||
if (impl.impl == KernelSU) strcpy(source_name, "KSU");
|
||||
else strcpy(source_name, "APatch");
|
||||
LOGI("[%s] Unmounting root", source_name);
|
||||
|
||||
LOGI("[%s] Unmounting root", source_name);
|
||||
const char **targets_to_unmount = NULL;
|
||||
size_t num_targets = 0;
|
||||
|
||||
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];
|
||||
|
||||
for (size_t i = 0; i < mounts.length; i++) {
|
||||
struct mountinfo mount = mounts.mounts[i];
|
||||
bool should_unmount = false;
|
||||
/* INFO: The root implementations have their own /system mounts, so we
|
||||
only skip the mount if they are from a module, not Magisk itself.
|
||||
*/
|
||||
if (strncmp(mount.target, "/system/", strlen("/system/")) == 0 &&
|
||||
strncmp(mount.root, "/adb/modules/", strlen("/adb/modules/")) == 0 &&
|
||||
strncmp(mount.target, "/system/etc/", strlen("/system/etc/")) != 0) continue;
|
||||
|
||||
bool should_unmount = false;
|
||||
if (strcmp(mount.source, source_name) == 0) should_unmount = true;
|
||||
if (strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0) should_unmount = true;
|
||||
if (strncmp(mount.root, "/adb/modules/", strlen("/adb/modules/")) == 0) should_unmount = true;
|
||||
|
||||
/* INFO: KernelSU has its own /system mounts, so we only skip the mount
|
||||
if they are from a module, not KSU itself.
|
||||
*/
|
||||
if (strncmp(mount.target, "/system/", strlen("/system/")) == 0 &&
|
||||
strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0) continue;
|
||||
if (!should_unmount) continue;
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
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);
|
||||
free_mounts(&mounts);
|
||||
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
case Magisk: {
|
||||
LOGI("[Magisk] Unmounting root");
|
||||
|
||||
const char **targets_to_unmount = NULL;
|
||||
size_t num_targets = 0;
|
||||
targets_to_unmount[num_targets - 1] = mount.target;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mounts.length; i++) {
|
||||
struct mountinfo mount = mounts.mounts[i];
|
||||
|
||||
bool should_unmount = false;
|
||||
/* INFO: Magisk has its own /system mounts, so we only skip the mount
|
||||
if they are from a module, not Magisk itself.
|
||||
*/
|
||||
if (strncmp(mount.target, "/system/", strlen("/system/")) == 0 &&
|
||||
strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0) continue;
|
||||
|
||||
if (strcmp(mount.source, "magisk") == 0) should_unmount = true;
|
||||
if (strncmp(mount.target, "/debug_ramdisk", strlen("/debug_ramdisk")) == 0) should_unmount = true;
|
||||
if (strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0) should_unmount = true;
|
||||
if (strncmp(mount.root, "/adb/modules", strlen("/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("[Magisk] Failed to allocate memory for targets_to_unmount\n");
|
||||
|
||||
free(targets_to_unmount);
|
||||
free_mounts(&mounts);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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("[Magisk] Failed to unmount %s: %s\n", target, strerror(errno));
|
||||
} else {
|
||||
LOGI("[Magisk] Unmounted %s\n", target);
|
||||
}
|
||||
}
|
||||
free(targets_to_unmount);
|
||||
|
||||
break;
|
||||
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);
|
||||
|
||||
free_mounts(&mounts);
|
||||
|
||||
@@ -844,7 +785,7 @@ int save_mns_fd(int pid, enum MountNamespaceState mns_state, struct root_impl im
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (impl.impl == Magisk && impl.variant == Kitsune && mns_state == Clean) {
|
||||
if (impl.impl == Magisk && impl.variant == MKitsune && mns_state == Clean) {
|
||||
LOGI("[Magisk] Magisk Kitsune detected, will skip cache first.");
|
||||
|
||||
/* INFO: MagiskSU of Kitsune has a special behavior: It is only mounted
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
return -1; \
|
||||
}
|
||||
|
||||
#define IS_ISOLATED_SERVICE(uid) \
|
||||
((uid) >= 90000 && (uid) < 1000000)
|
||||
|
||||
#define write_func_def(type) \
|
||||
ssize_t write_## type(int fd, type val)
|
||||
|
||||
|
||||
@@ -48,11 +48,14 @@ enum Architecture {
|
||||
#define ZYGISKD_PATH "/data/adb/modules/rezygisk/bin/zygiskd" lp_select("32", "64")
|
||||
|
||||
static enum Architecture get_arch(void) {
|
||||
char system_arch[32];
|
||||
char system_arch[64];
|
||||
get_property("ro.product.cpu.abilist", system_arch);
|
||||
|
||||
if (strstr(system_arch, "arm") != NULL) return lp_select(ARM32, ARM64);
|
||||
/* INFO: "PC" architectures should have priority because in an emulator
|
||||
the native architecture should have priority over the emulated
|
||||
architecture for "native" reasons. */
|
||||
if (strstr(system_arch, "x86") != NULL) return lp_select(X86, X86_64);
|
||||
if (strstr(system_arch, "arm") != NULL) return lp_select(ARM32, ARM64);
|
||||
|
||||
LOGE("Unsupported system architecture: %s\n", system_arch);
|
||||
exit(1);
|
||||
@@ -130,6 +133,8 @@ static void load_modules(enum Architecture arch, struct Context *restrict contex
|
||||
context->modules[context->len].companion = -1;
|
||||
context->len++;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static void free_modules(struct Context *restrict context) {
|
||||
@@ -355,6 +360,9 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct sigaction sa = { .sa_handler = SIG_IGN };
|
||||
sigaction(SIGPIPE, &sa, NULL);
|
||||
|
||||
bool first_process = true;
|
||||
while (1) {
|
||||
int client_fd = accept(socket_fd, NULL, NULL);
|
||||
|
||||
Reference in New Issue
Block a user