You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
Compare commits
52 Commits
rewrite/we
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
153097f9d8 | ||
|
|
0f27e455e7 | ||
|
|
3688df6450 | ||
|
|
0c7a756030 | ||
|
|
70805bb390 | ||
|
|
e6344d2e12 | ||
|
|
d2ebb2bfed | ||
|
|
7e823319b7 | ||
|
|
f9fcf1c2e7 | ||
|
|
08513b17e8 | ||
|
|
a7917e20fe | ||
|
|
e0ce1473dd | ||
|
|
bf3c73d72b | ||
|
|
510e8a2de4 | ||
|
|
38cfbb25ef | ||
|
|
d54cac89a7 | ||
|
|
90da42a10b | ||
|
|
872ba693a1 | ||
|
|
2dfa221287 | ||
|
|
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: |
|
||||
|
||||
6
.github/workflows/trusted_ci.yml
vendored
6
.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: |
|
||||
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 {
|
||||
|
||||
@@ -57,7 +57,6 @@ android {
|
||||
defaultConfig {
|
||||
externalNativeBuild.cmake {
|
||||
arguments += "-DANDROID_STL=none"
|
||||
arguments += "-DLSPLT_STANDALONE=ON"
|
||||
arguments += "-DCMAKE_BUILD_PARALLEL_LEVEL=${Runtime.getRuntime().availableProcessors()}"
|
||||
cFlags("-std=c18", *defaultCFlags)
|
||||
cppFlags("-std=c++20", *defaultCFlags)
|
||||
|
||||
@@ -15,7 +15,7 @@ target_link_libraries(common log)
|
||||
aux_source_directory(injector INJECTOR_SRC_LIST)
|
||||
add_library(zygisk SHARED ${INJECTOR_SRC_LIST})
|
||||
target_include_directories(zygisk PRIVATE include)
|
||||
target_link_libraries(zygisk cxx::cxx log common lsplt_static phmap)
|
||||
target_link_libraries(zygisk cxx::cxx log common lsplt_static)
|
||||
|
||||
aux_source_directory(ptracer PTRACER_SRC_LIST)
|
||||
add_executable(libzygisk_ptrace.so ${PTRACER_SRC_LIST})
|
||||
|
||||
@@ -108,43 +108,32 @@ void rezygiskd_get_info(struct rezygisk_info *info) {
|
||||
|
||||
read_uint32_t(fd, (uint32_t *)&info->pid);
|
||||
|
||||
read_size_t(fd, &info->modules->modules_count);
|
||||
if (info->modules->modules_count == 0) {
|
||||
info->modules->modules = NULL;
|
||||
read_size_t(fd, &info->modules.modules_count);
|
||||
if (info->modules.modules_count == 0) {
|
||||
info->modules.modules = NULL;
|
||||
|
||||
close(fd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
info->modules->modules = (char **)malloc(sizeof(char *) * info->modules->modules_count);
|
||||
if (info->modules->modules == NULL) {
|
||||
info->modules.modules = (char **)malloc(sizeof(char *) * info->modules.modules_count);
|
||||
if (!info->modules.modules) {
|
||||
PLOGE("allocating modules name memory");
|
||||
|
||||
free(info->modules);
|
||||
info->modules = NULL;
|
||||
info->modules->modules_count = 0;
|
||||
info->modules.modules_count = 0;
|
||||
|
||||
close(fd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < info->modules->modules_count; i++) {
|
||||
for (size_t i = 0; i < info->modules.modules_count; i++) {
|
||||
char *module_name = read_string(fd);
|
||||
if (module_name == NULL) {
|
||||
PLOGE("reading module name");
|
||||
|
||||
info->modules->modules_count = i;
|
||||
|
||||
free_rezygisk_info(info);
|
||||
|
||||
info->modules = NULL;
|
||||
info->modules->modules_count = 0;
|
||||
|
||||
close(fd);
|
||||
|
||||
return;
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
char module_path[PATH_MAX];
|
||||
@@ -156,43 +145,49 @@ void rezygiskd_get_info(struct rezygisk_info *info) {
|
||||
if (!module_prop) {
|
||||
PLOGE("failed to open module prop file %s", module_path);
|
||||
|
||||
info->modules->modules_count = i;
|
||||
|
||||
free_rezygisk_info(info);
|
||||
|
||||
info->modules = NULL;
|
||||
info->modules->modules_count = 0;
|
||||
|
||||
close(fd);
|
||||
|
||||
return;
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
info->modules.modules[i] = NULL;
|
||||
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), module_prop) != NULL) {
|
||||
if (strncmp(line, "name=", strlen("name=")) != 0) continue;
|
||||
|
||||
info->modules->modules[i] = strndup(line + 5, strlen(line) - 6);
|
||||
info->modules.modules[i] = strndup(line + 5, strlen(line) - 6);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (info->modules.modules[i] == NULL) {
|
||||
PLOGE("failed to read module name from %s", module_path);
|
||||
|
||||
fclose(module_prop);
|
||||
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
fclose(module_prop);
|
||||
|
||||
continue;
|
||||
|
||||
info_cleanup:
|
||||
info->modules.modules_count = i;
|
||||
free_rezygisk_info(info);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void free_rezygisk_info(struct rezygisk_info *info) {
|
||||
if (info->modules->modules) {
|
||||
for (size_t i = 0; i < info->modules->modules_count; i++) {
|
||||
free(info->modules->modules[i]);
|
||||
for (size_t i = 0; i < info->modules.modules_count; i++) {
|
||||
free(info->modules.modules[i]);
|
||||
}
|
||||
|
||||
free(info->modules->modules);
|
||||
}
|
||||
|
||||
free(info->modules);
|
||||
free(info->modules.modules);
|
||||
info->modules.modules = NULL;
|
||||
}
|
||||
|
||||
bool rezygiskd_read_modules(struct zygisk_modules *modules) {
|
||||
@@ -237,13 +232,11 @@ bool rezygiskd_read_modules(struct zygisk_modules *modules) {
|
||||
}
|
||||
|
||||
void free_modules(struct zygisk_modules *modules) {
|
||||
if (modules->modules) {
|
||||
for (size_t i = 0; i < modules->modules_count; i++) {
|
||||
free(modules->modules[i]);
|
||||
}
|
||||
|
||||
free(modules->modules);
|
||||
}
|
||||
}
|
||||
|
||||
int rezygiskd_connect_companion(size_t index) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/auxv.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -159,14 +160,14 @@ ElfImg *ElfImg_create(const char *elf, void *base) {
|
||||
}
|
||||
|
||||
if (base) {
|
||||
/* LOGI: Due to the use in zygisk-ptracer, we need to allow pre-
|
||||
/* INFO: Due to the use in zygisk-ptracer, we need to allow pre-
|
||||
fetched bases to be passed, as the linker (Android 7.1
|
||||
and below) is not loaded from dlopen, which makes it not
|
||||
be visible with dl_iterate_phdr.
|
||||
*/
|
||||
img->base = base;
|
||||
|
||||
LOGI("Using provided base address 0x%p for %s", base, elf);
|
||||
LOGD("Using provided base address 0x%p for %s", base, elf);
|
||||
} else {
|
||||
if (!_find_module_base(img)) {
|
||||
LOGE("Failed to find module base for %s using dl_iterate_phdr", elf);
|
||||
@@ -387,7 +388,7 @@ ElfImg *ElfImg_create(const char *elf, void *base) {
|
||||
img->symstr_offset_for_symtab = 0;
|
||||
}
|
||||
} else {
|
||||
LOGI("No .symtab section found or section headers missing");
|
||||
LOGD("No .symtab section found or section headers missing");
|
||||
|
||||
img->symtab_start = NULL;
|
||||
img->symtab_count = 0;
|
||||
@@ -403,45 +404,24 @@ ElfImg *ElfImg_create(const char *elf, void *base) {
|
||||
img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
|
||||
bias_calculated = true;
|
||||
|
||||
LOGI("Calculated bias %ld from PT_LOAD segment %d (vaddr %lx)", (long)img->bias, i, (unsigned long)phdr[i].p_vaddr);
|
||||
LOGD("Calculated bias %ld from PT_LOAD segment %d (vaddr %lx)", (long)img->bias, i, (unsigned long)phdr[i].p_vaddr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bias_calculated) {
|
||||
for (int i = 0; i < img->header->e_phnum; ++i) {
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
if (!bias_calculated) for (int i = 0; i < img->header->e_phnum; ++i) {
|
||||
if (phdr[i].p_type != PT_LOAD) continue;
|
||||
|
||||
img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
|
||||
bias_calculated = true;
|
||||
|
||||
LOGI("Calculated bias %ld from first PT_LOAD segment %d (vaddr %lx, offset %lx)",
|
||||
LOGD("Calculated bias %ld from first PT_LOAD segment %d (vaddr %lx, offset %lx)",
|
||||
(long)img->bias, i, (unsigned long)phdr[i].p_vaddr, (unsigned long)phdr[i].p_offset);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bias_calculated && shdr_base) {
|
||||
LOGW("Could not calculate bias from program headers, falling back to section method.");
|
||||
uintptr_t shoff_for_bias = (uintptr_t)shdr_base;
|
||||
for (int i = 0; i < img->header->e_shnum; i++, shoff_for_bias += img->header->e_shentsize) {
|
||||
ElfW(Shdr) *section_h = (ElfW(Shdr *))shoff_for_bias;
|
||||
|
||||
if ((section_h->sh_flags & SHF_ALLOC) && section_h->sh_addr != 0) {
|
||||
img->bias = (off_t)section_h->sh_addr - (off_t)section_h->sh_offset;
|
||||
bias_calculated = true;
|
||||
|
||||
char *sname = section_str ? (section_h->sh_name + section_str) : "<?>";
|
||||
LOGI("Calculated bias %ld from first allocated section %s (addr %lx, offset %lx)",
|
||||
(long)img->bias, sname, (unsigned long)section_h->sh_addr, (unsigned long)section_h->sh_offset);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bias_calculated)
|
||||
LOGE("Failed to calculate bias for %s. Assuming bias is 0.", elf);
|
||||
@@ -523,7 +503,7 @@ bool _load_symtabs(ElfImg *img) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
|
||||
ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash, unsigned char *sym_type) {
|
||||
if (img->gnu_nbucket_ == 0 || img->gnu_bloom_size_ == 0 || !img->gnu_bloom_filter_ || !img->gnu_bucket_ || !img->gnu_chain_ || !img->dynsym_start || !img->strtab_start)
|
||||
return 0;
|
||||
|
||||
@@ -535,15 +515,17 @@ 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)",
|
||||
/* 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;
|
||||
}
|
||||
|
||||
uint32_t sym_index = img->gnu_bucket_[hash % img->gnu_nbucket_];
|
||||
if (sym_index < img->gnu_symndx_) {
|
||||
LOGI("Symbol %s hash %u maps to bucket %u index %u (below gnu_symndx %u), not exported?", name, hash, hash % img->gnu_nbucket_, sym_index, img->gnu_symndx_);
|
||||
LOGW("Symbol %s hash %u maps to bucket %u index %u (below gnu_symndx %u), not exported?", name, hash, hash % img->gnu_nbucket_, sym_index, img->gnu_symndx_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -566,8 +548,12 @@ ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF)
|
||||
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF) {
|
||||
unsigned int type = ELF_ST_TYPE(sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return sym->st_value;
|
||||
}
|
||||
|
||||
while ((chain_val & 1) == 0) {
|
||||
sym_index++;
|
||||
@@ -587,14 +573,18 @@ ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF)
|
||||
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF) {
|
||||
unsigned int type = ELF_ST_TYPE(sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return sym->st_value;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash) {
|
||||
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash, unsigned char *sym_type) {
|
||||
if (img->nbucket_ == 0 || !img->bucket_ || !img->chain_ || !img->dynsym_start || !img->strtab_start)
|
||||
return 0;
|
||||
|
||||
@@ -603,14 +593,18 @@ ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t h
|
||||
for (size_t n = img->bucket_[hash % img->nbucket_]; n != STN_UNDEF; n = img->chain_[n]) {
|
||||
ElfW(Sym) *sym = img->dynsym_start + n;
|
||||
|
||||
if (strcmp(name, strings + sym->st_name) == 0 && sym->st_shndx != SHN_UNDEF)
|
||||
if (strcmp(name, strings + sym->st_name) == 0 && sym->st_shndx != SHN_UNDEF) {
|
||||
unsigned int type = ELF_ST_TYPE(sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return sym->st_value;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) {
|
||||
ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name, unsigned char *sym_type) {
|
||||
if (!_load_symtabs(img)) {
|
||||
LOGE("Failed to load symtabs for linear lookup of %s", name);
|
||||
|
||||
@@ -621,7 +615,7 @@ ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) {
|
||||
if (valid_symtabs_amount == 0) {
|
||||
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf);
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < valid_symtabs_amount; i++) {
|
||||
@@ -631,13 +625,16 @@ ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) {
|
||||
if (img->symtabs_[i].sym->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
|
||||
unsigned int type = ELF_ST_TYPE(img->symtabs_[i].sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return img->symtabs_[i].sym->st_value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix) {
|
||||
ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix, unsigned char *sym_type) {
|
||||
if (!_load_symtabs(img)) {
|
||||
LOGE("Failed to load symtabs for linear lookup by prefix of %s", prefix);
|
||||
|
||||
@@ -648,7 +645,7 @@ ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix) {
|
||||
if (valid_symtabs_amount == 0) {
|
||||
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf);
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t prefix_len = strlen(prefix);
|
||||
@@ -664,45 +661,161 @@ ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix) {
|
||||
if (img->symtabs_[i].sym->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
|
||||
unsigned int type = ELF_ST_TYPE(img->symtabs_[i].sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return img->symtabs_[i].sym->st_value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name) {
|
||||
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name, unsigned char *sym_type) {
|
||||
ElfW(Addr) offset = 0;
|
||||
|
||||
offset = GnuLookup(img, name, GnuHash(name));
|
||||
offset = GnuLookup(img, name, GnuHash(name), sym_type);
|
||||
if (offset != 0) return offset;
|
||||
|
||||
offset = ElfLookup(img, name, ElfHash(name));
|
||||
offset = ElfLookup(img, name, ElfHash(name), sym_type);
|
||||
if (offset != 0) return offset;
|
||||
|
||||
offset = LinearLookup(img, name);
|
||||
offset = LinearLookup(img, name, sym_type);
|
||||
if (offset != 0) return offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __aarch64__
|
||||
/* INFO: Struct containing information about hardware capabilities used in resolver. This
|
||||
struct information is pulled directly from the AOSP code.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/include/sys/ifunc.h#53
|
||||
*/
|
||||
struct __ifunc_arg_t {
|
||||
unsigned long _size;
|
||||
unsigned long _hwcap;
|
||||
unsigned long _hwcap2;
|
||||
};
|
||||
|
||||
/* INFO: This is a constant used in the AOSP code to indicate that the struct __ifunc_arg_t
|
||||
contains hardware capabilities.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/include/sys/ifunc.h#74
|
||||
*/
|
||||
#define _IFUNC_ARG_HWCAP (1ULL << 62)
|
||||
#elif defined(__riscv)
|
||||
/* INFO: Struct used in Linux RISC-V architecture to probe hardware capabilities.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/kernel/uapi/asm-riscv/asm/hwprobe.h#10
|
||||
*/
|
||||
struct riscv_hwprobe {
|
||||
int64_t key;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
/* INFO: This function is used in the AOSP code to probe hardware capabilities on RISC-V architecture
|
||||
by calling the syscall __NR_riscv_hwprobe and passing the parameters that will filled with
|
||||
the device hardware capabilities.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/bionic/vdso.cpp#86
|
||||
*/
|
||||
int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, size_t cpu_count, unsigned long *cpus, unsigned flags) {
|
||||
register long a0 __asm__("a0") = (long)pairs;
|
||||
register long a1 __asm__("a1") = pair_count;
|
||||
register long a2 __asm__("a2") = cpu_count;
|
||||
register long a3 __asm__("a3") = (long)cpus;
|
||||
register long a4 __asm__("a4") = flags;
|
||||
register long a7 __asm__("a7") = __NR_riscv_hwprobe;
|
||||
|
||||
__asm__ volatile(
|
||||
"ecall"
|
||||
: "=r"(a0)
|
||||
: "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a7)
|
||||
);
|
||||
|
||||
return -a0;
|
||||
}
|
||||
|
||||
/* INFO: This is a function pointer type that points how the signature of the __riscv_hwprobe
|
||||
function is.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/include/sys/hwprobe.h#62
|
||||
*/
|
||||
typedef int (*__riscv_hwprobe_t)(struct riscv_hwprobe *__pairs, size_t __pair_count, size_t __cpu_count, unsigned long *__cpus, unsigned __flags);
|
||||
#endif
|
||||
|
||||
/* INFO: GNU ifuncs (indirect functions) are functions that does not execute the code by itself,
|
||||
but instead lead to other functions that may very according to hardware capabilities,
|
||||
or other reasons, depending of the architecture.
|
||||
|
||||
This function is based on AOSP's (Android Open Source Project) code, and resolves the
|
||||
indirect symbol, leading to the correct, most appropriate for the hardware, symbol.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/linker/linker.cpp#2594
|
||||
- https://android.googlesource.com/platform/bionic/+/tags/android-16.0.0_r1/libc/bionic/bionic_call_ifunc_resolver.cpp#41
|
||||
*/
|
||||
static ElfW(Addr) handle_indirect_symbol(ElfImg *img, ElfW(Off) offset) {
|
||||
ElfW(Addr) resolver_addr = (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
|
||||
#ifdef __aarch64__
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, struct __ifunc_arg_t *);
|
||||
|
||||
struct __ifunc_arg_t args = {
|
||||
._size = sizeof(struct __ifunc_arg_t),
|
||||
._hwcap = getauxval(AT_HWCAP),
|
||||
._hwcap2 = getauxval(AT_HWCAP2)
|
||||
};
|
||||
|
||||
return ((ifunc_resolver_t)resolver_addr)(args._hwcap | _IFUNC_ARG_HWCAP, &args);
|
||||
#elif defined(__arm__)
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(unsigned long);
|
||||
|
||||
return ((ifunc_resolver_t)resolver_addr)(getauxval(AT_HWCAP));
|
||||
#elif defined(__riscv)
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __riscv_hwprobe_t, void *);
|
||||
|
||||
return ((ifunc_resolver_t)resolver_addr)(getauxval(AT_HWCAP), __riscv_hwprobe, NULL);
|
||||
#else
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(void);
|
||||
|
||||
return ((ifunc_resolver_t)resolver_addr)();
|
||||
#endif
|
||||
}
|
||||
|
||||
ElfW(Addr) getSymbAddress(ElfImg *img, const char *name) {
|
||||
ElfW(Addr) offset = getSymbOffset(img, name);
|
||||
unsigned char sym_type = 0;
|
||||
ElfW(Addr) offset = getSymbOffset(img, name, &sym_type);
|
||||
|
||||
if (offset == 0 || !img->base) return 0;
|
||||
|
||||
ElfW(Addr) address = (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
if (sym_type == STT_GNU_IFUNC) {
|
||||
LOGD("Resolving STT_GNU_IFUNC symbol %s", name);
|
||||
|
||||
return address;
|
||||
return handle_indirect_symbol(img, offset);
|
||||
}
|
||||
|
||||
return (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
}
|
||||
|
||||
ElfW(Addr) getSymbAddressByPrefix(ElfImg *img, const char *prefix) {
|
||||
ElfW(Addr) offset = LinearLookupByPrefix(img, prefix);
|
||||
unsigned char sym_type = 0;
|
||||
ElfW(Addr) offset = LinearLookupByPrefix(img, prefix, &sym_type);
|
||||
|
||||
if (offset == 0 || !img->base) return 0;
|
||||
|
||||
ElfW(Addr) address = (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
if (sym_type == STT_GNU_IFUNC) {
|
||||
LOGD("Resolving STT_GNU_IFUNC symbol by prefix %s", prefix);
|
||||
|
||||
return address;
|
||||
return handle_indirect_symbol(img, offset);
|
||||
}
|
||||
|
||||
return (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
}
|
||||
|
||||
void *getSymbValueByPrefix(ElfImg *img, const char *prefix) {
|
||||
|
||||
@@ -8,6 +8,50 @@
|
||||
|
||||
#include "socket_utils.h"
|
||||
|
||||
ssize_t write_loop(int fd, const void *buf, size_t count) {
|
||||
ssize_t written = 0;
|
||||
while (written < (ssize_t)count) {
|
||||
ssize_t ret = write(fd, (const char *)buf + written, count - written);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) continue;
|
||||
|
||||
PLOGE("write");
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
LOGE("write: 0 bytes written");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
written += ret;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
ssize_t read_loop(int fd, void *buf, size_t count) {
|
||||
ssize_t read_bytes = 0;
|
||||
while (read_bytes < (ssize_t)count) {
|
||||
ssize_t ret = read(fd, (char *)buf + read_bytes, count - read_bytes);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) continue;
|
||||
|
||||
PLOGE("read");
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
LOGE("read: 0 bytes read");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
read_bytes += ret;
|
||||
}
|
||||
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
/* TODO: Standardize how to log errors */
|
||||
int read_fd(int fd) {
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
@@ -25,7 +69,7 @@ int read_fd(int fd) {
|
||||
.msg_controllen = sizeof(cmsgbuf)
|
||||
};
|
||||
|
||||
ssize_t ret = recvmsg(fd, &msg, MSG_WAITALL);
|
||||
ssize_t ret = TEMP_FAILURE_RETRY(recvmsg(fd, &msg, MSG_WAITALL));
|
||||
if (ret == -1) {
|
||||
PLOGE("recvmsg");
|
||||
|
||||
@@ -47,14 +91,14 @@ int read_fd(int fd) {
|
||||
|
||||
ssize_t write_string(int fd, const char *str) {
|
||||
size_t str_len = strlen(str);
|
||||
ssize_t write_bytes = write(fd, &str_len, sizeof(size_t));
|
||||
ssize_t write_bytes = write_loop(fd, &str_len, sizeof(size_t));
|
||||
if (write_bytes != (ssize_t)sizeof(size_t)) {
|
||||
LOGE("Failed to write string length: Not all bytes were written (%zd != %zu).\n", write_bytes, sizeof(size_t));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_bytes = write(fd, str, str_len);
|
||||
write_bytes = write_loop(fd, str, str_len);
|
||||
if (write_bytes != (ssize_t)str_len) {
|
||||
LOGE("Failed to write string: Promised bytes doesn't exist (%zd != %zu).\n", write_bytes, str_len);
|
||||
|
||||
@@ -66,7 +110,7 @@ ssize_t write_string(int fd, const char *str) {
|
||||
|
||||
char *read_string(int fd) {
|
||||
size_t str_len = 0;
|
||||
ssize_t read_bytes = read(fd, &str_len, sizeof(size_t));
|
||||
ssize_t read_bytes = read_loop(fd, &str_len, sizeof(size_t));
|
||||
if (read_bytes != (ssize_t)sizeof(size_t)) {
|
||||
LOGE("Failed to read string length: Not all bytes were read (%zd != %zu).\n", read_bytes, sizeof(size_t));
|
||||
|
||||
@@ -80,7 +124,7 @@ char *read_string(int fd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
read_bytes = read(fd, buf, str_len);
|
||||
read_bytes = read_loop(fd, buf, str_len);
|
||||
if (read_bytes != (ssize_t)str_len) {
|
||||
LOGE("Failed to read string: Promised bytes doesn't exist (%zd != %zu).\n", read_bytes, str_len);
|
||||
|
||||
@@ -96,12 +140,12 @@ char *read_string(int fd) {
|
||||
|
||||
#define write_func(type) \
|
||||
ssize_t write_## type(int fd, type val) { \
|
||||
return write(fd, &val, sizeof(type)); \
|
||||
return write_loop(fd, &val, sizeof(type)); \
|
||||
}
|
||||
|
||||
#define read_func(type) \
|
||||
ssize_t read_## type(int fd, type *val) { \
|
||||
return read(fd, val, sizeof(type)); \
|
||||
return read_loop(fd, val, sizeof(type)); \
|
||||
}
|
||||
|
||||
write_func(uint8_t)
|
||||
|
||||
4
loader/src/external/CMakeLists.txt
vendored
4
loader/src/external/CMakeLists.txt
vendored
@@ -2,7 +2,3 @@ project(external)
|
||||
|
||||
OPTION(LSPLT_BUILD_SHARED OFF)
|
||||
add_subdirectory(lsplt/lsplt/src/main/jni)
|
||||
|
||||
add_library(phmap INTERFACE)
|
||||
target_include_directories(phmap INTERFACE parallel-hashmap)
|
||||
target_compile_options(phmap INTERFACE -Wno-unused-value)
|
||||
|
||||
2
loader/src/external/lsplt
vendored
2
loader/src/external/lsplt
vendored
Submodule loader/src/external/lsplt updated: 984804293e...5092fb399f
@@ -1,395 +0,0 @@
|
||||
/* Copyright 2022 John "topjohnwu" Wu
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// This is the public API for Zygisk modules.
|
||||
// DO NOT MODIFY ANY CODE IN THIS HEADER.
|
||||
|
||||
// WARNING: this file may contain changes that are not finalized.
|
||||
// Always use the following published header for development:
|
||||
// https://github.com/topjohnwu/zygisk-module-sample/blob/master/module/jni/zygisk.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#define ZYGISK_API_VERSION 4
|
||||
|
||||
/*
|
||||
|
||||
***************
|
||||
* Introduction
|
||||
***************
|
||||
|
||||
On Android, all app processes are forked from a special daemon called "Zygote".
|
||||
For each new app process, zygote will fork a new process and perform "specialization".
|
||||
This specialization operation enforces the Android security sandbox on the newly forked
|
||||
process to make sure that 3rd party application code is only loaded after it is being
|
||||
restricted within a sandbox.
|
||||
|
||||
On Android, there is also this special process called "system_server". This single
|
||||
process hosts a significant portion of system services, which controls how the
|
||||
Android operating system and apps interact with each other.
|
||||
|
||||
The Zygisk framework provides a way to allow developers to build modules and run custom
|
||||
code before and after system_server and any app processes' specialization.
|
||||
This enable developers to inject code and alter the behavior of system_server and app processes.
|
||||
|
||||
Please note that modules will only be loaded after zygote has forked the child process.
|
||||
THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM_SERVER PROCESS, NOT THE ZYGOTE DAEMON!
|
||||
|
||||
*********************
|
||||
* Development Guide
|
||||
*********************
|
||||
|
||||
Define a class and inherit zygisk::ModuleBase to implement the functionality of your module.
|
||||
Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk.
|
||||
|
||||
Example code:
|
||||
|
||||
static jint (*orig_logger_entry_max)(JNIEnv *env);
|
||||
static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); }
|
||||
|
||||
class ExampleModule : public zygisk::ModuleBase {
|
||||
public:
|
||||
void onLoad(zygisk::Api *api, JNIEnv *env) override {
|
||||
this->api = api;
|
||||
this->env = env;
|
||||
}
|
||||
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
|
||||
JNINativeMethod methods[] = {
|
||||
{ "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max },
|
||||
};
|
||||
api->hookJniNativeMethods(env, "android/util/Log", methods, 1);
|
||||
*(void **) &orig_logger_entry_max = methods[0].fnPtr;
|
||||
}
|
||||
private:
|
||||
zygisk::Api *api;
|
||||
JNIEnv *env;
|
||||
};
|
||||
|
||||
REGISTER_ZYGISK_MODULE(ExampleModule)
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
|
||||
Since your module class's code runs with either Zygote's privilege in pre[XXX]Specialize,
|
||||
or runs in the sandbox of the target process in post[XXX]Specialize, the code in your class
|
||||
never runs in a true superuser environment.
|
||||
|
||||
If your module require access to superuser permissions, you can create and register
|
||||
a root companion handler function. This function runs in a separate root companion
|
||||
daemon process, and an Unix domain socket is provided to allow you to perform IPC between
|
||||
your target process and the root companion process.
|
||||
|
||||
Example code:
|
||||
|
||||
static void example_handler(int socket) { ... }
|
||||
|
||||
REGISTER_ZYGISK_COMPANION(example_handler)
|
||||
|
||||
*/
|
||||
|
||||
namespace zygisk {
|
||||
|
||||
struct Api;
|
||||
struct AppSpecializeArgs;
|
||||
struct ServerSpecializeArgs;
|
||||
|
||||
class ModuleBase {
|
||||
public:
|
||||
|
||||
// This method is called as soon as the module is loaded into the target process.
|
||||
// A Zygisk API handle will be passed as an argument.
|
||||
virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {}
|
||||
|
||||
// This method is called before the app process is specialized.
|
||||
// At this point, the process just got forked from zygote, but no app specific specialization
|
||||
// is applied. This means that the process does not have any sandbox restrictions and
|
||||
// still runs with the same privilege of zygote.
|
||||
//
|
||||
// All the arguments that will be sent and used for app specialization is passed as a single
|
||||
// AppSpecializeArgs object. You can read and overwrite these arguments to change how the app
|
||||
// process will be specialized.
|
||||
//
|
||||
// If you need to run some operations as superuser, you can call Api::connectCompanion() to
|
||||
// get a socket to do IPC calls with a root companion process.
|
||||
// See Api::connectCompanion() for more info.
|
||||
virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {}
|
||||
|
||||
// This method is called after the app process is specialized.
|
||||
// At this point, the process has all sandbox restrictions enabled for this application.
|
||||
// This means that this method runs with the same privilege of the app's own code.
|
||||
virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}
|
||||
|
||||
// This method is called before the system server process is specialized.
|
||||
// See preAppSpecialize(args) for more info.
|
||||
virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {}
|
||||
|
||||
// This method is called after the system server process is specialized.
|
||||
// At this point, the process runs with the privilege of system_server.
|
||||
virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {}
|
||||
};
|
||||
|
||||
struct AppSpecializeArgs {
|
||||
// Required arguments. These arguments are guaranteed to exist on all Android versions.
|
||||
jint &uid;
|
||||
jint &gid;
|
||||
jintArray &gids;
|
||||
jint &runtime_flags;
|
||||
jobjectArray &rlimits;
|
||||
jint &mount_external;
|
||||
jstring &se_info;
|
||||
jstring &nice_name;
|
||||
jstring &instruction_set;
|
||||
jstring &app_data_dir;
|
||||
|
||||
// Optional arguments. Please check whether the pointer is null before de-referencing
|
||||
jintArray *const fds_to_ignore;
|
||||
jboolean *const is_child_zygote;
|
||||
jboolean *const is_top_app;
|
||||
jobjectArray *const pkg_data_info_list;
|
||||
jobjectArray *const whitelisted_data_info_list;
|
||||
jboolean *const mount_data_dirs;
|
||||
jboolean *const mount_storage_dirs;
|
||||
jboolean *const mount_sysprop_overrides;
|
||||
|
||||
AppSpecializeArgs() = delete;
|
||||
};
|
||||
|
||||
struct ServerSpecializeArgs {
|
||||
jint &uid;
|
||||
jint &gid;
|
||||
jintArray &gids;
|
||||
jint &runtime_flags;
|
||||
jlong &permitted_capabilities;
|
||||
jlong &effective_capabilities;
|
||||
|
||||
ServerSpecializeArgs() = delete;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
struct api_table;
|
||||
template <class T> void entry_impl(api_table *, JNIEnv *);
|
||||
}
|
||||
|
||||
// These values are used in Api::setOption(Option)
|
||||
enum Option : int {
|
||||
// Force Magisk's denylist unmount routines to run on this process.
|
||||
//
|
||||
// Setting this option only makes sense in preAppSpecialize.
|
||||
// The actual unmounting happens during app process specialization.
|
||||
//
|
||||
// Set this option to force all Magisk and modules' files to be unmounted from the
|
||||
// mount namespace of the process, regardless of the denylist enforcement status.
|
||||
FORCE_DENYLIST_UNMOUNT = 0,
|
||||
|
||||
// When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize.
|
||||
// Be aware that after dlclose-ing your module, all of your code will be unmapped from memory.
|
||||
// YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS.
|
||||
DLCLOSE_MODULE_LIBRARY = 1,
|
||||
};
|
||||
|
||||
// Bit masks of the return value of Api::getFlags()
|
||||
enum StateFlag : uint32_t {
|
||||
// The user has granted root access to the current process
|
||||
PROCESS_GRANTED_ROOT = (1u << 0),
|
||||
|
||||
// The current process was added on the denylist
|
||||
PROCESS_ON_DENYLIST = (1u << 1),
|
||||
};
|
||||
|
||||
// All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded
|
||||
// from the specialized process afterwards.
|
||||
struct Api {
|
||||
|
||||
// Connect to a root companion process and get a Unix domain socket for IPC.
|
||||
//
|
||||
// This API only works in the pre[XXX]Specialize methods due to SELinux restrictions.
|
||||
//
|
||||
// The pre[XXX]Specialize methods run with the same privilege of zygote.
|
||||
// If you would like to do some operations with superuser permissions, register a handler
|
||||
// function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func).
|
||||
// Another good use case for a companion process is that if you want to share some resources
|
||||
// across multiple processes, hold the resources in the companion process and pass it over.
|
||||
//
|
||||
// The root companion process is ABI aware; that is, when calling this method from a 32-bit
|
||||
// process, you will be connected to a 32-bit companion process, and vice versa for 64-bit.
|
||||
//
|
||||
// Returns a file descriptor to a socket that is connected to the socket passed to your
|
||||
// module's companion request handler. Returns -1 if the connection attempt failed.
|
||||
int connectCompanion();
|
||||
|
||||
// Get the file descriptor of the root folder of the current module.
|
||||
//
|
||||
// This API only works in the pre[XXX]Specialize methods.
|
||||
// Accessing the directory returned is only possible in the pre[XXX]Specialize methods
|
||||
// or in the root companion process (assuming that you sent the fd over the socket).
|
||||
// Both restrictions are due to SELinux and UID.
|
||||
//
|
||||
// Returns -1 if errors occurred.
|
||||
int getModuleDir();
|
||||
|
||||
// Set various options for your module.
|
||||
// Please note that this method accepts one single option at a time.
|
||||
// Check zygisk::Option for the full list of options available.
|
||||
void setOption(Option opt);
|
||||
|
||||
// Get information about the current process.
|
||||
// Returns bitwise-or'd zygisk::StateFlag values.
|
||||
uint32_t getFlags();
|
||||
|
||||
// Exempt the provided file descriptor from being automatically closed.
|
||||
//
|
||||
// This API only make sense in preAppSpecialize; calling this method in any other situation
|
||||
// is either a no-op (returns true) or an error (returns false).
|
||||
//
|
||||
// When false is returned, the provided file descriptor will eventually be closed by zygote.
|
||||
bool exemptFd(int fd);
|
||||
|
||||
// Hook JNI native methods for a class
|
||||
//
|
||||
// Lookup all registered JNI native methods and replace it with your own methods.
|
||||
// The original function pointer will be saved in each JNINativeMethod's fnPtr.
|
||||
// If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr
|
||||
// will be set to nullptr.
|
||||
void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods);
|
||||
|
||||
// Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory.
|
||||
//
|
||||
// Parsing /proc/[PID]/maps will give you the memory map of a process. As an example:
|
||||
//
|
||||
// <address> <perms> <offset> <dev> <inode> <pathname>
|
||||
// 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64
|
||||
// (More details: https://man7.org/linux/man-pages/man5/proc.5.html)
|
||||
//
|
||||
// The `dev` and `inode` pair uniquely identifies a file being mapped into memory.
|
||||
// For matching ELFs loaded in memory, replace function `symbol` with `newFunc`.
|
||||
// If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`.
|
||||
void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc);
|
||||
|
||||
// Commit all the hooks that was previously registered.
|
||||
// Returns false if an error occurred.
|
||||
bool pltHookCommit();
|
||||
|
||||
private:
|
||||
internal::api_table *tbl;
|
||||
template <class T> friend void internal::entry_impl(internal::api_table *, JNIEnv *);
|
||||
};
|
||||
|
||||
// Register a class as a Zygisk module
|
||||
|
||||
#define REGISTER_ZYGISK_MODULE(clazz) \
|
||||
void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \
|
||||
zygisk::internal::entry_impl<clazz>(table, env); \
|
||||
}
|
||||
|
||||
// Register a root companion request handler function for your module
|
||||
//
|
||||
// The function runs in a superuser daemon process and handles a root companion request from
|
||||
// your module running in a target process. The function has to accept an integer value,
|
||||
// which is a Unix domain socket that is connected to the target process.
|
||||
// See Api::connectCompanion() for more info.
|
||||
//
|
||||
// NOTE: the function can run concurrently on multiple threads.
|
||||
// Be aware of race conditions if you have globally shared resources.
|
||||
|
||||
#define REGISTER_ZYGISK_COMPANION(func) \
|
||||
void zygisk_companion_entry(int client) { func(client); }
|
||||
|
||||
/*********************************************************
|
||||
* The following is internal ABI implementation detail.
|
||||
* You do not have to understand what it is doing.
|
||||
*********************************************************/
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct module_abi {
|
||||
long api_version;
|
||||
ModuleBase *impl;
|
||||
|
||||
void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *);
|
||||
void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *);
|
||||
void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *);
|
||||
void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *);
|
||||
|
||||
module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), impl(module) {
|
||||
preAppSpecialize = [](auto m, auto args) { m->preAppSpecialize(args); };
|
||||
postAppSpecialize = [](auto m, auto args) { m->postAppSpecialize(args); };
|
||||
preServerSpecialize = [](auto m, auto args) { m->preServerSpecialize(args); };
|
||||
postServerSpecialize = [](auto m, auto args) { m->postServerSpecialize(args); };
|
||||
}
|
||||
};
|
||||
|
||||
struct api_table {
|
||||
// Base
|
||||
void *impl;
|
||||
bool (*registerModule)(api_table *, module_abi *);
|
||||
|
||||
void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
||||
void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **);
|
||||
bool (*exemptFd)(int);
|
||||
bool (*pltHookCommit)();
|
||||
int (*connectCompanion)(void * /* impl */);
|
||||
void (*setOption)(void * /* impl */, Option);
|
||||
int (*getModuleDir)(void * /* impl */);
|
||||
uint32_t (*getFlags)(void * /* impl */);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void entry_impl(api_table *table, JNIEnv *env) {
|
||||
ModuleBase *module = new T();
|
||||
if (!table->registerModule(table, new module_abi(module)))
|
||||
return;
|
||||
auto api = new Api();
|
||||
api->tbl = table;
|
||||
module->onLoad(api, env);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
inline int Api::connectCompanion() {
|
||||
return tbl->connectCompanion ? tbl->connectCompanion(tbl->impl) : -1;
|
||||
}
|
||||
inline int Api::getModuleDir() {
|
||||
return tbl->getModuleDir ? tbl->getModuleDir(tbl->impl) : -1;
|
||||
}
|
||||
inline void Api::setOption(Option opt) {
|
||||
if (tbl->setOption) tbl->setOption(tbl->impl, opt);
|
||||
}
|
||||
inline uint32_t Api::getFlags() {
|
||||
return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0;
|
||||
}
|
||||
inline bool Api::exemptFd(int fd) {
|
||||
return tbl->exemptFd != nullptr && tbl->exemptFd(fd);
|
||||
}
|
||||
inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) {
|
||||
if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods);
|
||||
}
|
||||
inline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc) {
|
||||
if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc);
|
||||
}
|
||||
inline bool Api::pltHookCommit() {
|
||||
return tbl->pltHookCommit != nullptr && tbl->pltHookCommit();
|
||||
}
|
||||
|
||||
} // namespace zygisk
|
||||
|
||||
extern "C" {
|
||||
|
||||
[[gnu::visibility("default")]] [[gnu::used]]
|
||||
void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *);
|
||||
|
||||
[[gnu::visibility("default")]] [[gnu::used]]
|
||||
void zygisk_companion_entry(int);
|
||||
|
||||
} // extern "C"
|
||||
@@ -42,7 +42,7 @@ enum root_impl {
|
||||
};
|
||||
|
||||
struct rezygisk_info {
|
||||
struct zygisk_modules *modules;
|
||||
struct zygisk_modules modules;
|
||||
enum root_impl root_impl;
|
||||
pid_t pid;
|
||||
bool running;
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
#ifndef ELF_UTIL_H
|
||||
#define ELF_UTIL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <link.h>
|
||||
#include <linux/elf.h>
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h> // Added for threading primitives
|
||||
|
||||
#define SHT_GNU_HASH 0x6ffffff6
|
||||
|
||||
// Function pointer types for constructors and destructors
|
||||
typedef void (*linker_simple_func_t)(void);
|
||||
typedef void (*linker_ctor_function_t)(int, char**, char**);
|
||||
typedef void (*linker_dtor_function_t)(void);
|
||||
|
||||
|
||||
struct symtabs {
|
||||
char *name;
|
||||
ElfW(Sym) *sym;
|
||||
@@ -54,17 +61,7 @@ void ElfImg_destroy(ElfImg *img);
|
||||
|
||||
ElfImg *ElfImg_create(const char *elf, void *base);
|
||||
|
||||
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash);
|
||||
|
||||
ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash);
|
||||
|
||||
ElfW(Addr) LinearLookup(ElfImg *restrict img, const char *restrict name);
|
||||
|
||||
ElfW(Addr) LinearLookupByPrefix(ElfImg *restrict img, const char *name);
|
||||
|
||||
int dl_cb(struct dl_phdr_info *info, size_t size, void *data);
|
||||
|
||||
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name);
|
||||
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name, unsigned char *sym_type);
|
||||
|
||||
ElfW(Addr) getSymbAddress(ElfImg *img, const char *name);
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
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.
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
ssize_t write_loop(int fd, const void *buf, size_t count);
|
||||
|
||||
ssize_t read_loop(int fd, void *buf, size_t count);
|
||||
|
||||
int read_fd(int fd);
|
||||
|
||||
ssize_t write_string(int fd, const char *str);
|
||||
|
||||
107
loader/src/injector/art_method.h
Normal file
107
loader/src/injector/art_method.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef ART_METHOD_H
|
||||
#define ART_METHOD_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
inline static jfieldID art_method_field = nullptr;
|
||||
inline static size_t art_method_size = 0;
|
||||
inline static size_t entry_point_offset = 0;
|
||||
inline static size_t data_offset = 0;
|
||||
|
||||
void *amethod_from_reflected_method(JNIEnv *env, jobject method);
|
||||
|
||||
bool amethod_init(JNIEnv *env) {
|
||||
jclass clazz = env->FindClass("java/lang/reflect/Executable");
|
||||
if (!clazz) {
|
||||
LOGE("Failed to found Executable");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (art_method_field = env->GetFieldID(clazz, "artMethod", "J"); !art_method_field) {
|
||||
LOGE("Failed to find artMethod field");
|
||||
|
||||
env->DeleteLocalRef(clazz);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
jclass throwable = env->FindClass("java/lang/Throwable");
|
||||
if (!throwable) {
|
||||
LOGE("Failed to found Executable");
|
||||
|
||||
env->DeleteLocalRef(clazz);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
jclass clz = env->FindClass("java/lang/Class");
|
||||
if (!clz) {
|
||||
LOGE("Failed to found Class");
|
||||
|
||||
env->DeleteLocalRef(clazz);
|
||||
env->DeleteLocalRef(throwable);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
jmethodID get_declared_constructors = env->GetMethodID(clz, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;");
|
||||
env->DeleteLocalRef(clz);
|
||||
|
||||
const auto constructors = (jobjectArray) env->CallObjectMethod(throwable, get_declared_constructors);
|
||||
env->DeleteLocalRef(throwable);
|
||||
if (!constructors || env->GetArrayLength(constructors) < 2) {
|
||||
LOGE("Throwable has less than 2 constructors");
|
||||
|
||||
env->DeleteLocalRef(clazz);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
jobject first_ctor = env->GetObjectArrayElement(constructors, 0);
|
||||
jobject second_ctor = env->GetObjectArrayElement(constructors, 1);
|
||||
|
||||
uintptr_t first = (uintptr_t)amethod_from_reflected_method(env, first_ctor);
|
||||
uintptr_t second = (uintptr_t)amethod_from_reflected_method(env, second_ctor);
|
||||
|
||||
env->DeleteLocalRef(first_ctor);
|
||||
env->DeleteLocalRef(second_ctor);
|
||||
env->DeleteLocalRef(constructors);
|
||||
|
||||
art_method_size = (size_t)(second - first);
|
||||
LOGD("ArtMethod size: %zu", art_method_size);
|
||||
if ((4 * 9 + 3 * sizeof(void *)) < art_method_size) {
|
||||
LOGE("ArtMethod size exceeds maximum assume. There may be something wrong.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
entry_point_offset = art_method_size - sizeof(void *);
|
||||
data_offset = entry_point_offset - sizeof(void *);
|
||||
LOGD("ArtMethod entrypoint offset: %zu", entry_point_offset);
|
||||
LOGD("ArtMethod data offset: %zu", data_offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void *amethod_get_data(uintptr_t self) {
|
||||
return *(void **)((uintptr_t)self + data_offset);
|
||||
}
|
||||
|
||||
void *amethod_from_reflected_method(JNIEnv *env, jobject method) {
|
||||
if (art_method_field) {
|
||||
return (void *)env->GetLongField(method, art_method_field);
|
||||
} else {
|
||||
return (void *)env->FromReflectedMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* ART_METHOD_H */
|
||||
@@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "logging.h"
|
||||
#include "jni_helper.hpp"
|
||||
|
||||
template <typename T>
|
||||
constexpr inline auto RoundUpTo(T v, size_t size) {
|
||||
return v + size - 1 - ((v + size - 1) & (size - 1));
|
||||
}
|
||||
|
||||
inline static constexpr auto kPointerSize = sizeof(void *);
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
class ArtMethod {
|
||||
|
||||
public:
|
||||
void *GetData() {
|
||||
return *reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) + data_offset);
|
||||
}
|
||||
|
||||
static art::ArtMethod *FromReflectedMethod(JNIEnv *env, jobject method) {
|
||||
if (art_method_field) [[likely]] {
|
||||
return reinterpret_cast<art::ArtMethod *>(
|
||||
JNI_GetLongField(env, method, art_method_field));
|
||||
} else {
|
||||
return reinterpret_cast<art::ArtMethod *>(env->FromReflectedMethod(method));
|
||||
}
|
||||
}
|
||||
|
||||
static bool Init(JNIEnv *env) {
|
||||
ScopedLocalRef<jclass> executable{env, nullptr};
|
||||
executable = JNI_FindClass(env, "java/lang/reflect/Executable");
|
||||
if (!executable) {
|
||||
LOGE("Failed to found Executable");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (art_method_field = JNI_GetFieldID(env, executable, "artMethod", "J");
|
||||
!art_method_field) {
|
||||
LOGE("Failed to find artMethod field");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto throwable = JNI_FindClass(env, "java/lang/Throwable");
|
||||
if (!throwable) {
|
||||
LOGE("Failed to found Executable");
|
||||
return false;
|
||||
}
|
||||
auto clazz = JNI_FindClass(env, "java/lang/Class");
|
||||
static_assert(std::is_same_v<decltype(clazz)::BaseType, jclass>);
|
||||
jmethodID get_declared_constructors = JNI_GetMethodID(env, clazz, "getDeclaredConstructors",
|
||||
"()[Ljava/lang/reflect/Constructor;");
|
||||
const auto constructors =
|
||||
JNI_Cast<jobjectArray>(JNI_CallObjectMethod(env, throwable, get_declared_constructors));
|
||||
if (constructors.size() < 2) {
|
||||
LOGE("Throwable has less than 2 constructors");
|
||||
return false;
|
||||
}
|
||||
auto &first_ctor = constructors[0];
|
||||
auto &second_ctor = constructors[1];
|
||||
auto *first = FromReflectedMethod(env, first_ctor.get());
|
||||
auto *second = FromReflectedMethod(env, second_ctor.get());
|
||||
art_method_size = reinterpret_cast<uintptr_t>(second) - reinterpret_cast<uintptr_t>(first);
|
||||
LOGD("ArtMethod size: %zu", art_method_size);
|
||||
if (RoundUpTo(4 * 9, kPointerSize) + kPointerSize * 3 < art_method_size) [[unlikely]] {
|
||||
LOGW("ArtMethod size exceeds maximum assume. There may be something wrong.");
|
||||
}
|
||||
entry_point_offset = art_method_size - kPointerSize;
|
||||
data_offset = entry_point_offset - kPointerSize;
|
||||
LOGD("ArtMethod::entrypoint offset: %zu", entry_point_offset);
|
||||
LOGD("ArtMethod::data offset: %zu", data_offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
inline static jfieldID art_method_field = nullptr;
|
||||
inline static size_t art_method_size = 0;
|
||||
inline static size_t entry_point_offset = 0;
|
||||
inline static size_t data_offset = 0;
|
||||
};
|
||||
|
||||
} // namespace lsplant::art
|
||||
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]);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "daemon.h"
|
||||
#include "logging.h"
|
||||
#include "solist.h"
|
||||
#include "zygisk.hpp"
|
||||
|
||||
using namespace std;
|
||||
@@ -8,7 +9,7 @@ void *start_addr = nullptr;
|
||||
size_t block_size = 0;
|
||||
|
||||
extern "C" [[gnu::visibility("default")]]
|
||||
void entry(void* addr, size_t size, const char* path) {
|
||||
void entry(void *addr, size_t size) {
|
||||
LOGD("Zygisk library injected, version %s", ZKSU_VERSION);
|
||||
|
||||
start_addr = addr;
|
||||
@@ -22,5 +23,11 @@ void entry(void* addr, size_t size, const char* path) {
|
||||
|
||||
LOGD("start plt hooking");
|
||||
hook_functions();
|
||||
clean_trace(path, 1, 0, false);
|
||||
|
||||
solist_drop_so_path(addr, true);
|
||||
solist_reset_counters(1, 1);
|
||||
|
||||
send_seccomp_event();
|
||||
|
||||
LOGD("Zygisk library execution done, addr: %p, size: %zu", addr, size);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class ForkAndSpec(JNIHook):
|
||||
return 'nativeForkAndSpecialize'
|
||||
|
||||
def init_args(self):
|
||||
return 'AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);'
|
||||
return 'struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);'
|
||||
|
||||
def body(self):
|
||||
decl = ''
|
||||
@@ -117,7 +117,7 @@ class ForkServer(ForkAndSpec):
|
||||
return 'nativeForkSystemServer'
|
||||
|
||||
def init_args(self):
|
||||
return 'ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);'
|
||||
return 'struct server_specialize_args_v1 args(&uid, &gid, &gids, &runtime_flags, &permitted_capabilities, &effective_capabilities);'
|
||||
|
||||
# Common args
|
||||
uid = Argument('uid', jint)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <sys/mount.h>
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
#include <regex.h>
|
||||
#include <bitset>
|
||||
#include <list>
|
||||
@@ -7,7 +8,7 @@
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include <lsplt.hpp>
|
||||
#include <lsplt.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
@@ -21,12 +22,12 @@
|
||||
|
||||
#include "daemon.h"
|
||||
#include "zygisk.hpp"
|
||||
#include "module.hpp"
|
||||
#include "module.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "solist.h"
|
||||
|
||||
#include "art_method.hpp"
|
||||
#include "art_method.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -61,12 +62,11 @@ struct ZygiskContext {
|
||||
JNIEnv *env;
|
||||
union {
|
||||
void *ptr;
|
||||
AppSpecializeArgs_v5 *app;
|
||||
ServerSpecializeArgs_v1 *server;
|
||||
struct app_specialize_args_v5 *app;
|
||||
struct server_specialize_args_v1 *server;
|
||||
} args;
|
||||
|
||||
const char *process;
|
||||
list<ZygiskModule> modules;
|
||||
|
||||
int pid;
|
||||
bitset<FLAG_MAX> flags;
|
||||
@@ -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)
|
||||
@@ -123,8 +124,14 @@ struct ZygiskContext {
|
||||
// Global variables
|
||||
vector<tuple<dev_t, ino_t, const char *, void **>> *plt_hook_list;
|
||||
map<string, vector<JNINativeMethod>> *jni_hook_list;
|
||||
|
||||
bool modules_loaded = false;
|
||||
struct rezygisk_module *zygisk_modules = NULL;
|
||||
size_t zygisk_module_length = 0;
|
||||
|
||||
bool should_unmap_zygisk = false;
|
||||
std::vector<lsplt::MapInfo> cached_map_infos = {};
|
||||
bool enable_unloader = false;
|
||||
bool hooked_unloader = false;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -134,7 +141,10 @@ namespace {
|
||||
ret (*old_##func)(__VA_ARGS__); \
|
||||
ret new_##func(__VA_ARGS__)
|
||||
|
||||
// Skip actual fork and return cached result if applicable
|
||||
/* INFO: ReZygisk already performs a fork in ZygiskContext::fork_pre, because of that,
|
||||
we avoid duplicate fork in nativeForkAndSpecialize and nativeForkSystemServer
|
||||
by caching the pid in fork_pre function and only performing fork if the pid
|
||||
is non-0, or in other words, if we (libzygisk.so) already forked. */
|
||||
DCL_HOOK_FUNC(int, fork) {
|
||||
return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork();
|
||||
}
|
||||
@@ -173,39 +183,51 @@ bool update_mnt_ns(enum mount_namespace_state mns_state, bool dry_run) {
|
||||
|
||||
return true;
|
||||
}
|
||||
struct FileDescriptorInfo {
|
||||
const int fd;
|
||||
const struct stat stat;
|
||||
const std::string file_path;
|
||||
const int open_flags;
|
||||
const int fd_flags;
|
||||
const int fs_flags;
|
||||
const off_t offset;
|
||||
const bool is_sock;
|
||||
};
|
||||
|
||||
// Unmount stuffs in the process's private mount namespace
|
||||
DCL_HOOK_FUNC(int, unshare, int flags) {
|
||||
int res = old_unshare(flags);
|
||||
if (g_ctx && (flags & CLONE_NEWNS) != 0 && res == 0 &&
|
||||
// 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->flags[SERVER_FORK_AND_SPECIALIZE] && !(g_ctx->info_flags & PROCESS_IS_FIRST_STARTED)) {
|
||||
/* INFO: This hook avoids that umounted overlays made by root modules lead to Zygote
|
||||
to Abort its operation as it cannot open anymore.
|
||||
|
||||
/* INFO: There might be cases, specifically in Magisk, where the app is in
|
||||
DenyList but also has root privileges. For those, it is up to the
|
||||
user remove it, and the weird behavior is expected, as the weird
|
||||
user behavior. */
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-14.0.0_r1/core/jni/fd_utils.cpp#346
|
||||
- https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-14.0.0_r1/core/jni/fd_utils.cpp#544
|
||||
- https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-14.0.0_r1/core/jni/com_android_internal_os_Zygote.cpp#2329
|
||||
*/
|
||||
DCL_HOOK_FUNC(void, _ZNK18FileDescriptorInfo14ReopenOrDetachERKNSt3__18functionIFvNS0_12basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEEEE, void *_this, void *fail_fn) {
|
||||
const int fd = *(const int *)((uintptr_t)_this + offsetof(FileDescriptorInfo, fd));
|
||||
const std::string *file_path = (const std::string *)((uintptr_t)_this + offsetof(FileDescriptorInfo, file_path));
|
||||
const int open_flags = *(const int *)((uintptr_t)_this + offsetof(FileDescriptorInfo, open_flags));
|
||||
const bool is_sock = *(const bool *)((uintptr_t)_this + offsetof(FileDescriptorInfo, is_sock));
|
||||
|
||||
/* INFO: For cases like Magisk, where you can only give an app SU if it was
|
||||
either requested before or if it's not in DenyList, we cannot
|
||||
umount it, or else it will not be (easily) possible to give new
|
||||
apps SU. Apps that are not marked in APatch/KernelSU to be umounted
|
||||
are also expected to have AP/KSU mounts there, so we will follow the
|
||||
same idea by not umounting any mount. */
|
||||
int new_fd;
|
||||
|
||||
if (g_ctx->info_flags & (PROCESS_IS_MANAGER | PROCESS_GRANTED_ROOT) || !(g_ctx->flags[DO_REVERT_UNMOUNT])) {
|
||||
update_mnt_ns(Mounted, false);
|
||||
if (is_sock)
|
||||
goto bypass_fd_check;
|
||||
|
||||
if (strncmp(file_path->c_str(), "/memfd:/boot-image-methods.art", strlen("/memfd:/boot-image-methods.art")) == 0)
|
||||
goto bypass_fd_check;
|
||||
|
||||
new_fd = TEMP_FAILURE_RETRY(open(file_path->c_str(), open_flags));
|
||||
close(new_fd);
|
||||
if (new_fd == -1) {
|
||||
LOGD("Failed to open file %s, detaching it", file_path->c_str());
|
||||
|
||||
close(fd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
old_unshare(CLONE_NEWNS);
|
||||
}
|
||||
|
||||
/* INFO: To spoof the errno value */
|
||||
errno = 0;
|
||||
|
||||
return res;
|
||||
bypass_fd_check:
|
||||
old__ZNK18FileDescriptorInfo14ReopenOrDetachERKNSt3__18functionIFvNS0_12basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEEEE(_this, fail_fn);
|
||||
}
|
||||
|
||||
// We cannot directly call `dlclose` to unload ourselves, otherwise when `dlclose` returns,
|
||||
@@ -215,13 +237,21 @@ 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;
|
||||
|
||||
if (should_unmap_zygisk) {
|
||||
unhook_functions();
|
||||
cached_map_infos.clear();
|
||||
|
||||
/* INFO: Modules might use libzygisk.so after postAppSpecialize. We can only
|
||||
free it when we are really before our unmap. */
|
||||
free(zygisk_modules);
|
||||
|
||||
lsplt_free_resources();
|
||||
|
||||
if (should_unmap_zygisk) {
|
||||
// Because both `pthread_attr_setstacksize` and `dlclose` have the same function signature,
|
||||
@@ -242,13 +272,32 @@ DCL_HOOK_FUNC(char *, strdup, const char *s) {
|
||||
if (strcmp(s, "com.android.internal.os.ZygoteInit") == 0) {
|
||||
LOGV("strdup %s", s);
|
||||
initialize_jni_hook();
|
||||
cached_map_infos = lsplt::MapInfo::Scan();
|
||||
LOGD("cached_map_infos updated");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@@ -283,21 +332,23 @@ void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods
|
||||
nm.fnPtr = nullptr;
|
||||
continue;
|
||||
}
|
||||
auto method = lsplant::JNI_ToReflectedMethod(env, clazz, mid, is_static);
|
||||
auto modifier = lsplant::JNI_CallIntMethod(env, method, member_getModifiers);
|
||||
auto method = env->ToReflectedMethod(clazz, mid, is_static);
|
||||
auto modifier = env->CallIntMethod(method, member_getModifiers);
|
||||
if ((modifier & MODIFIER_NATIVE) == 0) {
|
||||
nm.fnPtr = nullptr;
|
||||
continue;
|
||||
}
|
||||
auto artMethod = lsplant::art::ArtMethod::FromReflectedMethod(env, method);
|
||||
auto artMethod = amethod_from_reflected_method(env, method);
|
||||
hooks.push_back(nm);
|
||||
auto orig = artMethod->GetData();
|
||||
auto orig = amethod_get_data((uintptr_t)artMethod);
|
||||
LOGV("replaced %s %s orig %p", clz, nm.name, orig);
|
||||
nm.fnPtr = orig;
|
||||
}
|
||||
|
||||
if (hooks.empty()) return;
|
||||
env->RegisterNatives(clazz, hooks.data(), hooks.size());
|
||||
|
||||
env->DeleteLocalRef(clazz);
|
||||
}
|
||||
|
||||
// JNI method hook definitions, auto generated
|
||||
@@ -307,9 +358,18 @@ void initialize_jni_hook() {
|
||||
auto get_created_java_vms = reinterpret_cast<jint (*)(JavaVM **, jsize, jsize *)>(
|
||||
dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs"));
|
||||
if (!get_created_java_vms) {
|
||||
for (auto &map: cached_map_infos) {
|
||||
if (!map.path.ends_with("/libnativehelper.so")) continue;
|
||||
void *h = dlopen(map.path.data(), RTLD_LAZY);
|
||||
struct lsplt_map_info *map_infos = lsplt_scan_maps("self");
|
||||
if (!map_infos) {
|
||||
LOGE("Failed to scan maps for self");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < map_infos->length; i++) {
|
||||
struct lsplt_map_entry map = map_infos->maps[i];
|
||||
|
||||
if (!strstr(map.path, "/libnativehelper.so")) continue;
|
||||
void *h = dlopen(map.path, RTLD_LAZY);
|
||||
if (!h) {
|
||||
LOGW("cannot dlopen libnativehelper.so: %s", dlerror());
|
||||
break;
|
||||
@@ -318,6 +378,9 @@ void initialize_jni_hook() {
|
||||
dlclose(h);
|
||||
break;
|
||||
}
|
||||
|
||||
lsplt_free_maps(map_infos);
|
||||
|
||||
if (!get_created_java_vms) {
|
||||
LOGW("JNI_GetCreatedJavaVMs not found");
|
||||
return;
|
||||
@@ -331,16 +394,21 @@ void initialize_jni_hook() {
|
||||
res = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (res != JNI_OK || env == nullptr) return;
|
||||
|
||||
auto classMember = lsplant::JNI_FindClass(env, "java/lang/reflect/Member");
|
||||
if (classMember != nullptr) member_getModifiers = lsplant::JNI_GetMethodID(env, classMember, "getModifiers", "()I");
|
||||
auto classModifier = lsplant::JNI_FindClass(env, "java/lang/reflect/Modifier");
|
||||
auto classMember = env->FindClass("java/lang/reflect/Member");
|
||||
if (classMember != nullptr) member_getModifiers = env->GetMethodID(classMember, "getModifiers", "()I");
|
||||
auto classModifier = env->FindClass("java/lang/reflect/Modifier");
|
||||
if (classModifier != nullptr) {
|
||||
auto fieldId = lsplant::JNI_GetStaticFieldID(env, classModifier, "NATIVE", "I");
|
||||
if (fieldId != nullptr) MODIFIER_NATIVE = lsplant::JNI_GetStaticIntField(env, classModifier, fieldId);
|
||||
auto fieldId = env->GetStaticFieldID(classModifier, "NATIVE", "I");
|
||||
if (fieldId != nullptr) MODIFIER_NATIVE = env->GetStaticIntField(classModifier, fieldId);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(classMember);
|
||||
env->DeleteLocalRef(classModifier);
|
||||
|
||||
if (member_getModifiers == nullptr || MODIFIER_NATIVE == 0) return;
|
||||
if (!lsplant::art::ArtMethod::Init(env)) {
|
||||
LOGE("failed to init ArtMethod");
|
||||
if (!amethod_init(env)) {
|
||||
LOGE("failed to init amethod");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -350,51 +418,101 @@ void initialize_jni_hook() {
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
ZygiskModule::ZygiskModule(int id, void *handle, void *entry)
|
||||
: id(id), handle(handle), entry{entry}, api{}, mod{nullptr} {
|
||||
// Make sure all pointers are null
|
||||
memset(&api, 0, sizeof(api));
|
||||
api.base.impl = this;
|
||||
api.base.registerModule = &ZygiskModule::RegisterModuleImpl;
|
||||
}
|
||||
bool rezygisk_module_register(struct rezygisk_api *api, struct rezygisk_abi *module) {
|
||||
LOGD("Registering module with API version %ld", module->api_version);
|
||||
|
||||
bool ZygiskModule::RegisterModuleImpl(ApiTable *api, long *module) {
|
||||
if (api == nullptr || module == nullptr)
|
||||
return false;
|
||||
|
||||
long api_version = *module;
|
||||
// Unsupported version
|
||||
if (api_version > ZYGISK_API_VERSION)
|
||||
if (module->api_version > REZYGISK_API_VERSION)
|
||||
return false;
|
||||
|
||||
// Set the actual module_abi*
|
||||
api->base.impl->mod = { module };
|
||||
struct rezygisk_module *m = &zygisk_modules[(size_t)api->impl];
|
||||
m->abi = *module;
|
||||
m->api = *api;
|
||||
|
||||
api->hook_jni_native_methods = hookJniNativeMethods;
|
||||
if (module->api_version >= 4) {
|
||||
api->plt_hook_register_v4 = [](dev_t dev, ino_t inode, const char *symbol, void *fn, void **backup) {
|
||||
LOGD("plt_hook_register_v4 called for dev=%lu, inode=%lu, symbol=%s, fn=%p, backup=%p",
|
||||
(unsigned long)dev, (unsigned long)inode, symbol, fn, backup);
|
||||
if (dev == 0 || inode == 0 || symbol == NULL || fn == NULL) {
|
||||
LOGE("Invalid arguments to plt_hook_register");
|
||||
|
||||
// Fill in API accordingly with module API version
|
||||
if (api_version >= 1) {
|
||||
api->v1.hookJniNativeMethods = hookJniNativeMethods;
|
||||
api->v1.pltHookRegister = [](auto a, auto b, auto c, auto d) {
|
||||
if (g_ctx) g_ctx->plt_hook_register(a, b, c, d);
|
||||
};
|
||||
api->v1.pltHookExclude = [](auto a, auto b) {
|
||||
if (g_ctx) g_ctx->plt_hook_exclude(a, b);
|
||||
};
|
||||
api->v1.pltHookCommit = []() { return g_ctx && g_ctx->plt_hook_commit(); };
|
||||
api->v1.connectCompanion = [](ZygiskModule *m) { return m->connectCompanion(); };
|
||||
api->v1.setOption = [](ZygiskModule *m, auto opt) { m->setOption(opt); };
|
||||
}
|
||||
if (api_version >= 2) {
|
||||
api->v2.getModuleDir = [](ZygiskModule *m) { return m->getModuleDir(); };
|
||||
api->v2.getFlags = [](auto) { return ZygiskModule::getFlags(); };
|
||||
}
|
||||
if (api_version >= 4) {
|
||||
api->v4.pltHookCommit = []() { return lsplt::CommitHook(cached_map_infos); };
|
||||
api->v4.pltHookRegister = [](dev_t dev, ino_t inode, const char *symbol, void *fn, void **backup) {
|
||||
if (dev == 0 || inode == 0 || symbol == nullptr || fn == nullptr)
|
||||
return;
|
||||
lsplt::RegisterHook(dev, inode, symbol, fn, backup);
|
||||
}
|
||||
|
||||
lsplt_register_hook(dev, inode, symbol, fn, backup);
|
||||
};
|
||||
api->exempt_fd = [](int fd) {
|
||||
LOGD("exempt_fd called for fd=%d", fd);
|
||||
if (g_ctx) g_ctx->exempt_fd(fd);
|
||||
};
|
||||
api->plt_hook_commit = []() {
|
||||
LOGD("plt_hook_commit called");
|
||||
return lsplt_commit_hook();
|
||||
};
|
||||
} else {
|
||||
api->plt_hook_register = [](const char *regex, const char *symbol, void *fn, void **backup) {
|
||||
if (g_ctx) g_ctx->plt_hook_register(regex, symbol, fn, backup);
|
||||
};
|
||||
api->plt_hook_exclude = [](const char *regex, const char *symbol) {
|
||||
if (g_ctx) g_ctx->plt_hook_exclude(regex, symbol);
|
||||
};
|
||||
api->plt_hook_commit = []() {
|
||||
return g_ctx && g_ctx->plt_hook_commit();
|
||||
};
|
||||
}
|
||||
api->connect_companion = [](void *id) {
|
||||
LOGD("connect_companion called for id=%p", id);
|
||||
if ((size_t)id >= zygisk_module_length) {
|
||||
LOGE("Invalid module id %zu", (size_t)id);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rezygiskd_connect_companion((size_t)id);
|
||||
};
|
||||
api->set_option = [](void *id, enum rezygisk_options opt) {
|
||||
if (!g_ctx) return;
|
||||
|
||||
LOGD("set_option called for id=%p, opt=%d", id, opt);
|
||||
|
||||
if ((size_t)id >= zygisk_module_length) {
|
||||
LOGE("Invalid module id %zu", (size_t)id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (opt) {
|
||||
case FORCE_DENYLIST_UNMOUNT: {
|
||||
g_ctx->flags[DO_REVERT_UNMOUNT] = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case DLCLOSE_MODULE_LIBRARY: {
|
||||
struct rezygisk_module *m = &zygisk_modules[(size_t)id];
|
||||
m->unload = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (module->api_version >= 2) {
|
||||
api->get_module_dir = [](void *id) {
|
||||
LOGD("get_module_dir called for id=%p", id);
|
||||
if ((size_t)id >= zygisk_module_length) {
|
||||
LOGE("Invalid module id %zu", (size_t)id);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rezygiskd_get_module_dir((size_t)id);
|
||||
};
|
||||
api->get_flags = []() {
|
||||
LOGD("get_flags called");
|
||||
return g_ctx ? (g_ctx->info_flags & ~PRIVATE_MASK) : 0;
|
||||
};
|
||||
api->v4.exemptFd = [](int fd) { return g_ctx && g_ctx->exempt_fd(fd); };
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -424,14 +542,24 @@ void ZygiskContext::plt_hook_exclude(const char *regex, const char *symbol) {
|
||||
void ZygiskContext::plt_hook_process_regex() {
|
||||
if (register_info.empty())
|
||||
return;
|
||||
for (auto &map : cached_map_infos) {
|
||||
|
||||
struct lsplt_map_info *map_infos = lsplt_scan_maps("self");
|
||||
if (!map_infos) {
|
||||
LOGE("Failed to scan maps for self");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < map_infos->length; i++) {
|
||||
struct lsplt_map_entry map = map_infos->maps[i];
|
||||
|
||||
if (map.offset != 0 || !map.is_private || !(map.perms & PROT_READ)) continue;
|
||||
for (auto ®: register_info) {
|
||||
if (regexec(®.regex, map.path.data(), 0, nullptr, 0) != 0)
|
||||
if (regexec(®.regex, map.path, 0, nullptr, 0) != 0)
|
||||
continue;
|
||||
bool ignored = false;
|
||||
for (auto &ign: ignore_info) {
|
||||
if (regexec(&ign.regex, map.path.data(), 0, nullptr, 0) != 0)
|
||||
if (regexec(&ign.regex, map.path, 0, nullptr, 0) != 0)
|
||||
continue;
|
||||
if (ign.symbol.empty() || ign.symbol == reg.symbol) {
|
||||
ignored = true;
|
||||
@@ -439,10 +567,12 @@ void ZygiskContext::plt_hook_process_regex() {
|
||||
}
|
||||
}
|
||||
if (!ignored) {
|
||||
lsplt::RegisterHook(map.dev, map.inode, reg.symbol, reg.callback, reg.backup);
|
||||
lsplt_register_hook(map.dev, map.inode, reg.symbol.c_str(), reg.callback, reg.backup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lsplt_free_maps(map_infos);
|
||||
}
|
||||
|
||||
bool ZygiskContext::plt_hook_commit() {
|
||||
@@ -454,50 +584,7 @@ bool ZygiskContext::plt_hook_commit() {
|
||||
pthread_mutex_unlock(&hook_info_lock);
|
||||
}
|
||||
|
||||
return lsplt::CommitHook(cached_map_infos);
|
||||
}
|
||||
|
||||
|
||||
bool ZygiskModule::valid() const {
|
||||
if (mod.api_version == nullptr)
|
||||
return false;
|
||||
switch (*mod.api_version) {
|
||||
case 4:
|
||||
case 3:
|
||||
case 2:
|
||||
case 1:
|
||||
return mod.v1->impl && mod.v1->preAppSpecialize && mod.v1->postAppSpecialize &&
|
||||
mod.v1->preServerSpecialize && mod.v1->postServerSpecialize;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Zygisksu changed: Use own zygiskd */
|
||||
int ZygiskModule::connectCompanion() const {
|
||||
return rezygiskd_connect_companion(id);
|
||||
}
|
||||
|
||||
/* Zygisksu changed: Use own zygiskd */
|
||||
int ZygiskModule::getModuleDir() const {
|
||||
return rezygiskd_get_module_dir(id);
|
||||
}
|
||||
|
||||
void ZygiskModule::setOption(zygisk::Option opt) {
|
||||
if (g_ctx == nullptr)
|
||||
return;
|
||||
switch (opt) {
|
||||
case zygisk::FORCE_DENYLIST_UNMOUNT:
|
||||
g_ctx->flags[DO_REVERT_UNMOUNT] = true;
|
||||
break;
|
||||
case zygisk::DLCLOSE_MODULE_LIBRARY:
|
||||
unload = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ZygiskModule::getFlags() {
|
||||
return g_ctx ? (g_ctx->info_flags & ~PRIVATE_MASK) : 0;
|
||||
return lsplt_commit_hook();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@@ -563,8 +650,14 @@ void ZygiskContext::sanitize_fds() {
|
||||
allowed_fds[fd] = true;
|
||||
}
|
||||
}
|
||||
|
||||
jintArray old_array = *args.app->fds_to_ignore;
|
||||
|
||||
*args.app->fds_to_ignore = array;
|
||||
flags[SKIP_FD_SANITIZATION] = true;
|
||||
|
||||
env->DeleteLocalRef(old_array);
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
@@ -601,9 +694,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 +710,21 @@ 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;
|
||||
}
|
||||
|
||||
zygisk_modules = (struct rezygisk_module *)malloc(ms.modules_count * sizeof(struct rezygisk_module));
|
||||
if (!zygisk_modules) {
|
||||
LOGE("Failed to allocate memory for modules");
|
||||
|
||||
free_modules(&ms);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ms.modules_count; i++) {
|
||||
@@ -643,16 +746,30 @@ void ZygiskContext::run_modules_pre() {
|
||||
continue;
|
||||
}
|
||||
|
||||
modules.emplace_back(i, handle, entry);
|
||||
zygisk_modules[zygisk_module_length].api.register_module = rezygisk_module_register;
|
||||
zygisk_modules[zygisk_module_length].api.impl = (void *)zygisk_module_length;
|
||||
|
||||
zygisk_modules[zygisk_module_length].handle = handle;
|
||||
zygisk_modules[zygisk_module_length].zygisk_module_entry = (void (*)(void *, void *))entry;
|
||||
|
||||
zygisk_modules[zygisk_module_length].unload = false;
|
||||
|
||||
zygisk_module_length++;
|
||||
|
||||
/* INFO: The module will call register module function, so by then, it must be fully registered. */
|
||||
rezygisk_module_call_on_load(&zygisk_modules[zygisk_module_length - 1], env);
|
||||
}
|
||||
|
||||
free_modules(&ms);
|
||||
|
||||
for (auto &m : modules) {
|
||||
m.onLoad(env);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags[APP_SPECIALIZE]) m.preAppSpecialize(args.app);
|
||||
else if (flags[SERVER_FORK_AND_SPECIALIZE]) m.preServerSpecialize(args.server);
|
||||
/* Zygisksu changed: Load module fds */
|
||||
void ZygiskContext::run_modules_pre() {
|
||||
for (size_t i = 0; i < zygisk_module_length; i++) {
|
||||
if (flags[APP_SPECIALIZE]) rezygisk_module_call_pre_app_specialize(&zygisk_modules[i], args.app);
|
||||
else if (flags[SERVER_FORK_AND_SPECIALIZE]) rezygisk_module_call_pre_server_specialize(&zygisk_modules[i], args.server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,16 +777,31 @@ void ZygiskContext::run_modules_post() {
|
||||
flags[POST_SPECIALIZE] = true;
|
||||
|
||||
size_t modules_unloaded = 0;
|
||||
for (const auto &m : modules) {
|
||||
if (flags[APP_SPECIALIZE]) m.postAppSpecialize(args.app);
|
||||
else if (flags[SERVER_FORK_AND_SPECIALIZE]) m.postServerSpecialize(args.server);
|
||||
for (size_t i = 0; i < zygisk_module_length; i++) {
|
||||
struct rezygisk_module *m = &zygisk_modules[i];
|
||||
|
||||
if (m.tryUnload()) modules_unloaded++;
|
||||
if (flags[APP_SPECIALIZE]) rezygisk_module_call_post_app_specialize(m, args.app);
|
||||
else if (flags[SERVER_FORK_AND_SPECIALIZE]) rezygisk_module_call_post_server_specialize(m, args.server);
|
||||
|
||||
/* INFO: If module is unloaded by dlclose, there's no need to
|
||||
hide it from soinfo manually. */
|
||||
if (m->unload && dlclose(m->handle) == 0) modules_unloaded++;
|
||||
else if (m->unload) {
|
||||
PLOGE("Failed to unload module %zu", i);
|
||||
} else {
|
||||
bool has_dropped = solist_drop_so_path((void *)m->zygisk_module_entry, false);
|
||||
if (!has_dropped) continue;
|
||||
|
||||
LOGD("Dropped solist record for %p", (void *)m->zygisk_module_entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (modules.size() > 0) {
|
||||
LOGD("modules unloaded: %zu/%zu", modules_unloaded, modules.size());
|
||||
clean_trace("/data/adb", modules.size(), modules_unloaded, true);
|
||||
if (zygisk_module_length > 0) {
|
||||
LOGD("Modules unloaded: %zu/%zu", modules_unloaded, zygisk_module_length);
|
||||
|
||||
solist_reset_counters(zygisk_module_length, modules_unloaded);
|
||||
|
||||
LOGD("Returned global counters to their original values");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +809,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,24 +860,36 @@ 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. */
|
||||
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.
|
||||
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.
|
||||
*/
|
||||
if ((info_flags & PROCESS_ON_DENYLIST) == PROCESS_ON_DENYLIST) {
|
||||
setenv("ZYGISK_ENABLED", "1", 1);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@@ -714,7 +899,17 @@ void ZygiskContext::app_specialize_pre() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -722,7 +917,7 @@ void ZygiskContext::app_specialize_post() {
|
||||
run_modules_post();
|
||||
|
||||
// Cleanups
|
||||
env->ReleaseStringUTFChars(args.app->nice_name, process);
|
||||
env->ReleaseStringUTFChars(*args.app->nice_name, process);
|
||||
g_ctx = nullptr;
|
||||
}
|
||||
|
||||
@@ -738,7 +933,7 @@ bool ZygiskContext::exempt_fd(int fd) {
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
void ZygiskContext::nativeSpecializeAppProcess_pre() {
|
||||
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
||||
process = env->GetStringUTFChars(*args.app->nice_name, nullptr);
|
||||
LOGV("pre specialize [%s]", process);
|
||||
// App specialize does not check FD
|
||||
flags[SKIP_FD_SANITIZATION] = true;
|
||||
@@ -755,6 +950,11 @@ void ZygiskContext::nativeForkSystemServer_pre() {
|
||||
LOGV("pre forkSystemServer");
|
||||
flags[SERVER_FORK_AND_SPECIALIZE] = true;
|
||||
|
||||
if (!modules_loaded) {
|
||||
load_modules_only();
|
||||
modules_loaded = true;
|
||||
}
|
||||
|
||||
fork_pre();
|
||||
if (!is_child())
|
||||
return;
|
||||
@@ -774,12 +974,14 @@ void ZygiskContext::nativeForkSystemServer_post() {
|
||||
}
|
||||
|
||||
void ZygiskContext::nativeForkAndSpecialize_pre() {
|
||||
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
||||
process = env->GetStringUTFChars(*args.app->nice_name, nullptr);
|
||||
LOGV("pre forkAndSpecialize [%s]", process);
|
||||
flags[APP_FORK_AND_SPECIALIZE] = true;
|
||||
|
||||
fork_pre();
|
||||
if (pid == 0)
|
||||
if (!is_child())
|
||||
return;
|
||||
|
||||
app_specialize_pre();
|
||||
|
||||
sanitize_fds();
|
||||
@@ -806,28 +1008,31 @@ ZygiskContext::~ZygiskContext() {
|
||||
|
||||
// Unhook JNI methods
|
||||
for (const auto &[clz, methods] : *jni_hook_list) {
|
||||
if (!methods.empty() && env->RegisterNatives(
|
||||
env->FindClass(clz.data()), methods.data(),
|
||||
static_cast<int>(methods.size())) != 0) {
|
||||
jclass jc = env->FindClass(clz.data());
|
||||
if (jc) {
|
||||
if (!methods.empty() && env->RegisterNatives(jc, methods.data(),
|
||||
static_cast<jint>(methods.size())) != 0) {
|
||||
LOGE("Failed to restore JNI hook of class [%s]", clz.data());
|
||||
should_unmap_zygisk = false;
|
||||
}
|
||||
env->DeleteLocalRef(jc);
|
||||
}
|
||||
}
|
||||
delete jni_hook_list;
|
||||
jni_hook_list = nullptr;
|
||||
|
||||
// Strip out all API function pointers
|
||||
for (auto &m : modules) {
|
||||
m.clearApi();
|
||||
for (size_t i = 0; i < zygisk_module_length; i++) {
|
||||
memset(&zygisk_modules[i], 0, sizeof(zygisk_modules[i]));
|
||||
}
|
||||
|
||||
hook_unloader();
|
||||
enable_unloader = true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static bool hook_commit(std::vector<lsplt::MapInfo> &map_infos = cached_map_infos) {
|
||||
if (lsplt::CommitHook(map_infos)) {
|
||||
static bool hook_commit(struct lsplt_map_info *map_infos) {
|
||||
if (map_infos ? lsplt_commit_hook_manual(map_infos) : lsplt_commit_hook()) {
|
||||
return true;
|
||||
} else {
|
||||
LOGE("plt_hook failed");
|
||||
@@ -836,7 +1041,7 @@ static bool hook_commit(std::vector<lsplt::MapInfo> &map_infos = cached_map_info
|
||||
}
|
||||
|
||||
static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_func, void **old_func) {
|
||||
if (!lsplt::RegisterHook(dev, inode, symbol, new_func, old_func)) {
|
||||
if (!lsplt_register_hook(dev, inode, symbol, new_func, old_func)) {
|
||||
LOGE("Failed to register plt_hook \"%s\"", symbol);
|
||||
return;
|
||||
}
|
||||
@@ -849,40 +1054,6 @@ 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) {
|
||||
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;
|
||||
|
||||
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
|
||||
for (auto &map : lsplt::MapInfo::Scan()) {
|
||||
if (strstr(map.path.c_str(), path) && strstr(map.path.c_str(), "libzygisk") == 0)
|
||||
{
|
||||
void *addr = (void *)map.start;
|
||||
size_t size = map.end - map.start;
|
||||
void *copy = mmap(nullptr, size, PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
|
||||
if (copy == MAP_FAILED) {
|
||||
LOGE("failed to backup block %s [%p, %p]", map.path.c_str(), addr, (void*)map.end);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((map.perms & PROT_READ) == 0) {
|
||||
mprotect(addr, size, PROT_READ);
|
||||
}
|
||||
memcpy(copy, addr, size);
|
||||
mremap(copy, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, addr);
|
||||
mprotect(addr, size, map.perms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hook_functions() {
|
||||
plt_hook_list = new vector<tuple<dev_t, ino_t, const char *, void **>>();
|
||||
jni_hook_list = new map<string, vector<JNINativeMethod>>();
|
||||
@@ -890,20 +1061,33 @@ void hook_functions() {
|
||||
ino_t android_runtime_inode = 0;
|
||||
dev_t android_runtime_dev = 0;
|
||||
|
||||
cached_map_infos = lsplt::MapInfo::Scan();
|
||||
for (auto &map : cached_map_infos) {
|
||||
if (map.path.ends_with("libandroid_runtime.so")) {
|
||||
struct lsplt_map_info *map_infos = lsplt_scan_maps("self");
|
||||
if (!map_infos) {
|
||||
LOGE("Failed to scan maps for self");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < map_infos->length; i++) {
|
||||
struct lsplt_map_entry map = map_infos->maps[i];
|
||||
|
||||
if (!strstr(map.path, "libandroid_runtime.so")) continue;
|
||||
|
||||
android_runtime_inode = map.inode;
|
||||
android_runtime_dev = map.dev;
|
||||
|
||||
LOGD("Found libandroid_runtime.so at [%zu:%lu]", android_runtime_dev, android_runtime_inode);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
hook_commit();
|
||||
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, property_get);
|
||||
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, _ZNK18FileDescriptorInfo14ReopenOrDetachERKNSt3__18functionIFvNS0_12basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEEEE);
|
||||
hook_commit(map_infos);
|
||||
|
||||
lsplt_free_maps(map_infos);
|
||||
|
||||
// Remove unhooked methods
|
||||
plt_hook_list->erase(
|
||||
@@ -913,36 +1097,61 @@ void hook_functions() {
|
||||
}
|
||||
|
||||
static void hook_unloader() {
|
||||
if (hooked_unloader) return;
|
||||
hooked_unloader = true;
|
||||
|
||||
ino_t art_inode = 0;
|
||||
dev_t art_dev = 0;
|
||||
|
||||
for (auto &map : cached_map_infos) {
|
||||
if (map.path.ends_with("/libart.so")) {
|
||||
struct lsplt_map_info *map_infos = lsplt_scan_maps("self");
|
||||
if (!map_infos) {
|
||||
LOGE("Failed to scan maps for self");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < map_infos->length; i++) {
|
||||
struct lsplt_map_entry map = map_infos->maps[i];
|
||||
|
||||
if (strstr(map.path, "/libart.so") != nullptr) {
|
||||
art_inode = map.inode;
|
||||
art_dev = map.dev;
|
||||
|
||||
LOGD("Found libart.so at [%zu:%lu]", art_dev, art_inode);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
return;
|
||||
|
||||
hooked_unloader = false;
|
||||
} else {
|
||||
LOGD("hook_unloader called with libart.so [%zu:%lu]", art_dev, art_inode);
|
||||
}
|
||||
|
||||
PLT_HOOK_REGISTER(art_dev, art_inode, pthread_attr_setstacksize);
|
||||
hook_commit();
|
||||
hook_commit(map_infos);
|
||||
}
|
||||
|
||||
lsplt_free_maps(map_infos);
|
||||
}
|
||||
|
||||
static void unhook_functions() {
|
||||
// Unhook plt_hook
|
||||
for (const auto &[dev, inode, sym, old_func] : *plt_hook_list) {
|
||||
if (!lsplt::RegisterHook(dev, inode, sym, *old_func, nullptr)) {
|
||||
if (!lsplt_register_hook(dev, inode, sym, *old_func, NULL)) {
|
||||
LOGE("Failed to register plt_hook [%s]", sym);
|
||||
}
|
||||
}
|
||||
delete plt_hook_list;
|
||||
if (!hook_commit()) {
|
||||
if (!hook_commit(NULL)) {
|
||||
LOGE("Failed to restore plt_hook");
|
||||
should_unmap_zygisk = false;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ namespace {
|
||||
|
||||
void *nativeForkAndSpecialize_orig = nullptr;
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_l(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<decltype(&nativeForkAndSpecialize_l)>(nativeForkAndSpecialize_orig)(
|
||||
@@ -14,7 +14,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_o(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
@@ -25,7 +25,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_p(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
ZygiskContext ctx(env, &args);
|
||||
@@ -37,7 +37,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_q_alt(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
@@ -50,7 +50,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_r(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
@@ -67,7 +67,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
@@ -85,7 +85,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_m(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _0, jint _1, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_m)>(nativeForkAndSpecialize_orig)(
|
||||
@@ -95,7 +95,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_n(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _2, jint _3, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir, jint _4) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_n)>(nativeForkAndSpecialize_orig)(
|
||||
@@ -105,7 +105,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_o(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _5, jint _6, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
@@ -116,7 +116,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_p(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _7, jint _8, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
ZygiskContext ctx(env, &args);
|
||||
@@ -128,7 +128,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_grapheneos_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides, jlongArray _13) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
@@ -205,7 +205,7 @@ std::array nativeForkAndSpecialize_methods = {
|
||||
|
||||
void *nativeSpecializeAppProcess_orig = nullptr;
|
||||
[[clang::no_stack_protector]] void nativeSpecializeAppProcess_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
@@ -215,7 +215,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
[[clang::no_stack_protector]] void nativeSpecializeAppProcess_q_alt(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
ZygiskContext ctx(env, &args);
|
||||
@@ -226,7 +226,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
[[clang::no_stack_protector]] void nativeSpecializeAppProcess_r(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
args.pkg_data_info_list = &pkg_data_info_list;
|
||||
@@ -241,7 +241,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
[[clang::no_stack_protector]] void nativeSpecializeAppProcess_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
args.pkg_data_info_list = &pkg_data_info_list;
|
||||
@@ -257,7 +257,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
[[clang::no_stack_protector]] void nativeSpecializeAppProcess_samsung_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _9, jint _10, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
@@ -267,7 +267,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
[[clang::no_stack_protector]] void nativeSpecializeAppProcess_grapheneos_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides, jlongArray _14) {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
args.pkg_data_info_list = &pkg_data_info_list;
|
||||
@@ -317,7 +317,7 @@ std::array nativeSpecializeAppProcess_methods = {
|
||||
|
||||
void *nativeForkSystemServer_orig = nullptr;
|
||||
[[clang::no_stack_protector]] jint nativeForkSystemServer_l(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) {
|
||||
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
|
||||
struct server_specialize_args_v1 args(&uid, &gid, &gids, &runtime_flags, &permitted_capabilities, &effective_capabilities);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkSystemServer_pre();
|
||||
reinterpret_cast<decltype(&nativeForkSystemServer_l)>(nativeForkSystemServer_orig)(
|
||||
@@ -327,7 +327,7 @@ void *nativeForkSystemServer_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkSystemServer_samsung_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _11, jint _12, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) {
|
||||
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
|
||||
struct server_specialize_args_v1 args(&uid, &gid, &gids, &runtime_flags, &permitted_capabilities, &effective_capabilities);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkSystemServer_pre();
|
||||
reinterpret_cast<decltype(&nativeForkSystemServer_samsung_q)>(nativeForkSystemServer_orig)(
|
||||
@@ -337,7 +337,7 @@ void *nativeForkSystemServer_orig = nullptr;
|
||||
return ctx.pid;
|
||||
}
|
||||
[[clang::no_stack_protector]] jint nativeForkSystemServer_grapheneos_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) {
|
||||
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
|
||||
struct server_specialize_args_v1 args(&uid, &gid, &gids, &runtime_flags, &permitted_capabilities, &effective_capabilities);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkSystemServer_pre();
|
||||
reinterpret_cast<decltype(&nativeForkSystemServer_grapheneos_u)>(nativeForkSystemServer_orig)(
|
||||
|
||||
252
loader/src/injector/module.h
Normal file
252
loader/src/injector/module.h
Normal file
@@ -0,0 +1,252 @@
|
||||
#ifndef MODULE_H
|
||||
#define MODULE_H
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#define REZYGISK_API_VERSION 5
|
||||
|
||||
enum rezygiskd_flags : uint32_t {
|
||||
PROCESS_GRANTED_ROOT = (1u << 0),
|
||||
PROCESS_ON_DENYLIST = (1u << 1),
|
||||
|
||||
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_FIRST_STARTED = (1u << 31),
|
||||
|
||||
PRIVATE_MASK = PROCESS_IS_FIRST_STARTED
|
||||
};
|
||||
|
||||
struct app_specialize_args_v1 {
|
||||
jint *uid;
|
||||
jint *gid;
|
||||
jintArray *gids;
|
||||
jint *runtime_flags;
|
||||
jint *mount_external;
|
||||
jstring *se_info;
|
||||
jstring *nice_name;
|
||||
jstring *instruction_set;
|
||||
jstring *app_data_dir;
|
||||
|
||||
jboolean *is_child_zygote;
|
||||
jboolean *is_top_app;
|
||||
jobjectArray *pkg_data_info_list;
|
||||
jobjectArray *whitelisted_data_info_list;
|
||||
jboolean *mount_data_dirs;
|
||||
jboolean *mount_storage_dirs;
|
||||
};
|
||||
|
||||
struct app_specialize_args_v4 {
|
||||
jint *uid;
|
||||
jint *gid;
|
||||
jintArray *gids;
|
||||
jint *runtime_flags;
|
||||
jobjectArray *rlimits;
|
||||
jint *mount_external;
|
||||
jstring *se_info;
|
||||
jstring *nice_name;
|
||||
jstring *instruction_set;
|
||||
jstring *app_data_dir;
|
||||
|
||||
jintArray *fds_to_ignore;
|
||||
jboolean *is_child_zygote;
|
||||
jboolean *is_top_app;
|
||||
jobjectArray *pkg_data_info_list;
|
||||
jobjectArray *whitelisted_data_info_list;
|
||||
jboolean *mount_data_dirs;
|
||||
jboolean *mount_storage_dirs;
|
||||
};
|
||||
|
||||
struct app_specialize_args_v5 {
|
||||
jint *uid;
|
||||
jint *gid;
|
||||
jintArray *gids;
|
||||
jint *runtime_flags;
|
||||
jobjectArray *rlimits;
|
||||
jint *mount_external;
|
||||
jstring *se_info;
|
||||
jstring *nice_name;
|
||||
jstring *instruction_set;
|
||||
jstring *app_data_dir;
|
||||
|
||||
jintArray *fds_to_ignore;
|
||||
jboolean *is_child_zygote;
|
||||
jboolean *is_top_app;
|
||||
jobjectArray *pkg_data_info_list;
|
||||
jobjectArray *whitelisted_data_info_list;
|
||||
jboolean *mount_data_dirs;
|
||||
jboolean *mount_storage_dirs;
|
||||
|
||||
jboolean *mount_sysprop_overrides;
|
||||
};
|
||||
|
||||
struct server_specialize_args_v1 {
|
||||
jint *uid;
|
||||
jint *gid;
|
||||
jintArray *gids;
|
||||
jint *runtime_flags;
|
||||
jlong *permitted_capabilities;
|
||||
jlong *effective_capabilities;
|
||||
};
|
||||
|
||||
enum rezygisk_options : uint32_t {
|
||||
/* INFO: Force ReZygisk to umount the root related mounts on this process. This option
|
||||
will only take effect if set in pre...Specialize, as ReZygisk umounts at
|
||||
that point.
|
||||
|
||||
ReZygisk Umount System will not umount all root related mounts, read ReZygiskd
|
||||
umount_root function in utils.c file to understand how it selects the ones
|
||||
to umount.
|
||||
*/
|
||||
FORCE_DENYLIST_UNMOUNT = 0,
|
||||
|
||||
/* INFO: Once set, ReZygisk will dlclose your library from the process, this is assured to
|
||||
happen after post...Specialize, but not at a specific moment due to different
|
||||
implementations.
|
||||
|
||||
You should not use this option if you leave references in the process such as hooks,
|
||||
which will try to execute unitialized memory.
|
||||
*/
|
||||
DLCLOSE_MODULE_LIBRARY = 1
|
||||
};
|
||||
|
||||
struct rezygisk_api {
|
||||
void *impl;
|
||||
bool (*register_module)(struct rezygisk_api *, struct rezygisk_abi *);
|
||||
|
||||
void (*hook_jni_native_methods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
||||
union {
|
||||
void (*plt_hook_register)(const char *, const char *, void *, void **); /* INFO: v3 and below */
|
||||
void (*plt_hook_register_v4)(dev_t, ino_t, const char *, void *, void **); /* INFO: v4 */
|
||||
};
|
||||
union {
|
||||
void (*plt_hook_exclude)(const char *, const char *); /* INFO: v3 and below */
|
||||
void (*exempt_fd)(int); /* INFO: v4 */
|
||||
};
|
||||
bool (*plt_hook_commit)();
|
||||
int (*connect_companion)(void *);
|
||||
void (*set_option)(void *, enum rezygisk_options opt);
|
||||
int (*get_module_dir)(void *);
|
||||
uint32_t (*get_flags)();
|
||||
};
|
||||
|
||||
struct rezygisk_abi {
|
||||
long api_version;
|
||||
void *impl;
|
||||
|
||||
void (*pre_app_specialize)(void *, void *);
|
||||
void (*post_app_specialize)(void *, const void *);
|
||||
void (*pre_server_specialize)(void *, void *);
|
||||
void (*post_server_specialize)(void *, const void *);
|
||||
};
|
||||
|
||||
struct rezygisk_module {
|
||||
struct rezygisk_abi abi;
|
||||
struct rezygisk_api api;
|
||||
|
||||
void *handle;
|
||||
void (*zygisk_module_entry)(void *, void *);
|
||||
|
||||
bool unload;
|
||||
};
|
||||
|
||||
void rezygisk_module_call_on_load(struct rezygisk_module *m, void *env) {
|
||||
m->zygisk_module_entry((void *)&m->api, env);
|
||||
}
|
||||
|
||||
void rezygisk_module_call_pre_app_specialize(struct rezygisk_module *m, struct app_specialize_args_v5 *args) {
|
||||
switch (m->abi.api_version) {
|
||||
case 1:
|
||||
case 2: {
|
||||
struct app_specialize_args_v1 versioned_args = {
|
||||
.uid = args->uid,
|
||||
.gid = args->gid,
|
||||
.gids = args->gids,
|
||||
.runtime_flags = args->runtime_flags,
|
||||
.mount_external = args->mount_external,
|
||||
.se_info = args->se_info,
|
||||
.nice_name = args->nice_name,
|
||||
.instruction_set = args->instruction_set,
|
||||
.app_data_dir = args->app_data_dir,
|
||||
.is_child_zygote = args->is_child_zygote,
|
||||
.is_top_app = args->is_top_app,
|
||||
.pkg_data_info_list = args->pkg_data_info_list,
|
||||
.whitelisted_data_info_list = args->whitelisted_data_info_list,
|
||||
.mount_data_dirs = args->mount_data_dirs,
|
||||
.mount_storage_dirs = args->mount_storage_dirs
|
||||
};
|
||||
|
||||
m->abi.pre_app_specialize(m->abi.impl, &versioned_args);
|
||||
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
case 4: {
|
||||
struct app_specialize_args_v4 versioned_args;
|
||||
memcpy(&versioned_args, args, sizeof(struct app_specialize_args_v4));
|
||||
|
||||
m->abi.pre_app_specialize(m->abi.impl, &versioned_args);
|
||||
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
m->abi.pre_app_specialize(m->abi.impl, args);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rezygisk_module_call_post_app_specialize(struct rezygisk_module *m, const struct app_specialize_args_v5 *args) {
|
||||
switch (m->abi.api_version) {
|
||||
case 1:
|
||||
case 2: {
|
||||
struct app_specialize_args_v1 versioned_args = {
|
||||
.uid = args->uid,
|
||||
.gid = args->gid,
|
||||
.gids = args->gids,
|
||||
.runtime_flags = args->runtime_flags,
|
||||
.mount_external = args->mount_external,
|
||||
.se_info = args->se_info,
|
||||
.nice_name = args->nice_name,
|
||||
.instruction_set = args->instruction_set,
|
||||
.app_data_dir = args->app_data_dir,
|
||||
.is_child_zygote = args->is_child_zygote,
|
||||
.is_top_app = args->is_top_app,
|
||||
.pkg_data_info_list = args->pkg_data_info_list,
|
||||
.whitelisted_data_info_list = args->whitelisted_data_info_list,
|
||||
.mount_data_dirs = args->mount_data_dirs,
|
||||
.mount_storage_dirs = args->mount_storage_dirs
|
||||
};
|
||||
|
||||
m->abi.post_app_specialize(m->abi.impl, &versioned_args);
|
||||
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
case 4: {
|
||||
struct app_specialize_args_v4 versioned_args;
|
||||
memcpy(&versioned_args, args, sizeof(struct app_specialize_args_v4));
|
||||
|
||||
m->abi.post_app_specialize(m->abi.impl, &versioned_args);
|
||||
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
m->abi.post_app_specialize(m->abi.impl, args);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rezygisk_module_call_pre_server_specialize(struct rezygisk_module *m, struct server_specialize_args_v1 *args) {
|
||||
m->abi.pre_server_specialize(m->abi.impl, args);
|
||||
}
|
||||
|
||||
void rezygisk_module_call_post_server_specialize(struct rezygisk_module *m, const struct server_specialize_args_v1 *args) {
|
||||
m->abi.post_server_specialize(m->abi.impl, args);
|
||||
}
|
||||
|
||||
#endif /* MODULE_H */
|
||||
@@ -1,239 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
#include "api.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
struct ZygiskContext;
|
||||
struct ZygiskModule;
|
||||
|
||||
struct AppSpecializeArgs_v1;
|
||||
using AppSpecializeArgs_v2 = AppSpecializeArgs_v1;
|
||||
struct AppSpecializeArgs_v3;
|
||||
using AppSpecializeArgs_v4 = AppSpecializeArgs_v3;
|
||||
struct AppSpecializeArgs_v5;
|
||||
|
||||
struct module_abi_v1;
|
||||
using module_abi_v2 = module_abi_v1;
|
||||
using module_abi_v3 = module_abi_v1;
|
||||
using module_abi_v4 = module_abi_v1;
|
||||
using module_abi_v5 = module_abi_v1;
|
||||
|
||||
struct api_abi_v1;
|
||||
struct api_abi_v2;
|
||||
using api_abi_v3 = api_abi_v2;
|
||||
struct api_abi_v4;
|
||||
using api_abi_v5 = api_abi_v4;
|
||||
|
||||
union ApiTable;
|
||||
|
||||
struct AppSpecializeArgs_v3 {
|
||||
jint &uid;
|
||||
jint &gid;
|
||||
jintArray &gids;
|
||||
jint &runtime_flags;
|
||||
jobjectArray &rlimits;
|
||||
jint &mount_external;
|
||||
jstring &se_info;
|
||||
jstring &nice_name;
|
||||
jstring &instruction_set;
|
||||
jstring &app_data_dir;
|
||||
|
||||
jintArray *fds_to_ignore = nullptr;
|
||||
jboolean *is_child_zygote = nullptr;
|
||||
jboolean *is_top_app = nullptr;
|
||||
jobjectArray *pkg_data_info_list = nullptr;
|
||||
jobjectArray *whitelisted_data_info_list = nullptr;
|
||||
jboolean *mount_data_dirs = nullptr;
|
||||
jboolean *mount_storage_dirs = nullptr;
|
||||
|
||||
AppSpecializeArgs_v3(
|
||||
jint &uid, jint &gid, jintArray &gids, jint &runtime_flags,
|
||||
jobjectArray &rlimits, jint &mount_external, jstring &se_info, jstring &nice_name,
|
||||
jstring &instruction_set, jstring &app_data_dir) :
|
||||
uid(uid), gid(gid), gids(gids), runtime_flags(runtime_flags), rlimits(rlimits),
|
||||
mount_external(mount_external), se_info(se_info), nice_name(nice_name),
|
||||
instruction_set(instruction_set), app_data_dir(app_data_dir) {}
|
||||
};
|
||||
|
||||
struct AppSpecializeArgs_v5 : public AppSpecializeArgs_v3 {
|
||||
jboolean *mount_sysprop_overrides = nullptr;
|
||||
|
||||
AppSpecializeArgs_v5(
|
||||
jint &uid, jint &gid, jintArray &gids, jint &runtime_flags,
|
||||
jobjectArray &rlimits, jint &mount_external, jstring &se_info, jstring &nice_name,
|
||||
jstring &instruction_set, jstring &app_data_dir) : AppSpecializeArgs_v3(
|
||||
uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, nice_name, instruction_set, app_data_dir) {}
|
||||
};
|
||||
|
||||
struct AppSpecializeArgs_v1 {
|
||||
jint &uid;
|
||||
jint &gid;
|
||||
jintArray &gids;
|
||||
jint &runtime_flags;
|
||||
jint &mount_external;
|
||||
jstring &se_info;
|
||||
jstring &nice_name;
|
||||
jstring &instruction_set;
|
||||
jstring &app_data_dir;
|
||||
|
||||
jboolean *const is_child_zygote;
|
||||
jboolean *const is_top_app;
|
||||
jobjectArray *const pkg_data_info_list;
|
||||
jobjectArray *const whitelisted_data_info_list;
|
||||
jboolean *const mount_data_dirs;
|
||||
jboolean *const mount_storage_dirs;
|
||||
|
||||
AppSpecializeArgs_v1(const AppSpecializeArgs_v5 *a) :
|
||||
uid(a->uid), gid(a->gid), gids(a->gids), runtime_flags(a->runtime_flags),
|
||||
mount_external(a->mount_external), se_info(a->se_info), nice_name(a->nice_name),
|
||||
instruction_set(a->instruction_set), app_data_dir(a->app_data_dir),
|
||||
is_child_zygote(a->is_child_zygote), is_top_app(a->is_top_app),
|
||||
pkg_data_info_list(a->pkg_data_info_list),
|
||||
whitelisted_data_info_list(a->whitelisted_data_info_list),
|
||||
mount_data_dirs(a->mount_data_dirs), mount_storage_dirs(a->mount_storage_dirs) {}
|
||||
};
|
||||
|
||||
struct ServerSpecializeArgs_v1 {
|
||||
jint &uid;
|
||||
jint &gid;
|
||||
jintArray &gids;
|
||||
jint &runtime_flags;
|
||||
jlong &permitted_capabilities;
|
||||
jlong &effective_capabilities;
|
||||
|
||||
ServerSpecializeArgs_v1(
|
||||
jint &uid, jint &gid, jintArray &gids, jint &runtime_flags,
|
||||
jlong &permitted_capabilities, jlong &effective_capabilities) :
|
||||
uid(uid), gid(gid), gids(gids), runtime_flags(runtime_flags),
|
||||
permitted_capabilities(permitted_capabilities),
|
||||
effective_capabilities(effective_capabilities) {}
|
||||
};
|
||||
|
||||
struct module_abi_v1 {
|
||||
long api_version;
|
||||
void *impl;
|
||||
void (*preAppSpecialize)(void *, void *);
|
||||
void (*postAppSpecialize)(void *, const void *);
|
||||
void (*preServerSpecialize)(void *, void *);
|
||||
void (*postServerSpecialize)(void *, const void *);
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT,
|
||||
PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST,
|
||||
|
||||
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_FIRST_STARTED = (1u << 31),
|
||||
|
||||
PRIVATE_MASK = PROCESS_IS_FIRST_STARTED
|
||||
};
|
||||
|
||||
struct api_abi_base {
|
||||
ZygiskModule *impl;
|
||||
bool (*registerModule)(ApiTable *, long *);
|
||||
};
|
||||
|
||||
struct api_abi_v1 : public api_abi_base {
|
||||
/* 0 */ void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
||||
/* 1 */ void (*pltHookRegister)(const char *, const char *, void *, void **);
|
||||
/* 2 */ void (*pltHookExclude)(const char *, const char *);
|
||||
/* 3 */ bool (*pltHookCommit)();
|
||||
/* 4 */ int (*connectCompanion)(ZygiskModule *);
|
||||
/* 5 */ void (*setOption)(ZygiskModule *, zygisk::Option);
|
||||
};
|
||||
|
||||
struct api_abi_v2 : public api_abi_v1 {
|
||||
/* 6 */ int (*getModuleDir)(ZygiskModule *);
|
||||
/* 7 */ uint32_t (*getFlags)(ZygiskModule *);
|
||||
};
|
||||
|
||||
struct api_abi_v4 : public api_abi_base {
|
||||
/* 0 */ void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
||||
/* 1 */ void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **);
|
||||
/* 2 */ bool (*exemptFd)(int);
|
||||
/* 3 */ bool (*pltHookCommit)();
|
||||
/* 4 */ int (*connectCompanion)(ZygiskModule *);
|
||||
/* 5 */ void (*setOption)(ZygiskModule *, zygisk::Option);
|
||||
/* 6 */ int (*getModuleDir)(ZygiskModule *);
|
||||
/* 7 */ uint32_t (*getFlags)(ZygiskModule *);
|
||||
};
|
||||
|
||||
union ApiTable {
|
||||
api_abi_base base;
|
||||
api_abi_v1 v1;
|
||||
api_abi_v2 v2;
|
||||
api_abi_v4 v4;
|
||||
};
|
||||
|
||||
#define call_app(method) \
|
||||
switch (*mod.api_version) { \
|
||||
case 1: \
|
||||
case 2: { \
|
||||
AppSpecializeArgs_v1 a(args); \
|
||||
mod.v1->method(mod.v1->impl, &a); \
|
||||
break; \
|
||||
} \
|
||||
case 3: \
|
||||
case 4: \
|
||||
case 5: \
|
||||
mod.v1->method(mod.v1->impl, args);\
|
||||
break; \
|
||||
}
|
||||
|
||||
struct ZygiskModule {
|
||||
|
||||
void onLoad(void *env) {
|
||||
entry.fn(&api, env);
|
||||
}
|
||||
void preAppSpecialize(AppSpecializeArgs_v5 *args) const {
|
||||
call_app(preAppSpecialize)
|
||||
}
|
||||
void postAppSpecialize(const AppSpecializeArgs_v5 *args) const {
|
||||
call_app(postAppSpecialize)
|
||||
}
|
||||
void preServerSpecialize(ServerSpecializeArgs_v1 *args) const {
|
||||
mod.v1->preServerSpecialize(mod.v1->impl, args);
|
||||
}
|
||||
void postServerSpecialize(const ServerSpecializeArgs_v1 *args) const {
|
||||
mod.v1->postServerSpecialize(mod.v1->impl, args);
|
||||
}
|
||||
|
||||
bool valid() const;
|
||||
int connectCompanion() const;
|
||||
int getModuleDir() const;
|
||||
void setOption(zygisk::Option opt);
|
||||
static uint32_t getFlags();
|
||||
bool tryUnload() const { return unload && dlclose(handle) == 0; };
|
||||
void clearApi() { memset(&api, 0, sizeof(api)); }
|
||||
int getId() const { return id; }
|
||||
|
||||
ZygiskModule(int id, void *handle, void *entry);
|
||||
|
||||
static bool RegisterModuleImpl(ApiTable *api, long *module);
|
||||
|
||||
private:
|
||||
const int id;
|
||||
bool unload = false;
|
||||
|
||||
void * const handle;
|
||||
union {
|
||||
void * const ptr;
|
||||
void (* const fn)(void *, void *);
|
||||
} entry;
|
||||
|
||||
ApiTable api;
|
||||
|
||||
union {
|
||||
long *api_version;
|
||||
module_abi_v1 *v1;
|
||||
} mod;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -1,28 +1,30 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.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 void (*purge_unused_memory)(void) = NULL;
|
||||
struct link_map *r_debug_tail = NULL;
|
||||
|
||||
static inline const char *get_path(SoInfo *self) {
|
||||
if (get_realpath_sym)
|
||||
@@ -35,11 +37,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 +46,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 +73,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 +88,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,26 +99,14 @@ 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");
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -149,30 +118,63 @@ static bool solist_init() {
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("%p is find_containing_library", (void *)find_containing_library);
|
||||
|
||||
purge_unused_memory = (void (*)())getSymbAddress(linker, "__dl__Z19purge_unused_memoryv");
|
||||
if (purge_unused_memory == NULL) {
|
||||
LOGE("Failed to find purge_unused_memory __dl__Z19purge_unused_memoryv");
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("%p is purge_unused_memory", (void *)purge_unused_memory);
|
||||
|
||||
r_debug_tail = (struct link_map *)getSymbValueByPrefix(linker, "__dl__ZL12r_debug_tail");
|
||||
if (r_debug_tail == NULL) {
|
||||
LOGE("Failed to find r_debug_tail __dl__ZL10r_debug_tail");
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
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 +185,144 @@ static bool solist_init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solist_drop_so_path(const char *target_path) {
|
||||
if (solist == NULL && !solist_init()) {
|
||||
/* INFO: This is an AOSP function to remove a link map from
|
||||
the link map list.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker_gdb_support.cpp#63
|
||||
*/
|
||||
static void remove_link_map_from_debug_map(struct link_map *map) {
|
||||
if (r_debug_tail == map) {
|
||||
r_debug_tail = map->l_prev;
|
||||
}
|
||||
|
||||
if (map->l_prev) {
|
||||
map->l_prev->l_next = map->l_next;
|
||||
}
|
||||
|
||||
if (map->l_next) {
|
||||
map->l_next->l_prev = map->l_prev;
|
||||
}
|
||||
}
|
||||
|
||||
static struct link_map *find_link_map(SoInfo *si) {
|
||||
const char *path = get_path(si);
|
||||
if (path == NULL) {
|
||||
LOGE("Failed to get path for SoInfo %p", (void *)si);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOGD("Searching for link_map for %s", path);
|
||||
|
||||
struct link_map *map = r_debug_tail;
|
||||
while (map) {
|
||||
/* INFO: l_name uses the same pointer as realpath function of SoInfo, allowing us
|
||||
to directly compare the pointers instead of the strings.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#283
|
||||
*/
|
||||
if (map->l_name && (uintptr_t)map->l_name == (uintptr_t)path) {
|
||||
LOGD("Found link_map for %s: %p", path, (void *)map);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
map = map->l_next;
|
||||
}
|
||||
|
||||
LOGE("Failed to find link_map for %s", path);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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, bool unload) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
purge_unused_memory();
|
||||
|
||||
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;
|
||||
}
|
||||
strncpy(path, get_path(found), sizeof(path) - 1);
|
||||
|
||||
/* INFO: This area is guarded. Must unprotect first. */
|
||||
pdg_unprotect();
|
||||
set_size(found, 0);
|
||||
if (unload) pdg_protect();
|
||||
|
||||
LOGD("Set size of %p to 0", (void *)found);
|
||||
|
||||
/* INFO: We know that as libzygisk.so our limits, but modules are arbitrary, so
|
||||
calling deconstructors might break them. To avoid that, we manually call
|
||||
the separated structures, that however won't clean all traces in soinfo,
|
||||
not for now, at least. */
|
||||
if (unload && dlclose((void *)found) == -1) {
|
||||
LOGE("Failed to dlclose so path for %s: %s", path, dlerror());
|
||||
|
||||
return false;
|
||||
} else if (!unload) {
|
||||
LOGD("Not unloading so path for %s, only dropping it", path);
|
||||
|
||||
/*
|
||||
INFO: If the link map is not removed from the list, it gets inconsistent, resulting
|
||||
in a loop when listing through it, which can be detected. To fix that, we
|
||||
can remove the map, like expected.
|
||||
|
||||
We cannot use the notify_gdb_of_unload function as it is static, and not available
|
||||
in all linker binaries.
|
||||
*/
|
||||
struct link_map *map = find_link_map(found);
|
||||
if (!map) {
|
||||
LOGE("Failed to find link map for %s", path);
|
||||
|
||||
pdg_protect();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
remove_link_map_from_debug_map(map);
|
||||
/* INFO: unregister_soinfo_tls cannot be used since module might use JNI which may
|
||||
require TLS, so we cannot remove it. */
|
||||
soinfo_free(found);
|
||||
|
||||
pdg_protect();
|
||||
}
|
||||
|
||||
LOGD("Successfully hidden soinfo traces for %s", path);
|
||||
|
||||
/* INFO: Avoid leaks by ensuring the freed places are munmapped */
|
||||
purge_unused_memory();
|
||||
|
||||
LOGD("Purged unused memory successfully");
|
||||
|
||||
/* 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 +334,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct SoInfo SoInfo;
|
||||
|
||||
struct SoInfo {
|
||||
char data[0];
|
||||
};
|
||||
typedef void SoInfo;
|
||||
|
||||
#define FuncType(name) void (*name)
|
||||
|
||||
@@ -28,14 +24,20 @@ struct pdg {
|
||||
libzygisk.so, so that it doesn't create gaps between current module info
|
||||
and the next (soinfo).
|
||||
|
||||
To do that, we use 2 functions: soinfo_free, and set_size, which will
|
||||
zero the region size, and then remove all traces of that library (libzygisk.so)
|
||||
which was previously loaded.
|
||||
To do that, we use 2 functions: set_size and dlclose, which will first zero
|
||||
zero the size that the linker believes the shared library is, and then dlclose.
|
||||
Because the size is 0, it won't munmap the library, allowing us to keep loaded while
|
||||
having all other traces removed.
|
||||
|
||||
For the case of modules, which are arbitrary, we won't call dlclose, as it could break
|
||||
the module. Instead of using dlclose, we separately call soinfo_free, which will free
|
||||
the soinfo structure. That will allow to keep the data initialized by constructors
|
||||
mmaped, hence properly dropping most traces without breaking the module.
|
||||
|
||||
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, bool unload);
|
||||
|
||||
/*
|
||||
INFO: When dlopen'ing a library, the system will increment 1 to a global
|
||||
|
||||
@@ -7,4 +7,4 @@ 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);
|
||||
extern "C" void send_seccomp_event();
|
||||
|
||||
@@ -77,11 +77,11 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (info.modules->modules_count != 0) {
|
||||
printf("Modules: %zu\n", info.modules->modules_count);
|
||||
if (info.modules.modules_count != 0) {
|
||||
printf("Modules: %zu\n", info.modules.modules_count);
|
||||
|
||||
for (size_t i = 0; i < info.modules->modules_count; i++) {
|
||||
printf(" - %s\n", info.modules->modules[i]);
|
||||
for (size_t i = 0; i < info.modules.modules_count; i++) {
|
||||
printf(" - %s\n", info.modules.modules[i]);
|
||||
}
|
||||
} else {
|
||||
printf("Modules: N/A\n");
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "misc.h"
|
||||
#include "socket_utils.h"
|
||||
|
||||
#include "monitor.h"
|
||||
|
||||
@@ -29,6 +30,15 @@ static bool update_status(const char *message);
|
||||
|
||||
char monitor_stop_reason[32];
|
||||
|
||||
struct environment_information {
|
||||
char *root_impl;
|
||||
char **modules;
|
||||
uint32_t modules_len;
|
||||
};
|
||||
|
||||
static struct environment_information environment_information64;
|
||||
static struct environment_information environment_information32;
|
||||
|
||||
enum ptracer_tracing_state {
|
||||
TRACING,
|
||||
STOPPING,
|
||||
@@ -66,6 +76,7 @@ struct rezygiskd_status status32 = {
|
||||
|
||||
int monitor_epoll_fd;
|
||||
bool monitor_events_running = true;
|
||||
typedef void (*monitor_event_callback_t)();
|
||||
|
||||
bool monitor_events_init() {
|
||||
monitor_epoll_fd = epoll_create(1);
|
||||
@@ -78,14 +89,9 @@ bool monitor_events_init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct monitor_event_cbs {
|
||||
void (*callback)();
|
||||
void (*stop_callback)();
|
||||
};
|
||||
|
||||
bool monitor_events_register_event(struct monitor_event_cbs *event_cbs, int fd, uint32_t events) {
|
||||
bool monitor_events_register_event(monitor_event_callback_t event_cb, int fd, uint32_t events) {
|
||||
struct epoll_event ev = {
|
||||
.data.ptr = event_cbs,
|
||||
.data.ptr = (void *)event_cb,
|
||||
.events = events
|
||||
};
|
||||
|
||||
@@ -116,15 +122,16 @@ void monitor_events_loop() {
|
||||
struct epoll_event events[2];
|
||||
while (monitor_events_running) {
|
||||
int nfds = epoll_wait(monitor_epoll_fd, events, 2, -1);
|
||||
if (nfds == -1) {
|
||||
if (errno != EINTR) PLOGE("epoll_wait");
|
||||
if (nfds == -1 && errno != EINTR) {
|
||||
PLOGE("epoll_wait");
|
||||
|
||||
continue;
|
||||
monitor_events_running = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
struct monitor_event_cbs *event_cbs = (struct monitor_event_cbs *)events[i].data.ptr;
|
||||
event_cbs->callback();
|
||||
((monitor_event_callback_t)events[i].data.ptr)();
|
||||
|
||||
if (!monitor_events_running) break;
|
||||
}
|
||||
@@ -132,11 +139,6 @@ void monitor_events_loop() {
|
||||
|
||||
if (monitor_epoll_fd >= 0) close(monitor_epoll_fd);
|
||||
monitor_epoll_fd = -1;
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(events) / sizeof(events[0])); i++) {
|
||||
struct monitor_event_cbs *event_cbs = (struct monitor_event_cbs *)events[i].data.ptr;
|
||||
event_cbs->stop_callback();
|
||||
}
|
||||
}
|
||||
|
||||
int monitor_sock_fd;
|
||||
@@ -166,57 +168,26 @@ bool rezygiskd_listener_init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct __attribute__((__packed__)) MsgHead {
|
||||
unsigned int cmd;
|
||||
int length;
|
||||
};
|
||||
|
||||
void rezygiskd_listener_callback() {
|
||||
while (1) {
|
||||
struct MsgHead msg = { 0 };
|
||||
|
||||
size_t nread;
|
||||
|
||||
again:
|
||||
nread = read(monitor_sock_fd, &msg, sizeof(msg));
|
||||
if ((int)nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) goto again;
|
||||
|
||||
uint8_t cmd;
|
||||
ssize_t nread = TEMP_FAILURE_RETRY(read(monitor_sock_fd, &cmd, sizeof(cmd)));
|
||||
if (nread == -1) {
|
||||
PLOGE("read socket");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
char *msg_data = NULL;
|
||||
|
||||
if (msg.length != 0) {
|
||||
msg_data = malloc(msg.length);
|
||||
if (!msg_data) {
|
||||
LOGE("malloc msg data failed");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
again_msg_data:
|
||||
nread = read(monitor_sock_fd, msg_data, msg.length);
|
||||
if ((int)nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) goto again_msg_data;
|
||||
|
||||
PLOGE("read socket");
|
||||
|
||||
free(msg_data);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch (msg.cmd) {
|
||||
switch (cmd) {
|
||||
case START: {
|
||||
if (tracing_state == STOPPING) tracing_state = TRACING;
|
||||
else if (tracing_state == STOPPED) {
|
||||
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
|
||||
if (tracing_state == STOPPING) {
|
||||
LOGI("Continue tracing init");
|
||||
|
||||
LOGI("start tracing init");
|
||||
tracing_state = TRACING;
|
||||
} else if (tracing_state == STOPPED) {
|
||||
LOGI("Start tracing init");
|
||||
|
||||
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
|
||||
|
||||
tracing_state = TRACING;
|
||||
}
|
||||
@@ -227,7 +198,7 @@ void rezygiskd_listener_callback() {
|
||||
}
|
||||
case STOP: {
|
||||
if (tracing_state == TRACING) {
|
||||
LOGI("stop tracing requested");
|
||||
LOGI("Stop tracing requested");
|
||||
|
||||
tracing_state = STOPPING;
|
||||
strcpy(monitor_stop_reason, "user requested");
|
||||
@@ -239,7 +210,7 @@ void rezygiskd_listener_callback() {
|
||||
break;
|
||||
}
|
||||
case EXIT: {
|
||||
LOGI("prepare for exit ...");
|
||||
LOGI("Prepare for exit ...");
|
||||
|
||||
tracing_state = EXITING;
|
||||
strcpy(monitor_stop_reason, "user requested");
|
||||
@@ -249,104 +220,166 @@ void rezygiskd_listener_callback() {
|
||||
|
||||
break;
|
||||
}
|
||||
case ZYGOTE64_INJECTED: {
|
||||
status64.zygote_injected = true;
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case ZYGOTE64_INJECTED:
|
||||
case ZYGOTE32_INJECTED: {
|
||||
status32.zygote_injected = true;
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON64_SET_INFO: {
|
||||
LOGD("received daemon64 info %s", msg_data);
|
||||
|
||||
/* Will only happen if somehow the daemon restarts */
|
||||
if (status64.daemon_info) {
|
||||
free(status64.daemon_info);
|
||||
status64.daemon_info = NULL;
|
||||
}
|
||||
|
||||
status64.daemon_info = (char *)malloc(msg.length);
|
||||
if (!status64.daemon_info) {
|
||||
PLOGE("malloc daemon64 info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status64.daemon_info, msg_data);
|
||||
LOGI("Received Zygote%s injected command", cmd == ZYGOTE64_INJECTED ? "64" : "32");
|
||||
|
||||
struct rezygiskd_status *status = cmd == ZYGOTE64_INJECTED ? &status64 : &status32;
|
||||
status->zygote_injected = true;
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON64_SET_INFO:
|
||||
case DAEMON32_SET_INFO: {
|
||||
LOGD("received daemon32 info %s", msg_data);
|
||||
LOGD("Received ReZygiskd%s info", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
if (status32.daemon_info) {
|
||||
free(status32.daemon_info);
|
||||
status32.daemon_info = NULL;
|
||||
}
|
||||
|
||||
status32.daemon_info = (char *)malloc(msg.length);
|
||||
if (!status32.daemon_info) {
|
||||
PLOGE("malloc daemon32 info");
|
||||
uint32_t root_impl_len;
|
||||
if (read_uint32_t(monitor_sock_fd, &root_impl_len) != sizeof(root_impl_len)) {
|
||||
LOGE("read ReZygiskd%s root impl len", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status32.daemon_info, msg_data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON64_SET_ERROR_INFO: {
|
||||
LOGD("received daemon64 error info %s", msg_data);
|
||||
|
||||
status64.daemon_running = false;
|
||||
|
||||
if (status64.daemon_error_info) {
|
||||
free(status64.daemon_error_info);
|
||||
status64.daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status64.daemon_error_info = (char *)malloc(msg.length);
|
||||
if (!status64.daemon_error_info) {
|
||||
PLOGE("malloc daemon64 error info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status64.daemon_error_info, msg_data);
|
||||
struct environment_information *environment_information = cmd == DAEMON64_SET_INFO ? &environment_information64 : &environment_information32;
|
||||
if (environment_information->root_impl) {
|
||||
LOGD("freeing old ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
free((void *)environment_information->root_impl);
|
||||
environment_information->root_impl = NULL;
|
||||
}
|
||||
|
||||
environment_information->root_impl = malloc(root_impl_len + 1);
|
||||
if (environment_information->root_impl == NULL) {
|
||||
PLOGE("malloc ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (read_loop(monitor_sock_fd, (void *)environment_information->root_impl, root_impl_len) != (ssize_t)root_impl_len) {
|
||||
LOGE("read ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
free((void *)environment_information->root_impl);
|
||||
environment_information->root_impl = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
environment_information->root_impl[root_impl_len] = '\0';
|
||||
LOGD("ReZygiskd%s root impl: %s", cmd == DAEMON64_SET_INFO ? "64" : "32", environment_information->root_impl);
|
||||
|
||||
if (read_uint32_t(monitor_sock_fd, &environment_information->modules_len) != sizeof(environment_information->modules_len)) {
|
||||
LOGE("read ReZygiskd%s modules len", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
free((void *)environment_information->root_impl);
|
||||
environment_information->root_impl = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (environment_information->modules) {
|
||||
LOGD("freeing old ReZygiskd%s modules", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
for (size_t i = 0; i < environment_information->modules_len; i++) {
|
||||
free((void *)environment_information->modules[i]);
|
||||
}
|
||||
|
||||
free((void *)environment_information->modules);
|
||||
environment_information->modules = NULL;
|
||||
}
|
||||
|
||||
environment_information->modules = malloc(environment_information->modules_len * sizeof(char *));
|
||||
if (environment_information->modules == NULL) {
|
||||
PLOGE("malloc ReZygiskd%s modules", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
free((void *)environment_information->root_impl);
|
||||
environment_information->root_impl = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < environment_information->modules_len; i++) {
|
||||
uint32_t module_name_len;
|
||||
if (read_uint32_t(monitor_sock_fd, &module_name_len) != sizeof(module_name_len)) {
|
||||
LOGE("read ReZygiskd%s module name len", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
goto rezygiskd64_set_info_modules_cleanup;
|
||||
}
|
||||
|
||||
environment_information->modules[i] = malloc(module_name_len + 1);
|
||||
if (environment_information->modules[i] == NULL) {
|
||||
PLOGE("malloc ReZygiskd%s module name", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
goto rezygiskd64_set_info_modules_cleanup;
|
||||
}
|
||||
|
||||
if (read_loop(monitor_sock_fd, (void *)environment_information->modules[i], module_name_len) != (ssize_t)module_name_len) {
|
||||
LOGE("read ReZygiskd%s module name", cmd == DAEMON64_SET_INFO ? "64" : "32");
|
||||
|
||||
goto rezygiskd64_set_info_modules_cleanup;
|
||||
}
|
||||
|
||||
environment_information->modules[i][module_name_len] = '\0';
|
||||
LOGD("ReZygiskd%s module %zu: %s", cmd == DAEMON64_SET_INFO ? "64" : "32", i, environment_information->modules[i]);
|
||||
|
||||
continue;
|
||||
|
||||
rezygiskd64_set_info_modules_cleanup:
|
||||
free((void *)environment_information->root_impl);
|
||||
environment_information->root_impl = NULL;
|
||||
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
free((void *)environment_information->modules[j]);
|
||||
}
|
||||
|
||||
free((void *)environment_information->modules);
|
||||
environment_information->modules = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON64_SET_ERROR_INFO:
|
||||
case DAEMON32_SET_ERROR_INFO: {
|
||||
LOGD("received daemon32 error info %s", msg_data);
|
||||
LOGD("Received ReZygiskd%s error info", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32");
|
||||
|
||||
status32.daemon_running = false;
|
||||
|
||||
if (status32.daemon_error_info) {
|
||||
free(status32.daemon_error_info);
|
||||
status32.daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status32.daemon_error_info = (char *)malloc(msg.length);
|
||||
if (!status32.daemon_error_info) {
|
||||
PLOGE("malloc daemon32 error info");
|
||||
uint32_t error_info_len;
|
||||
if (read_uint32_t(monitor_sock_fd, &error_info_len) != sizeof(error_info_len)) {
|
||||
LOGE("read ReZygiskd%s error info len", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status32.daemon_error_info, msg_data);
|
||||
struct rezygiskd_status *status = cmd == DAEMON64_SET_ERROR_INFO ? &status64 : &status32;
|
||||
if (status->daemon_error_info) {
|
||||
LOGD("freeing old ReZygiskd%s error info", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32");
|
||||
|
||||
free(status->daemon_error_info);
|
||||
status->daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status->daemon_error_info = malloc(error_info_len + 1);
|
||||
if (status->daemon_error_info == NULL) {
|
||||
PLOGE("malloc ReZygiskd%s error info", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (read_loop(monitor_sock_fd, status->daemon_error_info, error_info_len) != (ssize_t)error_info_len) {
|
||||
LOGE("read ReZygiskd%s error info", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32");
|
||||
|
||||
free(status->daemon_error_info);
|
||||
status->daemon_error_info = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
status->daemon_error_info[error_info_len] = '\0';
|
||||
LOGD("ReZygiskd%s error info: %s", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32", status->daemon_error_info);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
@@ -363,8 +396,6 @@ void rezygiskd_listener_callback() {
|
||||
}
|
||||
}
|
||||
|
||||
if (msg_data) free(msg_data);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -448,17 +479,14 @@ static bool ensure_daemon_created(bool is_64bit) {
|
||||
status##abi.daemon_running = false; \
|
||||
\
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
status##abi.daemon_error_info = (char *)malloc(strlen(status_str) + 1); \
|
||||
status##abi.daemon_error_info = strdup(status_str); \
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
LOGE("malloc daemon" #abi " error info failed"); \
|
||||
\
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
memcpy(status##abi.daemon_error_info, status_str, strlen(status_str) + 1); \
|
||||
} \
|
||||
\
|
||||
update_status(NULL); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
@@ -467,19 +495,20 @@ static bool ensure_daemon_created(bool is_64bit) {
|
||||
tracer = "./bin/zygisk-ptrace" # abi; \
|
||||
\
|
||||
if (should_stop_inject ## abi()) { \
|
||||
LOGW("zygote" # abi " restart too much times, stop injecting"); \
|
||||
LOGW("Zygote" # abi " restart too much times, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
memcpy(monitor_stop_reason, "zygote crashed", sizeof("zygote crashed")); \
|
||||
strcpy(monitor_stop_reason, "Zygote crashed"); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if (!ensure_daemon_created(is_64)) { \
|
||||
LOGW("daemon" #abi " not running, stop injecting"); \
|
||||
LOGW("ReZygiskd " #abi "-bit not running, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
memcpy(monitor_stop_reason, "daemon not running", sizeof("daemon not running")); \
|
||||
strcpy(monitor_stop_reason, "ReZygiskd not running"); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
@@ -677,8 +706,6 @@ void sigchld_listener_callback() {
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
|
||||
update_status(NULL);
|
||||
} else {
|
||||
char status_str[64];
|
||||
parse_status(sigchld_status, status_str, sizeof(status_str));
|
||||
@@ -718,27 +745,20 @@ static char post_section[1024];
|
||||
|
||||
#define WRITE_STATUS_ABI(suffix) \
|
||||
if (status ## suffix.supported) { \
|
||||
strcat(status_text, " zygote" # suffix ": "); \
|
||||
if (tracing_state != TRACING) strcat(status_text, "❓ unknown, "); \
|
||||
else if (status ## suffix.zygote_injected) strcat(status_text, "😋 injected, "); \
|
||||
else strcat(status_text, "❌ not injected, "); \
|
||||
strcat(status_text, ", ReZygisk " # suffix "-bit: "); \
|
||||
\
|
||||
strcat(status_text, "daemon" # suffix ": "); \
|
||||
if (status ## suffix.daemon_running) { \
|
||||
strcat(status_text, "😋 running "); \
|
||||
if (tracing_state != TRACING) strcat(status_text, "❌"); \
|
||||
else if (status ## suffix.zygote_injected && status ## suffix.daemon_running) \
|
||||
strcat(status_text, "✅"); \
|
||||
else strcat(status_text, "⚠️"); \
|
||||
\
|
||||
if (status ## suffix.daemon_info != NULL) { \
|
||||
strcat(status_text, "("); \
|
||||
strcat(status_text, status ## suffix.daemon_info); \
|
||||
strcat(status_text, ")"); \
|
||||
} \
|
||||
} else { \
|
||||
strcat(status_text, "❌ crashed "); \
|
||||
\
|
||||
if (status ## suffix.daemon_error_info != NULL) { \
|
||||
strcat(status_text, "("); \
|
||||
if (!status ## suffix.daemon_running) { \
|
||||
if (status ## suffix.daemon_error_info) { \
|
||||
strcat(status_text, "(ReZygiskd: "); \
|
||||
strcat(status_text, status ## suffix.daemon_error_info); \
|
||||
strcat(status_text, ")"); \
|
||||
} else { \
|
||||
strcat(status_text, "(ReZygiskd: not running)"); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
@@ -758,41 +778,106 @@ static bool update_status(const char *message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char status_text[1024] = "monitor: ";
|
||||
|
||||
char status_text[256] = "Monitor: ";
|
||||
switch (tracing_state) {
|
||||
case TRACING: {
|
||||
strcat(status_text, "😋 tracing");
|
||||
strcat(status_text, "✅");
|
||||
|
||||
break;
|
||||
}
|
||||
case STOPPING: [[fallthrough]];
|
||||
case STOPPED: {
|
||||
strcat(status_text, "❌ stopped");
|
||||
strcat(status_text, "⛔");
|
||||
|
||||
break;
|
||||
}
|
||||
case EXITING: {
|
||||
strcat(status_text, "❌ exited");
|
||||
strcat(status_text, "❌");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tracing_state != TRACING && monitor_stop_reason[0] != '\0') {
|
||||
strcat(status_text, " (");
|
||||
strcat(status_text, monitor_stop_reason);
|
||||
strcat(status_text, ")");
|
||||
}
|
||||
strcat(status_text, ",");
|
||||
|
||||
WRITE_STATUS_ABI(64)
|
||||
WRITE_STATUS_ABI(32)
|
||||
|
||||
fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section);
|
||||
|
||||
fclose(prop);
|
||||
|
||||
if (environment_information64.root_impl || environment_information32.root_impl) {
|
||||
FILE *json = fopen("/data/adb/rezygisk/state.json", "w");
|
||||
if (json == NULL) {
|
||||
PLOGE("failed to open state.json");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(json, "{\n");
|
||||
fprintf(json, " \"root\": \"%s\",\n", environment_information64.root_impl ? environment_information64.root_impl : environment_information32.root_impl);
|
||||
|
||||
fprintf(json, " \"monitor\": {\n");
|
||||
fprintf(json, " \"state\": \"%d\"", tracing_state);
|
||||
if (monitor_stop_reason[0] != '\0') fprintf(json, ",\n \"reason\": \"%s\",\n", monitor_stop_reason);
|
||||
else fprintf(json, "\n");
|
||||
fprintf(json, " },\n");
|
||||
|
||||
|
||||
fprintf(json, " \"rezygiskd\": {\n");
|
||||
if (status64.supported) {
|
||||
fprintf(json, " \"64\": {\n");
|
||||
fprintf(json, " \"state\": %d,\n", status64.daemon_running);
|
||||
if (status64.daemon_error_info) fprintf(json, " \"reason\": \"%s\",\n", status64.daemon_error_info);
|
||||
fprintf(json, " \"modules\": [");
|
||||
|
||||
if (environment_information64.modules) for (uint32_t i = 0; i < environment_information64.modules_len; i++) {
|
||||
if (i > 0) fprintf(json, ", ");
|
||||
fprintf(json, "\"%s\"", environment_information64.modules[i]);
|
||||
}
|
||||
|
||||
fprintf(json, "]\n");
|
||||
fprintf(json, " }");
|
||||
if (status32.supported) fprintf(json, ",\n");
|
||||
else fprintf(json, "\n");
|
||||
}
|
||||
|
||||
if (status32.supported) {
|
||||
fprintf(json, " \"32\": {\n");
|
||||
fprintf(json, " \"state\": %d,\n", status32.daemon_running);
|
||||
if (status32.daemon_error_info) fprintf(json, " \"reason\": \"%s\",\n", status32.daemon_error_info);
|
||||
fprintf(json, " \"modules\": [");
|
||||
|
||||
if (environment_information32.modules) for (uint32_t i = 0; i < environment_information32.modules_len; i++) {
|
||||
if (i > 0) fprintf(json, ", ");
|
||||
fprintf(json, "\"%s\"", environment_information32.modules[i]);
|
||||
}
|
||||
|
||||
fprintf(json, "]\n");
|
||||
fprintf(json, " }\n");
|
||||
}
|
||||
|
||||
fprintf(json, " },\n");
|
||||
|
||||
fprintf(json, " \"zygote\": {\n");
|
||||
if (status64.supported) {
|
||||
fprintf(json, " \"64\": %d", status64.zygote_injected);
|
||||
if (status32.zygote_injected) fprintf(json, ",\n");
|
||||
else fprintf(json, "\n");
|
||||
}
|
||||
if (status32.zygote_injected) {
|
||||
fprintf(json, " \"32\": %d\n", status32.zygote_injected);
|
||||
}
|
||||
fprintf(json, " }\n");
|
||||
fprintf(json, "}\n");
|
||||
|
||||
fclose(json);
|
||||
} else {
|
||||
if (remove("/data/adb/rezygisk/state.json") == -1) {
|
||||
PLOGE("failed to remove state.json");
|
||||
}
|
||||
}
|
||||
|
||||
LOGI("status updated: %s", status_text);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -837,7 +922,7 @@ static bool prepare_environment() {
|
||||
return false;
|
||||
}
|
||||
|
||||
return update_status(NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_monitor() {
|
||||
@@ -847,10 +932,6 @@ void init_monitor() {
|
||||
|
||||
monitor_events_init();
|
||||
|
||||
struct monitor_event_cbs listener_cbs = {
|
||||
.callback = rezygiskd_listener_callback,
|
||||
.stop_callback = rezygiskd_listener_stop
|
||||
};
|
||||
if (!rezygiskd_listener_init()) {
|
||||
LOGE("failed to create socket");
|
||||
|
||||
@@ -859,12 +940,8 @@ void init_monitor() {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
monitor_events_register_event(&listener_cbs, monitor_sock_fd, EPOLLIN | EPOLLET);
|
||||
monitor_events_register_event(rezygiskd_listener_callback, monitor_sock_fd, EPOLLIN | EPOLLET);
|
||||
|
||||
struct monitor_event_cbs sigchld_cbs = {
|
||||
.callback = sigchld_listener_callback,
|
||||
.stop_callback = sigchld_listener_stop
|
||||
};
|
||||
if (sigchld_listener_init() == false) {
|
||||
LOGE("failed to create signalfd");
|
||||
|
||||
@@ -874,16 +951,37 @@ void init_monitor() {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
monitor_events_register_event(&sigchld_cbs, sigchld_signal_fd, EPOLLIN | EPOLLET);
|
||||
monitor_events_register_event(sigchld_listener_callback, sigchld_signal_fd, EPOLLIN | EPOLLET);
|
||||
|
||||
monitor_events_loop();
|
||||
|
||||
/* INFO: Once it stops the loop, we cannot access the epool data, so we
|
||||
either manually call the stops or save to a structure. */
|
||||
rezygiskd_listener_stop();
|
||||
sigchld_listener_stop();
|
||||
|
||||
if (status64.daemon_info) free(status64.daemon_info);
|
||||
if (status64.daemon_error_info) free(status64.daemon_error_info);
|
||||
if (status32.daemon_info) free(status32.daemon_info);
|
||||
if (status32.daemon_error_info) free(status32.daemon_error_info);
|
||||
|
||||
LOGI("exit");
|
||||
if (environment_information64.root_impl) free((void *)environment_information64.root_impl);
|
||||
if (environment_information64.modules) {
|
||||
for (uint32_t i = 0; i < environment_information64.modules_len; i++) {
|
||||
free((void *)environment_information64.modules[i]);
|
||||
}
|
||||
free((void *)environment_information64.modules);
|
||||
}
|
||||
|
||||
if (environment_information32.root_impl) free((void *)environment_information32.root_impl);
|
||||
if (environment_information32.modules) {
|
||||
for (uint32_t i = 0; i < environment_information32.modules_len; i++) {
|
||||
free((void *)environment_information32.modules[i]);
|
||||
}
|
||||
free((void *)environment_information32.modules);
|
||||
}
|
||||
|
||||
LOGI("Terminating ReZygisk monitor");
|
||||
}
|
||||
|
||||
int send_control_command(enum rezygiskd_command cmd) {
|
||||
@@ -899,9 +997,10 @@ int send_control_command(enum rezygiskd_command cmd) {
|
||||
|
||||
socklen_t socklen = sizeof(sa_family_t) + sun_path_len;
|
||||
|
||||
ssize_t nsend = sendto(sockfd, (void *)&cmd, sizeof(cmd), 0, (struct sockaddr *)&addr, socklen);
|
||||
uint8_t cmd_op = cmd;
|
||||
ssize_t nsend = sendto(sockfd, (void *)&cmd_op, sizeof(cmd_op), 0, (struct sockaddr *)&addr, socklen);
|
||||
|
||||
close(sockfd);
|
||||
|
||||
return nsend != sizeof(cmd) ? -1 : 0;
|
||||
return nsend != sizeof(cmd_op) ? -1 : 0;
|
||||
}
|
||||
|
||||
@@ -159,17 +159,47 @@ 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 < map->size; i++) {
|
||||
if (map->maps[i].path == NULL) continue;
|
||||
|
||||
const char *filename = position_after(map->maps[i].path, '/');
|
||||
|
||||
if (!libdl_path && strcmp(filename, "libdl.so") == 0) {
|
||||
libdl_path = map->maps[i].path;
|
||||
|
||||
LOGD("found libdl.so at %s", libdl_path);
|
||||
|
||||
/* INFO: If we had found libc.so too, no need to continue searching */
|
||||
if (libc_path) break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!libc_path && strcmp(filename, "libc.so") == 0) {
|
||||
libc_path = map->maps[i].path;
|
||||
|
||||
LOGD("found libc.so at %s", libc_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");
|
||||
|
||||
#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 +228,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");
|
||||
|
||||
#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 +255,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 +279,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 +290,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 +303,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");
|
||||
|
||||
#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");
|
||||
|
||||
@@ -331,10 +368,8 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
/* call injector entry(start_addr, block_size, path) */
|
||||
args[0] = (uintptr_t)start_addr;
|
||||
args[1] = block_size;
|
||||
str = push_string(pid, ®s, rezygiskd_get_path());
|
||||
args[2] = (uintptr_t)str;
|
||||
|
||||
remote_call(pid, ®s, injector_entry, (uintptr_t)libc_return_addr, args, 3);
|
||||
remote_call(pid, ®s, injector_entry, (uintptr_t)libc_return_addr, args, 2);
|
||||
|
||||
free(args);
|
||||
|
||||
@@ -370,7 +405,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 +440,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 {
|
||||
|
||||
@@ -119,8 +119,8 @@ struct maps *parse_maps(const char *filename) {
|
||||
path_offset++;
|
||||
}
|
||||
|
||||
maps->maps = (struct map *)realloc(maps->maps, (i + 1) * sizeof(struct map));
|
||||
if (!maps->maps) {
|
||||
struct map *tmp_maps = (struct map *)realloc(maps->maps, (i + 1) * sizeof(struct map));
|
||||
if (!tmp_maps) {
|
||||
LOGE("Failed to allocate memory for maps->maps");
|
||||
|
||||
maps->size = i;
|
||||
@@ -130,6 +130,7 @@ struct maps *parse_maps(const char *filename) {
|
||||
|
||||
return NULL;
|
||||
}
|
||||
maps->maps = tmp_maps;
|
||||
|
||||
maps->maps[i].start = addr_start;
|
||||
maps->maps[i].end = addr_end;
|
||||
@@ -313,20 +314,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 +523,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 +559,21 @@ void wait_for_trace(int pid, int *status, int flags) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(*status)) {
|
||||
/* INFO: We'll fork there. This will signal SIGCHLD. We just ignore and continue
|
||||
to avoid blocking/not continuing. */
|
||||
if (WSTOPSIG(*status) == SIGCHLD) {
|
||||
LOGI("process %d stopped by SIGCHLD, continue", pid);
|
||||
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
|
||||
continue;
|
||||
} else 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);
|
||||
|
||||
@@ -112,7 +112,8 @@ mkdir "$MODPATH/webroot"
|
||||
ui_print "- Extracting webroot"
|
||||
unzip -o "$ZIPFILE" "webroot/*" -x "*.sha256" -d "$MODPATH"
|
||||
|
||||
CPU_ABIS=$(getprop ro.product.cpu.abilist)
|
||||
CPU_ABIS=$(getprop ro.system.product.cpu.abilist)
|
||||
CPU_ABIS=${CPU_ABIS:-$(getprop ro.product.cpu.abilist)}
|
||||
|
||||
SUPPORTS_32BIT=false
|
||||
SUPPORTS_64BIT=false
|
||||
|
||||
@@ -46,7 +46,8 @@ if [ -f $MODDIR/lib/libzygisk.so ];then
|
||||
chcon u:object_r:system_file:s0 $TMP_PATH/lib/libzygisk.so
|
||||
fi
|
||||
|
||||
CPU_ABIS=$(getprop ro.product.cpu.abilist)
|
||||
CPU_ABIS=$(getprop ro.system.product.cpu.abilist)
|
||||
CPU_ABIS=${CPU_ABIS:-$(getprop ro.product.cpu.abilist)}
|
||||
|
||||
if [[ "$CPU_ABIS" == *"arm64-v8a"* || "$CPU_ABIS" == *"x86_64"* ]]; then
|
||||
./bin/zygisk-ptrace64 monitor &
|
||||
|
||||
@@ -11,6 +11,7 @@ allow zygote su {lnk_file file} read
|
||||
allow zygote adb_data_file dir search
|
||||
allow zygote adb_data_file file *
|
||||
allow zygote proc file {read open}
|
||||
allow zygote nsfs file {read open}
|
||||
allow zygote zygote process execmem
|
||||
allow system_server system_server process execmem
|
||||
allow zygote tmpfs file *
|
||||
|
||||
@@ -91,23 +91,31 @@ async function getModuleNames(modules) {
|
||||
const unameCmd = await exec('/system/bin/uname -r')
|
||||
if (unameCmd.errno !== 0) return setError('WebUI', unameCmd.stderr)
|
||||
|
||||
document.getElementById('kernel_version_div').innerHTML = unameCmd.stdout
|
||||
console.log('[rezygisk.js] Kernel version: ', unameCmd.stdout)
|
||||
document.getElementById('kernel_version_div').innerHTML = unameCmd.stdout.trim()
|
||||
console.log('[rezygisk.js] Kernel version: ', unameCmd.stdout.trim())
|
||||
|
||||
const catCmd = await exec('/system/bin/cat /data/adb/rezygisk/module.prop')
|
||||
console.log(`[rezygisk.js] ReZygisk module infomation:\n${catCmd.stdout}`)
|
||||
|
||||
let expectedWorking = 0
|
||||
let actuallyWorking = 0
|
||||
if (catCmd.errno !== 0) {
|
||||
console.error('[rezygisk.js] Failed to retrieve ReZygisk module information:', catCmd.stderr)
|
||||
|
||||
const ReZygiskInfo = {
|
||||
rootImpl: null,
|
||||
monitor: null,
|
||||
zygotes: [],
|
||||
daemons: []
|
||||
rezygisk_state.innerHTML = translations.page.home.status.notWorking
|
||||
rezygisk_icon_state.innerHTML = '<img class="dimc" src="assets/cross.svg">'
|
||||
|
||||
rootCss.style.setProperty('--bright', '#766000')
|
||||
|
||||
/* INFO: Hide zygote divs */
|
||||
zygote_divs.forEach((zygote_div) => {
|
||||
zygote_div.style.display = 'none'
|
||||
})
|
||||
|
||||
loading_screen.style.display = 'none'
|
||||
bottom_nav.style.display = 'flex'
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (catCmd.errno === 0) {
|
||||
/* INFO: Just ensure that they won't appear unless there's info */
|
||||
zygote_divs.forEach((zygote_div) => {
|
||||
zygote_div.style.display = 'none'
|
||||
@@ -115,78 +123,75 @@ async function getModuleNames(modules) {
|
||||
|
||||
version.innerHTML = catCmd.stdout.split('\n').find((line) => line.startsWith('version=')).substring('version='.length).trim()
|
||||
|
||||
let moduleInfo = catCmd.stdout.split('\n').find((line) => line.startsWith('description=')).substring('description='.length).split('[')[1].split(']')[0]
|
||||
const stateCmd = await exec('/system/bin/cat /data/adb/rezygisk/state.json')
|
||||
if (stateCmd.errno !== 0) {
|
||||
console.error('[rezygisk.js] Failed to retrieve ReZygisk state information:', stateCmd.stderr)
|
||||
|
||||
const daemonModules = []
|
||||
moduleInfo.match(/\(([^)]+)\)/g).forEach((area) => {
|
||||
moduleInfo = moduleInfo.replace(area, ',')
|
||||
rezygisk_state.innerHTML = translations.page.home.status.notWorking
|
||||
rezygisk_icon_state.innerHTML = '<img class="dimc" src="assets/cross.svg">'
|
||||
|
||||
const info = area.substring(1, area.length - 1).split(', ')
|
||||
if (info.length === 1) return; /* INFO: undefined as object */
|
||||
rootCss.style.setProperty('--bright', '#766000')
|
||||
|
||||
const rootImpl = info[0].substring('Root: '.length)
|
||||
|
||||
info[1] = info[1].substring('Modules: '.length)
|
||||
const modules = info.slice(1, info.length)
|
||||
|
||||
ReZygiskInfo.rootImpl = rootImpl
|
||||
if (modules[0] !== 'None') daemonModules.push(modules)
|
||||
/* INFO: Hide zygote divs */
|
||||
zygote_divs.forEach((zygote_div) => {
|
||||
zygote_div.style.display = 'none'
|
||||
})
|
||||
|
||||
const infoArea = moduleInfo.split(', ')
|
||||
infoArea.forEach((info) => {
|
||||
if (info.startsWith('monitor:')) {
|
||||
ReZygiskInfo.monitor = info.substring('monitor: X '.length).trim()
|
||||
loading_screen.style.display = 'none'
|
||||
bottom_nav.style.display = 'flex'
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.startsWith('zygote')) {
|
||||
ReZygiskInfo.zygotes.push({
|
||||
bits: info.substring('zygote'.length, 'zygote'.length + 'XX'.length),
|
||||
state: info.substring('zygoteXX: X '.length).trim()
|
||||
})
|
||||
}
|
||||
const ReZygiskState = JSON.parse(stateCmd.stdout)
|
||||
|
||||
if (info.startsWith('daemon')) {
|
||||
ReZygiskInfo.daemons.push({
|
||||
bits: info.substring('daemon'.length, 'daemon'.length + 'XX'.length),
|
||||
state: info.substring('daemonXX: X '.length).trim(),
|
||||
modules: daemonModules[ReZygiskInfo.daemons.length] || []
|
||||
})
|
||||
}
|
||||
})
|
||||
root_impl.innerHTML = ReZygiskState.root
|
||||
|
||||
switch (ReZygiskInfo.monitor) {
|
||||
case 'tracing': monitor_status.innerHTML = translations.page.actions.status.tracing; break;
|
||||
case 'stopping': monitor_status.innerHTML = translations.page.actions.status.stopping; break;
|
||||
case 'stopped': monitor_status.innerHTML = translations.page.actions.status.stopped; break;
|
||||
case 'exiting': monitor_status.innerHTML = translations.page.actions.status.exiting; break;
|
||||
switch (ReZygiskState.monitor.state) {
|
||||
case 0: monitor_status.innerHTML = translations.page.actions.status.tracing; break;
|
||||
case 1: monitor_status.innerHTML = translations.page.actions.status.stopping; break;
|
||||
case 2: monitor_status.innerHTML = translations.page.actions.status.stopped; break;
|
||||
case 3: monitor_status.innerHTML = translations.page.actions.status.exiting; break;
|
||||
default: monitor_status.innerHTML = translations.page.actions.status.unknown;
|
||||
}
|
||||
|
||||
expectedWorking = ReZygiskInfo.zygotes.length
|
||||
const expectedWorking = (ReZygiskState.zygote['64'] !== undefined ? 1 : 0) + (ReZygiskState.zygote['32'] !== undefined ? 1 : 0)
|
||||
let actuallyWorking = 0
|
||||
|
||||
for (let i = 0; i < ReZygiskInfo.zygotes.length; i++) {
|
||||
const zygote = ReZygiskInfo.zygotes[i]
|
||||
/* INFO: Not used ATM */
|
||||
/* const daemon = ReZygiskInfo.daemons[i] */
|
||||
if (ReZygiskState.zygote['64'] !== undefined) {
|
||||
const zygote64 = ReZygiskState.zygote['64']
|
||||
|
||||
const zygoteDiv = zygote_divs[zygote.bits === '64' ? 0 : 1]
|
||||
const zygoteStatusDiv = zygote_status_divs[zygote.bits === '64' ? 0 : 1]
|
||||
zygote_divs[0].style.display = 'block'
|
||||
|
||||
zygoteDiv.style.display = 'block'
|
||||
|
||||
switch (zygote.state) {
|
||||
case 'injected': {
|
||||
zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.injected;
|
||||
switch (zygote64) {
|
||||
case 1: {
|
||||
zygote_status_divs[0].innerHTML = translations.page.home.info.zygote.injected
|
||||
|
||||
actuallyWorking++
|
||||
|
||||
break;
|
||||
break
|
||||
}
|
||||
case 'not injected': zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.notInjected; break;
|
||||
default: zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.unknown;
|
||||
case 0: zygote_status_divs[0].innerHTML = translations.page.home.info.zygote.notInjected; break
|
||||
default: zygote_status_divs[0].innerHTML = translations.page.home.info.zygote.unknown
|
||||
}
|
||||
}
|
||||
|
||||
if (ReZygiskState.zygote['32'] !== undefined) {
|
||||
const zygote32 = ReZygiskState.zygote['32']
|
||||
|
||||
zygote_divs[1].style.display = 'block'
|
||||
|
||||
switch (zygote32) {
|
||||
case 1: {
|
||||
zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.injected
|
||||
|
||||
actuallyWorking++
|
||||
|
||||
break
|
||||
}
|
||||
case 0: zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.notInjected; break
|
||||
default: zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.unknown
|
||||
}
|
||||
}
|
||||
|
||||
if (expectedWorking === 0 || actuallyWorking === 0) {
|
||||
@@ -203,24 +208,24 @@ async function getModuleNames(modules) {
|
||||
rezygisk_icon_state.innerHTML = '<img class="brightc" src="assets/warn.svg">'
|
||||
}
|
||||
|
||||
if (ReZygiskInfo.rootImpl)
|
||||
root_impl.innerHTML = ReZygiskInfo.rootImpl
|
||||
|
||||
const all_modules = []
|
||||
ReZygiskInfo.daemons.forEach((daemon) => {
|
||||
Object.keys(ReZygiskState.rezygiskd).forEach((daemon_bit) => {
|
||||
const daemon = ReZygiskState.rezygiskd[daemon_bit]
|
||||
|
||||
if (daemon.modules && daemon.modules.length > 0) {
|
||||
daemon.modules.forEach((module_id) => {
|
||||
const module = all_modules.find((mod) => mod.id === module_id)
|
||||
|
||||
if (module) {
|
||||
module.bitsUsed.push(daemon.bits)
|
||||
module.bitsUsed.push(daemon_bit)
|
||||
} else {
|
||||
all_modules.push({
|
||||
id: module_id,
|
||||
name: null,
|
||||
bitsUsed: [ daemon.bits ]
|
||||
bitsUsed: [ daemon_bit ]
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (all_modules.length !== 0) {
|
||||
@@ -251,7 +256,6 @@ async function getModuleNames(modules) {
|
||||
loading_screen.style.display = 'none'
|
||||
bottom_nav.style.display = 'flex'
|
||||
|
||||
|
||||
const start_time = Number(localStorage.getItem('/system/boot-time'))
|
||||
console.log('[rezygisk.js] boot time: ', Date.now() - start_time, 'ms')
|
||||
localStorage.removeItem('/system/boot_time')
|
||||
|
||||
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) {
|
||||
@@ -157,7 +153,14 @@ void companion_entry(int fd) {
|
||||
LOGI("New companion request.\n - Module name: %s\n - Client fd: %d\n", name, client_fd);
|
||||
|
||||
ret = write_uint8_t(client_fd, 1);
|
||||
ASSURE_SIZE_WRITE("ZygiskdCompanion", "client_fd", ret, sizeof(uint8_t));
|
||||
if (ret != sizeof(uint8_t)) {
|
||||
LOGE("Failed to sent client_fd in ZygiskdCompanion: Expected %zu, got %zd\n", sizeof(uint8_t), ret);
|
||||
|
||||
free(args);
|
||||
close(client_fd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_t thread;
|
||||
if (pthread_create(&thread, NULL, entry_thread, (void *)args) == 0)
|
||||
|
||||
@@ -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;
|
||||
@@ -72,6 +73,14 @@ struct packages_config {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* WARNING: Dynamic memory based */
|
||||
bool _apatch_get_package_config(struct packages_config *restrict config) {
|
||||
config->configs = NULL;
|
||||
@@ -95,16 +104,18 @@ bool _apatch_get_package_config(struct packages_config *restrict config) {
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
config->configs = realloc(config->configs, (config->size + 1) * sizeof(struct package_config));
|
||||
if (config->configs == NULL) {
|
||||
struct package_config *tmp_configs = realloc(config->configs, (config->size + 1) * sizeof(struct package_config));
|
||||
if (tmp_configs == NULL) {
|
||||
LOGE("Failed to realloc APatch config struct: %s\n", strerror(errno));
|
||||
|
||||
_apatch_free_package_config(config);
|
||||
fclose(fp);
|
||||
|
||||
return false;
|
||||
}
|
||||
config->configs = tmp_configs;
|
||||
|
||||
strtok(line, ",");
|
||||
config->configs[config->size].process = strdup(strtok(line, ","));
|
||||
|
||||
char *exclude_str = strtok(NULL, ",");
|
||||
if (exclude_str == NULL) continue;
|
||||
@@ -127,17 +138,9 @@ bool _apatch_get_package_config(struct packages_config *restrict config) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void _apatch_free_package_config(struct packages_config *restrict config) {
|
||||
free(config->configs);
|
||||
}
|
||||
|
||||
bool apatch_uid_granted_root(uid_t uid) {
|
||||
struct packages_config config;
|
||||
if (!_apatch_get_package_config(&config)) {
|
||||
_apatch_free_package_config(&config);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!_apatch_get_package_config(&config)) return false;
|
||||
|
||||
for (size_t i = 0; i < config.size; i++) {
|
||||
if (config.configs[i].uid != uid) continue;
|
||||
@@ -155,13 +158,9 @@ 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);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!_apatch_get_package_config(&config)) return false;
|
||||
|
||||
for (size_t i = 0; i < config.size; i++) {
|
||||
if (config.configs[i].uid != uid) continue;
|
||||
@@ -174,6 +173,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) {
|
||||
@@ -73,8 +74,7 @@ void root_impls_setup(void) {
|
||||
}
|
||||
|
||||
void get_impl(struct root_impl *uimpl) {
|
||||
uimpl->impl = impl.impl;
|
||||
uimpl->variant = impl.variant;
|
||||
*uimpl = impl;
|
||||
}
|
||||
|
||||
bool uid_granted_root(uid_t uid) {
|
||||
@@ -100,7 +100,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,10 +19,18 @@
|
||||
#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 reply_ok = 0;
|
||||
|
||||
int version = 0;
|
||||
prctl((signed int)KERNEL_SU_OPTION, CMD_GET_VERSION, &version, 0, 0);
|
||||
prctl((signed int)KERNEL_SU_OPTION, CMD_GET_VERSION, &version, 0, &reply_ok);
|
||||
|
||||
if (version == 0) state->state = Abnormal;
|
||||
else if (version >= MIN_KSU_VERSION && version <= MAX_KSU_VERSION) {
|
||||
@@ -42,6 +50,25 @@ 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, NULL, &reply_ok);
|
||||
|
||||
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. */
|
||||
prctl((signed int)KERNEL_SU_OPTION, CMD_GET_MANAGER_UID, NULL, NULL, &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 +95,26 @@ 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) {
|
||||
int reply_ok = 0;
|
||||
|
||||
uid_t manager_uid = 0;
|
||||
prctl(KERNEL_SU_OPTION, CMD_GET_MANAGER_UID, &manager_uid, NULL, &reply_ok);
|
||||
|
||||
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;
|
||||
@@ -108,12 +109,9 @@ static void get_current_attr(char *restrict output, size_t size) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fread(output, 1, size, current) == 0) {
|
||||
if (fread(output, 1, size, current) == 0)
|
||||
LOGE("fread: %s\n", strerror(errno));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(current);
|
||||
}
|
||||
|
||||
@@ -436,7 +434,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 +445,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;
|
||||
}
|
||||
@@ -544,15 +540,13 @@ bool parse_mountinfo(const char *restrict pid, struct mountinfos *restrict mount
|
||||
&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) {
|
||||
struct mountinfo *tmp_mounts = (struct mountinfo *)realloc(mounts->mounts, (i + 1) * sizeof(struct mountinfo));
|
||||
if (!tmp_mounts) {
|
||||
LOGE("Failed to allocate memory for mounts->mounts");
|
||||
|
||||
fclose(mountinfo);
|
||||
free_mounts(mounts);
|
||||
|
||||
return false;
|
||||
goto cleanup_mount_allocs;
|
||||
}
|
||||
mounts->mounts = tmp_mounts;
|
||||
|
||||
unsigned int shared = 0;
|
||||
unsigned int master = 0;
|
||||
@@ -573,16 +567,64 @@ bool parse_mountinfo(const char *restrict pid, struct mountinfos *restrict mount
|
||||
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));
|
||||
if (mounts->mounts[i].root == NULL) {
|
||||
LOGE("Failed to allocate memory for root\n");
|
||||
|
||||
goto cleanup_mount_allocs;
|
||||
}
|
||||
mounts->mounts[i].target = strndup(line + target_start, (size_t)(target_end - target_start));
|
||||
if (mounts->mounts[i].target == NULL) {
|
||||
LOGE("Failed to allocate memory for target\n");
|
||||
|
||||
goto cleanup_root;
|
||||
}
|
||||
mounts->mounts[i].vfs_option = strndup(line + vfs_option_start, (size_t)(vfs_option_end - vfs_option_start));
|
||||
if (mounts->mounts[i].vfs_option == NULL) {
|
||||
LOGE("Failed to allocate memory for vfs_option\n");
|
||||
|
||||
goto cleanup_target;
|
||||
}
|
||||
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));
|
||||
if (mounts->mounts[i].type == NULL) {
|
||||
LOGE("Failed to allocate memory for type\n");
|
||||
|
||||
goto cleanup_vfs_option;
|
||||
}
|
||||
mounts->mounts[i].source = strndup(line + source_start, (size_t)(source_end - source_start));
|
||||
if (mounts->mounts[i].source == NULL) {
|
||||
LOGE("Failed to allocate memory for source\n");
|
||||
|
||||
goto cleanup_type;
|
||||
}
|
||||
mounts->mounts[i].fs_option = strndup(line + fs_option_start, (size_t)(fs_option_end - fs_option_start));
|
||||
if (mounts->mounts[i].fs_option == NULL) {
|
||||
LOGE("Failed to allocate memory for fs_option\n");
|
||||
|
||||
goto cleanup_source;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
continue;
|
||||
|
||||
cleanup_source:
|
||||
free((void *)mounts->mounts[i].source);
|
||||
cleanup_type:
|
||||
free((void *)mounts->mounts[i].type);
|
||||
cleanup_vfs_option:
|
||||
free((void *)mounts->mounts[i].vfs_option);
|
||||
cleanup_target:
|
||||
free((void *)mounts->mounts[i].target);
|
||||
cleanup_root:
|
||||
free((void *)mounts->mounts[i].root);
|
||||
cleanup_mount_allocs:
|
||||
fclose(mountinfo);
|
||||
free_mounts(mounts);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(mountinfo);
|
||||
@@ -603,15 +645,11 @@ bool umount_root(struct root_impl impl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (impl.impl) {
|
||||
case None: { break; }
|
||||
case Multiple: { break; }
|
||||
|
||||
case KernelSU:
|
||||
case APatch: {
|
||||
char source_name[LONGEST_ROOT_IMPL_NAME];
|
||||
/* 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 strcpy(source_name, "APatch");
|
||||
else if (impl.impl == APatch) strcpy(source_name, "APatch");
|
||||
else strcpy(source_name, "magisk");
|
||||
|
||||
LOGI("[%s] Unmounting root", source_name);
|
||||
|
||||
@@ -622,16 +660,16 @@ bool umount_root(struct root_impl impl) {
|
||||
struct mountinfo mount = mounts.mounts[i];
|
||||
|
||||
bool should_unmount = false;
|
||||
|
||||
/* INFO: KernelSU has its own /system mounts, so we only skip the mount
|
||||
if they are from a module, not KSU itself.
|
||||
/* 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) continue;
|
||||
strncmp(mount.root, "/adb/modules/", strlen("/adb/modules/")) == 0 &&
|
||||
strncmp(mount.target, "/system/etc/", strlen("/system/etc/")) != 0) 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;
|
||||
if (strncmp(mount.root, "/adb/modules/", strlen("/adb/modules/")) == 0) should_unmount = true;
|
||||
|
||||
if (!should_unmount) continue;
|
||||
|
||||
@@ -651,7 +689,6 @@ bool umount_root(struct root_impl impl) {
|
||||
|
||||
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 {
|
||||
@@ -660,59 +697,6 @@ bool umount_root(struct root_impl impl) {
|
||||
}
|
||||
free(targets_to_unmount);
|
||||
|
||||
break;
|
||||
}
|
||||
case Magisk: {
|
||||
LOGI("[Magisk] Unmounting root");
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
free_mounts(&mounts);
|
||||
|
||||
return true;
|
||||
@@ -844,7 +828,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,17 @@ 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] = { 0 };
|
||||
get_property("ro.system.product.cpu.abilist", system_arch);
|
||||
|
||||
if (system_arch[0] == '\0')
|
||||
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);
|
||||
@@ -117,7 +123,6 @@ static void load_modules(enum Architecture arch, struct Context *restrict contex
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
context->modules = realloc(context->modules, (size_t)((context->len + 1) * sizeof(struct Module)));
|
||||
if (context->modules == NULL) {
|
||||
LOGE("Failed reallocating memory for modules.\n");
|
||||
@@ -130,13 +135,17 @@ 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) {
|
||||
for (size_t i = 0; i < context->len; i++) {
|
||||
free(context->modules[i].name);
|
||||
if (context->modules[i].companion != -1) close(context->modules[i].companion);
|
||||
if (context->modules[i].companion >= 0) close(context->modules[i].companion);
|
||||
}
|
||||
|
||||
free(context->modules);
|
||||
}
|
||||
|
||||
static int create_daemon_socket(void) {
|
||||
@@ -252,11 +261,6 @@ static int spawn_companion(char *restrict argv[], char *restrict name, int lib_f
|
||||
exit(0);
|
||||
}
|
||||
|
||||
struct __attribute__((__packed__)) MsgHead {
|
||||
unsigned int cmd;
|
||||
int length;
|
||||
};
|
||||
|
||||
/* WARNING: Dynamic memory based */
|
||||
void zygiskd_start(char *restrict argv[]) {
|
||||
/* INFO: When implementation is None or Multiple, it won't set the values
|
||||
@@ -267,101 +271,63 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
struct root_impl impl;
|
||||
get_impl(&impl);
|
||||
if (impl.impl == None || impl.impl == Multiple) {
|
||||
char *msg_data = NULL;
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ DAEMON_SET_ERROR_INFO }, sizeof(uint8_t));
|
||||
|
||||
if (impl.impl == None) msg_data = "Unsupported environment: Unknown root implementation";
|
||||
else msg_data = "Unsupported environment: Multiple root implementations found";
|
||||
const char *msg = NULL;
|
||||
if (impl.impl == None) msg = "Unsupported environment: Unknown root implementation";
|
||||
else msg = "Unsupported environment: Multiple root implementations found";
|
||||
|
||||
struct MsgHead msg = {
|
||||
.cmd = DAEMON_SET_ERROR_INFO,
|
||||
.length = (int)strlen(msg_data) + 1
|
||||
};
|
||||
LOGE("%s", msg);
|
||||
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, msg_data, (size_t)msg.length);
|
||||
uint32_t msg_len = (uint32_t)strlen(msg);
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg_len, sizeof(msg_len));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, (void *)msg, msg_len);
|
||||
|
||||
free(msg_data);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
enum Architecture arch = get_arch();
|
||||
load_modules(arch, &context);
|
||||
|
||||
char *module_list = NULL;
|
||||
size_t module_list_len = 0;
|
||||
if (context.len == 0) {
|
||||
module_list = strdup("None");
|
||||
module_list_len = strlen("None");
|
||||
} else {
|
||||
for (size_t i = 0; i < context.len; i++) {
|
||||
if (i != context.len - 1) {
|
||||
module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + strlen(", ") + 1);
|
||||
if (module_list == NULL) {
|
||||
LOGE("Failed reallocating memory for module list.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(module_list + module_list_len, context.modules[i].name);
|
||||
|
||||
module_list_len += strlen(context.modules[i].name);
|
||||
|
||||
strcpy(module_list + module_list_len, ", ");
|
||||
|
||||
module_list_len += strlen(", ");
|
||||
} else {
|
||||
module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + 1);
|
||||
if (module_list == NULL) {
|
||||
LOGE("Failed reallocating memory for module list.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(module_list + module_list_len, context.modules[i].name);
|
||||
|
||||
module_list_len += strlen(context.modules[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ DAEMON_SET_INFO }, sizeof(uint8_t));
|
||||
|
||||
char impl_name[LONGEST_ROOT_IMPL_NAME];
|
||||
stringify_root_impl_name(impl, impl_name);
|
||||
|
||||
size_t msg_length = strlen("Root: , Modules: ") + strlen(impl_name) + module_list_len + 1;
|
||||
uint32_t root_impl_len = (uint32_t)strlen(impl_name);
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &root_impl_len, sizeof(root_impl_len));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, impl_name, root_impl_len);
|
||||
|
||||
struct MsgHead msg = {
|
||||
.cmd = DAEMON_SET_INFO,
|
||||
.length = (int)msg_length
|
||||
};
|
||||
uint32_t modules_len = (uint32_t)context.len;
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &modules_len, sizeof(modules_len));
|
||||
|
||||
char *msg_data = malloc(msg_length);
|
||||
if (msg_data == NULL) {
|
||||
LOGE("Failed allocating memory for message data.\n");
|
||||
|
||||
return;
|
||||
for (size_t i = 0; i < context.len; i++) {
|
||||
uint32_t module_name_len = (uint32_t)strlen(context.modules[i].name);
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &module_name_len, sizeof(module_name_len));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, context.modules[i].name, module_name_len);
|
||||
}
|
||||
|
||||
snprintf(msg_data, msg_length, "Root: %s, Modules: %s", impl_name, module_list);
|
||||
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, msg_data, msg_length);
|
||||
|
||||
free(msg_data);
|
||||
free(module_list);
|
||||
LOGI("Sent root implementation and modules information to controller socket");
|
||||
}
|
||||
|
||||
int socket_fd = create_daemon_socket();
|
||||
if (socket_fd == -1) {
|
||||
LOGE("Failed creating daemon socket\n");
|
||||
|
||||
free_modules(&context);
|
||||
|
||||
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);
|
||||
if (client_fd == -1) {
|
||||
LOGE("accept: %s\n", strerror(errno));
|
||||
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t action8 = 0;
|
||||
@@ -369,53 +335,33 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
if (len == -1) {
|
||||
LOGE("read: %s\n", strerror(errno));
|
||||
|
||||
return;
|
||||
break;
|
||||
} else if (len == 0) {
|
||||
LOGI("Client disconnected\n");
|
||||
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
enum DaemonSocketAction action = (enum DaemonSocketAction)action8;
|
||||
|
||||
switch (action) {
|
||||
case PingHeartbeat: {
|
||||
struct MsgHead msg = {
|
||||
.cmd = ZYGOTE_INJECTED,
|
||||
.length = 0
|
||||
};
|
||||
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ ZYGOTE_INJECTED }, sizeof(uint8_t));
|
||||
|
||||
break;
|
||||
}
|
||||
case ZygoteRestart: {
|
||||
for (size_t i = 0; i < context.len; i++) {
|
||||
if (context.modules[i].companion != -1) {
|
||||
if (context.modules[i].companion <= -1) continue;
|
||||
|
||||
close(context.modules[i].companion);
|
||||
context.modules[i].companion = -1;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SystemServerStarted: {
|
||||
struct MsgHead msg = {
|
||||
.cmd = SYSTEM_SERVER_STARTED,
|
||||
.length = 0
|
||||
};
|
||||
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
|
||||
|
||||
if (impl.impl == None || impl.impl == Multiple) {
|
||||
LOGI("Unsupported environment detected. Exiting.\n");
|
||||
|
||||
close(client_fd);
|
||||
close(socket_fd);
|
||||
free_modules(&context);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ SYSTEM_SERVER_STARTED }, sizeof(uint8_t));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -555,9 +501,19 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
ssize_t ret = read_size_t(client_fd, &index);
|
||||
ASSURE_SIZE_READ_BREAK("RequestCompanionSocket", "index", ret, sizeof(index));
|
||||
|
||||
struct Module *module = &context.modules[index];
|
||||
if (index >= context.len) {
|
||||
LOGE("Invalid module index: %zu\n", index);
|
||||
|
||||
if (module->companion != -1) {
|
||||
ret = write_uint8_t(client_fd, 0);
|
||||
ASSURE_SIZE_WRITE_BREAK("RequestCompanionSocket", "response", ret, sizeof(int));
|
||||
|
||||
close(client_fd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
struct Module *module = &context.modules[index];
|
||||
if (module->companion >= 0) {
|
||||
if (!check_unix_socket(module->companion, false)) {
|
||||
LOGE(" - Companion for module \"%s\" crashed\n", module->name);
|
||||
|
||||
@@ -566,10 +522,10 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (module->companion == -1) {
|
||||
if (module->companion <= -1) {
|
||||
module->companion = spawn_companion(argv, module->name, module->lib_fd);
|
||||
|
||||
if (module->companion > 0) {
|
||||
if (module->companion >= 0) {
|
||||
LOGI(" - Spawned companion for \"%s\": %d\n", module->name, module->companion);
|
||||
} else {
|
||||
if (module->companion == -2) {
|
||||
@@ -586,7 +542,7 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
so just sending the file descriptor of the client is
|
||||
safe.
|
||||
*/
|
||||
if (module->companion != -1) {
|
||||
if (module->companion >= 0) {
|
||||
LOGI(" - Sending companion fd socket of module \"%s\"\n", module->name);
|
||||
|
||||
if (write_fd(module->companion, client_fd) == -1) {
|
||||
@@ -618,6 +574,17 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
ssize_t ret = read_size_t(client_fd, &index);
|
||||
ASSURE_SIZE_READ_BREAK("GetModuleDir", "index", ret, sizeof(index));
|
||||
|
||||
if (index >= context.len) {
|
||||
LOGE("Invalid module index: %zu\n", index);
|
||||
|
||||
ret = write_uint8_t(client_fd, 0);
|
||||
ASSURE_SIZE_WRITE_BREAK("GetModuleDir", "response", ret, sizeof(int));
|
||||
|
||||
close(client_fd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
char module_dir[PATH_MAX];
|
||||
snprintf(module_dir, PATH_MAX, "%s/%s", PATH_MODULES_DIR, context.modules[index].name);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user